1classdef Station < StatefulNode
2 % An abstract
class for
nodes where jobs station
4 % Copyright (c) 2012-2026, Imperial College London
12 lldScaling; % limited load-dependence scaling factors
13 lcdScaling; % limited
class-dependence scaling factors
14 ljdScaling; % limited joint-dependence scaling factors (linearized)
15 ljdCutoffs; % per-
class cutoffs for joint dependence [N1, N2, ..., NR]
16 ljcdScaling; % limited joint-
class-dependence scaling factors (cell array, per
class)
17 ljcdCutoffs; % per-
class cutoffs for joint class dependence [N1, N2, ..., NR]
19 patienceDistributions; % per-
class patience distributions (cell array indexed by class)
24 function self = Station(name)
25 % SELF = STATION(NAME)
27 self@StatefulNode(name);
33 self.ljcdScaling = {};
34 self.ljcdCutoffs = [];
35 self.patienceDistributions = [];
38 % don
't expose to avoid accidental call without checking the queue
39 % scheduling discipline
40 function setLimitedLoadDependence(self, alpha)
41 % SETLIMITEDLOADDEPENDENCE(self, alpha)
42 % alpha(ni) is the service rate scaling when there are ni>=1
44 self.lldScaling = alpha;
47 % don't expose to avoid accidental call without checking the queue
48 % scheduling discipline
49 function setLimitedClassDependence(self, gamma)
50 % SETLIMITEDCLASSDEPENDENCE(self, gamma)
52 % gamma(ni)
is a function handle, where ni=[ni1,...,niR]
53 %
is the service rate scaling when there are nir jobs at
54 % station i in
class r
55 if isa(gamma,'function_handle')
56 self.lcdScaling = gamma;
58 line_error(mfilename,
'Class dependence must be specified through a function handle.');
62 % don
't expose to avoid accidental call without checking the queue
63 % scheduling discipline
64 function setLimitedJointDependence(self, scalingTable, cutoffs)
65 % SETLIMITEDJOINTDEPENDENCE(self, scalingTable, cutoffs)
67 % scalingTable is a linearized vector of scaling factors
68 % indexed by: idx = 1 + n1 + n2*(N1+1) + n3*(N1+1)*(N2+1) + ...
69 % cutoffs is a vector [N1, N2, ..., NR] of per-class cutoffs
70 self.ljdScaling = scalingTable(:)';
71 self.ljdCutoffs = cutoffs(:)
';
74 function [scalingTable, cutoffs] = getLimitedJointDependence(self)
75 % [SCALINGTABLE, CUTOFFS] = GETLIMITEDJOINTDEPENDENCE(self)
77 % Returns the joint-dependence scaling table and cutoffs
78 scalingTable = self.ljdScaling;
79 cutoffs = self.ljdCutoffs;
82 % don't expose to avoid accidental call without checking the queue
83 % scheduling discipline
84 function setLimitedJointClassDependence(self,
scalingTables, cutoffs)
85 % SETLIMITEDJOINTCLASSDEPENDENCE(self,
scalingTables, cutoffs)
88 % a linearized vector of scaling factors for that class
89 % indexed by: idx = 1 + n1 + n2*(N1+1) + n3*(N1+1)*(N2+1) + ...
90 % cutoffs
is a vector [N1, N2, ..., NR] of per-class cutoffs
92 self.ljcdCutoffs = cutoffs(:)
';
95 function [scalingTables, cutoffs] = getLimitedJointClassDependence(self)
96 % [SCALINGTABLES, CUTOFFS] = GETLIMITEDJOINTCLASSDEPENDENCE(self)
98 % Returns the joint-class-dependence scaling tables and cutoffs
99 scalingTables = self.ljcdScaling;
100 cutoffs = self.ljcdCutoffs;
107 function self = setDropRule(self, class, drop)
108 % SELF = SETDROPRULE(CLASS, DROPRULE)
110 self.dropRule(class) = drop;
114 function setNumServers(self, value)
115 % SETNUMSERVERS(VALUE)
117 self.numberOfServers = value;
120 function setNumberOfServers(self, value)
121 % SETNUMBEROFSERVERS(VALUE)
123 self.numberOfServers = value;
126 function value = getNumServers(self)
127 % VALUE = GETNUMSERVERS()
129 value = self.numberOfServers;
132 function value = getNumberOfServers(self)
133 % VALUE = GETNUMBEROFSERVERS()
135 value = self.numberOfServers;
138 function setCapacity(self, value)
144 function setCap(self, value)
146 % Alias for setCapacity() for backwards compatibility
148 self.setCapacity(value);
151 function setChainCapacity(self, values)
152 % SETCHAINCAPACITY(VALUES)
154 sn = self.model.getStruct;
155 if numel(values) ~= sn.nchains
156 line_error(mfilename,'The method requires in input a capacity value for each chain.
');
159 inchain = sn.inchain{c};
161 if ~self.isServiceDisabled(r)
162 self.classCap(r) = values(c);
164 self.classCap(r) = Inf;
168 self.cap = min(sum(self.classCap(self.classCap>0)), self.cap);
172 function isD = isServiceDefined(self, class)
173 K = size(self.model.getClasses(),2);
175 switch self.server.className
180 if isempty(self.server.serviceProcess{1,r})
187 function isD = isServiceDisabled(self, class)
188 % ISD = ISSERVICEDISABLED(CLASS)
190 switch self.server.className
194 isD = self.server.serviceProcess{1,class}{end}.isDisabled();
197 K = size(self.model.getClasses(),2);
199 switch self.server.className
203 %isD = cellfun(@(sp) sp{end}.isDisabled, self.server.serviceProcess);
205 isD(r) = self.server.serviceProcess{1,r}{end}.isDisabled();
211 function isI = isServiceImmediate(self, class)
212 % ISI = ISSERVICEIMMEDIATE(CLASS)
214 isI = self.server.serviceProcess{1,class}{end}.isImmediate();
217 function R = getNumberOfServiceClasses(self)
218 % R = GETNUMBEROFSERVICECLASSES()
220 R = size(self.server.serviceProcess,2);
223 function [p] = getSelfLoopProbabilities(self)
224 % [P] = GETSELFLOOPPROBABILITIES()
226 R = getNumberOfServiceClasses(self);
229 nOutLinks = length(self.output.outputStrategy{k}{end});
230 switch RoutingStrategy.toText(self.output.outputStrategy{k}{2})
232 p(k) = 1 / nOutLinks;
233 case RoutingStrategy.PROB
234 for t=1:nOutLinks % for all outgoing links
235 if strcmp(self.output.outputStrategy{k}{end}{t}{1}.name, self.name)
236 p(k) = self.output.outputStrategy{k}{end}{t}{2};
244 function [map, mu, phi] = getSourceRates(self)
245 % [PH,MU,PHI] = GETSOURCERATES()
247 nclasses = size(self.input.sourceClasses,2);
248 map = cell(1,nclasses);
249 mu = cell(1,nclasses);
250 phi = cell(1,nclasses);
252 if isempty(self.input.sourceClasses{r})
253 self.input.sourceClasses{r} = {[],ServiceStrategy.LI,Disabled.getInstance()};
254 map{r} = {[NaN],[NaN]};
257 elseif ~self.input.sourceClasses{r}{end}.isDisabled()
258 switch class(self.input.sourceClasses{r}{end})
259 case {'Replayer
', 'Trace
'}
260 aph = self.input.sourceClasses{r}{end}.fitAPH;
261 map{r} = aph.getProcess();
264 case {'Exp
','Coxian
','Erlang
','HyperExp
','Markovian
','APH
','PH
'}
265 map{r} = self.input.sourceClasses{r}{end}.getProcess;
266 mu{r} = self.input.sourceClasses{r}{end}.getMu;
267 phi{r} = self.input.sourceClasses{r}{end}.getPhi;
268 case {'Det
','Uniform
','Pareto
','Gamma
','Lognormal
','Weibull
'}
269 map{r} = self.input.sourceClasses{r}{end}.getProcess();
270 mu{r} = [1/self.input.sourceClasses{r}{end}.getMean];
273 map{r} = self.input.sourceClasses{r}{end}.getProcess();
274 mu{r} = self.input.sourceClasses{r}{end}.getMu;
275 phi{r} = self.input.sourceClasses{r}{end}.getPhi;
277 map{r} = self.input.sourceClasses{r}{end}.getProcess();
278 mu{r} = self.input.sourceClasses{r}{end}.getMu;
279 phi{r} = self.input.sourceClasses{r}{end}.getPhi;
281 % leave everything empty
284 map{r} = {[NaN],[NaN]};
291 function [map,mu,phi] = getServiceRates(self)
292 % [PH,MU,PHI] = GETSERVICERATES()
294 nclasses = size(self.server.serviceProcess,2);
295 map = cell(1,nclasses);
296 mu = cell(1,nclasses);
297 phi = cell(1,nclasses);
299 serviceProcess_r = self.server.serviceProcess{r};
300 if isempty(serviceProcess_r)
301 serviceProcess_r = {[],ServiceStrategy.LI,Disabled.getInstance()};
302 map{r} = {[NaN],[NaN]};
305 elseif serviceProcess_r{end}.isImmediate()
306 map{r} = {[-GlobalConstants.Immediate],[GlobalConstants.Immediate]};
307 mu{r} = [GlobalConstants.Immediate];
309 elseif ~serviceProcess_r{end}.isDisabled()
310 switch class(serviceProcess_r{end})
311 case {'Det
','Uniform
','Pareto
','Gamma
','Weibull
','Lognormal
'}
312 map{r} = serviceProcess_r{end}.getProcess();
313 mu{r} = [serviceProcess_r{end}.getRate];
315 case {'Replayer
', 'Trace
'}
316 aph = serviceProcess_r{end}.fitAPH;
317 map{r} = aph.getProcess();
320 case {'Exp
','Coxian
','Erlang
','HyperExp
','Markovian
','APH
','MAP
','PH
'}
321 map{r} = serviceProcess_r{end}.getProcess();
322 mu{r} = serviceProcess_r{end}.getMu;
323 phi{r} = serviceProcess_r{end}.getPhi;
325 map{r} = serviceProcess_r{end}.getProcess();
326 mu{r} = serviceProcess_r{end}.getMu;
327 phi{r} = serviceProcess_r{end}.getPhi;
330 map{r} = {[NaN],[NaN]};
337 function summary(self)
340 line_printf('\nNode: <strong>%s</strong>
',self.getName);
341 line_printf('\nScheduling: %s
',self.schedStrategy);
342 line_printf('\nNumber of Servers: %d
',self.numberOfServers);
343 for r=1:length(self.output.outputStrategy)
344 classes = self.model.getClasses();
345 line_printf('\nRouting %s: %s
',classes{r}.name,self.output.outputStrategy{r}{2});
347 % self.input.summary;
348 % self.server.summary;
349 % self.output.summary;