LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
JSIM2LINE.m
1function model = JSIM2LINE(filename,modelName)
2% MODEL = JSIM2LINE(FILENAME,MODELNAME)
3
4% Copyright (c) 2012-2026, Imperial College London
5% All rights reserved.
6T0=tic;
7% import model
8Pref.Str2Num = 'always';
9xDoc = xml_read(filename,Pref);
10try
11 xDoc = xDoc.sim;
12end
13
14% create network
15if nargin<2
16 [~,modelName] = fileparts(xDoc.ATTRIBUTE.name);
17end
18model = Network(modelName);
19
20% create stations
21node_name = cellfun(@(x) x.name, {xDoc.node.ATTRIBUTE},'UniformOutput',false)';
22orig_node_name = node_name;
23for i=1:length(node_name)
24 node_name{i}=strrep(node_name{i},'/','_');
25 node_name{i}=strrep(node_name{i},'\','_');
26end
27
28xsection = {xDoc.node.section};
29strategy = cell(1,length(node_name));
30xsection_par = {};
31xsection_i = {};
32xsection_javaClass = {};
33sink_idx = -1;
34source_idx = -1;
35
36% This is to create the cs elements last, unclear if it affects correctness
37% isStation = ones(1,length(node_name));
38% for i=1:length(node_name)
39% xsection_i{i} = {xsection{i}};
40% xsection_i{i} = xsection_i{i}{1}; % input, service, and output sections of node i
41% xsection_class{i} = {xsection_i{i}.ATTRIBUTE};
42% switch xsection_class{i}{1}.className % input section
43% case {'Buffer'}
44% xsection_i_type{i} = {xsection{i}.ATTRIBUTE};
45% switch xsection_i_type{i}{2}.className
46% case {'StatelessClassSwitcher'}
47% isStation(i) = 0;
48% end
49% end
50% end
51
52%for i=[find(isStation==1), find(isStation==0)]
53for i=1:length(node_name)
54 xsection_i{i} = {xsection{i}};
55 xsection_i{i} = xsection_i{i}{1}; % input, service, and output sections of node i
56 xsection_javaClass{i} = {xsection_i{i}.ATTRIBUTE};
57 switch xsection_javaClass{i}{1}.className % input section
58 case 'JobSink'
59 node{i} = Sink(model, node_name{i});
60 sink_idx = i;
61 case 'RandomSource'
62 node{i} = Source(model, node_name{i});
63 source_idx = i;
64 xrouting{i} = {xsection_i{i}(3).parameter.subParameter.ATTRIBUTE};
65 nSources=1;
66 case 'Join'
67 forkMap=find(cellfun(@any,strfind(cellfun(@class,model.nodes,'UniformOutput',false),'Fork')));
68 if length(forkMap)>1
69 line_error(mfilename,'JSIM2LINE supports at most a single fork-join pair.');
70 end
71 node{i} = Join(model, node_name{i}, node{forkMap});
72 xrouting{i} = {xsection_i{i}(3).parameter.subParameter.ATTRIBUTE};
73 case 'Queue'
74 switch xsection_javaClass{i}{3}.className
75 case 'Fork'
76 node{i} = Fork(model, node_name{i});
77 node{i}.setTasksPerLink(xsection_i{i}(3).parameter(1).value); %jobsPerLink
78 xrouting{i} = {xsection_i{i}(3).parameter(4).subParameter.ATTRIBUTE};
79 otherwise
80 switch xsection_javaClass{i}{2}.className
81 case 'ServiceTunnel'
82 node{i} = Router(model, node_name{i});
83 xrouting{i} = {xsection_i{i}(3).parameter.subParameter.ATTRIBUTE};
84 otherwise
85 xsection_par{i} = {xsection{i}.parameter};
86 xsection_i_par{i} = xsection_i{i}.parameter;
87
88 xsection_i_value{i} = {xsection_i_par{i}.value};
89 xsection_i_par_attr{i} = {xsection_i_par{i}.ATTRIBUTE};
90
91 xsection_i_subpar{i} = {xsection_i_par{i}.subParameter};
92 %if xsection_i_value{i}{1}==-1
93 % node{i} = Router(model, node_name{i});
94 %else
95
96 xsvc{i} = {xsection_i{i}(2).parameter.subParameter};
97 xrouting{i} = {xsection_i{i}(3).parameter.subParameter.ATTRIBUTE};
98
99 % xget_strategy{i} = {xsection_i_par{i}.ATTRIBUTE};
100 % switch xget_strategy{i}{3}.name
101 % case 'LCFSstrategy'
102 % strategy{i} = SchedStrategy.LCFS;
103 % case 'FCFSstrategy'
104 % strategy{i} = SchedStrategy.FCFS;
105 % end
106
107 xput_strategy{i} = xsection_i_par{i};
108 switch xput_strategy{i}(3).ATTRIBUTE.name
109 case 'retrialDistributions'
110 % new XML format from 1.2.0
111 %xretrial_strategy{i}= {xput_strategy{i}(4)};
112 xput_strategy{i}= {xput_strategy{i}(5).subParameter.ATTRIBUTE};
113 otherwise
114 xput_strategy{i}= {xput_strategy{i}(4).subParameter.ATTRIBUTE};
115 end
116 switch xput_strategy{i}{1}.name
117 case 'TailStrategy'
118 strategy{i} = SchedStrategy.FCFS;
119 case 'TailStrategyPriority'
120 strategy{i} = SchedStrategy.HOL;
121 case 'HeadStrategy'
122 strategy{i} = SchedStrategy.LCFS;
123 case 'RandStrategy'
124 strategy{i} = SchedStrategy.SIRO;
125 case 'SJFStrategy'
126 strategy{i} = SchedStrategy.SJF;
127 case 'SEPTStrategy'
128 strategy{i} = SchedStrategy.SEPT;
129 case 'LJFStrategy'
130 strategy{i} = SchedStrategy.LJF;
131 case 'LEPTStrategy'
132 strategy{i} = SchedStrategy.LEPT;
133 end
134
135 xsection_i_type{i} = {xsection{i}.ATTRIBUTE};
136 switch xsection_i_type{i}{2}.className
137 case 'Delay'
138 node{i} = Delay(model, node_name{i});
139 xcapacity = {xsection_i_par{i}.value};
140 node{i}.setCapacity(xcapacity{1}); % buffer size
141 case 'Server'
142 node{i} = Queue(model, node_name{i}, strategy{i});
143 xcapacity = {xsection_i_par{i}.value};
144 node{i}.setCapacity(xcapacity{1}); % buffer size
145 xsection_par_val{i} = {xsection_par{end}{2}.value};
146 node{i}.setNumServers(xsection_par_val{i}{1});
147 switch SchedStrategy.toId(strategy{i})
148 case SchedStrategy.SEPT
149 schedparams{i} = NaN;
150 end
151 case 'PSServer' % requires JMT >= 1.0.2
152 strategy_i_sub={xsection_par{i}{2}.subParameter};
153 strategy_i_sub4=strategy_i_sub{4}; strategy_i_sub4={strategy_i_sub4.ATTRIBUTE};
154 strategy_i_sub5=strategy_i_sub{5};
155 schedparams{i} = cell2mat({strategy_i_sub5.value});
156 r=1; % we assume the strategies are identical across classes
157 switch strategy_i_sub4{r}.name
158 case 'EPSStrategy'
159 strategy{i} = SchedStrategy.PS;
160 case 'DPSStrategy'
161 strategy{i} = SchedStrategy.DPS;
162 case 'GPSStrategy'
163 strategy{i} = SchedStrategy.GPS;
164 case 'EPSStrategyPriority'
165 strategy{i} = SchedStrategy.PSPRIO;
166 case 'DPSStrategyPriority'
167 strategy{i} = SchedStrategy.DPSPRIO;
168 case 'GPSStrategyPriority'
169 strategy{i} = SchedStrategy.GPSPRIO;
170 end
171 node{i} = Queue(model, node_name{i}, strategy{i});
172 xcapacity = {xsection_i_par{i}.value};
173 node{i}.setCapacity(xcapacity{1}); % buffer size
174 xsection_par_val{i} = {xsection_par{end}{2}.value};
175 node{i}.setNumServers(xsection_par_val{i}{1});
176 case 'ClassSwitch'
177 strategy_i_sub={xsection_par{i}{2}.subParameter};
178 strategy_i_sub1=strategy_i_sub{1}; strategy_i_sub1={strategy_i_sub1.subParameter};
179 csMatrix = zeros(length(strategy_i_sub1));
180 for r=1:length(strategy_i_sub1)
181 csMatrix(r,:) = cell2mat({strategy_i_sub1{r}.value});
182 end
183 node{i} = ClassSwitch(model, node_name{i}, csMatrix);
184 end
185 end
186 end
187 case 'Storage'
188 node{i} = Place(model, node_name{i});
189 case 'Enabling'
190 node{i} = Transition(model, node_name{i});
191 end
192end
193
194% create classes
195classes = {xDoc.userClass.ATTRIBUTE};
196% JMT uses higher priority value = higher priority, LINE uses lower value = higher priority
197% We need to invert priorities when importing from JMT
198maxPrio = 0;
199for r=1:length(classes)
200 if classes{r}.priority > maxPrio
201 maxPrio = classes{r}.priority;
202 end
203end
204for r=1:length(classes)
205 ref = findstring(node_name,classes{r}.referenceSource);
206 % Invert priority: JMT uses higher=higher, LINE uses lower=higher
207 linePrio = maxPrio - classes{r}.priority;
208 switch classes{r}.type
209 case JobClassType.toText(JobClassType.CLOSED)
210 jobclass{r} = ClosedClass(model, classes{r}.name, classes{r}.customers, node{ref}, linePrio);
211 case JobClassType.toText(JobClassType.OPEN)
212 % sink and source have been created before
213 jobclass{r} = OpenClass(model, classes{r}.name, linePrio);
214 if strcmpi(classes{r}.referenceSource,'StatelessClassSwitcher')
215 sourceIdx = cellisa(node,'Source');
216 node{sourceIdx}.setArrival(jobclass{r},Disabled.getInstance());
217 end
218 end
219end
220
221
222for i=1:length(node_name)
223 xsection_i{i} = {xsection{i}};
224 xsection_i{i} = xsection_i{i}{1}; % input, service, and output sections of node i
225 xsection_javaClass{i} = {xsection_i{i}.ATTRIBUTE};
226 switch xsection_javaClass{i}{1}.className % input section
227
228 case 'Storage'
229 node{i}.init();
230 if xsection_i{1,i}(1).parameter(1).value == -1
231 node{i}.setCapacity(Inf);
232 else
233 node{i}.setCapacity(xsection_i{1,i}(1).parameter(1).value);
234 end
235 if isa(xsection_i{1, i}(1).parameter(2).refClass,'cell')
236 nclasses = length(xsection_i{1, i}(1).parameter(2).refClass);
237 else
238 nclasses = 1;
239 end
240 for c=1:nclasses
241 if xsection_i{1, i}(1).parameter(2).subParameter(c).value == -1
242 node{i}.setClassCapacity(c, Inf);
243 else
244 node{i}.setClassCapacity(c, xsection_i{1, i}(1).parameter(2).subParameter(c).value);
245 end
246 switch xsection_i{1, i}(1).parameter(3).subParameter(c).value
247 case 'BAS blocking'
248 node{i}.setDropRule(c, DropStrategy.BAS);
249 case 'drop'
250 node{i}.setDropRule(c, DropStrategy.DROP);
251 case 'waiting queue'
252 node{i}.setDropRule(c, DropStrategy.WAITQ);
253 end
254 end
255 node{i}.setState(0);
256
257 case 'Enabling'
258 % Enabling Section
259 nmodes = length(xsection_i{1, i}(1).parameter(1).subParameter);
260 for m=1:nmodes
261 node{i}.setModeNames(m, xsection_i{1, i}(2).parameter(1).subParameter(m).value);
262 end
263 node{i}.init();
264 for m=1:nmodes
265 ninputs = length(xsection_i{1, i}(1).parameter(1).subParameter(m).subParameter.subParameter);
266 for j=1:ninputs
267 refClasses = xsection_i{1, i}(1).parameter(1).subParameter(m).subParameter.subParameter(j).subParameter(2).refClass;
268 if isa(refClasses,'cell')
269 nclasses = length(refClasses);
270 else
271 nclasses = 1;
272 end
273 nodeName = xsection_i{1, i}(1).parameter(1).subParameter(m).subParameter.subParameter(j).subParameter(1).value;
274 targetNode = model.getNodeByName(nodeName);
275 for k=1:nclasses
276 enable = xsection_i{1, i}(1).parameter(1).subParameter(m).subParameter.subParameter(j).subParameter(2).subParameter(k).value;
277 if enable == -1
278 node{i}.setEnablingConditions(m,k,targetNode,Inf);
279 else
280 node{i}.setEnablingConditions(m,k,targetNode,enable);
281 end
282 inhibit = xsection_i{1, i}(1).parameter(2).subParameter(m).subParameter.subParameter(j).subParameter(2).subParameter(k).value;
283 if inhibit == -1 || inhibit == 0
284 % JMT encodes "no inhibitor arc" as 0 (see the
285 % Inf->0 mapping in saveInhibitingConditions.m); a
286 % threshold of 0 would otherwise inhibit at >=0
287 % tokens, i.e. always, deadlocking the transition.
288 node{i}.setInhibitingConditions(m,k,targetNode,Inf);
289 else
290 node{i}.setInhibitingConditions(m,k,targetNode,inhibit);
291 end
292 end
293 end
294 end
295 % Timing Section
296 for m=1:nmodes
297 numOfServers = xsection_i{1, i}(2).parameter(2).subParameter(m).value;
298 if numOfServers == -1
299 node{i}.setNumberOfServers(m, Inf);
300 else
301 node{i}.setNumberOfServers(m, numOfServers);
302 end
303 timingSt = xsection_i{1, i}(2).parameter(3).subParameter(m).ATTRIBUTE.classPath;
304 if strcmp(timingSt,'jmt.engine.NetStrategies.ServiceStrategies.ZeroServiceTimeStrategy')
305 node{i}.setTimingStrategy(m,TimingStrategy.IMMEDIATE);
306 else
307 node{i}.setTimingStrategy(m,TimingStrategy.TIMED);
308 distribution = xsection_i{1, i}(2).parameter(3).subParameter(m).subParameter(1).ATTRIBUTE.name;
309 lambda = xsection_i{1, i}(2).parameter(3).subParameter(m).subParameter(2).subParameter(1).value;
310 switch distribution
311 case 'Exponential'
312 node{i}.setDistribution(m,Exp(lambda));
313 case 'Erlang'
314 lambda1 = xsection_i{1, i}(2).parameter(3).subParameter(m).subParameter(2).subParameter(2).value;
315 node{i}.setDistribution(m,Erlang(lambda, lambda1));
316 case 'Hyperexponential'
317 lambda1 = xsection_i{1, i}(2).parameter(3).subParameter(m).subParameter(2).subParameter(2).value;
318 lambda2 = xsection_i{1, i}(2).parameter(3).subParameter(m).subParameter(2).subParameter(3).value;
319 node{i}.setDistribution(m,HyperExp(lambda, lambda1, lambda2));
320 case 'Coxian'
321 lambda1 = xsection_i{1, i}(2).parameter(3).subParameter(m).subParameter(2).subParameter(2).value;
322 lambda2 = xsection_i{1, i}(2).parameter(3).subParameter(m).subParameter(2).subParameter(3).value;
323 node{i}.setDistribution(m,Coxian([lambda, lambda1], [lambda2,1]));
324 case 'Deterministic'
325 node{i}.setDistribution(m,Det(lambda));
326 case 'Pareto'
327 lambda1 = xsection_i{1, i}(2).parameter(3).subParameter(m).subParameter(2).subParameter(2).value;
328 node{i}.setDistribution(m,Pareto(lambda, lambda1));
329 case 'Gamma'
330 lambda1 = xsection_i{1, i}(2).parameter(3).subParameter(m).subParameter(2).subParameter(2).value;
331 node{i}.setDistribution(m,Gamma(lambda, lambda1));
332 case 'Uniform'
333 lambda1 = xsection_i{1, i}(2).parameter(3).subParameter(m).subParameter(2).subParameter(2).value;
334 node{i}.setDistribution(m,Uniform(lambda, lambda1));
335 case 'Replayer'
336 node{i}.setDistribution(m,Replayer(lambda));
337 case 'Trace'
338 node{i}.setDistribution(m,Trace(lambda));
339 case 'Weibull'
340 lambda1 = xsection_i{1, i}(2).parameter(3).subParameter(m).subParameter(2).subParameter(2).value;
341 node{i}.setDistribution(m,Weibull(lambda1, lambda)); % scale and shape are inverted in the constructor
342 case 'Lognormal'
343 lambda1 = xsection_i{1, i}(2).parameter(3).subParameter(m).subParameter(2).subParameter(2).value;
344 node{i}.setDistribution(m,Lognormal(lambda, lambda1));
345 otherwise
346 error('The model includes an arrival distribution not supported by the model-to-model transformation from JMT.')
347 end
348 end
349 firingPriorities = xsection_i{1, i}(2).parameter(4).subParameter(m).value;
350 node{i}.setFiringPriorities(m, firingPriorities);
351 firingWeights = xsection_i{1, i}(2).parameter(5).subParameter(m).value;
352 node{i}.setFiringWeights(m, firingWeights);
353 end
354 % Firing Section
355 for m=1:nmodes
356 if isfield(xsection_i{1, i}(3).parameter(1).subParameter(m).subParameter,'CONTENT')
357 noutputs = 0;
358 else
359 noutputs = length(xsection_i{1, i}(3).parameter(1).subParameter(m).subParameter.subParameter);
360 end
361 for j=1:noutputs
362 refClasses = xsection_i{1, i}(3).parameter(1).subParameter(m).subParameter.subParameter(j).subParameter(2).refClass;
363 if isa(refClasses, 'cell')
364 nclasses = length(refClasses);
365 else
366 nclasses = 1;
367 end
368 nodeName = xsection_i{1, i}(3).parameter(1).subParameter(m).subParameter.subParameter(j).subParameter(1).value;
369 for k=1:nclasses
370 outcome = xsection_i{1, i}(3).parameter(1).subParameter(m).subParameter.subParameter(j).subParameter(2).subParameter(k).value;
371 if outcome == -1
372 node{i}.setFiringOutcome(m,k,nodeName,Inf);
373 else
374 node{i}.setFiringOutcome(m,k,nodeName,outcome);
375 end
376 end
377 end
378 end
379 end
380end
381
382schedparams = cell(1,length(node_name));
383% set service distributions
384for i=1:length(node_name)
385 if isa(node{i},'Source')
386 for r=1:length(classes)
387 xsection_par{i} = {xsection{i}.parameter};
388 xsection_i_par{i} = xsection_i{i}.parameter;
389 xsection_i_subpar{i} = {xsection_i_par{i}.subParameter};
390 xarv_statdistrib{i}{r}={xsection_i_subpar{i}{1}.subParameter};
391 if isempty(xarv_statdistrib{i}{r}{r})
392 node{i}.setArrival(jobclass{r}, Disabled.getInstance());
393 else
394 xarv_statdistrib{i}{r}={xarv_statdistrib{i}{r}{r}.ATTRIBUTE};
395 xarv{i} = {xsection_i{i}(1).parameter.subParameter};
396 xarv_sec{i} = {xarv{i}{1}.subParameter};
397 switch xarv_statdistrib{i}{r}{1}.name
398 case 'Exponential'
399 par={xarv_sec{i}{r}.subParameter}; par=par{2};
400 node{i}.setArrival(jobclass{r}, Exp(par.value));
401 case 'Erlang'
402 par={xarv_sec{i}{r}.subParameter}; par=par{2};
403 node{i}.setArrival(jobclass{r}, Erlang(par(1).value,par(2).value));
404 case 'Hyperexponential'
405 par={xarv_sec{i}{r}.subParameter}; par=par{2};
406 node{i}.setArrival(jobclass{r}, HyperExp(par(1).value,par(2).value,par(3).value));
407 case 'Coxian'
408 par={xarv_sec{i}{r}.subParameter}; par=par{2};
409 node{i}.setArrival(jobclass{r}, Coxian([par(1).value,par(2).value],[par(3).value,1]));
410 case 'Deterministic'
411 par={xarv_sec{i}{r}.subParameter}; par=par{2};
412 node{i}.setArrival(jobclass{r}, Det(par.value));
413 case 'Pareto'
414 par={xarv_sec{i}{r}.subParameter}; par=par{2};
415 node{i}.setArrival(jobclass{r}, Pareto(par(1).value, par(2).value));
416 case 'Weibull'
417 par={xarv_sec{i}{r}.subParameter}; par=par{2};
418 node{i}.setArrival(jobclass{r}, Weibull(par(1).value, par(2).value));
419 case 'Lognormal'
420 par={xarv_sec{i}{r}.subParameter}; par=par{2};
421 node{i}.setArrival(jobclass{r}, Lognormal(par(1).value, par(2).value));
422 case 'Gamma'
423 par={xarv_sec{i}{r}.subParameter}; par=par{2};
424 node{i}.setArrival(jobclass{r}, Gamma(par(1).value, par(2).value));
425 case 'Uniform'
426 par={xarv_sec{i}{r}.subParameter}; par=par{2};
427 node{i}.setArrival(jobclass{r}, Uniform(par(1).value, par(2).value));
428 case 'Replayer'
429 par={xarv_sec{i}{r}.subParameter}; par=par{2};
430 node{i}.setArrival(jobclass{r}, Replayer(par.value));
431 case 'Trace'
432 par={xarv_sec{i}{r}.subParameter}; par=par{2};
433 node{i}.setArrival(jobclass{r}, Trace(par.value));
434 case 'Burst (MMPP2)'
435 par={xarv_sec{i}{r}.subParameter}; par=par{2};
436 node{i}.setArrival(jobclass{r}, MMPP2(par(1).value,par(2).value,par(3).value,par(4).value));
437 case 'Burst (MAP)'
438 par={xarv_sec{i}{r}.subParameter}; par=par{2};
439 pars = {par(1).subParameter.subParameter};
440 D0 = [];
441 for c=1:length(pars)
442 D0 = [D0; pars{c}.value];
443 end
444 pars = {par(2).subParameter.subParameter};
445 D1 = [];
446 for c=1:length(pars)
447 D1 = [D1; pars{c}.value];
448 end
449 ax = MAP(D0,D1);
450 node{i}.setArrival(jobclass{r}, ax);
451 case 'Phase-Type'
452 par={xarv_sec{i}{r}.subParameter}; par=par{2};
453 alpha = [par(1).subParameter.subParameter.value];
454 pars = {par(2).subParameter.subParameter};
455 T = [];
456 for c=1:length(pars)
457 T = [T; pars{c}.value];
458 end
459 if any(any(tril(T,-1))>0) % not APH, use general PH
460 ax = PH(alpha, T);
461 else % APH
462 ax = APH(alpha, T);
463 end
464 node{i}.setArrival(jobclass{r}, ax);
465 otherwise
466 line_warning(mfilename,'The model includes an arrival distribution (%s) not directly supported by the model-to-model transformation from JMT. Attempting APH moment-matching.', xarv_statdistrib{i}{r}{1}.name);
467 try
468 par={xarv_sec{i}{r}.subParameter}; par=par{2};
469 mean_val = 1/par(1).value; % JMT stores rate (lambda) as first parameter
470 if length(par) >= 2
471 scv_val = par(2).value^2; % JMT stores c (CoV) as second parameter
472 node{i}.setArrival(jobclass{r}, APH.fit(mean_val, scv_val));
473 else
474 node{i}.setArrival(jobclass{r}, Exp(par(1).value));
475 end
476 catch me
477 line_warning(mfilename,'APH moment-matching failed for arrival distribution %s: %s. Using Exp(1) as fallback.', xarv_statdistrib{i}{r}{1}.name, me.message);
478 node{i}.setArrival(jobclass{r}, Exp(1));
479 end
480 end
481 end
482 end
483 elseif isa(node{i},'Queue') || isa(node{i},'Delay') || isa(node{i},'DelayStation')
484 if isempty(schedparams{i})
485 switch SchedStrategy.toId(strategy{i})
486 case {SchedStrategy.SEPT,SchedStrategy.LEPT}
487 schedparams{i} = NaN*ones(1,length(classes));
488 otherwise
489 schedparams{i} = ones(1,length(classes));
490 end
491 end
492 for r=1:length(classes)
493 switch xsection_i_type{i}{2}.className
494 case 'StatelessClassSwitcher'
495 % do nothing
496 continue
497 case 'Delay'
498 xsvc_sec{i} = {xsvc{i}{1}.subParameter};
499 otherwise
500 xsvc_sec{i} = {xsvc{i}{3}.subParameter};
501 end
502 if isempty(xsvc_sec{i}{r})
503 xsvc_statdistrib{i}{r}={struct('name','Disabled')};
504 else
505 xsvc_statdistrib{i}{r}={xsvc_sec{i}{r}.ATTRIBUTE};
506 end
507 para_ir = schedparams{i}(r);
508 switch xsvc_statdistrib{i}{r}{1}.name
509 case 'Disabled'
510 node{i}.setService(jobclass{r}, Disabled.getInstance());
511 case 'Replayer'
512 par={xsvc_sec{i}{r}.subParameter}; par=par{2};
513 node{i}.setService(jobclass{r}, Replayer(par.value), para_ir);
514 case 'Trace'
515 par={xsvc_sec{i}{r}.subParameter}; par=par{2};
516 node{i}.setService(jobclass{r}, Trace(par.value), para_ir);
517 case 'Exponential'
518 par={xsvc_sec{i}{r}.subParameter}; par=par{2};
519 node{i}.setService(jobclass{r}, Exp(par.value), para_ir);
520 case 'Erlang'
521 par={xsvc_sec{i}{r}.subParameter}; par=par{2};
522 node{i}.setService(jobclass{r}, Erlang(par(1).value,par(2).value), para_ir);
523 case 'Hyperexponential'
524 par={xsvc_sec{i}{r}.subParameter}; par=par{2};
525 node{i}.setService(jobclass{r}, HyperExp(par(1).value,par(2).value,par(3).value), para_ir);
526 case 'Coxian'
527 par={xsvc_sec{i}{r}.subParameter}; par=par{2};
528 node{i}.setService(jobclass{r}, Coxian([par(1).value,par(2).value],[par(3).value,1]), para_ir);
529 case 'Deterministic'
530 par={xsvc_sec{i}{r}.subParameter}; par=par{2};
531 node{i}.setService(jobclass{r}, Det(par.value));
532 case 'Pareto'
533 par={xsvc_sec{i}{r}.subParameter}; par=par{2};
534 node{i}.setService(jobclass{r}, Pareto(par(1).value, par(2).value));
535 case 'Weibull'
536 par={xsvc_sec{i}{r}.subParameter}; par=par{2};
537 node{i}.setService(jobclass{r}, Weibull(par(1).value, par(2).value));
538 case 'Lognormal'
539 par={xsvc_sec{i}{r}.subParameter}; par=par{2};
540 node{i}.setService(jobclass{r}, Lognormal(par(1).value, par(2).value));
541 case 'Gamma'
542 par={xsvc_sec{i}{r}.subParameter}; par=par{2};
543 node{i}.setService(jobclass{r}, Gamma(par(1).value, par(2).value));
544 case 'Burst (MMPP2)'
545 par={xsvc_sec{i}{r}.subParameter}; par=par{2};
546 node{i}.setService(jobclass{r}, MMPP2(par(1).value,par(2).value,par(3).value,par(4).value));
547 case 'Burst (MAP)'
548 par={xsvc_sec{i}{r}.subParameter}; par=par{2};
549 pars = {par(1).subParameter.subParameter};
550 D0 = [];
551 for c=1:length(pars)
552 D0 = [D0; pars{c}.value];
553 end
554 pars = {par(2).subParameter.subParameter};
555 D1 = [];
556 for c=1:length(pars)
557 D1 = [D1; pars{c}.value];
558 end
559 ax = MAP(D0,D1);
560 node{i}.setService(jobclass{r}, ax);
561 case 'Phase-Type'
562 par={xsvc_sec{i}{r}.subParameter}; par=par{2};
563 alpha = [par(1).subParameter.subParameter.value];
564 pars = {par(2).subParameter.subParameter};
565 T = [];
566 for c=1:length(pars)
567 T = [T; pars{c}.value];
568 end
569 if any(any(tril(T,-1))>0) % not APH, use general PH
570 ax = PH(alpha, T);
571 else % APH
572 ax = APH(alpha, T);
573 end
574 node{i}.setService(jobclass{r}, ax);
575 case 'Uniform'
576 par={xsvc_sec{i}{r}.subParameter}; par=par{2};
577 node{i}.setService(jobclass{r}, Uniform(par(1).value, par(2).value));
578 otherwise
579 line_warning(mfilename,'The model includes a service distribution (%s) not directly supported by the model-to-model transformation from JMT. Attempting APH moment-matching.', xsvc_statdistrib{i}{r}{1}.name);
580 try
581 par={xsvc_sec{i}{r}.subParameter}; par=par{2};
582 mean_val = 1/par(1).value; % JMT stores rate (lambda) as first parameter
583 if length(par) >= 2
584 scv_val = par(2).value^2; % JMT stores c (CoV) as second parameter
585 node{i}.setService(jobclass{r}, APH.fit(mean_val, scv_val), para_ir);
586 else
587 node{i}.setService(jobclass{r}, Exp(par(1).value), para_ir);
588 end
589 catch me
590 line_warning(mfilename,'APH moment-matching failed for service distribution %s: %s. Using Exp(1) as fallback.', xsvc_statdistrib{i}{r}{1}.name, me.message);
591 node{i}.setService(jobclass{r}, Exp(1), para_ir);
592 end
593 end
594 end
595 for c=1:length(xsection_i_par_attr{i})
596 switch xsection_i_par_attr{i}{c}.name
597 case 'size'
598 node{i}.input.setSize(xsection_i_value{i}{c}); % buffer size
599 end
600 end
601 end
602end
603
604% create links
605C = zeros(length(node_name)); % connection matrix
606links = {xDoc.connection.ATTRIBUTE};
607for l=1:length(links)
608 source = findstring(orig_node_name,links{l}.source);
609 target = findstring(orig_node_name,links{l}.target);
610 % model.addLink(station{source},station{target});
611 C(source,target) = 1;
612end
613
614% assign routing probabilities
615P = zeros(length(node_name)*length(classes));
616for from=1:length(node_name)
617 for target=1:length(node_name)
618 if C(from,target)
619 model.addLink(node{from},node{target});
620 end
621 end
622end
623
624for from=1:length(node_name)
625 switch class(node{from})
626 case 'Place'
627 case 'Transition'
628 case 'Sink'
629 otherwise
630 for r=1:length(classes)
631 switch xrouting{from}{r}.name
632 case 'Random'
633 node{from}.setRouting(jobclass{r},RoutingStrategy.RAND);
634 % targets = find(C(from,:));
635 % if isa(jobclass{r},'Class')
636 % targets = setdiff(targets, [sink_idx, source_idx]);
637 % end
638 % for target = targets(:)'
639 % % node{from}.setProbRouting(jobclass{r}, node{target}, 1 / length(targets));
640 % P((from-1)*length(classes)+r, (target-1)*length(classes)+r) = 1 / length(targets);
641 % end
642 case 'Probabilities'
643 node{from}.setRouting(jobclass{r},RoutingStrategy.PROB);
644 xroutprobarray = {xsection_i{from}(3).parameter.subParameter.subParameter};
645 xroutprob = {xroutprobarray{r}.subParameter}; xroutprob = xroutprob{1};
646 xroutprobdest = {xroutprob.subParameter};
647 for j=1:length(xroutprobdest)
648 xprob={xroutprobdest{j}.value};
649 target = findstring(node_name,xprob{1});
650 prob = xprob{2};
651 node{from}.setProbRouting(jobclass{r}, node{target}, prob);
652 % P((from-1)*length(classes)+r, (target-1)*length(classes)+r) = prob;
653 end
654 case 'Power of k'
655 % Both parameters are read by NAME off class r's own
656 % strategy node. The exporter (JMTIO.saveRoutingStrategy)
657 % writes two children per class, <k> then <withMemory>,
658 % so flattening parameter.subParameter.subParameter and
659 % indexing it by r conflates the child position with the
660 % class index and can never reach withMemory.
661 k = 2; % JMT default when <k> is absent
662 withMemory = false; % JMT default when <withMemory> is absent
663 xstrat = xsection_i{from}(3).parameter.subParameter(r);
664 if isfield(xstrat,'subParameter')
665 xpars = xstrat.subParameter;
666 for pj = 1:numel(xpars)
667 if ~isfield(xpars(pj),'ATTRIBUTE') || ~isfield(xpars(pj),'value')
668 continue
669 end
670 switch xpars(pj).ATTRIBUTE.name
671 case 'k'
672 k = xpars(pj).value;
673 if ischar(k)
674 k = str2double(k);
675 end
676 case 'withMemory'
677 % A LINE-written file parses as logical,
678 % a hand-written or older JMT one as the
679 % text 'true'/'false'.
680 wm = xpars(pj).value;
681 if ischar(wm)
682 withMemory = strcmpi(strtrim(wm),'true');
683 else
684 withMemory = logical(wm);
685 end
686 end
687 end
688 end
689 node{from}.setRouting(jobclass{r}, RoutingStrategy.KCHOICES, k, withMemory);
690 case 'Round Robin'
691 node{from}.setRouting(jobclass{r},RoutingStrategy.RROBIN);
692 case 'Weighted Round Robin'
693 node{from}.setRouting(jobclass{r},RoutingStrategy.WRROBIN);
694 xroutprobarray = {xsection_i{from}(3).parameter.subParameter.subParameter};
695 xroutprob = {xroutprobarray{r}.subParameter}; xroutprob = xroutprob{1};
696 xroutprobdest = {xroutprob.subParameter};
697 for j=1:length(xroutprobdest)
698 xprob={xroutprobdest{j}.value};
699 target = findstring(node_name,xprob{1});
700 weight = xprob{2};
701 node{from}.setRouting(RoutingStrategy.WRROBIN, node{target}, jobclass{r}, weight);
702 end
703 case 'Join the Shortest Queue (JSQ)'
704 node{from}.setRouting(jobclass{r},RoutingStrategy.JSQ);
705 case 'Disabled'
706 node{from}.setRouting(jobclass{r},RoutingStrategy.DISABLED);
707 end
708 end
709 end
710end
711%model.link(P);
712%line_printf(['JMT2LINE parsing time: ',num2str(Ttot),' s\n']);
713
714if length(model.getIndexSourceStation)>1
715 txt = sprintf('LINE supports JMT models with at most a single source node. You can refactor your JMT model in several ways:\n - If you are mapping in JMT each class to a different source, this is not required. You can instead assign the same reference station to each class and configure class routing in the routing panel of the source node.\n - In more general cases, you may follow these three steps:\n (1) give a different name to each class of arrival, assigning these classes to a single source as reference station.\n (2) put a class-switch node after the source to switch the new classes into the original classes they were in the model with multiple sources.\n (3) configure the routing section of this class-switch node to set the same routing for the classes as they were in the original model.\n');
716 error(txt);
717end
718
719
720% Preload
721state = zeros(length(node),length(classes));
722if isfield(xDoc,'preload') && ~isempty(xDoc.preload.stationPopulations)
723 npreloadStates = length(xDoc.preload.stationPopulations);
724 for st=1:npreloadStates
725 nodeName = xDoc.preload.stationPopulations(st).ATTRIBUTE.stationName;
726 ind = model.getNodeIndex(nodeName);
727 for r=1:length(xDoc.preload.stationPopulations(st).classPopulation)
728 c = model.getClassIndex(xDoc.preload.stationPopulations(st).classPopulation(r).ATTRIBUTE.refClass);
729 state(ind,c) = xDoc.preload.stationPopulations(st).classPopulation(r).ATTRIBUTE.population;
730 end
731 if isa(node{ind},'Place')
732 %node{ind}.setState(state(ind,:));
733 if length(classes)>1
734 line_error(mfilename,'Import failed: Colored Petri net models are not yet supported in LINE.\n');
735 end
736 end
737 end
738end
739try
740 model.initFromMarginal(state);
741catch
742 line_warning(mfilename,'Import failed to automatically initialize the model.\n');
743end
744Ttot=toc(T0);
745end
Definition Station.m:245