From e48351a6281a939ec58dff7f29318a7368a9482f Mon Sep 17 00:00:00 2001
From: "francois.grand" <francois.grand@irstea.fr>
Date: Fri, 1 Sep 2017 11:45:25 +0200
Subject: [PATCH] =?UTF-8?q?-=20extraction=20d'un=20composant=20g=C3=A9n?=
 =?UTF-8?q?=C3=A9rique=20de=20formulaire=20-=20d=C3=A9but=20de=20cr=C3=A9a?=
 =?UTF-8?q?tion=20de=20la=20calculette=20Lechapt-Calmon?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .gitignore                                    |   1 +
 src/app/app.component.ts                      |   3 +-
 src/app/app.module.ts                         |   3 +-
 .../generic/calculator.component.ts           | 401 ++++++++++++++++++
 .../lechapt-calmon/lechapt-calmon.config.json | 216 ++++++++++
 .../lechapt-calmon.config.json.jacase         | 237 +++++++++++
 .../lechapt-calmon/lechapt-calmon.en.json     |  10 +
 .../lechapt-calmon/lechapt-calmon.fr.json     |  20 +
 .../lechaptcalmon.component.html              |   7 +
 .../lechapt-calmon/lechaptcalmon.component.ts |  57 +++
 10 files changed, 953 insertions(+), 2 deletions(-)
 create mode 100644 src/app/calculators/generic/calculator.component.ts
 create mode 100644 src/app/calculators/lechapt-calmon/lechapt-calmon.config.json
 create mode 100644 src/app/calculators/lechapt-calmon/lechapt-calmon.config.json.jacase
 create mode 100644 src/app/calculators/lechapt-calmon/lechapt-calmon.en.json
 create mode 100644 src/app/calculators/lechapt-calmon/lechapt-calmon.fr.json
 create mode 100644 src/app/calculators/lechapt-calmon/lechaptcalmon.component.html
 create mode 100644 src/app/calculators/lechapt-calmon/lechaptcalmon.component.ts

diff --git a/.gitignore b/.gitignore
index b6ac63494..c96a46760 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
 /src/main.js
 /src/app/**/*.js
 /src/app/**/*.js.map
+/src/main.js*
 
 # dependencies
 /node_modules
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 80e6fb02c..152c3b5d8 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -16,8 +16,9 @@ import { AlertDialog } from './components/alert-dialog/alert-dialog.component';
     <option *ngFor="let l of intlService.languages" [value]=l.code>{{l.label}}</option>
   </select>
 
-  <cond-distri></cond-distri>
+  <lechapt-calmon></lechapt-calmon>
   <!--
+  <cond-distri></cond-distri>
   <select name="Language" (change)=selectLang($event)>
     <option *ngFor="let l of intlService.languages" [value]=l.code>{{l.label}}</option>
   </select>
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 3d228c88e..ae0292682 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -11,6 +11,7 @@ import { ParamInputComponent } from './components/param-input/param-input.compon
 import { FieldSetComponent } from './components/field-set/field-set.component';
 import { ParamFieldLineComponent } from './components/param-field-line/param-field-line.component';
 import { CondDistriComponent } from './calculators/cond_distri/conddistri.component';
+import { LechaptCalmonComponent } from './calculators/lechapt-calmon/lechaptcalmon.component';
 import { AlertDialog } from './components/alert-dialog/alert-dialog.component';
 import { AppErrorModule } from './error.module';
 import { CalculatorResultsComponent } from './components/calculator-results/calculator-results.component';
@@ -31,7 +32,7 @@ import { CalculatorResultsComponent } from './components/calculator-results/calc
     ParamInputComponent,
     FieldSetComponent,
     ParamFieldLineComponent,
-    CondDistriComponent,
+    CondDistriComponent, LechaptCalmonComponent,
     AlertDialog,
     CalculatorResultsComponent
   ],
diff --git a/src/app/calculators/generic/calculator.component.ts b/src/app/calculators/generic/calculator.component.ts
new file mode 100644
index 000000000..1065155c9
--- /dev/null
+++ b/src/app/calculators/generic/calculator.component.ts
@@ -0,0 +1,401 @@
+import { Component, OnInit, DoCheck, ViewChild } from '@angular/core';
+import { Response } from '@angular/http';
+import { Observable } from "rxjs/Observable";
+import 'rxjs/add/operator/toPromise';
+
+import { IParamsEquation, Nub } from "jalhyd";
+
+import { ParamService } from '../../services/param/param.service';
+import { HttpService } from '../../services/http/http.service';
+import { InternationalisationService } from '../../services/internationalisation/internationalisation.service';
+import { FieldSet } from '../../components/field-set/field-set.component';
+import { NgParameter, ParamRadioConfig } from '../../services/ngparam';
+import { CalculatorResultsComponent } from '../../components/calculator-results/calculator-results.component';
+
+/*
+@Component({
+    selector: 'cond-distri',
+    templateUrl: "./conddistri.component.html",
+    styles: [`
+    .button_compute {
+        height: 3em;
+        width: 30%;
+    }
+    `
+    ]
+})
+*/
+export abstract class GenericCalculatorComponent implements OnInit, DoCheck {
+    /**
+     * objet JSON chargé depuis le fichier de traduction
+     */
+    private _localisation = {};
+
+    /**
+     * objet JSON chargé depuis le fichier de configuration de la calculette
+     */
+    private _config = {};
+
+    private _fieldSets: FieldSet[] = [];
+
+    /**
+     * symbole du paramètre à calculer par défaut (cf config "idCal")
+     */
+    private _defaultCalculatedParam: string;
+
+    // /**
+    //  * composant d'affichage des résultats
+    //  */
+    // @ViewChild(CalculatorResultsComponent)
+    // private resultsComponent: CalculatorResultsComponent;
+
+    /**
+     * flag d'affichage du composant de résultats
+     */
+    private _showResults: boolean = false;
+
+    private paramService: ParamService;
+    private httpService: HttpService;
+    private intlService: InternationalisationService;
+
+    // constructor(private paramService: ParamService, private httpService: HttpService, private intlService: InternationalisationService) {
+    constructor(paramService: ParamService, httpService: HttpService, intlService: InternationalisationService) {
+        this.paramService = paramService;
+        this.httpService = httpService;
+        this.intlService = intlService;
+    }
+
+    private loadLocalisation(): Promise<string> {
+        let ths = this;
+        let processData = function (s: string) {
+            // fermeture nécessaire pour capturer la valeur de this (undefined sinon)
+            ths._localisation = JSON.parse(s);
+            ths.paramService.updateLocalisation(ths._localisation);
+        }
+
+        // let f: string = "app/calculators/cond_distri/cond_distri." + this.intlService.currentLanguage.tag + ".json"
+        let f: string = this.getConfigPathPrefix() + this.intlService.currentLanguage.tag + ".json"
+        let resp: Observable<Response> = this.httpService.httpGetRequestResponse(undefined, undefined, undefined, f);
+
+        let prom = resp.map(res => res.text()).toPromise();
+        prom.then((res) => {
+            processData(res);
+        })
+
+        return prom;
+    }
+
+    private logObject(obj: {}, m?: string) {
+        // évite le message "Value below was evaluated just now" dans le debugger de Chrome
+        if (m == undefined)
+            console.log(JSON.stringify(obj));
+        else
+            console.log(m + " " + JSON.stringify(obj));
+    }
+
+    private parseConfig() {
+        this._fieldSets = [];
+
+        for (let conf_index in this._config) {
+
+            let conf = this._config[conf_index];
+            let conf_id: string = conf["id"];
+
+            // field set
+            if (conf_id.startsWith("fs_")) {
+                let fieldSet: FieldSet = new FieldSet(conf_id);
+
+                let fields = conf["fields"];
+                for (let field_index in fields) {
+                    let field = fields[field_index];
+                    if (field["type"] === "input") {
+                        let input_id = field["id"];
+                        let param: NgParameter = this.paramService.getParameter(input_id);
+                        if (param != undefined) {
+                            param.unit = field["unit"];
+                            param.v = +field["value"];
+                            param.radioConfig = NgParameter.getRadioConfig(conf["option"]);
+                            param.radioState = ParamRadioConfig.FIX;
+                            param.isDefault = false; // malgré le fait qu'il soit initialisé dans la déclaration de la classe NgParam à false, quand on relit sa valeur, il vaut undefined (merci Microsoft)
+                            fieldSet.params.push(param);
+                        }
+                    } else if (field["type"] === "select") {
+
+                    }
+                }
+                if (fieldSet.params.length > 0) {
+                    this._fieldSets.push(fieldSet);
+                }
+            }
+            // options globales
+            else if (conf_id === "options") {
+                // id du paramètre à calculer par défaut
+                this._defaultCalculatedParam = conf["idCal"];
+                let p = this.getParamFromSymbol(this._defaultCalculatedParam);
+                p.isDefault = true;
+                p.radioState = ParamRadioConfig.CAL;
+            }
+        }
+    }
+
+    private getParamFromSymbol(symbol: string): NgParameter {
+        for (let fs of this._fieldSets) {
+            for (let p of fs.params) {
+                if (p.symbol === symbol)
+                    return p;
+            }
+        }
+        return undefined;
+    }
+
+    private getParamFromState(st: ParamRadioConfig): NgParameter {
+        for (let fs of this._fieldSets) {
+            for (let p of fs.params) {
+                if (p.radioState == st)
+                    return p;
+            }
+        }
+        return undefined;
+    }
+
+    /**
+     * chaine préfixe pour le chargement de la config et de l'internationalisation
+     */
+    protected abstract getConfigPathPrefix(): string;
+
+    private loadConfig() {
+        let ths = this;
+        let processData = function (s: string) {
+            // fermeture nécessaire pour capturer la valeur de this (undefined sinon)
+            ths._config = JSON.parse(s);
+            ths.parseConfig();
+        }
+
+        // let f: string = "app/calculators/cond_distri/cond_distri.config.json"
+        let f: string = this.getConfigPathPrefix() + "config.json"
+        this.httpService.httpGetRequest(undefined, undefined, undefined, f, processData);
+    }
+
+    ngOnInit() {
+        this.loadLocalisation();
+        this.loadConfig();
+        this.updateLanguage();
+    }
+
+    private getFieldSet(id: string) {
+        for (let fs of this._fieldSets) {
+            if (fs.id == id)
+                return fs;
+        }
+        return undefined;
+    }
+
+    private updateLanguage() {
+        for (let conf_index in this._config) {
+            let conf = this._config[conf_index];
+            let conf_id: string = conf["id"];
+
+            if (conf_id.startsWith("fs_")) {
+                let fieldSet: FieldSet = this.getFieldSet(conf_id);
+                if (fieldSet != undefined)
+                    fieldSet.title = this._localisation[conf_id];
+            }
+        }
+    }
+
+    ngDoCheck() {
+        // let q = this.getParamFromSymbol("Q");  // A VIRER !!!!
+        // if (q != undefined) {
+        //     q.radioState = ParamRadioConfig.VAR;
+        //     q.minValue = 1.5;
+        //     q.maxValue = 6;
+        //     q.stepValue = 0.3;
+        // }
+
+        if (this.intlService.localeChanged) {
+            const promise = this.loadLocalisation()
+                .then(() => {
+                    this.updateLanguage();
+                });
+        }
+    }
+
+    /*
+     * gestion des événements clic sur les radios :
+     * envoi d'un message au composant parent
+     * cf. https://angular.io/guide/component-interaction#parent-listens-for-child-event
+     */
+
+    private onRadioClick(info: string) {
+        this._showResults = false;
+
+        let tmp: string[] = info.split("_");
+        let symbol: string = tmp[0];
+
+        let sourceParam = this.getParamFromSymbol(symbol);
+        let oldState: ParamRadioConfig = sourceParam.radioState;
+        let newState: ParamRadioConfig = ParamRadioConfig[tmp[1].toUpperCase()];
+
+        switch (oldState) {
+            case ParamRadioConfig.FIX:
+                switch (newState) {
+                    case ParamRadioConfig.VAR:
+                        this.resetOther(sourceParam, ParamRadioConfig.CAL);
+                        break;
+
+                    case ParamRadioConfig.CAL:
+                        this.resetOther(sourceParam, ParamRadioConfig.VAR);
+                        break;
+                }
+                break;
+
+            case ParamRadioConfig.VAR:
+                switch (newState) {
+                    case ParamRadioConfig.CAL:
+                        this.resetOther(sourceParam, ParamRadioConfig.VAR);
+                        break;
+                }
+                break;
+
+            case ParamRadioConfig.CAL:
+                switch (newState) {
+                    case ParamRadioConfig.FIX:
+                        this.setDefault();
+                        break;
+
+                    case ParamRadioConfig.VAR:
+                        this.resetOther(sourceParam, ParamRadioConfig.CAL);
+                        this.setDefault();
+                        break;
+                }
+        }
+
+        sourceParam.radioState = newState;
+
+        // this.appRef.tick();
+        //        this.changeDetectorRef.detectChanges();  // provoque une détection des changements dans les contrôles
+    }
+
+    /**
+     * remet tous les paramètres à FIX sauf "me" et ceux (celui) à l'état "except"
+     */
+    private resetOther(me: NgParameter, except: ParamRadioConfig) {
+        // console.log("reset me=" + me.symbol + " sauf=" + ParamRadioConfig[except])
+        for (let fs of this._fieldSets) {
+            for (let p of fs.params) {
+                if (p != me && p.radioState != except && p.radioConfig != ParamRadioConfig.FIX) {
+                    // console.log("reset " + p.symbol + " st " + ParamRadioConfig[p.radioState] + " -> FIX");
+                    p.radioState = ParamRadioConfig.FIX;
+                }
+            }
+        }
+    }
+
+    /**
+     * met le paramètre par défaut à CAL
+     */
+    private setDefault() {
+        let defaultParamCal = this.getParamFromSymbol(this._defaultCalculatedParam);
+        // console.log("setdefault " + defaultParamCal.symbol + " -> CAL")
+        defaultParamCal.radioState = ParamRadioConfig.CAL;
+    }
+
+    protected getParameterValue(symbol: string): number {
+        for (let fs of this._fieldSets) {
+            for (let p of fs.params) {
+                if (p.symbol === symbol) {
+                    switch (p.radioState) {
+                        case ParamRadioConfig.FIX:
+                            return p.v;
+
+                        case ParamRadioConfig.VAR:
+                        case ParamRadioConfig.CAL:
+                            return undefined;
+                    }
+                }
+            }
+        }
+    }
+
+    private getComputedParameter(): NgParameter {
+        return this.getParamFromState(ParamRadioConfig.CAL);
+    }
+
+    private getVariatedParameter(): NgParameter {
+        return this.getParamFromState(ParamRadioConfig.VAR);
+    }
+
+    private addFixedResults(nDigits: number) {
+        for (let fs of this._fieldSets) {
+            for (let p of fs.params) {
+                if (p.radioState == ParamRadioConfig.FIX && p.symbol !== "Pr") {
+                    this.getCalculatorResultsComponent().addFixedResult(p, p.v, nDigits);
+                }
+            }
+        }
+    }
+
+    protected abstract getNubAndParameters(): [Nub, IParamsEquation];
+
+    protected abstract getCalculatorResultsComponent(): CalculatorResultsComponent;
+
+    private doCompute() {
+        // let Q: number = this.getParameterValue("Q"); // débit Q
+        // let D: number = this.getParameterValue("D"); // diamètre D
+        // let J: number = this.getParameterValue("J"); // perte de charge J
+        // let Lg: number = this.getParameterValue("Lg"); // Longueur de la conduite Lg
+        // let Nu: number = this.getParameterValue("Nu"); // Viscosité dynamique Nu
+        // let prms = new ConduiteDistribParams(Q, D, J, Lg, Nu);
+        // let nub = new ConduiteDistrib(prms);
+        let np: [Nub, IParamsEquation] = this.getNubAndParameters();
+        let nub: Nub = np[0];
+        let prms: IParamsEquation = np[1];
+
+        let prec: number = this.getParameterValue("Pr"); // précision
+        let nDigits = -Math.log10(prec);
+
+        this._showResults = false;
+        let computedParam = this.getComputedParameter();
+
+        let varParam = this.getVariatedParameter();
+        let resultsComponent: CalculatorResultsComponent = this.getCalculatorResultsComponent();
+        resultsComponent.reset(varParam == undefined);
+        if (varParam == undefined) {
+            // pas de paramètre à varier
+
+            let res = nub.Calc(computedParam.symbol, 0, prec).vCalc;
+
+            this.addFixedResults(nDigits);
+            resultsComponent.addFixedResult(computedParam, res, nDigits);
+        }
+        else {
+            // il y a un paramètre à varier
+
+            this.addFixedResults(nDigits);
+            resultsComponent.setVariableParamHeader(varParam);
+            resultsComponent.setVariableResultHeader(computedParam);
+
+            let min: number = +varParam.minValue;
+            let max: number = +varParam.maxValue;
+            let step: number = +varParam.stepValue;
+
+            for (let val = min; val <= max; val += step) {
+                prms[varParam.symbol].v = val;
+
+                let res = nub.Calc(computedParam.symbol, 0, prec).vCalc;
+                resultsComponent.addVarResult(val, res, nDigits);
+            }
+            // for (let vr of this._varResults) {
+            //     console.log(vr);
+            // }
+
+            resultsComponent.setGraphTitle(computedParam.symbol + " = f( " + varParam.symbol + " )");
+            resultsComponent.generateGraph();
+        }
+        this._showResults = true;
+    }
+
+    private getResultsStyleDisplay() {
+        return this._showResults ? "block" : "none";
+    }
+}
diff --git a/src/app/calculators/lechapt-calmon/lechapt-calmon.config.json b/src/app/calculators/lechapt-calmon/lechapt-calmon.config.json
new file mode 100644
index 000000000..6449fdb0b
--- /dev/null
+++ b/src/app/calculators/lechapt-calmon/lechapt-calmon.config.json
@@ -0,0 +1,216 @@
+[
+    {
+        "id": "fs_materiau",
+        "name": "Type du matériau",
+        "option": "var",
+        "fields": [
+            {
+                "id": "select_material",
+                "type": "select",
+                "name": "Choix du matériau",
+                "select": [
+                    {
+                        "id": 1,
+                        "parameters": [
+                            {
+                                "id": "L",
+                                "value": "1.863"
+                            },
+                            {
+                                "id": "M",
+                                "value": "2"
+                            },
+                            {
+                                "id": "N",
+                                "value": "5.33"
+                            }
+                        ]
+                    },
+                    {
+                        "id": 2,
+                        "parameters": [
+                            {
+                                "id": "L",
+                                "value": "1.601"
+                            },
+                            {
+                                "id": "M",
+                                "value": "1.975"
+                            },
+                            {
+                                "id": "N",
+                                "value": "5.25"
+                            }
+                        ]
+                    },
+                    {
+                        "id": 3,
+                        "parameters": [
+                            {
+                                "id": "L",
+                                "value": "1.40"
+                            },
+                            {
+                                "id": "M",
+                                "value": "1.96"
+                            },
+                            {
+                                "id": "N",
+                                "value": "5.19"
+                            }
+                        ]
+                    },
+                    {
+                        "id": 4,
+                        "parameters": [
+                            {
+                                "id": "L",
+                                "value": "1.16"
+                            },
+                            {
+                                "id": "M",
+                                "value": "1.93"
+                            },
+                            {
+                                "id": "N",
+                                "value": "5.11"
+                            }
+                        ]
+                    },
+                    {
+                        "id": 5,
+                        "parameters": [
+                            {
+                                "id": "L",
+                                "value": "1.1"
+                            },
+                            {
+                                "id": "M",
+                                "value": "1.89"
+                            },
+                            {
+                                "id": "N",
+                                "value": "5.01"
+                            }
+                        ]
+                    },
+                    {
+                        "id": 6,
+                        "parameters": [
+                            {
+                                "id": "L",
+                                "value": "1.049"
+                            },
+                            {
+                                "id": "M",
+                                "value": "1.86"
+                            },
+                            {
+                                "id": "N",
+                                "value": "4.93"
+                            }
+                        ]
+                    },
+                    {
+                        "id": 7,
+                        "parameters": [
+                            {
+                                "id": "L",
+                                "value": "1.01"
+                            },
+                            {
+                                "id": "M",
+                                "value": "1.84"
+                            },
+                            {
+                                "id": "N",
+                                "value": "4.88"
+                            }
+                        ]
+                    },
+                    {
+                        "id": 8,
+                        "parameters": [
+                            {
+                                "id": "L",
+                                "value": "0.916"
+                            },
+                            {
+                                "id": "M",
+                                "value": "1.78"
+                            },
+                            {
+                                "id": "N",
+                                "value": "4.78"
+                            }
+                        ]
+                    },
+                    {
+                        "id": 9,
+                        "parameters": [
+                            {
+                                "id": "L",
+                                "value": "0.971"
+                            },
+                            {
+                                "id": "M",
+                                "value": "1.81"
+                            },
+                            {
+                                "id": "N",
+                                "value": "4.81"
+                            }
+                        ]
+                    }
+                ]
+            }
+        ]
+    },
+    {
+        "id": "fs_hydraulique",
+        "name": "Caractéristiques hydrauliques",
+        "option": "cal",
+        "fields": [
+            {
+                "type": "input",
+                "id": "Q",
+                "unit": "m³/s",
+                "value": 3
+            },
+            {
+                "type": "input",
+                "id": "D",
+                "unit": "m",
+                "value": 1.2
+            },
+            {
+                "type": "input",
+                "id": "J",
+                "unit": "m",
+                "value": 0.6
+            },
+            {
+                "type": "input",
+                "id": "Lg",
+                "unit": "m",
+                "value": 100
+            }
+        ]
+    },
+    {
+        "id": "fs_param_calc",
+        "option": "fix",
+        "fields": [
+            {
+                "type": "input",
+                "id": "Pr",
+                "unit": "m",
+                "value": 0.001
+            }
+        ]
+    },
+    {
+        "id": "options",
+        "idCal": "J"
+    }
+]
\ No newline at end of file
diff --git a/src/app/calculators/lechapt-calmon/lechapt-calmon.config.json.jacase b/src/app/calculators/lechapt-calmon/lechapt-calmon.config.json.jacase
new file mode 100644
index 000000000..7331aa1b3
--- /dev/null
+++ b/src/app/calculators/lechapt-calmon/lechapt-calmon.config.json.jacase
@@ -0,0 +1,237 @@
+{
+    "saisies":[
+        {
+            "id":"fs_materiau",
+            "name":"Type du matériau",
+            "option":"var",
+            "fields":[{
+                "type":"select",
+                "name": "Choix du matériau",
+                "value": 1,
+                
+                "select":[
+                {
+                    "id": 1,
+                    "name":"Fonte ou acier non revêtus - Béton grossier (eau corrosive)",
+                    "parameters":[
+                        {
+                            "id":"L",
+                            "value":"1.863"
+                        },
+                        {
+                            "id":"M",
+                            "value":"2"
+                        },
+                        {
+                            "id":"N",
+                            "value":"5.33"
+                        }
+                    ]                    
+            },
+            {
+                    "id": 2,
+                    "name":"Fonte ou acier non revêtus - Béton grossier (eau peu corrosive)",
+                    "parameters":[
+                        {
+                            "id":"L",
+                            "value":"1.601"
+                        },
+                        {
+                            "id":"M",
+                            "value":"1.975"
+                        },
+                        {
+                            "id":"N",
+                            "value":"5.25"
+                        }
+                    ]                    
+            },
+            {
+                    "id": 3,    
+                    "name":"Fonte ou acier revêtement ciment",
+                    "parameters":[
+                        {
+                            "id":"L",
+                            "value":"1.40"
+                        },
+                        {
+                            "id":"M",
+                            "value":"1.96"
+                        },
+                        {
+                            "id":"N",
+                            "value":"5.19"
+                        }
+                    ]                    
+            },
+            {
+                    "id": 4,
+                    "name":"Fonte ou acier revêtement bitume - Béton centrifugé",
+                    "parameters":[
+                        {
+                            "id":"L",
+                            "value":"1.16"
+                        },
+                        {
+                            "id":"M",
+                            "value":"1.93"
+                        },
+                        {
+                            "id":"N",
+                            "value":"5.11"
+                        }
+                    ]                    
+            },
+            {
+                    "id": 5,
+                    "name":"Acier laminé - Béton lisse",
+                    "parameters":[
+                        {
+                            "id":"L",
+                            "value":"1.1"
+                        },
+                        {
+                            "id":"M",
+                            "value":"1.89"
+                        },
+                        {
+                            "id":"N",
+                            "value":"5.01"
+                        }
+                    ]                    
+            },
+            {
+                    "id": 6,
+                    "name":"Fonte ou acier revêtement centrifugé",
+                    "parameters":[
+                        {
+                            "id":"L",
+                            "value":"1.049"
+                        },
+                        {
+                            "id":"M",
+                            "value":"1.86"
+                        },
+                        {
+                            "id":"N",
+                            "value":"4.93"
+                        }
+                    ]                    
+                },
+            {
+                "id": 7,
+                "name":"PVC - Polyéthylène",
+                "parameters":[
+                    {
+                        "id":"L",
+                        "value":"1.01"
+                    },
+                    {
+                        "id":"M",
+                        "value":"1.84"
+                    },
+                    {
+                        "id":"N",
+                        "value":"4.88"
+                    }
+                ]                    
+            },
+            {
+                "id": 8,
+                "name":"Tuyau hydrauliquement lisse - 0.05 ≤ D ≤ 0.2",
+                "parameters":[
+                    {
+                        "id":"L",
+                        "value":"0.916"
+                    },
+                    {
+                        "id":"M",
+                        "value":"1.78"
+                    },
+                    {
+                        "id":"N",
+                        "value":"4.78"
+                    }
+                ]                    
+            },
+            {
+                "id": 9,
+                "name":"Tuyau hydrauliquement lisse - 0.25 ≤ D ≤ 1",
+                "parameters":[
+                    {
+                        "id":"L",
+                        "value":"0.971"
+                    },
+                    {
+                        "id":"M",
+                        "value":"1.81"
+                    },
+                    {
+                        "id":"N",
+                        "value":"4.81"
+                    }
+                ]                    
+            }
+            ]
+            }]
+        },
+
+        {
+            "id":"fs_hydraulique",
+            "name":"Caractéristiques hydrauliques",
+            "option":"cal",
+            "fields":[
+            {
+                "type":"input",
+                "id" : "Q",
+                "name":"Débit",
+                "unit":"m³/s",
+                "value":3
+            },
+            {
+                "type":"input",
+                "id" : "D", 
+                "name":"Diamètre du tuyau",
+                "unit":"m",
+                "value":1.2
+                
+            },
+            {
+                "type":"input",
+                "id" : "J",
+                "name":"Perte de charge",
+                "unit":"m",
+                "value":0.6
+
+            },
+            {
+                "type":"input",
+                "id" : "Lg",
+                "name":"Longueur du tuyau",
+                "unit":"m",           
+                "value":100
+
+            }       
+            ]
+        },
+
+        {
+                "id":"fs_param_calc",                
+                "name":"Paramètres de calcul",
+                "option":"fix",
+                "fields":[{
+                    "type":"input",
+                    "id" : "Pr",
+                    "name":"Précision de calcul",
+                    "unit":"m",            
+                    "value":0.001 
+                }]
+        },
+
+        {
+            "id":"options",      
+            "idCal" : "J"
+        }
+    ]
+    
+}
\ No newline at end of file
diff --git a/src/app/calculators/lechapt-calmon/lechapt-calmon.en.json b/src/app/calculators/lechapt-calmon/lechapt-calmon.en.json
new file mode 100644
index 000000000..372fff647
--- /dev/null
+++ b/src/app/calculators/lechapt-calmon/lechapt-calmon.en.json
@@ -0,0 +1,10 @@
+{
+    "fs_hydraulique": "Hydraulic features",
+    "Q": "Flow",
+    "D": "Pipe diameter",
+    "J": "Head drop",
+    "Lg": "Pipe length",
+    "Nu": "Dynamic (shear) viscosity",
+    "fs_param_calc": "Calculation parameters",
+    "Pr": "Display accuracy"
+}
\ No newline at end of file
diff --git a/src/app/calculators/lechapt-calmon/lechapt-calmon.fr.json b/src/app/calculators/lechapt-calmon/lechapt-calmon.fr.json
new file mode 100644
index 000000000..cf4844217
--- /dev/null
+++ b/src/app/calculators/lechapt-calmon/lechapt-calmon.fr.json
@@ -0,0 +1,20 @@
+{
+    "fs_materiau": "Type du matériau",
+    "select_material": "Choix du matériau",
+    "fs_hydraulique": "Caractéristiques hydrauliques",
+    "Q": "Débit",
+    "D": "Diamètre du tuyau",
+    "J": "Perte de charge",
+    "Lg": "Longueur du tuyau",
+    "fs_param_calc": "Paramètres de calcul",
+    "Pr": "Précision de calcul",
+    "1": "Fonte ou acier non revêtus - Béton grossier (eau corrosive)",
+    "2": "Fonte ou acier non revêtus - Béton grossier (eau peu corrosive)",
+    "3": "Fonte ou acier revêtement ciment",
+    "4": "Fonte ou acier revêtement bitume - Béton centrifugé",
+    "5": "Acier laminé - Béton lisse",
+    "6": "Fonte ou acier revêtement centrifugé",
+    "7": "PVC - Polyéthylène",
+    "8": "Tuyau hydrauliquement lisse - 0.05 ≤ D ≤ 0.2",
+    "9": "Tuyau hydrauliquement lisse - 0.25 ≤ D ≤ 1"
+}
\ No newline at end of file
diff --git a/src/app/calculators/lechapt-calmon/lechaptcalmon.component.html b/src/app/calculators/lechapt-calmon/lechaptcalmon.component.html
new file mode 100644
index 000000000..3a9b25a4b
--- /dev/null
+++ b/src/app/calculators/lechapt-calmon/lechaptcalmon.component.html
@@ -0,0 +1,7 @@
+<h1>Lechapt-Calmon</h1>
+<field-set *ngFor="let fs of _fieldSets" [fieldSet]=fs (onRadio)=onRadioClick($event)></field-set>
+
+<div style="text-align:center;">
+    <button type="button" class="button_compute" name="Calculer" (click)="doCompute()" i18n="@@hyd_compute">Calculer</button>
+</div>
+<calc-results [style.display]="getResultsStyleDisplay()"></calc-results>
\ No newline at end of file
diff --git a/src/app/calculators/lechapt-calmon/lechaptcalmon.component.ts b/src/app/calculators/lechapt-calmon/lechaptcalmon.component.ts
new file mode 100644
index 000000000..4576a4200
--- /dev/null
+++ b/src/app/calculators/lechapt-calmon/lechaptcalmon.component.ts
@@ -0,0 +1,57 @@
+import { Component, ViewChild } from '@angular/core';
+
+//import { ConduiteDistrib, ConduiteDistribParams } from "jalhyd";
+import { IParamsEquation, Nub, LechaptCalmon, LechaptCalmonParams } from "jalhyd";
+
+import { ParamService } from '../../services/param/param.service';
+import { HttpService } from '../../services/http/http.service';
+import { InternationalisationService } from '../../services/internationalisation/internationalisation.service';
+import { GenericCalculatorComponent } from '../generic/calculator.component';
+import { CalculatorResultsComponent } from '../../components/calculator-results/calculator-results.component';
+
+
+@Component({
+    selector: 'lechapt-calmon',
+    templateUrl: "./lechaptcalmon.component.html",
+    styles: [`
+    .button_compute {
+        height: 3em;
+        width: 30%;
+    }
+    `
+    ]
+})
+export class LechaptCalmonComponent extends GenericCalculatorComponent {
+    /**
+     * composant d'affichage des résultats
+     */
+    @ViewChild(CalculatorResultsComponent)
+    private resultsComponent: CalculatorResultsComponent;
+
+    constructor(private paramSrv: ParamService, private httpSrv: HttpService, private intlSrv: InternationalisationService) {
+        super(paramSrv, httpSrv, intlSrv);
+    }
+
+    protected getConfigPathPrefix(): string {
+        return "app/calculators/lechapt-calmon/lechapt-calmon."
+    }
+
+    protected getNubAndParameters(): [Nub, IParamsEquation] {
+        let Q: number = this.getParameterValue("Q"); // débit Q
+        let D: number = this.getParameterValue("D"); // diamètre D
+        let J: number = this.getParameterValue("J"); // perte de charge J
+        let Lg: number = this.getParameterValue("Lg"); // Longueur de la conduite Lg
+        let L: number = this.getParameterValue("L"); // paramètre de matériau 1
+        let M: number = this.getParameterValue("M"); // paramètre de matériau 2
+        let N: number = this.getParameterValue("N"); // paramètre de matériau 3
+
+        let prms = new LechaptCalmonParams(Q, D, J, Lg, L, M, N);
+        let nub = new LechaptCalmon(prms);
+
+        return [nub, prms];
+    }
+
+    protected getCalculatorResultsComponent(): CalculatorResultsComponent {
+        return this.resultsComponent;
+    }
+}
-- 
GitLab