1 | package de.uka.ipd.sdq.dsexplore.analysis.simucom; |
2 | |
3 | import java.util.ArrayList; |
4 | import java.util.Collection; |
5 | import java.util.Collections; |
6 | import java.util.HashMap; |
7 | import java.util.Iterator; |
8 | import java.util.LinkedList; |
9 | import java.util.List; |
10 | import java.util.Map; |
11 | import java.util.Vector; |
12 | |
13 | import org.apache.log4j.Logger; |
14 | import org.eclipse.core.runtime.IStatus; |
15 | import org.eclipse.emf.common.util.EList; |
16 | import org.eclipse.emf.ecore.EObject; |
17 | import org.eclipse.emf.ecore.util.EcoreUtil; |
18 | import org.opt4j.core.Criterion; |
19 | import org.rosuda.JRI.REXP; |
20 | |
21 | import de.uka.ipd.sdq.codegen.simudatavisualisation.datatypes.AbstractPie; |
22 | import de.uka.ipd.sdq.codegen.simudatavisualisation.datatypes.PieEntity; |
23 | import de.uka.ipd.sdq.completions.Completion; |
24 | import de.uka.ipd.sdq.dsexplore.analysis.AbstractPerformanceAnalysisResult; |
25 | import de.uka.ipd.sdq.dsexplore.analysis.AnalysisFailedException; |
26 | import de.uka.ipd.sdq.dsexplore.analysis.IPerformanceAnalysisResult; |
27 | import de.uka.ipd.sdq.dsexplore.analysis.IStatisticAnalysisResult; |
28 | import de.uka.ipd.sdq.dsexplore.qml.pcm.datastructures.EvaluationAspectWithContext; |
29 | import de.uka.ipd.sdq.pcm.core.composition.AssemblyContext; |
30 | import de.uka.ipd.sdq.pcm.core.composition.ComposedStructure; |
31 | import de.uka.ipd.sdq.pcm.core.composition.CompositionFactory; |
32 | import de.uka.ipd.sdq.pcm.core.entity.Entity; |
33 | import de.uka.ipd.sdq.pcm.repository.BasicComponent; |
34 | import de.uka.ipd.sdq.pcm.repository.PassiveResource; |
35 | import de.uka.ipd.sdq.pcm.repository.Repository; |
36 | import de.uka.ipd.sdq.pcm.repository.RepositoryComponent; |
37 | import de.uka.ipd.sdq.pcm.resourceenvironment.LinkingResource; |
38 | import de.uka.ipd.sdq.pcm.resourceenvironment.ProcessingResourceSpecification; |
39 | import de.uka.ipd.sdq.pcm.resourceenvironment.ResourceContainer; |
40 | import de.uka.ipd.sdq.pcm.resourcetype.ResourceType; |
41 | import de.uka.ipd.sdq.pcm.resultdecorator.ResultDecoratorRepository; |
42 | import de.uka.ipd.sdq.pcm.resultdecorator.ResultdecoratorFactory; |
43 | import de.uka.ipd.sdq.pcm.resultdecorator.repositorydecorator.RepositorydecoratorFactory; |
44 | import de.uka.ipd.sdq.pcm.resultdecorator.repositorydecorator.ServiceResult; |
45 | import de.uka.ipd.sdq.pcm.resultdecorator.resourceenvironmentdecorator.ActiveResourceUtilisationResult; |
46 | import de.uka.ipd.sdq.pcm.resultdecorator.resourceenvironmentdecorator.LinkingResourceResults; |
47 | import de.uka.ipd.sdq.pcm.resultdecorator.resourceenvironmentdecorator.PassiveResourceResult; |
48 | import de.uka.ipd.sdq.pcm.resultdecorator.resourceenvironmentdecorator.ProcessingResourceSpecificationResult; |
49 | import de.uka.ipd.sdq.pcm.resultdecorator.resourceenvironmentdecorator.ResourceenvironmentdecoratorFactory; |
50 | import de.uka.ipd.sdq.pcm.resultdecorator.resourceenvironmentdecorator.UtilisationResult; |
51 | import de.uka.ipd.sdq.pcm.seff.ExternalCallAction; |
52 | import de.uka.ipd.sdq.pcm.seff.SeffFactory; |
53 | import de.uka.ipd.sdq.pcm.seff.ServiceEffectSpecification; |
54 | import de.uka.ipd.sdq.pcm.usagemodel.UsageScenario; |
55 | import de.uka.ipd.sdq.pcmsolver.models.PCMInstance; |
56 | import de.uka.ipd.sdq.pcmsolver.transformations.ContextWrapper; |
57 | import de.uka.ipd.sdq.pcmsolver.transformations.EMFHelper; |
58 | import de.uka.ipd.sdq.sensorframework.adapter.StateSensorToPieAdapter; |
59 | import de.uka.ipd.sdq.sensorframework.adapter.StateSensorToTimeDeltaPieAdapter; |
60 | import de.uka.ipd.sdq.sensorframework.entities.Experiment; |
61 | import de.uka.ipd.sdq.sensorframework.entities.ExperimentRun; |
62 | import de.uka.ipd.sdq.sensorframework.entities.Measurement; |
63 | import de.uka.ipd.sdq.sensorframework.entities.Sensor; |
64 | import de.uka.ipd.sdq.sensorframework.entities.SensorAndMeasurements; |
65 | import de.uka.ipd.sdq.sensorframework.entities.TimeSpanMeasurement; |
66 | import de.uka.ipd.sdq.sensorframework.entities.TimeSpanSensor; |
67 | import de.uka.ipd.sdq.sensorframework.visualisation.rvisualisation.RVisualisationPlugin; |
68 | import de.uka.ipd.sdq.sensorframework.visualisation.rvisualisation.reports.RReport.TimeseriesData; |
69 | import de.uka.ipd.sdq.sensorframework.visualisation.rvisualisation.utils.RConnection; |
70 | import de.uka.ipd.sdq.statistics.PhiMixingBatchAlgorithm; |
71 | import de.uka.ipd.sdq.statistics.estimation.ConfidenceInterval; |
72 | import de.uka.ipd.sdq.statistics.estimation.SampleMeanEstimator; |
73 | |
74 | |
75 | public class SimuComAnalysisResult extends AbstractPerformanceAnalysisResult implements IStatisticAnalysisResult, IPerformanceAnalysisResult { |
76 | |
77 | private ExperimentRun run; |
78 | |
79 | private double meanValue; |
80 | |
81 | private Experiment experiment; |
82 | |
83 | private double medianValue; |
84 | |
85 | private double throughput; |
86 | |
87 | private double stdDeviation; |
88 | |
89 | private ConfidenceInterval confidenceInterval; |
90 | |
91 | private double alpha = 0.95; |
92 | |
93 | private long observations = 0; |
94 | |
95 | /** You must not use the usage scenario to navigate in the PCM, as the |
96 | * model may not be longer valid for this result after the constructor call. |
97 | */ |
98 | private String usageSenarioName; |
99 | |
100 | /** |
101 | * Contains: resource utilisations |
102 | * Should also contain: Passive resources. |
103 | */ |
104 | private ResultDecoratorRepository results; |
105 | |
106 | private Map<Criterion, EvaluationAspectWithContext> objectiveToAspects; |
107 | |
108 | private SimuComQualityAttributeDeclaration qualityAttributeInfo; |
109 | |
110 | |
111 | private static Logger logger = |
112 | Logger.getLogger("de.uka.ipd.sdq.dsexplore"); |
113 | |
114 | public SimuComAnalysisResult(ExperimentRun run, Experiment experiment, PCMInstance pcmInstance, |
115 | UsageScenario usageScenario, Map<Criterion, EvaluationAspectWithContext> objectiveToAspect, |
116 | SimuComQualityAttributeDeclaration qualityAttributeInfo) |
117 | throws AnalysisFailedException { |
118 | super(pcmInstance); |
119 | this.run = run; |
120 | this.experiment = experiment; |
121 | this.usageSenarioName = usageScenario.getEntityName().replaceAll(" ", "_"); |
122 | |
123 | this.objectiveToAspects = objectiveToAspect; |
124 | this.qualityAttributeInfo = qualityAttributeInfo; |
125 | |
126 | SensorAndMeasurements sam = getUsageScenarioMeasurements(); |
127 | this.meanValue = calculateValue(sam,"mean", TimeseriesData.TIMESPAN); |
128 | this.stdDeviation = calculateValue(sam,"sd", TimeseriesData.TIMESPAN); |
129 | this.medianValue = calculateValue(sam,"median", TimeseriesData.TIMESPAN); |
130 | this.throughput = calculateThroughput(sam); |
131 | this.observations = sam.getMeasurements().size(); |
132 | |
133 | this.confidenceInterval = determineConfidenceInterval(); |
134 | |
135 | this.results = retrieveResults(pcmInstance); |
136 | |
137 | |
138 | logger.debug("Initialised SimuCom result"); |
139 | |
140 | } |
141 | |
142 | private double calculateThroughput(SensorAndMeasurements sam) throws AnalysisFailedException { |
143 | int numberOfMeasurements = sam.getMeasurements().size(); |
144 | // we assume that the last entry is the duration. |
145 | // still, no way to get the maximum time... because this is a collection. |
146 | double duration = calculateValue(sam,"max", TimeseriesData.EVENTTIME) - calculateValue(sam,"min", TimeseriesData.EVENTTIME); |
147 | |
148 | return numberOfMeasurements / duration; |
149 | } |
150 | |
151 | private ResultDecoratorRepository retrieveResults(PCMInstance pcmInstance) throws AnalysisFailedException { |
152 | |
153 | ResultDecoratorRepository repo = ResultdecoratorFactory.eINSTANCE.createResultDecoratorRepository(); |
154 | retrieveResourceUtilisation(pcmInstance, repo); |
155 | retrieveServiceResults(pcmInstance, repo); |
156 | retrievePassiveResourceUtil(repo, pcmInstance); |
157 | |
158 | return repo; |
159 | } |
160 | |
161 | /** |
162 | * Quite ugly method to get the service results, because sensors only contain strings for identification, |
163 | * and because only external calls currently have results, not SEFFs (TODO: define right probes when optimizing, then refactor this). |
164 | * @param pcmInstance |
165 | * @param repo |
166 | * @throws AnalysisFailedException |
167 | */ |
168 | private void retrieveServiceResults(PCMInstance pcmInstance, |
169 | ResultDecoratorRepository repo) throws AnalysisFailedException { |
170 | |
171 | //Need to get all times from all calls to that SEFF. |
172 | |
173 | //get all ExternalCalls from model |
174 | // get the eclass (strange way, but how to do it better? |
175 | ExternalCallAction sampleExternalCall = SeffFactory.eINSTANCE.createExternalCallAction(); |
176 | EMFHelper helper = new EMFHelper(); |
177 | List<Repository> respositories = pcmInstance.getRepositories(); |
178 | |
179 | HashMap<String, ExternalCallAction> idToExternalCallMap = new HashMap<String, ExternalCallAction>(100); |
180 | |
181 | for (Repository repository : respositories) { |
182 | List<EObject> allExternalCallsList = helper.getElements(repository, sampleExternalCall.eClass()); |
183 | |
184 | //put all external calls in a hash map based on their id |
185 | for (EObject eObject : allExternalCallsList) { |
186 | if (eObject instanceof ExternalCallAction){ |
187 | ExternalCallAction externalCall = (ExternalCallAction) eObject; |
188 | String id = externalCall.getId(); |
189 | idToExternalCallMap.put(id, externalCall); |
190 | } |
191 | //if not ignore it (although something is wrong with the query above in that case) |
192 | |
193 | } |
194 | } |
195 | |
196 | // need all AssemblyContexts to get the called SEFFs and to get the contexts for the external calls |
197 | // (they may be instantiated several times in the system) |
198 | AssemblyContext sampleAssemblyContext = CompositionFactory.eINSTANCE.createAssemblyContext(); |
199 | //only gets the AssemblyContexts directly contained in the system, so we also need to query the Repository for composite components |
200 | EList<EObject> eObjectAssemblyContexts = helper.getElements(pcmInstance.getSystem(), sampleAssemblyContext.eClass()); |
201 | |
202 | for (Repository repository : respositories) { |
203 | List<EObject> assemblyContextsInRepo = helper.getElements(repository, sampleAssemblyContext.eClass()); |
204 | eObjectAssemblyContexts.addAll(assemblyContextsInRepo); |
205 | } |
206 | |
207 | //Put them in map based on their id |
208 | Map<String, AssemblyContextContext> idToAssemblyContextMap = new HashMap<String, AssemblyContextContext>(eObjectAssemblyContexts.size()); |
209 | List<String> completionComponentIdsToIgnore = new ArrayList<String>(); |
210 | |
211 | //List<AssemblyContext> assemblyContextList = new ArrayList<AssemblyContext>(eObjectAssemblyContexts.size()); |
212 | for (EObject eObjectAssemblyContext : eObjectAssemblyContexts) { |
213 | if (eObjectAssemblyContext instanceof AssemblyContext){ |
214 | AssemblyContext assemblyContext = (AssemblyContext)eObjectAssemblyContext; |
215 | idToAssemblyContextMap.put(assemblyContext.getId(), new AssemblyContextContext(assemblyContext)); |
216 | } |
217 | } |
218 | |
219 | |
220 | Map<String, AssemblyContextContext> temporaryIdToAssemblyContextMap = new HashMap<String, AssemblyContextContext>(eObjectAssemblyContexts.size()); |
221 | //set parent contexts |
222 | for (AssemblyContextContext assemblyContextContext : idToAssemblyContextMap.values()) { |
223 | RepositoryComponent component = assemblyContextContext.getAssemblyContext().getEncapsulatedComponent__AssemblyContext(); |
224 | if (component instanceof ComposedStructure && ! (component instanceof Completion)){ |
225 | List<AssemblyContext> innerAssemblyContexts = ((ComposedStructure)component).getAssemblyContexts__ComposedStructure(); |
226 | for (AssemblyContext assemblyContext : innerAssemblyContexts) { |
227 | String id = assemblyContext.getId(); |
228 | AssemblyContextContext innerContext = idToAssemblyContextMap.get(id); |
229 | innerContext.setParent(assemblyContextContext); |
230 | temporaryIdToAssemblyContextMap.put(innerContext.getComposedId(), innerContext); |
231 | } |
232 | } else if (component instanceof Completion){ |
233 | completionComponentIdsToIgnore.add(component.getEntityName()); |
234 | } |
235 | |
236 | } |
237 | idToAssemblyContextMap.putAll(temporaryIdToAssemblyContextMap); |
238 | |
239 | //iterate through sensors and store the (ExternalCall,AssemblyContext) pairs with their sensors. |
240 | Collection<Sensor> sensorList = this.experiment.getSensors(); |
241 | List<ExternalCallActionWithSensors> externalCallsInContextWithSensorsList = new LinkedList<ExternalCallActionWithSensors>(); |
242 | |
243 | for (Sensor sensor : sensorList) { |
244 | String sensorName = sensor.getSensorName(); |
245 | if (sensorName.contains("CallID")){ |
246 | //current sensor is an ExternalCall sensor |
247 | |
248 | // parse external call id |
249 | if (sensorName.lastIndexOf(" ") < sensorName.length()-2 |
250 | && sensorName.lastIndexOf(" ") > -1 |
251 | && sensorName.length() > 2){ |
252 | String externalCallID = sensorName.substring(sensorName.lastIndexOf(" ")+1, sensorName.length()-1); |
253 | |
254 | // find the external call in the model |
255 | ExternalCallAction myCall = idToExternalCallMap.get(externalCallID); |
256 | |
257 | if (myCall != null){ |
258 | |
259 | //retrieve AssemblyContext from sensor name |
260 | String assemblyContextID = sensorName.substring(sensorName.indexOf("AssemblyCtx")+13,sensorName.indexOf("CallID")-2); |
261 | AssemblyContextContext myAssemblyContext = idToAssemblyContextMap.get(assemblyContextID); |
262 | if (myAssemblyContext != null){ |
263 | |
264 | ExternalCallActionWithSensors externalCallInContextWithSensors = new ExternalCallActionWithSensors(myCall, myAssemblyContext); |
265 | |
266 | //reuse pair if it already there |
267 | int index = externalCallsInContextWithSensorsList.indexOf(externalCallInContextWithSensors); |
268 | if (index > 0){ |
269 | externalCallInContextWithSensors = externalCallsInContextWithSensorsList.get(index); |
270 | } else { |
271 | externalCallsInContextWithSensorsList.add(externalCallInContextWithSensors); |
272 | } |
273 | |
274 | // map all result sensors to the SEFF they call |
275 | externalCallInContextWithSensors.addSensor(sensor); |
276 | continue; |
277 | } |
278 | } else { |
279 | // check if this is a completion component, if yes, ignore it and continue |
280 | String componentID = sensorName.substring(sensorName.indexOf("Component: ")+11,sensorName.indexOf("AssemblyCtx")-2); |
281 | if (completionComponentIdsToIgnore.contains(componentID)){ |
282 | logger.info("Ignoring completion component sensor "+sensorName+" when reading in SimuCom results."); |
283 | continue; |
284 | } |
285 | } |
286 | } |
287 | logger.warn("Cannot retrieve external call id from sensor. If this is a completion component, this is ok. Otherwise, sensor name labels must have changed. Contact developers if this sensors is needed. Sensor: "+sensorName); |
288 | } |
289 | |
290 | } |
291 | |
292 | //FIXME: This all does not work if composite components themselves are used several times in the system, because then, the AssemblyContexts of the inner components will not be unique anymore. |
293 | |
294 | |
295 | // get the average time for the SEFFs (careful: contains network) |
296 | // by getting the data of the chosen sensors from this.run |
297 | |
298 | for (ExternalCallActionWithSensors externalCallActionWithSensors : externalCallsInContextWithSensorsList) { |
299 | List<Sensor> mySensors = externalCallActionWithSensors.getSensors(); |
300 | |
301 | if (mySensors.size() > 0){ |
302 | |
303 | // create service result |
304 | ServiceResult myServiceResult = RepositorydecoratorFactory.eINSTANCE.createAllocationServiceResult(); |
305 | |
306 | //get SEFF for external call |
307 | |
308 | ContextWrapper contextWrapper = new ContextWrapper(pcmInstance); |
309 | //set the AssemblyContext hierarchy |
310 | List<AssemblyContext> assemblyContextHierarchy = new ArrayList<AssemblyContext>(4); |
311 | AssemblyContextContext currentContext = externalCallActionWithSensors.getAssemblyContext(); |
312 | while (currentContext != null) { |
313 | assemblyContextHierarchy.add(currentContext.getAssemblyContext()); |
314 | currentContext = currentContext.getParentAssemblyContext(); |
315 | } |
316 | //the currentContext needs to be last in the list. |
317 | Collections.reverse(assemblyContextHierarchy); |
318 | contextWrapper.setAssCtxList(assemblyContextHierarchy); |
319 | |
320 | ServiceEffectSpecification seff = contextWrapper.getNextSEFF(externalCallActionWithSensors.getExternalCall()); |
321 | |
322 | myServiceResult.setServiceEffectSpecification_ServiceResult(seff); |
323 | |
324 | double weightedAndCumulatedMeanResponseTime = 0; |
325 | int totalNumberOfMeasurements = 0; |
326 | |
327 | for (Sensor sensor : mySensors) { |
328 | SensorAndMeasurements results = run.getMeasurementsOfSensor(sensor); |
329 | Collection<Measurement> measurements = results.getMeasurements(); |
330 | totalNumberOfMeasurements += measurements.size(); |
331 | weightedAndCumulatedMeanResponseTime += calculateValue(results, "mean", TimeseriesData.TIMESPAN) * totalNumberOfMeasurements; |
332 | |
333 | } |
334 | double meanResponseTime = weightedAndCumulatedMeanResponseTime / totalNumberOfMeasurements; |
335 | myServiceResult.setMeanResponseTime(meanResponseTime); |
336 | |
337 | repo.getServiceResult_ResultDecoratorRepository().add(myServiceResult); |
338 | |
339 | } |
340 | |
341 | } |
342 | |
343 | |
344 | } |
345 | |
346 | |
347 | /** |
348 | * retrieves active resource utils. |
349 | * Should also do passive resource utils (but not yet collected in SimuCom). |
350 | * @param pcmInstance |
351 | * @param repo |
352 | * @return |
353 | * @throws AnalysisFailedException |
354 | */ |
355 | private ResultDecoratorRepository retrieveResourceUtilisation( |
356 | PCMInstance pcmInstance, ResultDecoratorRepository repo) throws AnalysisFailedException { |
357 | |
358 | List<ResourceContainer> resourceContainerList = pcmInstance.getResourceEnvironment().getResourceContainer_ResourceEnvironment(); |
359 | for (ResourceContainer resourceContainer : resourceContainerList) { |
360 | List<ProcessingResourceSpecification> resources = resourceContainer.getActiveResourceSpecifications_ResourceContainer(); |
361 | for (ProcessingResourceSpecification resource : resources) { |
362 | ProcessingResourceSpecificationResult result = ResourceenvironmentdecoratorFactory.eINSTANCE.createProcessingResourceSpecificationResult(); |
363 | this.getUtilisationOfResource(result, resourceContainer, resource.getActiveResourceType_ActiveResourceSpecification()); |
364 | if (result != null && result.getResourceUtilisation() != Double.NaN){ |
365 | result.setEntityName("Util of "+resourceContainer.getEntityName()+"_"+resource.getActiveResourceType_ActiveResourceSpecification().getEntityName()); |
366 | result.setProcessingResourceSpecification_ProcessingResourceSpecificationResult(resource); |
367 | repo.getUtilisationResults_ResultDecoratorRepository().add(result); |
368 | } |
369 | } |
370 | |
371 | } |
372 | List<LinkingResource> linkList = pcmInstance.getResourceEnvironment().getLinkingResources__ResourceEnvironment(); |
373 | for (LinkingResource linkingResource : linkList) { |
374 | ResourceType type = linkingResource.getCommunicationLinkResourceSpecifications_LinkingResource().getCommunicationLinkResourceType_CommunicationLinkResourceSpecification(); |
375 | LinkingResourceResults result = ResourceenvironmentdecoratorFactory.eINSTANCE.createLinkingResourceResults(); |
376 | result.setLinkingResource_LinkingResourceResults(linkingResource); |
377 | this.getUtilisationOfResource(result, linkingResource, type); |
378 | if (result != null && result.getResourceUtilisation() != Double.NaN){ |
379 | result.setEntityName("Util of "+linkingResource.getEntityName()+"_"+type); |
380 | repo.getUtilisationResults_ResultDecoratorRepository().add(result); |
381 | } |
382 | } |
383 | return repo; |
384 | } |
385 | |
386 | private Map<String, PassiveResourceResult> retrievePassiveResourceUtil(ResultDecoratorRepository repo, PCMInstance pcm) throws AnalysisFailedException{ |
387 | |
388 | |
389 | Map<String, PassiveResourceResult> idsToPassiveResourceResult = new HashMap<String, PassiveResourceResult>(); |
390 | |
391 | // results for passive resources |
392 | List<AssemblyContext> allAssemblyContexts = de.uka.ipd.sdq.dsexplore.helper.EMFHelper.getAllUsedAssemblyContexts(pcm.getSystem()); |
393 | for (AssemblyContext assemblyContext : allAssemblyContexts) { |
394 | RepositoryComponent innerComponent = assemblyContext.getEncapsulatedComponent__AssemblyContext(); |
395 | if (innerComponent instanceof BasicComponent){ |
396 | |
397 | BasicComponent basicComponent = (BasicComponent) innerComponent; |
398 | List<PassiveResource> passiveResourceOfComponentList = basicComponent |
399 | .getPassiveResource_BasicComponent(); |
400 | for (PassiveResource passiveResource : passiveResourceOfComponentList) { |
401 | |
402 | String passiveResourceAndAssemblyContextId = passiveResource.getId()+":"+assemblyContext.getId(); |
403 | |
404 | PassiveResourceResult result = ResourceenvironmentdecoratorFactory.eINSTANCE.createPassiveResourceResult(); |
405 | |
406 | result.setPassiveResource_PassiveResourceResult(passiveResource); |
407 | result.setAssemblyContext_PassiveResourceResult(assemblyContext); |
408 | result.setEntityName("Utilisation of "+passiveResource.getEntityName()+" id: "+passiveResourceAndAssemblyContextId); |
409 | |
410 | |
411 | repo.getUtilisationResults_ResultDecoratorRepository().add(result); |
412 | idsToPassiveResourceResult.put(passiveResourceAndAssemblyContextId, result); |
413 | |
414 | } |
415 | |
416 | } |
417 | |
418 | } |
419 | //iterate through sensors and store the (ExternalCall,AssemblyContext) pairs with their sensors. |
420 | Collection<Sensor> sensorList = this.experiment.getSensors(); |
421 | |
422 | for (Sensor sensor : sensorList) { |
423 | String sensorName = sensor.getSensorName(); |
424 | if (sensorName.contains("Passive Resource")){ |
425 | String passiveResourceAndAssemblyContextID = sensorName.substring(sensorName.lastIndexOf(" ")+1); |
426 | PassiveResourceResult passiveResourceResult = idsToPassiveResourceResult.get(passiveResourceAndAssemblyContextID); |
427 | |
428 | if (passiveResourceResult != null){ |
429 | |
430 | SensorAndMeasurements results = run.getMeasurementsOfSensor(sensor); |
431 | |
432 | if (sensorName.contains("Hold time")){ |
433 | passiveResourceResult.setAverageHoldingTime(calculateValue(results, "mean", TimeseriesData.TIMESPAN)); |
434 | } else if (sensorName.contains("Wait time")){ |
435 | passiveResourceResult.setAverageWaitTime(calculateValue(results, "mean", TimeseriesData.TIMESPAN)); |
436 | } else if (sensorName.contains("Util")){ |
437 | // for passive resources, also consider the capacity when calculating the util |
438 | int capacity = Integer.parseInt(passiveResourceResult.getPassiveResource_PassiveResourceResult().getCapacity_PassiveResource().getSpecification()); |
439 | retrieveUtilisationFromSensor(sensor, passiveResourceResult, capacity); |
440 | |
441 | } |
442 | } else { |
443 | logger.warn("Unknown passive resource id "+passiveResourceAndAssemblyContextID+", ignoring this sensor."); |
444 | } |
445 | } |
446 | |
447 | } |
448 | |
449 | return idsToPassiveResourceResult; |
450 | |
451 | } |
452 | |
453 | private ConfidenceInterval determineConfidenceInterval() throws AnalysisFailedException { |
454 | ConfidenceInterval ci = null; |
455 | SensorAndMeasurements meas = getUsageScenarioMeasurements(); |
456 | Sensor sensor = meas.getSensor(); |
457 | if (sensor instanceof TimeSpanSensor){ |
458 | PhiMixingBatchAlgorithm statisticChecker = new PhiMixingBatchAlgorithm(); |
459 | |
460 | for (Measurement m : meas.getMeasurements()) { |
461 | TimeSpanMeasurement t = (TimeSpanMeasurement)m; |
462 | statisticChecker.offerSample(t.getTimeSpan()); |
463 | } |
464 | if (statisticChecker.hasValidBatches()){ |
465 | ci = new SampleMeanEstimator().estimateConfidence(statisticChecker.getBatchMeans(),this.alpha); |
466 | } |
467 | if (ci == null) { |
468 | ci = new ConfidenceInterval(Double.NaN, 0, Double.POSITIVE_INFINITY, this.alpha); |
469 | } |
470 | return ci; |
471 | } else { |
472 | logger.error("Sensor of usage scenario is not a time span sensor, cannot calculate statistics."); |
473 | return null; |
474 | } |
475 | } |
476 | |
477 | |
478 | /** |
479 | * This implementation does not properly check the passed criterion... |
480 | */ |
481 | @Override |
482 | public double getValueFor(Criterion criterion) { |
483 | EvaluationAspectWithContext aspect = this.objectiveToAspects.get(criterion); |
484 | |
485 | if (aspect != null){ |
486 | if (EcoreUtil.equals(aspect.getDimension(), this.qualityAttributeInfo.getResponseTime())){ |
487 | return this.meanValue; |
488 | } else if (EcoreUtil.equals(aspect.getDimension(), this.qualityAttributeInfo.getThroughput())){ |
489 | return this.throughput; |
490 | } |
491 | } |
492 | |
493 | logger.warn("Unknown aspect for simu com result, adding NaN."); |
494 | return Double.NaN; |
495 | } |
496 | |
497 | @Override |
498 | public double getMeanValue() { |
499 | return meanValue; |
500 | } |
501 | |
502 | public double getStandardDeviation() { |
503 | return stdDeviation; |
504 | } |
505 | |
506 | public double getVariance() throws AnalysisFailedException { |
507 | double std = this.getStandardDeviation(); |
508 | return std * std; |
509 | } |
510 | |
511 | public double getCoefficientOfVariance() { |
512 | double std = this.getStandardDeviation(); |
513 | double mean = this.getMeanValue(); |
514 | return std / mean; |
515 | } |
516 | |
517 | /** |
518 | * Retrieves the confidence interval. When called for the first time, this |
519 | * method calculates the confidence interval with the given level from the |
520 | * data stored in the usage scenario sensor. |
521 | * If no batch independence is given, the confidence interval is set to |
522 | * 0 to positive infinity. |
523 | */ |
524 | public ConfidenceInterval getConfidenceInterval(){ |
525 | return this.confidenceInterval; |
526 | } |
527 | |
528 | private SensorAndMeasurements getUsageScenarioMeasurements() throws AnalysisFailedException{ |
529 | //Get usage scenario sensor. |
530 | Sensor respTimeSensor = getSensorForUsageScenario(experiment, this.usageSenarioName); |
531 | if (respTimeSensor != null){ |
532 | return run.getMeasurementsOfSensor(respTimeSensor); |
533 | |
534 | } else |
535 | throw new AnalysisFailedException("Could not find sensor for usage scenario "+this.usageSenarioName); |
536 | } |
537 | |
538 | public double getMedianValue() { |
539 | return medianValue; |
540 | } |
541 | |
542 | |
543 | private double calculateValue(SensorAndMeasurements sam, String command, TimeseriesData whichData) throws AnalysisFailedException { |
544 | AnalysisFailedException error = null; |
545 | try { |
546 | if (RConnection.isEngineAvailable()){ |
547 | |
548 | RConnection rConnection = RConnection.getRConnection(); |
549 | String sensorName = storeMeasurementsInRVector(sam, sam.getSensor().getSensorID(), whichData, rConnection); |
550 | Vector<REXP> rResult = rConnection.execute(command+"(" + sensorName + ")\n"); |
551 | if (rResult.size() > 0) { |
552 | if (rResult.get(0).rtype == REXP.REALSXP){ |
553 | return rResult.get(0).asDouble(); |
554 | } else { |
555 | error = new AnalysisFailedException("R engine returned a non-double when trying to calculate the mean value: "+rResult.get(0).asString()); |
556 | logger.error(error.getMessage()); |
557 | } |
558 | } else { |
559 | error = new AnalysisFailedException("Querying R engine returned an empty result. Maybe simulation time was too short and no results have been written to the response time sensor."); |
560 | logger.error(error.getMessage()); |
561 | } |
562 | } else { |
563 | error = new AnalysisFailedException("Could not connect to R engine! Check your R configuration."); |
564 | logger.error(error.getMessage()); |
565 | } |
566 | } catch (ExceptionInInitializerError e) { |
567 | error = new AnalysisFailedException("Could not connect to R engine! Check your R configuration.", e); |
568 | logger.error(error.getMessage()); |
569 | logger.error(e.getMessage()); |
570 | } |
571 | throw error; |
572 | } |
573 | |
574 | private static Sensor getSensorForUsageScenario(Experiment exp, String usageScenarioName) { |
575 | Collection<Sensor> sensors = exp.getSensors(); |
576 | for (Iterator<Sensor> iterator = sensors.iterator(); iterator.hasNext();) { |
577 | Sensor sensor = iterator.next(); |
578 | //logger.debug("Experiment has a sensor with ID "+sensor.getSensorID()+" and name "+sensor.getSensorName()+"."); |
579 | //TODO: repeat the java name adjustment here (parentheses etc.) |
580 | if (sensor.getSensorName().contains(usageScenarioName)){ |
581 | logger.debug("Found sensor for usage scenario "+usageScenarioName); |
582 | return sensor; |
583 | } |
584 | } |
585 | logger.error("No sensor found for usage scenario "+usageScenarioName); |
586 | return null; |
587 | } |
588 | |
589 | private static Sensor getSensorForResource(Experiment exp, Entity rc, ResourceType res, String sensorTypeString) { |
590 | Collection<Sensor> sensors = exp.getSensors(); |
591 | for (Iterator<Sensor> iterator = sensors.iterator(); iterator.hasNext();) { |
592 | Sensor sensor = iterator.next(); |
593 | //logger.debug("Experiment has a sensor with ID "+sensor.getSensorID()+" and name "+sensor.getSensorName()+"."); |
594 | if (sensor.getSensorName().contains(res.getEntityName()) |
595 | && sensor.getSensorName().contains(sensorTypeString) |
596 | && sensor.getSensorName().contains(rc.getEntityName())){ |
597 | logger.debug("Found sensor of "+sensorTypeString+" for the resource "+rc.getEntityName()+": "+res.getEntityName()); |
598 | return sensor; |
599 | } |
600 | } |
601 | logger.error("No sensor of "+sensorTypeString+" found for resource "+rc.getEntityName()+": "+res.getEntityName()); |
602 | return null; |
603 | } |
604 | |
605 | /**Export the measurements of a sensor to R. |
606 | * There are two alternatives. The measurements can be transferred |
607 | * via an array, which implies certain size restrictions. An alternative is |
608 | * to use a temporary file. The behavior can only be switched in source |
609 | * code by the constant <code>TRANSFER_TYPE</code>. |
610 | * Variable names in R are as follows:<br /> |
611 | * For timespan data: "sensor" + #<br /> |
612 | * For eventtime data: "sensor" + # + "_ET")<br /> |
613 | * |
614 | * @param measurements Measurements for a sensor. |
615 | * @param sensorNumber number of the sensor vector in R. |
616 | * @param dataSelection the data element to save. |
617 | * @param rConnection Connection to the R engine. |
618 | * @return R variable name which contains the data. |
619 | */ |
620 | protected static String storeMeasurementsInRVector( |
621 | final SensorAndMeasurements measurements, final long sensorNumber, |
622 | final TimeseriesData dataSelection, final RConnection rConnection) { |
623 | String sensorName = null; |
624 | |
625 | if (dataSelection == TimeseriesData.TIMESPAN) { |
626 | sensorName = "sensor" + sensorNumber; |
627 | } else |
628 | if (dataSelection == TimeseriesData.EVENTTIME) { |
629 | sensorName = "sensor" + sensorNumber + "_ET"; |
630 | } else { |
631 | throw new RuntimeException("Unknown data element of time series."); |
632 | } |
633 | |
634 | //if (TRANSFER_TYPE == TransferType.MEMORY) { |
635 | // Activate to transfer data via memory |
636 | double[] measurementsArray = |
637 | prepareExportToRByMemory(measurements, dataSelection); |
638 | rConnection.assign(sensorName, measurementsArray); |
639 | //} |
640 | /* if (TRANSFER_TYPE == TransferType.FILE) { |
641 | // Activate to transfer data via temporary file |
642 | String rCommand = sensorName + " <- " |
643 | + prepareExportToRByFile(measurements, dataSelection); |
644 | Vector<REXP> result = rConnection.execute(rCommand); |
645 | // Error handling |
646 | if (!rConnection.getLastConsoleMessage().equalsIgnoreCase("Read " |
647 | + measurements.getMeasurements().size() + " items\n")) { |
648 | String rResults = "Executing command: '" + rCommand + "' with "; |
649 | for (REXP currentResult : result) { |
650 | rResults += "String: " + currentResult.asString() |
651 | + ", SymbolName: " + currentResult.asSymbolName() |
652 | + ", Type: " + currentResult.getType() + "\n"; |
653 | } |
654 | RVisualisationPlugin.log(IStatus.INFO, |
655 | "Storing Measurements in R via file is most likely wrong. Last message " |
656 | + "on the console was: " + rConnection.getLastConsoleMessage() |
657 | + "R returned:\n" + rResults); |
658 | } |
659 | |
660 | }*/ |
661 | return sensorName; |
662 | } |
663 | |
664 | /**Prepares the export the measurements of a sensor to R. Therefore an |
665 | * array is filled with the measurements. |
666 | * |
667 | * Copied from |
668 | * de.uka.ipd.sdq.sensorframework.visualisation.rvisualisation.reports.RReport. |
669 | * TODO: Possibly make this public in RReport and use it properly or refactor it |
670 | * to a helper class. |
671 | * |
672 | * @param measurements Measurements for a sensor. |
673 | * @param dataSelection the data element to save. |
674 | * @return R command to read measurements. |
675 | * Can be used to store data in a r vector. |
676 | * @author Henning |
677 | */ |
678 | private static double[] prepareExportToRByMemory( |
679 | final SensorAndMeasurements measurements, |
680 | final TimeseriesData dataSelection) { |
681 | double[] measurementsArray = |
682 | new double[measurements.getMeasurements().size()]; |
683 | if (measurements.getMeasurements().size() == Integer.MAX_VALUE) { |
684 | RVisualisationPlugin.log(IStatus.ERROR, |
685 | "Too many measurements. Results might be inaccurate."); |
686 | } |
687 | int position = 0; |
688 | for (Measurement time : measurements.getMeasurements()) { |
689 | TimeSpanMeasurement tsm = (TimeSpanMeasurement) time; |
690 | measurementsArray[position++] = |
691 | (dataSelection == TimeseriesData.EVENTTIME) |
692 | ? tsm.getEventTime() |
693 | : tsm.getTimeSpan(); |
694 | } |
695 | return measurementsArray; |
696 | } |
697 | |
698 | /** |
699 | * Get the utilisation of the passed resource |
700 | */ |
701 | private void getUtilisationOfResource(ActiveResourceUtilisationResult resultToFill, Entity container, ResourceType resourceType) throws AnalysisFailedException { |
702 | Sensor utilSensor = getSensorForResource(this.experiment, container, resourceType, "Util"); |
703 | Sensor demandedSensor = getSensorForResource(this.experiment, container, resourceType, "Demanded"); |
704 | |
705 | // for later when wait os available |
706 | //Sensor waitSensor = getSensorForResource(this.experiment, container, resource, "Wait"); |
707 | |
708 | |
709 | |
710 | if (utilSensor != null /*&& waitSensor != null*/){ |
711 | |
712 | try { |
713 | |
714 | retrieveUtilisationFromSensor(utilSensor, resultToFill,1); |
715 | |
716 | /* |
717 | SensorAndMeasurements sam = run.getMeasurementsOfSensor(waitSensor); |
718 | result.setAverageWaitTime(calculateValue(sam, "mean")); |
719 | */ |
720 | resultToFill.setAverageWaitTime(Double.NaN); |
721 | |
722 | SensorAndMeasurements sam = run.getMeasurementsOfSensor(demandedSensor); |
723 | resultToFill.setDemandedTime(calculateValue(sam, "mean", TimeseriesData.TIMESPAN)); |
724 | |
725 | } catch (RuntimeException e) { |
726 | // FIXME: The call "SensorAndMeasurements sam = |
727 | // run.getMeasurementsOfSensor(sensor);" above sometimes results |
728 | // in a RuntimeException, because a State could not be |
729 | // deserialised. Better fix that porperly instead of catching |
730 | // the error here. |
731 | resultToFill.setResourceUtilisation(Double.NaN); |
732 | logger.error("A runtime exception occured while accessing the sendorframework. I'll try to ignore it and continue."); |
733 | e.printStackTrace(); |
734 | } |
735 | } else { |
736 | resultToFill.setResourceUtilisation(Double.NaN); |
737 | throw new AnalysisFailedException("Could not find sensor for resource "+container.getEntityName()+": "+resourceType.getEntityName()); |
738 | } |
739 | } |
740 | |
741 | /** |
742 | * |
743 | * @param sensor |
744 | * @param result |
745 | * @param capacity The capacity of the resource, will divide busyFraction by this number to |
746 | */ |
747 | private void retrieveUtilisationFromSensor(Sensor sensor, |
748 | UtilisationResult result, int capacity) { |
749 | SensorAndMeasurements sam = run.getMeasurementsOfSensor(sensor); |
750 | double busyFraction = 0; |
751 | |
752 | double totalTime = 0; |
753 | double weightedSumOfJobs = 0; |
754 | |
755 | if (sam.getMeasurements().size() > 0){ |
756 | StateSensorToPieAdapter dataAdapter = new StateSensorToTimeDeltaPieAdapter( |
757 | sam); |
758 | AbstractPie pie = (AbstractPie) dataAdapter.getAdaptedObject(); |
759 | Collection<PieEntity> pieParts = pie |
760 | .getEntities(Integer.MAX_VALUE); |
761 | double totalIdleTime = 0; |
762 | // I need to sum up all pie parts to get the 100% comparison |
763 | |
764 | double totalWeights = 0; |
765 | int maxNumberOfJobs = 0; |
766 | |
767 | for (Iterator<PieEntity> iterator = pieParts.iterator(); iterator |
768 | .hasNext();) { |
769 | PieEntity pieEntity = iterator.next(); |
770 | totalTime += pieEntity.getValue(); |
771 | totalWeights += pieEntity.getValue(); |
772 | |
773 | if (pieEntity.getLabel().contains("Idle")) { |
774 | // this returns a large number > 399 |
775 | totalIdleTime = pieEntity.getValue(); |
776 | } else { |
777 | String[] busyLabel = pieEntity.getLabel().split(" "); |
778 | if (busyLabel.length > 1){ |
779 | try { |
780 | String numberOfJobsString = busyLabel[1]; |
781 | int noOfJobs = Integer.parseInt(numberOfJobsString); |
782 | totalWeights += pieEntity.getValue(); |
783 | weightedSumOfJobs += noOfJobs * pieEntity.getValue(); |
784 | if (noOfJobs > maxNumberOfJobs){ |
785 | maxNumberOfJobs = noOfJobs; |
786 | } |
787 | } catch (Exception e){ |
788 | logger.warn("Cannot read in queue lengths, labels of the pie chart may have changed."); |
789 | } |
790 | } else { |
791 | logger.warn("Cannot read in queue lengths, labels of the pie chart may have changed."); |
792 | } |
793 | } |
794 | |
795 | } |
796 | busyFraction = (1 - (totalIdleTime / totalTime)); |
797 | //queue length including the active job. |
798 | result.setAverageQueueLength(weightedSumOfJobs / totalWeights); |
799 | result.setMaxQueueLength(maxNumberOfJobs); |
800 | } |
801 | if (capacity == 1){ |
802 | result.setResourceUtilisation(busyFraction); |
803 | } else if (totalTime > 0){ |
804 | /* if capacity is larger than 1 (for passive resources), calculate |
805 | the interval below the line of current resource usage, which is |
806 | for each level of usage (noOfJobs) the time that it is active (x axis) |
807 | times the noOfJobs. Thus, this corresponds to the weightedSumOfJobs value. |
808 | Then divide by totalTime * capacity which is the maximum utilisation. |
809 | A value of 100% then means that all free resources have been used all the time. |
810 | */ |
811 | double util = weightedSumOfJobs / (totalTime * capacity); |
812 | result.setResourceUtilisation(util); |
813 | } |
814 | } |
815 | |
816 | @Override |
817 | public long getNumberOfObservations() { |
818 | return this.observations; |
819 | } |
820 | |
821 | @Override |
822 | public ResultDecoratorRepository getResults() { |
823 | return this.results; |
824 | } |
825 | |
826 | /**Prepares to export the measurements of a time series sensor to R. |
827 | * Therefore a temporary file is created and the R command line to |
828 | * import this data is returned. |
829 | * |
830 | * @param measurements Measurements for a sensor. |
831 | * @param dataSelection the data element to save. |
832 | * @return R command to read measurements. |
833 | * Can be used to store data in a r vector. |
834 | */ |
835 | /* private String prepareExportToRByFile( |
836 | final SensorAndMeasurements measurements, |
837 | final TimeseriesData dataSelection) { |
838 | File temporaryFile; |
839 | try { |
840 | temporaryFile = File.createTempFile("data", |
841 | (dataSelection == TimeseriesData.EVENTTIME) |
842 | ? "_et.txt" |
843 | : "_ts.txt" |
844 | ); |
845 | temporaryFile.deleteOnExit(); |
846 | FileWriter temporaryFileWriter = new FileWriter(temporaryFile); |
847 | StringBuffer result = new StringBuffer(); |
848 | for (Measurement time : measurements.getMeasurements()) { |
849 | TimeSpanMeasurement tsm = (TimeSpanMeasurement) time; |
850 | result.append( |
851 | (dataSelection == TimeseriesData.EVENTTIME) |
852 | ? tsm.getEventTime() |
853 | : tsm.getTimeSpan() |
854 | ); |
855 | result.append(" "); |
856 | } |
857 | temporaryFileWriter.write(result.toString()); |
858 | temporaryFileWriter.close(); |
859 | return "scan(file=\"" |
860 | + temporaryFile.getAbsolutePath().replace(File.separator, |
861 | "\\\\") |
862 | + "\")"; |
863 | } catch (IOException e) { |
864 | RVisualisationPlugin.log(IStatus.ERROR, |
865 | "Error accessing temporary file to transfer sensordata " |
866 | + "to R. \n\n Details: " + e.getMessage()); |
867 | } |
868 | return ""; |
869 | }*/ |
870 | |
871 | } |
872 | |
873 | class ExternalCallActionWithSensors { |
874 | |
875 | private ExternalCallAction externalCallAction; |
876 | private AssemblyContextContext assemblyContext; |
877 | private List<Sensor> sensors; |
878 | |
879 | public ExternalCallActionWithSensors(ExternalCallAction e, AssemblyContextContext myAssemblyContext){ |
880 | this.externalCallAction = e; |
881 | this.assemblyContext = myAssemblyContext; |
882 | this.sensors = new LinkedList<Sensor>(); |
883 | } |
884 | |
885 | public ExternalCallAction getExternalCall() { |
886 | return this.externalCallAction; |
887 | } |
888 | |
889 | public AssemblyContextContext getAssemblyContext() { |
890 | return this.assemblyContext; |
891 | } |
892 | |
893 | public void addSensor(Sensor s){ |
894 | this.sensors.add(s); |
895 | } |
896 | |
897 | public List<Sensor> getSensors(){ |
898 | return this.sensors; |
899 | } |
900 | |
901 | @Override |
902 | public boolean equals(Object o){ |
903 | if (this == o){ |
904 | return true; |
905 | } |
906 | if (o instanceof ExternalCallActionWithSensors){ |
907 | ExternalCallActionWithSensors other = (ExternalCallActionWithSensors)o; |
908 | return other.getAssemblyContext().equals(this.assemblyContext) |
909 | && other.getExternalCall().equals(this.externalCallAction); |
910 | } else { |
911 | return false; |
912 | } |
913 | |
914 | } |
915 | } |
916 | |
917 | class AssemblyContextContext { |
918 | |
919 | private AssemblyContext assemblyContext; |
920 | private AssemblyContextContext parentAssemblyContext; |
921 | |
922 | |
923 | public AssemblyContextContext(AssemblyContext thisContext){ |
924 | this.assemblyContext = thisContext; |
925 | } |
926 | |
927 | /** |
928 | * return composed id |
929 | * <this.id><parent.id><grandparent.id>... |
930 | * |
931 | * @return |
932 | */ |
933 | public String getComposedId() { |
934 | String parentID = ""; |
935 | if (this.hasParent()){ |
936 | parentID = this.getParentAssemblyContext().getComposedId(); |
937 | } |
938 | return this.assemblyContext.getId()+parentID; |
939 | } |
940 | |
941 | public boolean hasParent(){ |
942 | return this.parentAssemblyContext != null; |
943 | } |
944 | |
945 | /** |
946 | * @return the assemblyContext |
947 | */ |
948 | public AssemblyContext getAssemblyContext() { |
949 | return assemblyContext; |
950 | } |
951 | |
952 | /** |
953 | * @return the parentAssemblyContext |
954 | */ |
955 | public AssemblyContextContext getParentAssemblyContext() { |
956 | return parentAssemblyContext; |
957 | } |
958 | |
959 | public void setParent(AssemblyContextContext assemblyContextContext){ |
960 | this.parentAssemblyContext = assemblyContextContext; |
961 | } |
962 | |
963 | |
964 | } |