Skip to content

Commit 3097878

Browse files
HanSur94claude
andcommitted
docs(dashboard): add example scripts for all new widget types
Create standalone runnable example scripts for the 6 Phase B widgets (HeatmapWidget, BarChartWidget, HistogramWidget, ScatterWidget, ImageWidget, MultiStatusWidget) and a documented placeholder for the planned GroupWidget. Also extend example_dashboard_all_widgets.m to include all 6 new widget types in the all-in-one demo. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent fd9954b commit 3097878

8 files changed

Lines changed: 878 additions & 0 deletions

examples/example_dashboard_all_widgets.m

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
% Number/Gauge/Status: driven by sensor data (latest value / threshold check)
77
% Text/Table: static display (no interactivity needed)
88
% Timeline: derived from threshold violations
9+
% Heatmap: 2D colour grid (DataFcn returning matrix)
10+
% BarChart: categorical bar chart (DataFcn returning struct)
11+
% Histogram: sensor value distribution with optional normal fit
12+
% Scatter: two-sensor X-vs-Y correlation plot
13+
% Image: generated test pattern via ImageFcn
14+
% MultiStatus: per-sensor status grid (dot indicators)
915
%
1016
% Usage:
1117
% example_dashboard_all_widgets
@@ -247,11 +253,92 @@
247253
'Position', [1 19 24 3], ...
248254
'Events', events);
249255

256+
% --- Row 22-27: Phase B widgets — Heatmap, BarChart, Histogram ---
257+
258+
% Heatmap: temperature binned by hour-of-day (24 cols) vs machine mode (3 rows)
259+
% Pre-compute mode at every sample to avoid calling valueAt N*24*3 times.
260+
tMode = zeros(1, N);
261+
for k = 1:N
262+
tMode(k) = scMode.valueAt(t(k));
263+
end
264+
hourBins = zeros(3, 24);
265+
for h = 0:23
266+
hourIdx = (floor(t / 3600) == h);
267+
for m = 0:2
268+
mIdx = hourIdx & (tMode == m);
269+
if any(mIdx)
270+
hourBins(m+1, h+1) = mean(temp(mIdx));
271+
end
272+
end
273+
end
274+
hourXLabels = cell(1, 24);
275+
for h = 0:23
276+
hourXLabels{h+1} = sprintf('%dh', h);
277+
end
278+
d.addWidget('heatmap', 'Title', 'Temp by Hour & Machine Mode', ...
279+
'Position', [1 22 14 6], ...
280+
'DataFcn', @() hourBins, ...
281+
'Colormap', 'jet', ...
282+
'XLabels', hourXLabels, ...
283+
'YLabels', {'Idle','Running','Maint'});
284+
285+
% BarChart: alarm counts by sensor tag
286+
alarmCounts = struct( ...
287+
'categories', {{'T-401','P-201','F-301'}}, ...
288+
'values', [sTemp.countViolations(), sPress.countViolations(), sFlow.countViolations()]);
289+
d.addWidget('barchart', 'Title', 'Violation Count by Sensor', ...
290+
'Position', [15 22 10 6], ...
291+
'DataFcn', @() alarmCounts, ...
292+
'Orientation', 'vertical');
293+
294+
% --- Row 28-33: Histogram, Scatter, Image, MultiStatus ---
295+
296+
% Histogram: temperature distribution with normal fit
297+
d.addWidget('histogram', 'Title', 'Temperature Distribution', ...
298+
'Position', [1 28 8 6], ...
299+
'Sensor', sTemp, ...
300+
'ShowNormalFit', true, ...
301+
'NumBins', 40);
302+
303+
% Scatter: temperature vs pressure, color-coded by flow rate
304+
d.addWidget('scatter', 'Title', 'Temp vs Pressure (color=Flow)', ...
305+
'Position', [9 28 8 6], ...
306+
'SensorX', sTemp, ...
307+
'SensorY', sPress, ...
308+
'SensorColor', sFlow, ...
309+
'MarkerSize', 4, ...
310+
'Colormap', 'parula');
311+
312+
% Image: procedural test pattern (no external file dependency)
313+
d.addWidget('image', 'Title', 'Process Diagram', ...
314+
'Position', [17 28 8 6], ...
315+
'ImageFcn', @() makePeaksThumb(), ...
316+
'Caption', 'Elevation map (peaks function)', ...
317+
'Scaling', 'fit');
318+
319+
% MultiStatus: all three process sensors at a glance
320+
d.addWidget('multistatus', 'Title', 'Sensor Status', ...
321+
'Position', [1 34 24 3], ...
322+
'Sensors', {sTemp, sPress, sFlow}, ...
323+
'Columns', 3, ...
324+
'IconStyle', 'dot', ...
325+
'ShowLabels', true);
326+
250327
%% Render
251328
d.render();
252329

253330
fprintf('Dashboard rendered with %d widgets.\n', numel(d.Widgets));
254331
fprintf('Sensors: T-401 (%d violations), P-201 (%d violations), F-301 (%d violations)\n', ...
255332
sTemp.countViolations(), sPress.countViolations(), sFlow.countViolations());
333+
fprintf('Phase B widgets added: heatmap, barchart, histogram, scatter, image, multistatus.\n');
256334
fprintf('Click "Edit" to enter GUI builder mode.\n');
257335
fprintf('Click "Save" to export as JSON, "Export" to generate .m script.\n');
336+
337+
%% ---- Helper function ----
338+
function img = makePeaksThumb()
339+
% Return a grayscale uint8 image derived from peaks(64).
340+
Z = peaks(64);
341+
Z = Z - min(Z(:));
342+
Z = Z ./ max(Z(:));
343+
img = uint8(round(Z * 255));
344+
end

examples/example_widget_barchart.m

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
%% BarChartWidget — All Configurations Demo
2+
% Demonstrates BarChartWidget with vertical and horizontal orientations,
3+
% DataFcn data sources, and a sensor-bound mode.
4+
%
5+
% BarChartWidget Properties:
6+
% DataFcn — function_handle returning a struct with fields:
7+
% categories — cell array of category labels
8+
% values — numeric vector of bar heights
9+
% or returning a plain numeric vector (no labels).
10+
% Sensor — Sensor object; Y vector used directly as bar values.
11+
% Orientation — 'vertical' (default) or 'horizontal'.
12+
% Stacked — stacked bars when multiple value columns (default false).
13+
% Title — widget title.
14+
% Position — [col row width height] on the 24-column grid.
15+
%
16+
% Usage:
17+
% example_widget_barchart
18+
19+
close all force;
20+
clear functions;
21+
projectRoot = fileparts(fileparts(mfilename('fullpath')));
22+
run(fullfile(projectRoot, 'install.m'));
23+
24+
%% 1. Define data sources
25+
26+
% --- Production output per shift (vertical bar chart) ---
27+
shiftData = struct( ...
28+
'categories', {{'Shift A', 'Shift B', 'Shift C', 'Shift D'}}, ...
29+
'values', [412, 387, 455, 398]);
30+
31+
% --- Alarm counts by sensor (horizontal — long category labels) ---
32+
alarmData = struct( ...
33+
'categories', {{'Temperature T-401', 'Pressure P-201', 'Flow F-301', ...
34+
'Level L-102', 'Vibration V-501'}}, ...
35+
'values', [8, 3, 12, 1, 5]);
36+
37+
% --- Weekly throughput — plain numeric, no labels ---
38+
weeklyValues = [1820 1975 1843 2010 1965 1750 1410];
39+
40+
% --- Sensor-bound: hourly flow averages (8 buckets) ---
41+
rng(42);
42+
N = 5000;
43+
t = linspace(0, 28800, N); % 8 hours in seconds
44+
sFlow = Sensor('F-301', 'Name', 'Flow Rate');
45+
sFlow.Units = 'L/min';
46+
sFlow.X = t;
47+
sFlow.Y = max(0, 120 + 20*sin(2*pi*t/3600) + randn(1,N)*5);
48+
sFlow.addThresholdRule(struct(), 140, ...
49+
'Direction', 'upper', 'Label', 'Hi Alarm', ...
50+
'Color', [1 0.2 0.2], 'LineStyle', '-');
51+
sFlow.resolve();
52+
53+
%% 2. Build dashboard
54+
d = DashboardEngine('Bar Chart Widget Demo');
55+
d.Theme = 'light';
56+
57+
% Row 1-5: Vertical — production by shift
58+
d.addWidget('barchart', 'Title', 'Production by Shift', ...
59+
'Position', [1 1 12 5], ...
60+
'DataFcn', @() shiftData, ...
61+
'Orientation', 'vertical');
62+
63+
% Row 1-5: Horizontal — alarm counts by sensor tag
64+
d.addWidget('barchart', 'Title', 'Alarm Count by Sensor', ...
65+
'Position', [13 1 12 5], ...
66+
'DataFcn', @() alarmData, ...
67+
'Orientation', 'horizontal');
68+
69+
% Row 6-10: Vertical, no labels — plain numeric vector
70+
d.addWidget('barchart', 'Title', 'Weekly Throughput (Mon-Sun)', ...
71+
'Position', [1 6 12 5], ...
72+
'DataFcn', @() weeklyValues, ...
73+
'Orientation', 'vertical');
74+
75+
% Row 6-10: Sensor-bound (uses Sensor.Y values directly as bar data)
76+
d.addWidget('barchart', 'Title', 'Flow Distribution (sensor)', ...
77+
'Position', [13 6 12 5], ...
78+
'Sensor', sFlow, ...
79+
'Orientation', 'vertical');
80+
81+
%% 3. Render
82+
d.render();
83+
fprintf('Dashboard rendered with %d bar chart widgets.\n', numel(d.Widgets));
84+
fprintf('Flow sensor %s: latest %.1f %s\n', ...
85+
sFlow.Key, sFlow.Y(end), sFlow.Units);

examples/example_widget_group.m

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
%% GroupWidget — All Modes Demo
2+
% Demonstrates GroupWidget as a container for child widgets, covering all
3+
% three grouping modes: panel, collapsible, and tabbed.
4+
%
5+
% NOTE: GroupWidget is defined in the Phase A implementation plan
6+
% (docs/superpowers/plans/2026-03-18-dashboard-groupwidget-phase-a.md).
7+
% This example script will run correctly once GroupWidget is implemented
8+
% and registered in DashboardEngine. Until then it documents the
9+
% intended API; uncommenting is the only change needed after Phase A lands.
10+
%
11+
% GroupWidget Properties:
12+
% Mode — 'panel' (default) | 'collapsible' | 'tabbed'.
13+
% Label — header title string shown in the group header bar.
14+
% Children — cell array of DashboardWidget (panel / collapsible modes).
15+
% Tabs — cell array of structs with fields 'name' and 'widgets'
16+
% (tabbed mode only).
17+
% ActiveTab — name of the initially visible tab (tabbed mode).
18+
% Collapsed — initial collapsed state (collapsible mode, default false).
19+
% ChildAutoFlow — auto-arrange children left-to-right (default true).
20+
% ChildColumns — column count of the child sub-grid (default 24).
21+
% Position — [col row width height] on the 24-column grid.
22+
%
23+
% Usage:
24+
% example_widget_group
25+
26+
close all force;
27+
clear functions;
28+
projectRoot = fileparts(fileparts(mfilename('fullpath')));
29+
run(fullfile(projectRoot, 'install.m'));
30+
31+
%% 1. Create sensors
32+
rng(42);
33+
N = 5000;
34+
t = linspace(0, 86400, N); % 24 hours
35+
36+
sTemp = Sensor('T-401', 'Name', 'Temperature');
37+
sTemp.Units = [char(176) 'F'];
38+
sTemp.X = t;
39+
sTemp.Y = 72 + 4*sin(2*pi*t/3600) + randn(1,N)*1.2;
40+
sTemp.Y(end) = 79; % near warning level
41+
sTemp.addThresholdRule(struct(), 78, ...
42+
'Direction', 'upper', 'Label', 'Hi Warn', ...
43+
'Color', [1 0.8 0], 'LineStyle', '--');
44+
sTemp.addThresholdRule(struct(), 85, ...
45+
'Direction', 'upper', 'Label', 'Hi Alarm', ...
46+
'Color', [1 0.2 0.2], 'LineStyle', '-');
47+
sTemp.resolve();
48+
49+
sPress = Sensor('P-201', 'Name', 'Pressure');
50+
sPress.Units = 'psi';
51+
sPress.X = t;
52+
sPress.Y = 55 + 8*sin(2*pi*t/7200) + randn(1,N)*1.5;
53+
sPress.addThresholdRule(struct(), 68, ...
54+
'Direction', 'upper', 'Label', 'Hi Warn', ...
55+
'Color', [1 0.8 0], 'LineStyle', '--');
56+
sPress.resolve();
57+
58+
sFlow = Sensor('F-301', 'Name', 'Flow Rate');
59+
sFlow.Units = 'L/min';
60+
sFlow.X = t;
61+
sFlow.Y = max(0, 120 + 10*sin(2*pi*t/1800) + randn(1,N)*4);
62+
sFlow.resolve();
63+
64+
sVib = Sensor('V-501', 'Name', 'Vibration RMS');
65+
sVib.Units = 'mm/s';
66+
sVib.X = t;
67+
sVib.Y = max(0.1, 1.5 + 0.4*sin(2*pi*t/5400) + randn(1,N)*0.2);
68+
sVib.resolve();
69+
70+
%% 2. Build dashboard
71+
d = DashboardEngine('Group Widget Demo');
72+
d.Theme = 'light';
73+
74+
%% --- Mode 1: Panel group (default) ---
75+
% A panel group acts as a titled container. Children auto-flow left to
76+
% right inside the group boundaries. Equivalent to placing widgets in a
77+
% named section of the dashboard.
78+
%
79+
% g1 = GroupWidget('Label', 'Motor Health', 'Mode', 'panel', ...
80+
% 'Position', [1 1 24 4]);
81+
% g1.addChild(NumberWidget('Sensor', sTemp));
82+
% g1.addChild(NumberWidget('Sensor', sPress));
83+
% g1.addChild(GaugeWidget('Sensor', sTemp, 'Style', 'arc'));
84+
% g1.addChild(GaugeWidget('Sensor', sPress, 'Style', 'arc'));
85+
% d.addWidget('group', 'Label', 'Motor Health', 'Mode', 'panel', ...
86+
% 'Position', [1 1 24 4], ...
87+
% 'Children', {NumberWidget('Sensor', sTemp), ...
88+
% NumberWidget('Sensor', sPress), ...
89+
% GaugeWidget('Sensor', sTemp, 'Style', 'arc'), ...
90+
% GaugeWidget('Sensor', sPress, 'Style', 'arc')});
91+
92+
%% --- Mode 2: Collapsible group ---
93+
% A collapsible group renders a clickable header that hides/shows children.
94+
% Collapse() reduces Position(4) to 1 grid row; DashboardLayout.reflow()
95+
% compacts widgets below it automatically.
96+
%
97+
% d.addWidget('group', 'Label', 'Vibration Details', 'Mode', 'collapsible', ...
98+
% 'Position', [1 5 24 6], ...
99+
% 'Collapsed', false, ...
100+
% 'Children', {FastSenseWidget('Sensor', sVib)});
101+
102+
%% --- Mode 3: Tabbed group ---
103+
% A tabbed group shows one tab at a time. Each tab gets its own named
104+
% panel. Tab buttons appear in the header bar. ActiveTab sets which tab
105+
% is visible on first render.
106+
%
107+
% tab1 = struct('name', 'Overview', 'widgets', {{ ...
108+
% NumberWidget('Sensor', sTemp), ...
109+
% NumberWidget('Sensor', sPress), ...
110+
% StatusWidget('Sensor', sTemp) ...
111+
% }});
112+
% tab2 = struct('name', 'Trends', 'widgets', {{ ...
113+
% FastSenseWidget('Sensor', sTemp), ...
114+
% FastSenseWidget('Sensor', sFlow) ...
115+
% }});
116+
% d.addWidget('group', 'Label', 'Process Monitor', 'Mode', 'tabbed', ...
117+
% 'Position', [1 11 24 8], ...
118+
% 'Tabs', {tab1, tab2}, ...
119+
% 'ActiveTab', 'Overview');
120+
121+
%% Fallback: render standalone widgets that mirror GroupWidget children
122+
% This section produces a working dashboard today and will be replaced once
123+
% GroupWidget lands.
124+
125+
% Panel group equivalent — KPI row + gauge row
126+
d.addWidget('number', 'Position', [1 1 6 2], 'Sensor', sTemp);
127+
d.addWidget('number', 'Position', [7 1 6 2], 'Sensor', sPress);
128+
d.addWidget('gauge', 'Position', [13 1 6 4], 'Sensor', sTemp, 'Style', 'arc');
129+
d.addWidget('gauge', 'Position', [19 1 6 4], 'Sensor', sPress, 'Style', 'arc');
130+
131+
% Collapsible group equivalent — vibration detail
132+
d.addWidget('fastsense', 'Position', [1 5 24 6], 'Sensor', sVib);
133+
134+
% Tabbed group equivalent — overview tab (visible) + trends tab (hidden)
135+
d.addWidget('number', 'Position', [1 11 6 2], 'Sensor', sTemp);
136+
d.addWidget('number', 'Position', [7 11 6 2], 'Sensor', sPress);
137+
d.addWidget('status', 'Position', [13 11 5 2], 'Sensor', sTemp);
138+
d.addWidget('fastsense', 'Position', [1 13 12 6], 'Sensor', sTemp);
139+
d.addWidget('fastsense', 'Position', [13 13 12 6], 'Sensor', sFlow);
140+
141+
%% 3. Render
142+
d.render();
143+
144+
fprintf('Dashboard rendered with %d widgets.\n', numel(d.Widgets));
145+
fprintf('GroupWidget Phase A required for group/collapsible/tabbed container modes.\n');
146+
fprintf('See: docs/superpowers/plans/2026-03-18-dashboard-groupwidget-phase-a.md\n');

0 commit comments

Comments
 (0)