LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
Station.m
1classdef Station < StatefulNode
2 % An abstract class for nodes where jobs station
3 %
4 % Copyright (c) 2012-2026, Imperial College London
5 % All rights reserved.
6
7 properties
8 numberOfServers;
9 cap;
10 dropRule;
11 classCap;
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]
18 stationIndex;
19 patienceDistributions; % per-class patience distributions (cell array indexed by class)
20 end
21
22 methods(Hidden)
23 %Constructor
24 function self = Station(name)
25 % SELF = STATION(NAME)
26
27 self@StatefulNode(name);
28 self.cap = Inf;
29 self.classCap = [];
30 self.lldScaling = [];
31 self.ljdScaling = [];
32 self.ljdCutoffs = [];
33 self.ljcdScaling = {};
34 self.ljcdCutoffs = [];
35 self.patienceDistributions = [];
36 end
37
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
43 % jobs in the system
44 self.lldScaling = alpha;
45 end
46
47 % don't expose to avoid accidental call without checking the queue
48 % scheduling discipline
49 function setLimitedClassDependence(self, gamma)
50 % SETLIMITEDCLASSDEPENDENCE(self, gamma)
51 %
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;
57 else
58 line_error(mfilename, 'Class dependence must be specified through a function handle.');
59 end
60 end
61
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)
66 %
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(:)';
72 end
73
74 function [scalingTable, cutoffs] = getLimitedJointDependence(self)
75 % [SCALINGTABLE, CUTOFFS] = GETLIMITEDJOINTDEPENDENCE(self)
76 %
77 % Returns the joint-dependence scaling table and cutoffs
78 scalingTable = self.ljdScaling;
79 cutoffs = self.ljdCutoffs;
80 end
81
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)
86 %
87 % scalingTables is a cell array {1,K} where each cell contains
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
91 self.ljcdScaling = scalingTables;
92 self.ljcdCutoffs = cutoffs(:)';
93 end
94
95 function [scalingTables, cutoffs] = getLimitedJointClassDependence(self)
96 % [SCALINGTABLES, CUTOFFS] = GETLIMITEDJOINTCLASSDEPENDENCE(self)
97 %
98 % Returns the joint-class-dependence scaling tables and cutoffs
99 scalingTables = self.ljcdScaling;
100 cutoffs = self.ljcdCutoffs;
101 end
102
103 end
104
105 methods
106
107 function self = setDropRule(self, class, drop)
108 % SELF = SETDROPRULE(CLASS, DROPRULE)
109
110 self.dropRule(class) = drop;
111 end
112
113
114 function setNumServers(self, value)
115 % SETNUMSERVERS(VALUE)
116
117 self.numberOfServers = value;
118 end
119
120 function setNumberOfServers(self, value)
121 % SETNUMBEROFSERVERS(VALUE)
122
123 self.numberOfServers = value;
124 end
125
126 function value = getNumServers(self)
127 % VALUE = GETNUMSERVERS()
128
129 value = self.numberOfServers;
130 end
131
132 function value = getNumberOfServers(self)
133 % VALUE = GETNUMBEROFSERVERS()
134
135 value = self.numberOfServers;
136 end
137
138 function setCapacity(self, value)
139 % SETCAPACITY(VALUE)
140
141 self.cap = value;
142 end
143
144 function setCap(self, value)
145 % SETCAP(VALUE)
146 % Alias for setCapacity() for backwards compatibility
147
148 self.setCapacity(value);
149 end
150
151 function setChainCapacity(self, values)
152 % SETCHAINCAPACITY(VALUES)
153
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.');
157 end
158 for c = 1:sn.nchains
159 inchain = sn.inchain{c};
160 for r = inchain
161 if ~self.isServiceDisabled(r)
162 self.classCap(r) = values(c);
163 else
164 self.classCap(r) = Inf;
165 end
166 end
167 end
168 self.cap = min(sum(self.classCap(self.classCap>0)), self.cap);
169 end
170
171
172 function isD = isServiceDefined(self, class)
173 K = size(self.model.getClasses(),2);
174 isD = true(1, K);
175 switch self.server.className
176 case 'ServiceTunnel'
177 %noop
178 otherwise
179 for r=1:K
180 if isempty(self.server.serviceProcess{1,r})
181 isD(r) = false;
182 end
183 end
184 end
185 end
186
187 function isD = isServiceDisabled(self, class)
188 % ISD = ISSERVICEDISABLED(CLASS)
189 if nargin>=2
190 switch self.server.className
191 case 'ServiceTunnel'
192 isD = false;
193 otherwise
194 isD = self.server.serviceProcess{1,class}{end}.isDisabled();
195 end
196 else
197 K = size(self.model.getClasses(),2);
198 isD = false(1, K);
199 switch self.server.className
200 case 'ServiceTunnel'
201 %noop
202 otherwise
203 %isD = cellfun(@(sp) sp{end}.isDisabled, self.server.serviceProcess);
204 for r=1:K
205 isD(r) = self.server.serviceProcess{1,r}{end}.isDisabled();
206 end
207 end
208 end
209 end
210
211 function isI = isServiceImmediate(self, class)
212 % ISI = ISSERVICEIMMEDIATE(CLASS)
213
214 isI = self.server.serviceProcess{1,class}{end}.isImmediate();
215 end
216
217 function R = getNumberOfServiceClasses(self)
218 % R = GETNUMBEROFSERVICECLASSES()
219
220 R = size(self.server.serviceProcess,2);
221 end
222
223 function [p] = getSelfLoopProbabilities(self)
224 % [P] = GETSELFLOOPPROBABILITIES()
225
226 R = getNumberOfServiceClasses(self);
227 p = zeros(1,R);
228 for k=1:R
229 nOutLinks = length(self.output.outputStrategy{k}{end});
230 switch RoutingStrategy.toText(self.output.outputStrategy{k}{2})
231 case 'Random'
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};
237 break
238 end
239 end
240 end
241 end
242 end
243
244 function [map, mu, phi] = getSourceRates(self)
245 % [PH,MU,PHI] = GETSOURCERATES()
246
247 nclasses = size(self.input.sourceClasses,2);
248 map = cell(1,nclasses);
249 mu = cell(1,nclasses);
250 phi = cell(1,nclasses);
251 for r=1:nclasses
252 if isempty(self.input.sourceClasses{r})
253 self.input.sourceClasses{r} = {[],ServiceStrategy.LI,Disabled.getInstance()};
254 map{r} = {[NaN],[NaN]};
255 mu{r} = NaN;
256 phi{r} = 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();
262 mu{r} = aph.getMu;
263 phi{r} = aph.getPhi;
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];
271 phi{r} = [1];
272 case 'MMPP2'
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;
276 case 'MAP'
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;
280 otherwise
281 % leave everything empty
282 end
283 else
284 map{r} = {[NaN],[NaN]};
285 mu{r} = NaN;
286 phi{r} = NaN;
287 end
288 end
289 end
290
291 function [map,mu,phi] = getServiceRates(self)
292 % [PH,MU,PHI] = GETSERVICERATES()
293
294 nclasses = size(self.server.serviceProcess,2);
295 map = cell(1,nclasses);
296 mu = cell(1,nclasses);
297 phi = cell(1,nclasses);
298 for r=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]};
303 mu{r} = NaN;
304 phi{r} = NaN;
305 elseif serviceProcess_r{end}.isImmediate()
306 map{r} = {[-GlobalConstants.Immediate],[GlobalConstants.Immediate]};
307 mu{r} = [GlobalConstants.Immediate];
308 phi{r} = [1];
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];
314 phi{r} = [1];
315 case {'Replayer', 'Trace'}
316 aph = serviceProcess_r{end}.fitAPH;
317 map{r} = aph.getProcess();
318 mu{r} = aph.getMu;
319 phi{r} = aph.getPhi;
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;
324 case 'MMPP2'
325 map{r} = serviceProcess_r{end}.getProcess();
326 mu{r} = serviceProcess_r{end}.getMu;
327 phi{r} = serviceProcess_r{end}.getPhi;
328 end
329 else
330 map{r} = {[NaN],[NaN]};
331 mu{r} = NaN;
332 phi{r} = NaN;
333 end
334 end
335 end
336
337 function summary(self)
338 % SUMMARY()
339
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});
346 end
347 % self.input.summary;
348 % self.server.summary;
349 % self.output.summary;
350 end
351
352 end
353end
Definition mmt.m:92