| /** |
| * ******************************************************************************* |
| * Copyright (c) 2019 Robert Bosch GmbH and others. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Robert Bosch GmbH - initial API and implementation |
| * ******************************************************************************* |
| */ |
| |
| package templates.m2m.constraints |
| |
| import com.google.inject.Inject |
| import com.google.inject.Singleton |
| import org.eclipse.app4mc.amalthea.model.EventChain |
| import templates.AbstractAmaltheaInchronTransformer |
| import org.eclipse.app4mc.amalthea.model.AbstractEventChain |
| import org.eclipse.app4mc.amalthea.model.Process |
| import templates.m2m.event.AbstractEventTransformer |
| import com.inchron.realtime.root.model.DataFlowEdge |
| import templates.m2m.sw.runnableItem.ChannelAccessTransformer |
| import templates.m2m.utils.DataFlowUtils |
| import org.eclipse.app4mc.amalthea.model.ChannelEvent |
| import org.eclipse.app4mc.amalthea.model.ChannelAccess |
| import templates.m2m.utils.EventChainCrawler |
| import templates.m2m.utils.EventSequenceUtils |
| |
| @Singleton |
| class EventChainTransformer extends AbstractAmaltheaInchronTransformer { |
| |
| @Inject AbstractEventTransformer abstractEventTransformer |
| @Inject ChannelAccessTransformer channelAccessTransformer |
| |
| def transformEventChain(EventChain amltEventChain) { |
| //check if amalthea structure is feasible to be transformed to data flow |
| //list of checks: |
| // - no circular dependencies within nested event chain references |
| // - channel event specifies: runnable, channel, access type (send or receive) |
| // - stimulus and response are of type channel event |
| // - stimulus and responsecan be traced to ONE specific channel access runnable item |
| // - rules above also apply to nested and referenced event chains |
| // Note: strands are not supported and will be neglected |
| val dataFlowCrawler = new EventChainCrawler[DataFlowUtils.testConditionEventChain2DataFlow(it)] |
| if (dataFlowCrawler.evaluate(amltEventChain)){ |
| dataFlowCrawler.applyOrder |
| dataFlowCrawler.applyBoundaries(amltEventChain.stimulus, amltEventChain.response) |
| val dataFlow = createDataFlow(amltEventChain) |
| val traversedEventChains = dataFlowCrawler.traversedEventChains |
| if (!traversedEventChains.isEmpty){ |
| for (AbstractEventChain eventChain : traversedEventChains){ |
| //create new edge |
| val DataFlowEdge dataFlowEdge = inchronModelFactory.createDataFlowEdge |
| // set stimulus & response |
| val Process stimulusTask = (eventChain.stimulus as ChannelEvent).process |
| val ChannelAccess stimulusChannelAccess = DataFlowUtils.findChannelAccess(eventChain.stimulus as ChannelEvent) |
| if (stimulusChannelAccess !== null){ |
| dataFlowEdge.stimulus = channelAccessTransformer.createVariableAccess(stimulusTask, stimulusChannelAccess) |
| } else { |
| dataFlowEdge.stimulus = null; |
| } |
| val Process responseTask = (eventChain.response as ChannelEvent).process |
| val ChannelAccess responseChannelAccess = DataFlowUtils.findChannelAccess(eventChain.response as ChannelEvent) |
| if (eventChain.response !== null){ |
| dataFlowEdge.response = channelAccessTransformer.createVariableAccess(responseTask, responseChannelAccess) |
| } else { |
| dataFlowEdge.response = null; |
| } |
| //add new edge dataflow |
| dataFlow.edges.add(dataFlowEdge) |
| } |
| getInchronRoot.eventChains.add(dataFlow) |
| }else{ |
| getLogger.error("Event chain " + amltEventChain.name + "'s elements are valid, but issues exist in sequence and/or boundaries") |
| } |
| return |
| } |
| |
| //check if amalthea structure is feasible to be transformed to event sequence |
| //list of checks: |
| // - no circular dependencies within nested event chain references |
| // - process event specifies: process & process be traced to inchron component |
| // - runnable event specifies: runnable, process & process be traced to inchron component |
| // - stimulus and response are both either of type runnable event or process event |
| // - rules above also apply to nested and referenced event chains |
| // Note: strands are not supported and will be neglected |
| val eventSequenceCrawler = new EventChainCrawler[EventSequenceUtils.testConditionEventChain2EventSequence(it)] |
| if (eventSequenceCrawler.evaluate(amltEventChain)){ |
| var eventSequence = createEventSequence(amltEventChain) |
| |
| //create trace events from (sorted) stimuli events of event chain |
| // events are sorted by stimulus/reponse: eg. EC1(stim=A, resp=B), EC2(stim=C, resp=D), EC3(stim=B, resp=C) |
| // sorting leads to: EC1, EC3, EC2 |
| // is transformed to event sequence: A--[loop]-->B--[loop]-->C--[add last]-->D |
| |
| // [sort] |
| eventSequenceCrawler.applyOrder |
| eventSequenceCrawler.applyBoundaries(amltEventChain.stimulus, amltEventChain.response) |
| val traversedEventChains = eventSequenceCrawler.traversedEventChains |
| if (!traversedEventChains.isEmpty){ |
| // [loop] |
| for (AbstractEventChain eventChain : traversedEventChains){ |
| var conditionalTraceEvent = getInchronModelFactory.createConditionalTraceEvent |
| conditionalTraceEvent.traceEvent = abstractEventTransformer.createTraceEvent(eventChain.stimulus) |
| eventSequence.sequence.add(conditionalTraceEvent) |
| } |
| // [add last] |
| var conditionalTraceEvent = getInchronModelFactory.createConditionalTraceEvent |
| conditionalTraceEvent.traceEvent = abstractEventTransformer.createTraceEvent(traversedEventChains.last.response) |
| eventSequence.sequence.add(conditionalTraceEvent) |
| //add event sequence to inchron root model |
| getInchronRoot.eventChains.add(eventSequence) |
| }else{ |
| getLogger.error("Event chain " + amltEventChain.name + "'s elements are valid, but issues exist in sequence and/or boundaries") |
| |
| } |
| return |
| } |
| |
| //default statement |
| getLogger.error("Event chain " + amltEventChain.name + " cannot be converted, as it is not a feasible data flow or event sequence") |
| |
| } |
| |
| def create inchronModelFactory.createDataFlow createDataFlow(EventChain amltEventChain) { |
| it.name = amltEventChain.name |
| } |
| |
| def create inchronModelFactory.createEventSequence createEventSequence(EventChain amltEventChain) { |
| it.name = amltEventChain.name |
| } |
| // |
| // |
| // def void extendDataFlow(DataFlow dataFlow, AbstractEventChain amltAbstractEventChain) { |
| // if (!amltAbstractEventChain.segments.isEmpty) { |
| // //event chain is hierarchical --> step down |
| // amltAbstractEventChain.segments.forEach[EventChainItem amltEventChainItem | { //item may be a container or a reference |
| // if (amltEventChainItem instanceof EventChainContainer){ |
| // //recursively process contained (sub)event chain |
| // extendDataFlow(dataFlow, (amltEventChainItem as EventChainContainer).eventChain) |
| // } else if (amltEventChainItem instanceof EventChainReference) { |
| // //create [cs]DataFlowReference from [amlt]EventChainReference |
| // |
| // dataFlow.references.add(eventChainReferenceTransformer.createDataFlowReference( |
| // (amltEventChainItem as EventChainReference), amltAbstractEventChain)) |
| // } |
| // }] |
| // } |
| // else { |
| // //create new edge |
| // val DataFlowEdge dataFlowEdge = inchronModelFactory.createDataFlowEdge |
| // |
| // //set stimulus and receiver according to channel events |
| // val Process stimulusTask = (amltAbstractEventChain.stimulus as ChannelEvent).process |
| // val ChannelAccess stimulus = DataFlowUtils.findChannelAccess(amltAbstractEventChain.stimulus as ChannelEvent) |
| // if (stimulus !== null){ |
| // dataFlowEdge.stimulus = channelAccessTransformer.createVariableAccess(stimulusTask, stimulus) |
| // } else { |
| // dataFlowEdge.stimulus = null; |
| // } |
| // |
| // val Process responseTask = (amltAbstractEventChain.response as ChannelEvent).process |
| // val ChannelAccess response = DataFlowUtils.findChannelAccess(amltAbstractEventChain.response as ChannelEvent) |
| // if (response !== null){ |
| // dataFlowEdge.response = channelAccessTransformer.createVariableAccess(responseTask, response) |
| // } else { |
| // dataFlowEdge.response = null; |
| // } |
| // |
| // //add new edge dataflow |
| // dataFlow.edges.add(dataFlowEdge) |
| // } |
| // } |
| |
| } |