LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
Signal.m
1classdef Signal < JobClass
2 % Signal Job class representing a signal (e.g., negative customer in G-networks)
3 %
4 % Signal is a placeholder class that automatically resolves to OpenSignal
5 % or ClosedSignal based on the network structure. Users can simply use
6 % Signal in both open and closed networks - the resolution happens when
7 % the model is finalized (during getStruct/refreshStruct).
8 %
9 % @brief Job class for modeling signals in G-networks and related models
10 %
11 % Key characteristics:
12 % - Automatically resolves to OpenSignal or ClosedSignal
13 % - Supports different signal types (NEGATIVE, REPLY, CATASTROPHE)
14 % - NEGATIVE signals remove jobs from destination queues
15 % - CATASTROPHE signals reset the state of queues
16 % - Used in G-networks (Gelenbe networks)
17 %
18 % Signal types:
19 % - SignalType.NEGATIVE: Removes a job from the destination queue
20 % - SignalType.REPLY: Triggers a reply action
21 % - SignalType.CATASTROPHE: Resets destination queue to empty state
22 %
23 % Example (Open Network):
24 % @code
25 % model = Network('GNetwork');
26 % source = Source(model, 'Source');
27 % sink = Sink(model, 'Sink');
28 % queue = Queue(model, 'Queue', SchedStrategy.FCFS);
29 % posClass = OpenClass(model, 'Positive'); % Normal customers
30 % negClass = Signal(model, 'Negative', SignalType.NEGATIVE); % Resolves to OpenSignal
31 % source.setArrival(posClass, Exp(1.0));
32 % source.setArrival(negClass, Exp(0.3));
33 % @endcode
34 %
35 % Example (Closed Network):
36 % @code
37 % model = Network('ClosedGNetwork');
38 % delay = Delay(model, 'Think');
39 % queue = Queue(model, 'Queue', SchedStrategy.FCFS);
40 % jobClass = ClosedClass(model, 'Job', 5, delay);
41 % replySignal = Signal(model, 'Reply', SignalType.REPLY).forJobClass(jobClass); % Resolves to ClosedSignal
42 % @endcode
43 %
44 % Reference: Gelenbe, E. (1991). "Product-form queueing networks with
45 % negative and positive customers", Journal of Applied Probability
46 %
47 % Copyright (c) 2012-2026, Imperial College London
48 % All rights reserved.
49
50 properties
51 signalType % SignalType constant (NEGATIVE, REPLY, CATASTROPHE)
52 targetJobClass % JobClass that this signal is associated with (for REPLY: the class to unblock)
53 removalDistribution % DiscreteDistribution for number of removals (empty = remove exactly 1)
54 removalPolicy % RemovalPolicy constant (RANDOM, FCFS, LCFS)
55 model % Reference to the Network model
56 end
57
58 methods
59
60 %Constructor
61 function self = Signal(model, name, signalType, prio, removalDistribution, removalPolicy)
62 % SIGNAL Create a signal class instance
63 %
64 % @brief Creates a Signal class for G-network modeling
65 % @param model Network model to add the signal class to
66 % @param name String identifier for the signal class
67 % @param signalType SignalType constant (REQUIRED: NEGATIVE, REPLY, or CATASTROPHE)
68 % @param prio Optional priority level (default: 0)
69 % @param removalDistribution Optional discrete distribution for batch removals (default: [])
70 % @param removalPolicy Optional RemovalPolicy constant (default: RemovalPolicy.RANDOM)
71 % @return self Signal instance ready for arrival specification
72
73 if nargin < 3 || isempty(signalType)
74 line_error(mfilename, 'signalType is required. Use SignalType.NEGATIVE, SignalType.REPLY, or SignalType.CATASTROPHE.');
75 end
76 if nargin < 6 || isempty(removalPolicy)
77 removalPolicy = RemovalPolicy.RANDOM;
78 end
79 if nargin < 5
80 removalDistribution = [];
81 end
82 if nargin < 4 || isempty(prio)
83 prio = 0;
84 end
85
86 % Initialize as JobClass (type will be determined during resolution)
87 self@JobClass(JobClassType.OPEN, name); % Default to OPEN, resolved later
88 self.priority = prio;
89 self.signalType = signalType;
90 self.targetJobClass = [];
91 self.removalDistribution = removalDistribution;
92 self.removalPolicy = removalPolicy;
93 self.model = model;
94
95 % Register with the model
96 model.addJobClass(self);
97
98 % Set default routing for this class at all nodes
99 for i = 1:length(model.nodes)
100 if isa(model.nodes{i}, 'Join')
101 model.nodes{i}.setStrategy(self, JoinStrategy.STD);
102 model.nodes{i}.setRequired(self, -1);
103 end
104 if ~isempty(model.nodes{i})
105 model.nodes{i}.setRouting(self, RoutingStrategy.RAND);
106 end
107 end
108
109 % Java interop is handled during resolution
110 end
111
112 function concrete = resolve(self, isOpen, refstat)
113 % RESOLVE Resolve this Signal placeholder to OpenSignal or ClosedSignal
114 %
115 % @param isOpen true if the network is open (has Source node)
116 % @param refstat Reference station for closed networks (ignored for open)
117 % @return concrete OpenSignal or ClosedSignal instance
118
119 % Save properties before removing placeholder
120 savedIndex = self.index;
121 savedName = self.name;
122 savedTargetJobClass = self.targetJobClass;
123
124 % Save outputStrategy entries for this signal at all nodes
125 % These need to be restored at the new index after resolution
126 savedOutputStrategies = cell(1, length(self.model.nodes));
127 for n = 1:length(self.model.nodes)
128 node = self.model.nodes{n};
129 if ~isempty(node.output) && isprop(node.output, 'outputStrategy')
130 outStrat = node.output.outputStrategy;
131 % Check both 1D and 2D indexing patterns
132 if iscell(outStrat) && length(outStrat) >= savedIndex
133 savedOutputStrategies{n} = outStrat{savedIndex};
134 elseif iscell(outStrat) && size(outStrat, 2) >= savedIndex
135 savedOutputStrategies{n} = outStrat{1, savedIndex};
136 end
137 end
138 end
139
140 % Remove placeholder from model's classes list to avoid duplicate
141 % when concrete signal is created (its constructor calls addJobClass)
142 placeholderIdx = find(cellfun(@(c) c == self, self.model.classes));
143 if ~isempty(placeholderIdx)
144 self.model.classes(placeholderIdx) = [];
145 % Update indices of classes that come after
146 for c = placeholderIdx:length(self.model.classes)
147 self.model.classes{c}.index = c;
148 end
149 end
150
151 % Create concrete signal (constructor will add it to model.classes)
152 if isOpen
153 concrete = OpenSignal(self.model, savedName, self.signalType, self.priority);
154 else
155 concrete = ClosedSignal(self.model, savedName, self.signalType, refstat, ...
156 self.priority, self.removalDistribution, self.removalPolicy);
157 end
158
159 % Restore saved outputStrategy entries at the new index
160 newIndex = concrete.index;
161 for n = 1:length(self.model.nodes)
162 if ~isempty(savedOutputStrategies{n})
163 node = self.model.nodes{n};
164 if ~isempty(node.output) && isprop(node.output, 'outputStrategy')
165 % Ensure outputStrategy has enough elements
166 outStrat = node.output.outputStrategy;
167 while length(outStrat) < newIndex
168 outStrat{end+1} = {}; %#ok<AGROW>
169 end
170 outStrat{newIndex} = savedOutputStrategies{n};
171 node.output.outputStrategy = outStrat;
172 end
173 end
174 end
175
176 % Copy over targetJobClass association
177 if ~isempty(savedTargetJobClass)
178 concrete.forJobClass(savedTargetJobClass);
179 end
180
181 % Copy removal configuration (both OpenSignal and ClosedSignal have these now)
182 concrete.removalDistribution = self.removalDistribution;
183 concrete.removalPolicy = self.removalPolicy;
184 end
185
186 function type = getSignalType(self)
187 % GETSIGNALTYPE Get the signal type
188 %
189 % @return type The SignalType of this signal class
190 type = self.signalType;
191 end
192
193 function self = forJobClass(self, jobClass)
194 % FORJOBCLASS Associate this signal with a job class
195 %
196 % self = FORJOBCLASS(self, jobClass) associates this signal with
197 % the specified job class. For REPLY signals, this specifies which
198 % job class's servers will be unblocked when this signal arrives.
199 %
200 % @param jobClass The JobClass to associate with this signal
201 % @return self The modified Signal instance (for chaining)
202 %
203 % Example:
204 % replySignal = Signal(model, 'Reply', SignalType.REPLY).forJobClass(reqClass);
205
206 self.targetJobClass = jobClass;
207
208 % Also update the JobClass to know about this signal (bidirectional link)
209 if ~isempty(jobClass)
210 jobClass.replySignalClass = self;
211 end
212 end
213
214 function jobClass = getTargetJobClass(self)
215 % GETTARGETJOBCLASS Get the associated job class
216 %
217 % @return jobClass The JobClass associated with this signal
218 jobClass = self.targetJobClass;
219 end
220
221 function idx = getTargetJobClassIndex(self)
222 % GETTARGETJOBCLASSINDEX Get the index of the associated job class
223 %
224 % @return idx Index of the associated JobClass, or -1 if none
225 if isempty(self.targetJobClass)
226 idx = -1;
227 else
228 idx = self.targetJobClass.index;
229 end
230 end
231
232 function dist = getRemovalDistribution(self)
233 % GETREMOVALDISTRIBUTION Get the removal distribution
234 %
235 % @return dist The discrete distribution for batch removals
236 dist = self.removalDistribution;
237 end
238
239 function self = setRemovalDistribution(self, dist)
240 % SETREMOVALDISTRIBUTION Set the removal distribution
241 %
242 % @param dist DiscreteDistribution for number of removals
243 self.removalDistribution = dist;
244 end
245
246 function policy = getRemovalPolicy(self)
247 % GETREMOVALPOLICY Get the removal policy
248 %
249 % @return policy The RemovalPolicy constant
250 policy = self.removalPolicy;
251 end
252
253 function self = setRemovalPolicy(self, policy)
254 % SETREMOVALPOLICY Set the removal policy
255 %
256 % @param policy RemovalPolicy constant (RANDOM, FCFS, LCFS)
257 self.removalPolicy = policy;
258 end
259
260 function b = isCatastrophe(self)
261 % ISCATASTROPHE Check if this is a catastrophe signal
262 %
263 % @return b true if signalType is SignalType.CATASTROPHE
264 b = (self.signalType == SignalType.CATASTROPHE);
265 end
266
267 end
268
269end
Definition mmt.m:92