1classdef Queue < ServiceStation
2 % A service station with queueing
4 % Copyright (c) 2012-2026, Imperial College London
15 balkingStrategies; % Cell
array: per-
class BalkingStrategy constant
16 balkingThresholds; % Cell
array: per-
class balking thresholds (list of {minJobs, maxJobs, probability})
18 retrialDelays; % Cell
array: per-
class retrial delay distributions
19 retrialMaxAttempts; % Array: per-
class max retrial attempts (-1 = unlimited)
20 orbitImpatienceDistributions; % Cell
array: per-
class orbit abandonment distributions
21 batchRejectProb; % Array: per-
class batch rejection probability [0,1]
22 % Heterogeneous server properties
23 serverTypes; % Cell
array of ServerType objects
24 heteroSchedPolicy; % HeteroSchedPolicy
for server assignment
25 heteroServiceDistributions; % containers.Map: ServerType -> (containers.Map: JobClass -> Distribution)
26 % Immediate feedback property
27 immediateFeedback; % Cell
array of
class indices, or 'all' for all
classes
28 % Pass-and-swap properties
29 swapGraph; % (nclasses x nclasses)
class-compatibility/swap graph
for PAS scheduling
30 svcRateFun; % function handle mu(c): total service rate as a function of the ordered state vector c (PAS scheduling)
35 function self = Queue(model, name, schedStrategy)
36 % SELF = QUEUE(MODEL, NAME, SCHEDSTRATEGY)
38 self@ServiceStation(name);
40 if model.isMatlabNative()
43 self.output = Dispatcher(
classes);
44 self.schedPolicy = SchedStrategyType.PR;
45 self.schedStrategy = SchedStrategy.PS;
46 self.serviceProcess = {};
48 self.numberOfServers = 1;
49 self.schedStrategyPar = zeros(1,length(model.getClasses()));
51 self.model.addNode(self);
55 self.delayoffTime = {};
56 self.pollingType = {};
57 self.switchoverTime = {};
58 self.patienceDistributions = {};
59 self.impatienceTypes = {};
60 self.balkingStrategies = {};
61 self.balkingThresholds = {};
62 self.retrialDelays = {};
63 self.retrialMaxAttempts = [];
64 self.orbitImpatienceDistributions = {};
65 self.batchRejectProb = [];
66 self.serverTypes = {};
67 self.heteroSchedPolicy = HeteroSchedPolicy.ORDER;
68 self.heteroServiceDistributions = containers.Map();
69 self.immediateFeedback = {};
73 if nargin>=3 %exist(
'schedStrategy',
'var')
74 self.schedStrategy = schedStrategy;
75 switch SchedStrategy.toId(self.schedStrategy)
76 case {SchedStrategy.PS, SchedStrategy.DPS,SchedStrategy.GPS, SchedStrategy.PSPRIO, SchedStrategy.DPSPRIO,SchedStrategy.GPSPRIO, SchedStrategy.LPS}
77 self.schedPolicy = SchedStrategyType.PR;
78 self.server = SharedServer(
classes);
79 case {SchedStrategy.LCFSPR, SchedStrategy.LCFSPRPRIO, SchedStrategy.FCFSPR, SchedStrategy.FCFSPRPRIO, SchedStrategy.LCFSPI, SchedStrategy.LCFSPIPRIO, SchedStrategy.FCFSPI, SchedStrategy.FCFSPIPRIO, SchedStrategy.EDF}
80 self.schedPolicy = SchedStrategyType.PR;
81 self.server = PreemptiveServer(
classes);
82 case {SchedStrategy.FCFS, SchedStrategy.LCFS, SchedStrategy.SIRO, SchedStrategy.SEPT, SchedStrategy.LEPT, SchedStrategy.SJF, SchedStrategy.LJF, SchedStrategy.EDD, SchedStrategy.SRPT, SchedStrategy.SRPTPRIO, SchedStrategy.PSJF, SchedStrategy.FB, SchedStrategy.LRPT}
83 self.schedPolicy = SchedStrategyType.NP;
85 case SchedStrategy.PAS
86 % Pass-and-swap: non-preemptive order-independent queue whose
class compatibility/swap graph defaults to complete (materialized at struct refresh) unless set via setSwapGraph.
87 self.schedPolicy = SchedStrategyType.NP;
89 case SchedStrategy.INF
90 self.schedPolicy = SchedStrategyType.NP;
91 self.server = InfiniteServer(
classes);
92 self.numberOfServers = Inf;
93 case {SchedStrategy.HOL, SchedStrategy.FCFSPRIO, SchedStrategy.LCFSPRIO}
94 self.schedPolicy = SchedStrategyType.NP;
96 case SchedStrategy.POLLING
97 self.schedPolicy = SchedStrategyType.NP;
98 self.server = PollingServer(
classes);
100 line_error(mfilename,sprintf(
'The specified scheduling strategy (%s) is unsupported.',schedStrategy));
103 elseif model.isJavaNative()
104 self.setModel(model);
105 switch SchedStrategy.toId(schedStrategy)
106 case SchedStrategy.INF
107 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.INF);
108 case SchedStrategy.FCFS
109 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.FCFS);
110 case SchedStrategy.LCFS
111 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.LCFS);
112 case SchedStrategy.SIRO
113 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.SIRO);
114 case SchedStrategy.SJF
115 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.SJF);
116 case SchedStrategy.LJF
117 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.LJF);
118 case SchedStrategy.PS
119 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.PS);
120 case SchedStrategy.PSPRIO
121 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.PSPRIO);
122 case SchedStrategy.DPS
123 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.DPS);
124 case SchedStrategy.DPSPRIO
125 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.DPSPRIO);
126 case SchedStrategy.GPS
127 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.GPS);
128 case SchedStrategy.GPSPRIO
129 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.GPSPRIO);
130 case SchedStrategy.SEPT
131 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.SEPT);
132 case SchedStrategy.LEPT
133 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.LEPT);
134 case SchedStrategy.SRPT
135 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.SRPT);
136 case SchedStrategy.SRPTPRIO
137 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.SRPTPRIO);
138 case SchedStrategy.PSJF
139 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.PSJF);
140 case SchedStrategy.FB
141 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.FB);
142 case SchedStrategy.LRPT
143 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.LRPT);
144 case SchedStrategy.HOL
145 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.HOL);
146 case SchedStrategy.FORK
147 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.FORK);
148 case SchedStrategy.EXT
149 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.EXT);
150 case SchedStrategy.REF
151 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.REF);
152 case SchedStrategy.LCFSPR
153 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.LCFSPR);
154 case SchedStrategy.FCFSPR
155 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.FCFSPR);
156 case SchedStrategy.FCFSPRPRIO
157 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.FCFSPRPRIO);
158 case SchedStrategy.EDD
159 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.EDD);
160 case SchedStrategy.EDF
161 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.EDF);
162 case SchedStrategy.LPS
163 self.obj = jline.lang.nodes.Queue(model.obj, name, jline.lang.constant.SchedStrategy.LPS);
165 self.obj.setNumberOfServers(1);
166 self.index = model.obj.getNodeIndex(self.obj);
170 function setSwapGraph(self, graph)
171 % SETSWAPGRAPH(GRAPH)
173 % Sets the
class compatibility/swap graph for a pass-and-swap (PAS)
174 % queue. GRAPH
is an (nclasses x nclasses) matrix whose (r,s) entry
175 %
is nonzero iff, upon completion of a
class-r job, a waiting
class-s
176 % job may swap into the freed position (order-independent service).
179 % graph - (nclasses x nclasses) numeric/logical adjacency matrix
181 if SchedStrategy.toId(self.schedStrategy) ~= SchedStrategy.PAS
182 line_error(mfilename,
'setSwapGraph is only applicable to PAS (pass-and-swap) queues.');
184 K = length(self.model.getClasses());
185 if size(graph,1) ~= K || size(graph,2) ~= K
186 line_error(mfilename, sprintf(
'Swap graph must be a %dx%d matrix (nclasses x nclasses).', K, K));
188 self.swapGraph = double(graph);
191 function graph = getSwapGraph(self)
192 % GRAPH = GETSWAPGRAPH()
194 % Returns the (nclasses x nclasses)
class compatibility/swap graph
195 %
for a pass-and-swap (PAS) queue, or []
if not configured.
197 graph = self.swapGraph;
200 function setServiceRateFunction(self, muFun)
201 % SETSERVICERATEFUNCTION(MUFUNCTION)
203 % Sets the total service rate function mu(c) of a pass-and-swap (PAS)
204 % queue. MUFUNCTION
is a function handle taking the ordered state
205 % vector c (a row vector of
class indices, c(1)=oldest job) and
206 % returning the scalar total service rate mu(c). The rate allocated
207 % to the job in position i
is the increment
208 % Delta_mu(c(1..i)) = mu(c(1..i)) - mu(c(1..i-1)).
210 % An order-independent/PAS queue
is parameterized by mu(c) as a whole
211 % and does not support per-
class service distributions.
213 if SchedStrategy.toId(self.schedStrategy) ~= SchedStrategy.PAS
214 line_error(mfilename,
'setServiceRateFunction is only applicable to PAS (pass-and-swap) queues.');
216 if ~isa(muFun,
'function_handle')
217 line_error(mfilename, 'PAS queues require a service rate function handle mu(c); per-class service distributions are not supported. Use setService(@(c) ...).');
219 if ~isempty(self.obj)
220 line_error(mfilename, 'PAS scheduling
is currently supported only in the MATLAB-native codebase.');
222 self.svcRateFun = muFun;
223 % Derive a representative per-class rate mu([r]) (single class-r job)
224 % so the standard rate/process machinery (refreshRates, procid) stays
225 % consistent; the authoritative service description remains mu(c).
226 classes = self.model.getClasses();
227 server = self.server;
230 if isfinite(rate_r) && rate_r > 0
233 dist = Disabled.getInstance();
235 if length(self.classCap) < r
236 self.classCap((length(self.classCap)+1):r) = Inf;
238 self.setStrategyParam(
classes{r}, 1.0);
239 if self.cap < intmax && ~isinf(self.cap)
240 self.dropRule(r) = DropStrategy.DROP;
242 self.dropRule(r) = DropStrategy.WAITQ;
244 server.serviceProcess{1, r}{2} = ServiceStrategy.LI;
245 server.serviceProcess{1, r}{3} = dist;
246 self.serviceProcess{r} = dist;
248 self.model.setInitialized(
false);
252 function muFun = getServiceRateFunction(self)
253 % MUFUNCTION = GETSERVICERATEFUNCTION()
255 % Returns the total service rate function mu(c) of a pass-and-swap
256 % (PAS) queue, or []
if not configured.
258 muFun = self.svcRateFun;
261 function setLoadDependence(self, alpha)
262 switch SchedStrategy.toId(self.schedStrategy)
263 case {SchedStrategy.PS, SchedStrategy.FCFS}
264 setLimitedLoadDependence(self, alpha);
266 line_error(mfilename,
'Load-dependence supported only for processor sharing (PS) and first-come first-serve (FCFS) stations.');
270 function setClassDependence(self, beta)
271 switch SchedStrategy.toId(self.schedStrategy)
272 case {SchedStrategy.PS, SchedStrategy.FCFS}
273 setLimitedClassDependence(self, beta);
275 line_error(mfilename,
'Class-dependence supported only for processor sharing (PS) and first-come first-serve (FCFS) stations.');
279 function setJointDependence(self, scalingTable, cutoffs)
280 % SETJOINTDEPENDENCE(SCALINGTABLE, CUTOFFS)
282 % Sets joint
class-dependent scaling
for service rates
using a
283 % lookup table indexed by per-
class population vector.
285 % scalingTable: R-dimensional
array or linearized 1D vector
286 % If R-dimensional: dimensions are [N1+1, N2+1, ..., NR+1]
287 % If linearized: index = 1 + n1 + n2*(N1+1) + n3*(N1+1)*(N2+1) + ...
288 % cutoffs: (optional) per-class cutoffs [N1, N2, ..., NR]
289 % If omitted, uses default: ceil(6000^(1/(M*K)))
291 switch SchedStrategy.toId(self.schedStrategy)
292 case {SchedStrategy.PS, SchedStrategy.FCFS}
294 % Auto-compute cutoffs
295 M = length(self.model.stations);
296 K = length(self.model.classes);
297 defaultCutoff = ceil(6000^(1/(M*K)));
298 cutoffs = defaultCutoff * ones(1, K);
301 % Convert multi-dimensional to linearized
if needed
302 if ~isvector(scalingTable)
303 scalingTable = scalingTable(:)
'; % linearize
306 % Validate table size matches cutoffs
307 expectedSize = prod(cutoffs + 1);
308 if length(scalingTable) ~= expectedSize
309 line_error(mfilename, sprintf('Scaling table size (%d) does not match expected size from cutoffs (%d).
', length(scalingTable), expectedSize));
312 setLimitedJointDependence(self, scalingTable, cutoffs);
314 line_error(mfilename,'Joint-dependence supported only for processor sharing (PS) and first-come first-serve (FCFS) stations.
');
318 function setJointClassDependence(self, scalingTables, cutoffs)
319 % SETJOINTCLASSDEPENDENCE(SCALINGTABLES, CUTOFFS)
321 % Sets per-class joint class-dependent scaling for service rates.
322 % Unlike setJointDependence which uses a single scaling factor for
323 % all classes, this method allows each class to have its own
324 % scaling table indexed by per-class population vector.
326 % This is essential for Flow-Equivalent Server (FES) aggregation
327 % where the service rate for class c in state (n1,...,nK) equals
328 % the throughput of class c in an isolated subnetwork.
330 % scalingTables: cell array {1,K} of linearized scaling vectors
331 % Each scalingTables{c} has length prod(cutoffs + 1)
332 % Index: idx = 1 + n1 + n2*(N1+1) + n3*(N1+1)*(N2+1) + ...
333 % cutoffs: per-class cutoffs [N1, N2, ..., NR]
335 switch SchedStrategy.toId(self.schedStrategy)
336 case {SchedStrategy.PS, SchedStrategy.FCFS}
338 expectedSize = prod(cutoffs + 1);
340 if ~iscell(scalingTables) || length(scalingTables) ~= K
341 line_error(mfilename, 'scalingTables must be cell
array of length K (number of
classes).
');
345 % Convert multi-dimensional to linearized if needed
346 if ~isvector(scalingTables{c})
347 scalingTables{c} = scalingTables{c}(:)';
350 if length(scalingTables{c}) ~= expectedSize
351 line_error(mfilename, sprintf(
'scalingTables{%d} size (%d) does not match expected size from cutoffs (%d).', c, length(scalingTables{c}), expectedSize));
355 setLimitedJointClassDependence(self, scalingTables, cutoffs);
357 line_error(mfilename,
'Joint-class-dependence supported only for processor sharing (PS) and first-come first-serve (FCFS) stations.');
361 function setNumberOfServers(self, value)
362 % SETNUMBEROFSERVERS(VALUE)
364 switch SchedStrategy.toId(self.schedStrategy)
365 case SchedStrategy.INF
366 %line_warning(mfilename,'A request to change the number of servers in an infinite server node has been ignored.');
369 self.setNumServers(value);
372 self.obj.setNumberOfServers(value);
376 function setNumServers(self, value)
377 % SETNUMSERVERS(VALUE)
379 switch SchedStrategy.toId(self.schedStrategy)
380 case {SchedStrategy.DPS, SchedStrategy.GPS}
382 line_error(mfilename,sprintf(
'Cannot use multi-server stations with %s scheduling.', self.schedStrategy));
385 self.numberOfServers = value;
388 self.obj.setNumberOfServers(value);
392 function self = setStrategyParam(self,
class, weight)
393 % SELF = SETSTRATEGYPARAM(CLASS, WEIGHT)
395 % For LPS scheduling, schedStrategyPar(1) stores the limit set via setLimit()
396 % Don't overwrite it with the default weight
397 if SchedStrategy.toId(self.schedStrategy) == SchedStrategy.LPS
398 % For LPS, only set weight if explicitly provided (not default 1.0)
399 % or if this
is not the first class (which would overwrite the limit)
400 if class.index == 1 && weight == 1.0 && ~isempty(self.schedStrategyPar) && self.schedStrategyPar(1) > 1
401 % Preserve the LPS limit, don't overwrite with default weight
405 self.schedStrategyPar(class.index) = weight;
408 function distribution = getService(self, class)
409 % DISTRIBUTION = GETSERVICE(CLASS)
411 % return the service distribution assigned to the given class
412 if nargin<2 %~exist('class','var')
413 for s = 1:length(self.model.getClasses())
414 classes = self.model.getClasses();
415 distribution{s} = self.server.serviceProcess{1,
classes{s}}{3};
419 distribution = self.server.serviceProcess{1,
class.index}{3};
422 line_warning(mfilename,
'No distribution is available for the specified class.\n');
427 function setService(self,
class, distribution, weight)
428 % SETSERVICE(CLASS, DISTRIBUTION, WEIGHT)
429 % distribution can be a Distribution
object or a Workflow
object
431 % SETSERVICE(MUFUNCTION) on a pass-and-swap (PAS) queue
432 % An order-independent/PAS queue
is parameterized by a single total
433 % service rate function mu(c) of the ordered state vector c (the row
434 % vector of
class indices, c(1)=oldest job), not by per-
class service
435 % distributions. The rate allocated to position i
is the increment
436 % Delta_mu(c(1..i)) = mu(c(1..i)) - mu(c(1..i-1)).
438 if SchedStrategy.toId(self.schedStrategy) == SchedStrategy.PAS
439 self.setServiceRateFunction(
class);
443 if nargin<4 %~exist(
'weight',
'var')
447 % If Workflow, convert to PH distribution
448 if isa(distribution, 'Workflow')
449 distribution = distribution.toPH();
452 if distribution.isImmediate()
453 distribution = Immediate.getInstance();
455 if isa(class,'SelfLoopingClass') && class.refstat.index ~= self.index && ~isa(distribution,'Disabled')
456 line_error(mfilename, 'For a self-looping class, service cannot be set on stations other than the reference station of the class.');
459 server = self.server; % by reference
461 if length(server.serviceProcess) >= c && ~isempty(server.serviceProcess{1,c}) %
if the distribution was already configured
462 %
this is a forced state reset in
case for example the number of phases changes
463 % appears to run faster without checks, probably due to
465 %oldDistribution = server.serviceProcess{1, c}{3};
466 %isOldMarkovian = isa(oldDistribution,
'Markovian');
467 %isNewMarkovian = isa(distribution,
'Markovian');
468 %
if distribution.getNumParams ~= oldDistribution.getNumParams
469 % %|| (isOldMarkovian && ~isNewMarkovian) || (~isOldMarkovian && isNewMarkovian) || (isOldMarkovian && isNewMarkovian && distribution.getNumberOfPhases ~= oldDistribution.getNumberOfPhases)
470 self.model.setInitialized(
false); %
this is a better way to invalidate to avoid that sequential calls to setService all trigger an initDefault
471 % Note: We no longer invalidate hasStruct here as it causes severe performance
472 % issues in iterative solvers like LN. The refreshRates/refreshProcesses methods
473 % called during solver post-iteration phase handle updating procid appropriately.
474 self.state=[]; % reset the state vector
476 else %
if first configuration
477 if length(self.classCap) < c
478 self.classCap((length(self.classCap)+1):c) = Inf;
480 self.setStrategyParam(
class, weight);
481 % Default to Drop
for finite capacity, WaitingQueue otherwise
482 if self.cap < intmax && ~isinf(self.cap)
483 self.dropRule(c) = DropStrategy.DROP;
485 self.dropRule(c) = DropStrategy.WAITQ;
487 server.serviceProcess{1, c}{2} = ServiceStrategy.LI;
489 server.serviceProcess{1, c}{3} = distribution;
490 self.serviceProcess{c} = distribution;
491 % Update cached procid
if struct exists to avoid stale values
492 % This
is needed because we don
't invalidate hasStruct for performance
493 if self.model.hasStruct && ~isempty(self.model.sn)
494 ist = self.model.getStationIndex(self);
495 procTypeId = ProcessType.toId(ProcessType.fromText(builtin('class', distribution)));
496 self.model.sn.procid(ist, c) = procTypeId;
499 self.obj.setService(class.obj, distribution.obj, weight);
500 % Also update MATLAB-side storage to keep in sync with Java object
501 % This ensures getService returns the correct distribution
503 self.serviceProcess{c} = distribution;
507 function setDelayOff(self, jobclass, setupTime, delayoffTime)
509 self.setupTime{1, c} = setupTime;
510 self.delayoffTime{1, c} = delayoffTime;
513 function dist = getSetupTime(self, jobclass)
515 if c <= length(self.setupTime) && ~isempty(self.setupTime{1, c})
516 dist = self.setupTime{1, c};
522 function dist = getDelayOffTime(self, jobclass)
524 if c <= length(self.delayoffTime) && ~isempty(self.delayoffTime{1, c})
525 dist = self.delayoffTime{1, c};
531 function setSwitchover(self, varargin)
532 if isempty(self.switchoverTime)
533 if SchedStrategy.toId(self.schedStrategy) == SchedStrategy.POLLING
534 self.switchoverTime = cell(1,length(self.model.getClasses()));
536 K = length(self.model.getClasses());
537 self.switchoverTime = cell(K,K);
540 self.switchoverTime{r,s} = Immediate();
545 if length(varargin)==2
546 jobclass = varargin{1};
547 soTime = varargin{2};
548 % time to switch from queue i to the next one
549 if SchedStrategy.toId(self.schedStrategy) ~= SchedStrategy.POLLING
550 line_error(mfilename,'setSwitchover(
jobclass, distrib) can only be invoked on queues with SchedStrategy.POLLING.\n
');
553 self.switchoverTime{1,c} = soTime;
554 elseif length(varargin)==3
555 jobclass_from = varargin{1};
556 jobclass_to = varargin{2};
557 soTime = varargin{3};
558 f = jobclass_from.index;
559 t = jobclass_to.index;
560 self.switchoverTime{f,t} = soTime;
564 function setPollingType(self, rule, par)
565 if PollingType.toId(rule) ~= PollingType.KLIMITED
567 elseif PollingType.toId(rule) == PollingType.KLIMITED && nargin<3
568 line_error(mfilename,'K-Limited polling
requires to specify the parameter K, e.g., setPollingType(PollingType.KLIMITED, 2).\n
');
570 % support only identical polling type at each class buffer
571 if SchedStrategy.toId(self.schedStrategy) ~= SchedStrategy.POLLING
572 line_error(mfilename,'setPollingType can only be invoked on queues with SchedStrategy.POLLING.\n
');
574 for r=1:length(self.model.getClasses())
575 self.pollingType{1,r} = rule;
576 self.pollingPar = par;
577 classes = self.model.getClasses();
578 setSwitchover(self, classes{r}, Immediate());
582 function setPatience(self, class, varargin)
583 % SETPATIENCE(CLASS, DISTRIBUTION) - Backwards compatible
584 % SETPATIENCE(CLASS, PATIENCETYPE, DISTRIBUTION) - Explicit type
586 % Sets the patience type and distribution for a specific job class at this queue.
587 % Jobs that wait longer than their patience time will abandon the queue.
590 % class - JobClass object
591 % impatienceType - (Optional) ImpatienceType constant (RENEGING or BALKING)
592 % If omitted, defaults to ImpatienceType.RENEGING
593 % distribution - Any LINE distribution (Exp, Erlang, HyperExp, etc.)
594 % excluding modulated processes (BMAP, MAP, MMPP2)
596 % Note: This setting takes precedence over the global class patience.
599 % queue.setPatience(jobclass, Exp(0.2)) % Defaults to RENEGING
600 % queue.setPatience(jobclass, ImpatienceType.RENEGING, Exp(0.2))
601 % queue.setPatience(jobclass, ImpatienceType.BALKING, Exp(0.5))
603 % Handle backwards compatibility: 2 or 3 arguments
604 if length(varargin) == 1
605 % Old signature: setPatience(class, distribution)
606 distribution = varargin{1};
607 impatienceType = ImpatienceType.RENEGING; % Default to RENEGING
608 elseif length(varargin) == 2
609 % New signature: setPatience(class, impatienceType, distribution)
610 impatienceType = varargin{1};
611 distribution = varargin{2};
613 line_error(mfilename, 'Invalid number of arguments. Use setPatience(
class, distribution) or setPatience(
class, impatienceType, distribution)
');
616 if isa(distribution, 'BMAP
') || isa(distribution, 'MAP
') || isa(distribution, 'DMAP
') || isa(distribution, 'MMPP2
')
617 line_error(mfilename, 'Modulated processes (BMAP, MAP, DMAP, MMPP2) are not supported
for patience distributions.
');
620 % Validate impatience type
621 if impatienceType ~= ImpatienceType.RENEGING && impatienceType ~= ImpatienceType.BALKING
622 line_error(mfilename, 'Invalid impatience type. Use ImpatienceType.RENEGING or ImpatienceType.BALKING.
');
625 % Only RENEGING is currently supported
626 if impatienceType == ImpatienceType.BALKING
627 line_error(mfilename, 'BALKING impatience type
is not yet supported. Use ImpatienceType.RENEGING.
');
630 if distribution.isImmediate()
631 distribution = Immediate.getInstance();
636 self.patienceDistributions{1, c} = distribution;
637 self.impatienceTypes{1, c} = impatienceType;
639 self.obj.setPatience(class.obj, impatienceType, distribution.obj);
643 function distribution = getPatience(self, class)
644 % DISTRIBUTION = GETPATIENCE(CLASS)
646 % Returns the patience distribution for a specific job class.
647 % Returns the queue-specific setting if available, otherwise
648 % falls back to the global class patience.
651 % class - JobClass object
654 % distribution - The patience distribution, or [] if not set
658 % Check queue-specific patience first
659 if c <= length(self.patienceDistributions) && ~isempty(self.patienceDistributions{1, c})
660 distribution = self.patienceDistributions{1, c};
662 % Fall back to global class patience
663 distribution = class.getPatience();
666 distObj = self.obj.getPatience(class.obj);
670 distribution = Distribution.fromJavaObject(distObj);
675 function setLimit(self, limit)
676 % SETLIMIT(LIMIT) Sets the maximum number of jobs for LPS scheduling
679 % limit - Maximum number of jobs in PS (processor sharing) mode for LPS
682 % MATLAB native implementation - store as a scheduling parameter
683 if SchedStrategy.toId(self.schedStrategy) ~= SchedStrategy.LPS
684 line_warning(mfilename, 'setLimit
is only applicable to LPS (Least Progress Scheduling) queues.
');
687 % Store limit in schedStrategyPar (use index 0 for queue-level parameter)
688 if length(self.schedStrategyPar) < 1
689 self.schedStrategyPar = zeros(1, 1);
691 self.schedStrategyPar(1) = limit;
693 % JavaNative implementation
694 if SchedStrategy.toId(self.schedStrategy) ~= SchedStrategy.LPS
695 line_warning(mfilename, 'setLimit
is only applicable to LPS (Least Progress Scheduling) queues.
');
698 self.obj.setLimit(limit);
702 function limit = getLimit(self)
703 % LIMIT = GETLIMIT() Returns the maximum number of jobs for LPS scheduling
706 % limit - Maximum number of jobs in PS mode for LPS
709 % MATLAB native implementation
710 if length(self.schedStrategyPar) >= 1
711 limit = self.schedStrategyPar(1);
716 % JavaNative implementation
717 limit = self.obj.getLimit();
721 function impatienceType = getImpatienceType(self, class)
722 % IMPATIENCETYPE = GETIMPATIENCETYPE(CLASS)
724 % Returns the impatience type for a specific job class.
725 % Returns the queue-specific setting if available, otherwise
726 % falls back to the global class impatience type.
729 % class - JobClass object
732 % impatienceType - The impatience type (ImpatienceType constant), or [] if not set
736 % Check queue-specific impatience type first
737 if c <= length(self.impatienceTypes) && ~isempty(self.impatienceTypes{1, c})
738 impatienceType = self.impatienceTypes{1, c};
740 % Fall back to global class impatience type
741 impatienceType = class.getImpatienceType();
744 impatienceTypeId = self.obj.getImpatienceType(class.obj);
745 if isempty(impatienceTypeId)
748 impatienceType = ImpatienceType.fromId(impatienceTypeId.getID());
753 function tf = hasPatience(self, class)
754 % TF = HASPATIENCE(CLASS)
756 % Returns true if this class has patience configured at this queue
757 % (either locally or globally).
759 dist = self.getPatience(class);
760 tf = ~isempty(dist) && ~isa(dist, 'Disabled
');
763 function setBalking(self, class, strategy, thresholds)
764 % SETBALKING(CLASS, STRATEGY, THRESHOLDS)
766 % Configures balking behavior for a specific job class at this queue.
767 % When a customer arrives, they may refuse to join based on queue length.
770 % class - JobClass object
771 % strategy - BalkingStrategy constant:
772 % QUEUE_LENGTH - Balk based on current queue length
773 % EXPECTED_WAIT - Balk based on expected waiting time
774 % COMBINED - Both conditions (OR logic)
775 % thresholds - Cell array of balking thresholds, each element is:
776 % {minJobs, maxJobs, probability}
777 % where probability is the chance to balk when queue
778 % length is in [minJobs, maxJobs] range.
781 % % Balk with 30% probability when 5-10 jobs in queue,
782 % % 80% when 11-20 jobs, 100% when >20 jobs
783 % queue.setBalking(jobclass, BalkingStrategy.QUEUE_LENGTH, ...
784 % {{5, 10, 0.3}, {11, 20, 0.8}, {21, Inf, 1.0}});
788 self.balkingStrategies{1, c} = strategy;
789 self.balkingThresholds{1, c} = thresholds;
791 % Java native - convert thresholds to Java format
792 jThresholds = jline.util.BalkingThresholdList();
793 for i = 1:length(thresholds)
798 maxJobs = java.lang.Integer.MAX_VALUE;
801 jThresholds.add(jline.lang.BalkingThreshold(minJobs, maxJobs, probability));
803 % Convert strategy to Java enum
805 case BalkingStrategy.QUEUE_LENGTH
806 jStrategy = jline.lang.constant.BalkingStrategy.QUEUE_LENGTH;
807 case BalkingStrategy.EXPECTED_WAIT
808 jStrategy = jline.lang.constant.BalkingStrategy.EXPECTED_WAIT;
809 case BalkingStrategy.COMBINED
810 jStrategy = jline.lang.constant.BalkingStrategy.COMBINED;
812 self.obj.setBalking(class.obj, jStrategy, jThresholds);
816 function [strategy, thresholds] = getBalking(self, class)
817 % [STRATEGY, THRESHOLDS] = GETBALKING(CLASS)
819 % Returns the balking configuration for a specific job class.
822 % class - JobClass object
825 % strategy - BalkingStrategy constant, or [] if not configured
826 % thresholds - Cell array of {minJobs, maxJobs, probability} tuples
830 if c <= length(self.balkingStrategies) && ~isempty(self.balkingStrategies{1, c})
831 strategy = self.balkingStrategies{1, c};
832 thresholds = self.balkingThresholds{1, c};
838 jStrategy = self.obj.getBalkingStrategy(class.obj);
839 if isempty(jStrategy)
843 strategy = BalkingStrategy.fromId(jStrategy.getId());
844 jThresholds = self.obj.getBalkingThresholds(class.obj);
846 if ~isempty(jThresholds)
847 for i = 0:(jThresholds.size()-1)
848 jTh = jThresholds.get(i);
849 maxJobs = jTh.getMaxJobs();
850 if maxJobs == java.lang.Integer.MAX_VALUE
853 thresholds{end+1} = {jTh.getMinJobs(), maxJobs, jTh.getProbability()};
860 function tf = hasBalking(self, class)
861 % TF = HASBALKING(CLASS)
863 % Returns true if this class has balking configured at this queue.
865 [strategy, ~] = self.getBalking(class);
866 tf = ~isempty(strategy);
869 function setRetrial(self, class, delayDistribution, maxAttempts)
870 % SETRETRIAL(CLASS, DELAYDISTRIBUTION, MAXATTEMPTS)
872 % Configures retrial behavior for a specific job class at this queue.
873 % When a customer is rejected (queue full), they move to an orbit
874 % and retry after a random delay.
877 % class - JobClass object
878 % delayDistribution - Distribution for retrial delay (e.g., Exp(0.5))
879 % maxAttempts - Maximum number of retrial attempts:
880 % -1 = unlimited retries (default)
881 % N = drop after N failed attempts
884 % % Retry with exponential delay, unlimited attempts
885 % queue.setRetrial(jobclass, Exp(0.5), -1);
887 % % Retry up to 3 times with Erlang delay
888 % queue.setRetrial(jobclass, Erlang(2, 0.3), 3);
891 maxAttempts = -1; % Unlimited by default
894 if isa(delayDistribution, 'BMAP
') || isa(delayDistribution, 'MAP
') || isa(delayDistribution, 'DMAP
') || isa(delayDistribution, 'MMPP2
')
895 line_error(mfilename, 'Modulated processes (BMAP, MAP, DMAP, MMPP2) are not supported
for retrial delay distributions.
');
900 self.retrialDelays{1, c} = delayDistribution;
901 % Ensure array is large enough
902 if length(self.retrialMaxAttempts) < c
903 self.retrialMaxAttempts(end+1:c) = -1;
905 self.retrialMaxAttempts(c) = maxAttempts;
906 % Also set drop rule to RETRIAL or RETRIAL_WITH_LIMIT
908 self.dropRule(c) = DropStrategy.RETRIAL;
910 self.dropRule(c) = DropStrategy.RETRIAL_WITH_LIMIT;
913 self.obj.setRetrial(class.obj, delayDistribution.obj, maxAttempts);
917 function [delayDistribution, maxAttempts] = getRetrial(self, class)
918 % [DELAYDISTRIBUTION, MAXATTEMPTS] = GETRETRIAL(CLASS)
920 % Returns the retrial configuration for a specific job class.
923 % class - JobClass object
926 % delayDistribution - Retrial delay distribution, or [] if not configured
927 % maxAttempts - Maximum retrial attempts (-1 = unlimited)
931 if c <= length(self.retrialDelays) && ~isempty(self.retrialDelays{1, c})
932 delayDistribution = self.retrialDelays{1, c};
933 if c <= length(self.retrialMaxAttempts)
934 maxAttempts = self.retrialMaxAttempts(c);
939 delayDistribution = [];
943 distObj = self.obj.getRetrialDelayDistribution(class.obj);
945 delayDistribution = [];
948 delayDistribution = Distribution.fromJavaObject(distObj);
949 maxAttempts = self.obj.getMaxRetrialAttempts(class.obj);
954 function tf = hasRetrial(self, class)
955 % TF = HASRETRIAL(CLASS)
957 % Returns true if this class has retrial configured at this queue.
959 [dist, ~] = self.getRetrial(class);
960 tf = ~isempty(dist) && ~isa(dist, 'Disabled
');
963 function setOrbitImpatience(self, class, distribution)
964 % SETORBITIMPATIENCE(CLASS, DISTRIBUTION)
966 % Sets the impatience (abandonment) rate for customers in the orbit.
967 % This is separate from queue patience (reneging from waiting queue).
968 % Used in BMAP/PH/N/N retrial queues where customers in the orbit
969 % may abandon before successfully retrying.
972 % class - JobClass object
973 % distribution - Distribution for orbit abandonment time (e.g., Exp(gamma))
976 % queue.setOrbitImpatience(jobclass, Exp(0.008)); % gamma = 0.008
978 if isa(distribution, 'BMAP
') || isa(distribution, 'MAP
') || isa(distribution, 'DMAP
') || isa(distribution, 'MMPP2
')
979 line_error(mfilename, 'Modulated processes (BMAP, MAP, DMAP, MMPP2) are not supported
for orbit impatience distributions.
');
984 self.orbitImpatienceDistributions{1, c} = distribution;
986 self.obj.setOrbitImpatience(class.obj, distribution.obj);
990 function distribution = getOrbitImpatience(self, class)
991 % DISTRIBUTION = GETORBITIMPATIENCE(CLASS)
993 % Returns the orbit impatience distribution for a specific job class.
996 % class - JobClass object
999 % distribution - The orbit impatience distribution, or [] if not set
1001 if isempty(self.obj)
1003 if c <= length(self.orbitImpatienceDistributions) && ~isempty(self.orbitImpatienceDistributions{1, c})
1004 distribution = self.orbitImpatienceDistributions{1, c};
1009 distObj = self.obj.getOrbitImpatience(class.obj);
1013 distribution = Distribution.fromJavaObject(distObj);
1018 function tf = hasOrbitImpatience(self, class)
1019 % TF = HASORBITORBITIMPATIENCE(CLASS)
1021 % Returns true if this class has orbit impatience configured at this queue.
1023 dist = self.getOrbitImpatience(class);
1024 tf = ~isempty(dist) && ~isa(dist, 'Disabled
');
1027 function setBatchRejectProbability(self, class, p)
1028 % SETBATCHREJECTPROBABILITY(CLASS, P)
1030 % Sets the probability that an entire batch is rejected when it
1031 % cannot be fully admitted. Used in BMAP/PH/N/N retrial queues
1032 % with batch arrivals.
1034 % When a batch of size k arrives and only m < k servers are free:
1035 % - With probability p: entire batch is rejected to orbit
1036 % - With probability (1-p): m customers are admitted, k-m go to orbit
1039 % class - JobClass object
1040 % p - Probability [0,1] that batch is rejected vs partially admitted
1041 % Default is 0 (partial admission allowed)
1044 % queue.setBatchRejectProbability(jobclass, 0.4);
1047 line_error(mfilename, 'Batch reject probability must be in [0, 1].
');
1050 if isempty(self.obj)
1052 % Ensure array is large enough
1053 if length(self.batchRejectProb) < c
1054 self.batchRejectProb(end+1:c) = 0;
1056 self.batchRejectProb(c) = p;
1058 self.obj.setBatchRejectProbability(class.obj, p);
1062 function p = getBatchRejectProbability(self, class)
1063 % P = GETBATCHREJECTPROBABILITY(CLASS)
1065 % Returns the batch reject probability for a specific job class.
1068 % class - JobClass object
1071 % p - Batch reject probability [0,1], or 0 if not set
1073 if isempty(self.obj)
1075 if c <= length(self.batchRejectProb) && self.batchRejectProb(c) > 0
1076 p = self.batchRejectProb(c);
1078 p = 0; % Default: partial admission allowed
1081 p = self.obj.getBatchRejectProbability(class.obj);
1085 % function distrib = getServiceProcess(self, oclass)
1086 % distrib = self.serviceProcess{oclass};
1089 % ==================== Heterogeneous Server Methods ====================
1091 function self = addServerType(self, serverType)
1092 % ADDSERVERTYPE Add a server type to this queue
1094 % self = ADDSERVERTYPE(serverType) adds a ServerType to this queue
1095 % for heterogeneous multiserver configuration.
1097 % When server types are added, the queue becomes a heterogeneous
1098 % multiserver queue where different server types can have different
1099 % service rates and serve different subsets of job classes.
1101 % @param serverType The ServerType object to add
1103 if isempty(serverType)
1104 line_error(mfilename, 'Server type cannot be empty
');
1107 % Check if already added
1108 for i = 1:length(self.serverTypes)
1109 if self.serverTypes{i} == serverType
1110 line_error(mfilename, 'Server type
''%s
'' is already added to
this queue
', serverType.getName());
1114 if isempty(self.obj)
1115 % MATLAB native implementation
1116 serverType.setId(length(self.serverTypes));
1117 serverType.setParentQueue(self);
1118 self.serverTypes{end+1} = serverType;
1120 % Initialize service distribution map for this server type
1121 self.heteroServiceDistributions(serverType.getName()) = containers.Map();
1123 % Update total number of servers
1124 self.updateTotalServerCount();
1126 % Java native - delegate to Java object
1127 self.obj.addServerType(serverType.obj);
1128 % Also store locally
1129 self.serverTypes{end+1} = serverType;
1133 function updateTotalServerCount(self)
1134 % UPDATETOTALSERVERCOUNT Update total server count from all types
1136 % Internal method to recalculate numberOfServers.
1138 if isempty(self.serverTypes)
1142 for i = 1:length(self.serverTypes)
1143 total = total + self.serverTypes{i}.getNumOfServers();
1145 self.numberOfServers = total;
1148 function types = getServerTypes(self)
1149 % GETSERVERTYPES Get the list of server types
1151 % types = GETSERVERTYPES() returns a cell array of ServerType objects.
1153 types = self.serverTypes;
1156 function n = getNumServerTypes(self)
1157 % GETNUMSERVERTYPES Get the number of server types
1159 % n = GETNUMSERVERTYPES() returns the number of server types,
1160 % or 0 if this is a homogeneous queue.
1162 n = length(self.serverTypes);
1165 function result = isHeterogeneous(self)
1166 % ISHETEROGENEOUS Check if this is a heterogeneous multiserver queue
1168 % result = ISHETEROGENEOUS() returns true if server types are defined.
1170 result = ~isempty(self.serverTypes);
1173 function self = setHeteroSchedPolicy(self, policy)
1174 % SETHETEROSCHEDPOLICY Set the heterogeneous server scheduling policy
1176 % self = SETHETEROSCHEDPOLICY(policy) sets the policy that determines
1177 % how jobs are assigned to server types when a job's
class is
1178 % compatible with multiple server types.
1180 % @param policy HeteroSchedPolicy constant (ORDER, ALIS, ALFS, FAIRNESS, FSF, RAIS)
1182 if isempty(self.obj)
1183 self.heteroSchedPolicy = policy;
1185 % Convert to Java enum
1187 case HeteroSchedPolicy.ORDER
1188 jPolicy = jline.lang.constant.HeteroSchedPolicy.ORDER;
1189 case HeteroSchedPolicy.ALIS
1190 jPolicy = jline.lang.constant.HeteroSchedPolicy.ALIS;
1191 case HeteroSchedPolicy.ALFS
1192 jPolicy = jline.lang.constant.HeteroSchedPolicy.ALFS;
1193 case HeteroSchedPolicy.FAIRNESS
1194 jPolicy = jline.lang.constant.HeteroSchedPolicy.FAIRNESS;
1195 case HeteroSchedPolicy.FSF
1196 jPolicy = jline.lang.constant.HeteroSchedPolicy.FSF;
1197 case HeteroSchedPolicy.RAIS
1198 jPolicy = jline.lang.constant.HeteroSchedPolicy.RAIS;
1200 self.obj.setHeteroSchedPolicy(jPolicy);
1201 self.heteroSchedPolicy = policy;
1205 function policy = getHeteroSchedPolicy(self)
1206 % GETHETEROSCHEDPOLICY Get the heterogeneous server scheduling policy
1208 % policy = GETHETEROSCHEDPOLICY() returns the HeteroSchedPolicy.
1210 policy = self.heteroSchedPolicy;
1213 function setHeteroService(self, jobClass, serverType, distribution)
1214 % SETHETEROSERVICE Set service distribution for a job class and server type
1216 % SETHETEROSERVICE(jobClass, serverType, distribution) sets the
1217 % service time distribution for a specific job class when served
1218 % by a specific server type.
1220 % @param jobClass The JobClass
object
1221 % @param serverType The ServerType
object
1222 % @param distribution The service time Distribution
1224 if isempty(jobClass)
1225 line_error(mfilename, 'Job class cannot be empty');
1227 if isempty(serverType)
1228 line_error(mfilename, 'Server type cannot be empty');
1230 if isempty(distribution)
1231 line_error(mfilename, 'Distribution cannot be empty');
1234 % Check if server type
is in this queue
1236 for i = 1:length(self.serverTypes)
1237 if self.serverTypes{i} == serverType
1243 line_error(mfilename,
'Server type ''%s'' is not added to this queue. Call addServerType() first.', serverType.getName());
1246 if isempty(self.obj)
1247 % MATLAB native implementation
1248 if ~isKey(self.heteroServiceDistributions, serverType.getName())
1249 self.heteroServiceDistributions(serverType.getName()) = containers.Map();
1251 classMap = self.heteroServiceDistributions(serverType.getName());
1252 classMap(jobClass.getName()) = distribution;
1253 self.heteroServiceDistributions(serverType.getName()) = classMap;
1255 % Ensure compatibility
1256 if ~serverType.isCompatible(jobClass)
1257 serverType.addCompatible(jobClass);
1260 % Java native - delegate to Java
object
1261 self.obj.setService(jobClass.obj, serverType.obj, distribution.obj);
1265 function distribution = getHeteroService(self, jobClass, serverType)
1266 % GETHETEROSERVICE Get service distribution for a job class and server type
1268 % distribution = GETHETEROSERVICE(jobClass, serverType) returns the
1269 % service time distribution for a specific job class and server type.
1271 % @param jobClass The JobClass
object
1272 % @param serverType The ServerType
object
1273 % @return distribution The service time Distribution, or [] if not set
1275 if isempty(self.obj)
1276 if isKey(self.heteroServiceDistributions, serverType.getName())
1277 classMap = self.heteroServiceDistributions(serverType.getName());
1278 if isKey(classMap, jobClass.getName())
1279 distribution = classMap(jobClass.getName());
1287 distObj = self.obj.getService(jobClass.obj, serverType.obj);
1291 distribution = Distribution.fromJavaObject(distObj);
1296 function st = getServerTypeById(self,
id)
1297 % GETSERVERTYPEBYID Get a server type by its ID
1299 % st = GETSERVERTYPEBYID(
id) returns the ServerType with the given ID,
1300 % or [] if not found.
1302 if
id >= 0 &&
id < length(self.serverTypes)
1303 st = self.serverTypes{
id + 1}; % MATLAB 1-indexed
1309 function st = getServerTypeByName(self, name)
1310 % GETSERVERTYPEBYNAME Get a server type by its name
1312 % st = GETSERVERTYPEBYNAME(name) returns the ServerType with the given
1313 % name, or []
if not found.
1316 for i = 1:length(self.serverTypes)
1317 if strcmp(self.serverTypes{i}.getName(), name)
1318 st = self.serverTypes{i};
1324 function result = validateCompatibility(self)
1325 % VALIDATECOMPATIBILITY Check all job
classes have compatible server types
1327 % result = VALIDATECOMPATIBILITY() returns true if all job
classes
1328 % in the model have at least one compatible server type at this queue.
1330 if ~self.isHeterogeneous()
1335 classes = self.model.getClasses();
1338 hasCompatible =
false;
1339 for s = 1:length(self.serverTypes)
1340 if self.serverTypes{s}.isCompatible(jobClass)
1341 hasCompatible =
true;
1353 % ==================== Immediate Feedback Methods ====================
1355 function setImmediateFeedback(self, varargin)
1356 % SETIMMEDIATEFEEDBACK Set immediate feedback
for self-loops
1358 % SETIMMEDIATEFEEDBACK(
true) enables immediate feedback for all
classes
1359 % SETIMMEDIATEFEEDBACK(false) disables immediate feedback for all
classes
1360 % SETIMMEDIATEFEEDBACK(jobClass) enables for a specific class
1361 % SETIMMEDIATEFEEDBACK({class1, class2}) enables
for multiple
classes
1363 % When enabled, a job that self-loops at
this station stays in service
1364 % instead of going back to the queue.
1366 if isempty(self.obj)
1367 % MATLAB native implementation
1370 if islogical(arg) || isnumeric(arg)
1373 self.immediateFeedback =
'all';
1376 self.immediateFeedback = {};
1378 elseif isa(arg,
'JobClass')
1380 if isempty(self.immediateFeedback) || ischar(self.immediateFeedback)
1381 self.immediateFeedback = {};
1383 if ~any(cellfun(@(x) x == arg.index, self.immediateFeedback))
1384 self.immediateFeedback{end+1} = arg.index;
1388 self.immediateFeedback = {};
1389 for i = 1:length(arg)
1390 if isa(arg{i},
'JobClass')
1391 self.immediateFeedback{end+1} = arg{i}.index;
1397 % Java native implementation
1400 if islogical(arg) || isnumeric(arg)
1401 self.obj.setImmediateFeedback(logical(arg));
1402 elseif isa(arg,
'JobClass')
1403 self.obj.setImmediateFeedback(arg.obj);
1405 classList = java.util.ArrayList();
1406 for i = 1:length(arg)
1407 if isa(arg{i},
'JobClass')
1408 classList.add(arg{i}.obj);
1411 self.obj.setImmediateFeedbackForClasses(classList);
1417 function tf = hasImmediateFeedback(self, varargin)
1418 % HASIMMEDIATEFEEDBACK Check
if immediate feedback
is enabled
1420 % TF = HASIMMEDIATEFEEDBACK() returns true if enabled for any class
1421 % TF = HASIMMEDIATEFEEDBACK(jobClass) returns true if enabled for specific class
1423 if isempty(self.obj)
1424 % MATLAB native implementation
1425 if isempty(self.immediateFeedback)
1427 elseif ischar(self.immediateFeedback) && strcmp(self.immediateFeedback, 'all')
1430 % No class specified - check if any class has it enabled
1431 tf = ~isempty(self.immediateFeedback);
1433 % Check specific class
1434 jobClass = varargin{1};
1435 if ischar(self.immediateFeedback) && strcmp(self.immediateFeedback,
'all')
1438 tf = any(cellfun(@(x) x == jobClass.index, self.immediateFeedback));
1442 % Java native implementation
1444 tf = self.obj.hasImmediateFeedback();
1446 jobClass = varargin{1};
1447 tf = self.obj.hasImmediateFeedback(jobClass.index - 1); % Java 0-indexed
1452 function
classes = getImmediateFeedbackClasses(self)
1453 % GETIMMEDIATEFEEDBACKCLASSES Get list of
class indices with immediate feedback
1455 % CLASSES = GETIMMEDIATEFEEDBACKCLASSES() returns cell
array of class indices
1457 if isempty(self.obj)
1458 if isempty(self.immediateFeedback)
1460 elseif ischar(self.immediateFeedback) && strcmp(self.immediateFeedback,
'all')
1463 classes = self.immediateFeedback;
1466 jClasses = self.obj.getImmediateFeedbackClasses();
1467 if isempty(jClasses)
1469 elseif jClasses.equals(
"all")
1473 for i = 0:(jClasses.size()-1)
1474 classes{end+1} = jClasses.get(i) + 1; % Convert to MATLAB 1-indexed