| /***************************************************************************** |
| * 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 |
| } |
| } |