| 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 | } |