-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgsFlattenConnections.m
More file actions
149 lines (133 loc) · 5.28 KB
/
Copy pathgsFlattenConnections.m
File metadata and controls
149 lines (133 loc) · 5.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
function conns = gsFlattenConnections(mdl)
%GSFLATTENCONNECTIONS Leaf-to-leaf signal set of an architecture model.
% CONNS = GSFLATTENCONNECTIONS(MDL) returns a sorted string array of
% "Source.port -> Dest.port" entries covering every signal path between
% LEAF components, with intermediate composite (bay) boundary ports
% traversed rather than reported.
%
% The point is hierarchy-invariance: moving components into bays changes
% the connector list at every level but must not change which leaf port
% ends up driving which. Diffing this set before and after a regrouping
% is what proves the regrouping was signal-preserving.
%
% Root-level architecture ports are reported as "<root>.portName" so
% that connections to the model boundary are covered too.
%
% Components are identified as "Name#uuid8" rather than by name alone.
% EverSimmer's three production cells hold identically named components
% (CellController, CellCookVat, ...), so a name-keyed set silently
% collapses the three cells into one and would not notice a signal
% rerouted from one cell to another. UUIDs survive regrouping, so they
% give an identity that is both unique and hierarchy-invariant.
%
% See also regroupPhysicalBays, verifyBayRegrouping.
arch = systemcomposer.loadModel(mdl);
drawnow;
leaves = collectLeafComponents(arch.Architecture);
conns = strings(0);
for k = 1:numel(leaves)
comp = leaves(k);
for p = comp.Ports
if ~strcmp(p.Direction, 'Output'), continue; end
dests = resolveDownstream(p);
for d = 1:numel(dests)
conns(end+1) = sprintf('%s.%s -> %s', ...
compId(comp), p.Name, dests(d)); %#ok<AGROW>
end
end
end
% Root inputs feeding into the design are not covered by the leaf-output
% sweep above, so walk them separately.
for ap = arch.Architecture.Ports
if ~strcmp(ap.Direction, 'Input'), continue; end
dests = resolveDownstream(ap);
for d = 1:numel(dests)
conns(end+1) = sprintf('<root>.%s -> %s', ap.Name, dests(d)); %#ok<AGROW>
end
end
conns = unique(sort(conns));
end
% ----------------------------------------------------------------------
function leaves = collectLeafComponents(architecture)
leaves = systemcomposer.arch.Component.empty;
for c = architecture.Components
if isempty(c.Architecture.Components)
leaves(end+1) = c; %#ok<AGROW>
else
leaves = [leaves, collectLeafComponents(c.Architecture)]; %#ok<AGROW>
end
end
end
% ----------------------------------------------------------------------
function dests = resolveDownstream(startPort)
%RESOLVEDOWNSTREAM Follow a signal from STARTPORT to every leaf input port
% it reaches, hopping through composite boundaries.
%
% Three kinds of hop are possible at each step:
% * into a composite - a bay's component port continues on the
% same-named architecture port inside it
% * out of a composite - an architecture port continues on the
% same-named component port of the owning bay
% * onto a leaf - terminal; recorded
dests = strings(0);
queue = {startPort};
seen = strings(0);
while ~isempty(queue)
p = queue{1};
queue(1) = [];
for c = p.Connectors
for other = [c.SourcePort, c.DestinationPort]
if other == p, continue; end
key = portKey(other);
if any(seen == key), continue; end
seen(end+1) = key; %#ok<AGROW>
if isa(other, 'systemcomposer.arch.ArchitecturePort')
owner = ownerComponent(other);
if isempty(owner)
% model boundary
dests(end+1) = sprintf('<root>.%s', other.Name); %#ok<AGROW>
else
twin = matchingPort(owner.Ports, other.Name);
if ~isempty(twin), queue{end+1} = twin; end %#ok<AGROW>
end
else
parent = other.Parent;
if isempty(parent.Architecture.Components)
dests(end+1) = sprintf('%s.%s', compId(parent), other.Name); %#ok<AGROW>
else
twin = matchingPort(parent.Architecture.Ports, other.Name);
if ~isempty(twin), queue{end+1} = twin; end %#ok<AGROW>
end
end
end
end
end
end
% ----------------------------------------------------------------------
function owner = ownerComponent(archPort)
%OWNERCOMPONENT Component owning the architecture that ARCHPORT sits on,
% or [] when that architecture is the model root.
owner = [];
a = archPort.Parent;
if isprop(a, 'Parent') && ~isempty(a.Parent) && ...
isa(a.Parent, 'systemcomposer.arch.Component')
owner = a.Parent;
end
end
% ----------------------------------------------------------------------
function p = matchingPort(ports, name)
p = [];
for k = 1:numel(ports)
if strcmp(ports(k).Name, name), p = ports(k); return; end
end
end
% ----------------------------------------------------------------------
function id = compId(comp)
%COMPID Name plus a UUID prefix, so replicated components stay distinct.
u = char(comp.UUID);
id = sprintf('%s#%s', comp.Name, u(1:8));
end
% ----------------------------------------------------------------------
function k = portKey(p)
k = string(class(p)) + "|" + string(p.UUID);
end