| /******************************************************************************* |
| * Copyright (c) 2014-2016 Akos Horvath, Abel Hegedus, Tamas Borbas, Marton Bur, Zoltan Ujhelyi, Robert Doczi, IncQuery Labs Ltd. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-v20.html. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| *******************************************************************************/ |
| package org.eclipse.viatra.examples.cps.xform.m2m.batch.eiq |
| |
| import com.google.common.base.Stopwatch |
| import com.google.common.collect.HashBasedTable |
| import com.google.common.collect.Maps |
| import com.google.common.collect.Table |
| import java.util.List |
| import java.util.Map |
| import java.util.concurrent.TimeUnit |
| import org.apache.log4j.Logger |
| import org.eclipse.viatra.examples.cps.cyberPhysicalSystem.ApplicationInstance |
| import org.eclipse.viatra.examples.cps.cyberPhysicalSystem.ApplicationType |
| import org.eclipse.viatra.examples.cps.cyberPhysicalSystem.CyberPhysicalSystem |
| import org.eclipse.viatra.examples.cps.cyberPhysicalSystem.HostInstance |
| import org.eclipse.viatra.examples.cps.cyberPhysicalSystem.Identifiable |
| import org.eclipse.viatra.examples.cps.cyberPhysicalSystem.State |
| import org.eclipse.viatra.examples.cps.cyberPhysicalSystem.StateMachine |
| import org.eclipse.viatra.examples.cps.cyberPhysicalSystem.Transition |
| import org.eclipse.viatra.examples.cps.deployment.BehaviorState |
| import org.eclipse.viatra.examples.cps.deployment.BehaviorTransition |
| import org.eclipse.viatra.examples.cps.deployment.DeploymentApplication |
| import org.eclipse.viatra.examples.cps.deployment.DeploymentBehavior |
| import org.eclipse.viatra.examples.cps.deployment.DeploymentElement |
| import org.eclipse.viatra.examples.cps.deployment.DeploymentFactory |
| import org.eclipse.viatra.examples.cps.deployment.DeploymentHost |
| import org.eclipse.viatra.examples.cps.traceability.CPS2DeploymentTrace |
| import org.eclipse.viatra.examples.cps.traceability.CPSToDeployment |
| import org.eclipse.viatra.examples.cps.traceability.TraceabilityFactory |
| import org.eclipse.viatra.examples.cps.xform.m2m.batch.eiq.queries.Cps2DepTraces |
| import org.eclipse.viatra.examples.cps.xform.m2m.batch.eiq.queries.CpsXformM2M |
| import org.eclipse.viatra.query.runtime.api.AdvancedViatraQueryEngine |
| import org.eclipse.viatra.query.runtime.api.GenericQueryGroup |
| import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint |
| |
| import static com.google.common.base.Preconditions.* |
| |
| import static extension org.eclipse.viatra.examples.cps.xform.m2m.util.NamingUtil.* |
| |
| class CPS2DeploymentBatchTransformationEiq { |
| |
| extension protected Logger logger = Logger.getLogger("cps.xform.m2m.batch.eiq") |
| extension protected CpsXformM2M cpsXformM2M = CpsXformM2M.instance |
| extension protected Cps2DepTraces cpsTraces = Cps2DepTraces.instance |
| |
| DeploymentFactory depFactory = DeploymentFactory.eINSTANCE |
| TraceabilityFactory tracFactory = TraceabilityFactory.eINSTANCE |
| |
| CPSToDeployment mapping |
| protected AdvancedViatraQueryEngine engine |
| protected QueryEvaluationHint hint |
| protected QueryEvaluationHint tracesHint |
| |
| Stopwatch clearModelPerformance; |
| Stopwatch hostTransformationPerformance; |
| Stopwatch appTransformationPerformance; |
| Stopwatch stateMachineTransformationPerformance; |
| Stopwatch stateTransformationPerformance; |
| Stopwatch transitionTransformationPerformance; |
| Stopwatch triggerTransformationPerformance; |
| |
| Stopwatch otherTimer |
| |
| Table<State, DeploymentBehavior, BehaviorState> stateTable |
| Map<Identifiable, CPS2DeploymentTrace> traceTable |
| |
| /** |
| * Initializes a new instance of the transformation using the specified |
| * model and IncQuery engine. |
| * |
| * @param mapping |
| * The traceability model cntaining the cps and deployment |
| * models. The transformation will be run on this. |
| * @param engine |
| * The IncQuery engine initialized on the mapping. |
| * |
| * @throws IllegalArgumentException |
| * If either of the input arguments are null, or the mapping |
| * does not contain a cps and a deployment model. |
| */ |
| new(CPSToDeployment mapping, AdvancedViatraQueryEngine engine, QueryEvaluationHint hint, QueryEvaluationHint tracesHint) { |
| checkArgument(mapping !== null, "Mapping cannot be null!") |
| checkArgument(mapping.cps !== null, "CPS not defined in mapping!") |
| checkArgument(mapping.deployment !== null, "Deployment not defined in mapping!") |
| checkArgument(engine !== null, "Engine cannot be null!") |
| |
| this.mapping = mapping |
| this.engine = engine |
| this.hint = hint |
| this.tracesHint = tracesHint |
| |
| debug("Preparing queries on engine.") |
| val watch = Stopwatch.createStarted |
| if(hint != tracesHint){ |
| engine.prepareGroup(cpsXformM2M, hint) |
| engine.prepareGroup(cpsTraces, tracesHint) |
| } else { |
| engine.prepareGroup(GenericQueryGroup.of(cpsXformM2M, cpsTraces), hint); |
| } |
| watch.stop |
| info('''Prepared queries on engine («watch.elapsed(TimeUnit.MILLISECONDS)» ms)''') |
| } |
| |
| /** |
| * Runs the transformation on the model the class was initialized on. |
| */ |
| def execute() { |
| initPerformanceTimers() |
| |
| clearModelPerformance.start |
| clearModel |
| clearModelPerformance.stop |
| |
| info( |
| ''' |
| Executing transformation on: |
| Cyber-physical system: «mapping.cps.identifier»''') |
| |
| stateTable = HashBasedTable.create |
| traceTable = Maps.newHashMap |
| |
| debug("Running host transformations.") |
| mapping.cps.hostTypes.map[instances].flatten.forEach[transform] |
| |
| debug("Running action transformations.") |
| transformActions() |
| |
| reportPerformance |
| } |
| |
| protected def void transformActions() { |
| engine.getMatcher(depTransition, hint).allMatches.map[depTransition].forEach[mapAction] |
| } |
| |
| private def initPerformanceTimers() { |
| clearModelPerformance = Stopwatch.createUnstarted |
| hostTransformationPerformance = Stopwatch.createUnstarted |
| appTransformationPerformance = Stopwatch.createUnstarted |
| stateMachineTransformationPerformance = Stopwatch.createUnstarted |
| stateTransformationPerformance = Stopwatch.createUnstarted |
| transitionTransformationPerformance = Stopwatch.createUnstarted |
| triggerTransformationPerformance = Stopwatch.createUnstarted |
| |
| otherTimer = Stopwatch.createUnstarted |
| } |
| |
| private def reportPerformance() { |
| info( |
| ''' |
| >>>Cleared model in: «clearModelPerformance.elapsed(TimeUnit.MILLISECONDS)» ms |
| >>>Host transformation: «hostTransformationPerformance.elapsed(TimeUnit.MILLISECONDS)» ms |
| >>>Application Instance transformation: «appTransformationPerformance.elapsed(TimeUnit.MILLISECONDS)» ms |
| >>>State Machine transformation: «stateMachineTransformationPerformance.elapsed(TimeUnit.MILLISECONDS)» ms |
| >>>State transformation: «stateTransformationPerformance.elapsed(TimeUnit.MILLISECONDS)» ms |
| >>>Transition transformation: «transitionTransformationPerformance.elapsed(TimeUnit.MILLISECONDS)» ms |
| >>>Trigger transformation: «triggerTransformationPerformance.elapsed(TimeUnit.MILLISECONDS)» ms |
| >>>Other perf: «otherTimer.elapsed(TimeUnit.MILLISECONDS)» ms''') |
| } |
| |
| /** |
| * Runs the transformation on the provided {@link HostInstance}. Creates the |
| * {@link DeploymentHost} in the deployment model and also runs the |
| * transformations of the applications. |
| * |
| * @param cpsHost |
| * The host to be transformed. |
| */ |
| private def DeploymentHost transform(HostInstance cpsHost) { |
| trace('''Executing: transform(cpsHost = «cpsHost.name»)''') |
| hostTransformationPerformance.start |
| val depHost = cpsHost.createDepHost |
| |
| debug('''Adding host («depHost.description») to deployment model.''') |
| mapping.deployment.hosts += depHost |
| addTrace(cpsHost, depHost) |
| |
| hostTransformationPerformance.stop |
| debug("Running application instance transformations.") |
| cpsHost.applications.filter[type?.cps == mapping.cps].forEach [ |
| transform(depHost) |
| ] |
| debug('''Running application instance transformations finished''') |
| trace('''Execution ended: transform''') |
| return depHost |
| } |
| |
| /** |
| * Runs the transformation on an {@link ApplicationInstance}. Creates the |
| * {@link DeploymentApplication} in the deployment model. Also creates an |
| * instance of the {@link StateMachine} referred in the |
| * {@link ApplicationType}. |
| * |
| * @param cpsInstance |
| * The application instance to be transformed. |
| * @param depHost |
| * The parent which will contain the transformed application. |
| */ |
| protected def transform(ApplicationInstance cpsInstance, DeploymentHost depHost) { |
| trace('''Executing: transform(cpsInstance = «cpsInstance.name», depHost = «depHost.name»)''') |
| appTransformationPerformance.start |
| val depApp = cpsInstance.createDepApplication |
| |
| depHost.applications += depApp |
| addTrace(cpsInstance, depApp) |
| |
| appTransformationPerformance.stop |
| debug("Running state machine transformations.") |
| val watch = Stopwatch.createStarted |
| cpsInstance.type.behavior?.transform(depApp) |
| debug('''Running state machine transformations («watch.elapsed(TimeUnit.MILLISECONDS)» ms)''') |
| trace('''Execution ended: transform''') |
| } |
| |
| /** |
| * Runs the transformation of a {@link StateMachine}. Creates the |
| * {@link DeploymentBehavior} in the deployment model. Also creates the |
| * {@link BehaviorState}s and {@link BehaviorTransition}s in the transformed |
| * state machine, furthermore sets the current state in the transformed |
| * state machine to the initial state of the cps state machine. |
| * |
| * @param cpsBehavior |
| * The state machine to be transformed. |
| * @param depApp |
| * The parent which will contain the transformed state machine. |
| */ |
| private def transform(StateMachine cpsBehavior, DeploymentApplication depApp) { |
| trace('''Executing: transform(cpsBehavior = «cpsBehavior.name», depApp = «depApp.name»)''') |
| stateMachineTransformationPerformance.start |
| val depBehavior = cpsBehavior.createDepBehavior |
| |
| depApp.behavior = depBehavior |
| addTraceOneToN(cpsBehavior, #[depBehavior]) |
| |
| stateMachineTransformationPerformance.stop |
| debug("Running state transformations.") |
| val watch = Stopwatch.createStarted |
| cpsBehavior.states.forEach [ |
| transform(depBehavior) |
| ] |
| debug('''Running state transformations finished''') |
| |
| debug("Resolving state relationships.") |
| watch.reset.start |
| cpsBehavior.states.forEach [ |
| buildStateRelations(depBehavior, cpsBehavior) |
| ] |
| debug('''Resolving state relationships finished''') |
| |
| debug("Resolving initial state.") |
| stateMachineTransformationPerformance.start |
| watch.reset.start |
| if (cpsBehavior.initial !== null) |
| depBehavior.current = engine.getMatcher(cps2depTrace, tracesHint).getAllMatches(null, cpsBehavior.initial, null).map[ |
| depElement].filter(BehaviorState).findFirst[depBehavior.states.contains(it)] |
| else |
| depBehavior.current = null |
| stateMachineTransformationPerformance.stop |
| debug('''Resolving initial state finished''') |
| trace('''Execution ended: transform''') |
| } |
| |
| /** |
| * Runs the transformation of a {@link State}. Creates the |
| * {@link BehaviorState} in the deployment model. |
| * |
| * @param cpsState |
| * The state to be transformed. |
| * @param depBehavior |
| * The parent which will contain the transformed state. |
| */ |
| private def transform(State cpsState, DeploymentBehavior depBehavior) { |
| trace('''Executing: transform(cpsState = «cpsState.name», depBehavior = «depBehavior.name»)''') |
| stateTransformationPerformance.start |
| val depState = cpsState.createDepState |
| |
| depBehavior.states += depState |
| addTraceOneToN(cpsState, #[depState]) |
| |
| stateTable.put(cpsState, depBehavior, depState) |
| |
| stateTransformationPerformance.stop |
| trace('''Execution ended: transform''') |
| } |
| |
| /** |
| * Builds the relationships between {@link BehaviorState}s. For each |
| * {@link Transition} in a {@link State} it sets the corresponding |
| * {@link BehaviorTransition}'s to property to the proper |
| * {@link BehaviorTransition}. |
| * |
| * @param cpsState |
| * The state for which the relation will be built. |
| * @param depBehavior |
| * The state from cps model. |
| * @param cpsBehavior |
| * The state from the deployment model. |
| */ |
| private def buildStateRelations(State cpsState, DeploymentBehavior depBehavior, StateMachine cpsBehavior) { |
| trace( |
| '''Executing: buildStateRelations(cpsState = «cpsState.name», depBehavior = «depBehavior.name», cpsBehavior = «cpsBehavior. |
| name»)''') |
| transitionTransformationPerformance.start |
| |
| // val depState = engine.getMatcher(cps2depTrace.getAllMatches(mapping, null, cpsState, null).map[depElement].filter( |
| // BehaviorState).findFirst[depBehavior.states.contains(it)] |
| val depState = stateTable.get(cpsState, depBehavior) |
| cpsState.outgoingTransitions.filter[targetState !== null && cpsBehavior.states.contains(targetState)].forEach [ |
| mapTransition(depState, depBehavior) |
| ] |
| transitionTransformationPerformance.stop |
| trace('''Execution ended: buildStateRelations''') |
| } |
| |
| /** |
| * Creates a {@link BehaviorTransition} representing the {@link Transition} |
| * provided as a parameter, and adds it to its parent. |
| * |
| * @param transition |
| * The transitions to be transformed. |
| * @param depState The state which will refer to the transition. |
| * @param depBehavior The deployment state machine to which the new transition will be added to. |
| */ |
| private def mapTransition(Transition transition, BehaviorState depState, DeploymentBehavior depBehavior) { |
| trace( |
| '''Executing: mapTransition(transition = «transition.name», depState = «depState.name», depBehavior = «depBehavior. |
| name»)''') |
| val depTransition = transition.createDepTransition |
| |
| depState.outgoing += depTransition |
| depBehavior.transitions += depTransition |
| otherTimer.start |
| addTraceOneToN(transition, #[depTransition]) |
| otherTimer.stop |
| depTransition.to = engine.getMatcher(cps2depTrace, tracesHint).getAllMatches(null, transition.targetState, null).map[ |
| depElement].filter(BehaviorState).findFirst [ |
| depBehavior.states.contains(it) |
| ] |
| trace('''Execution ended: mapTransition''') |
| } |
| |
| /** |
| * Sets the <i>trigger<i> value of the {@link BehaviorTransition} depending |
| * on the actions in the {@link CyberPhysicalSystem} model. |
| * |
| * @param depTrigger |
| * The transition for which the trigger will be set. |
| */ |
| private def mapAction(BehaviorTransition depSendTransition) { |
| trace('''Executing: mapAction(depTrigger = «depSendTransition.name»)''') |
| triggerTransformationPerformance.start |
| |
| val cpsSendTransition = engine.getMatcher(cps2depTrace, tracesHint).getAllValuesOfcpsElement(null, depSendTransition).head as Transition |
| val cpsWaitTransitions = getWaitTransitionsForSendTransition(cpsSendTransition) |
| |
| if(!cpsWaitTransitions.empty){ |
| val senderDepApp = depSendTransition.eContainer.eContainer as DeploymentApplication |
| val cpsSendAppInstance = engine.getMatcher(cps2depTrace, tracesHint).getAllValuesOfcpsElement(null, senderDepApp).head as ApplicationInstance |
| |
| cpsWaitTransitions.forEach[cpsWaitTransition | |
| val cpsWaitAppInstances = engine.getMatcher(cpsApplicationTransition, hint).getAllValuesOfcpsApp(cpsWaitTransition) |
| val communicatingWaitAppInstances = cpsWaitAppInstances.filter[ |
| val hasMatch = engine.getMatcher(communicatingAppInstances, hint).hasMatch(cpsSendAppInstance, it) |
| return hasMatch |
| ] |
| communicatingWaitAppInstances.forEach[cpsWaitAppInstance | |
| val waitTransitionTrace = engine.getMatcher(cps2depTrace, tracesHint).getAllValuesOftrace(cpsWaitTransition, null).filter(CPS2DeploymentTrace).head |
| val waitAppInstanceTrace = engine.getMatcher(cps2depTrace, tracesHint).getAllValuesOftrace(cpsWaitAppInstance, null).filter(CPS2DeploymentTrace).head |
| |
| val depWaitApp = waitAppInstanceTrace.deploymentElements.filter(DeploymentApplication).head |
| val depWaitTransition = waitTransitionTrace.deploymentElements.filter(BehaviorTransition).findFirst[ |
| depWaitApp == it.eContainer.eContainer |
| ] |
| depSendTransition.trigger += depWaitTransition |
| ] |
| ] |
| } |
| |
| triggerTransformationPerformance.stop |
| trace('''Execution ended: mapAction''') |
| } |
| |
| protected def Iterable<Transition> getWaitTransitionsForSendTransition(Transition cpsSendTransition) { |
| engine.getMatcher(triggerPair, hint).getAllValuesOfcpsTarget(cpsSendTransition).filter(Transition) |
| } |
| |
| /** |
| * Creates a {@link DeploymentHost} representing the {@link HostInstance} |
| * provided in the parameter. Furthermore it sets the host's <i>Ip</i>. |
| * |
| * @param cpsHost |
| * The base of the creation. |
| * @return The created deployment host |
| */ |
| private def createDepHost(HostInstance cpsHost) { |
| trace('''Executing: createDepHost(cpsHost = «cpsHost.name»)''') |
| val depHost = depFactory.createDeploymentHost |
| |
| depHost.ip = cpsHost.nodeIp |
| trace('''Execution ended: createDepHost''') |
| depHost |
| } |
| |
| /** |
| * Creates a {@link DeploymentApplication} representing the {@link ApplicationInstance} |
| * provided in the parameter. Furthermore it sets the application's <i>id</i>. |
| * |
| * @param cpsHost |
| * The base of the creation. |
| * @return The created deployment host |
| */ |
| private def createDepApplication(ApplicationInstance cpsAppInstance) { |
| trace('''Executing: createDepApplication(cpsAppInstance = «cpsAppInstance.name»)''') |
| val depApp = depFactory.createDeploymentApplication |
| |
| depApp.id = cpsAppInstance.identifier |
| trace('''Execution: createDepApplication''') |
| depApp |
| } |
| |
| /** |
| * Creates a {@link DeploymentBehavior} representing the {@link StateMachine} |
| * provided in the parameter. Furthermore it sets the behavior's <i>description</i>. |
| * |
| * @param cpsHost |
| * The base of the creation. |
| * @return The created deployment host |
| */ |
| private def createDepBehavior(StateMachine cpsBehavior) { |
| trace('''Executing: createDepBehavior(cpsBehavior = «cpsBehavior.name»)''') |
| val depBehavior = depFactory.createDeploymentBehavior |
| |
| depBehavior.description = cpsBehavior.identifier |
| trace('''Execution ended: createDepBehavior''') |
| depBehavior |
| } |
| |
| /** |
| * Creates a {@link BehaviorState} representing the {@link State} |
| * provided in the parameter. Furthermore it sets the state's <i>description</i> |
| * |
| * @param cpsHost |
| * The base of the creation. |
| * @return The created deployment host |
| */ |
| private def createDepState(State cpsState) { |
| trace('''Executing: createDepState(cpsState = «cpsState.name»)''') |
| val depState = depFactory.createBehaviorState |
| |
| depState.description = cpsState.identifier |
| trace('''Execution ended: createDepState''') |
| depState |
| } |
| |
| /** |
| * Creates a {@link BehaviorTransition} representing the {@link Transition} |
| * provided in the parameter. Furthermore it sets the transition's <i>description</i> |
| * |
| * @param cpsHost |
| * The base of the creation. |
| * @return The created deployment host |
| */ |
| private def createDepTransition(Transition cpsTransition) { |
| trace('''Executing: createDepTransition(cpsTransition = «cpsTransition.name»)''') |
| val depTransition = depFactory.createBehaviorTransition |
| |
| depTransition.description = cpsTransition.identifier |
| trace('''Execution ended: createDepTransition''') |
| depTransition |
| } |
| |
| /** |
| * Clears the initial model, removing every trace and host (thus deployment application etc.) from it. |
| */ |
| private def clearModel() { |
| trace('''Executing: clearModel()''') |
| mapping.traces.clear |
| mapping.deployment.hosts.clear |
| trace('''Execution ended: clearModel''') |
| } |
| |
| /** |
| * Creates a 1-N trace between the specified elements and adds it to the traceability model. |
| * @param cpsElement The element in the cps model |
| * @param depElements The elements in the deployment model |
| */ |
| private def addTraceOneToN(Identifiable cpsElement, List<? extends DeploymentElement> depElements) { |
| trace( |
| '''Executing: addTraceOneToN(cpsElement = «cpsElement.name», depElements = [«FOR e : depElements SEPARATOR ", "»«e. |
| name»«ENDFOR»])''') |
| |
| //var trace = engine.getMatcher(cps2depTrace.getOneArbitraryMatch(mapping, null, cpsElement, null)?.trace |
| var trace = traceTable.get(cpsElement) |
| if (trace === null) { |
| trace = tracFactory.createCPS2DeploymentTrace |
| traceTable.put(cpsElement, trace) |
| |
| trace.cpsElements += cpsElement |
| } |
| trace.deploymentElements += depElements |
| |
| debug( |
| '''Adding trace («cpsElement.name»->[«FOR e : depElements SEPARATOR ", "»«e.name»«ENDFOR»]) to traceability model.''') |
| mapping.traces += trace |
| trace('''Execution ended: addTraceOneToN''') |
| } |
| |
| /** |
| * Creates a 1-1 trace between the specified elements and adds it to the traceability model. |
| * @param cpsElement The element in the cps model |
| * @param depElements The element in the deployment model |
| */ |
| private def addTrace(Identifiable cpsElement, DeploymentElement depElement) { |
| trace('''Executing: addTrace(cpsElement = «cpsElement.name», depElement = «depElement.name»)''') |
| val trace = tracFactory.createCPS2DeploymentTrace |
| |
| trace.cpsElements += cpsElement |
| trace.deploymentElements += depElement |
| |
| debug('''Adding trace («cpsElement.name»->«depElement.name») to traceability model.''') |
| mapping.traces += trace |
| trace('''Execution ended: addTrace''') |
| } |
| } |