Commit 039f9fd2 authored by Markus Georg Bendel's avatar Markus Georg Bendel
Browse files

Merge branch 'integrate-someip' into 'master'

# Conflicts:
#   pom.xml
#   src/main/java/de/monticore/lang/monticar/generator/middleware/cli/DistributedTargetGeneratorCli.java
parents 59868f08 231f6e4c
Pipeline #188880 failed with stages
in 3 minutes and 11 seconds
# (c) https://github.com/MontiCore/monticore
stages: stages:
- windows - windows
- linuxCompile - linuxCompile
...@@ -33,6 +34,14 @@ DeployJobLinux: ...@@ -33,6 +34,14 @@ DeployJobLinux:
only: only:
- master - master
MqttIntegrationJob:
stage: integration
image: registry.git.rwth-aachen.de/monticore/embeddedmontiarc/generators/emam2mqtt:v1
dependencies:
- CompileJobLinux
script:
- ./src/test/bash/integrationTestMqtt.sh
RosIntegrationJob: RosIntegrationJob:
stage: integration stage: integration
image: registry.git.rwth-aachen.de/monticore/embeddedmontiarc/generators/emam2middleware/ema-ros-kinetic image: registry.git.rwth-aachen.de/monticore/embeddedmontiarc/generators/emam2middleware/ema-ros-kinetic
......
<!-- (c) https://github.com/MontiCore/monticore -->
# Dependencies needed to compile the generated projects # Dependencies needed to compile the generated projects
## Note ## Note
The generator creates compile scripts for all supported compilers. A project with ROS or ROS2 contains `compile.sh` and `compileMsbuild.bat` as Linux and Windows(with Msbuild) are supported. The generator creates compile scripts for all supported compilers. A project with ROS or ROS2 contains `compile.sh` and `compileMsbuild.bat` as Linux and Windows(with Msbuild) are supported.
......
<!-- (c) https://github.com/MontiCore/monticore -->
# Quickstart guide for generator developers # Quickstart guide for generator developers
- Download and install Java(8+), Maven, as well as Git. - Download and install Java(8+), Maven, as well as Git.
- Clone this repository: - Clone this repository:
...@@ -21,4 +22,4 @@ ...@@ -21,4 +22,4 @@
## Compiling the Projects ## Compiling the Projects
Options Options
1. Add your new generated test projects to the integration tests(check the scripts in [src/test/bash/](src/test/bash/) as well as [.gitlab-ci.yml](.gitlab-ci.yml)) and let the CI/CD system compile them. Alternatively you can use Docker to run the integration tests locally(reference [README.md](README.md) , Section 'Running the Integration tests locally') 1. Add your new generated test projects to the integration tests(check the scripts in [src/test/bash/](src/test/bash/) as well as [.gitlab-ci.yml](.gitlab-ci.yml)) and let the CI/CD system compile them. Alternatively you can use Docker to run the integration tests locally(reference [README.md](README.md) , Section 'Running the Integration tests locally')
2. Install all dependencies (reference [README.md](README.md) , Section 'Dependencies needed to compile the generated projects') and execute the generated compile scripts. 2. Install all dependencies (reference [README.md](README.md) , Section 'Dependencies needed to compile the generated projects') and execute the generated compile scripts.
\ No newline at end of file
<!-- (c) https://github.com/MontiCore/monticore -->
# Quickstart guide for generator users # Quickstart guide for generator users
- Download the latest version of the generator from the [se-nexus](https://nexus.se.rwth-aachen.de/service/rest/repository/browse/public/de/monticore/lang/monticar/embedded-montiarc-math-middleware-generator/) (e.g. .../0.0.20-20190311.154342-1/embedded-montiarc-math-middleware-generator-0.0.20-20190311.154342-1-jar-with-dependencies.jar) and save it as mw-generator.jar - Download the latest version of the generator from the [se-nexus](https://nexus.se.rwth-aachen.de/service/rest/repository/browse/public/de/monticore/lang/monticar/embedded-montiarc-math-middleware-generator/) (e.g. .../0.0.20-20190311.154342-1/embedded-montiarc-math-middleware-generator-0.0.20-20190311.154342-1-jar-with-dependencies.jar) and save it as mw-generator.jar
- Create a `project.json` config file for your project - Create a `project.json` config file for your project
...@@ -25,4 +26,4 @@ call substCompileMingw.bat ...@@ -25,4 +26,4 @@ call substCompileMingw.bat
For two example Projects using this generator see: For two example Projects using this generator see:
- [Cooperative Intersection](https://git.rwth-aachen.de/monticore/EmbeddedMontiArc/applications/cooperativeintersection) (Uses EMAM, ROS, CoInCar Simulator) - [Cooperative Intersection](https://git.rwth-aachen.de/monticore/EmbeddedMontiArc/applications/cooperativeintersection) (Uses EMAM, ROS, CoInCar Simulator)
- [Autonomous driving](https://git.rwth-aachen.de/autonomousdriving/torcs_dl) (Uses EMADL, ROS, Torcs Simulator) - [Autonomous driving](https://git.rwth-aachen.de/autonomousdriving/torcs_dl) (Uses EMADL, ROS, Torcs Simulator)
\ No newline at end of file
<!-- (c) https://github.com/MontiCore/monticore -->
# EMAM2Middleware # EMAM2Middleware
![pipeline](https://git.rwth-aachen.de/monticore/EmbeddedMontiArc/generators/EMAM2Middleware/badges/master/build.svg) ![pipeline](https://git.rwth-aachen.de/monticore/EmbeddedMontiArc/generators/EMAM2Middleware/badges/master/build.svg)
![coverage](https://git.rwth-aachen.de/monticore/EmbeddedMontiArc/generators/EMAM2Middleware/badges/master/coverage.svg) ![coverage](https://git.rwth-aachen.de/monticore/EmbeddedMontiArc/generators/EMAM2Middleware/badges/master/coverage.svg)
...@@ -36,7 +37,7 @@ An example config file with all clustering algorithms: [config](src/test/resourc ...@@ -36,7 +37,7 @@ An example config file with all clustering algorithms: [config](src/test/resourc
| outputDir | String | ✅ | path to output directory for generated files | | outputDir | String | ✅ | path to output directory for generated files |
| rootModel | String | ✅ | fully qualified name of the root model | | rootModel | String | ✅ | fully qualified name of the root model |
| generators | List | ✅ | List of generator identfiers<br> 'cpp', 'emadlcpp', 'roscpp', 'rclcpp' | | generators | List | ✅ | List of generator identfiers<br> 'cpp', 'emadlcpp', 'roscpp', 'rclcpp' |
| emadlBackend | String | ❓ | deep-learning-framework backend<br> 'MXNET'(Default), 'CAFFE2' | | emadlBackend | String | ❓ | deep-learning-framework backend<br> 'MXNET'(Default), 'CAFFE2', 'GLUON' |
| writeTagFile | Bool | ❓ | Writes a .tag file with all Middleware tags into the generated code<br> Defaults to false | | writeTagFile | Bool | ❓ | Writes a .tag file with all Middleware tags into the generated code<br> Defaults to false |
| clusteringParameters | Object | ❓ | Options to cluster the component before generating<br> See below | | clusteringParameters | Object | ❓ | Options to cluster the component before generating<br> See below |
...@@ -146,4 +147,4 @@ Run the tests by executing [dockerLocalIntegrationTestRos.sh](src/test/bash/dock ...@@ -146,4 +147,4 @@ Run the tests by executing [dockerLocalIntegrationTestRos.sh](src/test/bash/dock
```bash ```bash
sudo src/test/bash/dockerLocalIntegrationTestRos.sh sudo src/test/bash/dockerLocalIntegrationTestRos.sh
sudo src/test/bash/dockerLocalIntegrationTestRos2.sh sudo src/test/bash/dockerLocalIntegrationTestRos2.sh
``` ```
\ No newline at end of file
<!-- (c) https://github.com/MontiCore/monticore -->
# Adding a new middleware generator # Adding a new middleware generator
## EmbeddedMontiArc ## EmbeddedMontiArc
......
<!-- (c) https://github.com/MontiCore/monticore -->
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
...@@ -18,14 +19,15 @@ ...@@ -18,14 +19,15 @@
<!-- .. SE-Libraries .................................................. --> <!-- .. SE-Libraries .................................................. -->
<se-commons.version>1.7.7</se-commons.version> <se-commons.version>1.7.7</se-commons.version>
<embedded-montiarc.version>0.1.18-SNAPSHOT</embedded-montiarc.version> <embedded-montiarc.version>0.1.18-SNAPSHOT</embedded-montiarc.version>
<Embedded-montiarc-math-generator.version>0.1.12-SNAPSHOT</Embedded-montiarc-math-generator.version> <Embedded-montiarc-math-generator.version>0.1.14-SNAPSHOT</Embedded-montiarc-math-generator.version>
<Embedded-montiarc-math-roscpp-generator.version>0.1.6-SNAPSHOT</Embedded-montiarc-math-roscpp-generator.version> <Embedded-montiarc-math-roscpp-generator.version>0.1.8-SNAPSHOT</Embedded-montiarc-math-roscpp-generator.version>
<Embedded-montiarc-math-emam2someip-generator.version>1.1-SNAPSHOT</Embedded-montiarc-math-emam2someip-generator.version> <Embedded-montiarc-math-emam2someip-generator.version>1.1-SNAPSHOT</Embedded-montiarc-math-emam2someip-generator.version>
<EMADL2CPP.version>0.2.8</EMADL2CPP.version> <EMADL2CPP.version>0.3.6</EMADL2CPP.version>
<EMADL.version>0.2.7-SNAPSHOT</EMADL.version> <EMADL.version>0.2.10-SNAPSHOT</EMADL.version>
<SOMEIP.version>1.1-SNAPSHOT</SOMEIP.version> <SOMEIP.version>1.1-SNAPSHOT</SOMEIP.version>
<MQTT.version>1.5-SNAPSHOT</MQTT.version>
<embedded-montiarc-component-clustering.version>0.0.2-SNAPSHOT</embedded-montiarc-component-clustering.version> <embedded-montiarc-component-clustering.version>0.0.2-SNAPSHOT</embedded-montiarc-component-clustering.version>
<!-- .. Libraries .................................................. --> <!-- .. Libraries .................................................. -->
...@@ -114,6 +116,11 @@ ...@@ -114,6 +116,11 @@
<version>${SOMEIP.version}</version> <version>${SOMEIP.version}</version>
</dependency> </dependency>
<dependency>
<groupId>de.monticore.lang.monticar</groupId>
<artifactId>embedded-montiarc-math-mqtt-generator</artifactId>
<version>${MQTT.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- (c) https://github.com/MontiCore/monticore -->
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
...@@ -83,4 +84,4 @@ ...@@ -83,4 +84,4 @@
<activeProfiles> <activeProfiles>
<activeProfile>se-nexus</activeProfile> <activeProfile>se-nexus</activeProfile>
</activeProfiles> </activeProfiles>
</settings> </settings>
\ No newline at end of file
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.lang.monticar.generator.middleware; package de.monticore.lang.monticar.generator.middleware;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol; import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol;
......
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.lang.monticar.generator.middleware; package de.monticore.lang.monticar.generator.middleware;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol; import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol;
import de.monticore.lang.embeddedmontiarc.helper.SymbolPrinter;
import de.monticore.lang.monticar.clustering.AutomaticClusteringHelper; import de.monticore.lang.monticar.clustering.AutomaticClusteringHelper;
import de.monticore.lang.monticar.clustering.ClusteringResult; import de.monticore.lang.monticar.clustering.ClusteringResult;
import de.monticore.lang.monticar.clustering.ClusteringResultList; import de.monticore.lang.monticar.clustering.ClusteringResultList;
...@@ -15,6 +17,7 @@ import de.monticore.lang.monticar.generator.middleware.impls.*; ...@@ -15,6 +17,7 @@ import de.monticore.lang.monticar.generator.middleware.impls.*;
import de.monticore.lang.monticar.generator.middleware.templates.compile.CompilationGenerator; import de.monticore.lang.monticar.generator.middleware.templates.compile.CompilationGenerator;
import de.monticore.lang.tagging._symboltable.TaggingResolver; import de.monticore.lang.tagging._symboltable.TaggingResolver;
import de.se_rwth.commons.logging.Log; import de.se_rwth.commons.logging.Log;
import org.apache.commons.io.FileUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
...@@ -104,6 +107,10 @@ public class DistributedTargetGenerator{ ...@@ -104,6 +107,10 @@ public class DistributedTargetGenerator{
middlewareTagGen.setGenerationTargetPath(generationTargetPath + "emam/"); middlewareTagGen.setGenerationTargetPath(generationTargetPath + "emam/");
middlewareTagGen.setClusteringResults(clusteringResults); middlewareTagGen.setClusteringResults(clusteringResults);
files.addAll(middlewareTagGen.generate(componentInstanceSymbol,taggingResolver)); files.addAll(middlewareTagGen.generate(componentInstanceSymbol,taggingResolver));
File file = saveModel(componentInstanceSymbol);
files.add(file);
} }
files.addAll(completeGenerator.generate(componentInstanceSymbol, taggingResolver)); files.addAll(completeGenerator.generate(componentInstanceSymbol, taggingResolver));
...@@ -111,6 +118,17 @@ public class DistributedTargetGenerator{ ...@@ -111,6 +118,17 @@ public class DistributedTargetGenerator{
return files; return files;
} }
private File saveModel(EMAComponentInstanceSymbol componentInstance) throws IOException {
String name = componentInstance.getName().substring(0,1).toUpperCase() + componentInstance.getName().substring(1);
String pathname = generationTargetPath + "/emam/model/" + name + ".emam";
Log.info("Writing component into file: " + pathname, "files");
String modelData = SymbolPrinter.printEMAComponentInstanceAsEMAComponent(componentInstance);
File file = new File(pathname);
file.getParentFile().mkdirs();
FileUtils.write(file, modelData,"UTF-8");
return file;
}
private EMAComponentInstanceSymbol preprocessing(EMAComponentInstanceSymbol genComp) { private EMAComponentInstanceSymbol preprocessing(EMAComponentInstanceSymbol genComp) {
EMAComponentInstanceSymbol componentInstanceSymbol = genComp; EMAComponentInstanceSymbol componentInstanceSymbol = genComp;
if(clusteringParameters != null){ if(clusteringParameters != null){
......
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.lang.monticar.generator.middleware; package de.monticore.lang.monticar.generator.middleware;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol; import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol;
......
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.lang.monticar.generator.middleware; package de.monticore.lang.monticar.generator.middleware;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol; import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol;
......
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.lang.monticar.generator.middleware.cli; package de.monticore.lang.monticar.generator.middleware.cli;
import java.util.Optional; import java.util.Optional;
...@@ -55,5 +56,4 @@ public class CliParameters { ...@@ -55,5 +56,4 @@ public class CliParameters {
public Optional<ClusteringParameters> getClusteringParameters() { public Optional<ClusteringParameters> getClusteringParameters() {
return Optional.ofNullable(clusteringParameters); return Optional.ofNullable(clusteringParameters);
} }
} }
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.lang.monticar.generator.middleware.cli; package de.monticore.lang.monticar.generator.middleware.cli;
import com.google.gson.*; import com.google.gson.*;
......
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.lang.monticar.generator.middleware.cli; package de.monticore.lang.monticar.generator.middleware.cli;
import de.monticore.lang.monticar.clustering.cli.algorithms.AlgorithmCliParameters; import de.monticore.lang.monticar.clustering.cli.algorithms.AlgorithmCliParameters;
......
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.lang.monticar.generator.middleware.cli; package de.monticore.lang.monticar.generator.middleware.cli;
import com.google.gson.Gson; import com.google.gson.Gson;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol; import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstanceSymbol;
import de.monticore.lang.embeddedmontiarc.tagging.middleware.ros.RosToEmamTagSchema; import de.monticore.lang.embeddedmontiarc.tagging.middleware.ros.RosToEmamTagSchema;
import de.monticore.lang.embeddedmontiarc.tagging.middleware.mqtt.MqttToEmamTagSchema;
import de.monticore.lang.embeddedmontiarc.tagging.middleware.someip.SomeIPToEmamTagSchema; import de.monticore.lang.embeddedmontiarc.tagging.middleware.someip.SomeIPToEmamTagSchema;
import de.monticore.lang.monticar.emadl.generator.EMADLAbstractSymtab; import de.monticore.lang.monticar.emadl.generator.EMADLAbstractSymtab;
import de.monticore.lang.monticar.generator.middleware.DistributedTargetGenerator; import de.monticore.lang.monticar.generator.middleware.DistributedTargetGenerator;
import de.monticore.lang.monticar.generator.middleware.impls.CPPGenImpl;
import de.monticore.lang.monticar.generator.middleware.impls.EMADLGeneratorImpl;
import de.monticore.lang.monticar.generator.middleware.impls.ODVGenImpl;
import de.monticore.lang.monticar.generator.middleware.impls.RosCppGenImpl;
import de.monticore.lang.monticar.generator.middleware.impls.SomeIPGenImpl;
import de.monticore.lang.monticar.generator.middleware.impls.*; import de.monticore.lang.monticar.generator.middleware.impls.*;
import de.monticore.lang.monticar.generator.order.simulator.AbstractSymtab; import de.monticore.lang.monticar.generator.order.simulator.AbstractSymtab;
import de.monticore.lang.monticar.generator.roscpp.helper.TagHelper; import de.monticore.lang.monticar.generator.roscpp.helper.TagHelper;
import de.monticore.lang.monticar.generator.mqtt.helper.MqttTagHelper;
import de.monticore.lang.monticar.generator.someip.helper.SomeIPTagHelper; import de.monticore.lang.monticar.generator.someip.helper.SomeIPTagHelper;
import de.monticore.lang.tagging._symboltable.TaggingResolver; import de.monticore.lang.tagging._symboltable.TaggingResolver;
import de.se_rwth.commons.logging.Log; import de.se_rwth.commons.logging.Log;
...@@ -48,6 +46,7 @@ public final class DistributedTargetGeneratorCli { ...@@ -48,6 +46,7 @@ public final class DistributedTargetGeneratorCli {
public static final String GENERATOR_CPP = "cpp"; public static final String GENERATOR_CPP = "cpp";
public static final String GENERATOR_EMADL = "emadlcpp"; public static final String GENERATOR_EMADL = "emadlcpp";
public static final String GENERATOR_ROSCPP = "roscpp"; public static final String GENERATOR_ROSCPP = "roscpp";
public static final String GENERATOR_MQTT = "mqtt";
public static final String GENERATOR_ODV = "odv"; public static final String GENERATOR_ODV = "odv";
//ros2cpp is an alias for rclcpp //ros2cpp is an alias for rclcpp
public static final String GENERATOR_RCLCPP = "rclcpp"; public static final String GENERATOR_RCLCPP = "rclcpp";
...@@ -88,6 +87,7 @@ public final class DistributedTargetGeneratorCli { ...@@ -88,6 +87,7 @@ public final class DistributedTargetGeneratorCli {
res.add(GENERATOR_CPP); res.add(GENERATOR_CPP);
res.add(GENERATOR_EMADL); res.add(GENERATOR_EMADL);
res.add(GENERATOR_ROSCPP); res.add(GENERATOR_ROSCPP);
res.add(GENERATOR_MQTT);
res.add(GENERATOR_ODV); res.add(GENERATOR_ODV);
res.add(GENERATOR_ROS2CPP); res.add(GENERATOR_ROS2CPP);
res.add(GENERATOR_RCLCPP); res.add(GENERATOR_RCLCPP);
...@@ -171,6 +171,12 @@ public final class DistributedTargetGeneratorCli { ...@@ -171,6 +171,12 @@ public final class DistributedTargetGeneratorCli {
RosToEmamTagSchema.registerTagTypes(taggingResolver); RosToEmamTagSchema.registerTagTypes(taggingResolver);
TagHelper.resolveTags(taggingResolver, componentInstanceSymbol); TagHelper.resolveTags(taggingResolver, componentInstanceSymbol);
} }
if (generators.contains(GENERATOR_MQTT)) {
generator.add(new MqttGenImpl(), "mqtt");
MqttToEmamTagSchema.registerTagTypes(taggingResolver);
MqttTagHelper.resolveTags(taggingResolver, componentInstanceSymbol);
}
if (generators.contains(GENERATOR_RCLCPP) || generators.contains(GENERATOR_ROS2CPP)) { if (generators.contains(GENERATOR_RCLCPP) || generators.contains(GENERATOR_ROS2CPP)) {
generator.add(new RclCppGenImpl(), "rclcpp"); generator.add(new RclCppGenImpl(), "rclcpp");
......
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.lang.monticar.generator.middleware.cli; package de.monticore.lang.monticar.generator.middleware.cli;
public enum ResultChoosingStrategy { public enum ResultChoosingStrategy {
......
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.lang.monticar.generator.middleware.cli; package de.monticore.lang.monticar.generator.middleware.cli;
import com.google.gson.*; import com.google.gson.*;
......
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.lang.monticar.generator.middleware.helpers; package de.monticore.lang.monticar.generator.middleware.helpers;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.cncModel.EMAConnectorSymbol; import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.cncModel.EMAConnectorSymbol;
......
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.lang.monticar.generator.middleware.helpers; package de.monticore.lang.monticar.generator.middleware.helpers;
import de.monticore.lang.monticar.generator.FileContent; import de.monticore.lang.monticar.generator.FileContent;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment