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