Autors : Thomas DELAPART & Yazid BENJAMAA
Le projet est accessible sur le site : RoboML
Notre majeure est l'interpreteur et notre mineure est le compilateur.
Le projet RoboML est un projet de langage de programmation permettant de définir le déploiement d'un robot. Le langage permet de controler les déplacements du robot ainsi que de gérer des variables et des fonctions. Ce langage inspiré du Python est compilé en Arduino C.
classDiagram
class RoboMLProgram {
+functions: FunctionDec[]
}
class FunctionDec {
+functionName: string
+parameters: VariableFunDef[]
+body: Statement[]
+returnType: RMLObject
}
class Statement {
}
class Entry {
}
class UnitMeasure {
}
RoboMLProgram --> FunctionDec
FunctionDec --> Statement
Statement <|-- Assignement
Statement <|-- Condition
Statement <|-- Deplacement
Statement <|-- Loop
Statement <|-- Rotation
Statement <|-- SetSpeed
Statement <|-- SetRotation
Entry <|-- EntrySimple
Entry <|-- GetRotation
Entry <|-- GetSpeed
Entry <|-- FunctionCall
Entry <|-- VariableRef
Entry <|-- VariableDef
Entry <|-- Expression
Assignement <|-- Entry
Deplacement <|-- Entry
SetSpeed <|-- Entry
Rotation <|-- Entry
SetRotation <|-- Entry
Condition <|-- Entry
Loop <|-- Entry
Deplacement <|-- UnitMeasure
SetSpeed <|-- UnitMeasure
UnitMeasure <|-- UnitMeasure_cm
UnitMeasure <|-- UnitMeasure_dm
UnitMeasure <|-- UnitMeasure_m
UnitMeasure <|-- UnitMeasure_mm
-
RoboMLProgram : L'ensemble du programme RoboML, contenant les fonctions.
-
FunctionDec : Une fonction : un nom, des paramètres, un corps, un type retour.
- functionName : Chaîne représentant le nom de la fonction.
- parameters : Tableau de VariableFunDef pour les paramètres de la fonction.
- body : Tableau de Statement représentant le corps de la fonction.
- returnType : RMLObject représentant le type de retour de la fonction.
-
Statement : Classe de base pour différents types d'instructions.
- Assignement : L'affectation d'une valeur à une variable.
- Condition : Une condition logique avec des instructions if et else.
- Deplacement : Un mouvement : une distance, un type, une unité de mesure.
- Loop : Une boucle : une expression booléenne, un ensemble d'instructions.
- Rotation : Une rotation avec un angle et un sens de rotation.
- SetSpeed : Le réglage de la vitesse : une unité de mesure, une valeur.
- SetRotation : Le réglage de la rotation avec une valeur de variable.
-
Entry : Classe de base pour différents types d'entrées.
- EntrySimple : des entrées simples comme GetRotation, GetSpeed, etc.
- GetRotation : l'obtention de la rotation.
- GetSpeed : l'obtention de la vitesse.
- FunctionCall : un appel de fonction avec des arguments.
- VariableRef : Référence une variable existante.
- VariableDef : la définition d'une variable avec un nom, un type et une valeur.
- Expression : une expression avec des éléments et des opérateurs.
-
UnitMeasure : Classe de base pour différentes unités de mesure (cm, dm, m, mm).
- ArithmeticOperators : Opérateurs arithmétiques (+, -, *, **, %, ...)
- Assignement : Représente une assignation d'une valeur à une variable
- BooleanExpression : Représente une expression logique, avec des opérateurs logiques
- BooleanOperators : Opérateurs booléens supportés par le langage (==, <, >, <=, ...)
- Condition : Représente une condition logique, comme une comparaison.
- Entity : Représente une entité, avec un type spécifié.
- RMLObject : Objets propres au langage RoboML (RMLInt, RMLString, RMLFloat, RMLDouble, RMLBoolean)
- RoboMLProgram : Point d'entrée du langage, englobe l'ensemble du programme RoboML
- Sensor : Représente un capteur, avec la possibilité d'obtenir la valeur du capteur.
- Speed : Spécifie la vitesse de déplacement.
- Statement : Représente une instruction comme une assignation, une condition ou encore une boucle.
- UnitMeasure : Unité de mesure des distances (m, dm, cm, mm)
- Variable : Représente une variable identifiée par son nom et sa valeur associée
- VariableRef : Référence une variable existante dans le programme.
L'interpréteur du langage RoboML permet de lancer des simulations à travers une interface web. Ces simulations facilitent le processus de débogage et de détection des anomalies.
Le langage RoboML est supporté et traduis par l'interpréteur vers une suite d'instruction qui sont ensuite représentées sous forme d'animations représentatives de la trajectoire du robot. Ce support est géré par le biais de visiteurs (patron de conception) en Typescript qui facilient le parcours et les opérations sur les structures d'objets.
Le visiteur RoboMLVisitor
est défini dans visitor.ts
(/src/language/visitor.ts)
C'est le fichier main-browser.ts
(/src/language/visitor.ts) qui instancie le visiteur et qui effecture les parcours associés à chaque structure/concept.
Un exemple de visiteur simple est celui des noeuds correspondants à Condition
, le visiteur vérifie le résultat retourné par l'expression booléenne qu'il évalue avant de décider vers quel branchement rediriger le programme au moment de l'interprétation :
visitCondition(node: Condition) {
let condStatus:boolean = node.booleanExpression.accept(this);
if(condStatus) {
node.statementIf.forEach(statement => {
statement.accept(this);
});
} else {
node.statementElse.forEach(statement => {
statement.accept(this);
});
}
}
Le visiteur a permis également de faciliter l'implémentation de bon nombre de concepts du langage comme l'assignation de la valeur d'une variable à une autre variable, exemple :
let void main() {
var RMLInt val1 = 30 / 2 val1 = 6
var RMLInt val2 = val1 - 5 val2 = 1
var RMLInt val2 = val2 + 10 val2 = 11
var RMLInt val3 = val2 + val1 val3 = 17
}
Ces méthodes d'assignation sont gérées grâce au visiteur de Assignement
qui vérifie également qu'une variable a bien été déclarée avant d'être appellée :
visitAssignement(node: Assignement) {
const vToAssing = node.assignableVariable;
if (vToAssing !== undefined) {
if (this.variables.has(vToAssing)) {
this.variables.set(vToAssing, (node.entry as Entry).accept(this));
} else {
throw new Error(`Variable ${vToAssing} not found`);
}
}
}
Le compilateur du langage RoboML permet de générer un code Arduino C à partir d'un programme RoboML.
Pour ce second projet, nous utilisons aussi la classe visitor.ts
pour parcourir les noeuds du programme et appeller la méthode de compilerVisitor.ts
associée, qui va générer le code Arduino C et le concaténer dans une variable codeProgram
qui sera retournée à la fin de l'interprétation.
Le compileur ne s'utilise qu'en ligne de commande malheuresement, il prend en paramètre le chemin relatif vers le fichier RoboML à compiler depuis le dossier Robot_ML
et génère un fichier output.ino
dans le dossier output
du projet.
cd .\langium\Robot_ML\
node ./bin/cli.js compile .\files\test.rml
ou sinon le fichier compile.bat
permet de compiler le fichier test.rml
par défaut sur windows.
cd .\langium\Robot_ML\
.\compile.bat
Les 4 déplacements possible ont été implémentés dans le compilateur avec leur code Arduino C associé :
Forward
: AvancerOmni.setCarAdvance
Backward
: ReculerOmni.setCarBackoff
SideLeft
: Aller à gaucheOmni.setCarLeft
SideRight
: Aller à droiteOmni.setCarRight
Les 2 rotations possibles ont été implémentés dans le compilateur avec leur code Arduino C associé :
Clock
: Rotation horraireOmni.setCarRotateRight
AntiClock
: Rotation antihorraireOmni.setCarRotateLeft
Un delay et un stop sont ajoutés après chaque déplacement pour permettre au robot de s'arrêter et de ne pas enchaîner les déplacements trop rapidement.
Les unités de mesure des distances sont gérées dans le compilateur :
mm
: Millimètres ``cm
: Centimètres* 10
dm
: Décimètres* 100
m
: Mètres* 1000
- Les fonctions doivent être déclarées avant la fonction `main()`.
- Une variable doit être déclarée pour pouvoir être utilisée
- La logique du langage est "python-like", c à d toutes les variables ont un scope global par défaut.
Voici une liste d'exemples de codes RoboML illustrant les diverses fonctionnalités de notre langage. Ces exemples sont disponibles dans le dossier files
du projet dans les fichiers .rml
et .ino
associés.
let void triangle() {
var RMLInt sideLength = 100
var RMLInt rotationAngle = 120
var RMLInt count = 0
loop count < 3 {
Clock rotationAngle
Forward sideLength cm
Forward sideLength cm
Forward sideLength cm
count = count + 1
}
}
let void main() {
setSpeed(150 dm)
Clock 60
var RMLInt count = 0
loop count < 1 {
count = count + 1
triangle()
}
}
void triangle() {
int sideLength = 100;
int rotationAngle = 120;
int count = 0;
while (count < 3) {
Omni.setCarRotateRight(rotationAngle/ 180 * 3.1415926545 * global_rotation);
Omni.delayMS(3000);
Omni.setCarStop();
Omni.setCarAdvance(sideLength * 10 * global_speed);
Omni.delayMS(1000);
Omni.setCarStop();
Omni.setCarAdvance(sideLength * 10 * global_speed);
Omni.delayMS(1000);
Omni.setCarStop();
Omni.setCarAdvance(sideLength * 10 * global_speed);
Omni.delayMS(1000);
Omni.setCarStop();
count = count + 1;
}
}
void main() {
global_speed = 150;
Omni.setCarRotateRight(60/ 180 * 3.1415926545 * global_rotation);
Omni.delayMS(3000);
Omni.setCarStop();
int count = 0;
while (count < 1) {
count = count + 1;
triangle()
}
}
let void square() {
var RMLInt sideLength = 60
var RMLInt rotationAngle = 90
var RMLInt count = 0
loop count < 4 {
Clock rotationAngle
Backward sideLength cm
Backward sideLength cm
Backward sideLength cm
Backward sideLength cm
count = count + 1
}
}
let void main() {
setSpeed(250 dm)
var RMLInt count = 0
loop count < 1 {
count = count + 1
square()
}
}
void square() {
int sideLength = 60;
int rotationAngle = 90;
int count = 0;
while (count < 4) {
Omni.setCarRotateRight(rotationAngle/ 180 * 3.1415926545 * global_rotation);
Omni.delayMS(3000);
Omni.setCarStop();
Omni.setCarBackoff(sideLength * 10 * global_speed);
Omni.delayMS(1000);
Omni.setCarStop();
Omni.setCarBackoff(sideLength * 10 * global_speed);
Omni.delayMS(1000);
Omni.setCarStop();
Omni.setCarBackoff(sideLength * 10 * global_speed);
Omni.delayMS(1000);
Omni.setCarStop();
Omni.setCarBackoff(sideLength * 10 * global_speed);
Omni.delayMS(1000);
Omni.setCarStop();
count = count + 1;
}
}
void main() {
global_speed = 250;
int count = 0;
while (count < 1) {
count = count + 1;
square()
}
}
let void spiral() {
var RMLInt sideLength = 10
var RMLInt rotationAngle = 20
var RMLInt count = 0
loop count < 100 {
AntiClock rotationAngle
SideLeft sideLength mm
SideLeft sideLength mm
SideLeft sideLength mm
SideLeft sideLength mm
count = count + 1
sideLength = sideLength + 2
}
}
let void main() {
setSpeed(250 dm)
var RMLInt count = 0
loop count < 1 {
count = count + 1
spiral()
}
}
void spiral() {
int sideLength = 10;
int rotationAngle = 20;
int count = 0;
while (count < 100) {
Omni.setCarRotateLeft(rotationAngle/ 180 * 3.1415926545 * global_rotation);
Omni.delayMS(3000);
Omni.setCarStop();
Omni.setCarLeft(sideLength * global_speed);
Omni.delayMS(1000);
Omni.setCarStop();
Omni.setCarLeft(sideLength * global_speed);
Omni.delayMS(1000);
Omni.setCarStop();
Omni.setCarLeft(sideLength * global_speed);
Omni.delayMS(1000);
Omni.setCarStop();
Omni.setCarLeft(sideLength * global_speed);
Omni.delayMS(1000);
Omni.setCarStop();
count = count + 1;
sideLength = sideLength + 2;
}
}
void main() {
global_speed = 250;
int count = 0;
while (count < 1) {
count = count + 1;
spiral()
}
}
- Ajout du capteur de distance
- Ajout de la possibilité de lancer le compilateur depuis l'interface web
- Validation des programmes RoboML
- Interpréteur en ligne plus agréable visuellement