From 8f7afaac9534c33fdefcc21adb11dba2ee2af8cd Mon Sep 17 00:00:00 2001 From: ipa-nhg Date: Thu, 7 Mar 2024 14:43:09 +0100 Subject: [PATCH] First implementation of the PlantUML generator --- README.md | 1 + docu/PlantUML.md | 8 ++ .../generator/PlantUMLCompiler.xtend | 86 +++++++++++++++++++ .../generator/RosSystemGenerator.xtend | 5 ++ 4 files changed, 100 insertions(+) create mode 100644 docu/PlantUML.md create mode 100644 plugins/de.fraunhofer.ipa.rossystem.xtext/src/de/fraunhofer/ipa/rossystem/generator/PlantUMLCompiler.xtend diff --git a/README.md b/README.md index c57ba793..ae23c7d5 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ Publications: - Combine components to form a ROS System - [Create manually a new RosSystem description](docu/RosSystemModelDescription.md) + - [Visualize a system using PlantUML](docu/PlantUML.md) - Examples: - [Simple publisher-subscriber](docu/Example_PubSub.md) diff --git a/docu/PlantUML.md b/docu/PlantUML.md new file mode 100644 index 00000000..1e2f0034 --- /dev/null +++ b/docu/PlantUML.md @@ -0,0 +1,8 @@ +## System Models visualization + +The visualization of the models is built on top of the [PlantUML](https://plantuml.com/eclipse) viewer for Eclipse. To install this plugin the Eclipse Marketplace can be used. This tool can be easily opened under the menu Help->Eclipse Marketplace. Then Search the "PlantUML plugin" and install it. + +The RosTooling will generate for every rossystem file a new PlaUML textual model compatible with the viewer. By default, the RosSystem compiler creates for every valid system model a file under src-gen/**SystemName**/resources/ called **SystemName**.puml. + +The file can be opened with a standard textual editor and the corresponding model can be visualized by opening the visualizer, under "Window"->"Show View"->"Other" and searching for "PlantUML". +:bangbang the file with the extension *.puml must be open, otherwise, the visualizer will not detect it. diff --git a/plugins/de.fraunhofer.ipa.rossystem.xtext/src/de/fraunhofer/ipa/rossystem/generator/PlantUMLCompiler.xtend b/plugins/de.fraunhofer.ipa.rossystem.xtext/src/de/fraunhofer/ipa/rossystem/generator/PlantUMLCompiler.xtend new file mode 100644 index 00000000..31f121c6 --- /dev/null +++ b/plugins/de.fraunhofer.ipa.rossystem.xtext/src/de/fraunhofer/ipa/rossystem/generator/PlantUMLCompiler.xtend @@ -0,0 +1,86 @@ +package de.fraunhofer.ipa.rossystem.generator + +import system.System +import com.google.inject.Inject +import system.RosNode +import system.impl.RosInterfaceImpl +import system.RosInterface +import system.impl.RosSystemConnectionImpl + +class PlantUMLCompiler{ + + @Inject extension GeneratorHelpers + + def compile_plantuml(System system) '''«init_pkg()» +@startuml + +/'SUBSYSTEMS'/ +«FOR subsystem:system.subsystems» +component «subsystem.name» { +«FOR component:getNodes(subsystem)» +«compile_ports(component)» +«ENDFOR» +«ENDFOR» } + +«FOR component:getNodes(system)» +«compile_ports(component)» +«ENDFOR» +«FOR connection:system.connections» «get_connection_port((connection as RosSystemConnectionImpl).from)» --> «get_connection_port((connection as RosSystemConnectionImpl).to)» +«ENDFOR» + +@enduml''' + + + def compile_ports(RosNode component)''' + component «(component as RosNode).name» { + + /' PORTS DEFINED AS AVAILABLE IN THE ROSSYSTEM FILE '/ + «FOR port:(component as RosNode).rosinterfaces» + «IF port_type(port)=="INPUT"» portin «get_valid_name(component.name, port.name)» as "«port.name»"« + IF (port as RosInterfaceImpl).reference.toString.contains("RosSubscriberReference")» #blue«ENDIF»« + IF (port as RosInterfaceImpl).reference.toString.contains("RosServiceServerReference")» #orange«ENDIF»« + IF (port as RosInterfaceImpl).reference.toString.contains("RosActionServerReference")» #green«ENDIF»«ENDIF» + «IF port_type(port)=="OUTPUT"» portout «get_valid_name(component.name, port.name)» as "«port.name»"« + IF (port as RosInterfaceImpl).reference.toString.contains("RosPublisherReference")» #blue«ENDIF»« + IF (port as RosInterfaceImpl).reference.toString.contains("RosServiceClientReference")» #orange«ENDIF»« + IF (port as RosInterfaceImpl).reference.toString.contains("RosActionClientReference")» #green«ENDIF»«ENDIF» + «ENDFOR» + + /' PORTS FROM THE ORIGINAL NODE '/ + «FOR sub:(component as RosNode).from.subscriber» portin «get_valid_name(component.name, sub.name)» as "«sub.name»" #line:blue + «ENDFOR» + «FOR ss:(component as RosNode).from.serviceserver» portin «get_valid_name(component.name, ss.name)» as "«ss.name»" #line:orange + «ENDFOR» + «FOR acts:(component as RosNode).from.actionserver» portin «get_valid_name(component.name, acts.name)» as "«acts.name»" #line:green + «ENDFOR» + «FOR pub:(component as RosNode).from.publisher» portout «get_valid_name(component.name, pub.name)» as "«pub.name»" #line:blue + «ENDFOR» + «FOR sc:(component as RosNode).from.serviceclient» portout «get_valid_name(component.name, sc.name)» as "«sc.name»" #line:orange + «ENDFOR» + «FOR actc:(component as RosNode).from.actionclient» portout «get_valid_name(component.name, actc.name)» as "«actc.name»" #line:green«ENDFOR» + } + + ''' + + def String port_type (RosInterface rosinterface){ + if ((rosinterface as RosInterfaceImpl).reference.toString.matches + (".*RosSubscriberReferenceImpl.*|.*RosServiceServerReference.*|.*RosActionServerReference.*")){ + return "INPUT" + }else { + return "OUTPUT" + } + } + + def get_valid_name (String componentName, String PortName){ + val identifier = (componentName+"."+PortName).replace("/","_").replace("~","_") + return identifier + } + + def get_connection_port (RosInterface port){ + val componentName=(port.eContainer as RosNode).name + return get_valid_name (componentName, port.name) + } +} + + + diff --git a/plugins/de.fraunhofer.ipa.rossystem.xtext/src/de/fraunhofer/ipa/rossystem/generator/RosSystemGenerator.xtend b/plugins/de.fraunhofer.ipa.rossystem.xtext/src/de/fraunhofer/ipa/rossystem/generator/RosSystemGenerator.xtend index 23ac9a71..5100ccf3 100644 --- a/plugins/de.fraunhofer.ipa.rossystem.xtext/src/de/fraunhofer/ipa/rossystem/generator/RosSystemGenerator.xtend +++ b/plugins/de.fraunhofer.ipa.rossystem.xtext/src/de/fraunhofer/ipa/rossystem/generator/RosSystemGenerator.xtend @@ -21,6 +21,7 @@ class RosSystemGenerator extends AbstractGenerator { @Inject extension PackageXmlCompiler @Inject extension CMakeListsCompiler @Inject extension READMECompiler + @Inject extension PlantUMLCompiler override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) { for (system : resource.allContents.toIterable.filter(System)){ @@ -28,6 +29,10 @@ class RosSystemGenerator extends AbstractGenerator { system.getName().toLowerCase+"/README.md", compile_toREADME(system).toString().replace("\t"," ") ) + fsa.generateFile( + system.getName().toLowerCase+"/resource/" + system.getName().toLowerCase + ".puml", + compile_plantuml(system) + ) if (system.fromFile.isNullOrEmpty) { fsa.generateFile( system.getName().toLowerCase+"/launch/"+system.getName()+".launch.py",