Passed
Push — master ( c71db0...37262d )
by Alessandro
05:46 queued 14s
created

doFindFeasibleSchedule(ReservoirOverflow)   A

Complexity

Conditions 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
package it.cnr.istc.pst.platinum.ai.framework.microkernel.resolver.resource.reservoir;
2
3
4
import java.util.ArrayList;
5
import java.util.Collections;
6
import java.util.List;
7
8
import it.cnr.istc.pst.platinum.ai.framework.domain.component.Decision;
9
import it.cnr.istc.pst.platinum.ai.framework.domain.component.DomainComponent;
10
import it.cnr.istc.pst.platinum.ai.framework.domain.component.ex.FlawSolutionApplicationException;
11
import it.cnr.istc.pst.platinum.ai.framework.domain.component.ex.RelationPropagationException;
12
import it.cnr.istc.pst.platinum.ai.framework.domain.component.ex.ResourceProfileComputationException;
13
import it.cnr.istc.pst.platinum.ai.framework.domain.component.resource.ResourceEvent;
14
import it.cnr.istc.pst.platinum.ai.framework.domain.component.resource.reservoir.ReservoirResource;
15
import it.cnr.istc.pst.platinum.ai.framework.domain.component.resource.reservoir.ReservoirResourceProfile;
16
import it.cnr.istc.pst.platinum.ai.framework.domain.component.resource.reservoir.ResourceUsageProfileSample;
17
import it.cnr.istc.pst.platinum.ai.framework.microkernel.lang.ex.ConsistencyCheckException;
18
import it.cnr.istc.pst.platinum.ai.framework.microkernel.lang.flaw.Flaw;
19
import it.cnr.istc.pst.platinum.ai.framework.microkernel.lang.flaw.FlawSolution;
20
import it.cnr.istc.pst.platinum.ai.framework.microkernel.lang.relations.Relation;
21
import it.cnr.istc.pst.platinum.ai.framework.microkernel.lang.relations.RelationType;
22
import it.cnr.istc.pst.platinum.ai.framework.microkernel.lang.relations.temporal.BeforeRelation;
23
import it.cnr.istc.pst.platinum.ai.framework.microkernel.resolver.Resolver;
24
import it.cnr.istc.pst.platinum.ai.framework.microkernel.resolver.ResolverType;
25
import it.cnr.istc.pst.platinum.ai.framework.microkernel.resolver.ex.UnsolvableFlawException;
26
import it.cnr.istc.pst.platinum.ai.framework.time.TemporalInterval;
27
import it.cnr.istc.pst.platinum.ai.framework.time.ex.TemporalConstraintPropagationException;
28
import it.cnr.istc.pst.platinum.ai.framework.time.lang.TemporalConstraintType;
29
import it.cnr.istc.pst.platinum.ai.framework.time.lang.allen.BeforeIntervalConstraint;
30
import it.cnr.istc.pst.platinum.ai.framework.utils.properties.FilePropertyReader;
31
32
/**
33
 * 
34
 * @author alessandro
35
 *
36
 */
37
public class ReservoirResourceSchedulingResolver extends Resolver<ReservoirResource> 
38
{
39
	boolean load;
40
	private double schedulingCost;
41
	
42
	/**
43
	 * 
44
	 */
45
	protected ReservoirResourceSchedulingResolver() {
46
		super(ResolverType.RESERVOIR_RESOURCE_SCHEDULING_RESOLVER.getLabel(),
47
				ResolverType.RESERVOIR_RESOURCE_SCHEDULING_RESOLVER.getFlawTypes());
48
		// set load flag
49
		this.load = false;
50
	}
51
	
52
	/**
53
	 * 
54
	 */
55
	private void load() {
56
		// get deliberative property file
57
		FilePropertyReader properties = new FilePropertyReader(
58
				FRAMEWORK_HOME + FilePropertyReader.DEFAULT_DELIBERATIVE_PROPERTY);
59
		// get weight
60
		this.schedulingCost = Double.parseDouble(properties.getProperty("scheduling-cost"));
61
		// set flag
62
		this.load = true;
63
	}
64
	
65
	/**
66
	 * 
67
	 */
68
	@Override
69
	protected List<Flaw> doFindFlaws() 
70
	{
71
		// check load
72
		if (!this.load) {
73
			this.load();
74
		}
75
		
76
		// list of flaws
77
		List<Flaw> flaws = new ArrayList<>();
78
		try
79
		{
80
			// check pessimistic resource profile
81
			ReservoirResourceProfile prp = this.component.
82
					computePessimisticResourceProfile();
83
			/*
84
			 * Analyze the pessimistic profile and find peaks if 
85
			 * any and generate production checkpoints
86
			 */
87
			flaws = this.doComputeProfileOverflows(prp);
88
		}
89
		catch (ResourceProfileComputationException ex) {
90
			// profile computation error
91
			throw new RuntimeException("Resource profile computation error:\n- " + ex.getMessage() + "\n");
0 ignored issues
show
Best Practice introduced by
Dedicated exceptions should be preferred over throwing the generic Exception.
Loading history...
92
		}
93
		
94
		// get list of flaws detected
95
		return flaws;
96
	}
97
	
98
	/**
99
	 * 
100
	 */
101
	@Override
102
	protected void doComputeFlawSolutions(Flaw flaw) 
103
			throws UnsolvableFlawException 
104
	{
105
		// check flaw type
106
		switch (flaw.getType()) 
107
		{
108
			// resource peak
109
			case RESERVOIR_OVERFLOW : 
110
			{
111
				// get peak
112
				ReservoirOverflow overflow = (ReservoirOverflow) flaw;
113
				// solvable condition
114
				boolean solvable = false;
115
				// check the size of the critical set
116
				if (overflow.getCriticalSet().size() > 1) {
117
					// check if solvable through scheduling - at least one production and one consumption are needed
118
					solvable = !overflow.getProductions().isEmpty() && !overflow.getConsumptions().isEmpty();
119
				}
120
				
121
				// check if solvable
122
				if (solvable) {
123
					
124
					// find a feasible solution if any
125
					this.doFindFeasibleSchedule(overflow);
126
				}
127
			}
128
			break;
129
			
130
			default : {
131
				warning("Resolver [" + this.getClass().getName() + "] cannor resolver flaw of type " + flaw.getType() + "\n");
132
			}
133
		}
134
		
135
		// check solutions found
136
		if (flaw.getSolutions().isEmpty()) {
137
			throw new UnsolvableFlawException("No feasible solutions found the following peak on reservoir resource \"" + this.component.getName() + "\":\n- flaw: " + flaw + "\n");
138
		}
139
	}
140
	
141
	
142
	/**
143
	 * 
144
	 * @param schedule
145
	 * @param initialLevel
146
	 * @return
147
	 */
148
	private boolean checkCapacityFeasibility(List<ResourceEvent<?>> schedule, double initialLevel)
149
	{
150
		// feasibility flag
151
		boolean feasible = true;
152
		// level of resource
153
		double currentLevel = initialLevel;
154
		// check resource level resulting from the schedule
155
		for (int index = 0; index < schedule.size() && feasible; index++) {
156
			// get event
157
			ResourceEvent<?> event = schedule.get(index);
158
			// update the current level
159
			currentLevel += event.getAmount();
160
			
161
			// check feasibility
162
			feasible = currentLevel >= this.component.getMinCapacity() && 
163
					currentLevel <= this.component.getMaxCapacity();
164
		}
165
		
166
		// check feasibility
167
		if (!feasible) {
168
			// log data
169
			debug("Component [" + this.label + "] capacity unfeasible schedule:\n"
170
					+ "- potential schedule critical set: " + schedule + "\n");
171
		}
172
		
173
		// get feasibility flag
174
		return feasible;
175
	}
176
	
177
	
178
	/**
179
	 * 
180
	 * @param schedule
181
	 * @return
182
	 */
183
	private boolean checkTemporalFeasibility(List<ResourceEvent<?>> schedule) {
184
		// feasibility flag
185
		boolean feasible = true;
186
		// list of propagated constraints
187
		List<BeforeIntervalConstraint> committed = new ArrayList<>();
188
		
189
		// check pairs of events 
190
		for (int index = 0; index < schedule.size() - 1 && feasible; index++)
191
		{
192
			try
193
			{
194
				// get events
195
				ResourceEvent<?> e1 = schedule.get(index);
196
				ResourceEvent<?> e2 = schedule.get(index + 1);
197
				
198
				// get associated tokens and temporal intervals to check schedule feasibility
199
				TemporalInterval i1 = e1.getDecision().getToken().getInterval();
200
				TemporalInterval i2 = e2.getDecision().getToken().getInterval();
201
				
202
				// create precedence constraint "i1 < i2"
203
				BeforeIntervalConstraint before = this.tdb.createTemporalConstraint(
204
						TemporalConstraintType.BEFORE);
205
				
206
				// set constraint data
207
				before.setReference(i1);
208
				before.setTarget(i2);
209
				before.setLowerBound(0);
210
				before.setUpperBound(this.tdb.getHorizon());
211
				
212
				// propagate constraint
213
				this.tdb.propagate(before);
214
				// add constraints to committed
215
				committed.add(before);
216
				
217
				// check temporal feasibility
218
				this.tdb.verify();
219
			}
220
			catch (TemporalConstraintPropagationException | ConsistencyCheckException ex) {
221
				// not feasible schedule
222
				feasible = false;
223
				// log data
224
				debug("Component [" + this.label + "] temporally unfeasible schedule:\n"
225
						+ "- potential schedule critical set: " + schedule + "\n");
226
			}
227
			finally 
228
			{
229
				// retract all committed constraints
230
				for (BeforeIntervalConstraint before : committed) {
231
					// retract temporal constraint
232
					this.tdb.retract(before);
233
				}
234
			}
235
		}
236
		
237
		// get feasibility flag
238
		return feasible;
239
		
240
	}
241
	
242
	/**
243
	 * 
244
	 * @param overflow
245
	 */
246
	protected void doFindFeasibleSchedule(ReservoirOverflow overflow) {
247
		// get the critical set
248
		List<ResourceEvent<?>> cs = overflow.getCriticalSet();
249
		// shuffle the list to increase the variability of considered schedules
250
		Collections.shuffle(cs);
251
		// start looking for a feasible schedule recursively 
252
		this.doFindFeasibleSchedule(new ArrayList<>(), cs, overflow);
253
	}
254
	
255
	/**
256
	 * 
257
	 * @param schedule
258
	 * @param cs
259
	 * @param overflow
260
	 */
261
	private void doFindFeasibleSchedule(List<ResourceEvent<?>> schedule, List<ResourceEvent<?>> cs, ReservoirOverflow overflow) 
262
	{
263
		// check if a schedule is ready
264
		if (cs.isEmpty()) 
265
		{
266
			// check built permutation as possible schedule of the critical set
267
			debug("Component [" + this.label + "] check feasibility of schedule:\n"
268
					+ "- scheduled critical set: " + schedule + "\n");
269
			
270
			// check schedule resource feasibility first and then temporal feasibility
271
			if (this.checkCapacityFeasibility(schedule, overflow.getInitialLevel()) && 
272
					this.checkTemporalFeasibility(schedule))
273
			{
274
				// create flaw solution
275
				ResourceEventSchedule solution = new ResourceEventSchedule(overflow, schedule, this.schedulingCost);
276
				// add solution to the flaw
277
				overflow.addSolution(solution);
278
				// feasible solution found
279
				debug("Component [" + this.label + "] feasible solution found:\n"
280
						+ "- overflow: " + overflow + "\n"
281
						+ "- scheduled critical set: " + schedule + "\n");
282
			}
283
		}
284
		else 
285
		{
286
			// check possible schedules until no solution is found 
287
			for (int index = 0; index < cs.size() && overflow.getSolutions().isEmpty(); index++) 
288
			{
289
				// get an event from the critical set
290
				ResourceEvent<?> ev = cs.remove(index);
291
				// add the event to the possible schedule
292
				schedule.add(ev);
293
				
294
				// recursively build the permutation
295
				this.doFindFeasibleSchedule(schedule, cs, overflow);
296
				
297
				// remove event from the permutation
298
				schedule.remove(ev);
299
				// restore data
300
				cs.add(index, ev);
301
			}
302
		}
303
	}
304
	
305
	/**
306
	 * 
307
	 */
308
	@Override
309
	protected void doApply(FlawSolution solution) 
310
			throws FlawSolutionApplicationException 
311
	{
312
		// check flaw type
313
		switch (solution.getFlaw().getType())
314
		{
315
			// check flaw type
316
			case RESERVOIR_OVERFLOW : 
317
			{
318
				// cast flaw solution
319
				ResourceEventSchedule schedule = (ResourceEventSchedule) solution;
320
				// get events
321
				List<ResourceEvent<?>> events = schedule.getSchedule();
322
				try
323
				{
324
					// create relation between associated decisions
325
					for (int index = 0; index < events.size() - 1; index++)
326
					{
327
						// get decisions
328
						Decision reference = events.get(index).getDecision();
329
						Decision target = events.get(index + 1).getDecision();
330
						
331
						// create relation
332
						BeforeRelation before = this.component.create(
333
								RelationType.BEFORE, reference, target);
334
						
335
						// set relation bounds
336
						before.setBound(new long[] {
337
								0, 
338
								this.tdb.getHorizon()});
339
						
340
						// add created relation
341
						solution.addCreatedRelation(before);
342
						debug("Applying flaw solution:\n"
343
								+ "- solution: " + solution + "\n"
344
								+ "- created temporal constraint: " + before + "\n");
345
						
346
						// propagate relations
347
						this.component.activate(before);
348
						// add activated relations to solution
349
						solution.addActivatedRelation(before);
350
					}
351
					
352
					
353
					// check temporal feasibility
354
					this.tdb.verify();
355
				}
356
				catch (RelationPropagationException | ConsistencyCheckException ex) 
357
				{
358
					// failure while applying solution
359
					debug("Error while applying flaw solution:\n"
360
							+ "- solution: " + solution + "\n"
361
							+ "- message: " + ex.getMessage() + "\n");
362
					
363
					
364
					// deactivate created relation
365
					for (Relation rel : solution.getActivatedRelations()) {
366
						// get reference
367
						DomainComponent refComp = rel.getReference().getComponent();
368
						refComp.deactivate(rel);
369
					}
370
					
371
					// delete created relations
372
					for (Relation rel : solution.getCreatedRelations()) {
373
						// get reference component
374
						DomainComponent refComp = rel.getReference().getComponent();
375
						// delete relation from component
376
						refComp.delete(rel);
377
					}
378
					
379
					// throw exception
380
					throw new FlawSolutionApplicationException("Error while applying flaw solution:\n"
381
							+ "- solution: " + solution + "\n"
382
							+ "- message: " + ex.getMessage() + "\n");
383
				}
384
			}
385
			break;
386
			
387
			default : {
388
				throw new RuntimeException("Resolver [" + this.getClass().getSimpleName() +"] cannot handle flaws of type: " + solution.getFlaw().getType());
0 ignored issues
show
Best Practice introduced by
Dedicated exceptions should be preferred over throwing the generic Exception.
Loading history...
389
			}
390
		}
391
	}
392
393
	
394
	
395
	/**
396
	 * Analyze the profile of a reservoir resource in order to find peaks and compute production checkpoints
397
	 * 
398
	 * @param profile
399
	 * @return
400
	 */
401
	private List<Flaw> doComputeProfileOverflows(ReservoirResourceProfile profile)
402
	{
403
		// list of flaws found
404
		List<Flaw> flaws = new ArrayList<>();
405
		// get profile samples
406
		List<ResourceUsageProfileSample> samples = profile.getSamples();
407
		// long start peak level
408
		long startPeakLevel = 0;
409
		// reset the current level of resource
410
		long currentLevel = this.component.getInitialLevel();
411
		// set minimum and maximum level of resource within the critical set
412
		long minCriticalSetLevel = Long.MAX_VALUE - 1;
413
		long maxCriticalSetLevel = Long.MIN_VALUE + 1;
414
		// set of consumptions that may generate a peak
415
		List<ResourceEvent<?>> criticalSet = new ArrayList<>();
416
		// peak mode flag
417
		boolean peakMode = false;
418
		// analyze the resource profile until a peak is found
419
		for (int index = 0; index < samples.size() && flaws.isEmpty(); index++)
420
		{
421
			// current sample
422
			ResourceUsageProfileSample sample = samples.get(index);
423
			// get resource event
424
			ResourceEvent<?> event = sample.getEvent();
425
			
426
			// check peak mode
427
			if (!peakMode)
428
			{
429
				
430
				// update the start peak level
431
				startPeakLevel = currentLevel;
432
				
433
				// update the current level of the resource
434
				currentLevel += event.getAmount();					// positive amount in case of production, negative in case of consumption
435
				// check resource peak condition
436
				peakMode = currentLevel < this.component.getMinCapacity() || currentLevel > this.component.getMaxCapacity();
437
				
438
				// check if a peak is starting 
439
				if (peakMode) {
440
					
441
					// first event of the peak
442
					criticalSet.add(event);
443
					// update minimum and maximum level of resource within the critical set
444
					minCriticalSetLevel = Math.min(minCriticalSetLevel, currentLevel);
445
					maxCriticalSetLevel = Math.max(maxCriticalSetLevel, currentLevel);
446
				}
447
			}
448
			else		// peak mode  
449
			{
450
				// add the current event to the critical set
451
				criticalSet.add(event);
452
				// get current level of the resource
453
				currentLevel += event.getAmount();				// positive amount in case of production, negative in case of consumption
454
				
455
				// update minimum and maximum level of resource within the critical set
456
				minCriticalSetLevel = Math.min(minCriticalSetLevel, currentLevel);
457
				maxCriticalSetLevel = Math.max(maxCriticalSetLevel, currentLevel);
458
				
459
				// check peak condition
460
				peakMode = currentLevel < this.component.getMinCapacity() || currentLevel > this.component.getMaxCapacity();				
461
				
462
				// check if exit from peak condition
463
				if (!peakMode) 
464
				{
465
					// check over production
466
					if (maxCriticalSetLevel > this.component.getMaxCapacity()) 
467
					{
468
						// get the maximum (positive) amount of over production
469
						double delta = maxCriticalSetLevel - this.component.getMaxCapacity();
0 ignored issues
show
Bug introduced by
Math operands should be cast to prevent unwanted loss of precision when mixing types. Consider casting one of the operands of this subtraction to double.
Loading history...
470
						
471
						// create reservoir overflow flaw
472
						ReservoirOverflow overflow = new ReservoirOverflow(
473
								FLAW_COUNTER.getAndIncrement(), 
474
								this.component, 
475
								criticalSet, 
476
								startPeakLevel,
477
								delta);
478
						
479
						// add flaw and stop searching 
480
						flaws.add(overflow);
481
						
482
					}
483
					
484
					// check over consumption
485
					if (minCriticalSetLevel < this.component.getMinCapacity())
486
					{
487
						// get (negative) amount of over consumption
488
						double delta = minCriticalSetLevel - this.component.getMinCapacity();
0 ignored issues
show
Bug introduced by
Math operands should be cast to prevent unwanted loss of precision when mixing types. Consider casting one of the operands of this subtraction to double.
Loading history...
489
						
490
						// create reservoir overflow flaw
491
						ReservoirOverflow overflow = new ReservoirOverflow(
492
								FLAW_COUNTER.getAndIncrement(), 
493
								this.component, 
494
								criticalSet, 
495
								startPeakLevel,
496
								delta);
497
						
498
						// add flaw and stop searching 
499
						flaws.add(overflow);
500
					}
501
				}
502
			}
503
		}
504
		
505
		
506
		// check if a peak must be closed - "final peak"
507
		if (peakMode && flaws.isEmpty())	 
508
		{
509
			// check over production
510
			if (maxCriticalSetLevel > this.component.getMaxCapacity()) 
511
			{
512
				// get the maximum (positive) amount of over production
513
				double delta = maxCriticalSetLevel - this.component.getMaxCapacity();
0 ignored issues
show
Bug introduced by
Math operands should be cast to prevent unwanted loss of precision when mixing types. Consider casting one of the operands of this subtraction to double.
Loading history...
514
				
515
				// create reservoir overflow flaw
516
				ReservoirOverflow overflow = new ReservoirOverflow(
517
						FLAW_COUNTER.getAndIncrement(), 
518
						this.component, 
519
						criticalSet, 
520
						startPeakLevel,
521
						delta);
522
				
523
				// add flaw and stop searching 
524
				flaws.add(overflow);
525
			}
526
			
527
			// check over consumption
528
			if (minCriticalSetLevel < this.component.getMinCapacity())
529
			{
530
				// get (negative) amount of over consumption
531
				double delta = minCriticalSetLevel - this.component.getMinCapacity();
0 ignored issues
show
Bug introduced by
Math operands should be cast to prevent unwanted loss of precision when mixing types. Consider casting one of the operands of this subtraction to double.
Loading history...
532
				
533
				// create reservoir overflow flaw
534
				ReservoirOverflow overflow = new ReservoirOverflow(
535
						FLAW_COUNTER.getAndIncrement(), 
536
						this.component, 
537
						criticalSet, 
538
						startPeakLevel,
539
						delta);
540
				
541
				// add flaw and stop searching 
542
				flaws.add(overflow);
543
			}
544
			
545
		}
546
		
547
		// get found peaks - only one element expected
548
		return flaws;
549
	}
550
}
551
552
553