blob: 67f3ba9080594a4a7f882134e96028de9abcfbfe [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2018 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.message
import java.util.HashMap
import java.util.Map
import org.eclipse.papyrus.designer.languages.common.base.ElementUtils
import org.eclipse.papyrus.designer.transformation.base.utils.TransformationException
import org.eclipse.papyrus.designer.transformation.core.transformations.TransformationContext
import org.eclipse.papyrus.infra.tools.file.ProjectBasedFileAccess
import org.eclipse.papyrus.robotics.ros2.codegen.RosTransformations
import org.eclipse.papyrus.robotics.ros2.codegen.build.CreateMsgPkgCMakeLists
import org.eclipse.papyrus.robotics.ros2.codegen.build.CreateMsgPkgPackageXML
import org.eclipse.papyrus.robotics.ros2.codegen.utils.MessageUtils
import org.eclipse.papyrus.robotics.ros2.codegen.utils.ProjectTools
import org.eclipse.papyrus.robotics.ros2.preferences.Ros2PreferenceUtils
import org.eclipse.uml2.uml.DataType
import org.eclipse.uml2.uml.Package
import org.eclipse.uml2.uml.TemplateBinding
import static extension org.eclipse.papyrus.robotics.core.utils.InteractionUtils.*
import static extension org.eclipse.papyrus.robotics.ros2.codegen.utils.MessageUtils.*
import static extension org.eclipse.papyrus.robotics.ros2.codegen.utils.PackageTools.pkgName
import org.eclipse.core.runtime.CoreException
import org.eclipse.core.runtime.NullProgressMonitor
import org.eclipse.papyrus.designer.transformation.core.transformations.ExecuteTransformationChain
/**
* Handle creation of a ROS2 message package
*/
class CreateMsgPackage {
/**
* List of referenced packages
*/
Map<String, Boolean> packageNames
/**
* List of currently available ROS2 packages
*/
Map<String, Boolean> availRosPackages
/**
* Construct
*/
new() {
packageNames = new HashMap<String, Boolean>()
availRosPackages = MessageUtils.ros2AvailMsgPkgs(Ros2PreferenceUtils.standardMessagePath)
}
/**
* Create message packages for the communication objects referenced by a
* template binding
*/
def void createMsgPkgs(TemplateBinding tb) {
val cp = tb.communicationPattern
if (cp.isQuery || cp.isAction) {
// add message pkg of the service definition
val sd = tb.serviceDefinition
createMsgPkg(sd.messagePackage)
}
// add message pkg of used communication objects
for (tps : tb.parameterSubstitutions) {
val actual = tps.actual as DataType
actual.makeDTExternal
createMsgPkg(actual.messagePackage)
}
}
/**
* Create a message package for a given data type
* Convenience function - will obtain the package name
* from the data type
*/
def void createMsgPkg(DataType dt) {
createMsgPkg(dt.messagePackage)
}
/**
* Create a message package for a given package name
*/
def void createMsgPkg(Package msgPkg) {
val msgPkgName = msgPkg.name
if (packageNames.containsKey(msgPkgName)) {
// already created message package => nothing to do
return
}
packageNames.put(msgPkgName, true);
// pkg is typically a copy containing only a subset, since a lazy copier is used
// => obtain source package instead via the qualified name (the function below returns the first
// occurrence. It's the source package, since it is stored "before" the copy within the resource set)
// TODO - assumption above seems to hold, but might be unsafe in general
var srcPkg = ElementUtils.getQualifiedElementFromRS(TransformationContext.current.modelRoot,
msgPkgName) as Package;
// don't use msgPkgName, here we lookup the ROS2 pendant using
// lower case package names
val project = msgPkg.pkgName.messageProject;
if (project === null) {
// happens, if nothing to do (already in standard path)
return
}
val fileAccess = new ProjectBasedFileAccess(project)
CreateMsgPkgCMakeLists.generate(fileAccess, srcPkg);
CreateMsgPkgPackageXML.generate(fileAccess, srcPkg);
val cm = new CreateMessage(this);
// messages covers push and send (and enumerations)
for (msg : srcPkg.messages) {
cm.generateFile(fileAccess, msg.name, MessageUtils.MESSAGE, cm.createDtOrEnumMsg(msg))
}
for (sd : srcPkg.queries) {
// request and reply data types are found in communication package
val tb = sd.templateBinding
val req = MessageUtils.getRequest(tb)
val res = MessageUtils.getResponse(tb)
cm.generateFile(fileAccess, sd.nameWoPrefix, MessageUtils.SERVICE, cm.createServiceMsg(req as DataType, res as DataType))
}
for (sd : srcPkg.actions) {
// request and reply data types are found in communication package
val tb = sd.templateBinding
val goal = MessageUtils.getGoal(tb)
val res = MessageUtils.getResponse(tb)
val feedback = MessageUtils.getFeedback(tb)
cm.generateFile(fileAccess, sd.nameWoPrefix, MessageUtils.ACTION, cm.createActionMsg(goal as DataType, res as DataType, feedback as DataType))
}
try {
fileAccess.cleanUntouched(project.getFolder(MessageUtils.MESSAGE), new NullProgressMonitor);
fileAccess.cleanUntouched(project.getFolder(MessageUtils.SERVICE), new NullProgressMonitor);
fileAccess.cleanUntouched(project.getFolder(MessageUtils.ACTION), new NullProgressMonitor);
} catch (CoreException e) {
throw new RuntimeException(e);
}
}
/**
* Obtain the project/package for a given data type (communication object). This
* function determines the qualified ROS2 name and checks first, whether the
* type is already available via "ros2 interface show". If yes, no new project
* is created
*/
def getMessageProject(String msgPkgName) {
if (availRosPackages.containsKey(msgPkgName)) {
// message is available in standard message path => nothing to do.
return null;
}
val genProject = ProjectTools.getProject(msgPkgName);
if (genProject === null) {
throw new TransformationException(ExecuteTransformationChain.USER_CANCEL);
}
return genProject
}
}