diff options
Diffstat (limited to 'plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse')
13 files changed, 759 insertions, 542 deletions
diff --git a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/CodegenHelpers.java b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/CodegenHelpers.java index 891c6c789..4b7d5f8a3 100644 --- a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/CodegenHelpers.java +++ b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/CodegenHelpers.java @@ -15,9 +15,9 @@ package org.eclipse.etrice.generator.fsm.base; import org.eclipse.etrice.core.fsm.fSM.State; import org.eclipse.etrice.core.fsm.fSM.StateGraphItem; import org.eclipse.etrice.core.fsm.fSM.Transition; +import org.eclipse.etrice.core.fsm.fSM.TransitionBase; import org.eclipse.etrice.core.fsm.naming.FSMNameProvider; import org.eclipse.etrice.core.fsm.util.FSMHelpers; -import org.eclipse.etrice.core.genmodel.fsm.fsmgen.TransitionChain; /** * @author Henrik Rentz-Reichert @@ -32,7 +32,7 @@ public class CodegenHelpers { * @param t a {@link Transition} * @return a name for the action code operation the generator will generate */ - public String getActionCodeOperationName(Transition t) { + public String getActionCodeOperationName(TransitionBase t) { return "action_"+fsmNameProvider.getFullPath(t); } @@ -61,11 +61,11 @@ public class CodegenHelpers { } /** - * @param tc a {@link TransitionChain} + * @param tc a {@link Transition} * @return a name for the constant transition chain ID the generator will generate */ - public String getGenChainId(TransitionChain tc) { - return "CHAIN_"+fsmNameProvider.getFullPath(tc.getTransition()); + public String getGenChainId(TransitionBase tc) { + return "CHAIN_"+fsmNameProvider.getFullPath(tc); } /** diff --git a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/DefaultFSMTranslationProvider.java b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/DefaultFSMTranslationProvider.java index a96374d8f..48311b750 100644 --- a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/DefaultFSMTranslationProvider.java +++ b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/DefaultFSMTranslationProvider.java @@ -19,7 +19,7 @@ import org.eclipse.etrice.core.fsm.fSM.AbstractInterfaceItem; import org.eclipse.etrice.core.fsm.fSM.DetailCode; import org.eclipse.etrice.core.fsm.naming.FSMNameProvider; import org.eclipse.etrice.core.fsm.util.FSMHelpers; -import org.eclipse.etrice.core.genmodel.fsm.base.ILogger; +import org.eclipse.etrice.core.genmodel.fsm.ILogger; import com.google.inject.Inject; diff --git a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/Diagnostician.java b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/Diagnostician.java index a9f31a71e..832693c9e 100644 --- a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/Diagnostician.java +++ b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/Diagnostician.java @@ -15,7 +15,7 @@ package org.eclipse.etrice.generator.fsm.base; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.etrice.core.fsm.naming.FSMNameProvider; -import org.eclipse.etrice.core.genmodel.fsm.fsmgen.IDiagnostician; +import org.eclipse.etrice.core.genmodel.fsm.IDiagnostician; import com.google.inject.Inject; diff --git a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/ILineOutputLogger.java b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/ILineOutputLogger.java index e3bddc633..6c6379337 100644 --- a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/ILineOutputLogger.java +++ b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/ILineOutputLogger.java @@ -12,7 +12,7 @@ package org.eclipse.etrice.generator.fsm.base; -import org.eclipse.etrice.core.genmodel.fsm.base.ILogger; +import org.eclipse.etrice.core.genmodel.fsm.ILogger; /** diff --git a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/IncrementalGenerationFileIo.java b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/IncrementalGenerationFileIo.java index 8c227acdd..5a0996a90 100644 --- a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/IncrementalGenerationFileIo.java +++ b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/IncrementalGenerationFileIo.java @@ -17,7 +17,7 @@ import java.io.IOException; import java.util.zip.CRC32; import org.apache.commons.io.FileUtils; -import org.eclipse.etrice.core.genmodel.fsm.base.ILogger; +import org.eclipse.etrice.core.genmodel.fsm.ILogger; import org.eclipse.xtext.generator.JavaIoFileSystemAccess; import org.eclipse.xtext.util.RuntimeIOException; diff --git a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/Logger.java b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/Logger.java index d18e660c5..9eadc3c07 100644 --- a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/Logger.java +++ b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/Logger.java @@ -27,13 +27,25 @@ import org.eclipse.emf.ecore.util.EcoreUtil; */ public class Logger implements ILineOutputLogger { + private int errors = 0; private ILineOutput output = null; + + @Override + public boolean hasErrors() { + return errors!=0; + } public void logInfo(String text) { println("Info: " + text); } + @Override + public void logError(String text) { + logError(text, null); + } + public void logError(String text, EObject obj) { + ++errors; if (obj == null) println("Error: " + text); else { diff --git a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/NullLogger.java b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/NullLogger.java index 73c475986..2aec3f0e7 100644 --- a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/NullLogger.java +++ b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/base/NullLogger.java @@ -1,16 +1,29 @@ package org.eclipse.etrice.generator.fsm.base; import org.eclipse.emf.ecore.EObject; -import org.eclipse.etrice.core.genmodel.fsm.base.ILogger; +import org.eclipse.etrice.core.genmodel.fsm.ILogger; public class NullLogger implements ILogger { + private int errors = 0; + @Override public void logInfo(String text) { } @Override public void logError(String text, EObject obj) { + ++errors; } + @Override + public void logError(String text) { + ++errors; + } + + @Override + public boolean hasErrors() { + return errors!=0; + } + } diff --git a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/AbstractStateMachineGenerator.xtend b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/AbstractStateMachineGenerator.xtend index f86f9679a..64609294c 100644 --- a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/AbstractStateMachineGenerator.xtend +++ b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/AbstractStateMachineGenerator.xtend @@ -13,225 +13,232 @@ package org.eclipse.etrice.generator.fsm.generic import com.google.inject.Inject -import java.util.ArrayList -import java.util.List import org.eclipse.etrice.core.fsm.fSM.ComponentCommunicationType import org.eclipse.etrice.core.fsm.fSM.GuardedTransition +import org.eclipse.etrice.core.fsm.fSM.MessageFromIf import org.eclipse.etrice.core.fsm.fSM.ModelComponent import org.eclipse.etrice.core.fsm.fSM.State import org.eclipse.etrice.core.fsm.fSM.Transition import org.eclipse.etrice.core.fsm.fSM.TransitionPoint -import org.eclipse.etrice.core.fsm.fSM.TriggeredTransition +import org.eclipse.etrice.core.fsm.naming.FSMNameProvider import org.eclipse.etrice.core.fsm.util.FSMHelpers -import org.eclipse.etrice.core.genmodel.fsm.fsmgen.ActiveTrigger -import org.eclipse.etrice.core.genmodel.fsm.fsmgen.ExpandedModelComponent -import org.eclipse.etrice.core.genmodel.fsm.fsmgen.util.FsmGenUtil +import org.eclipse.etrice.core.genmodel.fsm.TriggerExtensions +import org.eclipse.etrice.core.genmodel.fsm.fsmgen.CommonTrigger +import org.eclipse.etrice.core.genmodel.fsm.fsmgen.GraphContainer +import org.eclipse.etrice.core.genmodel.fsm.fsmgen.Link +import org.eclipse.etrice.core.genmodel.fsm.fsmgen.Node import org.eclipse.etrice.generator.fsm.base.CodegenHelpers import org.eclipse.xtext.util.Pair import static org.eclipse.xtext.util.Tuples.* +import static extension org.eclipse.etrice.core.genmodel.fsm.FsmGenExtensions.* +import java.util.ArrayList + /** * @author Henrik Rentz-Reichert * */ abstract class AbstractStateMachineGenerator { - @Inject public extension FSMHelpers - @Inject public extension FsmGenUtil - @Inject public extension CodegenHelpers - @Inject public extension FSMExtensions - @Inject public ILanguageExtensionBase langExt - @Inject public IMessageIdGenerator msgIdGen - @Inject public IIfItemIdGenerator itemIdGen + @Inject public extension FSMHelpers + @Inject public extension CodegenHelpers + @Inject public extension FSMExtensions + @Inject public ILanguageExtensionBase langExt + @Inject public IMessageIdGenerator msgIdGen + @Inject public IIfItemIdGenerator itemIdGen @Inject public TransitionChainGenerator transitionChainGenerator @Inject public IDetailCodeTranslator translator - - /** - * generates trigger IDs. - * Inheritance (if available) is used for base class IDs. - * - * @param xpmc the {@link ExpandedModelComponent} - * @return the generated code - */ - def public String genTriggerConstants(ExpandedModelComponent xpmc) { - xpmc.genTriggerConstants(langExt.usesInheritance) - } - - /** - * generates trigger IDs. - * Inheritance (if available) is used for base class IDs. - * - * @param xpmc the {@link ExpandedModelComponent} - * @param omitBase use <code>true</code> if no base class trigger constants are needed - * - * @return the generated code - */ - def public String genTriggerConstants(ExpandedModelComponent xpmc, boolean omitBase) { - val triggers = if (omitBase) - xpmc.modelComponent.ownMessagesFromInterfaces - else xpmc.modelComponent.allMessagesFromInterfaces - - val list = new ArrayList<Pair<String, String>>() - list.add(pair("POLLING", "0")); - for (mif : triggers) { - if (mif.from.eventDriven) { - list.add(pair(xpmc.getTriggerCodeName(mif), itemIdGen.getIfItemId(mif.from)+" + EVT_SHIFT*"+msgIdGen.getMessageID(mif))) - } - } - - return langExt.genEnumeration("triggers", list) - } - - /** - * generates state ID constants. - * Inheritance (if available) is used for base class IDs. - * - * @param xpmc the {@link ExpandedModelComponent} - * @return the generated code - */ - def public genStateIdConstants(ExpandedModelComponent xpmc) { - xpmc.genStateIdConstants(langExt.usesInheritance) - } - - /** - * generates state ID constants. - * Inheritance (if available) is used for base class IDs. - * - * @param xpmc the {@link ExpandedModelComponent} - * @param omitBase use <code>true</code> if no base class state constants are needed - * - * @return the generated code - */ - def public genStateIdConstants(ExpandedModelComponent xpmc, boolean omitBase) { - val mc = xpmc.modelComponent - // with inheritance we exclude inherited base states - var offset = 2 + if (omitBase) - mc.getNumberOfInheritedBaseStates() else 0 - var baseStates = if (omitBase) - mc.stateMachine.getBaseStateList else xpmc.stateMachine.getBaseStateList - - baseStates = baseStates.leafStatesLast - - var list = new ArrayList<Pair<String, String>>() - if (!omitBase) { - list.add(pair("NO_STATE","0")) - list.add(pair("STATE_TOP","1")) - } - for (state : baseStates) { - list.add(pair(state.getGenStateId, offset.toString)) - offset = offset+1; - } - list.add(pair("STATE_MAX", offset.toString)) - - return langExt.genEnumeration("state_ids", list) - } - - /** - * generates transition chain ID constants. - * Inheritance can't be used used for base class IDs because of corner cases - * where base class and derived class chain IDs deviate (see bug 501354). - * - * @param xpmc the {@link ExpandedModelComponent} - * - * @return the generated code - */ - def public genTransitionChainConstants(ExpandedModelComponent xpmc) { - xpmc.genTransitionChainConstants(false/*langExt.usesInheritance*/) - } - - /** - * generates transition chain ID constants. - * Inheritance can't be used used for base class IDs because of corner cases - * where base class and derived class chain IDs deviate. - * - * @param xpmc the {@link ExpandedModelComponent} - * @param omitBase use <code>true</code> if no base class transition chain constants are needed - * - * @return the generated code - */ - def public genTransitionChainConstants(ExpandedModelComponent xpmc, boolean omitBase) { - var chains = if (omitBase) - xpmc.getOwnTransitionChains() else xpmc.transitionChains - var offset = if (omitBase) - xpmc.getTransitionChains().size-chains.size else 0 - - var list = new ArrayList<Pair<String, String>>() - for (chain : chains) { - offset = offset+1; - list.add(pair(chain.genChainId, offset.toString)) - } - - return langExt.genEnumeration("ChainIDs", list) - } - - /** - * generates entry and exit code for states - * - * @param xpmc the {@link ExpandedModelComponent} - * @param generateImplementation if <code>true</code> the implementation is generated, else the declaration - * - * @return the generated code - */ - def public String genEntryAndExitCodes(ExpandedModelComponent xpmc, boolean generateImplementation) { - xpmc.genEntryAndExitCodes(generateImplementation, langExt.usesInheritance) - } - - /** - * generates entry and exit code for states - * - * @param xpmc the {@link ExpandedModelComponent} - * @param generateImplementation if <code>true</code> the implementation is generated, else the declaration - * @param omitBase use <code>true</code> if no base class entry and exit codes are needed - * - * @return the generated code - */ - def public String genEntryAndExitCodes(ExpandedModelComponent xpmc, boolean generateImplementation, boolean omitBase) { - ''' - «FOR state : xpmc.stateMachine.getStateList()» - «IF !omitBase || xpmc.isOwnObject(state)» - «xpmc.genActionCodeMethods(state, generateImplementation)» - «ENDIF» - «ENDFOR» - ''' - } - - /** - * generates transition action codes - * - * @param xpmc the {@link ExpandedModelComponent} - * @param generateImplementation if <code>true</code> the implementation is generated, else the declaration - * - * @return the generated code - */ - def public String genActionCodes(ExpandedModelComponent xpmc, boolean generateImplementation) { - xpmc.genActionCodes(generateImplementation, langExt.usesInheritance) - } - - /** - * generates transition action codes - * - * @param xpmc the {@link ExpandedModelComponent} - * @param generateImplementation if <code>true</code> the implementation is generated, else the declaration - * @param omitBase use <code>true</code> if no base class action codes are needed - * - * @return the generated code - */ - def public String genActionCodes(ExpandedModelComponent xpmc, boolean generateImplementation, boolean omitBase) { - ''' - «FOR tr : xpmc.stateMachine.allTransitionsRecursive» - «IF (!omitBase || xpmc.isOwnObject(tr)) && tr.action.hasDetailCode» - «xpmc.genActionCodeMethod(tr, generateImplementation)» - «ENDIF» - «ENDFOR» - ''' - } - - def public String genStateSwitchMethods(ExpandedModelComponent xpmc, boolean generateImplementation) { - val mc = xpmc.modelComponent + @Inject public FSMNameProvider fsmNameProvider + + /** + * generates trigger IDs. + * Inheritance (if available) is used for base class IDs. + * + * @param gc the {@link GraphContainer} + * @return the generated code + */ + def public String genTriggerConstants(GraphContainer gc) { + gc.genTriggerConstants(langExt.usesInheritance) + } + + /** + * generates trigger IDs. + * Inheritance (if available) is used for base class IDs. + * + * @param gc the {@link GraphContainer} + * @param omitBase use <code>true</code> if no base class trigger constants are needed + * + * @return the generated code + */ + def public String genTriggerConstants(GraphContainer gc, boolean omitBase) { + val triggers = if (omitBase) + gc.component.ownMessagesFromInterfaces + else gc.component.allMessagesFromInterfaces + + val list = <Pair<String, String>>newArrayList + list.add(pair("POLLING", "0")); + for (mif : triggers) { + if (mif.from.eventDriven) { + list.add(pair(mif.triggerCodeName, itemIdGen.getIfItemId(mif.from)+" + EVT_SHIFT*"+msgIdGen.getMessageID(mif))) + } + } + + return langExt.genEnumeration("triggers", list) + } + + def String getTriggerCodeName(MessageFromIf mif) { + return "TRIG_"+mif.from.name+"__"+fsmNameProvider.getMessageName(mif.message); + } + + /** + * generates state ID constants. + * Inheritance (if available) is used for base class IDs. + * + * @param gc the {@link GraphContainer} + * @return the generated code + */ + def public genStateIdConstants(GraphContainer gc) { + gc.genStateIdConstants(langExt.usesInheritance) + } + + /** + * generates state ID constants. + * Inheritance (if available) is used for base class IDs. + * + * @param gc the {@link GraphContainer} + * @param omitBase use <code>true</code> if no base class state constants are needed + * + * @return the generated code + */ + def public genStateIdConstants(GraphContainer gc, boolean omitBase) { + // with inheritance we exclude inherited states + val allStateNodes = gc.graph.allStateNodes.toList // TODO: without toList this didn't work - why? + var offset = 2 + if (omitBase) + allStateNodes.filter[inherited].size else 0 + var baseStates = (if (omitBase) + allStateNodes.filter[!inherited] else allStateNodes).map[stateGraphNode].filter(typeof(State)).toList + + baseStates = baseStates.leafStatesLast.toList + + var list = <Pair<String, String>>newArrayList + if (!omitBase) { + list.add(pair("NO_STATE","0")) + list.add(pair("STATE_TOP","1")) + } + for (state : baseStates) { + list.add(pair(state.getGenStateId, offset.toString)) + offset = offset+1; + } + list.add(pair("STATE_MAX", offset.toString)) + + return langExt.genEnumeration("state_ids", list) + } + + /** + * generates transition chain ID constants. + * Inheritance can't be used used for base class IDs because of corner cases + * where base class and derived class chain IDs deviate (see bug 501354). + * + * @param gc the {@link GraphContainer} + * + * @return the generated code + */ + def public genTransitionChainConstants(GraphContainer gc) { + gc.genTransitionChainConstants(false/*langExt.usesInheritance*/) + } + + /** + * generates transition chain ID constants.<br/><br/> + * + * <b>Note:</b> Inheritance can't be used used for base class IDs because of corner cases + * where base class and derived class chain IDs deviate. + * + * @param gc the {@link GraphContainer} + * @param omitBase use <code>true</code> if no base class transition chain constants are needed + * + * @return the generated code + */ + def public genTransitionChainConstants(GraphContainer gc, boolean omitBase) { + val chains = (if (omitBase) + gc.graph.allLinks.filter[!inherited] else gc.graph.allLinks).map[transition].filter[isChainHead].filter(typeof(Transition)).toList + var offset = if (omitBase) + gc.graph.allLinks.filter[inherited].size else 0 + + var list = <Pair<String, String>>newArrayList + for (chain : chains) { + offset = offset+1; + list.add(pair(chain.genChainId, offset.toString)) + } + + return langExt.genEnumeration("ChainIDs", list) + } + + /** + * generates entry and exit code for states + * + * @param gc the {@link GraphContainer} + * @param generateImplementation if <code>true</code> the implementation is generated, else the declaration + * + * @return the generated code + */ + def public String genEntryAndExitCodes(GraphContainer gc, boolean generateImplementation) { + gc.genEntryAndExitCodes(generateImplementation, langExt.usesInheritance) + } + + /** + * generates entry and exit code for states + * + * @param gc the {@link GraphContainer} + * @param generateImplementation if <code>true</code> the implementation is generated, else the declaration + * @param omitBase use <code>true</code> if no base class entry and exit codes are needed + * + * @return the generated code + */ + def public String genEntryAndExitCodes(GraphContainer gc, boolean generateImplementation, boolean omitBase) { + val states = gc.graph.allStateNodes.filter[!omitBase || !inherited].toList + ''' + «FOR state : states» + «gc.genActionCodeMethods(state, generateImplementation)» + «ENDFOR» + ''' + } + + /** + * generates transition action codes + * + * @param gc the {@link GraphContainer} + * @param generateImplementation if <code>true</code> the implementation is generated, else the declaration + * + * @return the generated code + */ + def public String genActionCodes(GraphContainer gc, boolean generateImplementation) { + gc.genActionCodes(generateImplementation, langExt.usesInheritance) + } + + /** + * generates transition action codes + * + * @param gc the {@link GraphContainer} + * @param generateImplementation if <code>true</code> the implementation is generated, else the declaration + * @param omitBase use <code>true</code> if no base class action codes are needed + * + * @return the generated code + */ + def public String genActionCodes(GraphContainer gc, boolean generateImplementation, boolean omitBase) { + val transitions = gc.graph.allLinks.filter[!omitBase || !inherited].filter[transition.action.hasDetailCode].toList + ''' + «FOR tr : transitions» + «gc.genActionCodeMethod(tr, generateImplementation)» + «ENDFOR» + ''' + } + + def public String genStateSwitchMethods(GraphContainer gc, boolean generateImplementation) { + val mc = gc.component val async = mc.commType==ComponentCommunicationType::ASYNCHRONOUS val eventDriven = mc.commType==ComponentCommunicationType::EVENT_DRIVEN - val ifItemPtr = interfaceItemType()+langExt.pointerLiteral() + val ifItemPtr = interfaceItemType + langExt.pointerLiteral val handleEvents = async || eventDriven val chainIDScope = if (langExt.usesInheritance) mc.className+langExt.scopeSeparator else "" val opScope = langExt.operationScope(mc.className, !generateImplementation) @@ -258,8 +265,13 @@ abstract class AbstractStateMachineGenerator { "const "+ifItemPtr else ifItemPtr - val usesHdlr = usesHandlerTrPoints(xpmc) - ''' + val usesHdlr = usesHandlerTrPoints(gc) + val nodes = gc.graph.allStateNodes.toList.sortBy[(stateGraphNode as State).genStateId] + val state2node = newHashMap + nodes.forEach[state2node.put(stateGraphNode as State, it)] + val states = nodes.map[stateGraphNode].filter(typeof(State)).toList + val transitionChains = gc.graph.allLinks.filter[isChainHead].toList.sortBy[transition.genChainId] + ''' /** * calls exit codes while exiting from the current state to one of its * parent states while remembering the history @@ -273,11 +285,11 @@ abstract class AbstractStateMachineGenerator { «privAccess»void «opScopePriv»exitTo(«selfPtr»«stateType» current__et, «stateType» to«IF usesHdlr», «boolType» handler__et«ENDIF») { while (current__et!=to) { switch (current__et) { - «FOR state : xpmc.stateMachine.getBaseStateList()» - case «state.getGenStateId()»: - «IF state.hasExitCode(true)»«IF usesHdlr»if (!handler__et) «ENDIF»«state.getExitCodeOperationName()»(«langExt.selfPointer(false)»);«ENDIF» - «setHistory(state.getParentStateId(), state.getGenStateId())»; - current__et = «state.getParentStateId()»; + «FOR state : states» + case «state.getGenStateId»: + «IF state.hasExitCode(true)»«IF usesHdlr»if (!handler__et) «ENDIF»«state.exitCodeOperationName»(«langExt.selfPointer(false)»);«ENDIF» + «setHistory(state.parentStateId, state.genStateId)»; + current__et = «state.parentStateId»; break; «ENDFOR» default: @@ -300,11 +312,10 @@ abstract class AbstractStateMachineGenerator { «IF generateImplementation» «privAccess»«stateType» «opScopePriv»executeTransitionChain(«selfPtr»int chain__et«IF handleEvents», «constIfItemPtr» ifitem, «langExt.voidPointer» generic_data__et«ENDIF») { switch (chain__et) { - «var allchains = xpmc.getTransitionChains()» - «FOR tc : allchains» - case «chainIDScope»«tc.genChainId»: + «FOR tc : transitionChains» + case «chainIDScope»«tc.transition.genChainId»: { - «transitionChainGenerator.generateExecuteChain(xpmc, tc)» + «transitionChainGenerator.generateExecuteChain(gc, tc)» } «ENDFOR» default: @@ -327,8 +338,7 @@ abstract class AbstractStateMachineGenerator { */ «IF generateImplementation» «privAccess»«stateType» «opScopePriv»enterHistory(«selfPtr»«stateType» state__et«IF usesHdlr», «boolType» handler__et«ENDIF») { - «val baseStateList = xpmc.stateMachine.baseStateList» - «val needsSkipVar = !baseStateList.filter(s|s.hasEntryCode(true)).empty» + «val needsSkipVar = !states.filter(s|s.hasEntryCode(true)).empty» «IF needsSkipVar» «boolType» skip_entry__et = «langExt.booleanConstant(false)»; «ENDIF» @@ -340,26 +350,26 @@ abstract class AbstractStateMachineGenerator { } while («langExt.booleanConstant(true)») { switch (state__et) { - «FOR state : baseStateList» - case «state.getGenStateId()»: - «IF state.hasEntryCode(true)»if (!(skip_entry__et«IF usesHdlr» || handler__et«ENDIF»)) «state.getEntryCodeOperationName()»(«langExt.selfPointer(false)»);«ENDIF» - «IF state.isLeaf()» + «FOR state : states» + case «state.genStateId»: + «IF state.hasEntryCode(true)»if (!(skip_entry__et«IF usesHdlr» || handler__et«ENDIF»)) «state.entryCodeOperationName»(«langExt.selfPointer(false)»);«ENDIF» + «IF state.isLeaf» /* in leaf state: return state id */ - return «state.getGenStateId()»; + return «state.getGenStateId»; «ELSE» /* state has a sub graph */ - «IF state.subgraph.hasInitTransition()» + «var sub_initt = state2node.get(state).subgraph.initialTransition» + «IF sub_initt!==null» /* with init transition */ - if («getHistory(state.getGenStateId())»==NO_STATE) { - «var sub_initt = state.subgraph.getInitTransition()» - state__et = executeTransitionChain(«langExt.selfPointer(true)»«chainIDScope»«xpmc.getChain(sub_initt).genChainId»«IF handleEvents», «langExt.nullPointer», «langExt.nullPointer»«ENDIF»); + if («getHistory(state.genStateId)»==NO_STATE) { + state__et = executeTransitionChain(«langExt.selfPointer(true)»«chainIDScope»«sub_initt.genChainId»«IF handleEvents», «langExt.nullPointer», «langExt.nullPointer»«ENDIF»); } else { - state__et = «getHistory(state.getGenStateId())»; + state__et = «state.genStateId.history»; } «ELSE» /* without init transition */ - state__et = «getHistory(state.getGenStateId())»; + state__et = «state.genStateId.history»; «ENDIF» break; «ENDIF» @@ -383,8 +393,8 @@ abstract class AbstractStateMachineGenerator { «IF generateImplementation» «publicIf»void «opScope»executeInitTransition(«selfOnly») { - «var initt = xpmc.stateMachine.getInitTransition()» - int chain__et = «chainIDScope»«xpmc.getChain(initt).genChainId»; + «var initt = gc.graph.initialTransition» + int chain__et = «chainIDScope»«initt.genChainId»; «stateType» next__et = «opScopePriv»executeTransitionChain(«langExt.selfPointer(true)»chain__et«IF handleEvents», «langExt.nullPointer», «langExt.nullPointer»«ENDIF»); next__et = «opScopePriv»enterHistory(«langExt.selfPointer(true)»next__et«IF usesHdlr», «langExt.booleanConstant(false)»«ENDIF»); setState(«langExt.selfPointer(true)»next__et); @@ -412,10 +422,10 @@ abstract class AbstractStateMachineGenerator { «IF handleEvents» if (!handleSystemEvent(ifitem, evt, generic_data__et)) { - «genStateSwitch(xpmc, usesHdlr)» + «genStateSwitch(gc, usesHdlr)» } «ELSE» - «genStateSwitch(xpmc, usesHdlr)» + «genStateSwitch(gc, usesHdlr)» «ENDIF» if (chain__et != NOT_CAUGHT) { «opScopePriv»exitTo(«langExt.selfPointer(true)»getState(«langExt.selfPointer(false)»), catching_state__et«IF usesHdlr», is_handler__et«ENDIF»); @@ -423,7 +433,7 @@ abstract class AbstractStateMachineGenerator { «stateType» next__et = «opScopePriv»executeTransitionChain(«langExt.selfPointer(true)»chain__et«IF handleEvents», ifitem, generic_data__et«ENDIF»); next__et = «opScopePriv»enterHistory(«langExt.selfPointer(true)»next__et«IF usesHdlr», is_handler__et«ENDIF»); setState(«langExt.selfPointer(true)»next__et); - «finalAction()» + «finalAction» } } } @@ -440,230 +450,233 @@ abstract class AbstractStateMachineGenerator { void «opScope»receiveEvent(«langExt.selfPointer(true)»«ifItemPtr» ifitem, int evt, «langExt.voidPointer» generic_data__et); «ENDIF» «ENDIF» - ''' - } - - /** - * generate the do code calls for a given state - * - * @param state the {@link State} - * @return the generated code - */ - def public String genDoCodes(State state) {''' - «IF state.hasDoCode(true)» - «state.getDoCodeOperationName()»(«langExt.selfPointer(false)»); - «ENDIF» - «IF state.eContainer.eContainer instanceof State» - «genDoCodes(state.eContainer.eContainer as State)» - «ENDIF» - '''} - - /** - * helper method which generates the state switch. - * Asynchronous, data driven and event driven state machines are distinguished - * - * @param xpmc the {@link ExpandedModelComponent} - * @param usesHdlr if the state machine uses no handler {@link TransitionPoint}s - * at all then unused variables can be avoided by passing <code>true</code> - * @return the generated code - */ - def public genStateSwitch(ExpandedModelComponent xpmc, boolean usesHdlr) { - var async = xpmc.modelComponent.commType==ComponentCommunicationType::ASYNCHRONOUS - var eventDriven = xpmc.modelComponent.commType==ComponentCommunicationType::EVENT_DRIVEN - var dataDriven = xpmc.modelComponent.commType==ComponentCommunicationType::DATA_DRIVEN - ''' - switch (getState(«langExt.selfPointer(false)»)) { - «FOR state : xpmc.stateMachine.getLeafStateList()» - case «state.getGenStateId()»: - «IF async» - «var atlist = xpmc.getActiveTriggers(state)» - «IF !atlist.isEmpty» - switch(trigger__et) { - case POLLING: - «genDataDrivenTriggers(xpmc, state, usesHdlr)» - break; - «genEventDrivenTriggers(xpmc, state, atlist, usesHdlr)» - } - «ELSE» - «genDataDrivenTriggers(xpmc, state, usesHdlr)» - «ENDIF» - «ELSEIF dataDriven» - «genDataDrivenTriggers(xpmc, state, usesHdlr)» - «ELSEIF eventDriven» - «var atlist = xpmc.getActiveTriggers(state)» - «IF !atlist.isEmpty» - switch(trigger__et) { - «genEventDrivenTriggers(xpmc, state, atlist, usesHdlr)» - } - «ENDIF» - «ENDIF» - break; - «ENDFOR» - default: - /* should not occur */ - break; - } - ''' - } - - /** - * helper method which generates the data driven triggers - * - * @param xpmc the {@link ExpandedModelComponent} - * @param state the {@link State} for which the trigger if-else switch should be generated - * @param usesHdlr if the state machine uses no handler {@link TransitionPoints} - * at all then unused variables can be avoided by passing <code>true</code> - * @return the generated code - */ - def public genDataDrivenTriggers(ExpandedModelComponent xpmc, State state, boolean usesHdlr) { - val chainIDScope = if (langExt.usesInheritance) xpmc.className+langExt.scopeSeparator else "" - ''' - «genDoCodes(state)» - «var transitions = xpmc.getOutgoingTransitionsHierarchical(state).filter(t|t instanceof GuardedTransition)» - «FOR tr : transitions» - if («guard((tr as GuardedTransition), "", xpmc)») - { - «var chain = xpmc.getChain(tr)» - chain__et = «chainIDScope»«chain.genChainId»; - catching_state__et = «chain.stateContext.genStateId»; - «IF chain.isHandler() && usesHdlr» - is_handler__et = TRUE; - «ENDIF» - } - «IF tr!=transitions.last» - else - «ENDIF» - «ENDFOR» - ''' - } - - /** - * helper method which generates the event driven triggers - * - * @param xpmc the {@link ExpandedModelComponent} - * @param state the {@link State} for which the trigger switch should be generated - * @param atlist the list of {@link ActiveTrigger}s of this state - * @param usesHdlr if the state machine uses no handler {@link TransitionPoints} - * at all then unused variables can be avoided by passing <code>true</code> - * @return the generated code - */ - def public genEventDrivenTriggers(ExpandedModelComponent xpmc, State state, List<ActiveTrigger> atlist, boolean usesHdlr) { - val chainIDScope = if (langExt.usesInheritance) xpmc.className+langExt.scopeSeparator else "" - ''' - «FOR at : atlist» - case «xpmc.getTriggerCodeName(at)»: - «var needData = at.hasGuard» - «IF needData»{ «langExt.getTypedDataDefinition(at.msg)»«ENDIF» - «FOR tt : at.transitions SEPARATOR " else "» - «var chain = xpmc.getChain(tt)» - «guard(chain.getTransition as TriggeredTransition, at.trigger, xpmc)» - { - chain__et = «chainIDScope»«chain.genChainId»; - catching_state__et = «chain.stateContext.genStateId»; - «IF chain.isHandler() && usesHdlr» - is_handler__et = «langExt.booleanConstant(true)»; - «ENDIF» - } - «ENDFOR» - «IF needData»}«ENDIF» - break; - «ENDFOR» - default: - /* should not occur */ - break; - ''' - } - - def public getClassName(ExpandedModelComponent xpmc) { - xpmc.modelComponent.className - } - - def public getClassName(ModelComponent mc) { - mc.componentName - } - - /** - * getter for history array - * - * @param state the ID of the history state - * @return the generated code - */ - def public getHistory(String state) { - langExt.memberAccess+"history["+state+"]" - } - - /** - * setter for history array - * - * @param state the ID of the state whose history should be set - * @param historyState the ID of the state that should be assigned - * @return the generated code - */ - def public setHistory(String state, String historyState) { - langExt.memberAccess+"history["+state+"] = "+historyState - } - - /** - * @return the type of (temporary) state variables (defaults to "int") - * and has to be signed - */ - def public stateType() { - "int" - } - - /** - * allow target language dependent generation of unreachable return in generated enterHistory method. - * The default is just a comment. - * @return the generated code - */ - def public unreachableReturn() { - "/* return NO_STATE; // required by CDT but detected as unreachable by JDT because of while (true) */" - } - - /** - * type of (temporary) boolean variables (defaults to "boolean") - * @return the generated code - */ - def public boolType() { - return "boolean" - } - - /** - * empty, but may be overridden - */ - def public finalAction() { - '''''' - } - - /** - * the type of the interface item passed into the receiveEvent() method - */ - def public interfaceItemType() { - "InterfaceItemBase" - } - - /** - * empty, but may be overridden - */ - def markVariableUsed(String varname) { - '''''' - } - - /** - * helper method to determine whether this state machine uses handler transitions - * points at all - * - * @param xpax the {@link ExpandedModelComponent} - * @return <code>true</code> if the state machine uses handler transition points - */ - def public usesHandlerTrPoints(ExpandedModelComponent xpmc) { - if (xpmc.stateMachine.empty) - return false - !xpmc.stateMachine.allTrPointsRecursive.filter(t|t instanceof TransitionPoint && ((t as TransitionPoint).handler)).empty - } - - def public String guard(TriggeredTransition tt, String trigger, ExpandedModelComponent mc) - def public String guard(GuardedTransition tt, String trigger, ExpandedModelComponent mc) - def public String genActionCodeMethod(ExpandedModelComponent xpmc, Transition tr, boolean generateImplementation) - def public String genActionCodeMethods(ExpandedModelComponent xpmc, State state, boolean generateImplementation) + ''' + } + + /** + * generate the do code calls for a given state + * + * @param state the {@link State} + * @return the generated code + */ + def public String genDoCodes(State state) {''' + «IF state.hasDoCode(true)» + «state.doCodeOperationName»(«langExt.selfPointer(false)»); + «ENDIF» + «IF state.eContainer.eContainer instanceof State» + «genDoCodes(state.eContainer.eContainer as State)» + «ENDIF» + '''} + + /** + * helper method which generates the state switch. + * Asynchronous, data driven and event driven state machines are distinguished + * + * @param gc the {@link GraphContainer} + * @param usesHdlr if the state machine uses no handler {@link TransitionPoint}s + * at all then unused variables can be avoided by passing <code>true</code> + * @return the generated code + */ + def public genStateSwitch(GraphContainer gc, boolean usesHdlr) { + var async = gc.component.commType==ComponentCommunicationType::ASYNCHRONOUS + var eventDriven = gc.component.commType==ComponentCommunicationType::EVENT_DRIVEN + var dataDriven = gc.component.commType==ComponentCommunicationType::DATA_DRIVEN + val allLeafStateNodes = gc.graph.allStateNodes.filter[isLeaf].toList.sortBy[(stateGraphNode as State).genStateId] + ''' + switch (getState(«langExt.selfPointer(false)»)) { + «FOR stateNode : allLeafStateNodes» + «val state = stateNode.stateGraphNode as State» + case «state.genStateId»: + «val caughtTriggers = stateNode.caughtTriggers» + «IF async» + «IF !caughtTriggers.isEmpty» + switch(trigger__et) { + case POLLING: + «genDataDrivenTriggers(gc, stateNode, usesHdlr)» + break; + «genEventDrivenTriggers(gc, stateNode, usesHdlr)» + } + «ELSE» + «genDataDrivenTriggers(gc, stateNode, usesHdlr)» + «ENDIF» + «ELSEIF dataDriven» + «genDataDrivenTriggers(gc, stateNode, usesHdlr)» + «ELSEIF eventDriven» + «IF !caughtTriggers.isEmpty» + switch(trigger__et) { + «genEventDrivenTriggers(gc, stateNode, usesHdlr)» + } + «ENDIF» + «ENDIF» + break; + «ENDFOR» + default: + /* should not occur */ + break; + } + ''' + } + + /** + * helper method which generates the data driven triggers + * + * @param gc the {@link GraphContainer} + * @param state the {@link State} for which the trigger if-else switch should be generated + * @param usesHdlr if the state machine uses no handler {@link TransitionPoints} + * at all then unused variables can be avoided by passing <code>true</code> + * @return the generated code + */ + def public genDataDrivenTriggers(GraphContainer gc, Node stateNode, boolean usesHdlr) { + val chainIDScope = if (langExt.usesInheritance) gc.className+langExt.scopeSeparator else "" + val state = stateNode.stateGraphNode as State + ''' + «genDoCodes(state)» + «var links = stateNode.getOutgoingLinksHierarchically.filter[transition instanceof GuardedTransition]» + «FOR l : links» + if («genGuardedTransitionGuard(l, "", gc)») + { + chain__et = «chainIDScope»«l.transition.genChainId»; + catching_state__et = «l.transition.superState.genStateId»; + «IF l.isHandler && usesHdlr» + is_handler__et = TRUE; + «ENDIF» + } + «IF l!=links.last» + else + «ENDIF» + «ENDFOR» + ''' + } + + /** + * helper method which generates the event driven triggers + * + * @param gc the {@link GraphContainer} + * @param state the {@link State} for which the trigger switch should be generated + * @param usesHdlr if the state machine uses no handler {@link TransitionPoints} + * at all then unused variables can be avoided by passing <code>true</code> + * @return the generated code + */ + def public genEventDrivenTriggers(GraphContainer gc, Node stateNode, boolean usesHdlr) { + val caughtTriggers = new ArrayList(stateNode.caughtTriggers).sortBy[triggerCodeName] + val chainIDScope = if (langExt.usesInheritance) gc.className+langExt.scopeSeparator else "" + ''' + «FOR ct : caughtTriggers» + case «ct.triggerCodeName»: + «var needData = ct.hasGuard» + «IF needData»{ «langExt.getTypedDataDefinition(ct.msg)»«ENDIF» + «FOR link : ct.links SEPARATOR " else "» + «genTriggeredTransitionGuard(link, ct.trigger, gc)» + { + chain__et = «chainIDScope»«link.transition.genChainId»; + catching_state__et = «link.transition.superState.genStateId»; + «IF link.isHandler && usesHdlr» + is_handler__et = «langExt.booleanConstant(true)»; + «ENDIF» + } + «ENDFOR» + «IF needData»}«ENDIF» + break; + «ENDFOR» + default: + /* should not occur */ + break; + ''' + } + + def public getClassName(GraphContainer gc) { + gc.component.className + } + + def public getClassName(ModelComponent mc) { + mc.componentName + } + + def getTriggerCodeName(CommonTrigger tr) { + val parts = tr.trigger.split(TriggerExtensions.TRIGGER_SEP) + return "TRIG_"+parts.get(0)+"__"+parts.get(1) + } + + /** + * getter for history array + * + * @param state the ID of the history state + * @return the generated code + */ + def public getHistory(String state) { + langExt.memberAccess+"history["+state+"]" + } + + /** + * setter for history array + * + * @param state the ID of the state whose history should be set + * @param historyState the ID of the state that should be assigned + * @return the generated code + */ + def public setHistory(String state, String historyState) { + langExt.memberAccess+"history["+state+"] = "+historyState + } + + /** + * @return the type of (temporary) state variables (defaults to "int") + * and has to be signed + */ + def public stateType() { + "int" + } + + /** + * allow target language dependent generation of unreachable return in generated enterHistory method. + * The default is just a comment. + * @return the generated code + */ + def public unreachableReturn() { + "/* return NO_STATE; // required by CDT but detected as unreachable by JDT because of while (true) */" + } + + /** + * type of (temporary) boolean variables (defaults to "boolean") + * @return the generated code + */ + def public boolType() { + return "boolean" + } + + /** + * empty, but may be overridden + */ + def public finalAction() { + '''''' + } + + /** + * the type of the interface item passed into the receiveEvent() method + */ + def public interfaceItemType() { + "InterfaceItemBase" + } + + /** + * empty, but may be overridden + */ + def markVariableUsed(String varname) { + '''''' + } + + /** + * helper method to determine whether this state machine uses handler transitions + * points at all + * + * @param xpax the {@link GraphContainer} + * @return <code>true</code> if the state machine uses handler transition points + */ + def public usesHandlerTrPoints(GraphContainer gc) { + !gc.graph.allTransitionPointNodes.filter(t|((t.stateGraphNode as TransitionPoint).handler)).empty + } + + def public String genTriggeredTransitionGuard(Link link, String trigger, GraphContainer mc) + def public String genGuardedTransitionGuard(Link link, String trigger, GraphContainer mc) + def public String genActionCodeMethod(GraphContainer gc, Link link, boolean generateImplementation) + def public String genActionCodeMethods(GraphContainer gc, Node node, boolean generateImplementation) }
\ No newline at end of file diff --git a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/FSMExtensions.xtend b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/FSMExtensions.xtend index 0a88f76c5..94f1a313c 100644 --- a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/FSMExtensions.xtend +++ b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/FSMExtensions.xtend @@ -12,19 +12,15 @@ package org.eclipse.etrice.generator.fsm.generic -import org.eclipse.etrice.core.fsm.fSM.Transition -import org.eclipse.etrice.core.genmodel.fsm.fsmgen.ExpandedModelComponent -import java.util.List -import org.eclipse.etrice.core.fsm.fSM.State -import java.util.ArrayList -import org.eclipse.etrice.core.fsm.fSM.StateGraph -import org.eclipse.etrice.core.fsm.fSM.TransitionPoint -import org.eclipse.etrice.core.fsm.fSM.ModelComponent -import org.eclipse.etrice.core.fsm.util.FSMHelpers import com.google.inject.Inject +import java.util.ArrayList +import java.util.List +import org.eclipse.etrice.core.fsm.fSM.CPBranchTransition import org.eclipse.etrice.core.fsm.fSM.DetailCode import org.eclipse.etrice.core.fsm.fSM.Guard -import org.eclipse.etrice.core.fsm.fSM.CPBranchTransition +import org.eclipse.etrice.core.fsm.fSM.ModelComponent +import org.eclipse.etrice.core.fsm.fSM.State +import org.eclipse.etrice.core.fsm.util.FSMHelpers /** * @author Henrik Rentz-Reichert @@ -75,36 +71,6 @@ class FSMExtensions { return ret; } - //------------------------------------------------------- - // state graph related methods - - /** - * @param ac an {@link ExpandedActorClass} - * @param s a {@link State} - * @return a list of {@link Transition}s starting at the state and going up in the hierarchy - * following the logic of evaluation of firing conditions - */ - def List<Transition> getOutgoingTransitionsHierarchical(ExpandedModelComponent ac, State s) { - var result = new ArrayList<Transition>() - - // own transitions - result.addAll(ac.getOutgoingTransitions(s)) - - // transition points on same level - var sg = s.eContainer() as StateGraph - for (tp : sg.getTrPoints()) { - if (tp instanceof TransitionPoint) - result.addAll(ac.getOutgoingTransitions(tp)) - } - - // recurse to super states - if (sg.eContainer() instanceof State) { - result.addAll(getOutgoingTransitionsHierarchical(ac, sg.eContainer() as State)) - } - - return result; - } - /** * @param states a list of {@link State}s * @return a list ordered such that leaf states are last @@ -113,7 +79,7 @@ class FSMExtensions { val leaf = states.filter(s|s.leaf) val nonLeaf = states.filter(s|!s.leaf) - nonLeaf.union(leaf) + nonLeaf + leaf } /** @@ -129,7 +95,7 @@ class FSMExtensions { * @return a list of simple states with leaf states last */ def List<State> getAllBaseStatesLeavesLast(ModelComponent mc) { - mc.allBaseStates.getLeafStatesLast + mc.allBaseStates.getLeafStatesLast.toList } /** diff --git a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/ILanguageExtensionBase.java b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/ILanguageExtensionBase.java index 2c2c3be69..bc46b0d50 100644 --- a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/ILanguageExtensionBase.java +++ b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/ILanguageExtensionBase.java @@ -194,10 +194,19 @@ public interface ILanguageExtensionBase { String superCall(String baseClassName, String method, String arguments); /** + * the three results returned by {@link #org.eclipse.etrice.generator.fsm.generic.ILanguageExtensionBase.generateArglistAndTypedData(EObject)} + */ + enum TypedDataKind { + COMMA_SEPARATED_PARAM_IN_CALL, + DECLARATION_AND_INITIALIZATION, + COMMA_SEPARATED_PARAM_IN_DECLARATION + } + + /** * return three strings used by the generator * * @param data the variable declaration - * @return an array of three strings + * @return an array of three strings (see {@link #TypedDataKind}) * <ol> * <li>the string that performs the cast from generic_data to the correct type and assigns it to a new variable</li> * <li>the data as it appears in a method call</li> diff --git a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/ITransitionChainVisitor.java b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/ITransitionChainVisitor.java new file mode 100644 index 000000000..2633814c8 --- /dev/null +++ b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/ITransitionChainVisitor.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2010 protos software gmbh (http://www.protos.de). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * CONTRIBUTORS: + * Thomas Schuetz and Henrik Rentz-Reichert (initial contribution) + * + *******************************************************************************/ + +package org.eclipse.etrice.generator.fsm.generic; + +import org.eclipse.etrice.core.fsm.fSM.CPBranchTransition; +import org.eclipse.etrice.core.fsm.fSM.ContinuationTransition; +import org.eclipse.etrice.core.fsm.fSM.State; +import org.eclipse.etrice.core.fsm.fSM.TransitionBase; + +/** + * This interface describes the transition chain visitor. + * The visitor is used by the code generator to generate the code associated with a transition chain. + * It is passed in a call to + * {@link org.eclipse.etrice.core.genmodel.etricegen.TransitionChain#genExecuteChain(ITransitionChainVisitor) + * TransitionChain.genExecuteChain(ITransitionChainVisitor)}. + * + * <p> + * The visitor has to be implemented by the concrete target language generator. + * </p> + * + * @author Henrik Rentz-Reichert + * + */ +public interface ITransitionChainVisitor { + + /** + * @param tc the transition chain + * @return a typed declaration of the data associated with this message + */ + String genTypedData(TransitionBase tc); + + /** + * @param tr a transition + * @return a call of the action operation (as generated by the generator) + */ + String genActionOperationCall(TransitionBase tr); + + /** + * @param state a state + * @return a call of the entry operation (as generated by the generator) + */ + String genEntryOperationCall(State state); + + /** + * @param state a state + * @return a call of the exit operation (as generated by the generator) + */ + String genExitOperationCall(State state); + + /** + * @param tr a choice point branch transition (not the default branch) + * @param isFirst <code>true</code> if this is the first of a series of if statements + * @return code for the [else] if statement with condition (guard) and block opening + */ + String genElseIfBranch(CPBranchTransition tr, boolean isFirst); + + /** + * @param tr the choice point default branch transition + * @return code for the final else with block opening + */ + String genElseBranch(ContinuationTransition tr); + + /** + * @return the final closing of the block + */ + String genEndIf(); + + /** + * @param state a state + * @param executeEntryCode <code>true</code> if entry code of state should be executed + * @return a return statement with the ID of the state + */ + String genReturnState(State state, boolean executeEntryCode); + +} diff --git a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/TransitionChainGenerator.java b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/TransitionChainGenerator.java index 5d124b208..8ccc57d76 100644 --- a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/TransitionChainGenerator.java +++ b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/TransitionChainGenerator.java @@ -12,13 +12,30 @@ package org.eclipse.etrice.generator.fsm.generic; -import org.eclipse.emf.ecore.EObject; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.etrice.core.fsm.fSM.CPBranchTransition; +import org.eclipse.etrice.core.fsm.fSM.ChoicePoint; +import org.eclipse.etrice.core.fsm.fSM.ContinuationTransition; +import org.eclipse.etrice.core.fsm.fSM.EntryPoint; +import org.eclipse.etrice.core.fsm.fSM.ExitPoint; import org.eclipse.etrice.core.fsm.fSM.InitialTransition; -import org.eclipse.etrice.core.fsm.fSM.Transition; -import org.eclipse.etrice.core.fsm.fSM.TriggeredTransition; -import org.eclipse.etrice.core.genmodel.fsm.fsmgen.ExpandedModelComponent; -import org.eclipse.etrice.core.genmodel.fsm.fsmgen.TransitionChain; +import org.eclipse.etrice.core.fsm.fSM.State; +import org.eclipse.etrice.core.fsm.fSM.StateGraphNode; +import org.eclipse.etrice.core.fsm.fSM.TrPoint; +import org.eclipse.etrice.core.fsm.fSM.TransitionPoint; +import org.eclipse.etrice.core.fsm.naming.FSMNameProvider; +import org.eclipse.etrice.core.fsm.util.FSMHelpers; +import org.eclipse.etrice.core.genmodel.fsm.FsmGenExtensions; +import org.eclipse.etrice.core.genmodel.fsm.fsmgen.GraphContainer; +import org.eclipse.etrice.core.genmodel.fsm.fsmgen.Link; +import org.eclipse.etrice.core.genmodel.fsm.fsmgen.Node; import org.eclipse.etrice.generator.fsm.base.CodegenHelpers; +import org.eclipse.etrice.generator.fsm.generic.ILanguageExtensionBase.TypedDataKind; import com.google.inject.Inject; @@ -32,27 +49,125 @@ public class TransitionChainGenerator { @Inject private ILanguageExtensionBase languageExt; @Inject private CodegenHelpers codegenHelpers; @Inject private IDetailCodeTranslator translator; + @Inject private FSMNameProvider fsmNameProvider; + @Inject private FSMHelpers fsmHelpers; - public String generateExecuteChain(ExpandedModelComponent xpmc, TransitionChain tc) { - TransitionChainVisitor tcv = new TransitionChainVisitor(xpmc, languageExt, codegenHelpers, translator); - tcv.init(tc); + public String generateExecuteChain(GraphContainer gc, Link l) { + TransitionChainVisitor tcv = new TransitionChainVisitor(gc, languageExt, codegenHelpers, translator); + tcv.init(l.getTransition()); - return tc.genExecuteChain(tcv); + return genExecuteChain(l, tcv); } - public String generateArgumentList(ExpandedModelComponent xpmc, Transition t) { - if (t instanceof InitialTransition) + public String generateArgumentList(GraphContainer gc, Link l) { + if (l.getTransition() instanceof InitialTransition) // actually is InitialTransition return ""; - TransitionChain chain = xpmc.getChain(t); - if (!(chain.getTransition() instanceof TriggeredTransition)) + if (!l.isIfitemTriggered()) return ""; - return generateTypedArgumentList(xpmc.getData(t)); + return languageExt.generateArglistAndTypedData(l.getCommonData())[TypedDataKind.COMMA_SEPARATED_PARAM_IN_DECLARATION.ordinal()]; + } + + public String genExecuteChain(Link l, ITransitionChainVisitor tcv) { + StringBuilder result = new StringBuilder(); + + /* TODO: the next generated code declares a correctly typed variable for the generic data. + * It is hard to determine whether it is actually needed though. + * It is needed in non-initial transitions with action code that are not data driven. + * It might be needed in condition expressions. But this code would have to be parsed + * with uncertain result (because of e.g. comments). + */ + result.append(tcv.genTypedData(l.getTransition())); + + genChainCode(l, tcv, result); + + return result.toString(); } + + private void genChainCode(Link l, ITransitionChainVisitor tcv, StringBuilder result) { + + result.append(tcv.genActionOperationCall(l.getTransition())); + + Node target = l.getTarget(); + StateGraphNode stateGraphNode = target.getStateGraphNode(); + List<Link> outgoing = new ArrayList<Link>(target.getOutgoing()); + + // TODO: remove sorting again + final CodegenHelpers cgh = new CodegenHelpers(); + Collections.sort(outgoing, new Comparator<Link>() { + + @Override + public int compare(Link o1, Link o2) { + String id1 = cgh.getGenChainId(o1.getTransition()); + String id2 = cgh.getGenChainId(o2.getTransition()); + return id1.compareTo(id2); + } + + }); + + if (stateGraphNode instanceof ChoicePoint) { + Link dflt = FsmGenExtensions.getChoicepointDefaultBranch(target); + assert(dflt!=null): "ChoicePoint "+fsmNameProvider.getFullPath(stateGraphNode)+" has no default branch!"; + + // first generate all choicepoint branches as if/else + boolean isFirst = true; + for (Link cond : outgoing) { + if (cond==dflt) + continue; + + assert(cond.getTransition() instanceof CPBranchTransition): "The non default ChoicePoint branch " + +fsmNameProvider.getFullPath(cond.getTransition())+" must be of type CPBranchTransition!"; + + result.append(tcv.genElseIfBranch((CPBranchTransition) cond.getTransition(), isFirst)); + isFirst = false; - public String generateTypedArgumentList(EObject data) { - return languageExt.generateArglistAndTypedData(data)[2]; + genChainCode(cond, tcv, result); + } + + // then generate the default branch + result.append(tcv.genElseBranch((ContinuationTransition) dflt.getTransition())); + + genChainCode(dflt, tcv, result); + + result.append(tcv.genEndIf()); + } + else { + if (stateGraphNode instanceof TrPoint) { + if (stateGraphNode instanceof TransitionPoint) { + // TransitionPoint is final destination of the chain + result.append(tcv.genReturnState(fsmHelpers.getParentState(stateGraphNode), false)); + return; + } + else { + assert(outgoing.size()<=1): "TrPoint "+fsmNameProvider.getFullPath(stateGraphNode) + +" is expected to have at most one outgoing transition!"; + if (outgoing.size()==1) { + State state = fsmHelpers.getParentState(stateGraphNode); + if (stateGraphNode instanceof EntryPoint) { + if (state!=null && !target.isInherited() && fsmHelpers.hasEntryCode(state, true)) + result.append(tcv.genEntryOperationCall(state)); + } + else if (stateGraphNode instanceof ExitPoint) { + if (state!=null && !target.isInherited() && fsmHelpers.hasExitCode(state, true)) + result.append(tcv.genExitOperationCall(state)); + } + else { + assert(false): "unexpected sub type"; + } + } + } + if (! outgoing.isEmpty()) { + genChainCode(outgoing.get(0), tcv, result); + } + } + else { + // the following assertion should always hold true + assert(stateGraphNode instanceof State): "A transition target can be a ChoicePoint, a TrPoint or a State!"; + + result.append(tcv.genReturnState((State) stateGraphNode, true)); + } + } } } diff --git a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/TransitionChainVisitor.java b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/TransitionChainVisitor.java index 1354d593d..2e4a17115 100644 --- a/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/TransitionChainVisitor.java +++ b/plugins/org.eclipse.etrice.generator.fsm/src/org/eclipse/etrice/generator/fsm/generic/TransitionChainVisitor.java @@ -16,13 +16,15 @@ import org.eclipse.etrice.core.fsm.fSM.CPBranchTransition; import org.eclipse.etrice.core.fsm.fSM.ContinuationTransition; import org.eclipse.etrice.core.fsm.fSM.GuardedTransition; import org.eclipse.etrice.core.fsm.fSM.InitialTransition; +import org.eclipse.etrice.core.fsm.fSM.RefinedTransition; import org.eclipse.etrice.core.fsm.fSM.State; -import org.eclipse.etrice.core.fsm.fSM.Transition; +import org.eclipse.etrice.core.fsm.fSM.TransitionBase; import org.eclipse.etrice.core.fsm.util.FSMHelpers; -import org.eclipse.etrice.core.genmodel.fsm.fsmgen.ExpandedModelComponent; -import org.eclipse.etrice.core.genmodel.fsm.fsmgen.ITransitionChainVisitor; -import org.eclipse.etrice.core.genmodel.fsm.fsmgen.TransitionChain; +import org.eclipse.etrice.core.genmodel.fsm.FsmGenExtensions; +import org.eclipse.etrice.core.genmodel.fsm.fsmgen.GraphContainer; +import org.eclipse.etrice.core.genmodel.fsm.fsmgen.Link; import org.eclipse.etrice.generator.fsm.base.CodegenHelpers; +import org.eclipse.etrice.generator.fsm.generic.ILanguageExtensionBase.TypedDataKind; /** * Implementation of the {@link org.eclipse.etrice.core.genmodel.fsm.fsmgen.ITransitionChainVisitor ITransitionChainVisitor} interface. @@ -36,12 +38,11 @@ public class TransitionChainVisitor implements ITransitionChainVisitor { private FSMHelpers fsmHelpers = new FSMHelpers(); // Initialized in constructor - private ExpandedModelComponent xpac; + private GraphContainer gc; private ILanguageExtensionBase langExt; private CodegenHelpers codegenHelpers; private IDetailCodeTranslator translationProvider; - private TransitionChain tc = null; private boolean dataDriven = false; /** @@ -52,42 +53,45 @@ public class TransitionChainVisitor implements ITransitionChainVisitor { * @param languageExt */ protected TransitionChainVisitor( - ExpandedModelComponent xpac, + GraphContainer gc, ILanguageExtensionBase languageExt, CodegenHelpers codegenHelpers, IDetailCodeTranslator translationProvider ) { - this.xpac = xpac; + this.gc = gc; this.langExt = languageExt; this.codegenHelpers = codegenHelpers; this.translationProvider = translationProvider; } - protected void init(TransitionChain tc) { - this.tc = tc; - - if (tc.getTransition() instanceof GuardedTransition) { + protected void init(TransitionBase tr) { + while (tr instanceof RefinedTransition) { + tr = ((RefinedTransition) tr).getTarget(); + } + if (tr instanceof GuardedTransition) { dataDriven = true; } - else if (tc.getTransition() instanceof InitialTransition) { + else if (tr instanceof InitialTransition) { dataDriven = true; } } // ITransitionChainVisitor interface - public String genActionOperationCall(Transition tr) { + public String genActionOperationCall(TransitionBase tr) { boolean noIfItem = dataDriven; - for(TransitionChain tc : xpac.getChains(tr)) - noIfItem |= tc.getTransition() instanceof InitialTransition; + Link l = FsmGenExtensions.getLinkFor(gc, tr); + for (Link ch : l.getChainHeads()) { + noIfItem |= ch.getTransition() instanceof InitialTransition; + } if (fsmHelpers.hasDetailCode(tr.getAction())) { if (noIfItem) return codegenHelpers.getActionCodeOperationName(tr)+"("+langExt.selfPointer(false)+");\n"; else { String dataArg = ""; - if(xpac.getData(tr) != null) - dataArg = langExt.generateArglistAndTypedData(tc.getData())[0]; + if (l.getCommonData() != null) + dataArg = langExt.generateArglistAndTypedData(l.getCommonData())[TypedDataKind.COMMA_SEPARATED_PARAM_IN_CALL.ordinal()]; return codegenHelpers.getActionCodeOperationName(tr)+"("+langExt.selfPointer(true)+"ifitem"+dataArg+");\n"; } } @@ -129,9 +133,9 @@ public class TransitionChainVisitor implements ITransitionChainVisitor { return "return " + codegenHelpers.getGenStateId(state) + " + STATE_MAX;"; } - public String genTypedData(TransitionChain tc) { - String[] result = langExt.generateArglistAndTypedData(tc.getData()); - return result[1]; + public String genTypedData(TransitionBase tr) { + Link l = FsmGenExtensions.getLinkFor(gc, tr); + return langExt.generateArglistAndTypedData(l.getCommonData())[TypedDataKind.DECLARATION_AND_INITIALIZATION.ordinal()]; } } |