Commit c31b81c8 authored by Rabotin Michael's avatar Rabotin Michael
Browse files

Added irrigation and gateway code from Theo Labrosse

parent f5fdd223
1 merge request!1Irrigation
Showing with 24283 additions and 0 deletions
+24283 -0
# Default ignored files
/shelf/
/workspace.xml
.idea/.name 0 → 100644
hrudelin_parms_J2000.py
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
</module>
\ No newline at end of file
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="N803" />
<option value="N802" />
<option value="N806" />
</list>
</option>
<option name="ignoredBaseClasses">
<list>
<option value="unittest.TestCase" />
<option value="unittest.case.TestCase" />
<option value="pythonGate.Argument.Argument" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredIdentifiers">
<list>
<option value="set.__getitem__" />
<option value="bytes.encode" />
<option value="dict.__add__" />
</list>
</option>
</inspection_tool>
</profile>
</component>
\ No newline at end of file
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8" project-jdk-type="Python SDK" />
<component name="PythonCompatibilityInspectionAdvertiser">
<option name="version" value="3" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/hru-delin-master.iml" filepath="$PROJECT_DIR$/.idea/hru-delin-master.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RGraphicsSettings">
<option name="height" value="450" />
<option name="resolution" value="75" />
<option name="version" value="2" />
<option name="width" value="800" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RSettings">
<option name="interpreterPath" value="/usr/bin/R" />
</component>
</project>
\ No newline at end of file
# 1- Utilisation de la passerelle
## 1-2- La passerelle (Gateway)
La passerelle utilise le protocole de communication RPC (Remote Procedure Call), dans notre cas, cela signifie que l'on va appeler un programme depuis un autre en utilisant des appels de commandes.
Pour l'utilisateur de la passerelle, cela est rendu abstrait grâce à une série de librairies écrite dans différents langages.
Le but de cette passerelle étant de faciliter la communication d'un programme A avec un programme B, sachant que A et B peuvent être dans deux langages de programmation différent.
Pour cela, l'utilisateur va utiliser une librairie qui sera écrite dans le même langage que son programme. Que son but soit d'écrire le programme appelant ou appelé ne change rien à l'ajout de la librairie dans son projet, cependant cela changera ce qu'il va faire avec et comment il va l'utiliser.
Les librairies de la passerelle servent ainsi d’interface entre la passerelle en elle-même et les programmes ciblent, rendant la programmation de la communication entre les programmes similaires à des appels d’API.
## 1-2- Sender (programme A)
Cette section est là pour vous aider lorsque vous coderez le programme du sender - soit le programme qui va lancer un second programme.
### 1-2-1- Ce qui est possible
Le programme sender est celui qui va demander à la passerelle de lancer le second programme.
Vous pourrez utiliser la classe SenderStub pour préparer les données à envoyer au second programme, préparer un fichier de sorties pour de possible outputs et bien sûr, vous pouvez lancer le second programme avec la fonction run.
### 1-2-2- Ce que vous voudrez faire
Après avoir importé la librairie dans votre projet, l'une des premières choses à faire est d'instancier la classe SenderStub, c'est à partir d'elle que vous ferez toutes vos opérations par rapport à l'exécution du second programme.
Pour l'instancier, vous aurez besoin d'un ExecFile (qui représente le programme à exécuter) et un OutputFile (fichier qui contiendra de possible sorties de la part de la passerelle et de l'ExecFile)
Vous pouvez ajouter ces fichiers plus tard au SenderStub, mais je conseil de le faire à l'initialisation pour ne pas l'oublier plus tard.
Vous pouvez aussi ne pas renseigner de fichier d'output si vous n'en avez pas l'utilité.
Une fois, cela fait, vous pourrez ajouter une série de valeurs que vous voulez faire passer au second programme.
Par exemple, si le second programme a besoin d’un fichier pour fonctionner, c'est le moment de faire passer le chemin vers ce fichier.
Pour cela, on va faire passer une série d'Argument, sous forme de Parameter et de groupement de Parameter : les Dictionary.
Un Dictionary peut contenir plusieurs Parameter, mais aussi plusieurs Dictionary. Un Parameter représente la variable à faire passer directement, avec un nom et une valeur.
Vous voudrez donc créer un premier Dictionary de valeur, en l'instanciant avec un nom. Puis le remplir d'Argument.
Soit avec addArgument() (qui permet donc d'ajouter soit un Dictionary, soit un Parameter) soit avec addParameter() qui va directement ajouter un paramètre au Dictionary.
Finalement, vous pouvez utiliser la fonction run() de SenderStub, en passant en paramètre le chemin de l'exécutable de la passerelle.
Cela aura pour effet de lancer la passerelle, qui va ensuite lancer votre programme.
Via la sortie standard (le terminal/console normalement) vous aurez toutes les sorties normales (un print ou un cout auras le même effet que d'habitude, même si utilisé dans le programme lancé par la passerelle par exemple). De même que pour les sorties d'erreur.
## 1-3- Receiver (programme B)
Cette section est là pour vous aider lorsque vous coderez le programme du receiver - soit le programme qui se fait lancer par un premier programme.
### 1-3-1- Ce qui est possible
Vous pouvez tout à fait ne rien faire du tout. Le programme sera lancé normalement comme si vous l'aviez lancé vous-même à la main.
En revanche, si vous voulez récupérer des données depuis la passerelle ou envoyer des résultats (outputs) au premier programme, vous devrez écrire quelques lignes de code.
### 1-3-2- Ce que vous voudrez faire
Si vous ne voulez pas / n'avez pas besoin de récupérer de données depuis la passerelle, vous pouvez tout à fait ne rien faire du tout et ne pas changer votre programme.
En revanche, si vous voulez interagir avec la passerelle, la première chose à faire après avoir importé la librairie dans votre projet sera d'instancier ReceiverStub.
À partir de ReceiverStub, vous pouvez utiliser les différentes fonctions d'affichage (aussi disponible dans SenderStub) pour voir les données auxquelles vous avez accès.
Vous pouvez ensuite utiliser les fonctions telles que findDictionaryWithName ou findArgumentWithName ou plus directement getArgument (qui fonctionne aussi sur les Dictionary que vous récupérerez) pour récupérer les variables et autres valeurs qui viennent du programme sender.
Quand vous aurez récupéré ce que vous voulez, vous pourrez les utiliser facilement dans le reste de votre code.
Finalement, une dernière possibilité que vous offre ReceiverStub est d'utiliser un outputFile et d'envoyer des données de sortie (seulement s'il a été prédéfini par le sender) de cette manière, vous pouvez passer des dictionnaires au fichier de sortie pour être récupéré facilement par le programme sender.
## 2- Aide à l’Installation
Pour utiliser la passerelle, vous devez télécharger les librairies et la passerelle C++, vous devrez ensuite importer les librairies nécessaires à vos projets, et compilez le code C++ pour pouvoir l’utiliser.
Pour cela, rendez-vous sur le lien gitlab ci-dessous et clonez ou téléchargez le zip du projet (qu’il faudra donc dézipper)
https://gitlab.com/theolabt/gateway
Dans ce projet, les deux dossiers qui vous intéressent seront :
- gateway : qui contient la passerelle en C++
- libraries : qui contiens les différentes librairies à intégrer ou non à vos projets
## 2-1- Compiler la passerelle
Pour compiler la passerelle, vous aurez besoin de g++ d’installer.
Pour vérifier si c’est le cas, ouvrez un terminal (PowerShell sous Windows par exemple) et entrez :
`g++ --version`
Si en retour vous obtenez un message vous disant que la commande n’a pas été trouvé, c’est que vous devrez installer g++.
Si g++ a déjà été installer, mais que vous avez quand même le message d’erreur, il est possible que vous deviez ajouter le chemin d’installation dans votre PATH.
Si vous avez bien g++ d’installé, rendez-vous dans le dossier gateway qui contient la passerelle en c++ et ouvrez un terminal.
Pour compiler, tapez :
`g++ main.cpp GateStub/*.cpp -o build/gate.exe`
Si cela fonctionne, vous n’aurez pas de message d’erreur dans le terminal et vous trouverez un nouveau fichier gate.exe dans le dossier build.
C’est le chemin vers ce fichier gate.exe que vous devrez passer au SenderStub quand vous voudrez utiliser la fonction run() qui permet d’appeler votre second programme.
## 2-2- Importer la librairie au projet
Les librairies se situent dans le dossier libraries.
Vous aurez besoin de récupérer le dossier qui a le nom du langage que vous utilisez + Gate.
Par exemple pour python, il s’agira de pythonGate.
Ensuite, les manières d’importer la librairie dans votre projet dépendent du langage que vous utilisez.
### Python
Récupérer le package pythonGate qui se trouve dans le dossier libraries du projet gateway et copiez collez le à la racine du projet sur lequel vous travaillez.
Pour ensuite utilisez la librairie pythonGate dans votre programme, vous pouvez écrire
```python
import pythonGate as pyGate
sndStub = pyGate.SenderStub()
```
Vous pouvez désormais utilisez la librairie pythonGate dans python pour communiquer avec la passerelle.
### R
Récupérer le dossier Rgate qui se trouve dans le dossier libraries du projet gateway et copiez collez le au même niveau que le programme sur lequel vous travaillez.
changer le nom du dossier en `lib` (ou mettez le contenue dans un autres dossier lib)
```R
source("lib/rgate.R")
sndStub = SenderStub$new()
```
Vous devriez désormais pouvoir utilisez la librairie rGate dans R pour communiquer avec la passerelle.
/!\ les classes en R utilisent R6, les arguments et fonctions sont donc accessible avec le symbole $
### C++
Récupérer le dossier cppGate qui se trouve dans le dossier libraries du projet gateway et placez le dans votre projet.
Vous devrez simplement récupérer le chemin relatif vers le fichier ReceiverStub.h ou SenderStub.h en fonction de votre programme.
```cpp
#include "../lib/cppGate/SenderStub.h" // par exemple
SenderStub* sndStub = new SenderStub()
```
Vous devriez désormais pouvoir utilisez la librairie rGate dans R pour communiquer avec la passerelle.
/!\ les objets sont ici géré avec des pointeurs.
# 3- Documentation
## 3-1- Diagrammes de classe et explications
![](./image/Image1.png)
Ce diagramme de classe représente l’architecture objet utilisé dans chaque librairie, mais aussi dans la passerelle.
On y retrouve toutes les classes utilisées lors du fonctionnement de la passerelle, sauf pour les stubs que l’on verra plus tard.
Commençons par parler des classes File.
La classe File en elle-même est abstraite (et ne peut donc pas être instanciée) mais elle sert de modèle pour ses classes filles : ExecFile et OutputFile qui ont donc aussi les attributs name et path.
Les classes Argument sont organisées suivant le design pattern Composite.
Les classes Dictionary et Parameter héritent d’Argument. Dictionary contient un tableau d’Argument.
Un Dictionary peut donc contenir une série de Paramater, mais aussi une série de Dictionary (qui eux même pourront contenir d’autres Dictionary et d’autres Parameter).
Toutes les classes héritent de l’interface Serializable qui pousse les classes à définir une fonction serialize(), le but étant de pouvoir récupérer les informations de toutes les classes sous un format facilement transportable (ici, un string json). Serializable sert donc à simplifier l’utilisation de cette implémentation. En tant qu’utilisateurs, vous n’aurez pas besoin de vous servir de cette classe ni des fonctions serialize(), mais savoir qu’elle existe peut vous servir si vous cherchez à comprendre comment la passerelle fonctionne.
![](./image/Image2.png)
Ce second diagramme de classe se concentre sur les stubs.
Les stubs sont des classes qui servent d’interfaces. Ce sont elles que l’utilisateur va directement manipuler pour communiquer avec la passerelle.
Il y a 3 types de stubs :
- SenderStub, qui est utilisé par le programme qui appelle la passerelle
- ReceiverStub, qui est utilisé par le programme qui se fait appeler
- GateStub, qui est l’interface du côté de la passerelle et qu’en tant qu’utilisateurs, vous ne manipulerez pas.
Ce diagramme montre que chaque stub contient une liste de DataFile et de Dictionary pour faire passer les données et un OutputFile (même si c’est possible de ne pas en renseigner).
De plus, la SenderStub et la GateStub contiennent un ExecFile qu’il faut renseigner dans le SenderStub, sans quoi la passerelle ne pourra pas exécuter le second programme.
## 3-2- Classes et fonctions
Dans cette section, vous trouverez une description de toutes les classes et de leurs fonctions.
### 3-2-1- File
File est une classe abstraite qui gère les données générales des fichiers
Attributs :
name - string
file.txt
Name est le nom du fichier, donnée avec son extension
path - string
/home/files/
Path est le chemin absolu vers le fichier
### 3-2-2- ExecFile
Cette classe sert à trouver le programme qui va être appelé par la passerelle
Attributs :
cmd - string
python3
Cmd est la commande qui sera utilisée pour appeler le programme, dans le cas de python ça peut être python3 ou python par exemple.
### 3-2-1- OutputFile
OutputFile est utilisé pour accéder facilement à un fichier qui contiendra les sorties des résultats de la passerelle.
L'utilisateur peut également ajouter ses propres sorties (comme les données qui seront réutilisées dans le premier programme). Si vous ne souhaitez pas utiliser cette fonctionnalité, il suffit de ne pas remplir le champ OutputFile dans le SenderStub.
### 3-2-4- Argument
Argument est la classe principale pour les informations qui sont passées du programme A au programme B. Cette classe est abstraite, elle ne peut être instanciée que via les classes Parameter et Dictionary.
Attributs:
name - string
Values name
Donne un nom à l’attribut. Il permet de représenter la valeur, mais aussi de le retrouver plus tard.
### 3-2-5- Parameter
La classe Parameter est la plus basique pour enregistrer et transporter les informations sous un format nom – valeur
Attributs :
value - string
value
Représente la valeur qui est passée dans la passerelle. Elle est enregistrée en string pour faciliter son transport et sa sérialisation, mais elle peut ensuite être parsé pour redevenir dans le type souhaité.
Fonctions :
string display ()
Affiche le contenu du Parameter et retourne le string de l’affichage
Argument getArgument (name - string)
Vérifie que le paramètre a le bon nom et le retourne si oui, sinon retourne null.
Existe pour fonctionner avec la fonction récursive getArgument de Dictionary.
int getValueAsInt ()
Parse la value enregistré en string dans Parameter et le retourne en int.
float getValueAsFloat ()
Parse la value enregistré en string dans Parameter et le retourne en float.
list getValueAsList ()
Parse la value enregistré en string dans Parameter et le retourne en list.
### 3-2-6- Dictionary
Le dictionnaire est également utilisé pour stocker des données, mais d'une manière plus générale puisqu'il stocke des arguments au lieu de stocker directement des paramètres.
Attributs :
value – list of Argument
list of Parameter and Dictionary
Représente la valeur qui est passée dans la passerelle. Elle est enregistrée en string pour faciliter son transport et sa sérialisation, mais elle peut ensuite être parser pour redevenir dans le type souhaité.
Fonctions :
string display ()
Affiche le contenu du Dictionary et retourne le string de l’affichage
Argument getArgument (name - string)
Cherche récursivement dans la liste du Dictionary pour trouver un Argument (un autre Dictionary ou un Parameter) avec le nom recherché. S’il y a deux Argument avec le même nom, seul le premier trouvé sera retourné.
Argument addParameter (name - string, value - Any)
Récupère les argument name et value pour créer un Parameter et l’ajouter à la liste du Dictionary.
Argument addArgument (argument - Argument)
Prends un Argument (Parameter ou Dictionary) en paramètre et l’ajoute à la liste d’Argument du Dictionary.
//
// Created by tlabrosse on july 2022.
// licence : GNU lgpl
// you can contact me at : theo.labt@gmail.com
//
#include <stdio.h>
#include "ExecFile.h"
#ifdef _WIN32
#define OS 1
#endif
#ifdef linux
#define OS 2
#endif
namespace gateway {
ExecFile::ExecFile(const std::string &path, const std::string &name, OutputFile *output, const std::string &cmd)
: File(path, name), output(output), cmd(cmd) {}
ExecFile::ExecFile(const std::string &path, const std::string &name, OutputFile *output, const std::string &cmd,
const std::string &cmdLinux)
: File(path, name), output(output), cmd(cmd), cmdLinux(cmdLinux) {}
std::string ExecFile::run(const std::string& jsonLine) const {
std::string cmd;
if(OS == 1 || this->cmdLinux.empty())
cmd = this->cmd;
else
cmd = this->cmdLinux;
std::string command = cmd + " " + this->getPath() + this->getName() + " " + jsonLine;
FILE *file = popen(command.c_str(), "r");
char buffer[100];
std::string stringBuff;
if (file == nullptr) perror ("Error opening file");
else {
while ( !feof(file) ) {
if ( fgets (buffer , 100 , file) == nullptr ) break;
stringBuff += buffer;
}
fclose (file);
}
return stringBuff;
}
} // gateway
\ No newline at end of file
//
// Created by tlabrosse on july 2022.
// licence : GNU lgpl
// you can contact me at : theo.labt@gmail.com
//
#ifndef GATEWAY_EXECFILE_H
#define GATEWAY_EXECFILE_H
#include "OutputFile.h"
namespace gateway {
class ExecFile: public File {
private:
OutputFile *output;
std::string cmd;
std::string cmdLinux = "";
public:
ExecFile(const std::string &path, const std::string &name, OutputFile *output, const std::string &cmd);
ExecFile(const std::string &path, const std::string &name, OutputFile *output, const std::string &cmd, const std::string &cmdLinux);
std::string run(const std::string& jsonLine) const;
};
} // gateway
#endif //GATEWAY_EXECFILE_H
//
// Created by tlabrosse on july 2022.
// licence : GNU lgpl
// you can contact me at : theo.labt@gmail.com
//
#include "File.h"
namespace gateway {
File::File(const std::string &path, const std::string &name) : path(path), name(name) {}
File::~File() {}
const std::string &File::getPath() const {
return path;
}
const std::string &File::getName() const {
return name;
}
} // gateway
\ No newline at end of file
//
// Created by tlabrosse on july 2022.
// licence : GNU lgpl
// you can contact me at : theo.labt@gmail.com
//
#ifndef GATEWAY_FILE_H
#define GATEWAY_FILE_H
#include <string>
namespace gateway {
class File {
protected:
std::string path;
std::string name;
public:
File(const std::string &path, const std::string &name);
virtual ~File() =0;
const std::string &getPath() const;
const std::string &getName() const;
};
} // gateway
#endif //GATEWAY_FILE_H
//
// Created by tlabrosse on july 2022.
// licence : GNU lgpl
// you can contact me at : theo.labt@gmail.com
//
#include <iostream>
#include "GateStub.h"
using json = nlohmann::json;
namespace gateway {
//private:
int GateStub::fillFromJson(const std::string& jsonLine) {
auto data = json::parse(jsonLine);
auto outputFile = data["OutputFile"];
auto execFile = data["ExecFile"];
auto dataFiles = data["DataFiles"];
this->output = new OutputFile(outputFile["path"], outputFile["name"]);
this->execFile = new ExecFile(execFile["path"], execFile["name"], this->output, execFile["cmd"],
execFile["cmdAlt"]);
return 0;
}
//public:
GateStub::GateStub(const std::string &jsonLine) {
fillFromJson(jsonLine);
this->jsonLine = jsonLine;
output->initialize();
}
ExecFile *GateStub::getExecFile() const {
return execFile;
}
OutputFile *GateStub::getOutput() const {
return output;
}
const std::string &GateStub::getJsonLine() const {
return jsonLine;
}
} // gateway
\ No newline at end of file
//
// Created by tlabrosse on july 2022.
// licence : GNU lgpl
// you can contact me at : theo.labt@gmail.com
//
#ifndef GATEWAY_GATESTUB_H
#define GATEWAY_GATESTUB_H
#include "../lib/json.hpp"
#include "ExecFile.h"
namespace gateway {
class GateStub {
private:
ExecFile *execFile;
OutputFile *output;
std::string jsonLine;
int fillFromJson(const std::string& jsonLine);
public:
GateStub(const std::string& jsonLine);
ExecFile *getExecFile() const;
OutputFile *getOutput() const;
const std::string &getJsonLine() const;
};
} // gateway
#endif //GATEWAY_GATESTUB_H
//
// Created by tlabrosse on july 2022.
// licence : GNU lgpl
// you can contact me at : theo.labt@gmail.com
//
#include <fstream>
#include "OutputFile.h"
#include "../lib/json.hpp"
using json = nlohmann::json;
namespace gateway {
//private
std::string OutputFile::read() {
if(this->name != "" && this->path != "") {
std::ifstream file (this->path + this->name);
if(!file.is_open()) {
return "{}";
}
std::string line, output = "";
while ( getline (file,line) )
{
output += line;
}
file.close();
return output;
}
return "{}";
}
//public
OutputFile::OutputFile(const std::string &path, const std::string &name) : File(path, name) {}
int OutputFile::initialize() const {
if(this->name != "" && this->path != "") {
std::ofstream file(this->path + this->name);
file << R"( {"Outputs":[]})" << std::endl;
file.close();
return 0;
}
return -1;
}
} // gateway
\ No newline at end of file
//
// Created by tlabrosse on july 2022.
// licence : GNU lgpl
// you can contact me at : theo.labt@gmail.com
//
#ifndef GATEWAY_OUTPUTFILE_H
#define GATEWAY_OUTPUTFILE_H
#include "File.h"
namespace gateway {
class OutputFile: public File {
private:
std::string read();
public:
OutputFile(const std::string &path, const std::string &name);
int initialize() const;
};
} // gateway
#endif //GATEWAY_OUTPUTFILE_H
This diff is collapsed.
Supports Markdown
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