LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
Queue.m
1classdef Queue < ServiceStation
2 % A service station with queueing
3 %
4 % Copyright (c) 2012-2026, Imperial College London
5 % All rights reserved.
6
7 properties
8 setupTime;
9 delayoffTime;
10 switchoverTime;
11 pollingType;
12 pollingPar;
13 patienceTypes;
14 end
15
16 methods
17 %Constructor
18 function self = Queue(model, name, schedStrategy)
19 % SELF = QUEUE(MODEL, NAME, SCHEDSTRATEGY)
20
21 self@ServiceStation(name);
22
23 if model.isMatlabNative()
24 classes = model.getClasses();
25 self.input = Buffer(classes);
26 self.output = Dispatcher(classes);
27 self.schedPolicy = SchedStrategyType.PR;
28 self.schedStrategy = SchedStrategy.PS;
29 self.serviceProcess = {};
30 self.server = Server(classes);
31 self.numberOfServers = 1;
32 self.schedStrategyPar = zeros(1,length(model.getClasses()));
33 self.setModel(model);
34 self.model.addNode(self);
35 self.dropRule = [];
36 self.obj = [];
37 self.setupTime = {};
38 self.delayoffTime = {};
39 self.pollingType = {};
40 self.switchoverTime = {};
41 self.patienceDistributions = {};
42 self.patienceTypes = {};
43
44 if nargin>=3 %exist('schedStrategy','var')
45 self.schedStrategy = schedStrategy;
46 switch SchedStrategy.toId(self.schedStrategy)
47 case {SchedStrategy.PS, SchedStrategy.DPS,SchedStrategy.GPS, SchedStrategy.PSPRIO, SchedStrategy.DPSPRIO,SchedStrategy.GPSPRIO}
48 self.schedPolicy = SchedStrategyType.PR;
49 self.server = SharedServer(classes);
50 case {SchedStrategy.LCFSPR, SchedStrategy.LCFSPRPRIO, SchedStrategy.LCFSPI, SchedStrategy.LCFSPIPRIO, SchedStrategy.EDF}
51 self.schedPolicy = SchedStrategyType.PR;
52 self.server = PreemptiveServer(classes);
53 case {SchedStrategy.FCFS, SchedStrategy.LCFS, SchedStrategy.SIRO, SchedStrategy.SEPT, SchedStrategy.LEPT, SchedStrategy.SJF, SchedStrategy.LJF, SchedStrategy.EDD, SchedStrategy.SRPT, SchedStrategy.SRPTPRIO}
54 self.schedPolicy = SchedStrategyType.NP;
55 self.server = Server(classes);
56 case SchedStrategy.INF
57 self.schedPolicy = SchedStrategyType.NP;
58 self.server = InfiniteServer(classes);
59 self.numberOfServers = Inf;
60 case {SchedStrategy.HOL, SchedStrategy.FCFSPRIO, SchedStrategy.LCFSPRIO}
61 self.schedPolicy = SchedStrategyType.NP;
62 self.server = Server(classes);
63 case SchedStrategy.POLLING
64 self.schedPolicy = SchedStrategyType.NP;
65 self.server = PollingServer(classes);
66 otherwise
67 line_error(mfilename,sprintf('The specified scheduling strategy (%s) is unsupported.',schedStrategy));
68 end
69 end
70 elseif model.isJavaNative()
71 self.setModel(model);
72 switch SchedStrategy.toId(schedStrategy)
73 case SchedStrategy.INF
74 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.INF);
75 case SchedStrategy.FCFS
76 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.FCFS);
77 case SchedStrategy.LCFS
78 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.LCFS);
79 case SchedStrategy.SIRO
80 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.SIRO);
81 case SchedStrategy.SJF
82 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.SJF);
83 case SchedStrategy.LJF
84 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.LJF);
85 case SchedStrategy.PS
86 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.PS);
87 case SchedStrategy.PSPRIO
88 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.PSPRIO);
89 case SchedStrategy.DPS
90 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.DPS);
91 case SchedStrategy.DPSPRIO
92 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.DPSPRIO);
93 case SchedStrategy.GPS
94 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.GPS);
95 case SchedStrategy.GPSPRIO
96 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.GPSPRIO);
97 case SchedStrategy.SEPT
98 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.SEPT);
99 case SchedStrategy.LEPT
100 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.LEPT);
101 case SchedStrategy.SRPT
102 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.SRPT);
103 case SchedStrategy.SRPTPRIO
104 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.SRPTPRIO);
105 case SchedStrategy.HOL
106 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.HOL);
107 case SchedStrategy.FORK
108 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.FORK);
109 case SchedStrategy.EXT
110 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.EXT);
111 case SchedStrategy.REF
112 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.REF);
113 case SchedStrategy.LCFSPR
114 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.LCFSPR);
115 case SchedStrategy.EDD
116 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.EDD);
117 case SchedStrategy.EDF
118 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.EDF);
119 end
120 self.obj.setNumberOfServers(1);
121 self.index = model.obj.getNodeIndex(self.obj);
122 end
123 end
124
125 function setLoadDependence(self, alpha)
126 switch SchedStrategy.toId(self.schedStrategy)
127 case {SchedStrategy.PS, SchedStrategy.FCFS}
128 setLimitedLoadDependence(self, alpha);
129 otherwise
130 line_error(mfilename,'Load-dependence supported only for processor sharing (PS) and first-come first-serve (FCFS) stations.');
131 end
132 end
133
134 function setClassDependence(self, beta)
135 switch SchedStrategy.toId(self.schedStrategy)
136 case {SchedStrategy.PS, SchedStrategy.FCFS}
137 setLimitedClassDependence(self, beta);
138 otherwise
139 line_error(mfilename,'Class-dependence supported only for processor sharing (PS) and first-come first-serve (FCFS) stations.');
140 end
141 end
142
143 function setNumberOfServers(self, value)
144 % SETNUMBEROFSERVERS(VALUE)
145 if isempty(self.obj)
146 switch SchedStrategy.toId(self.schedStrategy)
147 case SchedStrategy.INF
148 %line_warning(mfilename,'A request to change the number of servers in an infinite server node has been ignored.');
149 %ignore
150 otherwise
151 self.setNumServers(value);
152 end
153 else
154 self.obj.setNumberOfServers(value);
155 end
156 end
157
158 function setNumServers(self, value)
159 % SETNUMSERVERS(VALUE)
160 if isempty(self.obj)
161 switch SchedStrategy.toId(self.schedStrategy)
162 case {SchedStrategy.DPS, SchedStrategy.GPS}
163 if value ~= 1
164 line_error(mfilename,sprintf('Cannot use multi-server stations with %s scheduling.', self.schedStrategy));
165 end
166 otherwise
167 self.numberOfServers = value;
168 end
169 else
170 self.obj.setNumberOfServers(value);
171 end
172 end
173
174 function self = setStrategyParam(self, class, weight)
175 % SELF = SETSTRATEGYPARAM(CLASS, WEIGHT)
176
177 self.schedStrategyPar(class.index) = weight;
178 end
179
180 function distribution = getService(self, class)
181 % DISTRIBUTION = GETSERVICE(CLASS)
182
183 % return the service distribution assigned to the given class
184 if nargin<2 %~exist('class','var')
185 for s = 1:length(self.model.getClasses())
186 classes = self.model.getClasses();
187 distribution{s} = self.server.serviceProcess{1, classes{s}}{3};
188 end
189 else
190 try
191 distribution = self.server.serviceProcess{1, class.index}{3};
192 catch ME
193 distribution = [];
194 line_warning(mfilename,'No distribution is available for the specified class.\n');
195 end
196 end
197 end
198
199 function setService(self, class, distribution, weight)
200 % SETSERVICE(CLASS, DISTRIBUTION, WEIGHT)
201 if nargin<4 %~exist('weight','var')
202 weight=1.0;
203 end
204 if distribution.isImmediate()
205 distribution = Immediate.getInstance();
206 end
207 if isa(class,'SelfLoopingClass') && class.refstat.index ~= self.index && ~isa(distribution,'Disabled')
208 line_error(mfilename, 'For a self-looping class, service cannot be set on stations other than the reference station of the class.');
209 end
210 if isempty(self.obj)
211 server = self.server; % by reference
212 c = class.index;
213 if length(server.serviceProcess) >= c && ~isempty(server.serviceProcess{1,c}) % if the distribution was already configured
214 % this is a forced state reset in case for example the number of phases changes
215 % appears to run faster without checks, probably due to
216 % isa being slow
217 %oldDistribution = server.serviceProcess{1, c}{3};
218 %isOldMarkovian = isa(oldDistribution,'Markovian');
219 %isNewMarkovian = isa(distribution,'Markovian');
220 %if distribution.getNumParams ~= oldDistribution.getNumParams
221 % %|| (isOldMarkovian && ~isNewMarkovian) || (~isOldMarkovian && isNewMarkovian) || (isOldMarkovian && isNewMarkovian && distribution.getNumberOfPhases ~= oldDistribution.getNumberOfPhases)
222 self.model.setInitialized(false); % this is a better way to invalidate to avoid that sequential calls to setService all trigger an initDefault
223 self.state=[]; % reset the state vector
224 %end
225 else % if first configuration
226 if length(self.classCap) < c
227 self.classCap((length(self.classCap)+1):c) = Inf;
228 end
229 self.setStrategyParam(class, weight);
230 % Default to Drop for finite capacity, WaitingQueue otherwise
231 if self.cap < intmax && ~isinf(self.cap)
232 self.dropRule(c) = DropStrategy.DROP;
233 else
234 self.dropRule(c) = DropStrategy.WAITQ;
235 end
236 server.serviceProcess{1, c}{2} = ServiceStrategy.LI;
237 end
238 server.serviceProcess{1, c}{3} = distribution;
239 self.serviceProcess{c} = distribution;
240 else
241 self.obj.setService(class.obj, distribution.obj, weight);
242 end
243 end
244
245 function setDelayOff(self, jobclass, setupTime, delayoffTime)
246 c = jobclass.index;
247 self.setupTime{1, c} = setupTime;
248 self.delayoffTime{1, c} = delayoffTime;
249 end
250
251 function dist = getSetupTime(self, jobclass)
252 c = jobclass.index;
253 if c <= length(self.setupTime) && ~isempty(self.setupTime{1, c})
254 dist = self.setupTime{1, c};
255 else
256 dist = [];
257 end
258 end
259
260 function dist = getDelayOffTime(self, jobclass)
261 c = jobclass.index;
262 if c <= length(self.delayoffTime) && ~isempty(self.delayoffTime{1, c})
263 dist = self.delayoffTime{1, c};
264 else
265 dist = [];
266 end
267 end
268
269 function setSwitchover(self, varargin)
270 if isempty(self.switchoverTime)
271 if SchedStrategy.toId(self.schedStrategy) == SchedStrategy.POLLING
272 self.switchoverTime = cell(1,length(self.model.getClasses()));
273 else
274 K = length(self.model.getClasses());
275 self.switchoverTime = cell(K,K);
276 for r=1:K
277 for s=1:K
278 self.switchoverTime{r,s} = Immediate();
279 end
280 end
281 end
282 end
283 if length(varargin)==2
284 jobclass = varargin{1};
285 soTime = varargin{2};
286 % time to switch from queue i to the next one
287 if SchedStrategy.toId(self.schedStrategy) ~= SchedStrategy.POLLING
288 line_error(mfilename,'setSwitchover(jobclass, distrib) can only be invoked on queues with SchedStrategy.POLLING.\n');
289 end
290 c = jobclass.index;
291 self.switchoverTime{1,c} = soTime;
292 elseif length(varargin)==3
293 jobclass_from = varargin{1};
294 jobclass_to = varargin{2};
295 soTime = varargin{3};
296 f = jobclass_from.index;
297 t = jobclass_to.index;
298 self.switchoverTime{f,t} = soTime;
299 end
300 end
301
302 function setPollingType(self, rule, par)
303 if PollingType.toId(rule) ~= PollingType.KLIMITED
304 par = [];
305 elseif PollingType.toId(rule) == PollingType.KLIMITED && nargin<3
306 line_error(mfilename,'K-Limited polling requires to specify the parameter K, e.g., setPollingType(PollingType.KLIMITED, 2).\n');
307 end
308 % support only identical polling type at each class buffer
309 if SchedStrategy.toId(self.schedStrategy) ~= SchedStrategy.POLLING
310 line_error(mfilename,'setPollingType can only be invoked on queues with SchedStrategy.POLLING.\n');
311 end
312 for r=1:length(self.model.getClasses())
313 self.pollingType{1,r} = rule;
314 self.pollingPar = par;
315 classes = self.model.getClasses();
316 setSwitchover(self, classes{r}, Immediate());
317 end
318 end
319
320 function setPatience(self, class, varargin)
321 % SETPATIENCE(CLASS, DISTRIBUTION) - Backwards compatible
322 % SETPATIENCE(CLASS, PATIENCETYPE, DISTRIBUTION) - Explicit type
323 %
324 % Sets the patience type and distribution for a specific job class at this queue.
325 % Jobs that wait longer than their patience time will abandon the queue.
326 %
327 % Parameters:
328 % class - JobClass object
329 % patienceType - (Optional) PatienceType constant (RENEGING or BALKING)
330 % If omitted, defaults to PatienceType.RENEGING
331 % distribution - Any LINE distribution (Exp, Erlang, HyperExp, etc.)
332 % excluding modulated processes (BMAP, MAP, MMPP2)
333 %
334 % Note: This setting takes precedence over the global class patience.
335 %
336 % Examples:
337 % queue.setPatience(jobclass, Exp(0.2)) % Defaults to RENEGING
338 % queue.setPatience(jobclass, PatienceType.RENEGING, Exp(0.2))
339 % queue.setPatience(jobclass, PatienceType.BALKING, Exp(0.5))
340
341 % Handle backwards compatibility: 2 or 3 arguments
342 if length(varargin) == 1
343 % Old signature: setPatience(class, distribution)
344 distribution = varargin{1};
345 patienceType = PatienceType.RENEGING; % Default to RENEGING
346 elseif length(varargin) == 2
347 % New signature: setPatience(class, patienceType, distribution)
348 patienceType = varargin{1};
349 distribution = varargin{2};
350 else
351 line_error(mfilename, 'Invalid number of arguments. Use setPatience(class, distribution) or setPatience(class, patienceType, distribution)');
352 end
353
354 if isa(distribution, 'BMAP') || isa(distribution, 'MAP') || isa(distribution, 'MMPP2')
355 line_error(mfilename, 'Modulated processes (BMAP, MAP, MMPP2) are not supported for patience distributions.');
356 end
357
358 % Validate patience type
359 if patienceType ~= PatienceType.RENEGING && patienceType ~= PatienceType.BALKING
360 line_error(mfilename, 'Invalid patience type. Use PatienceType.RENEGING or PatienceType.BALKING.');
361 end
362
363 % Only RENEGING is currently supported
364 if patienceType == PatienceType.BALKING
365 line_error(mfilename, 'BALKING patience type is not yet supported. Use PatienceType.RENEGING.');
366 end
367
368 if distribution.isImmediate()
369 distribution = Immediate.getInstance();
370 end
371
372 if isempty(self.obj)
373 c = class.index;
374 self.patienceDistributions{1, c} = distribution;
375 self.patienceTypes{1, c} = patienceType;
376 else
377 self.obj.setPatience(class.obj, patienceType, distribution.obj);
378 end
379 end
380
381 function distribution = getPatience(self, class)
382 % DISTRIBUTION = GETPATIENCE(CLASS)
383 %
384 % Returns the patience distribution for a specific job class.
385 % Returns the queue-specific setting if available, otherwise
386 % falls back to the global class patience.
387 %
388 % Parameters:
389 % class - JobClass object
390 %
391 % Returns:
392 % distribution - The patience distribution, or [] if not set
393
394 if isempty(self.obj)
395 c = class.index;
396 % Check queue-specific patience first
397 if c <= length(self.patienceDistributions) && ~isempty(self.patienceDistributions{1, c})
398 distribution = self.patienceDistributions{1, c};
399 else
400 % Fall back to global class patience
401 distribution = class.getPatience();
402 end
403 else
404 distObj = self.obj.getPatience(class.obj);
405 if isempty(distObj)
406 distribution = [];
407 else
408 distribution = Distribution.fromJavaObject(distObj);
409 end
410 end
411 end
412
413 function patienceType = getPatienceType(self, class)
414 % PATIENCETYPE = GETPATIENCETYPE(CLASS)
415 %
416 % Returns the patience type for a specific job class.
417 % Returns the queue-specific setting if available, otherwise
418 % falls back to the global class patience type.
419 %
420 % Parameters:
421 % class - JobClass object
422 %
423 % Returns:
424 % patienceType - The patience type (PatienceType constant), or [] if not set
425
426 if isempty(self.obj)
427 c = class.index;
428 % Check queue-specific patience type first
429 if c <= length(self.patienceTypes) && ~isempty(self.patienceTypes{1, c})
430 patienceType = self.patienceTypes{1, c};
431 else
432 % Fall back to global class patience type
433 patienceType = class.getPatienceType();
434 end
435 else
436 patienceTypeId = self.obj.getPatienceType(class.obj);
437 if isempty(patienceTypeId)
438 patienceType = [];
439 else
440 patienceType = PatienceType.fromId(patienceTypeId.getID());
441 end
442 end
443 end
444
445 function tf = hasPatience(self, class)
446 % TF = HASPATIENCE(CLASS)
447 %
448 % Returns true if this class has patience configured at this queue
449 % (either locally or globally).
450
451 dist = self.getPatience(class);
452 tf = ~isempty(dist) && ~isa(dist, 'Disabled');
453 end
454
455 % function distrib = getServiceProcess(self, oclass)
456 % distrib = self.serviceProcess{oclass};
457 % end
458
459 end
460end