| /***************************************************************************** |
| * Copyright (c) 2020 CEA LIST. |
| * |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * Contributors: |
| * Ansgar Radermacher ansgar.radermacher@cea.fr |
| * |
| *****************************************************************************/ |
| |
| package org.eclipse.papyrus.robotics.ros2.codegen.component |
| |
| import org.eclipse.core.resources.IProject |
| import org.eclipse.emf.ecore.util.EcoreUtil |
| import org.eclipse.papyrus.designer.languages.common.profile.Codegen.TemplateBinding |
| import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.Include |
| import org.eclipse.papyrus.designer.transformation.base.utils.TransformationException |
| import org.eclipse.papyrus.designer.transformation.core.transformations.ExecuteTransformationChain |
| import org.eclipse.papyrus.designer.transformation.core.transformations.TransformationContext |
| import org.eclipse.papyrus.infra.tools.file.IPFileSystemAccess |
| import org.eclipse.papyrus.robotics.codegen.common.utils.ApplyProfiles |
| import org.eclipse.papyrus.robotics.core.utils.PortUtils |
| import org.eclipse.papyrus.robotics.profile.robotics.components.Activity |
| import org.eclipse.papyrus.robotics.profile.robotics.components.ComponentDefinition |
| import org.eclipse.papyrus.robotics.profile.robotics.functions.Function |
| import org.eclipse.papyrus.robotics.ros2.codegen.message.CreateMsgPackage |
| import org.eclipse.papyrus.robotics.ros2.codegen.utils.ProjectTools |
| import org.eclipse.papyrus.uml.tools.utils.StereotypeUtil |
| import org.eclipse.uml2.uml.Behavior |
| import org.eclipse.uml2.uml.Class |
| import org.eclipse.uml2.uml.Property |
| import org.eclipse.uml2.uml.util.UMLUtil |
| |
| import static extension org.eclipse.papyrus.robotics.codegen.common.utils.ActivityUtils.* |
| import static extension org.eclipse.papyrus.robotics.codegen.common.utils.ComponentUtils.isRegistered |
| import static extension org.eclipse.papyrus.robotics.core.utils.InteractionUtils.* |
| import static extension org.eclipse.papyrus.robotics.core.utils.ParameterUtils.getAllParameters |
| import static extension org.eclipse.papyrus.robotics.ros2.codegen.component.Constructor.createConstructor |
| import static extension org.eclipse.papyrus.robotics.ros2.codegen.component.CreateMain.createMain |
| import static extension org.eclipse.papyrus.robotics.ros2.codegen.component.CreateMain.registerComponent |
| import static extension org.eclipse.papyrus.robotics.ros2.codegen.component.ParameterTransformations.declareParameters |
| import static extension org.eclipse.papyrus.robotics.ros2.codegen.component.ParameterTransformations.initParameters |
| import static extension org.eclipse.papyrus.robotics.ros2.codegen.component.ParameterTransformations.moveParameters |
| import static extension org.eclipse.papyrus.robotics.ros2.codegen.utils.MessageUtils.* |
| import static extension org.eclipse.papyrus.robotics.ros2.codegen.utils.RosHelpers.* |
| import static extension org.eclipse.papyrus.designer.languages.common.base.ElementUtils.varName |
| import org.eclipse.uml2.uml.OpaqueBehavior |
| import java.util.ArrayList |
| |
| class ComponentTransformations { |
| |
| IPFileSystemAccess fileAccess; |
| IProject genProject |
| |
| new(IPFileSystemAccess fileAccess, IProject genProject) { |
| this.fileAccess = fileAccess; |
| this.genProject = genProject; |
| } |
| |
| /** |
| * Move functions in passed activity to component (node) level |
| */ |
| def static liftFunctions(Class component) { |
| val cd = UMLUtil.getStereotypeApplication(component, ComponentDefinition) |
| for (activity : cd.activities) { |
| activity.liftFunctions(component) |
| } |
| } |
| |
| /** |
| * Remove activities from node |
| */ |
| def static removeActivities(Class component) { |
| val cd = UMLUtil.getStereotypeApplication(component, ComponentDefinition) |
| for (activity : cd.activities.clone) { |
| // remove activity and associated attribute |
| component.getAttribute(null, activity.base_Class).destroy |
| activity.base_Class.destroy |
| } |
| } |
| |
| /** |
| * The service definition is used as parameter type for the callback parameters with a suitable ptr |
| * declaration that sub-selects one of the contained types. In case of a service definition of query |
| * for instance, ROS uses <service def name>::Request::SharedPtr |
| * The C++ code generator follows the template binding of the service definition and includes the |
| * referenced types. Therefore, we need to remove this binding to avoid this code is generated. Adding |
| * a no-code-gen tag to these would not a solution, as the referenced type could actually be used in |
| * another message definition. |
| */ |
| def static removeTemplateSig(Class component) { |
| for(port : PortUtils.getAllPorts(component)) { |
| if (port.serviceDefinition !== null && port.serviceDefinition.templateBinding !== null) { |
| port.serviceDefinition.templateBinding.destroy(); |
| } |
| } |
| } |
| |
| /** |
| * Move functions in passed activity to component level |
| */ |
| def static liftFunctions(Activity activity, Class component) { |
| for (fct : activity.base_Class.attributes) { |
| if (fct.type instanceof Behavior) { |
| val fctType = fct.type as Behavior |
| // moving the component implies losing the stereotype |
| // application, save information before move |
| var fctSt = UMLUtil.getStereotypeApplication(fctType, Function); |
| if (fctSt !== null) { |
| // Use generic code, avoid that it would break if additional properties are added to functions |
| val copy = EcoreUtil.copy(fctSt) as Function |
| component.ownedBehaviors.add(fctType) |
| fctSt = StereotypeUtil.applyApp(fctType, Function); |
| for (feature : fctSt.eClass.EStructuralFeatures) { |
| if (feature.changeable) { |
| fctSt.eSet(feature, copy.eGet(feature)) |
| } |
| } |
| } |
| else { |
| // should not happen, i.e. all activity attributes should be functions |
| component.ownedBehaviors.add(fctType) |
| } |
| val specification = component.createOwnedOperation(fctType.name, null, null) |
| fctType.specification = specification |
| } |
| } |
| } |
| |
| def static createPubsSubsAttrs(Class component) { |
| for (port : PortUtils.getAllPorts(component)) { |
| val cp = port.communicationPattern |
| if (cp.isPush || cp.isPubSub) { |
| var Property attribute |
| if (port.provideds.size() > 0) { |
| val rosPublisher = getRosType(port, "ros2Library::rclcpp_lifecycle::LifecyclePublisher") |
| attribute = component.createOwnedAttribute('''«port.varName»_pub_''', rosPublisher) |
| } else if (port.requireds.size() > 0) { |
| val rosSubscriber = getRosType(port, "ros2Library::rclcpp::Subscription") |
| attribute = component.createOwnedAttribute('''«port.varName»_sub_''', rosSubscriber) |
| } |
| ApplyProfiles.applyCommonProfile(attribute) |
| val template = StereotypeUtil.applyApp(attribute, TemplateBinding) |
| template.actuals.add(port.commObject) |
| attribute.useSharedPtr |
| } |
| } |
| } |
| |
| def static createSendAttrs(Class component) { |
| for (port : PortUtils.getAllPorts(component)) { |
| if (port.communicationPattern.isSend) { |
| var Property attribute |
| if (port.provideds.size() > 0) { |
| val rosSubscriber = getRosType(port, "ros2Library::rclcpp::Subscription") |
| attribute = component.createOwnedAttribute('''«port.varName»_recv_''', rosSubscriber) |
| } else if (port.requireds.size() > 0) { |
| val rosPublisher = getRosType(port, "ros2Library::rclcpp_lifecycle::LifecyclePublisher") |
| attribute = component.createOwnedAttribute('''«port.varName»_send_''', rosPublisher) |
| } |
| ApplyProfiles.applyCommonProfile(attribute) |
| val template = StereotypeUtil.applyApp(attribute, TemplateBinding) |
| template.actuals.add(port.commObject) |
| attribute.useSharedPtr |
| } |
| } |
| } |
| |
| def static createServiceAttrs(Class component) { |
| for (port : PortUtils.getAllPorts(component)) { |
| if (port.communicationPattern.isQuery) { |
| var Property attribute |
| if (port.provideds.size() > 0) { |
| val rosService = getRosType(port, "ros2Library::rclcpp::Service"); |
| attribute = component.createOwnedAttribute('''«port.varName»_srv_''', rosService); |
| } else if (port.requireds.size() > 0) { |
| val rosClient = getRosType(port, "ros2Library::rclcpp::Client"); |
| attribute = component.createOwnedAttribute('''«port.varName»_client_''', rosClient); |
| } |
| var template = StereotypeUtil.applyApp(attribute, TemplateBinding) |
| if (template === null) { |
| ApplyProfiles.applyCommonProfile(attribute) |
| template = StereotypeUtil.applyApp(attribute, TemplateBinding) |
| } |
| template.actuals.add(port.serviceType) |
| attribute.useSharedPtr |
| } |
| } |
| } |
| |
| /** |
| * Add action attributes |
| */ |
| def static createActionAttrs(Class component) { |
| for (port : PortUtils.getAllPorts(component)) { |
| if (port.communicationPattern.isAction) { |
| var Property attribute |
| if (port.provideds.size() > 0) { |
| val rosService = getRosType(port, "ros2Library::rclcpp_action::Server"); |
| attribute = component.createOwnedAttribute('''«port.varName»_actsrv_''', rosService); |
| } else if (port.requireds.size() > 0) { |
| val rosClient = getRosType(port, "ros2Library::rclcpp_action::Client"); |
| attribute = component.createOwnedAttribute('''«port.varName»_actcli_''', rosClient); |
| } |
| var template = StereotypeUtil.applyApp(attribute, TemplateBinding) |
| if (template === null) { |
| ApplyProfiles.applyCommonProfile(attribute) |
| template = StereotypeUtil.applyApp(attribute, TemplateBinding) |
| } |
| template.actuals.add(port.serviceType) |
| attribute.useSharedPtr |
| } |
| } |
| } |
| |
| /** |
| * Remove the port, as well as its type (component service, |
| * normally a nested classifier) |
| */ |
| def static removePorts(Class component) { |
| for (port : PortUtils.getAllPorts(component)) { |
| port.type.destroy(); |
| port.destroy(); |
| } |
| } |
| |
| /** |
| * Remove functions that are not referenced by activities (this can happen after |
| * deletion of a function from an activity) |
| */ |
| def static removeUnrefFunctions(Class component) { |
| val cd = UMLUtil.getStereotypeApplication(component, ComponentDefinition) |
| val fctList = new ArrayList<Function>(); |
| |
| for (activity : cd.activities) { |
| fctList.addAll(activity.functions) |
| } |
| for (pe : component.nearestPackage.packagedElements.clone) { |
| if (pe instanceof OpaqueBehavior) { |
| val fct = UMLUtil.getStereotypeApplication(pe, Function) |
| if (fct !== null && !fctList.contains(fct)) { |
| pe.destroy(); |
| } |
| } |
| } |
| } |
| |
| def componentTrafo(Class component, CreateMsgPackage msgPkgCreator) { |
| msgPkgCreator.createMessagesOrServices(component) |
| |
| if (genProject === null) { |
| throw new TransformationException(ExecuteTransformationChain.USER_CANCEL); |
| } |
| component.liftFunctions |
| component.createConstructor |
| var include = StereotypeUtil.applyApp(component, Include); |
| |
| // TODO: do via external type add rclcpp via external type? |
| include.header = include.header + "#include <rclcpp/rclcpp.hpp>\n" |
| if (component.hasActions) { |
| include.header = include.header + "#include <rclcpp_action/rclcpp_action.hpp>\n" |
| } |
| if (component.isRegistered) { |
| include.body = include.body + component.registerComponent; |
| } |
| |
| component.createMain; |
| |
| if (component.hasExternalCode) { |
| new Ros2CodeSkeleton().createSkeleton(component); |
| } |
| // val stdString = getRosType(node, "ros2Library::std_msgs::String"); |
| // node.createDependency(stdString) |
| component.removeActivities |
| component.createPubsSubsAttrs |
| component.createSendAttrs |
| component.createServiceAttrs |
| component.createActionAttrs |
| |
| if (component.allParameters.size > 0) { |
| component.declareParameters |
| component.initParameters |
| } |
| // move parameter needs to be called even if there are not parameters |
| // as it also removes the nested class holding parameters |
| component.moveParameters |
| |
| } |
| |
| def componentCodegen(Class component, CreateMsgPackage msgPkgCreator) { |
| val codeGen = new RoboticsCppCreator(genProject, fileAccess, "src-skel/", "src/"); |
| |
| component.removeTemplateSig |
| component.removePorts |
| component.removeUnrefFunctions; |
| TransformationContext.current.project = genProject |
| ProjectTools.genCode(codeGen, component) |
| } |
| } |