@@ -408,13 +408,43 @@ function resolveInspectorState_(obj)
408408 % RESOLVEINSPECTORSTATE_ Compute (state, payload) and fire InspectorStateChanged.
409409 % Single notify point for the inspector. Inspector subscribes via the
410410 % InspectorStateChanged listener wired in the constructor / setProject.
411+ %
412+ % Resolution strategy: prefer tag HANDLES from the catalog snapshot
413+ % (CatalogPane_.getSelectedTags()) over a TagRegistry.get() round
414+ % trip. The catalog already has resolved Tag handles; using them
415+ % directly bypasses any drift between the catalog snapshot and
416+ % the TagRegistry singleton's current state (e.g. cooling.health
417+ % visible in catalog but missing from get() — a real bug seen in
418+ % the industrial plant demo). Keys without a matching handle in
419+ % the catalog snapshot are silently dropped.
411420 try
412- [state , payload ] = inspectorResolveState( ...
413- obj .LastInteraction_ , ...
414- obj .SelectedTagKeys_ , ...
415- obj .SelectedDashboardIdx_ , ...
416- obj .Engines_ , ...
417- obj .Registry_ );
421+ state = ' ' ;
422+ payload = struct();
423+ tags = obj .CatalogPane_ .getSelectedTags();
424+ nTags = numel(tags );
425+
426+ if nTags == 1
427+ state = ' tag' ;
428+ payload = struct(' tag' , tags{1 }, ...
429+ ' tagKeys' , {obj .SelectedTagKeys_ });
430+ elseif nTags >= 2
431+ state = ' multitag' ;
432+ payload = struct(' tags' , {tags }, ...
433+ ' tagKeys' , {obj .SelectedTagKeys_ });
434+ elseif strcmp(obj .LastInteraction_ , ' dashboard' ) ...
435+ && isnumeric(obj .SelectedDashboardIdx_ ) ...
436+ && isscalar(obj .SelectedDashboardIdx_ ) ...
437+ && obj .SelectedDashboardIdx_ > 0 ...
438+ && obj .SelectedDashboardIdx_ <= numel(obj .Engines_ )
439+ state = ' dashboard' ;
440+ payload = struct(' dashboard' , ...
441+ obj.Engines_{obj .SelectedDashboardIdx_ });
442+ else
443+ state = ' welcome' ;
444+ payload = struct(' nTags' , nTags , ...
445+ ' nDashboards' , numel(obj .Engines_ ));
446+ end
447+
418448 ed = InspectorStateEventData(state , payload );
419449 notify(obj , ' InspectorStateChanged' , ed );
420450 catch err
@@ -435,9 +465,27 @@ function onOpenAdHocPlotRequested_(obj, ~, evt)
435465 try
436466 keys = evt .TagKeys ;
437467 mode = evt .Mode ;
438- tags = cell(1 , numel(keys ));
468+ % Prefer the catalog snapshot's already-resolved Tag handles
469+ % over a TagRegistry.get() round-trip (catalog/registry can
470+ % drift; see resolveInspectorState_).
471+ allTags = obj .CatalogPane_ .getSelectedTags();
472+ allKeys = obj .SelectedTagKeys_ ;
473+ tags = {};
439474 for k = 1 : numel(keys )
440- tags{k } = obj .Registry_ .get(keys{k });
475+ matched = false ;
476+ for j = 1 : numel(allTags )
477+ if strcmp(allKeys{j }, keys{k })
478+ tags{end + 1 } = allTags{j }; % #ok<AGROW>
479+ matched = true ;
480+ break ;
481+ end
482+ end
483+ if ~matched
484+ % Last-resort registry fallback (still wrapped in
485+ % the outer try/catch so a missing key surfaces as
486+ % uialert instead of crashing the figure callback).
487+ tags{end + 1 } = obj .Registry_ .get(keys{k }); % #ok<AGROW>
488+ end
441489 end
442490 [~ , skipped ] = openAdHocPlot(tags , mode , obj .Theme );
443491 if ~isempty(skipped )
0 commit comments