LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
refreshStruct.m
1function refreshStruct(self, hardRefresh)
2% REFRESHSTRUCT()
3%
4% Copyright (c) 2012-2026, Imperial College London
5% All rights reserved.
6
7sanitize(self);
8resolveSignals(self); % Resolve Signal placeholders to OpenSignal or ClosedSignal
9if nargin<2
10 hardRefresh = true;
11end
12
13%% store invariant information
14if self.hasStruct && ~hardRefresh
15 rtorig = self.sn.rtorig; % this must be destroyed with resetNetwork
16end
17
18if self.hasStruct && ~hardRefresh
19 nodetypes = sn.nodetypes;
20 classnames = sn.classnames;
21 nodenames = sn.nodenames;
22 refstat = sn.refstat;
23else
24 nodetypes = getNodeTypes(self);
25 classnames = getClassNames(self);
26 nodenames = getNodeNames(self);
27 refstat = getReferenceStations(self);
28
29 % Append FCR names and types to node lists (only when refreshing)
30 for f = 1:length(self.regions)
31 fcr = self.regions{f};
32 nodenames{end+1} = fcr.getName();
33 nodetypes(end+1) = NodeType.Region;
34 end
35end
36conn = self.getConnectionMatrix;
37njobs = getNumberOfJobs(self);
38numservers = getStationServers(self);
39lldscaling = getLimitedLoadDependence(self);
40cdscaling = getLimitedClassDependence(self);
41[ljdscaling, ljdcutoffs] = getLimitedJointDependence(self);
42[ljcdscaling, ljcdcutoffs] = getLimitedJointClassDependence(self);
43
44%% init minimal structure
45sn = NetworkStruct(); % create in self to ensure propagation
46if isempty(self.sn)
47 sn.rtorig = {};
48 sn.reward = {};
49else
50 sn.rtorig = self.sn.rtorig;
51 if isfield(self.sn, 'reward')
52 sn.reward = self.sn.reward; % preserve reward definitions
53 else
54 sn.reward = {};
55 end
56end
57% sn.nnodes counts physical nodes only; FCRs are virtual nodes appended to nodenames/nodetypes
58sn.nnodes = numel(self.nodes);
59sn.nclasses = length(classnames);
60
61%% get routing strategies
62routing = zeros(sn.nnodes, sn.nclasses);
63for ind=1:sn.nnodes
64 for r=1:sn.nclasses
65 if isempty(self.nodes{ind}.output.outputStrategy{r})
66 routing(ind,r) = RoutingStrategy.DISABLED;
67 else
68 routing(ind,r) = RoutingStrategy.fromText(self.nodes{ind}.output.outputStrategy{r}{2});
69 end
70 end
71end
72sn.isslc = false(sn.nclasses,1);
73for r=1:sn.nclasses
74 if isa(self.classes{r},'SelfLoopingClass')
75 sn.isslc(r) = true;
76 end
77end
78sn.issignal = false(sn.nclasses,1);
79sn.signaltype = cell(sn.nclasses,1);
80for r=1:sn.nclasses
81 sn.signaltype{r} = NaN;
82end
83% Detect Signal classes and populate issignal/signaltype
84% Check for Signal, OpenSignal, and ClosedSignal classes
85for r=1:sn.nclasses
86 if isa(self.classes{r},'Signal') || isa(self.classes{r},'OpenSignal') || isa(self.classes{r},'ClosedSignal')
87 sn.issignal(r) = true;
88 sn.signaltype{r} = self.classes{r}.signalType;
89 end
90end
91% Initialize syncreply - maps each class to its expected reply signal class index (-1 if none)
92sn.syncreply = -ones(sn.nclasses, 1);
93for r=1:sn.nclasses
94 if ~isempty(self.classes{r}.replySignalClass)
95 sn.syncreply(r) = self.classes{r}.replySignalClass.index - 1; % 0-based for JAR
96 end
97end
98% Initialize signal removal configuration fields
99sn.signalRemovalDist = cell(sn.nclasses,1);
100sn.signalRemovalPolicy = zeros(sn.nclasses, 1);
101sn.isCatastrophe = false(sn.nclasses, 1);
102% Populate removal configuration from Signal and ClosedSignal classes
103for r=1:sn.nclasses
104 if isa(self.classes{r}, 'Signal') || isa(self.classes{r}, 'OpenSignal')
105 if self.classes{r}.isCatastrophe()
106 sn.isCatastrophe(r) = true;
107 end
108 sn.signalRemovalDist{r} = self.classes{r}.removalDistribution;
109 sn.signalRemovalPolicy(r) = self.classes{r}.removalPolicy;
110 elseif isa(self.classes{r}, 'ClosedSignal')
111 sn.signalRemovalDist{r} = self.classes{r}.removalDistribution;
112 sn.signalRemovalPolicy(r) = self.classes{r}.removalPolicy;
113 end
114end
115sn.nclosedjobs = sum(njobs(isfinite(njobs)));
116sn.nservers = numservers;
117sn.isstation = (nodetypes == NodeType.Source | nodetypes == NodeType.Delay | nodetypes == NodeType.Queue | nodetypes == NodeType.Join | nodetypes == NodeType.Place);
118sn.nstations = sum(sn.isstation);
119sn.scv = ones(sn.nstations,sn.nclasses);
120sn.njobs = njobs(:)';
121sn.refstat = refstat;
122sn.space = cell(sn.nstations,1);
123sn.routing = routing;
124sn.chains = [];
125sn.lst = {};
126sn.lldscaling = lldscaling;
127sn.cdscaling = cdscaling;
128sn.ljdscaling = ljdscaling;
129sn.ljdcutoffs = ljdcutoffs;
130sn.ljcdscaling = ljcdscaling;
131sn.ljcdcutoffs = ljcdcutoffs;
132sn.nodetype = nodetypes;
133sn.nstations = sum(sn.isstation);
134sn.isstateful = (nodetypes == NodeType.Source | nodetypes == NodeType.Delay | nodetypes == NodeType.Queue | nodetypes == NodeType.Cache | nodetypes == NodeType.Join | nodetypes == NodeType.Router | nodetypes == NodeType.Place | nodetypes == NodeType.Transition);
135sn.isstatedep = false(sn.nnodes,3); % col 1: buffer, col 2: srv, col 3: routing
136sn.isfunction = [];
137for ind = 1:sn.nstations
138 if isa(self.stations{ind},'Queue')
139 sn.isfunction(ind) = ~isempty(self.stations{ind}.setupTime);
140 end
141end
142
143for ind=1:sn.nnodes
144 switch sn.nodetype(ind)
145 case NodeType.Cache
146 sn.isstatedep(ind,2) = true; % state dependent service
147 % case NodeType.Place
148 % self.nodes{ind}.init();
149 % case NodeType.Transition
150 % self.nodes{ind}.init(); % this erases enablingConditions
151 end
152
153 for r=1:sn.nclasses
154 switch sn.routing(ind,r)
155 case {RoutingStrategy.RROBIN, RoutingStrategy.WRROBIN, RoutingStrategy.JSQ, RoutingStrategy.RL, RoutingStrategy.KCHOICES}
156 sn.isstatedep(ind,3) = true; % state dependent routing
157 end
158 end
159end
160
161sn.nstateful = sum(sn.isstateful);
162sn.state = cell(sn.nstations,1);
163for i=1:sn.nstateful
164 sn.state{i} = [];
165end
166sn.nodenames = nodenames;
167sn.classnames = classnames;
168sn.connmatrix = conn;
169
170sn.nodeToStateful =[];
171sn.nodeToStation =[];
172sn.stationToNode =[];
173sn.stationToStateful =[];
174sn.statefulToNode =[];
175sn.statefulToStation =[];
176for ind=1:sn.nnodes
177 sn.nodeToStateful(ind) = nd2sf(sn,ind);
178 sn.nodeToStation(ind) = nd2st(sn,ind);
179end
180for ist=1:sn.nstations
181 sn.stationToNode(ist) = st2nd(sn,ist);
182 sn.stationToStateful(ist) = st2sf(sn,ist);
183end
184for isf=1:sn.nstateful
185 sn.statefulToNode(isf) = sf2nd(sn,isf);
186 sn.statefulToStation(isf) = sf2st(sn,isf);
187end
188
189% Populate immediate feedback matrix (station x class)
190sn.immfeed = false(sn.nstations, sn.nclasses);
191for ist=1:sn.nstations
192 nodeIdx = sn.stationToNode(ist);
193 node = self.nodes{nodeIdx};
194 for r=1:sn.nclasses
195 % Check station-level setting (Queue only)
196 stationHas = false;
197 if isa(node, 'Queue') && ~isempty(node.immediateFeedback)
198 if ischar(node.immediateFeedback) && strcmp(node.immediateFeedback, 'all')
199 stationHas = true;
200 elseif iscell(node.immediateFeedback)
201 stationHas = any(cellfun(@(x) x == r, node.immediateFeedback));
202 end
203 end
204 % Check class-level setting
205 classHas = self.classes{r}.immediateFeedback;
206 sn.immfeed(ist, r) = stationHas || classHas;
207 end
208end
209
210sn.fj = self.getForkJoins();
211self.sn = sn;
212refreshPriorities(self);
213if exist('refreshDeadlines', 'file')
214 refreshDeadlines(self);
215else
216 % Inline implementation if method not loaded
217 K = getNumberOfClasses(self);
218 classdeadline = zeros(1,K);
219 for r=1:K
220 classdeadline(r) = self.getClassByIndex(r).deadline;
221 end
222 if ~isempty(self.sn)
223 self.sn.classdeadline = classdeadline;
224 end
225end
226refreshProcesses(self);
227
228% Export patience/impatience fields for abandonment-aware solvers (MAPMsG, JMT, etc.)
229sn = self.sn;
230sn.patienceProc = cell(sn.nstations, sn.nclasses);
231sn.impatienceClass = zeros(sn.nstations, sn.nclasses); % ImpatienceType (RENEGING, BALKING)
232sn.impatienceType = zeros(sn.nstations, sn.nclasses); % ProcessType (EXP, ERLANG, etc.)
233sn.impatienceMu = zeros(sn.nstations, sn.nclasses); % Rate parameter (1/mean)
234sn.impatiencePhi = zeros(sn.nstations, sn.nclasses); % SCV parameter
235sn.impatiencePhases = zeros(sn.nstations, sn.nclasses);
236sn.impatienceProc = cell(sn.nstations, sn.nclasses);
237sn.impatiencePie = cell(sn.nstations, sn.nclasses);
238for ist = 1:sn.nstations
239 node = self.stations{ist};
240 if isa(node, 'Queue')
241 for r = 1:sn.nclasses
242 patienceDist = node.getPatience(self.classes{r});
243 if ~isempty(patienceDist) && ~isa(patienceDist, 'Disabled')
244 % Convert patience distribution to MAP representation
245 patienceMAP = patienceDist.getProcess();
246 if iscell(patienceMAP) && length(patienceMAP) >= 2
247 sn.patienceProc{ist, r} = patienceMAP;
248 sn.impatienceProc{ist, r} = patienceMAP;
249
250 % Get the ProcessType of the patience distribution
251 if isprop(patienceDist, 'type')
252 sn.impatienceType(ist, r) = patienceDist.type;
253 elseif isa(patienceDist, 'Exp')
254 sn.impatienceType(ist, r) = ProcessType.EXP;
255 elseif isa(patienceDist, 'Erlang')
256 sn.impatienceType(ist, r) = ProcessType.ERLANG;
257 elseif isa(patienceDist, 'HyperExp')
258 sn.impatienceType(ist, r) = ProcessType.HYPEREXP;
259 elseif isa(patienceDist, 'Det')
260 sn.impatienceType(ist, r) = ProcessType.DET;
261 elseif isa(patienceDist, 'Gamma')
262 sn.impatienceType(ist, r) = ProcessType.GAMMA;
263 elseif isa(patienceDist, 'Pareto')
264 sn.impatienceType(ist, r) = ProcessType.PARETO;
265 elseif isa(patienceDist, 'Weibull')
266 sn.impatienceType(ist, r) = ProcessType.WEIBULL;
267 elseif isa(patienceDist, 'Lognormal')
268 sn.impatienceType(ist, r) = ProcessType.LOGNORMAL;
269 elseif isa(patienceDist, 'Uniform')
270 sn.impatienceType(ist, r) = ProcessType.UNIFORM;
271 elseif isa(patienceDist, 'Coxian')
272 sn.impatienceType(ist, r) = ProcessType.COXIAN;
273 elseif isa(patienceDist, 'APH')
274 sn.impatienceType(ist, r) = ProcessType.APH;
275 elseif isa(patienceDist, 'PH')
276 sn.impatienceType(ist, r) = ProcessType.PH;
277 else
278 % Default to PH for other Markovian distributions
279 sn.impatienceType(ist, r) = ProcessType.PH;
280 end
281
282 % Extract distribution parameters for JMT export
283 % Get rate (mu = 1/mean)
284 if ismethod(patienceDist, 'getMean')
285 meanVal = patienceDist.getMean();
286 if meanVal > 0
287 sn.impatienceMu(ist, r) = 1 / meanVal;
288 else
289 sn.impatienceMu(ist, r) = Inf;
290 end
291 else
292 % Estimate from MAP: mean = -pi * D0^(-1) * e
293 D0 = patienceMAP{1};
294 D1 = patienceMAP{2};
295 n = size(D0, 1);
296 pi = ones(1, n) / n; % Approximate stationary distribution
297 meanVal = -pi * (D0 \ ones(n, 1));
298 sn.impatienceMu(ist, r) = 1 / meanVal;
299 end
300
301 % Get SCV (phi)
302 if ismethod(patienceDist, 'getSCV')
303 sn.impatiencePhi(ist, r) = patienceDist.getSCV();
304 else
305 sn.impatiencePhi(ist, r) = 1.0; % Default to exponential SCV
306 end
307
308 % Get number of phases
309 if ismethod(patienceDist, 'getNumberOfPhases')
310 sn.impatiencePhases(ist, r) = patienceDist.getNumberOfPhases();
311 else
312 sn.impatiencePhases(ist, r) = size(patienceMAP{1}, 1);
313 end
314
315 % Get initial probability vector (pie)
316 n = size(patienceMAP{1}, 1);
317 D0 = patienceMAP{1};
318 D1 = patienceMAP{2};
319 % For PH: pie is from the MAP representation D1 = (-D0*e)*pie
320 exitRates = -D0 * ones(n, 1);
321 idx = find(exitRates > 1e-10, 1);
322 if ~isempty(idx)
323 sn.impatiencePie{ist, r} = D1(idx, :) / exitRates(idx);
324 else
325 sn.impatiencePie{ist, r} = ones(1, n) / n;
326 end
327 end
328 % Get impatience class (RENEGING, BALKING)
329 impType = node.getImpatienceType(self.classes{r});
330 if ~isempty(impType)
331 sn.impatienceClass(ist, r) = impType;
332 end
333 end
334 end
335 end
336end
337
338% Initialize varsparam for cache item state tracking (mirrors JAR Network.java:4745-4747)
339sn.varsparam = -ones(sn.nnodes, 1);
340
341% Export balking and retrial fields for sn-driven solvers
342sn.balkingStrategy = zeros(sn.nstations, sn.nclasses);
343sn.balkingThresholds = cell(sn.nstations, sn.nclasses);
344sn.retrialType = zeros(sn.nstations, sn.nclasses);
345sn.retrialMu = zeros(sn.nstations, sn.nclasses);
346sn.retrialPhi = zeros(sn.nstations, sn.nclasses);
347sn.retrialProc = cell(sn.nstations, sn.nclasses);
348sn.retrialMaxAttempts = -ones(sn.nstations, sn.nclasses);
349sn.orbitImpatience = cell(sn.nstations, sn.nclasses);
350for ist = 1:sn.nstations
351 node = self.stations{ist};
352 if isa(node, 'Queue')
353 for r = 1:sn.nclasses
354 % Balking
355 [bStrat, bThresh] = node.getBalking(self.classes{r});
356 if ~isempty(bStrat)
357 if isnumeric(bStrat)
358 sn.balkingStrategy(ist, r) = bStrat;
359 elseif isa(bStrat, 'BalkingStrategy') && isprop(bStrat, 'id')
360 sn.balkingStrategy(ist, r) = bStrat.id;
361 else
362 sn.balkingStrategy(ist, r) = double(bStrat);
363 end
364 sn.balkingThresholds{ist, r} = bThresh;
365 end
366 % Retrial
367 [rDist, rMax] = node.getRetrial(self.classes{r});
368 if ~isempty(rDist) && ~isa(rDist, 'Disabled')
369 if isprop(rDist, 'type')
370 sn.retrialType(ist, r) = rDist.type;
371 elseif isa(rDist, 'Exp')
372 sn.retrialType(ist, r) = ProcessType.EXP;
373 elseif isa(rDist, 'Erlang')
374 sn.retrialType(ist, r) = ProcessType.ERLANG;
375 elseif isa(rDist, 'HyperExp')
376 sn.retrialType(ist, r) = ProcessType.HYPEREXP;
377 elseif isa(rDist, 'Det')
378 sn.retrialType(ist, r) = ProcessType.DET;
379 elseif isa(rDist, 'Gamma')
380 sn.retrialType(ist, r) = ProcessType.GAMMA;
381 elseif isa(rDist, 'Pareto')
382 sn.retrialType(ist, r) = ProcessType.PARETO;
383 elseif isa(rDist, 'Weibull')
384 sn.retrialType(ist, r) = ProcessType.WEIBULL;
385 elseif isa(rDist, 'Lognormal')
386 sn.retrialType(ist, r) = ProcessType.LOGNORMAL;
387 elseif isa(rDist, 'Uniform')
388 sn.retrialType(ist, r) = ProcessType.UNIFORM;
389 elseif isa(rDist, 'Coxian')
390 sn.retrialType(ist, r) = ProcessType.COXIAN;
391 elseif isa(rDist, 'APH')
392 sn.retrialType(ist, r) = ProcessType.APH;
393 elseif isa(rDist, 'PH')
394 sn.retrialType(ist, r) = ProcessType.PH;
395 else
396 sn.retrialType(ist, r) = ProcessType.PH;
397 end
398 if ismethod(rDist, 'getMean')
399 meanVal = rDist.getMean();
400 if meanVal > 0
401 sn.retrialMu(ist, r) = 1 / meanVal;
402 else
403 sn.retrialMu(ist, r) = Inf;
404 end
405 end
406 if ismethod(rDist, 'getSCV')
407 sn.retrialPhi(ist, r) = rDist.getSCV();
408 else
409 sn.retrialPhi(ist, r) = 1.0;
410 end
411 if ismethod(rDist, 'getProcess')
412 sn.retrialProc{ist, r} = rDist.getProcess();
413 end
414 sn.retrialMaxAttempts(ist, r) = rMax;
415 end
416 % Orbit impatience (abandonment from the retrial orbit); store {D0,D1}
417 oDist = node.getOrbitImpatience(self.classes{r});
418 if ~isempty(oDist) && ~isa(oDist, 'Disabled') && ismethod(oDist, 'getProcess')
419 sn.orbitImpatience{ist, r} = oDist.getProcess();
420 end
421 end
422 end
423end
424self.sn = sn;
425
426% Check if priorities are specified but no priority-aware scheduling policy is used
427sn = self.sn;
428if ~all(sn.classprio == sn.classprio(1))
429 % Priority classes exist, check if any station uses priority-aware scheduling
430 prioScheds = [SchedStrategy.PSPRIO, SchedStrategy.DPSPRIO, SchedStrategy.GPSPRIO, ...
431 SchedStrategy.HOL, SchedStrategy.FCFSPRIO, SchedStrategy.LCFSPRIO, ...
432 SchedStrategy.LCFSPRPRIO, SchedStrategy.LCFSPIPRIO, ...
433 SchedStrategy.FCFSPRPRIO, SchedStrategy.FCFSPIPRIO, ...
434 SchedStrategy.LCFS, SchedStrategy.LCFSPR, ...
435 SchedStrategy.FCFSPR];
436 if ~any(ismember(sn.sched, prioScheds))
437 line_warning(mfilename, 'Priority classes are specified but no priority-aware scheduling policy is used in the model. Priorities will be ignored.');
438 else
439 % Display priority info unless silent
440 global LINEVerbose;
441 if isempty(LINEVerbose) || LINEVerbose ~= VerboseLevel.SILENT
442 [minPrio, minIdx] = min(sn.classprio);
443 [maxPrio, maxIdx] = max(sn.classprio);
444 highestPrioClasses = find(sn.classprio == minPrio);
445 lowestPrioClasses = find(sn.classprio == maxPrio);
446 highNames = strjoin(arrayfun(@(i) sn.classnames{i}, highestPrioClasses, 'UniformOutput', false), ',');
447 lowNames = strjoin(arrayfun(@(i) sn.classnames{i}, lowestPrioClasses, 'UniformOutput', false), ',');
448 line_printf('Priority: highest=%s, lowest=%s\n', highNames, lowNames);
449 end
450 end
451end
452
453if any(nodetypes == NodeType.Cache)
454 % this also refreshes the routing matrix and the visits
455 refreshChains(self, false); % wantVisits
456else
457 % this also refreshes the routing matrix and the visits
458 refreshChains(self, true); % wantVisits
459end
460sn = self.sn;
461refclasses = getReferenceClasses(self);
462refclass = zeros(1,sn.nchains);
463for c=1:sn.nchains
464 isect = intersect(sn.inchain{c},find(refclasses));
465 if any(isect)
466 refclass(c) = isect;
467 end
468end
469sn.refclass = refclass;
470self.sn = sn;
471refreshLocalVars(self); % depends on chains (rtnodes)
472refreshPetriNetNodes(self);
473refreshSync(self); % this assumes that refreshChain is called before
474refreshGlobalSync(self);
475
476sn = self.sn;
477self.hasStruct = true;
478
479if any(sn.fj(:)) % if there are forks
480 % try to recompute visits after mixed-model transformation:
481 % we obtain the visits of auxiliary class from the transformed model
482 % then we sum them to the original classes
483 %try
484 [nonfjmodel, fjclassmap, forkmap, fanOut] = ModelAdapter.mmt(self);
485 if any(fanOut==1)
486 % in this case, one of the forks is degenerate with a single
487 % outgoing link so no visit correction is needed
488 line_warning(mfilename,'The specified fork-join topology has partial support, only SolverJMT simulation results may be reliable.\n');
489 % return;
490 end
491 fsn = nonfjmodel.getStruct();
492 for new_chain=(sn.nchains+1):fsn.nchains
493 anyAuxClass = fsn.inchain{new_chain}(1);
494 origFork = forkmap(anyAuxClass);
495 origChain = find(sn.chains(:,fjclassmap(anyAuxClass))); % original chain of the class
496 fsn.nodevisits{new_chain}(fsn.nodetype == NodeType.Source | fsn.nodetype == NodeType.Sink | fsn.nodetype == NodeType.Fork,:) = 0;
497 Vaux = fsn.nodevisits{new_chain}(:,fsn.inchain{new_chain});
498 if fsn.nnodes ~= sn.nnodes
499 % Build mapping from fsn nodes to sn nodes by name
500 % This handles cases where ClassSwitch/Source/Sink differ between models
501 VauxMapped = zeros(sn.nnodes, size(Vaux,2));
502 for fsnRow = 1:fsn.nnodes
503 nodeName = fsn.nodenames{fsnRow};
504 snRow = find(strcmp(sn.nodenames, nodeName), 1);
505 if ~isempty(snRow)
506 % This fsn node exists in sn - copy its visit data
507 VauxMapped(snRow, :) = Vaux(fsnRow, :);
508 end
509 % Nodes not in sn (ClassSwitch, Source, Sink) are skipped
510 end
511 Vaux = VauxMapped;
512 end
513 X = sn.nodevisits{origChain};
514 for jaux=1:length(fsn.inchain{new_chain})
515 % Pair each auxiliary class with its original class explicitly
516 % via fjclassmap: positional pairing against sn.inchain{origChain}
517 % breaks when the mmt confinement splits the auxiliary classes
518 % across several chains (the correction then lands in the wrong
519 % class columns and scrambles the nodevisits supports).
520 a = fsn.inchain{new_chain}(jaux);
521 if a > length(fjclassmap) || fjclassmap(a) <= 0
522 continue; % not an auxiliary class
523 end
524 j = fjclassmap(a); % original class mirrored by aux class a
525 self.sn.nodevisits{origChain}(:,j) = sn.nodeparam{origFork}.fanOut*(X(:,j) + Vaux(:,jaux));
526 end
527 end
528end
529
530sn = refreshRegions(self);
531self.sn = sn;
532
533% Populate heterogeneous server fields from Queue nodes into nodeparam.
534% These are ragged, node-type-conditional parameters and therefore live in
535% the nodeparam container (indexed by node) rather than as flat root fields.
536% Done last so refreshLocalVars (which rebuilds nodeparam) cannot wipe them.
537for ist = 1:self.sn.nstations
538 if isa(self.stations{ist}, 'Queue') && ~isempty(self.stations{ist}.serverTypes)
539 nodeIdx = self.sn.stationToNode(ist);
540 nTypes = length(self.stations{ist}.serverTypes);
541 self.sn.nodeparam{nodeIdx}.nservertypes = nTypes;
542 self.sn.nodeparam{nodeIdx}.servertypenames = cell(1, nTypes);
543 self.sn.nodeparam{nodeIdx}.serverspertype = zeros(1, nTypes);
544 self.sn.nodeparam{nodeIdx}.servercompat = zeros(nTypes, self.sn.nclasses);
545
546 for t = 1:nTypes
547 st = self.stations{ist}.serverTypes{t};
548 self.sn.nodeparam{nodeIdx}.servertypenames{t} = st.getName();
549 self.sn.nodeparam{nodeIdx}.serverspertype(t) = st.numOfServers;
550
551 % Build compatibility matrix
552 for r = 1:self.sn.nclasses
553 if st.isCompatible(self.classes{r})
554 self.sn.nodeparam{nodeIdx}.servercompat(t, r) = 1;
555 end
556 end
557 end
558
559 % Get heterogeneous scheduling policy
560 if ~isempty(self.stations{ist}.heteroSchedPolicy)
561 self.sn.nodeparam{nodeIdx}.heteroschedpolicy = self.stations{ist}.heteroSchedPolicy;
562 end
563 end
564end
565end
566
567function stat_idx = nd2st(sn, node_idx)
568% STAT_IDX = ND2ST(NODE_IDX)
569
570if sn.isstation(node_idx)
571 stat_idx = at(cumsum(sn.isstation),node_idx);
572else
573 stat_idx = NaN;
574end
575end
576
577function node_idx = st2nd(sn,stat_idx)
578% NODE_IDX = ST2ND(SELF,STAT_IDX)
579
580v = cumsum(sn.isstation) == stat_idx;
581if any(v)
582 node_idx = find(v, 1);
583else
584 node_idx = NaN;
585end
586end
587
588function sful_idx = st2sf(sn,stat_idx)
589% SFUL_IDX = ST2SF(SELF,STAT_IDX)
590
591sful_idx = nd2sf(sn,st2nd(sn,stat_idx));
592end
593
594function sful_idx = nd2sf(sn, node_idx)
595% SFUL_IDX = ND2SF(NODE_IDX)
596
597if sn.isstateful(node_idx)
598 sful_idx = at(cumsum(sn.isstateful),node_idx);
599else
600 sful_idx = NaN;
601end
602end
603
604function node_idx = sf2nd(sn,stat_idx)
605% NODE_IDX = SF2ND(SELF,STAT_IDX)
606
607v = cumsum(sn.isstateful) == stat_idx;
608if any(v)
609 node_idx = find(v, 1);
610else
611 node_idx = NaN;
612end
613end
614
615function stat_idx = sf2st(sn,sful_idx)
616% STAT_IDX = SF2ST(SELF,SFUL_IDX)
617
618stat_idx = nd2st(sn,sf2nd(sn,sful_idx));
619end
Definition mmt.m:124