| /******************************************************************************* |
| * Copyright (c) 2011 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: |
| * Henrik Rentz-Reichert (initial contribution) |
| * Thomas Schuetz (changed for C code generator) |
| * |
| *******************************************************************************/ |
| |
| package org.eclipse.etrice.generator.c.gen |
| |
| import com.google.inject.Inject |
| import com.google.inject.Singleton |
| import org.eclipse.etrice.core.fsm.fSM.ComponentCommunicationType |
| import org.eclipse.etrice.core.genmodel.etricegen.ExpandedActorClass |
| import org.eclipse.etrice.core.genmodel.etricegen.Root |
| import org.eclipse.etrice.core.genmodel.fsm.base.ILogger |
| import org.eclipse.etrice.core.room.CommunicationType |
| import org.eclipse.etrice.core.room.Operation |
| import org.eclipse.etrice.core.room.ProtocolClass |
| import org.eclipse.etrice.core.room.RoomModel |
| import org.eclipse.etrice.generator.c.Main |
| import org.eclipse.etrice.generator.fsm.base.IGeneratorFileIo |
| import org.eclipse.etrice.generator.generic.GenericActorClassGenerator |
| import org.eclipse.etrice.generator.generic.ILanguageExtension |
| import org.eclipse.etrice.generator.generic.ProcedureHelpers |
| import org.eclipse.etrice.generator.generic.RoomExtensions |
| |
| @Singleton |
| class ActorClassGen extends GenericActorClassGenerator { |
| |
| @Inject protected extension RoomExtensions |
| @Inject protected extension CExtensions |
| @Inject protected extension ProcedureHelpers |
| @Inject protected extension StateMachineGen |
| |
| @Inject protected ILanguageExtension langExt |
| @Inject protected IGeneratorFileIo fileIO |
| @Inject protected ILogger logger |
| |
| def doGenerate(Root root) { |
| for (xpac: root.xpActorClasses) { |
| val path = xpac.actorClass.generationTargetPath+xpac.actorClass.getPath |
| val infopath = xpac.actorClass.generationInfoPath+xpac.actorClass.getPath |
| var file = xpac.actorClass.getCHeaderFileName |
| |
| // header file |
| fileIO.generateFile("generating ActorClass header", path, infopath, file, root.generateHeaderFile(xpac)) |
| |
| // utils file |
| file = xpac.actorClass.getCUtilsFileName |
| fileIO.generateFile("generating ActorClass utils", path, infopath, file, root.generateUtilsFile(xpac)) |
| |
| // source file |
| if (xpac.actorClass.isBehaviorAnnotationPresent("BehaviorManual")) { |
| logger.logInfo("omitting ActorClass source for '"+xpac.actorClass.name+"' since @BehaviorManual is specified") |
| } |
| else { |
| file = xpac.actorClass.getCSourceFileName |
| fileIO.generateFile("generating ActorClass source", path, infopath, file, root.generateSourceFile(xpac)) |
| } |
| } |
| } |
| |
| def protected generateHeaderFile(Root root, ExpandedActorClass xpac) { |
| val ac = xpac.actorClass |
| val eventPorts = ac.allEndPorts.filter(p|(p.protocol as ProtocolClass).commType==CommunicationType::EVENT_DRIVEN) |
| val sendPorts = ac.allEndPorts.filter(p|(p.protocol as ProtocolClass).commType==CommunicationType::DATA_DRIVEN && p.conjugated) |
| val recvPorts = ac.allEndPorts.filter(p|(p.protocol as ProtocolClass).commType==CommunicationType::DATA_DRIVEN && !p.conjugated) |
| val dataDriven = ac.commType==ComponentCommunicationType::DATA_DRIVEN |
| val async = ac.commType==ComponentCommunicationType::ASYNCHRONOUS |
| val hasConstData = !(eventPorts.empty && recvPorts.empty && ac.allSAPs.empty && ac.allServiceImplementations.empty) |
| || Main::settings.generateMSCInstrumentation |
| val hasVarData = !(sendPorts.empty && ac.allAttributes.empty && xpac.stateMachine.empty && !hasConstData) |
| |
| ''' |
| /** |
| * @author generated by eTrice |
| * |
| * Header File of ActorClass «ac.name» |
| * |
| */ |
| |
| «generateIncludeGuardBegin(ac)» |
| |
| #include "etDatatypes.h" |
| #include "messaging/etMessage.h" |
| |
| «FOR dataClass : root.getReferencedDataClasses(ac)» |
| #include «dataClass.includePath» |
| «ENDFOR» |
| «FOR enumClass : root.getReferencedEnumClasses(ac)» |
| #include «enumClass.includePath» |
| «ENDFOR» |
| «FOR pc : root.getReferencedProtocolClasses(ac)» |
| #include «pc.includePath» |
| «ENDFOR» |
| |
| «ac.userCode(1, true)» |
| |
| typedef struct «ac.name» «ac.name»; |
| |
| /* const part of ActorClass (ROM) */ |
| «IF hasConstData» |
| typedef struct «ac.name»_const { |
| «IF Main::settings.generateMSCInstrumentation» |
| const char* instName; |
| |
| «ENDIF» |
| /* simple ports */ |
| «FOR ep : eventPorts» |
| «IF ep.multiplicity==1» |
| const «ep.getPortClassName()» «ep.name»; |
| «ENDIF» |
| «ENDFOR» |
| |
| /* data receive ports */ |
| «FOR ep : recvPorts» |
| «IF ep.multiplicity==1» |
| const «ep.getPortClassName()» «ep.name»; |
| «ENDIF» |
| «ENDFOR» |
| |
| /* saps */ |
| «FOR sap: ac.allSAPs» |
| const «sap.getPortClassName()» «sap.name»; |
| «ENDFOR» |
| |
| /* replicated ports */ |
| «FOR ep : ac.allEndPorts» |
| «IF ep.multiplicity!=1» |
| const etReplPort «ep.name»; |
| «ENDIF» |
| «ENDFOR» |
| |
| /* services */ |
| «FOR svc : ac.allServiceImplementations» |
| const etReplPort «svc.spp.name»; |
| «ENDFOR» |
| } «ac.name»_const; |
| «ELSE» |
| /* this actor class has no ports and thus no constant data */ |
| «ENDIF» |
| |
| «IF !xpac.stateMachine.empty» |
| |
| «xpac.genHeaderConstants» |
| «ENDIF» |
| |
| /* variable part of ActorClass (RAM) */ |
| «IF hasVarData» |
| struct «ac.name» { |
| «IF hasConstData» |
| const «ac.name»_const* const constData; |
| |
| «ENDIF» |
| /* data send ports */ |
| «FOR ep : sendPorts» |
| «IF ep.multiplicity==1» |
| «ep.getPortClassName()» «ep.name»; |
| «ENDIF» |
| «ENDFOR» |
| |
| «ac.allAttributes.attributes» |
| |
| «IF !xpac.stateMachine.empty» |
| |
| «xpac.genDataMembers» |
| «ENDIF» |
| }; |
| «ELSE» |
| struct «ac.name» { |
| /* This actor class has no data at all. |
| But the private actor instance data is passed to all life cycle functions. |
| By introducing the dummy data we keep this case simple |
| */ |
| int dummy; |
| }; |
| «ENDIF» |
| |
| void «ac.name»_init(«ac.name»* self); |
| |
| void «ac.name»_receiveMessage(void* self, const void* ifitem, const etMessage* msg); |
| |
| «IF dataDriven || async» |
| void «ac.name»_execute(«ac.name»* self); |
| «ENDIF» |
| |
| «ac.userStructorsDeclaration» |
| |
| «ac.latestOperations.operationsDeclaration(ac.name)» |
| |
| «ac.userCode(2, true)» |
| |
| «generateIncludeGuardEnd(ac)» |
| |
| ''' |
| } |
| |
| def protected generateUtilsFile(Root root, ExpandedActorClass xpac) { |
| val ac = xpac.actorClass |
| val eventPorts = ac.allEndPorts.filter(p|(p.protocol as ProtocolClass).commType==CommunicationType::EVENT_DRIVEN) |
| val replEventPorts = eventPorts.filter[multiplicity!=1] |
| val sendPorts = ac.allEndPorts.filter(p|(p.protocol as ProtocolClass).commType==CommunicationType::DATA_DRIVEN && p.conjugated && p.multiplicity==1) |
| val recvPorts = ac.allEndPorts.filter(p|(p.protocol as ProtocolClass).commType==CommunicationType::DATA_DRIVEN && !p.conjugated && p.multiplicity==1) |
| val portsWithOperations = ac.allInterfaceItems.filter(p|p.portClass!=null && p.portClass.operations.size>0) |
| val filename = (ac.eContainer as RoomModel).name.replaceAll("\\.","_")+"_"+ac.name+"_Utils" |
| |
| ''' |
| /** |
| * @author generated by eTrice |
| * |
| * Utils File of ActorClass «ac.name» |
| * |
| */ |
| |
| «generateIncludeGuardBegin(filename)» |
| |
| #include «ac.includePath» |
| |
| /* |
| * access macros for ports, operations and attributes |
| */ |
| |
| /* simple event ports */ |
| «FOR ep : eventPorts.filter[multiplicity==1]» |
| «FOR msg : ep.outgoing» |
| «val data1 = if (msg.data!=null) "data" else ""» |
| «val data2 = if (msg.data!=null) ", data" else ""» |
| #define «ep.name»_«msg.name»(«data1») «ep.portClassName»_«msg.name»(&self->constData->«ep.name»«data2») |
| «ENDFOR» |
| «ENDFOR» |
| |
| /* data receive ports */ |
| «FOR ep : recvPorts» |
| «FOR msg : ep.incoming» |
| #define «ep.name»_«msg.name» «ep.portClassName»_«msg.name»_get(&self->constData->«ep.name») |
| «ENDFOR» |
| «ENDFOR» |
| |
| /* data send ports */ |
| «FOR ep : sendPorts» |
| «FOR msg : ep.outgoing» |
| «val data1 = if (msg.data!=null) "data" else ""» |
| «val data2 = if (msg.data!=null) ", data" else ""» |
| #define «ep.name»_«msg.name»(«data1») «ep.portClassName»_«msg.name»_set(&self->«ep.name»«data2») |
| «ENDFOR» |
| «ENDFOR» |
| |
| /* saps */ |
| «FOR sap: ac.allSAPs» |
| «FOR msg : sap.outgoing» |
| «val data1 = if (msg.data!=null) "data" else ""» |
| «val data2 = if (msg.data!=null) ", data" else ""» |
| #define «sap.name»_«msg.name»(«data1») «sap.portClassName»_«msg.name»(&self->constData->«sap.name»«data2») |
| «ENDFOR» |
| «ENDFOR» |
| |
| /* replicated event ports */ |
| «IF !replEventPorts.empty» |
| #define ifitem_index (((etReplSubPort*)ifitem)->index) |
| «ENDIF» |
| «FOR ep : replEventPorts» |
| «FOR msg : ep.outgoing» |
| «val data1 = if (msg.data!=null) "data" else ""» |
| «val data2 = if (msg.data!=null) ", data" else ""» |
| #define «ep.name»_«msg.name»_broadcast(«data1») «ep.portClassName»_«msg.name»_broadcast(&self->constData->«ep.name»«data2») |
| #define «ep.name»_«msg.name»(idx«data2») «ep.portClassName»_«msg.name»(&self->constData->«ep.name», idx«data2») |
| «ENDFOR» |
| «ENDFOR» |
| |
| /* services */ |
| «FOR svc : ac.allServiceImplementations» |
| «FOR msg : svc.spp.outgoing» |
| «val data1 = if (msg.data!=null) "data" else ""» |
| «val data2 = if (msg.data!=null) ", data" else ""» |
| #define «svc.spp.name»_«msg.name»_broadcast(«data1») «svc.spp.portClassName»_«msg.name»_broadcast(&self->constData->«svc.spp.name»«data2») |
| #define «svc.spp.name»_«msg.name»(idx«data2») «svc.spp.portClassName»_«msg.name»(&self->constData->«svc.spp.name», idx«data2») |
| «ENDFOR» |
| «ENDFOR» |
| |
| /* operations */ |
| «FOR op : ac.latestOperations» |
| «val args = op.argList» |
| #define «op.name»(«args») «ac.name»_«op.name»(self«IF !op.arguments.empty», «args»«ENDIF») |
| «ENDFOR» |
| |
| /* attributes */ |
| «FOR a : ac.allAttributes» |
| #define «a.name» (self->«a.name») |
| «ENDFOR» |
| |
| /* port operations */ |
| «FOR p : portsWithOperations» |
| «FOR op : p.portClass.operations» |
| «val args = op.argList» |
| #define «p.name»_«op.name»(«args») «p.portClassName»_«op.name»((«p.portClassName»*)&self->constData->«p.name»«IF !op.arguments.empty», «args»«ENDIF») |
| «ENDFOR» |
| «ENDFOR» |
| |
| «generateIncludeGuardEnd(filename)» |
| |
| ''' |
| } |
| |
| private def argList(Operation op) { |
| '''«FOR a : op.arguments SEPARATOR ", "»«a.name»«ENDFOR»''' |
| } |
| |
| def protected generateSourceFile(Root root, ExpandedActorClass xpac) { |
| val ac = xpac.actorClass |
| val async = ac.commType==ComponentCommunicationType::ASYNCHRONOUS |
| val eventDriven = ac.commType==ComponentCommunicationType::EVENT_DRIVEN |
| val dataDriven = ac.commType==ComponentCommunicationType::DATA_DRIVEN |
| val handleEvents = async || eventDriven |
| |
| ''' |
| /** |
| * @author generated by eTrice |
| * |
| * Source File of ActorClass «ac.name» |
| * |
| */ |
| |
| #include "«ac.getCHeaderFileName»" |
| |
| #include "modelbase/etActor.h" |
| #include "debugging/etLogger.h" |
| #include "debugging/etMSCLogger.h" |
| #include "etUnit/etUnit.h" |
| #include "base/etMemory.h" |
| |
| «FOR pc : root.getReferencedProtocolClasses(ac)» |
| #include «pc.includePath» |
| «ENDFOR» |
| |
| #include "«ac.getCUtilsFileName»" |
| |
| «ac.userCode(3, true)» |
| |
| /* interface item IDs */ |
| «xpac.genInterfaceItemConstants» |
| |
| «IF !xpac.stateMachine.empty» |
| «xpac.genStateMachine()» |
| «ENDIF» |
| |
| void «ac.name»_init(«ac.name»* self){ |
| ET_MSC_LOGGER_SYNC_ENTRY("«ac.name»", "init") |
| «IF !xpac.stateMachine.empty» |
| «xpac.genInitialization» |
| «ENDIF» |
| ET_MSC_LOGGER_SYNC_EXIT |
| } |
| |
| |
| void «ac.name»_receiveMessage(void* self, const void* ifitem, const etMessage* msg){ |
| ET_MSC_LOGGER_SYNC_ENTRY("«ac.name»", "_receiveMessage") |
| «IF !xpac.stateMachine.empty» |
| «IF handleEvents» |
| «langExt.operationScope(ac.name, false)»receiveEvent(self, (etPort*)ifitem, msg->evtID, (void*)(((char*)msg)+MEM_CEIL(sizeof(etMessage)))); |
| «ELSE» |
| «langExt.operationScope(ac.name, false)»receiveEventInternal(self); |
| «ENDIF» |
| «ENDIF» |
| |
| ET_MSC_LOGGER_SYNC_EXIT |
| } |
| |
| «IF dataDriven || async» |
| void «ac.name»_execute(«ac.name»* self) { |
| ET_MSC_LOGGER_SYNC_ENTRY("«ac.name»", "_execute") |
| «IF !xpac.stateMachine.empty» |
| |
| «IF handleEvents» |
| «langExt.operationScope(ac.name, false)»receiveEvent(self, NULL, 0, NULL); |
| «ELSE» |
| «langExt.operationScope(ac.name, false)»receiveEventInternal(self); |
| «ENDIF» |
| «ENDIF» |
| |
| ET_MSC_LOGGER_SYNC_EXIT |
| } |
| «ENDIF» |
| |
| «ac.userStructorsImplementation» |
| |
| «operationsImplementation(ac.latestOperations, ac.name)» |
| |
| ''' |
| } |
| } |