blob: f20086aee3130e85e1d38918faf3789b8dc2c067 [file] [log] [blame]
/**
* *******************************************************************************
* 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)
// }
// }
}