Passed
Push — master ( ebb470...bb3bf3 )
by Alessandro
05:54
created

doFindFeasibleSchedule(List,List,ReservoirOverflow)   A

Complexity

Conditions 5

Size

Total Lines 30
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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