From c93f88b87db4dfc32360cff03bbcc71cdd7f6694 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Mon, 29 Apr 2019 12:26:56 +0200
Subject: [PATCH 01/44] update jalhyd_branch

---
 jalhyd_branch | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/jalhyd_branch b/jalhyd_branch
index c20acdd3f..507bddeeb 100644
--- a/jalhyd_branch
+++ b/jalhyd_branch
@@ -1,2 +1,2 @@
-refactor-nubs
+33-ajout-du-calcul-d-une-passe-a-bassin
 
-- 
GitLab


From 95cc384c73698307002bb1cee7b3fec4e6c142c1 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Mon, 29 Apr 2019 14:26:01 +0200
Subject: [PATCH 02/44] Fix jalhyd#86

---
 src/app/calculators/cloisons/cloisons.config.json | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/src/app/calculators/cloisons/cloisons.config.json b/src/app/calculators/cloisons/cloisons.config.json
index cb9fff4b6..59e6a9510 100644
--- a/src/app/calculators/cloisons/cloisons.config.json
+++ b/src/app/calculators/cloisons/cloisons.config.json
@@ -165,18 +165,6 @@
                         "refvalue": "select_loidebit1_kivi"
                     }
                 ]
-            },
-            {
-                "type": "input",
-                "id": "ZRAM",
-                "unit": "",
-                "nodeType": "StructureRectangle",
-                "dep_exist": [
-                    {
-                        "refid": "select_loidebit1",
-                        "refvalue": "select_loidebit1_kivi"
-                    }
-                ]
             }
         ]
     },
-- 
GitLab


From b9dfd2ba76bd530291b42f9379a09c2e33ae2092 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Mon, 29 Apr 2019 14:26:15 +0200
Subject: [PATCH 03/44] Ajout traduction

---
 src/locale/messages.en.json | 1 +
 src/locale/messages.fr.json | 1 +
 2 files changed, 2 insertions(+)

diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index bbfb5899c..eba7d7d16 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -110,6 +110,7 @@
     "INFO_EXTRARES_LIB_YF": "Subcritical depth (m)",
     "INFO_EXTRARES_LIB_YN": "Normal depth (m)",
     "INFO_EXTRARES_LIB_YT": "Supercritical depth (m)",
+    "INFO_EXTRARES_LIB_ZRB": "Downstream basin bottom elevation (m)",
     "INFO_EXTRARES_LIB_ZF2": "Downstream bottom elevation (m)",
     "INFO_LECHAPTCALMON_TITRE": "Lechapt-Calmon",
     "INFO_LECHAPTCALMON_TITRE_COURT": "Lechapt-C.",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index 819433f19..377791704 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -110,6 +110,7 @@
     "INFO_EXTRARES_LIB_YF": "Tirant d'eau fluvial (m)",
     "INFO_EXTRARES_LIB_YN": "Tirant d'eau normal (m)",
     "INFO_EXTRARES_LIB_YT": "Tirant d'eau torrentiel (m)",
+    "INFO_EXTRARES_LIB_ZRB": "Cote de radier du bassin aval (m)",
     "INFO_EXTRARES_LIB_ZF2": "Cote de fond aval (m)",
     "INFO_LECHAPTCALMON_TITRE": "Lechapt-Calmon",
     "INFO_LECHAPTCALMON_TITRE_COURT": "Lechapt-C.",
-- 
GitLab


From 4a8ed8bc65451d7d911307430e722b45bf13e146 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Tue, 30 Apr 2019 17:48:53 +0200
Subject: [PATCH 04/44] =?UTF-8?q?Adaptation=20=C3=A0=20JaLHyd=20:=20proper?=
 =?UTF-8?q?ly=20separating=20singleValue=20and=20currentValue=20concepts?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Panneau des résultats fixés: protection contre un résultat vide
Mode calcul : protection contre valeur non définie
---
 .../fixedvar-results/fixed-results.component.ts       |  1 +
 .../ngparam-input/ngparam-input.component.ts          |  4 +---
 .../param-field-line/param-field-line.component.ts    |  3 ---
 src/app/formulaire/definition/form-compute.ts         |  5 ++---
 src/app/formulaire/ngparam.ts                         | 11 ++++++++---
 5 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/app/components/fixedvar-results/fixed-results.component.ts b/src/app/components/fixedvar-results/fixed-results.component.ts
index 182ed6f68..a6909ac0b 100644
--- a/src/app/components/fixedvar-results/fixed-results.component.ts
+++ b/src/app/components/fixedvar-results/fixed-results.component.ts
@@ -84,6 +84,7 @@ export class FixedResultsComponent {
         const res = this._fixedResults.result;
         if (
             res
+            && res.nbResultElements > 0
             && res.resultElement
             && res.extraResults
         ) {
diff --git a/src/app/components/ngparam-input/ngparam-input.component.ts b/src/app/components/ngparam-input/ngparam-input.component.ts
index 2717729d8..c61a4fb78 100644
--- a/src/app/components/ngparam-input/ngparam-input.component.ts
+++ b/src/app/components/ngparam-input/ngparam-input.component.ts
@@ -47,9 +47,7 @@ export class NgParamInputComponent extends GenericInputComponent implements Obse
      */
     protected afterSetModel() {
         if (this._paramDef) {
-            if (this._paramDef.isDefined) {
-                this._tmp = this._paramDef.getValue();
-            }
+            this._tmp = this._paramDef.getValue();
             this._paramDef.addObserver(this);
         }
     }
diff --git a/src/app/components/param-field-line/param-field-line.component.ts b/src/app/components/param-field-line/param-field-line.component.ts
index 8aae7a7c7..df956b33a 100644
--- a/src/app/components/param-field-line/param-field-line.component.ts
+++ b/src/app/components/param-field-line/param-field-line.component.ts
@@ -208,9 +208,6 @@ export class ParamFieldLineComponent implements OnChanges {
         switch (option) {
             case "fix":
                 this.param.valueMode = ParamValueMode.SINGLE;
-                // reset the value to avoid "undefined" after exiting CALC or LINK mode
-                // @TODO not always necessary; find out why
-                this.param.setValue(this, this.param.paramDefinition.singleValue);
                 break;
 
             case "var":
diff --git a/src/app/formulaire/definition/form-compute.ts b/src/app/formulaire/definition/form-compute.ts
index 64dd70555..af5c587cd 100644
--- a/src/app/formulaire/definition/form-compute.ts
+++ b/src/app/formulaire/definition/form-compute.ts
@@ -43,9 +43,8 @@ export abstract class FormCompute implements Observer {
             computedParam = nub.calculatedParam;
         }
 
-        // require chain computation; redundant with Nub.CalcSerie but required
-        // to get initial value here...
-        const computedParamValue = computedParam.getValue();
+        // const computedParamValue = computedParam.getValue();
+        const computedParamValue = computedParam.singleValue;
 
         switch (computedParam.domain.domain) {
             case ParamDomainValue.ANY:
diff --git a/src/app/formulaire/ngparam.ts b/src/app/formulaire/ngparam.ts
index 5681b58cd..294bbc53f 100644
--- a/src/app/formulaire/ngparam.ts
+++ b/src/app/formulaire/ngparam.ts
@@ -266,11 +266,16 @@ export class NgParameter extends InputField implements Observer {
     }
 
     /**
-     * Asks the ParamDefinition for its current value
-     * @TODO replace with singleValue to avoid displaying computation results ?
+     * Asks the ParamDefinition for its singleValue (not currentValue,
+     * to avoid displaying computation results); calculated linked
+     * value might not be available yet
      */
     public getValue() {
-        return this._paramDef.getValue();
+        try {
+            return this._paramDef.getValue();
+        } catch (error) {
+            return undefined;
+        }
     }
 
     /**
-- 
GitLab


From 43f63d28b3f18a6a6ae4c152e7a1541a370a02a7 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 2 May 2019 11:20:58 +0200
Subject: [PATCH 05/44] Ajout traductions

---
 src/locale/messages.en.json | 8 +++++++-
 src/locale/messages.fr.json | 6 ++++++
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index eba7d7d16..4e3136b6b 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -38,6 +38,7 @@
     "INFO_CALCULATOR_CALCULER": "Compute",
     "INFO_CALCULATOR_CALC_NAME": "Calculator name",
     "INFO_CALCULATOR_PARAMFIXES": "Fixed parameters",
+    "INFO_CALCULATOR_RESULTS_GENERATE_PAB": "Generate a fish ladder",
     "INFO_CALCULATOR_RESULTS_TITLE": "Results",
     "INFO_CALCULATOR_VALEURS": "Values",
     "INFO_CLOISONS_TITRE": "Fish ladder: Cross walls",
@@ -58,7 +59,11 @@
     "INFO_DIALOG_LOAD_SESSION_TITLE": "Load calculator modules",
     "INFO_DIALOG_SAVE_SESSION_FILENAME": "File name",
     "INFO_DIALOG_SAVE_SESSION_TITLE": "Save calculator modules",
-    "INFO_EMPTY_SESSION_DIALOGUE_TEXT": "Warning ! All open calculators will be lost.",
+    "INFO_DIALOG_PAB_Q": "Flow (m³/s)",
+    "INFO_DIALOG_PAB_Z1": "Upstream elevation (m)",
+    "INFO_DIALOG_PAB_Z2": "Downstream elevation (m)",
+    "INFO_DIALOG_PAB_NB": "Number of basins",
+    "INFO_EMPTY_SESSION_DIALOGUE_TEXT": "Warning ! All open calculators will be lost. Continue ?",
     "INFO_EMPTY_SESSION_DIALOGUE_TITRE": "New session",
     "INFO_EXTRARES_ENUM_MACRORUGOFLOWTYPE_0": "Emergent",
     "INFO_EXTRARES_ENUM_MACRORUGOFLOWTYPE_1": "Quasi-emergent",
@@ -168,6 +173,7 @@
     "INFO_OPTION_START_NEW": "Start new session",
     "INFO_OPTION_VALIDATE": "Validate",
     "INFO_OPTION_YES": "Yes",
+    "INFO_OPTION_GENERATE": "Generate",
     "INFO_OUVRAGE": "Structure",
     "INFO_PABCHUTE_TITRE": "Fish ladder: fall",
     "INFO_PABCHUTE_TITRE_COURT": "FL: fall",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index 377791704..9ef167874 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -38,6 +38,7 @@
     "INFO_CALCULATOR_CALCULER": "Calculer",
     "INFO_CALCULATOR_CALC_NAME": "Nom du module de calcul",
     "INFO_CALCULATOR_PARAMFIXES": "Paramètres fixés",
+    "INFO_CALCULATOR_RESULTS_GENERATE_PAB": "Générer une passe à bassin",
     "INFO_CALCULATOR_RESULTS_TITLE": "Résultats",
     "INFO_CALCULATOR_VALEURS": "Valeurs",
     "INFO_CLOISONS_TITRE": "Passe à bassins&nbsp;: Cloisons",
@@ -59,6 +60,10 @@
     "INFO_DIALOG_SAVE_SESSION_FILENAME": "Nom de fichier",
     "INFO_DIALOG_SAVE_SESSION_TITLE": "Enregistrer les modules de calcul",
     "INFO_EMPTY_SESSION_DIALOGUE_TEXT": "Attention&nbsp;! Tous les modules de calcul ouverts seront perdus.",
+    "INFO_DIALOG_PAB_Q": "Débit (m³/s)",
+    "INFO_DIALOG_PAB_Z1": "Cote amont (m)",
+    "INFO_DIALOG_PAB_Z2": "Cote aval (m)",
+    "INFO_DIALOG_PAB_NB": "Nombre de bassins",
     "INFO_EMPTY_SESSION_DIALOGUE_TITRE": "Démarrer une nouvelle session",
     "INFO_EXTRARES_ENUM_MACRORUGOFLOWTYPE_0": "Émergent",
     "INFO_EXTRARES_ENUM_MACRORUGOFLOWTYPE_1": "Quasi-émergent",
@@ -168,6 +173,7 @@
     "INFO_OPTION_START_NEW": "Démarrer une nouvelle session",
     "INFO_OPTION_VALIDATE": "Valider",
     "INFO_OPTION_YES": "Oui",
+    "INFO_OPTION_GENERATE": "Générer",
     "INFO_OUVRAGE": "Ouvrage",
     "INFO_PABCHUTE_TITRE": "Passe à bassins&nbsp;: chute",
     "INFO_PABCHUTE_TITRE_COURT": "PAB&nbsp;: chute",
-- 
GitLab


From 0d0b57bb76bf6578124bfd7ca6f1f327a4cc09ed Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 2 May 2019 14:09:09 +0200
Subject: [PATCH 06/44] =?UTF-8?q?Bouton=20et=20dialogue=20pour=20cr=C3=A9e?=
 =?UTF-8?q?r=20une=20passe=20=C3=A0=20bassins=20depuis=20un=20module=20Clo?=
 =?UTF-8?q?isons?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/app/app.module.ts                         |  3 +
 .../dialog-generate-pab.component.html        | 59 +++++++++++++++
 .../dialog-generate-pab.component.scss        | 18 +++++
 .../dialog-generate-pab.component.ts          | 74 +++++++++++++++++++
 .../calculator.component.html                 |  4 +
 .../calculator.component.scss                 |  4 +
 .../calculator.component.ts                   | 37 +++++++++-
 src/locale/messages.en.json                   |  5 +-
 src/locale/messages.fr.json                   |  7 +-
 9 files changed, 202 insertions(+), 9 deletions(-)
 create mode 100644 src/app/components/dialog-generate-pab/dialog-generate-pab.component.html
 create mode 100644 src/app/components/dialog-generate-pab/dialog-generate-pab.component.scss
 create mode 100644 src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts

diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 059c34d50..3367199a6 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -77,6 +77,7 @@ import { DialogConfirmEmptySessionComponent } from "./components/dialog-confirm-
 import { DialogConfirmCloseCalcComponent } from "./components/dialog-confirm-close-calc/dialog-confirm-close-calc.component";
 import { DialogEditParamComputedComponent } from "./components/dialog-edit-param-computed/dialog-edit-param-computed.component";
 import { DialogEditParamValuesComponent } from "./components/dialog-edit-param-values/dialog-edit-param-values.component";
+import { DialogGeneratePABComponent } from "./components/dialog-generate-pab/dialog-generate-pab.component";
 import { DialogLoadSessionComponent } from "./components/dialog-load-session/dialog-load-session.component";
 import { DialogSaveSessionComponent } from "./components/dialog-save-session/dialog-save-session.component";
 
@@ -146,6 +147,7 @@ const appRoutes: Routes = [
     DialogConfirmEmptySessionComponent,
     DialogEditParamComputedComponent,
     DialogEditParamValuesComponent,
+    DialogGeneratePABComponent,
     DialogLoadSessionComponent,
     DialogSaveSessionComponent,
     FieldSetComponent,
@@ -181,6 +183,7 @@ const appRoutes: Routes = [
     DialogConfirmEmptySessionComponent,
     DialogEditParamComputedComponent,
     DialogEditParamValuesComponent,
+    DialogGeneratePABComponent,
     DialogSaveSessionComponent,
     DialogLoadSessionComponent
   ],
diff --git a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.html b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.html
new file mode 100644
index 000000000..f404ad363
--- /dev/null
+++ b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.html
@@ -0,0 +1,59 @@
+<h1 mat-dialog-title [innerHTML]="uitextGeneratePAB"></h1>
+
+<form id="form-generate-pab">
+
+  <div mat-dialog-content>
+
+    <mat-form-field>
+      <input matInput required [placeholder]="uitextDebit" pattern="^([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$"
+      [(ngModel)]="debit" name="debit" #inputDebit="ngModel">
+    </mat-form-field>
+    <mat-error *ngIf="inputDebit.invalid && (inputDebit.dirty || inputDebit.touched)">
+        <div *ngIf="inputDebit.errors.required || inputDebit.errors.pattern">
+            {{ uitextMustBePositive }}
+        </div>
+    </mat-error>
+
+    <mat-form-field>
+      <input matInput required [placeholder]="uitextCoteAmont" pattern="^-?([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$"
+      [(ngModel)]="coteAmont" name="coteAmont" #inputCoteAmont="ngModel">
+    </mat-form-field>
+    <mat-error *ngIf="inputCoteAmont.invalid && (inputCoteAmont.dirty || inputCoteAmont.touched)">
+        <div *ngIf="inputCoteAmont.errors.required || inputCoteAmont.errors.pattern">
+            {{ uitextMustBeANumber }}
+        </div>
+    </mat-error>
+
+    <mat-form-field>
+      <input matInput required [placeholder]="uitextCoteAval" pattern="^-?([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$"
+      [(ngModel)]="coteAval" name="coteAval" #inputCoteAval="ngModel">
+    </mat-form-field>
+    <mat-error *ngIf="inputCoteAval.invalid && (inputCoteAval.dirty || inputCoteAval.touched)">
+        <div *ngIf="inputCoteAval.errors.required || inputCoteAval.errors.pattern">
+            {{ uitextMustBeANumber }}
+        </div>
+    </mat-error>
+
+    <mat-form-field>
+      <input matInput required [placeholder]="uitextNBBassins" pattern="^[1-9][0-9]*$"
+      [(ngModel)]="nbBassins" name="nbBassins" #inputNbBassins="ngModel">
+    </mat-form-field>
+    <mat-error *ngIf="inputNbBassins.invalid && (inputNbBassins.dirty || inputNbBassins.touched)">
+        <div *ngIf="inputNbBassins.errors.required || inputNbBassins.errors.pattern">
+            {{ uitextMustBePositive }}
+        </div>
+    </mat-error>
+
+  </div>
+
+  <div mat-dialog-actions>
+    <button mat-raised-button color="primary" [mat-dialog-close]="false" cdkFocusInitial>
+      {{ uitextCancel }}
+    </button>
+    <button mat-raised-button type="submit" color="warn" (click)="generatePAB()"
+      [disabled]="(inputDebit.invalid || inputCoteAmont.invalid || inputCoteAval.invalid || inputNbBassins.invalid)">
+      {{ uitextGenerate }}
+    </button>
+  </div>
+
+</form>
diff --git a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.scss b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.scss
new file mode 100644
index 000000000..032bdab00
--- /dev/null
+++ b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.scss
@@ -0,0 +1,18 @@
+#form-generate-pab {
+    max-width: 350px;
+}
+
+mat-form-field {
+    display: block;
+
+    .mat-input-element {
+        font-size: .9em;
+    }
+}
+
+mat-error {
+    font-weight: 500;
+    font-size: .8em;
+    margin-top: -1.2em;
+    margin-bottom: 1.2em;
+}
diff --git a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
new file mode 100644
index 000000000..0fc792862
--- /dev/null
+++ b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
@@ -0,0 +1,74 @@
+import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
+import { Inject, Component } from "@angular/core";
+
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
+import { FormGroup, FormBuilder, Validators } from "@angular/forms";
+
+@Component({
+    selector: "dialog-generate-pab",
+    templateUrl: "dialog-generate-pab.component.html",
+    styleUrls: ["dialog-generate-pab.component.scss"]
+})
+export class DialogGeneratePABComponent {
+
+  public debit: number;
+
+  public coteAmont: number;
+
+  public coteAval: number;
+
+  public nbBassins: number;
+
+  constructor(
+    public dialogRef: MatDialogRef<DialogGeneratePABComponent>,
+    private intlService: I18nService,
+    private fb: FormBuilder,
+    @Inject(MAT_DIALOG_DATA) public data: any
+  ) { }
+
+  public generatePAB() {
+    this.dialogRef.close({
+      generate: true,
+      debit: this.debit,
+      coteAmont: this.coteAmont,
+      coteAval: this.coteAval,
+      nbBassins: this.nbBassins
+    });
+  }
+
+  public get uitextDebit() {
+    return this.intlService.localizeText("INFO_DIALOG_PAB_Q");
+  }
+
+  public get uitextCoteAmont() {
+    return this.intlService.localizeText("INFO_DIALOG_PAB_Z1");
+  }
+
+  public get uitextCoteAval() {
+    return this.intlService.localizeText("INFO_DIALOG_PAB_Z2");
+  }
+
+  public get uitextNBBassins() {
+    return this.intlService.localizeText("INFO_DIALOG_PAB_NB");
+  }
+
+  public get uitextMustBeANumber() {
+    return this.intlService.localizeText("ERROR_PARAM_MUST_BE_A_NUMBER");
+  }
+
+  public get uitextMustBePositive() {
+    return this.intlService.localizeText("ERROR_PARAM_MUST_BE_POSITIVE");
+  }
+
+  public get uitextGeneratePAB() {
+      return this.intlService.localizeText("INFO_CALCULATOR_RESULTS_GENERATE_PAB");
+  }
+
+  public get uitextGenerate() {
+    return this.intlService.localizeText("INFO_OPTION_GENERATE");
+  }
+
+  public get uitextCancel() {
+    return this.intlService.localizeText("INFO_OPTION_CANCEL");
+  }
+}
diff --git a/src/app/components/generic-calculator/calculator.component.html b/src/app/components/generic-calculator/calculator.component.html
index 27eb0116b..db06e3792 100644
--- a/src/app/components/generic-calculator/calculator.component.html
+++ b/src/app/components/generic-calculator/calculator.component.html
@@ -57,6 +57,10 @@
                         <mat-card-title>
                             <h1 [innerHTML]="uitextResultsTitle"></h1>
                         </mat-card-title>
+                        <div fxFlex></div>
+                        <button mat-raised-button color="accent" id="generate-pab" *ngIf="isPABCloisons" (click)="generatePAB()">
+                            {{ uitextGeneratePAB }}
+                        </button>
                     </mat-card-header>
                     <mat-card-content>
                         <calc-results id="resultsComp" (afterViewChecked)="onCalcResultsViewChecked()"></calc-results>
diff --git a/src/app/components/generic-calculator/calculator.component.scss b/src/app/components/generic-calculator/calculator.component.scss
index f6af036eb..c9cbd66b2 100644
--- a/src/app/components/generic-calculator/calculator.component.scss
+++ b/src/app/components/generic-calculator/calculator.component.scss
@@ -53,6 +53,10 @@ mat-card {
 
         mat-card-header {
             margin-bottom: 1em;
+
+            #generate-pab {
+                height: min-content;
+            }
         }
     }
 
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index c6c4aaa83..bc846c090 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -1,8 +1,8 @@
 import { Component, OnInit, DoCheck, OnDestroy, ViewChild, ViewChildren,
-         QueryList, AfterViewChecked, ElementRef, Renderer2 } from "@angular/core";
+         QueryList, AfterViewChecked, ElementRef } from "@angular/core";
 import { ActivatedRoute, Router } from "@angular/router";
 
-import { Observer, Session, ParamValueMode } from "jalhyd";
+import { Observer, Session, ParamValueMode, CalculatorType } from "jalhyd";
 
 import { FormulaireService } from "../../services/formulaire/formulaire.service";
 import { I18nService } from "../../services/internationalisation/internationalisation.service";
@@ -19,6 +19,7 @@ import { FieldsetContainerComponent } from "../fieldset-container/fieldset-conta
 import { ServiceFactory } from "../../services/service-factory";
 import { MatDialog } from "@angular/material";
 import { DialogConfirmCloseCalcComponent } from "../dialog-confirm-close-calc/dialog-confirm-close-calc.component";
+import { DialogGeneratePABComponent } from "../dialog-generate-pab/dialog-generate-pab.component";
 
 @Component({
     selector: "hydrocalc",
@@ -100,8 +101,8 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
         private route: ActivatedRoute,
         private router: Router,
         private confirmCloseCalcDialog: MatDialog,
-        private _elementRef: ElementRef,
-        private renderer: Renderer2
+        private generatePABDialog: MatDialog,
+        private _elementRef: ElementRef
     ) {
         super();
         this.intlService = ServiceFactory.instance.i18nService;
@@ -158,6 +159,10 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
         return this.intlService.localizeText("INFO_CALCULATOR_RESULTS_TITLE");
     }
 
+    public get uitextGeneratePAB() {
+        return this.intlService.localizeText("INFO_CALCULATOR_RESULTS_GENERATE_PAB");
+    }
+
     /**
      * Triggered at calculator instanciation
      */
@@ -419,6 +424,30 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
         return false;
     }
 
+    public get isPABCloisons() {
+        return (
+            this._formulaire
+            && this._formulaire.currentNub
+            && this._formulaire.currentNub.calcType === CalculatorType.Cloisons
+        );
+    }
+
+    public generatePAB() {
+        // création du dialogue de génération d'une passe à bassin
+        const dialogRef = this.generatePABDialog.open(
+          DialogGeneratePABComponent,
+          { disableClose: true }
+        );
+        dialogRef.afterClosed().subscribe(result => {
+          if (result) {
+            if (result.generate) {
+              console.log("ON GÉNÈRE !!", result);
+              // this.doEmptySession();
+            }
+          }
+        });
+    }
+
     public saveCalculator() {
         this.formulaireService.saveForm(this._formulaire);
     }
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index 4e3136b6b..780336339 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -13,6 +13,9 @@
     "ERROR_MINMAXSTEP_MIN": "Value is not in [%s,%s[",
     "ERROR_MINMAXSTEP_STEP": "Value is not in %s",
     "ERROR_NEWTON_DERIVEE_NULLE": "Null function derivative in Newton computation",
+    "ERROR_PARAM_NULL": "Parameter value must not be NULL",
+    "ERROR_PARAM_MUST_BE_A_NUMBER": "Please type a numeric value",
+    "ERROR_PARAM_MUST_BE_POSITIVE": "Please type a positive numeric value",
     "ERROR_PARAMDEF_CALC_UNDEFINED": "calculability of '%symbol%' parameter is undefined",
     "ERROR_PARAMDEF_LINKED_VALUE_UNDEFINED": "value of '%symbol%' linked parameter is undefined",
     "ERROR_PARAMDEF_VALUE_FIXED": "value of '%symbol%' parameter cannot be changed",
@@ -23,8 +26,6 @@
     "ERROR_PARAMDEF_VALUE_UNDEFINED": "value of '%symbol%' parameter is undefined",
     "ERROR_PARAMDOMAIN_INTERVAL_BOUNDS": "invalid %minValue%/%maxValue% min/max boundaries for 'interval' parameter definition domain",
     "ERROR_PARAMDOMAIN_INVALID": "parameter '%symbol%: non supported '%domain%' definition domain",
-    "ERROR_PARAM_MUST_BE_A_NUMBER": "Please type a numeric value",
-    "ERROR_PARAM_NULL": "Parameter value must not be NULL",
     "ERROR_REMOUS_PAS_CALCUL_DEPUIS_AMONT": "Upstream boundary condition < Critical elevation: no possible calculation from upstream",
     "ERROR_REMOUS_PAS_CALCUL_DEPUIS_AVAL": "Downstream boundary condition < Critical elevation: no possible calculation from downstream",
     "ERROR_REMOUS_PENTE_FORTE": "The water line slope is too steep at abscissa %x% m (the discretisation step should be reduced)",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index 9ef167874..65ee8a16e 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -13,6 +13,9 @@
     "ERROR_MINMAXSTEP_MIN": "La valeur n'est pas dans [%s,%s[",
     "ERROR_MINMAXSTEP_STEP": "La valeur n'est pas dans %s",
     "ERROR_NEWTON_DERIVEE_NULLE": "Dérivée nulle dans un calcul par la méthode de Newton",
+    "ERROR_PARAM_NULL": "La valeur du paramètre ne peut pas être NULL",
+    "ERROR_PARAM_MUST_BE_A_NUMBER": "Veuillez entrer une valeur numérique",
+    "ERROR_PARAM_MUST_BE_POSITIVE": "Veuillez entrer une valeur numérique positive",
     "ERROR_PARAMDEF_CALC_UNDEFINED": "La calculabilité du paramètre %symbol% n'est pas définie",
     "ERROR_PARAMDEF_LINKED_VALUE_UNDEFINED": "La valeur du paramètre lié %symbol% n'est pas définie",
     "ERROR_PARAMDEF_VALUE_FIXED": "La valeur du paramètre %symbol% ne peut pas être changée",
@@ -23,8 +26,6 @@
     "ERROR_PARAMDEF_VALUE_UNDEFINED": "La valeur du paramètre %symbol% n'est pas définie",
     "ERROR_PARAMDOMAIN_INTERVAL_BOUNDS": "Les bornes (%minValue%/%maxValue%) de l'intervalle sont incorrectes",
     "ERROR_PARAMDOMAIN_INVALID": "Paramètre '%symbol%'&nbsp;: le domaine de définition '%domain%' est incorrect",
-    "ERROR_PARAM_MUST_BE_A_NUMBER": "Veuillez entrer une valeur numérique",
-    "ERROR_PARAM_NULL": "La valeur du paramètre ne peut pas être NULL",
     "ERROR_REMOUS_PAS_CALCUL_DEPUIS_AMONT": "Condition limite amont > Hauteur critique&nbsp;: pas de calcul possible depuis l'amont",
     "ERROR_REMOUS_PAS_CALCUL_DEPUIS_AVAL": "Condition limite aval < Hauteur critique&nbsp;: pas de calcul possible depuis l'aval",
     "ERROR_REMOUS_PENTE_FORTE": "La pente de la ligne d'eau est trop forte à l'abscisse %x% m (il faudrait réduire le pas de discrétisation)",
@@ -38,7 +39,7 @@
     "INFO_CALCULATOR_CALCULER": "Calculer",
     "INFO_CALCULATOR_CALC_NAME": "Nom du module de calcul",
     "INFO_CALCULATOR_PARAMFIXES": "Paramètres fixés",
-    "INFO_CALCULATOR_RESULTS_GENERATE_PAB": "Générer une passe à bassin",
+    "INFO_CALCULATOR_RESULTS_GENERATE_PAB": "Générer une passe à bassins",
     "INFO_CALCULATOR_RESULTS_TITLE": "Résultats",
     "INFO_CALCULATOR_VALEURS": "Valeurs",
     "INFO_CLOISONS_TITRE": "Passe à bassins&nbsp;: Cloisons",
-- 
GitLab


From 7ad47435b31c8cd017d703e0df05de5a8c522aca Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Mon, 6 May 2019 15:00:17 +0200
Subject: [PATCH 07/44] =?UTF-8?q?Ajout=20module=20Passe=20=C3=A0=20Bassins?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Small CSS fix for mat-select label font-size
removed unused code
excluded PabCloisons from modules list
---
 src/app/calculators/pab/pab.config.json       |  68 +++++
 src/app/calculators/pab/pab.en.json           |   9 +
 src/app/calculators/pab/pab.fr.json           |   9 +
 .../calculator-list.component.ts              |  11 +
 .../dialog-generate-pab.component.ts          |  10 +-
 .../calculator.component.ts                   |  27 +-
 .../select-field-line.component.scss          |  16 +-
 src/app/config.json                           |   2 +-
 .../definition/concrete/form-base.ts          |   8 -
 .../definition/concrete/form-pab.ts           | 234 ++++++++++++++++++
 .../concrete/form-parallel-structures.ts      |   9 +-
 .../concrete/form-regime-uniforme.ts          |   1 -
 .../formulaire/definition/form-compute-pab.ts |  63 +++++
 .../form-compute-parallel-structures.ts       |   5 +-
 .../definition/form-def-fixedvar.ts           |  14 --
 .../form-def-parallel-structures.ts           |   5 -
 .../definition/form-def-paramcalc.ts          |  16 --
 .../services/formulaire/formulaire.service.ts |  28 ++-
 src/locale/messages.en.json                   |   4 +
 src/locale/messages.fr.json                   |   4 +
 20 files changed, 467 insertions(+), 76 deletions(-)
 create mode 100644 src/app/calculators/pab/pab.config.json
 create mode 100644 src/app/calculators/pab/pab.en.json
 create mode 100644 src/app/calculators/pab/pab.fr.json
 create mode 100644 src/app/formulaire/definition/concrete/form-pab.ts
 create mode 100644 src/app/formulaire/definition/form-compute-pab.ts
 delete mode 100644 src/app/formulaire/definition/form-def-fixedvar.ts
 delete mode 100644 src/app/formulaire/definition/form-def-parallel-structures.ts
 delete mode 100644 src/app/formulaire/definition/form-def-paramcalc.ts

diff --git a/src/app/calculators/pab/pab.config.json b/src/app/calculators/pab/pab.config.json
new file mode 100644
index 000000000..a869b2b81
--- /dev/null
+++ b/src/app/calculators/pab/pab.config.json
@@ -0,0 +1,68 @@
+[
+    {
+        "id": "fs_param_hydro",
+        "type": "fieldset",
+        "calcType": "Pab",
+        "option": "cal",
+        "fields": [
+            {
+                "type": "input",
+                "id": "Q",
+                "symbol": "Q",
+                "unit": "m³/s"
+            },
+            {
+                "type": "input",
+                "id": "Z1",
+                "unit": "m"
+            },
+            {
+                "type": "input",
+                "id": "Z2",
+                "unit": "m"
+            }
+        ]
+    },
+    {
+        "id": "fs_bassin",
+        "type": "fieldset_template",
+        "calcType": "PabCloisons",
+        "option": "fix",
+        "fields": [
+            {
+                "id": "select_modele_cloisons",
+                "type": "select",
+                "select": []
+            },
+            {
+                "type": "input",
+                "id": "QA",
+                "unit": "m³/s"
+            }
+        ]
+    },
+    {
+        "id": "bassin_container",
+        "type": "template_container",
+        "templates": [
+            "fs_bassin"
+        ]
+    },
+    {
+        "id": "fs_param_calc",
+        "type": "fieldset",
+        "calcType": "ParallelStructure",
+        "option": "fix",
+        "fields": [
+            {
+                "type": "input",
+                "id": "Pr"
+            }
+        ]
+    },
+    {
+        "type": "options",
+        "modeleCloisonsSelectId": "select_modele_cloisons",
+        "idCal": "Q"
+    }
+]
\ No newline at end of file
diff --git a/src/app/calculators/pab/pab.en.json b/src/app/calculators/pab/pab.en.json
new file mode 100644
index 000000000..955f0fb0b
--- /dev/null
+++ b/src/app/calculators/pab/pab.en.json
@@ -0,0 +1,9 @@
+{
+    "fs_param_hydro": "Hydraulic parameters",
+    "Q": "Flow",
+    "Z1": "Upstream elevation",
+    "Z2": "Downstream elevation",
+    "fs_bassin": "Basin",
+    "bassin_container": "Basins",
+    "select_modele_cloisons": "Cross walls model"
+}
\ No newline at end of file
diff --git a/src/app/calculators/pab/pab.fr.json b/src/app/calculators/pab/pab.fr.json
new file mode 100644
index 000000000..e64d3e88f
--- /dev/null
+++ b/src/app/calculators/pab/pab.fr.json
@@ -0,0 +1,9 @@
+{
+    "fs_param_hydro": "Paramètres hydrauliques",
+    "Q": "Débit",
+    "Z1": "Cote amont",
+    "Z2": "Cote aval",
+    "fs_bassin": "Bassin",
+    "bassin_container": "Bassins",
+    "select_modele_cloisons": "Modèle de cloisons"
+}
\ No newline at end of file
diff --git a/src/app/components/calculator-list/calculator-list.component.ts b/src/app/components/calculator-list/calculator-list.component.ts
index b44a8e986..dfbce4f92 100644
--- a/src/app/components/calculator-list/calculator-list.component.ts
+++ b/src/app/components/calculator-list/calculator-list.component.ts
@@ -8,6 +8,7 @@ import { ServiceFactory } from "../../services/service-factory";
 import { I18nService } from "../../services/internationalisation/internationalisation.service";
 import { FormulaireParallelStructure } from "../../formulaire/definition/concrete/form-parallel-structures";
 import { FieldsetContainer } from "../../formulaire/fieldset-container";
+import { FormulairePab } from "../../formulaire/definition/concrete/form-pab";
 
 
 @Component({
@@ -78,6 +79,7 @@ export class CalculatorListComponent implements OnInit {
                     if ( // those 2 sub-Nub types cannot be built outside a parent
                         t !== CalculatorType.Structure
                         && t !== CalculatorType.Section
+                        && t !== CalculatorType.PabCloisons
                     ) {
                         unusedTheme.calculators.push({
                             type: t,
@@ -108,6 +110,15 @@ export class CalculatorListComponent implements OnInit {
                     }
                 }
             }
+            // on ajoute un ouvrage après l'ouverture du module de calcul "passe à bassins"
+            if (f instanceof FormulairePab) {
+                for (const e of f.allFormElements) {
+                    if (e instanceof FieldsetContainer) {
+                        e.addFromTemplate(0);
+                        break;
+                    }
+                }
+            }
         });
     }
 
diff --git a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
index 0fc792862..3b8683598 100644
--- a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
+++ b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
@@ -2,7 +2,7 @@ import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
 import { Inject, Component } from "@angular/core";
 
 import { I18nService } from "../../services/internationalisation/internationalisation.service";
-import { FormGroup, FormBuilder, Validators } from "@angular/forms";
+import { FormBuilder } from "@angular/forms";
 
 @Component({
     selector: "dialog-generate-pab",
@@ -11,13 +11,13 @@ import { FormGroup, FormBuilder, Validators } from "@angular/forms";
 })
 export class DialogGeneratePABComponent {
 
-  public debit: number;
+  public debit = 1.5;
 
-  public coteAmont: number;
+  public coteAmont = 102;
 
-  public coteAval: number;
+  public coteAval = 99;
 
-  public nbBassins: number;
+  public nbBassins = 6;
 
   constructor(
     public dialogRef: MatDialogRef<DialogGeneratePABComponent>,
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index bc846c090..e0eafb473 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -2,7 +2,7 @@ import { Component, OnInit, DoCheck, OnDestroy, ViewChild, ViewChildren,
          QueryList, AfterViewChecked, ElementRef } from "@angular/core";
 import { ActivatedRoute, Router } from "@angular/router";
 
-import { Observer, Session, ParamValueMode, CalculatorType } from "jalhyd";
+import { Observer, Session, Cloisons, Pab, PabCloisons, ParamValueMode, CalculatorType } from "jalhyd";
 
 import { FormulaireService } from "../../services/formulaire/formulaire.service";
 import { I18nService } from "../../services/internationalisation/internationalisation.service";
@@ -432,6 +432,9 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
         );
     }
 
+    /**
+     * Génère une passe à bassins à partir du modèle de cloisons en cours
+     */
     public generatePAB() {
         // création du dialogue de génération d'une passe à bassin
         const dialogRef = this.generatePABDialog.open(
@@ -441,8 +444,26 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
         dialogRef.afterClosed().subscribe(result => {
           if (result) {
             if (result.generate) {
-              console.log("ON GÉNÈRE !!", result);
-              // this.doEmptySession();
+              this.formulaireService.createFormulaire(CalculatorType.Pab).then((f: FormulaireDefinition) => {
+                const pab = (f.currentNub as Pab);
+                const params = pab.prms;
+                // paramètres hydrauliques
+                params.Q.singleValue = result.debit;
+                params.Z1.singleValue = result.coteAmont;
+                params.Z2.singleValue = result.coteAval;
+                // création des bassins
+                for (let i = 0; i < result.nbBassins; i++) {
+                    const modeleCloisons = new PabCloisons((this._formulaire.currentNub as Cloisons));
+                    pab.addChild(modeleCloisons);
+                    for (const e of f.allFormElements) {
+                        if (e instanceof FieldsetContainer) {
+                            // @TODO how to pass Nub here ? (parameter exists but is never used)
+                            e.addFromTemplate(0, undefined, modeleCloisons);
+                            break;
+                        }
+                    }
+                }
+              });
             }
           }
         });
diff --git a/src/app/components/select-field-line/select-field-line.component.scss b/src/app/components/select-field-line/select-field-line.component.scss
index 7b16e5427..6f94917d1 100644
--- a/src/app/components/select-field-line/select-field-line.component.scss
+++ b/src/app/components/select-field-line/select-field-line.component.scss
@@ -10,15 +10,15 @@ mat-form-field {
                 }
             }
         }
+    }
 
-        ::ng-deep .mat-form-field-label {
-            font-size: 1.1em;
-            line-height: 1.4em;
-            margin-top: -2px;
-    
-            &.mat-form-field-empty {
-                font-size: 1em;
-            }
+    ::ng-deep .mat-form-field-label {
+        font-size: 1.1em;
+        line-height: 1.4em;
+        margin-top: -2px;
+
+        &.mat-form-field-empty {
+            font-size: 1em;
         }
     }
 }
diff --git a/src/app/config.json b/src/app/config.json
index 86e823ea8..19625acb4 100644
--- a/src/app/config.json
+++ b/src/app/config.json
@@ -13,7 +13,7 @@
                 "title": "Passe à poisson sur le Lez, entre Bollène et Suze",
                 "credits": "Hervé Capra / Irstea"
             },
-            "calculators": [ 5, 6, 12, 13, 10, 9 ]
+            "calculators": [ 15, 5, 6, 12, 13, 10, 9 ]
         },
         {
             "name": "PASSE_NATURELLE",
diff --git a/src/app/formulaire/definition/concrete/form-base.ts b/src/app/formulaire/definition/concrete/form-base.ts
index f1afa17ea..d033e5076 100644
--- a/src/app/formulaire/definition/concrete/form-base.ts
+++ b/src/app/formulaire/definition/concrete/form-base.ts
@@ -1,6 +1,5 @@
 import { FormulaireDefinition } from "../form-definition";
 import { CalculatorResults } from "../../../results/calculator-results";
-import { FormDefParamToCalculate } from "../form-def-paramcalc";
 import { FormResult } from "../form-result";
 import { FormCompute } from "../form-compute";
 import { FormResultFixedVar } from "../form-result-fixedvar";
@@ -9,23 +8,16 @@ import { ServiceFactory } from "../../../services/service-factory";
 
 export class FormulaireBase extends FormulaireDefinition {
 
-    protected _formParamCalc: FormDefParamToCalculate;
-
     protected _formCompute: FormCompute;
 
     protected _formResult: FormResult;
 
     constructor() {
         super();
-        this._formParamCalc = new FormDefParamToCalculate(this);
         this._formResult = new FormResultFixedVar(this, false);
         this._formCompute = new FormComputeFixedVar(this, (this._formResult as FormResultFixedVar));
     }
 
-    protected completeParse(json: {}) {
-        this._formParamCalc.parseOptions(json);
-    }
-
     /**
      * Resets the form results, the results panel on screen, the model
      * results, and does the same for all depending modules
diff --git a/src/app/formulaire/definition/concrete/form-pab.ts b/src/app/formulaire/definition/concrete/form-pab.ts
new file mode 100644
index 000000000..cb329818d
--- /dev/null
+++ b/src/app/formulaire/definition/concrete/form-pab.ts
@@ -0,0 +1,234 @@
+import { Structure, Nub, ParallelStructure, StructureProperties, Props, Session, PabCloisons, Pab } from "jalhyd";
+
+import { FormComputeParallelStructures } from "../form-compute-parallel-structures";
+import { FormResultFixedVar } from "../form-result-fixedvar";
+import { FieldsetContainer } from "../../fieldset-container";
+import { FieldSet } from "../../fieldset";
+import { SelectField } from "../../select-field";
+import { NgParameter } from "../../ngparam";
+import { FieldsetTemplate } from "../../fieldset-template";
+import { FormulaireNode } from "../../formulaire-node";
+import { FormulaireBase } from "./form-base";
+
+/**
+ * Formulaire pour les passes à bassins, inspiré du formulaire
+ * pour les structures en parallèle
+ */
+export class FormulairePab extends FormulaireBase {
+
+    /** id du select configurant le modèle de cloisons */
+    private __modeleCloisonsSelectId: string;
+
+    constructor() {
+        super();
+        this._formResult = new FormResultFixedVar(this, false);
+
+        // remove obsolete observer set by super()
+        this.removeObserver(this._formCompute);
+        this._formCompute = new FormComputeParallelStructures(this, (this._formResult as FormResultFixedVar));
+    }
+
+    private createPabCloisons(templ: FieldsetTemplate): Nub {
+        // !!! attention !!!
+        // Il doit y avoir cohérence dans le fichier de conf entre les valeurs defaultXXX et les valeurs possibles pour les select
+        // cad valeur par défaut du 1er select (type d'ouvrage), du 2ème (loi de débit).
+        // A terme, il faudrait analyser le fichier de conf (dépendances d'existence) pour déterminer automatiquement ces valeurs
+
+        const params = {};
+        params["calcType"] = templ.calcTypeFromConfig;
+        params["nodeType"] = templ.defaultNodeTypeFromConfig;
+
+        return this.createBassin(new Props(params));
+    }
+
+    /**
+     * ajoute un Nub Structure
+     * @param st structure à ajouter
+     * @param after position après laquelle insérer la structure, à la fin sinon
+     */
+    private addPabCloisons(st: Structure, after?: number) {
+        this.parallelStructureNub.addChild(st, after);
+    }
+
+    private get parallelStructureNub(): ParallelStructure {
+        return this.currentNub as ParallelStructure;
+    }
+
+    /**
+     * Asks JaLHyd to create a PabCloisons Nub as a child of the current Calculator Module
+     * and return it; does not store it in the Session (for PabCloisons, not for Calculator Modules)
+     * @param p properties for the new Nub
+     */
+    protected createBassin(p: Props): PabCloisons {
+        return Session.getInstance().createNub(p, this.currentNub as Pab) as PabCloisons;
+    }
+
+    /**
+     * Replaces the current Nub in the current calculator module, with a new one built with properties "params"
+     * @param params properties to build the new Nub (calcType, loiDebit...)
+     */
+    protected replaceNub(sn: Structure, params: Props): Nub {
+        const parent = (this.currentNub as PabCloisons);
+        const newBassin = this.createBassin(params);
+        parent.replaceChildInplace(sn, newBassin);
+        return newBassin;
+    }
+
+    /**
+     * Deleted the given child Nub in the current calculator module
+     * @param params properties to build the new Nub (calcType, loiDebit...)
+     */
+    protected deleteNub(sn: Structure) {
+        const parent = (this.currentNub as PabCloisons);
+        parent.deleteChild(parent.getIndexForChild(sn));
+    }
+
+    public createFieldset(parent: FormulaireNode, json: {}, data?: {}, nub?: Nub): FieldSet {
+        console.log("++++++++++ CREATE FIELDSET +++++++++++", parent.constructor.name, json, data, nub ? nub.constructor.name : "zizi");
+        if (json["calcType"] === "PabCloisons") {
+            console.log("youpidou");
+            // indice après lequel insérer le nouveau FieldSet
+            const after = data["after"];
+
+            const res: FieldSet = new FieldSet(parent);
+            let n: Nub;
+            if (nub) { // use existing Nub (build interface based on model)
+                n = nub;
+            } else {
+                n = this.createPabCloisons(data["template"]);
+                this.addPabCloisons(n as Structure, after);
+            }
+            res.setNub(n, false);
+
+            if (after !== undefined) {
+                parent.kids.splice(after + 1, 0, res);
+            } else {
+                parent.kids.push(res);
+            }
+
+            this.resetResults();
+
+            return res;
+        } else {
+            return super.createFieldset(parent, json, data);
+        }
+    }
+
+    protected parseOptions(json: {}) {
+        super.parseOptions(json);
+
+        // id du select configurant le type d'ouvrage
+        this.__modeleCloisonsSelectId = this.getOption(json, "ouvrageSelectId");
+    }
+
+    public afterParseFieldset(fs: FieldSet) {
+        // si le FieldSet contient le select de type d'ouvrage
+        if (this.__modeleCloisonsSelectId) {
+            const sel = fs.getFormulaireNodeById(this.__modeleCloisonsSelectId);
+            if (sel) {
+                // on abonne le formulaire aux propriétés du FieldSet
+                fs.properties.addObserver(this);
+            }
+        }
+    }
+
+    public moveFieldsetUp(fs: FieldSet) {
+        if (fs.nub instanceof Structure) {
+            // déplacement du nub
+            fs.nub.parent.moveChildUp(fs.nub);
+            // déplacement du fieldset
+            this.fieldsetContainer.moveFieldsetUp(fs);
+
+            this.resetResults();
+        } else {
+            super.moveFieldsetUp(fs);
+        }
+    }
+
+    public moveFieldsetDown(fs: FieldSet) {
+        if (fs.nub instanceof Structure) {
+            // déplacement du nub
+            fs.nub.parent.moveChildDown(fs.nub);
+            // déplacement du fieldset
+            this.fieldsetContainer.moveFieldsetDown(fs);
+
+            this.resetResults();
+        } else { super.moveFieldsetDown(fs); }
+    }
+
+    public removeFieldset(fs: FieldSet) {
+        if (fs.nub instanceof Structure) {
+            // suppression du sous-nub dans le Nub parent
+            this.deleteNub(fs.nub);
+
+            // suppression du fieldset
+            this.fieldsetContainer.removeFieldset(fs);
+
+            this.resetResults();
+        } else { super.removeFieldset(fs); }
+    }
+
+    protected completeParse(json: {}) {
+        this.subscribeFieldsetContainer();
+    }
+
+    private get fieldsetContainer(): FieldsetContainer {
+        const n = this.getFormulaireNodeById("bassin_container");
+        if (n === undefined || !(n instanceof FieldsetContainer)) {
+            throw new Error("l'élément 'bassin_container' n'est pas du type FieldsetContainer");
+        }
+        return n as FieldsetContainer;
+    }
+
+    /**
+     * abonnement en tant qu'observateur du FieldsetContainer
+     */
+    private subscribeFieldsetContainer() {
+        this.fieldsetContainer.addObserver(this);
+    }
+
+    /**
+     * abonnement en tant qu'observateur des NgParameter des FieldSet contenus dans le FieldsetContainer
+     */
+    private subscribeBasinFields(fs: FieldSet) {
+        for (const n of fs.allFormElements) {
+            if (n instanceof NgParameter || n instanceof SelectField) {
+                n.addObserver(this);
+            }
+        }
+    }
+
+    // interface Observer
+
+    public update(sender: any, data: any) {
+
+        super.update(sender, data);
+
+        if (sender instanceof FieldsetContainer) {
+            switch (data.action) {
+                case "newFieldset":
+                    console.log(">>>>>>>>> yeah new fieldset !", data);
+                    this.reset();
+                    this.subscribeBasinFields(data["fieldset"]);
+            }
+        } else if (sender instanceof FieldSet && data.action === "propertyChange") {
+            console.log(">>>>>>>>> PAB property change !", data);
+            switch (sender.id) {
+                case "fs_bassin":
+                    // @TODO set modelCloisons
+
+                    /* const props = sender.properties;
+                    // ensure loiDebit is set
+                    props.setPropValue("loiDebit", data.value);
+                    this.adjustProperties(props, data["name"], data["value"]);
+                    // replace Structure Nub
+                    const newNub = this.replaceNub((sender.nub as Structure), props);
+                    sender.setNub(newNub);
+                    // treat the fieldset as new to re-subscribe to Nub properties change events
+                    this.afterParseFieldset(sender);
+                    this.reset(); */
+                    break;
+            }
+        }
+    }
+}
diff --git a/src/app/formulaire/definition/concrete/form-parallel-structures.ts b/src/app/formulaire/definition/concrete/form-parallel-structures.ts
index 2d703d832..bc954c296 100644
--- a/src/app/formulaire/definition/concrete/form-parallel-structures.ts
+++ b/src/app/formulaire/definition/concrete/form-parallel-structures.ts
@@ -1,6 +1,5 @@
-import { Structure, Nub, ParallelStructure, LoiDebit, StructureProperties, Props, Session, StructureType } from "jalhyd";
+import { Structure, Nub, ParallelStructure, StructureProperties, Props, Session } from "jalhyd";
 
-import { FormDefParallelStructures } from "../form-def-parallel-structures";
 import { FormComputeParallelStructures } from "../form-compute-parallel-structures";
 import { FormResultFixedVar } from "../form-result-fixedvar";
 import { FieldsetContainer } from "../../fieldset-container";
@@ -13,8 +12,6 @@ import { FormulaireBase } from "./form-base";
 
 export class FormulaireParallelStructure extends FormulaireBase {
 
-    private _formParallelStruct: FormDefParallelStructures;
-
     /**
      * id du select configurant le type d'ouvrage
      */
@@ -23,11 +20,10 @@ export class FormulaireParallelStructure extends FormulaireBase {
     constructor() {
         super();
         this._formResult = new FormResultFixedVar(this, false);
-        this._formParallelStruct = new FormDefParallelStructures();
 
         // remove obsolete observer set by super()
         this.removeObserver(this._formCompute);
-        this._formCompute = new FormComputeParallelStructures(this, this._formParallelStruct, (this._formResult as FormResultFixedVar));
+        this._formCompute = new FormComputeParallelStructures(this, (this._formResult as FormResultFixedVar));
     }
 
     private createStructNub(templ: FieldsetTemplate): Nub {
@@ -171,7 +167,6 @@ export class FormulaireParallelStructure extends FormulaireBase {
     }
 
     protected completeParse(json: {}) {
-        this._formParamCalc.parseOptions(json);
         this.subscribeFieldsetContainer();
     }
 
diff --git a/src/app/formulaire/definition/concrete/form-regime-uniforme.ts b/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
index 9cfd228f1..d7d7c8fbe 100644
--- a/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
+++ b/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
@@ -23,7 +23,6 @@ export class FormulaireRegimeUniforme extends FormulaireBase implements Observer
     }
 
     protected completeParse(json: {}) {
-        this._formParamCalc.parseOptions(json);
         super.completeParse(json);
     }
 
diff --git a/src/app/formulaire/definition/form-compute-pab.ts b/src/app/formulaire/definition/form-compute-pab.ts
new file mode 100644
index 000000000..12bb58180
--- /dev/null
+++ b/src/app/formulaire/definition/form-compute-pab.ts
@@ -0,0 +1,63 @@
+import { ComputeNode, ParamDefinition, PabCloisons, Pab } from "jalhyd";
+
+import { FormComputeFixedVar } from "./form-compute-fixedvar";
+import { FormResultFixedVar } from "./form-result-fixedvar";
+import { FormulaireDefinition } from "./form-definition";
+import { NgParameter } from "../ngparam";
+import { FormulaireNode } from "../formulaire-node";
+import { FieldSet } from "../fieldset";
+import { FieldsetContainer } from "../fieldset-container";
+
+export class FormComputePab extends FormComputeFixedVar {
+
+    constructor(formBase: FormulaireDefinition, formResult: FormResultFixedVar) {
+        super(formBase, formResult);
+    }
+
+    /**
+     * @return dans le cas d'un paramètre de bassin, le FieldSet parent d'un paramètre,
+     * le FieldsetContainer parent du FieldSet
+     * ainsi que l'indice du FieldSet parent dans le FieldsetContainer
+     */
+    private structureParents(p: NgParameter): [FieldsetContainer, FieldSet, number] {
+        const parent1: FormulaireNode = p.parent;
+        if (parent1 !== undefined) {
+            if (parent1 instanceof FieldSet) {
+                const parent2 = parent1.parent;
+                if (parent2 instanceof FieldsetContainer) {
+                    const fsIndex = parent1.indexAsKid();
+                    return [parent2, parent1, fsIndex];
+                }
+            }
+        }
+
+        return [undefined, undefined, -1];
+    }
+
+    /**
+     * construit un identifiant de type { uid: "abcdef", symbol: "X" }
+     * avec "abcdef" l'index du bassin et "X" son paramètre
+     */
+    protected getParameterRefid(p: ParamDefinition): any {
+        const nub = p.parentComputeNode;
+        if (nub instanceof PabCloisons) {
+            return {
+                uid: nub.uid,
+                symbol: p.symbol
+            };
+        } else {
+            return super.getParameterRefid(p);
+        }
+    }
+
+    protected setParameterValue(node: ComputeNode, p: NgParameter, val: number) {
+        const [fsc, fs, i] = this.structureParents(p);
+        if (i === -1) {
+            super.setParameterValue(node, p, val);
+        } else {
+            const n: Pab = node as Pab;
+            const prm = n.children[i].getParameter(p.symbol);
+            prm.v = val;
+        }
+    }
+}
diff --git a/src/app/formulaire/definition/form-compute-parallel-structures.ts b/src/app/formulaire/definition/form-compute-parallel-structures.ts
index 8f169e5e3..e05615dd7 100644
--- a/src/app/formulaire/definition/form-compute-parallel-structures.ts
+++ b/src/app/formulaire/definition/form-compute-parallel-structures.ts
@@ -7,14 +7,11 @@ import { NgParameter } from "../ngparam";
 import { FormulaireNode } from "../formulaire-node";
 import { FieldSet } from "../fieldset";
 import { FieldsetContainer } from "../fieldset-container";
-import { FormDefParallelStructures } from "./form-def-parallel-structures";
 
 export class FormComputeParallelStructures extends FormComputeFixedVar {
-    private _formParallelStruct: FormDefParallelStructures;
 
-    constructor(formBase: FormulaireDefinition, formParallelStruct: FormDefParallelStructures, formResult: FormResultFixedVar) {
+    constructor(formBase: FormulaireDefinition, formResult: FormResultFixedVar) {
         super(formBase, formResult);
-        this._formParallelStruct = formParallelStruct;
     }
 
     /**
diff --git a/src/app/formulaire/definition/form-def-fixedvar.ts b/src/app/formulaire/definition/form-def-fixedvar.ts
deleted file mode 100644
index efad7a577..000000000
--- a/src/app/formulaire/definition/form-def-fixedvar.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { FormulaireDefinition } from "./form-definition";
-
-/**
- * gestion des formulaires avec "paramètre fixé" et "paramètre à varier"
- */
-export class FormDefFixedVar {
-
-    protected _formBase: FormulaireDefinition;
-
-    constructor(base: FormulaireDefinition) {
-        this._formBase = base;
-    }
-
-}
diff --git a/src/app/formulaire/definition/form-def-parallel-structures.ts b/src/app/formulaire/definition/form-def-parallel-structures.ts
deleted file mode 100644
index 5206d9cc0..000000000
--- a/src/app/formulaire/definition/form-def-parallel-structures.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-/**
- * gestion des formulaires "ouvrages parallèles"
- */
-export class FormDefParallelStructures {
-}
diff --git a/src/app/formulaire/definition/form-def-paramcalc.ts b/src/app/formulaire/definition/form-def-paramcalc.ts
deleted file mode 100644
index c16a0397e..000000000
--- a/src/app/formulaire/definition/form-def-paramcalc.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { FormulaireDefinition } from "./form-definition";
-import { FormDefFixedVar } from "./form-def-fixedvar";
-
-/**
- * gestion des formulaires avec "paramètre à calculer" (conduite distributrice, Lechapt-Calmon, régime uniforme, passes à bassin)
- */
-export class FormDefParamToCalculate extends FormDefFixedVar {
-
-    constructor(base: FormulaireDefinition) {
-        super(base);
-    }
-
-    public parseOptions(json: {}) {
-    }
-
-}
diff --git a/src/app/services/formulaire/formulaire.service.ts b/src/app/services/formulaire/formulaire.service.ts
index 8490ef828..78035999c 100644
--- a/src/app/services/formulaire/formulaire.service.ts
+++ b/src/app/services/formulaire/formulaire.service.ts
@@ -3,7 +3,7 @@ import { Injectable } from "@angular/core";
 import { decode } from "he";
 import { saveAs } from "file-saver";
 
-import { CalculatorType, LinkedValue, Observable, ParamDefinition, Session, Nub, ParallelStructure } from "jalhyd";
+import { CalculatorType, LinkedValue, Observable, ParamDefinition, Session, Nub, ParallelStructure, Pab } from "jalhyd";
 
 import { HttpService } from "../../services/http/http.service";
 import { I18nService } from "../../services/internationalisation/internationalisation.service";
@@ -22,6 +22,7 @@ import { NgParameter } from "../../formulaire/ngparam";
 import { FieldsetContainer } from "../..//formulaire/fieldset-container";
 import { ApplicationSetupService } from "../app-setup/app-setup.service";
 import { NotificationsService } from "../notifications/notifications.service";
+import { FormulairePab } from "../../formulaire/definition/concrete/form-pab";
 
 @Injectable()
 export class FormulaireService extends Observable {
@@ -60,6 +61,7 @@ export class FormulaireService extends Observable {
         this.calculatorPaths[CalculatorType.Dever] = "dever";
         this.calculatorPaths[CalculatorType.Cloisons] = "cloisons";
         this.calculatorPaths[CalculatorType.MacroRugo] = "macrorugo";
+        this.calculatorPaths[CalculatorType.Pab] = "pab";
     }
 
     private get _intlService(): I18nService {
@@ -213,6 +215,10 @@ export class FormulaireService extends Observable {
                 f = new FormulaireParallelStructure();
                 break;
 
+            case CalculatorType.Pab:
+                f = new FormulairePab();
+                break;
+
             default:
                 f = new FormulaireBase();
         }
@@ -256,16 +262,30 @@ export class FormulaireService extends Observable {
 
             f.parseConfig();
 
-            // add fieldsets for existing structures if needed
+            // add fieldsets for existing Structures if needed
+            // (when loading session only)
             if (f.currentNub instanceof ParallelStructure) {
-                for (s of f.currentNub.structures) {
+                for (const struct of f.currentNub.structures) {
+                    for (const e of f.allFormElements) {
+                        if (e instanceof FieldsetContainer) { // @TODO manage many containers one day ?
+                            e.addFromTemplate(0, undefined, struct);
+                        }
+                    }
+                }
+            }
+
+            // add fieldsets for existing PabCloisons if needed
+            // (when loading session only)
+            if (f.currentNub instanceof Pab) {
+                for (const child of f.currentNub.children) {
                     for (const e of f.allFormElements) {
                         if (e instanceof FieldsetContainer) { // @TODO manage many containers one day ?
-                            e.addFromTemplate(0, undefined, s);
+                            e.addFromTemplate(0, undefined, child);
                         }
                     }
                 }
             }
+
             return f;
 
         }).then(fi => {
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index 780336339..67d14da0e 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -132,6 +132,8 @@
     "INFO_LIB_PR": "Display accuracy",
     "INFO_LIB_Q": "Discharge",
     "INFO_LIB_SELECT_LOIDEBIT1": "Stage-discharge law",
+    "INFO_LIB_QA": "Attraction flow",
+    "INFO_LIB_SELECT_LOIDEBIT": "Stage-discharge law",
     "INFO_LIB_SELECT_LOIDEBIT1_KIVI": "Kindsvater-Carter and Villemonte",
     "INFO_LIB_SELECT_LOIDEBIT2": "Stage-discharge law",
     "INFO_LIB_SELECT_LOIDEBIT3": "Stage-discharge law",
@@ -176,6 +178,8 @@
     "INFO_OPTION_YES": "Yes",
     "INFO_OPTION_GENERATE": "Generate",
     "INFO_OUVRAGE": "Structure",
+    "INFO_PAB_TITRE": "Fish ladder",
+    "INFO_PAB_TITRE_COURT": "Fish ladder",
     "INFO_PABCHUTE_TITRE": "Fish ladder: fall",
     "INFO_PABCHUTE_TITRE_COURT": "FL: fall",
     "INFO_PABDIMENSIONS_TITRE": "Fish ladder: dimensions",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index 65ee8a16e..a7aaa7a93 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -132,6 +132,8 @@
     "INFO_LIB_PR": "Précision de calcul",
     "INFO_LIB_Q": "Débit",
     "INFO_LIB_SELECT_LOIDEBIT1": "Loi de débit",
+    "INFO_LIB_QA": "Débit d'attrait",
+    "INFO_LIB_SELECT_LOIDEBIT": "Loi de débit",
     "INFO_LIB_SELECT_LOIDEBIT1_KIVI": "Kindsvater-Carter et Villemonte",
     "INFO_LIB_SELECT_LOIDEBIT2": "Loi de débit",
     "INFO_LIB_SELECT_LOIDEBIT3": "Loi de débit",
@@ -177,6 +179,8 @@
     "INFO_OPTION_GENERATE": "Générer",
     "INFO_OUVRAGE": "Ouvrage",
     "INFO_PABCHUTE_TITRE": "Passe à bassins&nbsp;: chute",
+    "INFO_PAB_TITRE": "Passe à bassins",
+    "INFO_PAB_TITRE_COURT": "PAB",
     "INFO_PABCHUTE_TITRE_COURT": "PAB&nbsp;: chute",
     "INFO_PABDIMENSIONS_TITRE": "Passe à bassins&nbsp;: dimensions",
     "INFO_PABDIMENSIONS_TITRE_COURT": "PAB&nbsp;: dimensions",
-- 
GitLab


From 4967992717c3eb82827a41b356d393fa2c377578 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Mon, 6 May 2019 17:46:54 +0200
Subject: [PATCH 08/44] New select field for Cloisons models

---
 src/app/app.module.ts                         |  2 +
 .../field-set/field-set.component.html        |  3 +
 .../field-set/field-set.component.ts          | 18 +++-
 .../calculator.component.ts                   |  2 +
 .../param-link/param-link.component.ts        | 11 +--
 .../select-cloisons-field-line.component.html | 24 +++++
 .../select-cloisons-field-line.component.scss | 33 +++++++
 .../select-cloisons-field-line.component.ts   | 93 +++++++++++++++++++
 .../definition/concrete/form-pab.ts           | 53 ++++++-----
 src/app/formulaire/select-field.ts            | 14 ++-
 10 files changed, 220 insertions(+), 33 deletions(-)
 create mode 100644 src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html
 create mode 100644 src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.scss
 create mode 100644 src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts

diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 3367199a6..40aa77006 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -89,6 +89,7 @@ import {
   JalhydModelValidationStepDirective
 } from "./directives/jalhyd-model-validation.directive";
 import { ImmediateErrorStateMatcher } from "./formulaire/immediate-error-state-matcher";
+import { SelectCloisonsFieldLineComponent } from "./components/select-cloisons-field-line/select-cloisons-field-line.component";
 
 const appRoutes: Routes = [
   { path: "list", component: CalculatorListComponent },
@@ -176,6 +177,7 @@ const appRoutes: Routes = [
     SectionCanvasComponent,
     SectionResultsComponent,
     SelectFieldLineComponent,
+    SelectCloisonsFieldLineComponent,
     VarResultsComponent
   ],
   entryComponents: [
diff --git a/src/app/components/field-set/field-set.component.html b/src/app/components/field-set/field-set.component.html
index 0dea7034b..81765bfc5 100644
--- a/src/app/components/field-set/field-set.component.html
+++ b/src/app/components/field-set/field-set.component.html
@@ -26,5 +26,8 @@
 
         <select-field-line *ngIf="isSelectField(p)" [_select]=p>
         </select-field-line>
+
+        <select-cloisons-field-line *ngIf="isSelectCloisonsField(p)" [_select]=p>
+        </select-cloisons-field-line>
     </ng-template>
 </mat-card-content>
diff --git a/src/app/components/field-set/field-set.component.ts b/src/app/components/field-set/field-set.component.ts
index bb3c51de8..30ae96fea 100644
--- a/src/app/components/field-set/field-set.component.ts
+++ b/src/app/components/field-set/field-set.component.ts
@@ -6,6 +6,7 @@ import { ParamFieldLineComponent } from "../param-field-line/param-field-line.co
 import { Field } from "../../formulaire/field";
 import { InputField } from "../../formulaire/input-field";
 import { SelectField } from "../../formulaire/select-field";
+import { FormulairePab } from "../../formulaire/definition/concrete/form-pab";
 
 @Component({
     selector: "field-set",
@@ -172,7 +173,22 @@ export class FieldSetComponent implements DoCheck {
      * détermine si un Field est du type SelectField
      */
     private isSelectField(f: Field): boolean {
-        return f instanceof SelectField && f.isDisplayed;
+        return (
+            f instanceof SelectField
+            && ! (f.parentForm instanceof FormulairePab)
+            && f.isDisplayed
+        );
+    }
+
+    /**
+     * détermine si un Field est du type SelectField
+     */
+    private isSelectCloisonsField(f: Field): boolean {
+        return (
+            f instanceof SelectField
+            && f.parentForm instanceof FormulairePab
+            && f.id === (f.parentForm as FormulairePab).modeleCloisonsSelectId
+        );
     }
 
     /*
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index e0eafb473..35fb50bb5 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -463,6 +463,8 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
                         }
                     }
                 }
+                // go to new PAB
+                this.router.navigate(["/calculator", f.uid]);
               });
             }
           }
diff --git a/src/app/components/param-link/param-link.component.ts b/src/app/components/param-link/param-link.component.ts
index 7fab3306b..dd211d884 100644
--- a/src/app/components/param-link/param-link.component.ts
+++ b/src/app/components/param-link/param-link.component.ts
@@ -1,7 +1,6 @@
 import { Component, Input, Output, EventEmitter, OnChanges, OnDestroy } from "@angular/core";
 
 import { NgParameter } from "../../formulaire/ngparam";
-import { ServiceFactory } from "../../services/service-factory";
 import { LinkedValue, ParamValueMode, Observer, Structure, acSection } from "jalhyd";
 import { FormulaireService } from "../../services/formulaire/formulaire.service";
 import { I18nService } from "../../services/internationalisation/internationalisation.service";
@@ -40,8 +39,6 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
     /** liste des paramètres liables */
     private _linkableParams: LinkedValue[];
 
-    private _formService: FormulaireService;
-
     public get selectId() {
         let id = "linked_" + this.param.symbol;
         // if inside a nested Structure, prefix with Structure position
@@ -84,11 +81,11 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
     }
 
     constructor(
-        private intlService: I18nService
+        private intlService: I18nService,
+        private formService: FormulaireService
     ) {
         this.valid = new EventEmitter();
-        this._formService = ServiceFactory.instance.formulaireService;
-        this._formService.addObserver(this);
+        this.formService.addObserver(this);
     }
 
     public get linkableParams() {
@@ -254,7 +251,7 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
     public updateParamList() {
         // liste des paramètres liables
         if (this.param.valueMode === ParamValueMode.LINK) {
-            this._linkableParams = this._formService.getLinkableValues(this.param);
+            this._linkableParams = this.formService.getLinkableValues(this.param);
         } else {
             this._linkableParams = [];
         }
diff --git a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html
new file mode 100644
index 000000000..5f8e91036
--- /dev/null
+++ b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html
@@ -0,0 +1,24 @@
+<div class="container" fxLayout="row wrap" fxLayoutAlign="space-between start">
+
+    <!-- input de saisie de la valeur -->
+    <div fxFlex="1 0 120px">
+        
+        <mat-form-field>
+            <mat-select [id]="selectId" [name]="selectId" [placeholder]="label" [(value)]="selectedValue" required>
+                <mat-option *ngFor="let e of entries" [value]="e">
+                    {{ selectItemLabel(e) }}
+                </mat-option>
+            </mat-select>
+        </mat-form-field>
+    </div>
+
+    <div class="button-container" fxFlex="0 0 auto">
+        <button mat-icon-button *ngIf="selectedValue" [routerLink]="['/calculator/', cloisonsUid]">
+            <mat-icon>settings</mat-icon>
+        </button>
+        <button mat-icon-button *ngIf="entries.length === 0" (click)="createCloisons()">
+            <mat-icon>add_circle_outline</mat-icon>
+        </button>
+    </div>
+
+</div>
\ No newline at end of file
diff --git a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.scss b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.scss
new file mode 100644
index 000000000..3e2afd4a8
--- /dev/null
+++ b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.scss
@@ -0,0 +1,33 @@
+mat-form-field {
+    width: calc(100% - 8px);
+    margin-right: 8px;
+
+    mat-select {
+
+        ::ng-deep .mat-select-value {
+            > span {
+                > span {
+                    line-height: 1.3em;
+                }
+            }
+        }
+    }
+
+    ::ng-deep .mat-form-field-label {
+        font-size: 1.1em;
+        line-height: 1.4em;
+        margin-top: -2px;
+
+        &.mat-form-field-empty {
+            font-size: 1em;
+        }
+    }
+}
+
+.button-container {
+    text-align: right;
+
+    button {
+        margin-top: 4px;
+    }
+}
diff --git a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts
new file mode 100644
index 000000000..80612a8bf
--- /dev/null
+++ b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts
@@ -0,0 +1,93 @@
+import { Component, Input, OnInit } from "@angular/core";
+
+import { SelectField } from "../../formulaire/select-field";
+import { SelectEntry } from "../../formulaire/select-entry";
+import { FormulaireService } from "../../services/formulaire/formulaire.service";
+import { Session } from "jalhyd";
+
+@Component({
+    selector: "select-cloisons-field-line",
+    templateUrl: "./select-cloisons-field-line.component.html",
+    styleUrls: [
+        "./select-cloisons-field-line.component.scss"
+    ]
+})
+export class SelectCloisonsFieldLineComponent implements OnInit {
+    @Input()
+    private _select: SelectField;
+
+    public constructor(
+        private _formService: FormulaireService
+    ) {}
+
+    public get selectId() {
+        return this._select.id;
+    }
+
+    public get entries(): SelectEntry[] {
+        if (! this._select) {
+            return [];
+        }
+        return this._select.entries;
+    }
+
+    public get cloisonsUid() {
+        return this.selectedValue ? this.selectedValue.id : "";
+    }
+
+    // called every time we navigate to the module
+    ngOnInit(): void {
+        this.updateAvailableEntries();
+    }
+
+    /**
+     * Populates the select with available Cloisons in the session
+     */
+    public updateAvailableEntries() {
+        // store previous selected entry
+        const pse = this._select.getValue();
+        this._select.clearEntries();
+        // populate with available Cloisons
+        const cloisonsNubs = Session.getInstance().getCloisonsNubs();
+        console.log(">> UAE − cloisons dispo", cloisonsNubs.length, cloisonsNubs);
+        for (const cl of cloisonsNubs) {
+            const e = new SelectEntry(cl.uid, cl);
+            this._select.addEntry(e);
+        }
+        // keep previously selected entry if possible
+        if (pse && pse.id) {
+            this._select.setValueFromId(pse.id);
+        }
+    }
+
+    /**
+     * Label d'une entrée du select des modèles de cloisons
+     */
+    public selectItemLabel(entry: SelectEntry): string {
+        if (entry && entry.id) {
+            const cloisonsForm = this._formService.getFormulaireFromNubId(entry.id);
+            const name = cloisonsForm.calculatorName;
+            return name;
+        }
+    }
+
+    public get selectedValue(): SelectEntry {
+        return this._select.getValue();
+    }
+
+    public set selectedValue(v: SelectEntry) {
+        this._select.setValue(v);
+    }
+
+    public get label() {
+        if (this._select) {
+            return this._select.label;
+        } else {
+            return "";
+        }
+    }
+
+    public createCloisons() {
+        console.log("CREATE CLOISONS !!");
+    }
+}
diff --git a/src/app/formulaire/definition/concrete/form-pab.ts b/src/app/formulaire/definition/concrete/form-pab.ts
index cb329818d..79a3192f4 100644
--- a/src/app/formulaire/definition/concrete/form-pab.ts
+++ b/src/app/formulaire/definition/concrete/form-pab.ts
@@ -1,6 +1,5 @@
-import { Structure, Nub, ParallelStructure, StructureProperties, Props, Session, PabCloisons, Pab } from "jalhyd";
+import { Nub, Props, Session, PabCloisons, Pab } from "jalhyd";
 
-import { FormComputeParallelStructures } from "../form-compute-parallel-structures";
 import { FormResultFixedVar } from "../form-result-fixedvar";
 import { FieldsetContainer } from "../../fieldset-container";
 import { FieldSet } from "../../fieldset";
@@ -9,6 +8,7 @@ import { NgParameter } from "../../ngparam";
 import { FieldsetTemplate } from "../../fieldset-template";
 import { FormulaireNode } from "../../formulaire-node";
 import { FormulaireBase } from "./form-base";
+import { FormComputePab } from "../form-compute-pab";
 
 /**
  * Formulaire pour les passes à bassins, inspiré du formulaire
@@ -17,7 +17,7 @@ import { FormulaireBase } from "./form-base";
 export class FormulairePab extends FormulaireBase {
 
     /** id du select configurant le modèle de cloisons */
-    private __modeleCloisonsSelectId: string;
+    private _modeleCloisonsSelectId: string;
 
     constructor() {
         super();
@@ -25,7 +25,11 @@ export class FormulairePab extends FormulaireBase {
 
         // remove obsolete observer set by super()
         this.removeObserver(this._formCompute);
-        this._formCompute = new FormComputeParallelStructures(this, (this._formResult as FormResultFixedVar));
+        this._formCompute = new FormComputePab(this, (this._formResult as FormResultFixedVar));
+    }
+
+    public get modeleCloisonsSelectId(): string {
+        return this._modeleCloisonsSelectId;
     }
 
     private createPabCloisons(templ: FieldsetTemplate): Nub {
@@ -42,16 +46,15 @@ export class FormulairePab extends FormulaireBase {
     }
 
     /**
-     * ajoute un Nub Structure
-     * @param st structure à ajouter
+     * ajoute un Nub PabCloisons
      * @param after position après laquelle insérer la structure, à la fin sinon
      */
-    private addPabCloisons(st: Structure, after?: number) {
-        this.parallelStructureNub.addChild(st, after);
+    private addPabCloisons(st: PabCloisons, after?: number) {
+        this.pabNub.addChild(st, after);
     }
 
-    private get parallelStructureNub(): ParallelStructure {
-        return this.currentNub as ParallelStructure;
+    private get pabNub(): Pab {
+        return this.currentNub as Pab;
     }
 
     /**
@@ -67,7 +70,7 @@ export class FormulairePab extends FormulaireBase {
      * Replaces the current Nub in the current calculator module, with a new one built with properties "params"
      * @param params properties to build the new Nub (calcType, loiDebit...)
      */
-    protected replaceNub(sn: Structure, params: Props): Nub {
+    protected replaceNub(sn: PabCloisons, params: Props): Nub {
         const parent = (this.currentNub as PabCloisons);
         const newBassin = this.createBassin(params);
         parent.replaceChildInplace(sn, newBassin);
@@ -78,15 +81,13 @@ export class FormulairePab extends FormulaireBase {
      * Deleted the given child Nub in the current calculator module
      * @param params properties to build the new Nub (calcType, loiDebit...)
      */
-    protected deleteNub(sn: Structure) {
+    protected deleteNub(sn: PabCloisons) {
         const parent = (this.currentNub as PabCloisons);
         parent.deleteChild(parent.getIndexForChild(sn));
     }
 
     public createFieldset(parent: FormulaireNode, json: {}, data?: {}, nub?: Nub): FieldSet {
-        console.log("++++++++++ CREATE FIELDSET +++++++++++", parent.constructor.name, json, data, nub ? nub.constructor.name : "zizi");
         if (json["calcType"] === "PabCloisons") {
-            console.log("youpidou");
             // indice après lequel insérer le nouveau FieldSet
             const after = data["after"];
 
@@ -96,7 +97,7 @@ export class FormulairePab extends FormulaireBase {
                 n = nub;
             } else {
                 n = this.createPabCloisons(data["template"]);
-                this.addPabCloisons(n as Structure, after);
+                this.addPabCloisons(n as PabCloisons, after);
             }
             res.setNub(n, false);
 
@@ -116,15 +117,15 @@ export class FormulairePab extends FormulaireBase {
 
     protected parseOptions(json: {}) {
         super.parseOptions(json);
-
         // id du select configurant le type d'ouvrage
-        this.__modeleCloisonsSelectId = this.getOption(json, "ouvrageSelectId");
+        this._modeleCloisonsSelectId = this.getOption(json, "modeleCloisonsSelectId");
     }
 
     public afterParseFieldset(fs: FieldSet) {
         // si le FieldSet contient le select de type d'ouvrage
-        if (this.__modeleCloisonsSelectId) {
-            const sel = fs.getFormulaireNodeById(this.__modeleCloisonsSelectId);
+        if (this._modeleCloisonsSelectId) {
+            const node = fs.getFormulaireNodeById(this._modeleCloisonsSelectId);
+            const sel = (node as SelectField);
             if (sel) {
                 // on abonne le formulaire aux propriétés du FieldSet
                 fs.properties.addObserver(this);
@@ -133,7 +134,7 @@ export class FormulairePab extends FormulaireBase {
     }
 
     public moveFieldsetUp(fs: FieldSet) {
-        if (fs.nub instanceof Structure) {
+        if (fs.nub instanceof PabCloisons) {
             // déplacement du nub
             fs.nub.parent.moveChildUp(fs.nub);
             // déplacement du fieldset
@@ -146,18 +147,20 @@ export class FormulairePab extends FormulaireBase {
     }
 
     public moveFieldsetDown(fs: FieldSet) {
-        if (fs.nub instanceof Structure) {
+        if (fs.nub instanceof PabCloisons) {
             // déplacement du nub
             fs.nub.parent.moveChildDown(fs.nub);
             // déplacement du fieldset
             this.fieldsetContainer.moveFieldsetDown(fs);
 
             this.resetResults();
-        } else { super.moveFieldsetDown(fs); }
+        } else {
+            super.moveFieldsetDown(fs);
+        }
     }
 
     public removeFieldset(fs: FieldSet) {
-        if (fs.nub instanceof Structure) {
+        if (fs.nub instanceof PabCloisons) {
             // suppression du sous-nub dans le Nub parent
             this.deleteNub(fs.nub);
 
@@ -165,7 +168,9 @@ export class FormulairePab extends FormulaireBase {
             this.fieldsetContainer.removeFieldset(fs);
 
             this.resetResults();
-        } else { super.removeFieldset(fs); }
+        } else {
+            super.removeFieldset(fs);
+        }
     }
 
     protected completeParse(json: {}) {
diff --git a/src/app/formulaire/select-field.ts b/src/app/formulaire/select-field.ts
index c60b4dd50..0ae14e633 100644
--- a/src/app/formulaire/select-field.ts
+++ b/src/app/formulaire/select-field.ts
@@ -15,13 +15,17 @@ export class SelectField extends Field {
 
     constructor(parent: FormulaireNode) {
         super(parent);
-        this._entries = [];
+        this.clearEntries();
     }
 
     public get entries() {
         return this._entries;
     }
 
+    public clearEntries() {
+        this._entries = [];
+    }
+
     public addEntry(e: SelectEntry) {
         this._entries.push(e);
         if (! this._selectedEntry) {
@@ -51,6 +55,14 @@ export class SelectField extends Field {
         }
     }
 
+    public setValueFromId(id: string) {
+        for (const e of this._entries) {
+            if (e.id === id) {
+                this.setValue(e);
+            }
+        }
+    }
+
     public getLabel() {
         if (this._selectedEntry) {
             return this._selectedEntry.label;
-- 
GitLab


From a84ac119c92322e61e8b7fc1dc8b42a4691fd559 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Tue, 7 May 2019 09:24:46 +0200
Subject: [PATCH 09/44] =?UTF-8?q?Ajout=20bouton=20pour=20cr=C3=A9er=20un?=
 =?UTF-8?q?=20module=20Cloisons=20depuis=20une=20PAB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../select-cloisons-field-line.component.html |  2 +-
 .../select-cloisons-field-line.component.ts   | 22 ++++++++++++++++---
 2 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html
index 5f8e91036..5e2938657 100644
--- a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html
+++ b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html
@@ -14,7 +14,7 @@
 
     <div class="button-container" fxFlex="0 0 auto">
         <button mat-icon-button *ngIf="selectedValue" [routerLink]="['/calculator/', cloisonsUid]">
-            <mat-icon>settings</mat-icon>
+            <mat-icon>edit</mat-icon>
         </button>
         <button mat-icon-button *ngIf="entries.length === 0" (click)="createCloisons()">
             <mat-icon>add_circle_outline</mat-icon>
diff --git a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts
index 80612a8bf..652f36fbe 100644
--- a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts
+++ b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts
@@ -1,9 +1,12 @@
 import { Component, Input, OnInit } from "@angular/core";
+import { Router } from "@angular/router";
 
 import { SelectField } from "../../formulaire/select-field";
 import { SelectEntry } from "../../formulaire/select-entry";
 import { FormulaireService } from "../../services/formulaire/formulaire.service";
-import { Session } from "jalhyd";
+import { Session, CalculatorType } from "jalhyd";
+import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
+import { FieldsetContainer } from "../../formulaire/fieldset-container";
 
 @Component({
     selector: "select-cloisons-field-line",
@@ -17,7 +20,8 @@ export class SelectCloisonsFieldLineComponent implements OnInit {
     private _select: SelectField;
 
     public constructor(
-        private _formService: FormulaireService
+        private _formService: FormulaireService,
+        private router: Router
     ) {}
 
     public get selectId() {
@@ -88,6 +92,18 @@ export class SelectCloisonsFieldLineComponent implements OnInit {
     }
 
     public createCloisons() {
-        console.log("CREATE CLOISONS !!");
+        const p: Promise<FormulaireDefinition> = this._formService.createFormulaire(CalculatorType.Cloisons);
+        p.then(f => {
+            this.router.navigate(["/calculator", f.uid]);
+            return f;
+        }).then(f => {
+            // on ajoute un ouvrage au module "cloisons"
+            for (const e of f.allFormElements) {
+                if (e instanceof FieldsetContainer) {
+                    e.addFromTemplate(0);
+                    break;
+                }
+            }
+        });
     }
 }
-- 
GitLab


From 252e678140e9e6a02d5933978663388f28b6c8fe Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Tue, 7 May 2019 14:36:43 +0200
Subject: [PATCH 10/44] New select field for Cloisons models (2)

---
 src/app/calculators/pab/pab.config.json       |  2 +-
 .../dialog-generate-pab.component.html        | 12 +--
 .../dialog-generate-pab.component.ts          | 11 ++-
 .../field-set/field-set.component.ts          |  3 +-
 .../fieldset-container.component.ts           |  8 +-
 .../calculator.component.ts                   | 20 +++--
 .../select-cloisons-field-line.component.ts   | 29 ++-----
 src/app/formulaire/fieldset.ts                | 21 +++--
 src/app/formulaire/select-field-cloisons.ts   | 84 +++++++++++++++++++
 src/app/formulaire/select-field.ts            | 39 ++++++---
 10 files changed, 166 insertions(+), 63 deletions(-)
 create mode 100644 src/app/formulaire/select-field-cloisons.ts

diff --git a/src/app/calculators/pab/pab.config.json b/src/app/calculators/pab/pab.config.json
index a869b2b81..295b7cfa0 100644
--- a/src/app/calculators/pab/pab.config.json
+++ b/src/app/calculators/pab/pab.config.json
@@ -31,7 +31,7 @@
         "fields": [
             {
                 "id": "select_modele_cloisons",
-                "type": "select",
+                "type": "select_cloisons",
                 "select": []
             },
             {
diff --git a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.html b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.html
index f404ad363..36b68b2c2 100644
--- a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.html
+++ b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.html
@@ -24,16 +24,6 @@
         </div>
     </mat-error>
 
-    <mat-form-field>
-      <input matInput required [placeholder]="uitextCoteAval" pattern="^-?([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$"
-      [(ngModel)]="coteAval" name="coteAval" #inputCoteAval="ngModel">
-    </mat-form-field>
-    <mat-error *ngIf="inputCoteAval.invalid && (inputCoteAval.dirty || inputCoteAval.touched)">
-        <div *ngIf="inputCoteAval.errors.required || inputCoteAval.errors.pattern">
-            {{ uitextMustBeANumber }}
-        </div>
-    </mat-error>
-
     <mat-form-field>
       <input matInput required [placeholder]="uitextNBBassins" pattern="^[1-9][0-9]*$"
       [(ngModel)]="nbBassins" name="nbBassins" #inputNbBassins="ngModel">
@@ -51,7 +41,7 @@
       {{ uitextCancel }}
     </button>
     <button mat-raised-button type="submit" color="warn" (click)="generatePAB()"
-      [disabled]="(inputDebit.invalid || inputCoteAmont.invalid || inputCoteAval.invalid || inputNbBassins.invalid)">
+      [disabled]="(inputDebit.invalid || inputCoteAmont.invalid || inputNbBassins.invalid)">
       {{ uitextGenerate }}
     </button>
   </div>
diff --git a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
index 3b8683598..0bfec942a 100644
--- a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
+++ b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
@@ -15,7 +15,7 @@ export class DialogGeneratePABComponent {
 
   public coteAmont = 102;
 
-  public coteAval = 99;
+  public chute: number;
 
   public nbBassins = 6;
 
@@ -24,14 +24,19 @@ export class DialogGeneratePABComponent {
     private intlService: I18nService,
     private fb: FormBuilder,
     @Inject(MAT_DIALOG_DATA) public data: any
-  ) { }
+  ) {
+    this.chute = data.chute;
+  }
 
   public generatePAB() {
+    // calculate downstream elevation
+    const coteAval = this.coteAmont - (this.chute * this.nbBassins);
+    // create PAB
     this.dialogRef.close({
       generate: true,
       debit: this.debit,
       coteAmont: this.coteAmont,
-      coteAval: this.coteAval,
+      coteAval: coteAval,
       nbBassins: this.nbBassins
     });
   }
diff --git a/src/app/components/field-set/field-set.component.ts b/src/app/components/field-set/field-set.component.ts
index 30ae96fea..6f59b3505 100644
--- a/src/app/components/field-set/field-set.component.ts
+++ b/src/app/components/field-set/field-set.component.ts
@@ -6,6 +6,7 @@ import { ParamFieldLineComponent } from "../param-field-line/param-field-line.co
 import { Field } from "../../formulaire/field";
 import { InputField } from "../../formulaire/input-field";
 import { SelectField } from "../../formulaire/select-field";
+import { SelectFieldCloisons } from "../../formulaire/select-field-cloisons";
 import { FormulairePab } from "../../formulaire/definition/concrete/form-pab";
 
 @Component({
@@ -185,7 +186,7 @@ export class FieldSetComponent implements DoCheck {
      */
     private isSelectCloisonsField(f: Field): boolean {
         return (
-            f instanceof SelectField
+            f instanceof SelectFieldCloisons
             && f.parentForm instanceof FormulairePab
             && f.id === (f.parentForm as FormulairePab).modeleCloisonsSelectId
         );
diff --git a/src/app/components/fieldset-container/fieldset-container.component.ts b/src/app/components/fieldset-container/fieldset-container.component.ts
index b5e7be12e..3753cc223 100644
--- a/src/app/components/fieldset-container/fieldset-container.component.ts
+++ b/src/app/components/fieldset-container/fieldset-container.component.ts
@@ -64,7 +64,11 @@ export class FieldsetContainerComponent implements DoCheck, AfterViewInit {
     @Output()
     protected tabPressed = new EventEmitter<any>();
 
-    private addStructure(after?: FieldSet) {
+    /**
+     * Ajoute un nouveau sous-nub (Structure, PabCloisons selon le cas)
+     * dans un nouveau fieldset
+     */
+    private addSubNub(after?: FieldSet) {
         if (after) {
             this._container.addFromTemplate(0, after.indexAsKid());
         } else {
@@ -182,7 +186,7 @@ export class FieldsetContainerComponent implements DoCheck, AfterViewInit {
     * réception d'un événement de demande d'ajout d'un FieldSet
     */
     private onAddFieldset(fs: FieldSet) {
-        this.addStructure(fs);
+        this.addSubNub(fs);
     }
 
     /**
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index 35fb50bb5..0976b51a9 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -20,6 +20,7 @@ import { ServiceFactory } from "../../services/service-factory";
 import { MatDialog } from "@angular/material";
 import { DialogConfirmCloseCalcComponent } from "../dialog-confirm-close-calc/dialog-confirm-close-calc.component";
 import { DialogGeneratePABComponent } from "../dialog-generate-pab/dialog-generate-pab.component";
+import { SelectFieldCloisons } from "../../formulaire/select-field-cloisons";
 
 @Component({
     selector: "hydrocalc",
@@ -439,7 +440,12 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
         // création du dialogue de génération d'une passe à bassin
         const dialogRef = this.generatePABDialog.open(
           DialogGeneratePABComponent,
-          { disableClose: true }
+          {
+            data: {
+                chute: (this._formulaire.currentNub as Cloisons).prms.DH.v
+            },
+            disableClose: true
+          }
         );
         dialogRef.afterClosed().subscribe(result => {
           if (result) {
@@ -453,12 +459,16 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
                 params.Z2.singleValue = result.coteAval;
                 // création des bassins
                 for (let i = 0; i < result.nbBassins; i++) {
-                    const modeleCloisons = new PabCloisons((this._formulaire.currentNub as Cloisons));
-                    pab.addChild(modeleCloisons);
+                    const pabCloisons = new PabCloisons(this._formulaire.currentNub as Cloisons);
+                    pab.addChild(pabCloisons); // @TODO should be replace afterwards
                     for (const e of f.allFormElements) {
                         if (e instanceof FieldsetContainer) {
-                            // @TODO how to pass Nub here ? (parameter exists but is never used)
-                            e.addFromTemplate(0, undefined, modeleCloisons);
+                            const newFieldset = e.addFromTemplate(0, undefined, pabCloisons);
+                            // set selected value by ID; nub should be set by "select value changed" event listener
+                            const modeleSelect = (newFieldset.getFormulaireNodeById("select_modele_cloisons") as SelectFieldCloisons);
+                            modeleSelect.updateEntries();
+                            // ID of the Cloisons nub used by pabCloisons as a model
+                            modeleSelect.setValueFromId(this._formulaire.currentNub.uid);
                             break;
                         }
                     }
diff --git a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts
index 652f36fbe..3aa546880 100644
--- a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts
+++ b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts
@@ -1,12 +1,12 @@
 import { Component, Input, OnInit } from "@angular/core";
 import { Router } from "@angular/router";
 
-import { SelectField } from "../../formulaire/select-field";
 import { SelectEntry } from "../../formulaire/select-entry";
 import { FormulaireService } from "../../services/formulaire/formulaire.service";
-import { Session, CalculatorType } from "jalhyd";
+import { CalculatorType } from "jalhyd";
 import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
 import { FieldsetContainer } from "../../formulaire/fieldset-container";
+import { SelectFieldCloisons } from "../../formulaire/select-field-cloisons";
 
 @Component({
     selector: "select-cloisons-field-line",
@@ -17,7 +17,7 @@ import { FieldsetContainer } from "../../formulaire/fieldset-container";
 })
 export class SelectCloisonsFieldLineComponent implements OnInit {
     @Input()
-    private _select: SelectField;
+    private _select: SelectFieldCloisons;
 
     public constructor(
         private _formService: FormulaireService,
@@ -41,27 +41,8 @@ export class SelectCloisonsFieldLineComponent implements OnInit {
 
     // called every time we navigate to the module
     ngOnInit(): void {
-        this.updateAvailableEntries();
-    }
-
-    /**
-     * Populates the select with available Cloisons in the session
-     */
-    public updateAvailableEntries() {
-        // store previous selected entry
-        const pse = this._select.getValue();
-        this._select.clearEntries();
-        // populate with available Cloisons
-        const cloisonsNubs = Session.getInstance().getCloisonsNubs();
-        console.log(">> UAE − cloisons dispo", cloisonsNubs.length, cloisonsNubs);
-        for (const cl of cloisonsNubs) {
-            const e = new SelectEntry(cl.uid, cl);
-            this._select.addEntry(e);
-        }
-        // keep previously selected entry if possible
-        if (pse && pse.id) {
-            this._select.setValueFromId(pse.id);
-        }
+        console.log("------------------------- SF dans le onInit", this._select.constructor.name);
+        this._select.updateEntries();
     }
 
     /**
diff --git a/src/app/formulaire/fieldset.ts b/src/app/formulaire/fieldset.ts
index 3781d0355..41fd6a7ac 100644
--- a/src/app/formulaire/fieldset.ts
+++ b/src/app/formulaire/fieldset.ts
@@ -3,12 +3,11 @@ import { CalculatorType, ComputeNodeType, ParamDefinition, LoiDebit, Props, Obse
 import { FormulaireElement } from "./formulaire-element";
 import { Field } from "./field";
 import { SelectField } from "./select-field";
+import { SelectFieldCloisons } from "./select-field-cloisons";
 import { NgParameter, ParamRadioConfig } from "./ngparam";
-import { ServiceFactory } from "../services/service-factory";
 import { FormulaireDefinition } from "./definition/form-definition";
 import { StringMap } from "../stringmap";
 import { FormulaireNode } from "./formulaire-node";
-import { ApplicationSetupService } from "../services/app-setup/app-setup.service";
 
 export class FieldSet extends FormulaireElement implements Observer {
     /**
@@ -26,11 +25,8 @@ export class FieldSet extends FormulaireElement implements Observer {
      */
     private _jsonConfig: {};
 
-    private _appSetupService: ApplicationSetupService;
-
     constructor(parent: FormulaireNode) {
         super(parent);
-        this._appSetupService = ServiceFactory.instance.applicationSetupService;
     }
 
     public get nub(): Nub {
@@ -81,6 +77,15 @@ export class FieldSet extends FormulaireElement implements Observer {
         return res;
     }
 
+    // non-generic version of parse_select for SelectFieldCloisons because
+    // downcasting is not possible with @Input() apparently
+    private parse_select_cloisons(json: {}): SelectField {
+        const res: SelectFieldCloisons = new SelectFieldCloisons(this);
+        res.parseConfig(json);
+        res.addObserver(this);
+        return res;
+    }
+
     public get properties(): Props {
         return this.nub.properties;
     }
@@ -152,6 +157,9 @@ export class FieldSet extends FormulaireElement implements Observer {
             } else if (field["type"] === "select") {
                 const param = this.parse_select(field);
                 this.addField(param);
+            } else if (field["type"] === "select_cloisons") {
+                const param = this.parse_select_cloisons(field);
+                this.addField(param);
             }
         }
     }
@@ -370,6 +378,9 @@ export class FieldSet extends FormulaireElement implements Observer {
                         case "select_target": // courbes de remous, variable à calculer
                             this.setPropValue("varCalc", data.value.value);
                             break;
+                        case "select_modele_cloisons": // courbes de remous, variable à calculer
+                            this.setPropValue("modeleCloisons", data.value.id);
+                            break;
                     }
                     break;
             }
diff --git a/src/app/formulaire/select-field-cloisons.ts b/src/app/formulaire/select-field-cloisons.ts
new file mode 100644
index 000000000..e9d51d6c2
--- /dev/null
+++ b/src/app/formulaire/select-field-cloisons.ts
@@ -0,0 +1,84 @@
+import { SelectField } from "./select-field";
+import { Session } from "jalhyd";
+import { SelectEntry } from "./select-entry";
+
+/**
+ * A select field that populates itself with references to
+ * available Cloisons modules (used by PAB)
+ */
+export class SelectFieldCloisons extends SelectField {
+
+    /**
+     * Populates entries with available Cloisons
+     */
+    protected populate() {
+        const cloisonsNubs = Session.getInstance().getCloisonsNubs();
+        console.log(">> UAE − cloisons dispo", cloisonsNubs.length, cloisonsNubs);
+        for (const cl of cloisonsNubs) {
+            const e = new SelectEntry(cl.uid, cl);
+            this.addEntry(e);
+        }
+    }
+
+    /**
+     * Reloads available entries, trying to keep the current selected
+     * value; does not notify observers if value did not change
+     */
+    public updateEntries() {
+        console.log("=>>>> updating entries \o/", this.constructor.name);
+        // store previous selected entry
+        const pse = this._selectedEntry;
+        // empty
+        this.clearEntries();
+        // populate
+        this.populate();
+        // keep previously selected entry if possible
+        if (pse && pse.id) {
+            this.setValueFromId(pse.id);
+        }
+    }
+
+    /**
+     * Updates selectedValue; notifies observers only if
+     * value.id has changed
+     */
+    public setValue(v: SelectEntry) {
+        const previousSelectedEntry = this._selectedEntry;
+        this._selectedEntry = v;
+        if (
+            ! previousSelectedEntry
+            || (previousSelectedEntry.id !== v.id)
+        ) {
+            this.notifyObservers({
+                "action": "select",
+                "value": v
+            }, this);
+        }
+    }
+
+    /**
+     * Sets value from given ID; if it was not found, sets the
+     * first available entry as selectedValue
+     */
+    public setValueFromId(id: string) {
+        let found = false;
+        for (const e of this._entries) {
+            if (e.id === id) {
+                found = true;
+                this.setValue(e);
+            }
+        }
+        if (! found) {
+            // default to first available entry if any
+            if (this._entries.length > 0) {
+                this.setValue(this._entries[0]);
+            } else {
+                // notify observers that no value is selected anymore
+                this.notifyObservers({
+                    "action": "select",
+                    "value": undefined
+                }, this);
+            }
+        }
+    }
+}
diff --git a/src/app/formulaire/select-field.ts b/src/app/formulaire/select-field.ts
index 0ae14e633..77178478b 100644
--- a/src/app/formulaire/select-field.ts
+++ b/src/app/formulaire/select-field.ts
@@ -9,9 +9,10 @@ import { StringMap } from "../stringmap";
 import { FormulaireNode } from "./formulaire-node";
 
 export class SelectField extends Field {
-    private _entries: SelectEntry[];
 
-    private _selectedEntry: SelectEntry;
+    protected _entries: SelectEntry[];
+
+    protected _selectedEntry: SelectEntry;
 
     constructor(parent: FormulaireNode) {
         super(parent);
@@ -29,10 +30,34 @@ export class SelectField extends Field {
     public addEntry(e: SelectEntry) {
         this._entries.push(e);
         if (! this._selectedEntry) {
-            this._selectedEntry = e;
+            this.setValue(e);
+        }
+    }
+
+    /**
+     * Reloads available entries, trying to keep the current selected
+     * value; should not notify observers if value did not change
+     */
+    public updateEntries() {
+        // store previous selected entry
+        const pse = this._selectedEntry;
+        this._selectedEntry = undefined;
+        // empty
+        this.clearEntries();
+        // populate
+        this.populate();
+        // keep previously selected entry if possible
+        if (pse) {
+            this.setValue(pse);
         }
     }
 
+    /**
+     * Empties then refills entries list with available entries; does
+     * nothing by default (to be overloaded)
+     */
+    protected populate() { }
+
     public getSelectedEntryFromValue(val: any): SelectEntry {
         for (const se of this._entries) {
             if (se.value === val) {
@@ -55,14 +80,6 @@ export class SelectField extends Field {
         }
     }
 
-    public setValueFromId(id: string) {
-        for (const e of this._entries) {
-            if (e.id === id) {
-                this.setValue(e);
-            }
-        }
-    }
-
     public getLabel() {
         if (this._selectedEntry) {
             return this._selectedEntry.label;
-- 
GitLab


From 9ce666c956b5f591df86259a1ef532c9ded6fffc Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 9 May 2019 09:57:35 +0200
Subject: [PATCH 11/44] =?UTF-8?q?Changement=20de=20mod=C3=A8le=20dans=20le?=
 =?UTF-8?q?s=20PAB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../calculator-list.component.ts              |  2 +-
 .../select-cloisons-field-line.component.html |  2 +-
 .../select-cloisons-field-line.component.ts   |  1 -
 .../definition/concrete/form-pab.ts           | 59 +++++++++++--------
 src/app/formulaire/fieldset.ts                |  2 +-
 5 files changed, 37 insertions(+), 29 deletions(-)

diff --git a/src/app/components/calculator-list/calculator-list.component.ts b/src/app/components/calculator-list/calculator-list.component.ts
index dfbce4f92..f05e933f0 100644
--- a/src/app/components/calculator-list/calculator-list.component.ts
+++ b/src/app/components/calculator-list/calculator-list.component.ts
@@ -76,7 +76,7 @@ export class CalculatorListComponent implements OnInit {
                 };
 
                 for (const t of unusedCalculators) {
-                    if ( // those 2 sub-Nub types cannot be built outside a parent
+                    if ( // those sub-Nub types cannot be built outside a parent
                         t !== CalculatorType.Structure
                         && t !== CalculatorType.Section
                         && t !== CalculatorType.PabCloisons
diff --git a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html
index 5e2938657..de6505d96 100644
--- a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html
+++ b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html
@@ -17,7 +17,7 @@
             <mat-icon>edit</mat-icon>
         </button>
         <button mat-icon-button *ngIf="entries.length === 0" (click)="createCloisons()">
-            <mat-icon>add_circle_outline</mat-icon>
+            <mat-icon>add</mat-icon>
         </button>
     </div>
 
diff --git a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts
index 3aa546880..edfd03e33 100644
--- a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts
+++ b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts
@@ -41,7 +41,6 @@ export class SelectCloisonsFieldLineComponent implements OnInit {
 
     // called every time we navigate to the module
     ngOnInit(): void {
-        console.log("------------------------- SF dans le onInit", this._select.constructor.name);
         this._select.updateEntries();
     }
 
diff --git a/src/app/formulaire/definition/concrete/form-pab.ts b/src/app/formulaire/definition/concrete/form-pab.ts
index 79a3192f4..59cd8f998 100644
--- a/src/app/formulaire/definition/concrete/form-pab.ts
+++ b/src/app/formulaire/definition/concrete/form-pab.ts
@@ -1,4 +1,4 @@
-import { Nub, Props, Session, PabCloisons, Pab } from "jalhyd";
+import { Nub, Props, Session, PabCloisons, Pab, Cloisons } from "jalhyd";
 
 import { FormResultFixedVar } from "../form-result-fixedvar";
 import { FieldsetContainer } from "../../fieldset-container";
@@ -32,16 +32,13 @@ export class FormulairePab extends FormulaireBase {
         return this._modeleCloisonsSelectId;
     }
 
-    private createPabCloisons(templ: FieldsetTemplate): Nub {
-        // !!! attention !!!
-        // Il doit y avoir cohérence dans le fichier de conf entre les valeurs defaultXXX et les valeurs possibles pour les select
-        // cad valeur par défaut du 1er select (type d'ouvrage), du 2ème (loi de débit).
-        // A terme, il faudrait analyser le fichier de conf (dépendances d'existence) pour déterminer automatiquement ces valeurs
-
+    /**
+     * Creates a virgin PabCloisons when a new fieldset is added through the GUI,
+     * to ensure consistency; this object is not related to any Cloisons
+     */
+    private createDummyPabCloisons(templ: FieldsetTemplate): Nub {
         const params = {};
         params["calcType"] = templ.calcTypeFromConfig;
-        params["nodeType"] = templ.defaultNodeTypeFromConfig;
-
         return this.createBassin(new Props(params));
     }
 
@@ -71,7 +68,7 @@ export class FormulairePab extends FormulaireBase {
      * @param params properties to build the new Nub (calcType, loiDebit...)
      */
     protected replaceNub(sn: PabCloisons, params: Props): Nub {
-        const parent = (this.currentNub as PabCloisons);
+        const parent = (this.currentNub as Pab);
         const newBassin = this.createBassin(params);
         parent.replaceChildInplace(sn, newBassin);
         return newBassin;
@@ -96,7 +93,7 @@ export class FormulairePab extends FormulaireBase {
             if (nub) { // use existing Nub (build interface based on model)
                 n = nub;
             } else {
-                n = this.createPabCloisons(data["template"]);
+                n = this.createDummyPabCloisons(data["template"]);
                 this.addPabCloisons(n as PabCloisons, after);
             }
             res.setNub(n, false);
@@ -212,28 +209,40 @@ export class FormulairePab extends FormulaireBase {
         if (sender instanceof FieldsetContainer) {
             switch (data.action) {
                 case "newFieldset":
-                    console.log(">>>>>>>>> yeah new fieldset !", data);
                     this.reset();
                     this.subscribeBasinFields(data["fieldset"]);
             }
         } else if (sender instanceof FieldSet && data.action === "propertyChange") {
-            console.log(">>>>>>>>> PAB property change !", data);
             switch (sender.id) {
                 case "fs_bassin":
-                    // @TODO set modelCloisons
-
-                    /* const props = sender.properties;
-                    // ensure loiDebit is set
-                    props.setPropValue("loiDebit", data.value);
-                    this.adjustProperties(props, data["name"], data["value"]);
-                    // replace Structure Nub
-                    const newNub = this.replaceNub((sender.nub as Structure), props);
-                    sender.setNub(newNub);
-                    // treat the fieldset as new to re-subscribe to Nub properties change events
-                    this.afterParseFieldset(sender);
-                    this.reset(); */
+                    switch (data.name) {
+                        case "modeleCloisons":
+                            // change PabCloisons Nub property "modeleCloisons" and reinit it
+                            // with new Cloisons values
+                            const newModeleUID = sender.properties.getPropValue("modeleCloisons");
+                            sender.nub.properties.setPropValue("modeleCloisons", newModeleUID);
+                            const cloisons = (Session.getInstance().findNubByUid(newModeleUID) as Cloisons);
+                            (sender.nub as PabCloisons).initModelCloisons(cloisons);
+                            // treat the fieldset as new to re-subscribe to Nub properties change events
+                            this.afterParseFieldset(sender);
+                            // this.reset();
+
+                            // console.log(this.currentNub);
+                            this.dumpPabStructure(this.currentNub as Pab);
+                            break;
+                    }
                     break;
             }
         }
     }
+
+    private dumpPabStructure(pab: Pab) {
+        console.log(`PAB: ${pab.uid}, ${pab.children.length} children`);
+        for (const c of pab.children) {
+            console.log(
+                ` * child: ${c.uid}, based on ${c.properties.getPropValue("modeleCloisons")}`
+                + ` (cote amont ${c.prms.Z1.singleValue}, longueur ${c.prms.LB.singleValue})`
+            );
+        }
+    }
 }
diff --git a/src/app/formulaire/fieldset.ts b/src/app/formulaire/fieldset.ts
index 41fd6a7ac..797be11e5 100644
--- a/src/app/formulaire/fieldset.ts
+++ b/src/app/formulaire/fieldset.ts
@@ -378,7 +378,7 @@ export class FieldSet extends FormulaireElement implements Observer {
                         case "select_target": // courbes de remous, variable à calculer
                             this.setPropValue("varCalc", data.value.value);
                             break;
-                        case "select_modele_cloisons": // courbes de remous, variable à calculer
+                        case "select_modele_cloisons": // passe à bassins, modèle de cloisons
                             this.setPropValue("modeleCloisons", data.value.id);
                             break;
                     }
-- 
GitLab


From 198cc12d576b08cd07276ab48e67a5fa384b538f Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 9 May 2019 11:29:59 +0200
Subject: [PATCH 12/44] Update fixPAB

---
 src/app/formulaire/definition/concrete/form-pab.ts | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/app/formulaire/definition/concrete/form-pab.ts b/src/app/formulaire/definition/concrete/form-pab.ts
index 59cd8f998..ebd6fa413 100644
--- a/src/app/formulaire/definition/concrete/form-pab.ts
+++ b/src/app/formulaire/definition/concrete/form-pab.ts
@@ -220,9 +220,7 @@ export class FormulairePab extends FormulaireBase {
                             // change PabCloisons Nub property "modeleCloisons" and reinit it
                             // with new Cloisons values
                             const newModeleUID = sender.properties.getPropValue("modeleCloisons");
-                            sender.nub.properties.setPropValue("modeleCloisons", newModeleUID);
-                            const cloisons = (Session.getInstance().findNubByUid(newModeleUID) as Cloisons);
-                            (sender.nub as PabCloisons).initModelCloisons(cloisons);
+                            (sender.nub as PabCloisons).setModel(newModeleUID);
                             // treat the fieldset as new to re-subscribe to Nub properties change events
                             this.afterParseFieldset(sender);
                             // this.reset();
-- 
GitLab


From 649fa7ccc092dc8eaeb7a52f5c3f70e51a372272 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 9 May 2019 12:08:29 +0200
Subject: [PATCH 13/44] =?UTF-8?q?Chargement=20de=20session=20:=20d=C3=A9te?=
 =?UTF-8?q?ction=20des=20d=C3=A9pendances=20des=20PAB=20aux=20Cloisons?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../dialog-load-session.component.html              |  2 +-
 .../dialog-load-session.component.ts                | 13 +++++++++----
 src/app/formulaire/select-field-cloisons.ts         |  3 +--
 src/app/services/formulaire/formulaire.service.ts   | 12 +++++++++++-
 4 files changed, 22 insertions(+), 8 deletions(-)

diff --git a/src/app/components/dialog-load-session/dialog-load-session.component.html b/src/app/components/dialog-load-session/dialog-load-session.component.html
index 5a560987c..ff34d88ed 100644
--- a/src/app/components/dialog-load-session/dialog-load-session.component.html
+++ b/src/app/components/dialog-load-session/dialog-load-session.component.html
@@ -13,7 +13,7 @@
     </mat-form-field>
 
     <div class="cb-container">
-      <mat-checkbox [name]="c.uid" *ngFor="let c of calculators" (change)="checkLinkedParamsDependencies()"
+      <mat-checkbox [name]="c.uid" *ngFor="let c of calculators" (change)="checkLinkedParamsAndModelsDependencies()"
         [(ngModel)]="c.selected" [ngModelOptions]="{standalone: true}">
         {{ c.title }}
       </mat-checkbox>
diff --git a/src/app/components/dialog-load-session/dialog-load-session.component.ts b/src/app/components/dialog-load-session/dialog-load-session.component.ts
index 18a61355a..5d9014430 100644
--- a/src/app/components/dialog-load-session/dialog-load-session.component.ts
+++ b/src/app/components/dialog-load-session/dialog-load-session.component.ts
@@ -40,7 +40,7 @@ export class DialogLoadSessionComponent {
         c.selected = true;
       }
       // re-run dependency checking
-      this.checkLinkedParamsDependencies();
+      this.checkLinkedParamsAndModelsDependencies();
     }
 
     public selectNone() {
@@ -48,10 +48,15 @@ export class DialogLoadSessionComponent {
         c.selected = false;
       }
       // re-run dependency checking
-      this.checkLinkedParamsDependencies();
+      this.checkLinkedParamsAndModelsDependencies();
     }
 
-    public checkLinkedParamsDependencies() {
+    /**
+     * Checks the dependencies between Nubs :
+     *  - linked params depend on their targets
+     *  - PabCloisons depend on their models
+     */
+    public checkLinkedParamsAndModelsDependencies() {
       this.dependenciesProblems = [];
       // for all checked Nubs
       this.calculators.forEach((c) => {
@@ -116,7 +121,7 @@ export class DialogLoadSessionComponent {
         }
       });
       // re-run dependency checking
-      this.checkLinkedParamsDependencies();
+      this.checkLinkedParamsAndModelsDependencies();
     }
 
     public onFileSelected(event: any) {
diff --git a/src/app/formulaire/select-field-cloisons.ts b/src/app/formulaire/select-field-cloisons.ts
index e9d51d6c2..4d7d8556b 100644
--- a/src/app/formulaire/select-field-cloisons.ts
+++ b/src/app/formulaire/select-field-cloisons.ts
@@ -13,7 +13,7 @@ export class SelectFieldCloisons extends SelectField {
      */
     protected populate() {
         const cloisonsNubs = Session.getInstance().getCloisonsNubs();
-        console.log(">> UAE − cloisons dispo", cloisonsNubs.length, cloisonsNubs);
+        console.log(">> UAE populating entries − cloisons dispo", cloisonsNubs.length, cloisonsNubs);
         for (const cl of cloisonsNubs) {
             const e = new SelectEntry(cl.uid, cl);
             this.addEntry(e);
@@ -25,7 +25,6 @@ export class SelectFieldCloisons extends SelectField {
      * value; does not notify observers if value did not change
      */
     public updateEntries() {
-        console.log("=>>>> updating entries \o/", this.constructor.name);
         // store previous selected entry
         const pse = this._selectedEntry;
         // empty
diff --git a/src/app/services/formulaire/formulaire.service.ts b/src/app/services/formulaire/formulaire.service.ts
index 78035999c..04c1ba9dd 100644
--- a/src/app/services/formulaire/formulaire.service.ts
+++ b/src/app/services/formulaire/formulaire.service.ts
@@ -485,7 +485,7 @@ export class FormulaireService extends Observable {
     }
 
     /**
-     * obtient des infos (nom, uid des modules de calcul) d'un fichier session
+     * obtient des infos (nom, uid des modules de calcul, dépendances) d'un fichier session
      * @param f fichier session
      */
     public calculatorInfosFromSessionFile(f: File): Promise<any[]> {
@@ -510,6 +510,16 @@ export class FormulaireService extends Observable {
                             }
                         });
                     }
+                    // list Cloisons models dependencies for each PAB Nub
+                    if (e.props.calcType === CalculatorType.Pab) {
+                        e.children.forEach((c) => {
+                            if (c.props.calcType === CalculatorType.PabCloisons) { // who knows ?
+                                if (c.props.modeleCloisons && ! nubInfo.requires.includes(c.props.modeleCloisons)) {
+                                    nubInfo.requires.push(c.props.modeleCloisons);
+                                }
+                            }
+                        });
+                    }
                     // list children nubs for each Nub
                     if (e.children) {
                         e.children.forEach((p) => {
-- 
GitLab


From c6307f069f078192b9a94da60b842c9537aaf856 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 9 May 2019 12:26:05 +0200
Subject: [PATCH 14/44] =?UTF-8?q?Modale=20de=20cr=C3=A9ation=20de=20PAB=20?=
 =?UTF-8?q?:=20copie=20de=20Q=20et=20Z1=20depuis=20le=20module=20Cloisons?=
 =?UTF-8?q?=20source?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../dialog-generate-pab/dialog-generate-pab.component.ts     | 2 ++
 .../components/generic-calculator/calculator.component.ts    | 5 ++++-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
index 0bfec942a..a0f55835b 100644
--- a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
+++ b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
@@ -25,6 +25,8 @@ export class DialogGeneratePABComponent {
     private fb: FormBuilder,
     @Inject(MAT_DIALOG_DATA) public data: any
   ) {
+    this.coteAmont = data.coteAmont;
+    this.debit = data.debit;
     this.chute = data.chute;
   }
 
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index 0976b51a9..0364e5325 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -438,11 +438,14 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
      */
     public generatePAB() {
         // création du dialogue de génération d'une passe à bassin
+        const cloisons = (this._formulaire.currentNub as Cloisons);
         const dialogRef = this.generatePABDialog.open(
           DialogGeneratePABComponent,
           {
             data: {
-                chute: (this._formulaire.currentNub as Cloisons).prms.DH.v
+                chute: cloisons.prms.DH.singleValue,
+                debit: cloisons.prms.Q.singleValue,
+                coteAmont: cloisons.prms.Z1.singleValue
             },
             disableClose: true
           }
-- 
GitLab


From 3f2a6bcab3f03ca628adb4696df7af6184fdf0a5 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 9 May 2019 15:53:42 +0200
Subject: [PATCH 15/44] =?UTF-8?q?PAB:=20chargement=20du=20<select>=20avec?=
 =?UTF-8?q?=20la=20valeur=20de=20la=20propri=C3=A9t=C3=A9=20modeleCloisons?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/app/formulaire/definition/concrete/form-pab.ts |  3 +--
 src/app/formulaire/fieldset.ts                     |  6 +++++-
 src/app/formulaire/select-field-cloisons.ts        | 12 ++++++++++++
 3 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/src/app/formulaire/definition/concrete/form-pab.ts b/src/app/formulaire/definition/concrete/form-pab.ts
index ebd6fa413..adcce8c45 100644
--- a/src/app/formulaire/definition/concrete/form-pab.ts
+++ b/src/app/formulaire/definition/concrete/form-pab.ts
@@ -223,9 +223,8 @@ export class FormulairePab extends FormulaireBase {
                             (sender.nub as PabCloisons).setModel(newModeleUID);
                             // treat the fieldset as new to re-subscribe to Nub properties change events
                             this.afterParseFieldset(sender);
-                            // this.reset();
+                            this.reset();
 
-                            // console.log(this.currentNub);
                             this.dumpPabStructure(this.currentNub as Pab);
                             break;
                     }
diff --git a/src/app/formulaire/fieldset.ts b/src/app/formulaire/fieldset.ts
index 797be11e5..1a0806deb 100644
--- a/src/app/formulaire/fieldset.ts
+++ b/src/app/formulaire/fieldset.ts
@@ -379,7 +379,11 @@ export class FieldSet extends FormulaireElement implements Observer {
                             this.setPropValue("varCalc", data.value.value);
                             break;
                         case "select_modele_cloisons": // passe à bassins, modèle de cloisons
-                            this.setPropValue("modeleCloisons", data.value.id);
+                            let valToSet;
+                            if (data.value) {
+                                valToSet = data.value.id;
+                            }
+                            this.setPropValue("modeleCloisons", valToSet);
                             break;
                     }
                     break;
diff --git a/src/app/formulaire/select-field-cloisons.ts b/src/app/formulaire/select-field-cloisons.ts
index 4d7d8556b..39b4da2bf 100644
--- a/src/app/formulaire/select-field-cloisons.ts
+++ b/src/app/formulaire/select-field-cloisons.ts
@@ -1,6 +1,8 @@
 import { SelectField } from "./select-field";
 import { Session } from "jalhyd";
 import { SelectEntry } from "./select-entry";
+import { FormulaireNode } from "./formulaire-node";
+import { FieldSet } from "./fieldset";
 
 /**
  * A select field that populates itself with references to
@@ -8,6 +10,16 @@ import { SelectEntry } from "./select-entry";
  */
 export class SelectFieldCloisons extends SelectField {
 
+    constructor(parent: FormulaireNode) {
+        super(parent);
+        if (this.parent instanceof FieldSet) {
+            const mc = this.parent.nub.properties.getPropValue("modeleCloisons");
+            if (mc) {
+                this._selectedEntry = new SelectEntry(mc, {});
+            } // else if current model is undefined, do not select it so that default Cloisons will be chosen (if available)
+        }
+    }
+
     /**
      * Populates entries with available Cloisons
      */
-- 
GitLab


From 50d3ecfb9416279a002f65e591c001feb8238860 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 9 May 2019 15:54:14 +0200
Subject: [PATCH 16/44] Suppression message debug

---
 src/app/formulaire/select-field-cloisons.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/app/formulaire/select-field-cloisons.ts b/src/app/formulaire/select-field-cloisons.ts
index 39b4da2bf..6c85c9480 100644
--- a/src/app/formulaire/select-field-cloisons.ts
+++ b/src/app/formulaire/select-field-cloisons.ts
@@ -25,7 +25,6 @@ export class SelectFieldCloisons extends SelectField {
      */
     protected populate() {
         const cloisonsNubs = Session.getInstance().getCloisonsNubs();
-        console.log(">> UAE populating entries − cloisons dispo", cloisonsNubs.length, cloisonsNubs);
         for (const cl of cloisonsNubs) {
             const e = new SelectEntry(cl.uid, cl);
             this.addEntry(e);
-- 
GitLab


From 9491cbbff82eee33c4dc2bcc77a4b79b4715c9f0 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 9 May 2019 15:58:03 +0200
Subject: [PATCH 17/44] =?UTF-8?q?Ajout=20de=20type=3D"button"=20sur=20les?=
 =?UTF-8?q?=20boutons=20du=20s=C3=A9lecteur=20de=20cloisons?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../select-cloisons-field-line.component.html                 | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html
index de6505d96..a593d8224 100644
--- a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html
+++ b/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html
@@ -13,10 +13,10 @@
     </div>
 
     <div class="button-container" fxFlex="0 0 auto">
-        <button mat-icon-button *ngIf="selectedValue" [routerLink]="['/calculator/', cloisonsUid]">
+        <button type="button" mat-icon-button *ngIf="selectedValue" [routerLink]="['/calculator/', cloisonsUid]">
             <mat-icon>edit</mat-icon>
         </button>
-        <button mat-icon-button *ngIf="entries.length === 0" (click)="createCloisons()">
+        <button type="button" mat-icon-button *ngIf="entries.length === 0" (click)="createCloisons()">
             <mat-icon>add</mat-icon>
         </button>
     </div>
-- 
GitLab


From b51f4f1a2697048fc38bcef13c35789e7930d232 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 9 May 2019 16:42:27 +0200
Subject: [PATCH 18/44] Updated e2e tests

---
 e2e/calculate-all-params.e2e-spec.ts | 2 +-
 e2e/calculator.po.ts                 | 2 +-
 e2e/clone-all-calc.e2e-spec.ts       | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/e2e/calculate-all-params.e2e-spec.ts b/e2e/calculate-all-params.e2e-spec.ts
index 49d6cc12c..ca53ff2ba 100644
--- a/e2e/calculate-all-params.e2e-spec.ts
+++ b/e2e/calculate-all-params.e2e-spec.ts
@@ -15,7 +15,7 @@ describe("ngHyd − calculate all parameters of all calculators", () => {
   });
 
   // get calculators list (IDs) @TODO read it from config !
-  const calcTypes = [ 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11 ];
+  const calcTypes = [ 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 15 ];
 
   // for each calculator
   for (const ct of calcTypes) {
diff --git a/e2e/calculator.po.ts b/e2e/calculator.po.ts
index 3c84b64f6..dedd1ba01 100644
--- a/e2e/calculator.po.ts
+++ b/e2e/calculator.po.ts
@@ -147,7 +147,7 @@ export class CalculatorPage {
     await inputs.each(async (i) => {
       const inputId = await i.getAttribute("id");
       const inputValue = await i.getAttribute("value");
-      values[inputId] = inputValue;
+      values[inputId] = +inputValue; // cast to number to avoid false negative (integers starting with 0)
     });
     return values;
   }
diff --git a/e2e/clone-all-calc.e2e-spec.ts b/e2e/clone-all-calc.e2e-spec.ts
index 69ab36417..a6e46288c 100644
--- a/e2e/clone-all-calc.e2e-spec.ts
+++ b/e2e/clone-all-calc.e2e-spec.ts
@@ -18,7 +18,7 @@ describe("ngHyd − clone all calculators with all possible <select> values", ()
   });
 
   // get calculators list (IDs) @TODO read it from config !
-  const calcTypes = [ 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11 ];
+  const calcTypes = [ 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 15 ];
 
   // for each calculator
   for (const ct of calcTypes) {
-- 
GitLab


From 93655a6327e18cd95e06f8fd1b59b69f826ba7af Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Fri, 10 May 2019 10:08:08 +0200
Subject: [PATCH 19/44] =?UTF-8?q?Mise=20=C3=A0=20jour=20tests=20e2e=20pour?=
 =?UTF-8?q?=20les=20PAB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 e2e/calculator.po.ts                          |   4 +
 e2e/pab.e2e-spec.ts                           | 219 ++++++++++++
 e2e/session-pab-mauvais-ordre.json            | 327 ++++++++++++++++++
 e2e/session-pab-modeles-vides.json            |  84 +++++
 e2e/session-pab.json                          |   1 +
 .../dialog-generate-pab.component.html        |   8 +-
 6 files changed, 639 insertions(+), 4 deletions(-)
 create mode 100644 e2e/pab.e2e-spec.ts
 create mode 100644 e2e/session-pab-mauvais-ordre.json
 create mode 100644 e2e/session-pab-modeles-vides.json
 create mode 100644 e2e/session-pab.json

diff --git a/e2e/calculator.po.ts b/e2e/calculator.po.ts
index dedd1ba01..660aa1e1d 100644
--- a/e2e/calculator.po.ts
+++ b/e2e/calculator.po.ts
@@ -45,6 +45,10 @@ export class CalculatorPage {
     return element(by.css("button#trigger-calculate"));
   }
 
+  getGeneratePabButton() {
+    return element(by.css("button#generate-pab"));
+  }
+
   getCheckedCalcModeButtons() {
     // tslint:disable-next-line:quotemark
     return element.all(by.css('mat-button-toggle.radio_cal[ng-reflect-checked="true"]'));
diff --git a/e2e/pab.e2e-spec.ts b/e2e/pab.e2e-spec.ts
new file mode 100644
index 000000000..eeb3ae5ce
--- /dev/null
+++ b/e2e/pab.e2e-spec.ts
@@ -0,0 +1,219 @@
+import { ListPage } from "./list.po";
+import { CalculatorPage } from "./calculator.po";
+import { Navbar } from "./navbar.po";
+import { browser, by, element } from "protractor";
+import { AppPage } from "./app.po";
+import { SideNav } from "./sidenav.po";
+
+/**
+ * Clone calculators
+ */
+describe("ngHyd − Passe à Bassins", () => {
+  let startPage: AppPage;
+  let listPage: ListPage;
+  let calcPage: CalculatorPage;
+  let navbar: Navbar;
+  let sidenav: SideNav;
+
+  beforeEach(() => {
+    startPage = new AppPage();
+    listPage = new ListPage();
+    calcPage = new CalculatorPage();
+    navbar = new Navbar();
+    sidenav = new SideNav();
+  });
+
+  describe("create PAB - ", async () => {
+
+    it("when PAB is created after Cloisons", async() => {
+      // create Cloisons
+      await startPage.navigateTo();
+      await listPage.clickMenuEntryForCalcType(10);
+      // create PAB
+      await navbar.clickNewCalculatorButton();
+      await listPage.clickMenuEntryForCalcType(15);
+      // check number of basins
+      const innerFieldsets = element.all(by.css(".fieldset-inner"));
+      expect(await innerFieldsets.count()).toBe(1);
+      // check <select> value
+      const smc = calcPage.getSelectById("select_modele_cloisons");
+      const v = await calcPage.getSelectValueText(smc);
+      expect(v).toEqual("Cloisons");
+    });
+
+    it("when PAB is created before Cloisons", async() => {
+      // create PAB
+      await startPage.navigateTo();
+      await listPage.clickMenuEntryForCalcType(15);
+      // create Cloisons
+      await navbar.clickNewCalculatorButton();
+      await listPage.clickMenuEntryForCalcType(10);
+      // check number of basins
+      await navbar.clickCalculatorTab(0);
+      const innerFieldsets = element.all(by.css(".fieldset-inner"));
+      expect(await innerFieldsets.count()).toBe(1);
+      // check <select> value
+      const smc = calcPage.getSelectById("select_modele_cloisons");
+      const v = await calcPage.getSelectValueText(smc);
+      expect(v).toEqual("Cloisons");
+    });
+
+  });
+
+  describe("generate PAB - ", async () => {
+
+    it("from a Cloisons among many", async() => {
+      // create many Cloisons
+      await startPage.navigateTo();
+      await listPage.clickMenuEntryForCalcType(10);
+      await navbar.clickNewCalculatorButton();
+      await listPage.clickMenuEntryForCalcType(10);
+      await navbar.clickNewCalculatorButton();
+      await listPage.clickMenuEntryForCalcType(10);
+      // choose one of them and change its parameters
+      await navbar.clickCalculatorTab(1);
+      const Z1 = calcPage.getInputById("Z1");
+      await Z1.clear();
+      await Z1.sendKeys("114");
+      const LB = calcPage.getInputById("LB");
+      await LB.clear();
+      await LB.sendKeys("11.5");
+      const DH = calcPage.getInputById("DH");
+      await DH.clear();
+      await DH.sendKeys("0.72");
+      // create PAB from it, changing modal parameters
+      const genButton = calcPage.getGeneratePabButton();
+      await genButton.click();
+      const debit = calcPage.getInputById("generatePabDebit");
+      expect(await debit.getAttribute("value")).toBe("1.5");
+      await debit.clear();
+      await debit.sendKeys("1.");
+      await debit.sendKeys("6"); // send "1.6" in 2 movements, because "." triggers an error that interrupts sendKeys() !
+      const coteAmont = calcPage.getInputById("generatePabCoteAmont");
+      expect(await coteAmont.getAttribute("value")).toBe("114");
+      await coteAmont.clear();
+      await coteAmont.sendKeys("115");
+      const nbBassins = calcPage.getInputById("generatePabNbBassins");
+      expect(await nbBassins.getAttribute("value")).toBe("6");
+      await nbBassins.clear();
+      await nbBassins.sendKeys("5");
+      // click "Generate"
+      await element(by.css("dialog-generate-pab button#do-generate")).click();
+      await browser.sleep(1000);
+      // check parameters values
+      const P_Q = calcPage.getInputById("Q");
+      expect(await P_Q.getAttribute("value")).toBe("1.6");
+      const P_Z2 = calcPage.getInputById("Z2");
+      expect(await P_Z2.getAttribute("value")).toBe("111.4");
+      // check number of basins
+      const innerFieldsets = element.all(by.css(".fieldset-inner"));
+      expect(await innerFieldsets.count()).toBe(5);
+      // check <select> values
+      for (let i = 0; i < innerFieldsets.length; i++) {
+        const inf = innerFieldsets[i];
+        const smc = inf.find("mat-select#select_modele_cloisons");
+        expect(await calcPage.getSelectValueText(smc)).toEqual("Cloisons 1");
+      }
+    });
+
+  });
+
+  describe("load session files - ", async () => {
+
+    it("when PAB is loaded after Cloisons", async() => {
+      await startPage.navigateTo();
+      // load
+      await navbar.clickMenuButton();
+      await browser.sleep(200);
+      await sidenav.clickLoadSessionButton();
+      await browser.sleep(200);
+      await sidenav.loadSessionFile("./session-pab.json");
+      await browser.sleep(500);
+      // check existence of the loaded modules
+      expect(await navbar.getAllCalculatorTabs().count()).toBe(4);
+      // check parameters values
+      await navbar.clickCalculatorTab(3);
+      await browser.sleep(700); // otherwise fails :/
+      const P_Q = calcPage.getInputById("Q");
+      expect(await P_Q.getAttribute("value")).toBe("1.533");
+      const P_Z2 = calcPage.getInputById("Z2");
+      expect(await P_Z2.getAttribute("value")).toBe("99.44");
+      // check number of basins
+      const innerFieldsets = element.all(by.css(".fieldset-inner"));
+      expect(await innerFieldsets.count()).toBe(5);
+      // check <select> values
+      const expectedCloisons = [ "Cloisons 1", "Cloisons 2", "Cloisons", "Cloisons 2", "Cloisons 1" ];
+      for (let i = 0; i < innerFieldsets.length; i++) {
+        const inf = innerFieldsets[i];
+        const smc = inf.find("mat-select#select_modele_cloisons");
+        expect(await calcPage.getSelectValueText(smc)).toEqual(expectedCloisons[i]);
+      }
+    });
+
+    it("when PAB is loaded before Cloisons", async() => {
+      await startPage.navigateTo();
+      // load
+      await navbar.clickMenuButton();
+      await browser.sleep(200);
+      await sidenav.clickLoadSessionButton();
+      await browser.sleep(200);
+      await sidenav.loadSessionFile("./session-pab-mauvais-ordre.json");
+      await browser.sleep(500);
+      // check existence of the loaded modules
+      expect(await navbar.getAllCalculatorTabs().count()).toBe(4);
+      // check parameters values
+      await navbar.clickCalculatorTab(0);
+      await browser.sleep(200);
+      const P_Q = calcPage.getInputById("Q");
+      expect(await P_Q.getAttribute("value")).toBe("1.533");
+      const P_Z2 = calcPage.getInputById("Z2");
+      expect(await P_Z2.getAttribute("value")).toBe("99.44");
+      // check number of basins
+      const innerFieldsets = element.all(by.css(".fieldset-inner"));
+      expect(await innerFieldsets.count()).toBe(5);
+      // check <select> values
+      const expectedCloisons = [ "Cloisons 1", "Cloisons 2", "Cloisons", "Cloisons 2", "Cloisons 1" ];
+      for (let i = 0; i < innerFieldsets.length; i++) {
+        const inf = innerFieldsets[i];
+        const smc = inf.find("mat-select#select_modele_cloisons");
+        expect(await calcPage.getSelectValueText(smc)).toEqual(expectedCloisons[i]);
+      }
+    });
+
+    it("when PAB is loaded without Cloisons", async() => {
+      await startPage.navigateTo();
+      // load
+      await navbar.clickMenuButton();
+      await browser.sleep(200);
+      await sidenav.clickLoadSessionButton();
+      await browser.sleep(200);
+      await sidenav.loadSessionFile("./session-pab-modeles-vides.json");
+      await browser.sleep(500);
+      // check existence of the loaded modules
+      expect(await navbar.getAllCalculatorTabs().count()).toBe(1);
+      // check number of basins
+      await navbar.clickCalculatorTab(0);
+      await browser.sleep(200);
+      const innerFieldsets = element.all(by.css(".fieldset-inner"));
+      expect(await innerFieldsets.count()).toBe(3);
+      // check empty <select>
+      for (let i = 0; i < innerFieldsets.length; i++) {
+        const inf = innerFieldsets[i];
+        const smc = inf.find("mat-select#select_modele_cloisons");
+        expect(await calcPage.getSelectValueText(smc)).toEqual("");
+      }
+      // create a Cloisons
+      await navbar.clickNewCalculatorButton();
+      await listPage.clickMenuEntryForCalcType(10);
+      // check that it is selected
+      await navbar.clickCalculatorTab(0);
+      for (let i = 0; i < innerFieldsets.length; i++) {
+        const inf = innerFieldsets[i];
+        const smc = inf.find("mat-select#select_modele_cloisons");
+        expect(await calcPage.getSelectValueText(smc)).toEqual("Cloisons");
+      }
+    });
+
+  });
+
+});
diff --git a/e2e/session-pab-mauvais-ordre.json b/e2e/session-pab-mauvais-ordre.json
new file mode 100644
index 000000000..05f1b640f
--- /dev/null
+++ b/e2e/session-pab-mauvais-ordre.json
@@ -0,0 +1,327 @@
+{
+    "header": {
+        "source": "jalhyd",
+        "format_version": "1.0",
+        "created": "2019-05-09T08:16:58.151Z"
+    },
+    "session": [
+        {
+            "uid": "NXd0dH",
+            "props": {
+                "calcType": 15,
+                "nodeType": 0
+            },
+            "meta": {
+                "title": "PAB"
+            },
+            "children": [
+                {
+                    "uid": "bWswdD",
+                    "props": {
+                        "calcType": 16,
+                        "nodeType": 0,
+                        "modeleCloisons": "bTkxem"
+                    },
+                    "parameters": [
+                        {
+                            "symbol": "QA",
+                            "mode": "SINGLE",
+                            "value": 2
+                        }
+                    ]
+                },
+                {
+                    "uid": "am1oNz",
+                    "props": {
+                        "calcType": 16,
+                        "nodeType": 0,
+                        "modeleCloisons": "bnNhen"
+                    },
+                    "parameters": [
+                        {
+                            "symbol": "QA",
+                            "mode": "SINGLE",
+                            "value": 1
+                        }
+                    ]
+                },
+                {
+                    "uid": "cWJrZm",
+                    "props": {
+                        "calcType": 16,
+                        "nodeType": 0,
+                        "modeleCloisons": "amp6N2"
+                    },
+                    "parameters": [
+                        {
+                            "symbol": "QA",
+                            "mode": "SINGLE",
+                            "value": 3
+                        }
+                    ]
+                },
+                {
+                    "uid": "Y25nNn",
+                    "props": {
+                        "calcType": 16,
+                        "nodeType": 0,
+                        "modeleCloisons": "bnNhen"
+                    },
+                    "parameters": [
+                        {
+                            "symbol": "QA",
+                            "mode": "SINGLE",
+                            "value": 5
+                        }
+                    ]
+                },
+                {
+                    "uid": "M2gxaX",
+                    "props": {
+                        "calcType": 16,
+                        "nodeType": 0,
+                        "modeleCloisons": "bTkxem"
+                    },
+                    "parameters": [
+                        {
+                            "symbol": "QA",
+                            "mode": "SINGLE",
+                            "value": 4
+                        }
+                    ]
+                }
+            ],
+            "parameters": [
+                {
+                    "symbol": "Pr",
+                    "mode": "SINGLE",
+                    "value": 0.0001
+                },
+                {
+                    "symbol": "Q",
+                    "mode": "SINGLE",
+                    "value": "1.533"
+                },
+                {
+                    "symbol": "Z1",
+                    "mode": "CALCUL"
+                },
+                {
+                    "symbol": "Z2",
+                    "mode": "SINGLE",
+                    "value": 99.44
+                }
+            ]
+        },
+        {
+            "uid": "amp6N2",
+            "props": {
+                "calcType": 10,
+                "nodeType": 0
+            },
+            "meta": {
+                "title": "Cloisons"
+            },
+            "children": [
+                {
+                    "uid": "YzNjam",
+                    "props": {
+                        "calcType": 7,
+                        "nodeType": 5,
+                        "structureType": 2,
+                        "loiDebit": 12
+                    },
+                    "children": [],
+                    "parameters": [
+                        {
+                            "symbol": "S",
+                            "mode": "SINGLE",
+                            "value": 0.1
+                        },
+                        {
+                            "symbol": "Cd",
+                            "mode": "SINGLE",
+                            "value": 0.7
+                        }
+                    ]
+                }
+            ],
+            "parameters": [
+                {
+                    "symbol": "Pr",
+                    "mode": "SINGLE",
+                    "value": 0.0001
+                },
+                {
+                    "symbol": "Q",
+                    "mode": "CALCUL"
+                },
+                {
+                    "symbol": "Z1",
+                    "mode": "SINGLE",
+                    "value": 102
+                },
+                {
+                    "symbol": "LB",
+                    "mode": "SINGLE",
+                    "value": 10
+                },
+                {
+                    "symbol": "BB",
+                    "mode": "SINGLE",
+                    "value": 1
+                },
+                {
+                    "symbol": "PB",
+                    "mode": "SINGLE",
+                    "value": 1
+                },
+                {
+                    "symbol": "DH",
+                    "mode": "SINGLE",
+                    "value": 0.5
+                }
+            ]
+        },
+        {
+            "uid": "bTkxem",
+            "props": {
+                "calcType": 10,
+                "nodeType": 0
+            },
+            "meta": {
+                "title": "Cloisons 1"
+            },
+            "children": [
+                {
+                    "uid": "Y3JjaW",
+                    "props": {
+                        "calcType": 7,
+                        "nodeType": 5,
+                        "structureType": 2,
+                        "loiDebit": 12
+                    },
+                    "children": [],
+                    "parameters": [
+                        {
+                            "symbol": "S",
+                            "mode": "SINGLE",
+                            "value": 0.1
+                        },
+                        {
+                            "symbol": "Cd",
+                            "mode": "SINGLE",
+                            "value": 0.7
+                        }
+                    ]
+                }
+            ],
+            "parameters": [
+                {
+                    "symbol": "Pr",
+                    "mode": "SINGLE",
+                    "value": 0.0001
+                },
+                {
+                    "symbol": "Q",
+                    "mode": "CALCUL"
+                },
+                {
+                    "symbol": "Z1",
+                    "mode": "SINGLE",
+                    "value": 102
+                },
+                {
+                    "symbol": "LB",
+                    "mode": "SINGLE",
+                    "value": 10
+                },
+                {
+                    "symbol": "BB",
+                    "mode": "SINGLE",
+                    "value": 1
+                },
+                {
+                    "symbol": "PB",
+                    "mode": "SINGLE",
+                    "value": 1
+                },
+                {
+                    "symbol": "DH",
+                    "mode": "SINGLE",
+                    "value": 0.5
+                }
+            ]
+        },
+        {
+            "uid": "bnNhen",
+            "props": {
+                "calcType": 10,
+                "nodeType": 0
+            },
+            "meta": {
+                "title": "Cloisons 2"
+            },
+            "children": [
+                {
+                    "uid": "amJzem",
+                    "props": {
+                        "calcType": 7,
+                        "nodeType": 5,
+                        "structureType": 2,
+                        "loiDebit": 12
+                    },
+                    "children": [],
+                    "parameters": [
+                        {
+                            "symbol": "S",
+                            "mode": "SINGLE",
+                            "value": 0.1
+                        },
+                        {
+                            "symbol": "Cd",
+                            "mode": "SINGLE",
+                            "value": 0.7
+                        }
+                    ]
+                }
+            ],
+            "parameters": [
+                {
+                    "symbol": "Pr",
+                    "mode": "SINGLE",
+                    "value": 0.0001
+                },
+                {
+                    "symbol": "Q",
+                    "mode": "CALCUL"
+                },
+                {
+                    "symbol": "Z1",
+                    "mode": "SINGLE",
+                    "value": 102
+                },
+                {
+                    "symbol": "LB",
+                    "mode": "SINGLE",
+                    "value": 10
+                },
+                {
+                    "symbol": "BB",
+                    "mode": "SINGLE",
+                    "value": 1
+                },
+                {
+                    "symbol": "PB",
+                    "mode": "SINGLE",
+                    "value": 1
+                },
+                {
+                    "symbol": "DH",
+                    "mode": "SINGLE",
+                    "value": 0.5
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/e2e/session-pab-modeles-vides.json b/e2e/session-pab-modeles-vides.json
new file mode 100644
index 000000000..b67b85a2e
--- /dev/null
+++ b/e2e/session-pab-modeles-vides.json
@@ -0,0 +1,84 @@
+{
+    "header": {
+        "source": "jalhyd",
+        "format_version": "1.0",
+        "created": "2019-05-09T09:57:38.480Z"
+    },
+    "session": [
+        {
+            "uid": "b2xqMj",
+            "props": {
+                "calcType": 15,
+                "nodeType": 0
+            },
+            "meta": {
+                "title": "PAB"
+            },
+            "children": [
+                {
+                    "uid": "czlkYn",
+                    "props": {
+                        "calcType": 16,
+                        "nodeType": 0
+                    },
+                    "parameters": [
+                        {
+                            "symbol": "QA",
+                            "mode": "SINGLE",
+                            "value": 0
+                        }
+                    ]
+                },
+                {
+                    "uid": "eTlyYT",
+                    "props": {
+                        "calcType": 16,
+                        "nodeType": 0
+                    },
+                    "parameters": [
+                        {
+                            "symbol": "QA",
+                            "mode": "SINGLE",
+                            "value": 0
+                        }
+                    ]
+                },
+                {
+                    "uid": "d3Q3an",
+                    "props": {
+                        "calcType": 16,
+                        "nodeType": 0
+                    },
+                    "parameters": [
+                        {
+                            "symbol": "QA",
+                            "mode": "SINGLE",
+                            "value": 0
+                        }
+                    ]
+                }
+            ],
+            "parameters": [
+                {
+                    "symbol": "Pr",
+                    "mode": "SINGLE",
+                    "value": 0.0001
+                },
+                {
+                    "symbol": "Q",
+                    "mode": "SINGLE",
+                    "value": 1.5
+                },
+                {
+                    "symbol": "Z1",
+                    "mode": "CALCUL"
+                },
+                {
+                    "symbol": "Z2",
+                    "mode": "SINGLE",
+                    "value": 99
+                }
+            ]
+        }
+    ]
+}
diff --git a/e2e/session-pab.json b/e2e/session-pab.json
new file mode 100644
index 000000000..03792069e
--- /dev/null
+++ b/e2e/session-pab.json
@@ -0,0 +1 @@
+{"header":{"source":"jalhyd","format_version":"1.0","created":"2019-05-09T08:16:58.151Z"},"session":[{"uid":"amp6N2","props":{"calcType":10,"nodeType":0},"meta":{"title":"Cloisons"},"children":[{"uid":"YzNjam","props":{"calcType":7,"nodeType":5,"structureType":2,"loiDebit":12},"children":[],"parameters":[{"symbol":"S","mode":"SINGLE","value":0.1},{"symbol":"Cd","mode":"SINGLE","value":0.7}]}],"parameters":[{"symbol":"Pr","mode":"SINGLE","value":0.0001},{"symbol":"Q","mode":"CALCUL"},{"symbol":"Z1","mode":"SINGLE","value":102},{"symbol":"LB","mode":"SINGLE","value":10},{"symbol":"BB","mode":"SINGLE","value":1},{"symbol":"PB","mode":"SINGLE","value":1},{"symbol":"DH","mode":"SINGLE","value":0.5}]},{"uid":"bTkxem","props":{"calcType":10,"nodeType":0},"meta":{"title":"Cloisons 1"},"children":[{"uid":"Y3JjaW","props":{"calcType":7,"nodeType":5,"structureType":2,"loiDebit":12},"children":[],"parameters":[{"symbol":"S","mode":"SINGLE","value":0.1},{"symbol":"Cd","mode":"SINGLE","value":0.7}]}],"parameters":[{"symbol":"Pr","mode":"SINGLE","value":0.0001},{"symbol":"Q","mode":"CALCUL"},{"symbol":"Z1","mode":"SINGLE","value":102},{"symbol":"LB","mode":"SINGLE","value":10},{"symbol":"BB","mode":"SINGLE","value":1},{"symbol":"PB","mode":"SINGLE","value":1},{"symbol":"DH","mode":"SINGLE","value":0.5}]},{"uid":"bnNhen","props":{"calcType":10,"nodeType":0},"meta":{"title":"Cloisons 2"},"children":[{"uid":"amJzem","props":{"calcType":7,"nodeType":5,"structureType":2,"loiDebit":12},"children":[],"parameters":[{"symbol":"S","mode":"SINGLE","value":0.1},{"symbol":"Cd","mode":"SINGLE","value":0.7}]}],"parameters":[{"symbol":"Pr","mode":"SINGLE","value":0.0001},{"symbol":"Q","mode":"CALCUL"},{"symbol":"Z1","mode":"SINGLE","value":102},{"symbol":"LB","mode":"SINGLE","value":10},{"symbol":"BB","mode":"SINGLE","value":1},{"symbol":"PB","mode":"SINGLE","value":1},{"symbol":"DH","mode":"SINGLE","value":0.5}]},{"uid":"NXd0dH","props":{"calcType":15,"nodeType":0},"meta":{"title":"PAB"},"children":[{"uid":"bWswdD","props":{"calcType":16,"nodeType":0,"modeleCloisons":"bTkxem"},"parameters":[{"symbol":"QA","mode":"SINGLE","value":2}]},{"uid":"am1oNz","props":{"calcType":16,"nodeType":0,"modeleCloisons":"bnNhen"},"parameters":[{"symbol":"QA","mode":"SINGLE","value":1}]},{"uid":"cWJrZm","props":{"calcType":16,"nodeType":0,"modeleCloisons":"amp6N2"},"parameters":[{"symbol":"QA","mode":"SINGLE","value":3}]},{"uid":"Y25nNn","props":{"calcType":16,"nodeType":0,"modeleCloisons":"bnNhen"},"parameters":[{"symbol":"QA","mode":"SINGLE","value":5}]},{"uid":"M2gxaX","props":{"calcType":16,"nodeType":0,"modeleCloisons":"bTkxem"},"parameters":[{"symbol":"QA","mode":"SINGLE","value":4}]}],"parameters":[{"symbol":"Pr","mode":"SINGLE","value":0.0001},{"symbol":"Q","mode":"SINGLE","value":"1.533"},{"symbol":"Z1","mode":"CALCUL"},{"symbol":"Z2","mode":"SINGLE","value":99.44}]}]}
\ No newline at end of file
diff --git a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.html b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.html
index 36b68b2c2..e94097590 100644
--- a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.html
+++ b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.html
@@ -6,7 +6,7 @@
 
     <mat-form-field>
       <input matInput required [placeholder]="uitextDebit" pattern="^([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$"
-      [(ngModel)]="debit" name="debit" #inputDebit="ngModel">
+      [(ngModel)]="debit" name="debit" #inputDebit="ngModel" id="generatePabDebit">
     </mat-form-field>
     <mat-error *ngIf="inputDebit.invalid && (inputDebit.dirty || inputDebit.touched)">
         <div *ngIf="inputDebit.errors.required || inputDebit.errors.pattern">
@@ -16,7 +16,7 @@
 
     <mat-form-field>
       <input matInput required [placeholder]="uitextCoteAmont" pattern="^-?([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$"
-      [(ngModel)]="coteAmont" name="coteAmont" #inputCoteAmont="ngModel">
+      [(ngModel)]="coteAmont" name="coteAmont" #inputCoteAmont="ngModel" id="generatePabCoteAmont">
     </mat-form-field>
     <mat-error *ngIf="inputCoteAmont.invalid && (inputCoteAmont.dirty || inputCoteAmont.touched)">
         <div *ngIf="inputCoteAmont.errors.required || inputCoteAmont.errors.pattern">
@@ -26,7 +26,7 @@
 
     <mat-form-field>
       <input matInput required [placeholder]="uitextNBBassins" pattern="^[1-9][0-9]*$"
-      [(ngModel)]="nbBassins" name="nbBassins" #inputNbBassins="ngModel">
+      [(ngModel)]="nbBassins" name="nbBassins" #inputNbBassins="ngModel" id="generatePabNbBassins">
     </mat-form-field>
     <mat-error *ngIf="inputNbBassins.invalid && (inputNbBassins.dirty || inputNbBassins.touched)">
         <div *ngIf="inputNbBassins.errors.required || inputNbBassins.errors.pattern">
@@ -40,7 +40,7 @@
     <button mat-raised-button color="primary" [mat-dialog-close]="false" cdkFocusInitial>
       {{ uitextCancel }}
     </button>
-    <button mat-raised-button type="submit" color="warn" (click)="generatePAB()"
+    <button mat-raised-button type="submit" color="warn" (click)="generatePAB()" id="do-generate"
       [disabled]="(inputDebit.invalid || inputCoteAmont.invalid || inputNbBassins.invalid)">
       {{ uitextGenerate }}
     </button>
-- 
GitLab


From da8b12745629c7c5403565c58ea2c1e9a20a3dbf Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Fri, 10 May 2019 10:39:58 +0200
Subject: [PATCH 20/44] =?UTF-8?q?M=C3=A0J=20tests=20e2e?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 e2e/pab.e2e-spec.ts | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/e2e/pab.e2e-spec.ts b/e2e/pab.e2e-spec.ts
index eeb3ae5ce..781a5935b 100644
--- a/e2e/pab.e2e-spec.ts
+++ b/e2e/pab.e2e-spec.ts
@@ -133,7 +133,7 @@ describe("ngHyd − Passe à Bassins", () => {
       expect(await navbar.getAllCalculatorTabs().count()).toBe(4);
       // check parameters values
       await navbar.clickCalculatorTab(3);
-      await browser.sleep(700); // otherwise fails :/
+      await browser.sleep(700); // sometime fails because of tabs order @see nghyd#197
       const P_Q = calcPage.getInputById("Q");
       expect(await P_Q.getAttribute("value")).toBe("1.533");
       const P_Z2 = calcPage.getInputById("Z2");
@@ -141,12 +141,15 @@ describe("ngHyd − Passe à Bassins", () => {
       // check number of basins
       const innerFieldsets = element.all(by.css(".fieldset-inner"));
       expect(await innerFieldsets.count()).toBe(5);
-      // check <select> values
+      // check <select> and QA values
       const expectedCloisons = [ "Cloisons 1", "Cloisons 2", "Cloisons", "Cloisons 2", "Cloisons 1" ];
+      const expectedQA = [ 2, 1, 3, 5, 4 ];
       for (let i = 0; i < innerFieldsets.length; i++) {
         const inf = innerFieldsets[i];
         const smc = inf.find("mat-select#select_modele_cloisons");
         expect(await calcPage.getSelectValueText(smc)).toEqual(expectedCloisons[i]);
+        const QA = inf.find("input#QA");
+        expect(Number(await QA.getAttribute("value"))).toBe(expectedQA[i]);
       }
     });
 
@@ -173,10 +176,13 @@ describe("ngHyd − Passe à Bassins", () => {
       expect(await innerFieldsets.count()).toBe(5);
       // check <select> values
       const expectedCloisons = [ "Cloisons 1", "Cloisons 2", "Cloisons", "Cloisons 2", "Cloisons 1" ];
+      const expectedQA = [ 2, 1, 3, 5, 4 ];
       for (let i = 0; i < innerFieldsets.length; i++) {
         const inf = innerFieldsets[i];
         const smc = inf.find("mat-select#select_modele_cloisons");
         expect(await calcPage.getSelectValueText(smc)).toEqual(expectedCloisons[i]);
+        const QA = inf.find("input#QA");
+        expect(Number(await QA.getAttribute("value"))).toBe(expectedQA[i]);
       }
     });
 
-- 
GitLab


From 12dcb855e337e09f3ca2c32bbea8567a27f48ab9 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Fri, 10 May 2019 14:59:20 +0200
Subject: [PATCH 21/44] PAB: ajout de la cloison aval

---
 src/app/app.module.ts                         |  4 +-
 src/app/calculators/pab/pab.config.json       | 15 ++++-
 src/app/calculators/pab/pab.en.json           |  4 +-
 src/app/calculators/pab/pab.fr.json           |  4 +-
 .../field-set/field-set.component.html        |  4 +-
 .../field-set/field-set.component.ts          | 14 +++--
 .../calculator.component.ts                   |  4 +-
 .../select-model-field-line.component.html}   |  4 +-
 .../select-model-field-line.component.scss}   |  0
 .../select-model-field-line.component.ts}     | 27 ++++----
 .../definition/concrete/form-pab.ts           | 22 ++++++-
 src/app/formulaire/fieldset.ts                | 61 ++++++++++++++-----
 .../select-field-model-cloison-aval.ts        | 31 ++++++++++
 .../formulaire/select-field-model-cloisons.ts | 31 ++++++++++
 ...ield-cloisons.ts => select-field-model.ts} | 36 ++++++-----
 .../services/formulaire/formulaire.service.ts |  7 ++-
 16 files changed, 201 insertions(+), 67 deletions(-)
 rename src/app/components/{select-cloisons-field-line/select-cloisons-field-line.component.html => select-model-field-line/select-model-field-line.component.html} (90%)
 rename src/app/components/{select-cloisons-field-line/select-cloisons-field-line.component.scss => select-model-field-line/select-model-field-line.component.scss} (100%)
 rename src/app/components/{select-cloisons-field-line/select-cloisons-field-line.component.ts => select-model-field-line/select-model-field-line.component.ts} (72%)
 create mode 100644 src/app/formulaire/select-field-model-cloison-aval.ts
 create mode 100644 src/app/formulaire/select-field-model-cloisons.ts
 rename src/app/formulaire/{select-field-cloisons.ts => select-field-model.ts} (70%)

diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 40aa77006..09827b43e 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -89,7 +89,7 @@ import {
   JalhydModelValidationStepDirective
 } from "./directives/jalhyd-model-validation.directive";
 import { ImmediateErrorStateMatcher } from "./formulaire/immediate-error-state-matcher";
-import { SelectCloisonsFieldLineComponent } from "./components/select-cloisons-field-line/select-cloisons-field-line.component";
+import { SelectModelFieldLineComponent } from "./components/select-model-field-line/select-model-field-line.component";
 
 const appRoutes: Routes = [
   { path: "list", component: CalculatorListComponent },
@@ -177,7 +177,7 @@ const appRoutes: Routes = [
     SectionCanvasComponent,
     SectionResultsComponent,
     SelectFieldLineComponent,
-    SelectCloisonsFieldLineComponent,
+    SelectModelFieldLineComponent,
     VarResultsComponent
   ],
   entryComponents: [
diff --git a/src/app/calculators/pab/pab.config.json b/src/app/calculators/pab/pab.config.json
index 295b7cfa0..568202378 100644
--- a/src/app/calculators/pab/pab.config.json
+++ b/src/app/calculators/pab/pab.config.json
@@ -48,10 +48,22 @@
             "fs_bassin"
         ]
     },
+    {
+        "id": "fs_cloison_aval",
+        "type": "fieldset",
+        "calcType": "Pab",
+        "fields": [
+            {
+                "id": "select_modele_cloison_aval",
+                "type": "select_cloison_aval",
+                "select": []
+            }
+        ]
+    },
     {
         "id": "fs_param_calc",
         "type": "fieldset",
-        "calcType": "ParallelStructure",
+        "calcType": "Pab",
         "option": "fix",
         "fields": [
             {
@@ -63,6 +75,7 @@
     {
         "type": "options",
         "modeleCloisonsSelectId": "select_modele_cloisons",
+        "modeleCloisonAvalSelectId": "select_modele_cloison_aval",
         "idCal": "Q"
     }
 ]
\ No newline at end of file
diff --git a/src/app/calculators/pab/pab.en.json b/src/app/calculators/pab/pab.en.json
index 955f0fb0b..f1f1cd6b4 100644
--- a/src/app/calculators/pab/pab.en.json
+++ b/src/app/calculators/pab/pab.en.json
@@ -4,6 +4,8 @@
     "Z1": "Upstream elevation",
     "Z2": "Downstream elevation",
     "fs_bassin": "Basin",
+    "fs_cloison_aval": "Downstream wall",
     "bassin_container": "Basins",
-    "select_modele_cloisons": "Cross walls model"
+    "select_modele_cloisons": "Cross walls model",
+    "select_modele_cloison_aval": "Downstream wall model"
 }
\ No newline at end of file
diff --git a/src/app/calculators/pab/pab.fr.json b/src/app/calculators/pab/pab.fr.json
index e64d3e88f..501c23cde 100644
--- a/src/app/calculators/pab/pab.fr.json
+++ b/src/app/calculators/pab/pab.fr.json
@@ -4,6 +4,8 @@
     "Z1": "Cote amont",
     "Z2": "Cote aval",
     "fs_bassin": "Bassin",
+    "fs_cloison_aval": "Cloison aval",
     "bassin_container": "Bassins",
-    "select_modele_cloisons": "Modèle de cloisons"
+    "select_modele_cloisons": "Modèle de cloisons",
+    "select_modele_cloison_aval": "Modèle de la cloison aval"
 }
\ No newline at end of file
diff --git a/src/app/components/field-set/field-set.component.html b/src/app/components/field-set/field-set.component.html
index 81765bfc5..f99f507db 100644
--- a/src/app/components/field-set/field-set.component.html
+++ b/src/app/components/field-set/field-set.component.html
@@ -27,7 +27,7 @@
         <select-field-line *ngIf="isSelectField(p)" [_select]=p>
         </select-field-line>
 
-        <select-cloisons-field-line *ngIf="isSelectCloisonsField(p)" [_select]=p>
-        </select-cloisons-field-line>
+        <select-model-field-line *ngIf="isSelectModelField(p)" [_select]=p>
+        </select-model-field-line>
     </ng-template>
 </mat-card-content>
diff --git a/src/app/components/field-set/field-set.component.ts b/src/app/components/field-set/field-set.component.ts
index 6f59b3505..82b34b5a1 100644
--- a/src/app/components/field-set/field-set.component.ts
+++ b/src/app/components/field-set/field-set.component.ts
@@ -6,7 +6,7 @@ import { ParamFieldLineComponent } from "../param-field-line/param-field-line.co
 import { Field } from "../../formulaire/field";
 import { InputField } from "../../formulaire/input-field";
 import { SelectField } from "../../formulaire/select-field";
-import { SelectFieldCloisons } from "../../formulaire/select-field-cloisons";
+import { SelectFieldModel } from "../../formulaire/select-field-model";
 import { FormulairePab } from "../../formulaire/definition/concrete/form-pab";
 
 @Component({
@@ -182,16 +182,18 @@ export class FieldSetComponent implements DoCheck {
     }
 
     /**
-     * détermine si un Field est du type SelectField
+     * détermine si un Field est du type SelectFieldModel
      */
-    private isSelectCloisonsField(f: Field): boolean {
+    private isSelectModelField(f: Field): boolean {
         return (
-            f instanceof SelectFieldCloisons
+            f instanceof SelectFieldModel
             && f.parentForm instanceof FormulairePab
-            && f.id === (f.parentForm as FormulairePab).modeleCloisonsSelectId
+            && (
+                f.id === (f.parentForm as FormulairePab).modeleCloisonsSelectId
+                || f.id === (f.parentForm as FormulairePab).modeleCloisonAvalSelectId
+            )
         );
     }
-
     /*
      * gestion des événements clic sur les radios :
      * réception d'un message du composant enfant (param-field)
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index 0364e5325..245b3acaf 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -20,7 +20,7 @@ import { ServiceFactory } from "../../services/service-factory";
 import { MatDialog } from "@angular/material";
 import { DialogConfirmCloseCalcComponent } from "../dialog-confirm-close-calc/dialog-confirm-close-calc.component";
 import { DialogGeneratePABComponent } from "../dialog-generate-pab/dialog-generate-pab.component";
-import { SelectFieldCloisons } from "../../formulaire/select-field-cloisons";
+import { SelectFieldModel } from "../../formulaire/select-field-model";
 
 @Component({
     selector: "hydrocalc",
@@ -468,7 +468,7 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
                         if (e instanceof FieldsetContainer) {
                             const newFieldset = e.addFromTemplate(0, undefined, pabCloisons);
                             // set selected value by ID; nub should be set by "select value changed" event listener
-                            const modeleSelect = (newFieldset.getFormulaireNodeById("select_modele_cloisons") as SelectFieldCloisons);
+                            const modeleSelect = (newFieldset.getFormulaireNodeById("select_modele_cloisons") as SelectFieldModel);
                             modeleSelect.updateEntries();
                             // ID of the Cloisons nub used by pabCloisons as a model
                             modeleSelect.setValueFromId(this._formulaire.currentNub.uid);
diff --git a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html b/src/app/components/select-model-field-line/select-model-field-line.component.html
similarity index 90%
rename from src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html
rename to src/app/components/select-model-field-line/select-model-field-line.component.html
index a593d8224..37b2b3295 100644
--- a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.html
+++ b/src/app/components/select-model-field-line/select-model-field-line.component.html
@@ -13,10 +13,10 @@
     </div>
 
     <div class="button-container" fxFlex="0 0 auto">
-        <button type="button" mat-icon-button *ngIf="selectedValue" [routerLink]="['/calculator/', cloisonsUid]">
+        <button type="button" mat-icon-button *ngIf="selectedValue" [routerLink]="['/calculator/', modelUid]">
             <mat-icon>edit</mat-icon>
         </button>
-        <button type="button" mat-icon-button *ngIf="entries.length === 0" (click)="createCloisons()">
+        <button type="button" mat-icon-button *ngIf="entries.length === 0" (click)="createModel()">
             <mat-icon>add</mat-icon>
         </button>
     </div>
diff --git a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.scss b/src/app/components/select-model-field-line/select-model-field-line.component.scss
similarity index 100%
rename from src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.scss
rename to src/app/components/select-model-field-line/select-model-field-line.component.scss
diff --git a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts b/src/app/components/select-model-field-line/select-model-field-line.component.ts
similarity index 72%
rename from src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts
rename to src/app/components/select-model-field-line/select-model-field-line.component.ts
index edfd03e33..306a8993c 100644
--- a/src/app/components/select-cloisons-field-line/select-cloisons-field-line.component.ts
+++ b/src/app/components/select-model-field-line/select-model-field-line.component.ts
@@ -3,21 +3,19 @@ import { Router } from "@angular/router";
 
 import { SelectEntry } from "../../formulaire/select-entry";
 import { FormulaireService } from "../../services/formulaire/formulaire.service";
-import { CalculatorType } from "jalhyd";
-import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
 import { FieldsetContainer } from "../../formulaire/fieldset-container";
-import { SelectFieldCloisons } from "../../formulaire/select-field-cloisons";
+import { SelectFieldModel } from "../../formulaire/select-field-model";
 
 @Component({
-    selector: "select-cloisons-field-line",
-    templateUrl: "./select-cloisons-field-line.component.html",
+    selector: "select-model-field-line",
+    templateUrl: "./select-model-field-line.component.html",
     styleUrls: [
-        "./select-cloisons-field-line.component.scss"
+        "./select-model-field-line.component.scss"
     ]
 })
-export class SelectCloisonsFieldLineComponent implements OnInit {
+export class SelectModelFieldLineComponent implements OnInit {
     @Input()
-    private _select: SelectFieldCloisons;
+    private _select: SelectFieldModel;
 
     public constructor(
         private _formService: FormulaireService,
@@ -35,7 +33,7 @@ export class SelectCloisonsFieldLineComponent implements OnInit {
         return this._select.entries;
     }
 
-    public get cloisonsUid() {
+    public get modelUid() {
         return this.selectedValue ? this.selectedValue.id : "";
     }
 
@@ -71,13 +69,16 @@ export class SelectCloisonsFieldLineComponent implements OnInit {
         }
     }
 
-    public createCloisons() {
-        const p: Promise<FormulaireDefinition> = this._formService.createFormulaire(CalculatorType.Cloisons);
-        p.then(f => {
+    /**
+     * Creates a new Nub of type _select.calcType, to be used as a model
+     * when no other is available
+     */
+    public createModel() {
+        this._formService.createFormulaire(this._select.calcType).then(f => {
             this.router.navigate(["/calculator", f.uid]);
             return f;
         }).then(f => {
-            // on ajoute un ouvrage au module "cloisons"
+            // on ajoute un ouvrage aux modulex de type "parallèle"
             for (const e of f.allFormElements) {
                 if (e instanceof FieldsetContainer) {
                     e.addFromTemplate(0);
diff --git a/src/app/formulaire/definition/concrete/form-pab.ts b/src/app/formulaire/definition/concrete/form-pab.ts
index adcce8c45..66c54f284 100644
--- a/src/app/formulaire/definition/concrete/form-pab.ts
+++ b/src/app/formulaire/definition/concrete/form-pab.ts
@@ -19,6 +19,9 @@ export class FormulairePab extends FormulaireBase {
     /** id du select configurant le modèle de cloisons */
     private _modeleCloisonsSelectId: string;
 
+    /** id du select configurant le modèle de la cloison aval */
+    private _modeleCloisonAvalSelectId: string;
+
     constructor() {
         super();
         this._formResult = new FormResultFixedVar(this, false);
@@ -32,6 +35,10 @@ export class FormulairePab extends FormulaireBase {
         return this._modeleCloisonsSelectId;
     }
 
+    public get modeleCloisonAvalSelectId(): string {
+        return this._modeleCloisonAvalSelectId;
+    }
+
     /**
      * Creates a virgin PabCloisons when a new fieldset is added through the GUI,
      * to ensure consistency; this object is not related to any Cloisons
@@ -114,12 +121,14 @@ export class FormulairePab extends FormulaireBase {
 
     protected parseOptions(json: {}) {
         super.parseOptions(json);
-        // id du select configurant le type d'ouvrage
+        // id du select configurant les modèles de cloisons
         this._modeleCloisonsSelectId = this.getOption(json, "modeleCloisonsSelectId");
+        // id du select configurant le modèle de cloison aval
+        this._modeleCloisonAvalSelectId = this.getOption(json, "modeleCloisonAvalSelectId");
     }
 
     public afterParseFieldset(fs: FieldSet) {
-        // si le FieldSet contient le select de type d'ouvrage
+        // si le FieldSet contient le select de modèles de cloisons
         if (this._modeleCloisonsSelectId) {
             const node = fs.getFormulaireNodeById(this._modeleCloisonsSelectId);
             const sel = (node as SelectField);
@@ -128,6 +137,15 @@ export class FormulairePab extends FormulaireBase {
                 fs.properties.addObserver(this);
             }
         }
+        // si le FieldSet contient le select du modèle de la cloison aval
+        if (this._modeleCloisonAvalSelectId) {
+            const node = fs.getFormulaireNodeById(this._modeleCloisonAvalSelectId);
+            const sel = (node as SelectField);
+            if (sel) {
+                // on abonne le formulaire aux propriétés du FieldSet
+                fs.properties.addObserver(this);
+            }
+        }
     }
 
     public moveFieldsetUp(fs: FieldSet) {
diff --git a/src/app/formulaire/fieldset.ts b/src/app/formulaire/fieldset.ts
index 1a0806deb..0aa18c2e5 100644
--- a/src/app/formulaire/fieldset.ts
+++ b/src/app/formulaire/fieldset.ts
@@ -3,11 +3,13 @@ import { CalculatorType, ComputeNodeType, ParamDefinition, LoiDebit, Props, Obse
 import { FormulaireElement } from "./formulaire-element";
 import { Field } from "./field";
 import { SelectField } from "./select-field";
-import { SelectFieldCloisons } from "./select-field-cloisons";
+import { SelectFieldModel } from "./select-field-model";
 import { NgParameter, ParamRadioConfig } from "./ngparam";
 import { FormulaireDefinition } from "./definition/form-definition";
 import { StringMap } from "../stringmap";
 import { FormulaireNode } from "./formulaire-node";
+import { SelectFieldModelCloisons } from "./select-field-model-cloisons";
+import { SelectFieldModelCloisonAval } from "./select-field-model-cloison-aval";
 
 export class FieldSet extends FormulaireElement implements Observer {
     /**
@@ -77,10 +79,19 @@ export class FieldSet extends FormulaireElement implements Observer {
         return res;
     }
 
-    // non-generic version of parse_select for SelectFieldCloisons because
+    // non-generic version of parse_select for SelectFieldModel because
     // downcasting is not possible with @Input() apparently
     private parse_select_cloisons(json: {}): SelectField {
-        const res: SelectFieldCloisons = new SelectFieldCloisons(this);
+        const res: SelectFieldModel = new SelectFieldModelCloisons(this, CalculatorType.Cloisons);
+        res.parseConfig(json);
+        res.addObserver(this);
+        return res;
+    }
+
+    // non-generic version of parse_select for SelectFieldCloisonAval because
+    // downcasting is not possible with @Input() apparently
+    private parse_select_cloison_aval(json: {}): SelectField {
+        const res: SelectFieldModel = new SelectFieldModelCloisonAval(this, CalculatorType.ParallelStructure);
         res.parseConfig(json);
         res.addObserver(this);
         return res;
@@ -146,20 +157,33 @@ export class FieldSet extends FormulaireElement implements Observer {
         const fields = this._jsonConfig["fields"];
         for (const field_index in fields) {
             const field = fields[field_index];
+            let param: Field;
+
+            switch (field["type"]) {
+                case "input":
+                    const default_radio_config = this._jsonConfig["option"];
+                    param = this.parse_input(field, default_radio_config);
+                    // tslint:disable-next-line:max-line-length
+                    if (param) { // potentiellement undefined car certaines définitions de FieldSet comportent des paramètres qui ne sont pas tous affichés en même temps (cf. ouvrages //)
+                        this.addField(param);
+                    }
+                    break;
 
-            if (field["type"] === "input") {
-                const default_radio_config = this._jsonConfig["option"];
-                const param = this.parse_input(field, default_radio_config);
-                // tslint:disable-next-line:max-line-length
-                if (param) { // potentiellement undefined car certaines définitions de FieldSet comportent des paramètres qui ne sont pas tous affichés en même temps (cf. ouvrages //)
+                case "select":
+                    param = this.parse_select(field);
                     this.addField(param);
-                }
-            } else if (field["type"] === "select") {
-                const param = this.parse_select(field);
-                this.addField(param);
-            } else if (field["type"] === "select_cloisons") {
-                const param = this.parse_select_cloisons(field);
-                this.addField(param);
+                    break;
+
+                case "select_cloisons":
+                    param = this.parse_select_cloisons(field);
+                    this.addField(param);
+                    break;
+
+                case "select_cloison_aval":
+                    param = this.parse_select_cloison_aval(field);
+                    this.addField(param);
+                    break;
+
             }
         }
     }
@@ -385,6 +409,13 @@ export class FieldSet extends FormulaireElement implements Observer {
                             }
                             this.setPropValue("modeleCloisons", valToSet);
                             break;
+                        case "select_modele_cloison_aval": // passe à bassins, modèle de la cloison aval
+                            let valToSet2;
+                            if (data.value) {
+                                valToSet2 = data.value.id;
+                            }
+                            this.setPropValue("modeleCloisonAval", valToSet2);
+                            break;
                     }
                     break;
             }
diff --git a/src/app/formulaire/select-field-model-cloison-aval.ts b/src/app/formulaire/select-field-model-cloison-aval.ts
new file mode 100644
index 000000000..b178d9af3
--- /dev/null
+++ b/src/app/formulaire/select-field-model-cloison-aval.ts
@@ -0,0 +1,31 @@
+import { Session } from "jalhyd";
+import { SelectEntry } from "./select-entry";
+import { FieldSet } from "./fieldset";
+import { SelectFieldModel } from "./select-field-model";
+
+/**
+ * A select field that populates itself with references to
+ * available ParallelStructure modules (used by PAB)
+ */
+export class SelectFieldModelCloisonAval extends SelectFieldModel {
+
+    protected initSelectedValue() {
+        if (this.parent instanceof FieldSet) {
+            const mc = this.parent.nub.properties.getPropValue("modeleCloisonAval");
+            if (mc) {
+                this._selectedEntry = new SelectEntry(mc, {});
+            } // else if current model is undefined, do not select it so that default ParallelStructures will be chosen (if available)
+        }
+    }
+
+    /**
+     * Populates entries with available ParallelStructures
+     */
+    protected populate() {
+        const psNubs = Session.getInstance().getParallelStructureNubs();
+        for (const cl of psNubs) {
+            const e = new SelectEntry(cl.uid, cl);
+            this.addEntry(e);
+        }
+    }
+}
diff --git a/src/app/formulaire/select-field-model-cloisons.ts b/src/app/formulaire/select-field-model-cloisons.ts
new file mode 100644
index 000000000..8a0241a4d
--- /dev/null
+++ b/src/app/formulaire/select-field-model-cloisons.ts
@@ -0,0 +1,31 @@
+import { Session } from "jalhyd";
+import { SelectEntry } from "./select-entry";
+import { FieldSet } from "./fieldset";
+import { SelectFieldModel } from "./select-field-model";
+
+/**
+ * A select field that populates itself with references to
+ * available Cloisons modules (used by PAB)
+ */
+export class SelectFieldModelCloisons extends SelectFieldModel {
+
+    protected initSelectedValue() {
+        if (this.parent instanceof FieldSet) {
+            const mc = this.parent.nub.properties.getPropValue("modeleCloisons");
+            if (mc) {
+                this._selectedEntry = new SelectEntry(mc, {});
+            } // else if current model is undefined, do not select it so that default Cloisons will be chosen (if available)
+        }
+    }
+
+    /**
+     * Populates entries with available Cloisons
+     */
+    protected populate() {
+        const cloisonsNubs = Session.getInstance().getCloisonsNubs();
+        for (const cl of cloisonsNubs) {
+            const e = new SelectEntry(cl.uid, cl);
+            this.addEntry(e);
+        }
+    }
+}
diff --git a/src/app/formulaire/select-field-cloisons.ts b/src/app/formulaire/select-field-model.ts
similarity index 70%
rename from src/app/formulaire/select-field-cloisons.ts
rename to src/app/formulaire/select-field-model.ts
index 6c85c9480..aa4147022 100644
--- a/src/app/formulaire/select-field-cloisons.ts
+++ b/src/app/formulaire/select-field-model.ts
@@ -1,35 +1,29 @@
 import { SelectField } from "./select-field";
-import { Session } from "jalhyd";
 import { SelectEntry } from "./select-entry";
 import { FormulaireNode } from "./formulaire-node";
-import { FieldSet } from "./fieldset";
+import { CalculatorType } from "jalhyd";
 
 /**
  * A select field that populates itself with references to
- * available Cloisons modules (used by PAB)
+ * available modules
  */
-export class SelectFieldCloisons extends SelectField {
+export abstract class SelectFieldModel extends SelectField {
 
-    constructor(parent: FormulaireNode) {
+    /** type of the targetted models; used by enclosing component to create new model */
+    public calcType: CalculatorType;
+
+    constructor(parent: FormulaireNode, calcType: CalculatorType) {
         super(parent);
-        if (this.parent instanceof FieldSet) {
-            const mc = this.parent.nub.properties.getPropValue("modeleCloisons");
-            if (mc) {
-                this._selectedEntry = new SelectEntry(mc, {});
-            } // else if current model is undefined, do not select it so that default Cloisons will be chosen (if available)
-        }
+        this.calcType = calcType;
+        this.initSelectedValue();
     }
 
+    protected abstract initSelectedValue();
+
     /**
-     * Populates entries with available Cloisons
+     * Populates entries with available models
      */
-    protected populate() {
-        const cloisonsNubs = Session.getInstance().getCloisonsNubs();
-        for (const cl of cloisonsNubs) {
-            const e = new SelectEntry(cl.uid, cl);
-            this.addEntry(e);
-        }
-    }
+    protected abstract populate();
 
     /**
      * Reloads available entries, trying to keep the current selected
@@ -46,6 +40,10 @@ export class SelectFieldCloisons extends SelectField {
         if (pse && pse.id) {
             this.setValueFromId(pse.id);
         }
+        // if no entry is available anymore, unset value
+        if (this.entries.length === 0) {
+            super.setValue(undefined);
+        }
     }
 
     /**
diff --git a/src/app/services/formulaire/formulaire.service.ts b/src/app/services/formulaire/formulaire.service.ts
index 04c1ba9dd..655be967d 100644
--- a/src/app/services/formulaire/formulaire.service.ts
+++ b/src/app/services/formulaire/formulaire.service.ts
@@ -510,8 +510,9 @@ export class FormulaireService extends Observable {
                             }
                         });
                     }
-                    // list Cloisons models dependencies for each PAB Nub
+                    // list Cloisons / downWall models dependencies for each PAB Nub
                     if (e.props.calcType === CalculatorType.Pab) {
+                        // Cloisons models
                         e.children.forEach((c) => {
                             if (c.props.calcType === CalculatorType.PabCloisons) { // who knows ?
                                 if (c.props.modeleCloisons && ! nubInfo.requires.includes(c.props.modeleCloisons)) {
@@ -519,6 +520,10 @@ export class FormulaireService extends Observable {
                                 }
                             }
                         });
+                        // Downstream wall
+                        if (e.props.modeleCloisonAval && ! nubInfo.requires.includes(e.props.modeleCloisonAval)) {
+                            nubInfo.requires.push(e.props.modeleCloisonAval);
+                        }
                     }
                     // list children nubs for each Nub
                     if (e.children) {
-- 
GitLab


From 00bb43516b1fdbb7504585502f9a1c5596f2ed7a Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Fri, 10 May 2019 15:53:17 +0200
Subject: [PATCH 22/44] =?UTF-8?q?M=C3=A0J=20tests=20e2e=20pour=20PAB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 e2e/calculator.po.ts                          |  10 +
 e2e/pab.e2e-spec.ts                           |  94 ++--
 e2e/session-pab-mauvais-ordre.json            | 139 +++++-
 e2e/session-pab.json                          | 459 +++++++++++++++++-
 .../definition/concrete/form-pab.ts           |   6 +-
 5 files changed, 670 insertions(+), 38 deletions(-)

diff --git a/e2e/calculator.po.ts b/e2e/calculator.po.ts
index 660aa1e1d..a3e578709 100644
--- a/e2e/calculator.po.ts
+++ b/e2e/calculator.po.ts
@@ -33,6 +33,16 @@ export class CalculatorPage {
     return await select.element(by.css(".mat-select-value-text > span")).getText();
   }
 
+  async isSelectEmpty(select: ElementFinder) {
+    try {
+      const text = select.element(by.css(".mat-select-value-text > span"));
+      await text.getAttribute("outerHTML"); // await anything trigger the error
+      return false;
+    } catch (e) { // should be NoSuchElementError
+      return true;
+    }
+  }
+
   getInputById(id: string) {
     return element(by.css("input#" + id));
   }
diff --git a/e2e/pab.e2e-spec.ts b/e2e/pab.e2e-spec.ts
index 781a5935b..6c3006e89 100644
--- a/e2e/pab.e2e-spec.ts
+++ b/e2e/pab.e2e-spec.ts
@@ -25,10 +25,13 @@ describe("ngHyd − Passe à Bassins", () => {
 
   describe("create PAB - ", async () => {
 
-    it("when PAB is created after Cloisons", async() => {
+    it("when PAB is created after Cloisons & ParallelStructure", async() => {
       // create Cloisons
       await startPage.navigateTo();
       await listPage.clickMenuEntryForCalcType(10);
+      // create ParallelStructure
+      await navbar.clickNewCalculatorButton();
+      await listPage.clickMenuEntryForCalcType(8);
       // create PAB
       await navbar.clickNewCalculatorButton();
       await listPage.clickMenuEntryForCalcType(15);
@@ -39,15 +42,21 @@ describe("ngHyd − Passe à Bassins", () => {
       const smc = calcPage.getSelectById("select_modele_cloisons");
       const v = await calcPage.getSelectValueText(smc);
       expect(v).toEqual("Cloisons");
+      // check downstream wall
+      const smca = calcPage.getSelectById("select_modele_cloison_aval");
+      expect(await calcPage.getSelectValueText(smca)).toEqual("Ouvrages");
     });
 
-    it("when PAB is created before Cloisons", async() => {
+    it("when PAB is created before Cloisons & ParallelStructure", async() => {
       // create PAB
       await startPage.navigateTo();
       await listPage.clickMenuEntryForCalcType(15);
       // create Cloisons
       await navbar.clickNewCalculatorButton();
       await listPage.clickMenuEntryForCalcType(10);
+      // create ParallelStructure
+      await navbar.clickNewCalculatorButton();
+      await listPage.clickMenuEntryForCalcType(8);
       // check number of basins
       await navbar.clickCalculatorTab(0);
       const innerFieldsets = element.all(by.css(".fieldset-inner"));
@@ -56,6 +65,9 @@ describe("ngHyd − Passe à Bassins", () => {
       const smc = calcPage.getSelectById("select_modele_cloisons");
       const v = await calcPage.getSelectValueText(smc);
       expect(v).toEqual("Cloisons");
+      // check downstream wall
+      const smca = calcPage.getSelectById("select_modele_cloison_aval");
+      expect(await calcPage.getSelectValueText(smca)).toEqual("Ouvrages");
     });
 
   });
@@ -89,6 +101,7 @@ describe("ngHyd − Passe à Bassins", () => {
       await debit.clear();
       await debit.sendKeys("1.");
       await debit.sendKeys("6"); // send "1.6" in 2 movements, because "." triggers an error that interrupts sendKeys() !
+      await browser.sleep(300);
       const coteAmont = calcPage.getInputById("generatePabCoteAmont");
       expect(await coteAmont.getAttribute("value")).toBe("114");
       await coteAmont.clear();
@@ -109,18 +122,17 @@ describe("ngHyd − Passe à Bassins", () => {
       const innerFieldsets = element.all(by.css(".fieldset-inner"));
       expect(await innerFieldsets.count()).toBe(5);
       // check <select> values
-      for (let i = 0; i < innerFieldsets.length; i++) {
-        const inf = innerFieldsets[i];
-        const smc = inf.find("mat-select#select_modele_cloisons");
+      innerFieldsets.each(async (inf) => {
+        const smc = inf.element(by.css("mat-select#select_modele_cloisons"));
         expect(await calcPage.getSelectValueText(smc)).toEqual("Cloisons 1");
-      }
+      });
     });
 
   });
 
   describe("load session files - ", async () => {
 
-    it("when PAB is loaded after Cloisons", async() => {
+    it("when PAB is loaded after Cloisons & ParallelStructure", async() => {
       await startPage.navigateTo();
       // load
       await navbar.clickMenuButton();
@@ -130,9 +142,9 @@ describe("ngHyd − Passe à Bassins", () => {
       await sidenav.loadSessionFile("./session-pab.json");
       await browser.sleep(500);
       // check existence of the loaded modules
-      expect(await navbar.getAllCalculatorTabs().count()).toBe(4);
+      expect(await navbar.getAllCalculatorTabs().count()).toBe(6);
       // check parameters values
-      await navbar.clickCalculatorTab(3);
+      await navbar.clickCalculatorTab(5);
       await browser.sleep(700); // sometime fails because of tabs order @see nghyd#197
       const P_Q = calcPage.getInputById("Q");
       expect(await P_Q.getAttribute("value")).toBe("1.533");
@@ -144,16 +156,20 @@ describe("ngHyd − Passe à Bassins", () => {
       // check <select> and QA values
       const expectedCloisons = [ "Cloisons 1", "Cloisons 2", "Cloisons", "Cloisons 2", "Cloisons 1" ];
       const expectedQA = [ 2, 1, 3, 5, 4 ];
-      for (let i = 0; i < innerFieldsets.length; i++) {
-        const inf = innerFieldsets[i];
-        const smc = inf.find("mat-select#select_modele_cloisons");
+      let i = 0;
+      innerFieldsets.each(async (inf) => {
+        const smc = inf.element(by.css("mat-select#select_modele_cloisons"));
         expect(await calcPage.getSelectValueText(smc)).toEqual(expectedCloisons[i]);
-        const QA = inf.find("input#QA");
+        const QA = inf.element(by.css("input#QA"));
         expect(Number(await QA.getAttribute("value"))).toBe(expectedQA[i]);
-      }
+        i++;
+      });
+      // check downstream wall
+      const smca = calcPage.getSelectById("select_modele_cloison_aval");
+      expect(await calcPage.getSelectValueText(smca)).toEqual("Ouvrages 1");
     });
 
-    it("when PAB is loaded before Cloisons", async() => {
+    it("when PAB is loaded before Cloisons & ParallelStructure", async() => {
       await startPage.navigateTo();
       // load
       await navbar.clickMenuButton();
@@ -163,7 +179,7 @@ describe("ngHyd − Passe à Bassins", () => {
       await sidenav.loadSessionFile("./session-pab-mauvais-ordre.json");
       await browser.sleep(500);
       // check existence of the loaded modules
-      expect(await navbar.getAllCalculatorTabs().count()).toBe(4);
+      expect(await navbar.getAllCalculatorTabs().count()).toBe(6);
       // check parameters values
       await navbar.clickCalculatorTab(0);
       await browser.sleep(200);
@@ -177,16 +193,20 @@ describe("ngHyd − Passe à Bassins", () => {
       // check <select> values
       const expectedCloisons = [ "Cloisons 1", "Cloisons 2", "Cloisons", "Cloisons 2", "Cloisons 1" ];
       const expectedQA = [ 2, 1, 3, 5, 4 ];
-      for (let i = 0; i < innerFieldsets.length; i++) {
-        const inf = innerFieldsets[i];
-        const smc = inf.find("mat-select#select_modele_cloisons");
+      let i = 0;
+      innerFieldsets.each(async (inf) => {
+        const smc = inf.element(by.css("mat-select#select_modele_cloisons"));
         expect(await calcPage.getSelectValueText(smc)).toEqual(expectedCloisons[i]);
-        const QA = inf.find("input#QA");
+        const QA = inf.element(by.css("input#QA"));
         expect(Number(await QA.getAttribute("value"))).toBe(expectedQA[i]);
-      }
+        i++;
+      });
+      // check downstream wall
+      const smca = calcPage.getSelectById("select_modele_cloison_aval");
+      expect(await calcPage.getSelectValueText(smca)).toEqual("Ouvrages 1");
     });
 
-    it("when PAB is loaded without Cloisons", async() => {
+    it("when PAB is loaded without Cloisons & ParallelStructure", async() => {
       await startPage.navigateTo();
       // load
       await navbar.clickMenuButton();
@@ -195,6 +215,7 @@ describe("ngHyd − Passe à Bassins", () => {
       await browser.sleep(200);
       await sidenav.loadSessionFile("./session-pab-modeles-vides.json");
       await browser.sleep(500);
+
       // check existence of the loaded modules
       expect(await navbar.getAllCalculatorTabs().count()).toBe(1);
       // check number of basins
@@ -203,21 +224,30 @@ describe("ngHyd − Passe à Bassins", () => {
       const innerFieldsets = element.all(by.css(".fieldset-inner"));
       expect(await innerFieldsets.count()).toBe(3);
       // check empty <select>
-      for (let i = 0; i < innerFieldsets.length; i++) {
-        const inf = innerFieldsets[i];
-        const smc = inf.find("mat-select#select_modele_cloisons");
-        expect(await calcPage.getSelectValueText(smc)).toEqual("");
-      }
+      innerFieldsets.each(async (inf) => {
+        const smc = inf.element(by.css("mat-select#select_modele_cloisons"));
+        const smcEmpty = await calcPage.isSelectEmpty(smc);
+        console.log("SMC EMPTY ?", smcEmpty);
+        expect(smcEmpty).toBe(true);
+      });
+      // check empty <select> for downstream wall
+      const smca = calcPage.getSelectById("select_modele_cloison_aval");
+      expect(await calcPage.isSelectEmpty(smca)).toBe(true);
+
       // create a Cloisons
       await navbar.clickNewCalculatorButton();
       await listPage.clickMenuEntryForCalcType(10);
-      // check that it is selected
+      // create a ParallelStructure
+      await navbar.clickNewCalculatorButton();
+      await listPage.clickMenuEntryForCalcType(8);
+      // check that they are selected
       await navbar.clickCalculatorTab(0);
-      for (let i = 0; i < innerFieldsets.length; i++) {
-        const inf = innerFieldsets[i];
-        const smc = inf.find("mat-select#select_modele_cloisons");
+      innerFieldsets.each(async (inf) => {
+        const smc = inf.element(by.css("mat-select#select_modele_cloisons"));
         expect(await calcPage.getSelectValueText(smc)).toEqual("Cloisons");
-      }
+      });
+      const smca2 = calcPage.getSelectById("select_modele_cloison_aval");
+      expect(await calcPage.getSelectValueText(smca2)).toEqual("Ouvrages");
     });
 
   });
diff --git a/e2e/session-pab-mauvais-ordre.json b/e2e/session-pab-mauvais-ordre.json
index 05f1b640f..c60b52be8 100644
--- a/e2e/session-pab-mauvais-ordre.json
+++ b/e2e/session-pab-mauvais-ordre.json
@@ -2,14 +2,15 @@
     "header": {
         "source": "jalhyd",
         "format_version": "1.0",
-        "created": "2019-05-09T08:16:58.151Z"
+        "created": "2019-05-10T13:09:33.792Z"
     },
     "session": [
         {
             "uid": "NXd0dH",
             "props": {
                 "calcType": 15,
-                "nodeType": 0
+                "nodeType": 0,
+                "modeleCloisonAval": "eWtnaj"
             },
             "meta": {
                 "title": "PAB"
@@ -190,7 +191,7 @@
                 "nodeType": 0
             },
             "meta": {
-                "title": "Cloisons 1"
+                "title": "Cloisons 2"
             },
             "children": [
                 {
@@ -260,7 +261,7 @@
                 "nodeType": 0
             },
             "meta": {
-                "title": "Cloisons 2"
+                "title": "Cloisons 1"
             },
             "children": [
                 {
@@ -322,6 +323,136 @@
                     "value": 0.5
                 }
             ]
+        },
+        {
+            "uid": "M2Q0NX",
+            "props": {
+                "calcType": 8,
+                "nodeType": 0
+            },
+            "meta": {
+                "title": "Ouvrages"
+            },
+            "children": [
+                {
+                    "uid": "MmJxen",
+                    "props": {
+                        "calcType": 7,
+                        "nodeType": 5,
+                        "structureType": 1,
+                        "loiDebit": 1
+                    },
+                    "children": [],
+                    "parameters": [
+                        {
+                            "symbol": "ZDV",
+                            "mode": "SINGLE",
+                            "value": 100
+                        },
+                        {
+                            "symbol": "W",
+                            "mode": "SINGLE",
+                            "value": 0.5
+                        },
+                        {
+                            "symbol": "L",
+                            "mode": "SINGLE",
+                            "value": 2
+                        },
+                        {
+                            "symbol": "Cd",
+                            "mode": "SINGLE",
+                            "value": 0.4
+                        }
+                    ]
+                }
+            ],
+            "parameters": [
+                {
+                    "symbol": "Pr",
+                    "mode": "SINGLE",
+                    "value": 0.0001
+                },
+                {
+                    "symbol": "Q",
+                    "mode": "CALCUL"
+                },
+                {
+                    "symbol": "Z1",
+                    "mode": "SINGLE",
+                    "value": 102
+                },
+                {
+                    "symbol": "Z2",
+                    "mode": "SINGLE",
+                    "value": 101.5
+                }
+            ]
+        },
+        {
+            "uid": "eWtnaj",
+            "props": {
+                "calcType": 8,
+                "nodeType": 0
+            },
+            "meta": {
+                "title": "Ouvrages 1"
+            },
+            "children": [
+                {
+                    "uid": "ZmhpeG",
+                    "props": {
+                        "calcType": 7,
+                        "nodeType": 5,
+                        "structureType": 1,
+                        "loiDebit": 1
+                    },
+                    "children": [],
+                    "parameters": [
+                        {
+                            "symbol": "ZDV",
+                            "mode": "SINGLE",
+                            "value": 100
+                        },
+                        {
+                            "symbol": "W",
+                            "mode": "SINGLE",
+                            "value": 0.5
+                        },
+                        {
+                            "symbol": "L",
+                            "mode": "SINGLE",
+                            "value": 2
+                        },
+                        {
+                            "symbol": "Cd",
+                            "mode": "SINGLE",
+                            "value": 0.4
+                        }
+                    ]
+                }
+            ],
+            "parameters": [
+                {
+                    "symbol": "Pr",
+                    "mode": "SINGLE",
+                    "value": 0.0001
+                },
+                {
+                    "symbol": "Q",
+                    "mode": "CALCUL"
+                },
+                {
+                    "symbol": "Z1",
+                    "mode": "SINGLE",
+                    "value": 102
+                },
+                {
+                    "symbol": "Z2",
+                    "mode": "SINGLE",
+                    "value": 101.5
+                }
+            ]
         }
     ]
 }
\ No newline at end of file
diff --git a/e2e/session-pab.json b/e2e/session-pab.json
index 03792069e..2d00f2eec 100644
--- a/e2e/session-pab.json
+++ b/e2e/session-pab.json
@@ -1 +1,458 @@
-{"header":{"source":"jalhyd","format_version":"1.0","created":"2019-05-09T08:16:58.151Z"},"session":[{"uid":"amp6N2","props":{"calcType":10,"nodeType":0},"meta":{"title":"Cloisons"},"children":[{"uid":"YzNjam","props":{"calcType":7,"nodeType":5,"structureType":2,"loiDebit":12},"children":[],"parameters":[{"symbol":"S","mode":"SINGLE","value":0.1},{"symbol":"Cd","mode":"SINGLE","value":0.7}]}],"parameters":[{"symbol":"Pr","mode":"SINGLE","value":0.0001},{"symbol":"Q","mode":"CALCUL"},{"symbol":"Z1","mode":"SINGLE","value":102},{"symbol":"LB","mode":"SINGLE","value":10},{"symbol":"BB","mode":"SINGLE","value":1},{"symbol":"PB","mode":"SINGLE","value":1},{"symbol":"DH","mode":"SINGLE","value":0.5}]},{"uid":"bTkxem","props":{"calcType":10,"nodeType":0},"meta":{"title":"Cloisons 1"},"children":[{"uid":"Y3JjaW","props":{"calcType":7,"nodeType":5,"structureType":2,"loiDebit":12},"children":[],"parameters":[{"symbol":"S","mode":"SINGLE","value":0.1},{"symbol":"Cd","mode":"SINGLE","value":0.7}]}],"parameters":[{"symbol":"Pr","mode":"SINGLE","value":0.0001},{"symbol":"Q","mode":"CALCUL"},{"symbol":"Z1","mode":"SINGLE","value":102},{"symbol":"LB","mode":"SINGLE","value":10},{"symbol":"BB","mode":"SINGLE","value":1},{"symbol":"PB","mode":"SINGLE","value":1},{"symbol":"DH","mode":"SINGLE","value":0.5}]},{"uid":"bnNhen","props":{"calcType":10,"nodeType":0},"meta":{"title":"Cloisons 2"},"children":[{"uid":"amJzem","props":{"calcType":7,"nodeType":5,"structureType":2,"loiDebit":12},"children":[],"parameters":[{"symbol":"S","mode":"SINGLE","value":0.1},{"symbol":"Cd","mode":"SINGLE","value":0.7}]}],"parameters":[{"symbol":"Pr","mode":"SINGLE","value":0.0001},{"symbol":"Q","mode":"CALCUL"},{"symbol":"Z1","mode":"SINGLE","value":102},{"symbol":"LB","mode":"SINGLE","value":10},{"symbol":"BB","mode":"SINGLE","value":1},{"symbol":"PB","mode":"SINGLE","value":1},{"symbol":"DH","mode":"SINGLE","value":0.5}]},{"uid":"NXd0dH","props":{"calcType":15,"nodeType":0},"meta":{"title":"PAB"},"children":[{"uid":"bWswdD","props":{"calcType":16,"nodeType":0,"modeleCloisons":"bTkxem"},"parameters":[{"symbol":"QA","mode":"SINGLE","value":2}]},{"uid":"am1oNz","props":{"calcType":16,"nodeType":0,"modeleCloisons":"bnNhen"},"parameters":[{"symbol":"QA","mode":"SINGLE","value":1}]},{"uid":"cWJrZm","props":{"calcType":16,"nodeType":0,"modeleCloisons":"amp6N2"},"parameters":[{"symbol":"QA","mode":"SINGLE","value":3}]},{"uid":"Y25nNn","props":{"calcType":16,"nodeType":0,"modeleCloisons":"bnNhen"},"parameters":[{"symbol":"QA","mode":"SINGLE","value":5}]},{"uid":"M2gxaX","props":{"calcType":16,"nodeType":0,"modeleCloisons":"bTkxem"},"parameters":[{"symbol":"QA","mode":"SINGLE","value":4}]}],"parameters":[{"symbol":"Pr","mode":"SINGLE","value":0.0001},{"symbol":"Q","mode":"SINGLE","value":"1.533"},{"symbol":"Z1","mode":"CALCUL"},{"symbol":"Z2","mode":"SINGLE","value":99.44}]}]}
\ No newline at end of file
+{
+    "header": {
+        "source": "jalhyd",
+        "format_version": "1.0",
+        "created": "2019-05-10T13:09:33.792Z"
+    },
+    "session": [
+        {
+            "uid": "amp6N2",
+            "props": {
+                "calcType": 10,
+                "nodeType": 0
+            },
+            "meta": {
+                "title": "Cloisons"
+            },
+            "children": [
+                {
+                    "uid": "YzNjam",
+                    "props": {
+                        "calcType": 7,
+                        "nodeType": 5,
+                        "structureType": 2,
+                        "loiDebit": 12
+                    },
+                    "children": [],
+                    "parameters": [
+                        {
+                            "symbol": "S",
+                            "mode": "SINGLE",
+                            "value": 0.1
+                        },
+                        {
+                            "symbol": "Cd",
+                            "mode": "SINGLE",
+                            "value": 0.7
+                        }
+                    ]
+                }
+            ],
+            "parameters": [
+                {
+                    "symbol": "Pr",
+                    "mode": "SINGLE",
+                    "value": 0.0001
+                },
+                {
+                    "symbol": "Q",
+                    "mode": "CALCUL"
+                },
+                {
+                    "symbol": "Z1",
+                    "mode": "SINGLE",
+                    "value": 102
+                },
+                {
+                    "symbol": "LB",
+                    "mode": "SINGLE",
+                    "value": 10
+                },
+                {
+                    "symbol": "BB",
+                    "mode": "SINGLE",
+                    "value": 1
+                },
+                {
+                    "symbol": "PB",
+                    "mode": "SINGLE",
+                    "value": 1
+                },
+                {
+                    "symbol": "DH",
+                    "mode": "SINGLE",
+                    "value": 0.5
+                }
+            ]
+        },
+        {
+            "uid": "bTkxem",
+            "props": {
+                "calcType": 10,
+                "nodeType": 0
+            },
+            "meta": {
+                "title": "Cloisons 2"
+            },
+            "children": [
+                {
+                    "uid": "Y3JjaW",
+                    "props": {
+                        "calcType": 7,
+                        "nodeType": 5,
+                        "structureType": 2,
+                        "loiDebit": 12
+                    },
+                    "children": [],
+                    "parameters": [
+                        {
+                            "symbol": "S",
+                            "mode": "SINGLE",
+                            "value": 0.1
+                        },
+                        {
+                            "symbol": "Cd",
+                            "mode": "SINGLE",
+                            "value": 0.7
+                        }
+                    ]
+                }
+            ],
+            "parameters": [
+                {
+                    "symbol": "Pr",
+                    "mode": "SINGLE",
+                    "value": 0.0001
+                },
+                {
+                    "symbol": "Q",
+                    "mode": "CALCUL"
+                },
+                {
+                    "symbol": "Z1",
+                    "mode": "SINGLE",
+                    "value": 102
+                },
+                {
+                    "symbol": "LB",
+                    "mode": "SINGLE",
+                    "value": 10
+                },
+                {
+                    "symbol": "BB",
+                    "mode": "SINGLE",
+                    "value": 1
+                },
+                {
+                    "symbol": "PB",
+                    "mode": "SINGLE",
+                    "value": 1
+                },
+                {
+                    "symbol": "DH",
+                    "mode": "SINGLE",
+                    "value": 0.5
+                }
+            ]
+        },
+        {
+            "uid": "bnNhen",
+            "props": {
+                "calcType": 10,
+                "nodeType": 0
+            },
+            "meta": {
+                "title": "Cloisons 1"
+            },
+            "children": [
+                {
+                    "uid": "amJzem",
+                    "props": {
+                        "calcType": 7,
+                        "nodeType": 5,
+                        "structureType": 2,
+                        "loiDebit": 12
+                    },
+                    "children": [],
+                    "parameters": [
+                        {
+                            "symbol": "S",
+                            "mode": "SINGLE",
+                            "value": 0.1
+                        },
+                        {
+                            "symbol": "Cd",
+                            "mode": "SINGLE",
+                            "value": 0.7
+                        }
+                    ]
+                }
+            ],
+            "parameters": [
+                {
+                    "symbol": "Pr",
+                    "mode": "SINGLE",
+                    "value": 0.0001
+                },
+                {
+                    "symbol": "Q",
+                    "mode": "CALCUL"
+                },
+                {
+                    "symbol": "Z1",
+                    "mode": "SINGLE",
+                    "value": 102
+                },
+                {
+                    "symbol": "LB",
+                    "mode": "SINGLE",
+                    "value": 10
+                },
+                {
+                    "symbol": "BB",
+                    "mode": "SINGLE",
+                    "value": 1
+                },
+                {
+                    "symbol": "PB",
+                    "mode": "SINGLE",
+                    "value": 1
+                },
+                {
+                    "symbol": "DH",
+                    "mode": "SINGLE",
+                    "value": 0.5
+                }
+            ]
+        },
+        {
+            "uid": "M2Q0NX",
+            "props": {
+                "calcType": 8,
+                "nodeType": 0
+            },
+            "meta": {
+                "title": "Ouvrages"
+            },
+            "children": [
+                {
+                    "uid": "MmJxen",
+                    "props": {
+                        "calcType": 7,
+                        "nodeType": 5,
+                        "structureType": 1,
+                        "loiDebit": 1
+                    },
+                    "children": [],
+                    "parameters": [
+                        {
+                            "symbol": "ZDV",
+                            "mode": "SINGLE",
+                            "value": 100
+                        },
+                        {
+                            "symbol": "W",
+                            "mode": "SINGLE",
+                            "value": 0.5
+                        },
+                        {
+                            "symbol": "L",
+                            "mode": "SINGLE",
+                            "value": 2
+                        },
+                        {
+                            "symbol": "Cd",
+                            "mode": "SINGLE",
+                            "value": 0.4
+                        }
+                    ]
+                }
+            ],
+            "parameters": [
+                {
+                    "symbol": "Pr",
+                    "mode": "SINGLE",
+                    "value": 0.0001
+                },
+                {
+                    "symbol": "Q",
+                    "mode": "CALCUL"
+                },
+                {
+                    "symbol": "Z1",
+                    "mode": "SINGLE",
+                    "value": 102
+                },
+                {
+                    "symbol": "Z2",
+                    "mode": "SINGLE",
+                    "value": 101.5
+                }
+            ]
+        },
+        {
+            "uid": "eWtnaj",
+            "props": {
+                "calcType": 8,
+                "nodeType": 0
+            },
+            "meta": {
+                "title": "Ouvrages 1"
+            },
+            "children": [
+                {
+                    "uid": "ZmhpeG",
+                    "props": {
+                        "calcType": 7,
+                        "nodeType": 5,
+                        "structureType": 1,
+                        "loiDebit": 1
+                    },
+                    "children": [],
+                    "parameters": [
+                        {
+                            "symbol": "ZDV",
+                            "mode": "SINGLE",
+                            "value": 100
+                        },
+                        {
+                            "symbol": "W",
+                            "mode": "SINGLE",
+                            "value": 0.5
+                        },
+                        {
+                            "symbol": "L",
+                            "mode": "SINGLE",
+                            "value": 2
+                        },
+                        {
+                            "symbol": "Cd",
+                            "mode": "SINGLE",
+                            "value": 0.4
+                        }
+                    ]
+                }
+            ],
+            "parameters": [
+                {
+                    "symbol": "Pr",
+                    "mode": "SINGLE",
+                    "value": 0.0001
+                },
+                {
+                    "symbol": "Q",
+                    "mode": "CALCUL"
+                },
+                {
+                    "symbol": "Z1",
+                    "mode": "SINGLE",
+                    "value": 102
+                },
+                {
+                    "symbol": "Z2",
+                    "mode": "SINGLE",
+                    "value": 101.5
+                }
+            ]
+        },
+        {
+            "uid": "NXd0dH",
+            "props": {
+                "calcType": 15,
+                "nodeType": 0,
+                "modeleCloisonAval": "eWtnaj"
+            },
+            "meta": {
+                "title": "PAB"
+            },
+            "children": [
+                {
+                    "uid": "bWswdD",
+                    "props": {
+                        "calcType": 16,
+                        "nodeType": 0,
+                        "modeleCloisons": "bTkxem"
+                    },
+                    "parameters": [
+                        {
+                            "symbol": "QA",
+                            "mode": "SINGLE",
+                            "value": 2
+                        }
+                    ]
+                },
+                {
+                    "uid": "am1oNz",
+                    "props": {
+                        "calcType": 16,
+                        "nodeType": 0,
+                        "modeleCloisons": "bnNhen"
+                    },
+                    "parameters": [
+                        {
+                            "symbol": "QA",
+                            "mode": "SINGLE",
+                            "value": 1
+                        }
+                    ]
+                },
+                {
+                    "uid": "cWJrZm",
+                    "props": {
+                        "calcType": 16,
+                        "nodeType": 0,
+                        "modeleCloisons": "amp6N2"
+                    },
+                    "parameters": [
+                        {
+                            "symbol": "QA",
+                            "mode": "SINGLE",
+                            "value": 3
+                        }
+                    ]
+                },
+                {
+                    "uid": "Y25nNn",
+                    "props": {
+                        "calcType": 16,
+                        "nodeType": 0,
+                        "modeleCloisons": "bnNhen"
+                    },
+                    "parameters": [
+                        {
+                            "symbol": "QA",
+                            "mode": "SINGLE",
+                            "value": 5
+                        }
+                    ]
+                },
+                {
+                    "uid": "M2gxaX",
+                    "props": {
+                        "calcType": 16,
+                        "nodeType": 0,
+                        "modeleCloisons": "bTkxem"
+                    },
+                    "parameters": [
+                        {
+                            "symbol": "QA",
+                            "mode": "SINGLE",
+                            "value": 4
+                        }
+                    ]
+                }
+            ],
+            "parameters": [
+                {
+                    "symbol": "Pr",
+                    "mode": "SINGLE",
+                    "value": 0.0001
+                },
+                {
+                    "symbol": "Q",
+                    "mode": "SINGLE",
+                    "value": "1.533"
+                },
+                {
+                    "symbol": "Z1",
+                    "mode": "CALCUL"
+                },
+                {
+                    "symbol": "Z2",
+                    "mode": "SINGLE",
+                    "value": 99.44
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/src/app/formulaire/definition/concrete/form-pab.ts b/src/app/formulaire/definition/concrete/form-pab.ts
index 66c54f284..9c05c514a 100644
--- a/src/app/formulaire/definition/concrete/form-pab.ts
+++ b/src/app/formulaire/definition/concrete/form-pab.ts
@@ -243,7 +243,7 @@ export class FormulairePab extends FormulaireBase {
                             this.afterParseFieldset(sender);
                             this.reset();
 
-                            this.dumpPabStructure(this.currentNub as Pab);
+                            // this.dumpPabStructure(this.currentNub as Pab);
                             break;
                     }
                     break;
@@ -251,6 +251,7 @@ export class FormulairePab extends FormulaireBase {
         }
     }
 
+    // debug method
     private dumpPabStructure(pab: Pab) {
         console.log(`PAB: ${pab.uid}, ${pab.children.length} children`);
         for (const c of pab.children) {
@@ -259,5 +260,8 @@ export class FormulairePab extends FormulaireBase {
                 + ` (cote amont ${c.prms.Z1.singleValue}, longueur ${c.prms.LB.singleValue})`
             );
         }
+        if (pab.downWall) {
+            console.log(`+ downstream wall: ${pab.downWall.uid}`);
+        }
     }
 }
-- 
GitLab


From 18804499b1b3e2460e65678b9b83913ac2b13474 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Fri, 10 May 2019 16:35:11 +0200
Subject: [PATCH 23/44] Correction bug cloison aval

---
 .../definition/concrete/form-pab.ts           | 21 +++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/src/app/formulaire/definition/concrete/form-pab.ts b/src/app/formulaire/definition/concrete/form-pab.ts
index 9c05c514a..3a5bac9c4 100644
--- a/src/app/formulaire/definition/concrete/form-pab.ts
+++ b/src/app/formulaire/definition/concrete/form-pab.ts
@@ -90,6 +90,11 @@ export class FormulairePab extends FormulaireBase {
         parent.deleteChild(parent.getIndexForChild(sn));
     }
 
+    public doCompute() {
+        this.dumpPabStructure(this.pabNub);
+        super.doCompute();
+    }
+
     public createFieldset(parent: FormulaireNode, json: {}, data?: {}, nub?: Nub): FieldSet {
         if (json["calcType"] === "PabCloisons") {
             // indice après lequel insérer le nouveau FieldSet
@@ -235,15 +240,27 @@ export class FormulairePab extends FormulaireBase {
                 case "fs_bassin":
                     switch (data.name) {
                         case "modeleCloisons":
-                            // change PabCloisons Nub property "modeleCloisons" and reinit it
+                            // change PabCloisons property "modeleCloisons" and reinit it
                             // with new Cloisons values
                             const newModeleUID = sender.properties.getPropValue("modeleCloisons");
                             (sender.nub as PabCloisons).setModel(newModeleUID);
                             // treat the fieldset as new to re-subscribe to Nub properties change events
                             this.afterParseFieldset(sender);
                             this.reset();
+                            break;
+                    }
+                    break;
 
-                            // this.dumpPabStructure(this.currentNub as Pab);
+                case "fs_cloison_aval":
+                    switch (data.name) {
+                        case "modeleCloisonAval":
+                            // change Pab property "modeleCloisonAval" and reinit it
+                            // with new ParallelStructure values
+                            const newModeleAvalUID = sender.properties.getPropValue("modeleCloisonAval");
+                            this.pabNub.setDownWall(newModeleAvalUID);
+                            // treat the fieldset as new to re-subscribe to Nub properties change events
+                            this.afterParseFieldset(sender);
+                            this.reset();
                             break;
                     }
                     break;
-- 
GitLab


From 3c507ea7ffea28735271eadf2600a48ff71d2542 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Fri, 10 May 2019 16:51:52 +0200
Subject: [PATCH 24/44] =?UTF-8?q?Validation=20des=20s=C3=A9lecteurs=20de?=
 =?UTF-8?q?=20mod=C3=A8les:=20ne=20doivent=20pas=20=C3=AAtre=20vides?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../field-set/field-set.component.ts          | 35 +++++++++++++++++--
 .../select-model-field-line.component.ts      |  4 +++
 2 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/src/app/components/field-set/field-set.component.ts b/src/app/components/field-set/field-set.component.ts
index 82b34b5a1..2fd4c2ad4 100644
--- a/src/app/components/field-set/field-set.component.ts
+++ b/src/app/components/field-set/field-set.component.ts
@@ -8,6 +8,7 @@ import { InputField } from "../../formulaire/input-field";
 import { SelectField } from "../../formulaire/select-field";
 import { SelectFieldModel } from "../../formulaire/select-field-model";
 import { FormulairePab } from "../../formulaire/definition/concrete/form-pab";
+import { SelectModelFieldLineComponent } from "../select-model-field-line/select-model-field-line.component";
 
 @Component({
     selector: "field-set",
@@ -51,6 +52,9 @@ export class FieldSetComponent implements DoCheck {
     @ViewChildren(ParamFieldLineComponent)
     private _paramComponents: QueryList<ParamFieldLineComponent>;
 
+    @ViewChildren(SelectModelFieldLineComponent)
+    private _selectModelComponents: QueryList<SelectModelFieldLineComponent>;
+
     /**
      * événement de changement de validité
      */
@@ -205,13 +209,17 @@ export class FieldSetComponent implements DoCheck {
     }
 
     /**
-     * calcul de la validité de tous les ParamFieldLineComponent de la vue
+     * calcul de la validité de tous les ParamFieldLineComponent et tous les
+     * SelectModelFieldLineComponent de la vue
      */
     private updateValidity() {
         this._isValid = false;
+        let paramsAreValid = true;
+        let selectAreValid = true;
 
         if (this._paramComponents) {
-            this._isValid = this._paramComponents.reduce(
+            paramsAreValid = false;
+            paramsAreValid = this._paramComponents.reduce(
                 // callback
                 (
                     // accumulator (valeur précédente du résultat)
@@ -229,6 +237,29 @@ export class FieldSetComponent implements DoCheck {
                 , true);
         }
 
+        if (this._selectModelComponents) {
+            selectAreValid = false;
+            selectAreValid = this._selectModelComponents.reduce(
+                // callback
+                (
+                    // accumulator (valeur précédente du résultat)
+                    acc,
+                    // currentValue (élément courant dans le tableau)
+                    select,
+                    // currentIndex (indice courant dans le tableau)
+                    currIndex,
+                    // array (tableau parcouru)
+                    array
+                ) => {
+                    return acc && select.isValid;
+                }
+                // valeur initiale
+                , true);
+        }
+
+        // global validity
+        this._isValid = (paramsAreValid && selectAreValid);
+
         this.validChange.emit();
     }
 
diff --git a/src/app/components/select-model-field-line/select-model-field-line.component.ts b/src/app/components/select-model-field-line/select-model-field-line.component.ts
index 306a8993c..36921d55e 100644
--- a/src/app/components/select-model-field-line/select-model-field-line.component.ts
+++ b/src/app/components/select-model-field-line/select-model-field-line.component.ts
@@ -37,6 +37,10 @@ export class SelectModelFieldLineComponent implements OnInit {
         return this.selectedValue ? this.selectedValue.id : "";
     }
 
+    public get isValid(): boolean {
+        return (this._select.getValue() !== undefined);
+    }
+
     // called every time we navigate to the module
     ngOnInit(): void {
         this._select.updateEntries();
-- 
GitLab


From efed8c3b0554574d909d5ce42831bf67146619a9 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Fri, 10 May 2019 17:46:12 +0200
Subject: [PATCH 25/44] Correction erreurs
 ExpressionChangedAfterItHasBeenChecked

---
 .../field-set/field-set.component.ts          | 54 +++++++++++--------
 .../fieldset-container.component.ts           | 25 +--------
 .../definition/concrete/form-pab.ts           |  2 +-
 src/app/formulaire/fieldset-container.ts      | 10 ++++
 src/app/formulaire/fieldset.ts                |  9 ++++
 src/app/formulaire/formulaire-element.ts      | 10 ++--
 6 files changed, 57 insertions(+), 53 deletions(-)

diff --git a/src/app/components/field-set/field-set.component.ts b/src/app/components/field-set/field-set.component.ts
index 2fd4c2ad4..e1e2eb9b6 100644
--- a/src/app/components/field-set/field-set.component.ts
+++ b/src/app/components/field-set/field-set.component.ts
@@ -9,6 +9,7 @@ import { SelectField } from "../../formulaire/select-field";
 import { SelectFieldModel } from "../../formulaire/select-field-model";
 import { FormulairePab } from "../../formulaire/definition/concrete/form-pab";
 import { SelectModelFieldLineComponent } from "../select-model-field-line/select-model-field-line.component";
+import { FieldsetContainer } from "../../formulaire/fieldset-container";
 
 @Component({
     selector: "field-set",
@@ -26,9 +27,9 @@ export class FieldSetComponent implements DoCheck {
         return this._fieldSet.kids;
     }
 
-    public set fieldsetNumber(n: number) {
+    /* public set fieldsetNumber(n: number) {
         this._fieldSet.labelNumber = n;
-    }
+    } */
 
     public get title(): string {
         if (! this._fieldSet) {
@@ -44,6 +45,35 @@ export class FieldSetComponent implements DoCheck {
         return this._isValid;
     }
 
+    /** flag d'affichage des boutons ajouter, supprimer, monter, descendre */
+    public get showButtons() {
+        return (this._fieldSet.parent instanceof FieldsetContainer);
+    }
+
+    /** flag d'activation du bouton monter */
+    public get enableUpButton() {
+        if (this._fieldSet.parent instanceof FieldsetContainer) {
+            return this._fieldSet.parent.getFieldsetPosition(this._fieldSet) !== 0;
+        }
+        return false;
+    }
+
+    /** flag d'activation du bouton descendre */
+    public get enableDownButton() {
+        if (this._fieldSet.parent instanceof FieldsetContainer) {
+            return this._fieldSet.parent.getFieldsetPosition(this._fieldSet) < this._fieldSet.parent.fieldsets.length - 1;
+        }
+        return false;
+    }
+
+    /** flag d'activation du bouton supprimer */
+    public get enableRemoveButton() {
+        if (this._fieldSet.parent instanceof FieldsetContainer) {
+            return this._fieldSet.parent.fieldsets.length > 0;
+        }
+        return false;
+    }
+
     /**
     * field set attribute
     */
@@ -96,26 +126,6 @@ export class FieldSetComponent implements DoCheck {
      */
     private _isValid = false;
 
-    /**
-     * flag d'affichage des boutons ajouter, supprimer, monter, descendre
-     */
-    public showButtons = false;
-
-    /**
-     * flag d'activation du bouton monter
-     */
-    public enableUpButton = true;
-
-    /**
-     * flag d'activation du bouton descendre
-     */
-    public enableDownButton = true;
-
-    /**
-     * flag d'activation du bouton supprimer
-     */
-    public enableRemoveButton = true;
-
     /**
      * événement de changement d'état d'un radio
      */
diff --git a/src/app/components/fieldset-container/fieldset-container.component.ts b/src/app/components/fieldset-container/fieldset-container.component.ts
index 3753cc223..ede0fd363 100644
--- a/src/app/components/fieldset-container/fieldset-container.component.ts
+++ b/src/app/components/fieldset-container/fieldset-container.component.ts
@@ -77,34 +77,13 @@ export class FieldsetContainerComponent implements DoCheck, AfterViewInit {
     }
 
     private onFieldsetListChange() {
-        // affichage des boutons ajouter, supprimer, monter, descendre
-        this._fieldsetComponents.forEach(fs => fs.showButtons = true);
-
-        // désactivation du bouton supprimer s'il n'en reste qu'un
-        if (this._fieldsetComponents.length === 1) {
-            const fs = this._fieldsetComponents.last as FieldSetComponent;
-            fs.enableRemoveButton = false;
-        } else {
-            this._fieldsetComponents.forEach(fs => fs.enableRemoveButton = true);
-        }
-
-        // désactivation du bouton monter pour le 1er
-        this._fieldsetComponents.forEach(fs => {
-            fs.enableUpButton = true;
-            fs.enableDownButton = true;
-        });
-        this._fieldsetComponents.first.enableUpButton = false;
-
-        // désactivation du bouton monter pour le dernier
-        this._fieldsetComponents.last.enableDownButton = false;
-
         // renumérotation
-        let n = 1;
+        /* let n = 1;
         this._fieldsetComponents.forEach(fsc => {
             fsc.fieldsetNumber = n;
             n++;
         }
-        );
+        ); */
     }
 
     public ngAfterViewInit() {
diff --git a/src/app/formulaire/definition/concrete/form-pab.ts b/src/app/formulaire/definition/concrete/form-pab.ts
index 3a5bac9c4..71243ff67 100644
--- a/src/app/formulaire/definition/concrete/form-pab.ts
+++ b/src/app/formulaire/definition/concrete/form-pab.ts
@@ -1,4 +1,4 @@
-import { Nub, Props, Session, PabCloisons, Pab, Cloisons } from "jalhyd";
+import { Nub, Props, Session, PabCloisons, Pab } from "jalhyd";
 
 import { FormResultFixedVar } from "../form-result-fixedvar";
 import { FieldsetContainer } from "../../fieldset-container";
diff --git a/src/app/formulaire/fieldset-container.ts b/src/app/formulaire/fieldset-container.ts
index b94eac578..0884e2111 100644
--- a/src/app/formulaire/fieldset-container.ts
+++ b/src/app/formulaire/fieldset-container.ts
@@ -40,6 +40,16 @@ export class FieldsetContainer extends FormulaireElement {
         this.fieldsets.push(fs);
     }
 
+    public getFieldsetPosition(fs: FieldSet) {
+        let i = 0;
+        for (const f of this.kids) {
+            if (f.uid === fs.uid ) {
+                return i;
+            }
+            i++;
+        }
+    }
+
     public moveFieldsetUp(fs: FieldSet) {
         let i = 0;
         for (const f of this.kids) {
diff --git a/src/app/formulaire/fieldset.ts b/src/app/formulaire/fieldset.ts
index 0aa18c2e5..2f21b31fe 100644
--- a/src/app/formulaire/fieldset.ts
+++ b/src/app/formulaire/fieldset.ts
@@ -10,6 +10,7 @@ import { StringMap } from "../stringmap";
 import { FormulaireNode } from "./formulaire-node";
 import { SelectFieldModelCloisons } from "./select-field-model-cloisons";
 import { SelectFieldModelCloisonAval } from "./select-field-model-cloison-aval";
+import { FieldsetContainer } from "./fieldset-container";
 
 export class FieldSet extends FormulaireElement implements Observer {
     /**
@@ -35,6 +36,14 @@ export class FieldSet extends FormulaireElement implements Observer {
         return this._nub;
     }
 
+    get label(): string {
+        let label = super.label;
+        if (this.parent instanceof FieldsetContainer) {
+            label += " n°" + String(this.parent.getFieldsetPosition(this) + 1);
+        }
+        return label;
+    }
+
     public setNub(sn: Nub, update: boolean = true) {
         this._nub = sn;
         this.setParentNubForAllFields();
diff --git a/src/app/formulaire/formulaire-element.ts b/src/app/formulaire/formulaire-element.ts
index 95ddd4437..60ee03bda 100644
--- a/src/app/formulaire/formulaire-element.ts
+++ b/src/app/formulaire/formulaire-element.ts
@@ -56,16 +56,12 @@ export abstract class FormulaireElement extends FormulaireNode {
     }
 
     get label(): string {
-        let res = this._label;
-        if (res && this._labelNumber) {
-            res += " n°" + String(this._labelNumber);
-        }
-        return res;
+        return this._label;
     }
 
-    set labelNumber(n: number) {
+    /* set labelNumber(n: number) {
         this._labelNumber = n;
-    }
+    } */
 
     public getKids(): FormulaireElement[] {
         return this.kids as FormulaireElement[];
-- 
GitLab


From a3728c2e8f7272242929725a70ae4dba543dee8d Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Mon, 13 May 2019 10:17:16 +0200
Subject: [PATCH 26/44] =?UTF-8?q?Suppression=20code=20inutilis=C3=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../definition/form-compute-fixedvar.ts       | 10 -----
 .../formulaire/definition/form-compute-pab.ts | 37 +------------------
 .../form-compute-parallel-structures.ts       | 37 +------------------
 3 files changed, 2 insertions(+), 82 deletions(-)

diff --git a/src/app/formulaire/definition/form-compute-fixedvar.ts b/src/app/formulaire/definition/form-compute-fixedvar.ts
index 69995f72b..875a52747 100644
--- a/src/app/formulaire/definition/form-compute-fixedvar.ts
+++ b/src/app/formulaire/definition/form-compute-fixedvar.ts
@@ -32,16 +32,6 @@ export class FormComputeFixedVar extends FormCompute {
         return this._formBase.getDisplayedParamFromState(ParamRadioConfig.CAL);
     }
 
-    /**
-     * fixe la valeur d'un paramètre de ComputeNode
-     * @param node ComputeNode contenant le paramètre
-     * @param p paramètre dont on fixe la valeur
-     * @param val valeur à fixer
-     */
-    protected setParameterValue(node: ComputeNode, p: NgParameter, val: number) {
-        node.getParameter(p.symbol).v = val;
-    }
-
     protected compute() {
         this.runNubCalc(this._formBase.currentNub);
         this.reaffectResultComponents();
diff --git a/src/app/formulaire/definition/form-compute-pab.ts b/src/app/formulaire/definition/form-compute-pab.ts
index 12bb58180..888019181 100644
--- a/src/app/formulaire/definition/form-compute-pab.ts
+++ b/src/app/formulaire/definition/form-compute-pab.ts
@@ -1,12 +1,8 @@
-import { ComputeNode, ParamDefinition, PabCloisons, Pab } from "jalhyd";
+import { ParamDefinition, PabCloisons, Pab } from "jalhyd";
 
 import { FormComputeFixedVar } from "./form-compute-fixedvar";
 import { FormResultFixedVar } from "./form-result-fixedvar";
 import { FormulaireDefinition } from "./form-definition";
-import { NgParameter } from "../ngparam";
-import { FormulaireNode } from "../formulaire-node";
-import { FieldSet } from "../fieldset";
-import { FieldsetContainer } from "../fieldset-container";
 
 export class FormComputePab extends FormComputeFixedVar {
 
@@ -14,26 +10,6 @@ export class FormComputePab extends FormComputeFixedVar {
         super(formBase, formResult);
     }
 
-    /**
-     * @return dans le cas d'un paramètre de bassin, le FieldSet parent d'un paramètre,
-     * le FieldsetContainer parent du FieldSet
-     * ainsi que l'indice du FieldSet parent dans le FieldsetContainer
-     */
-    private structureParents(p: NgParameter): [FieldsetContainer, FieldSet, number] {
-        const parent1: FormulaireNode = p.parent;
-        if (parent1 !== undefined) {
-            if (parent1 instanceof FieldSet) {
-                const parent2 = parent1.parent;
-                if (parent2 instanceof FieldsetContainer) {
-                    const fsIndex = parent1.indexAsKid();
-                    return [parent2, parent1, fsIndex];
-                }
-            }
-        }
-
-        return [undefined, undefined, -1];
-    }
-
     /**
      * construit un identifiant de type { uid: "abcdef", symbol: "X" }
      * avec "abcdef" l'index du bassin et "X" son paramètre
@@ -49,15 +25,4 @@ export class FormComputePab extends FormComputeFixedVar {
             return super.getParameterRefid(p);
         }
     }
-
-    protected setParameterValue(node: ComputeNode, p: NgParameter, val: number) {
-        const [fsc, fs, i] = this.structureParents(p);
-        if (i === -1) {
-            super.setParameterValue(node, p, val);
-        } else {
-            const n: Pab = node as Pab;
-            const prm = n.children[i].getParameter(p.symbol);
-            prm.v = val;
-        }
-    }
 }
diff --git a/src/app/formulaire/definition/form-compute-parallel-structures.ts b/src/app/formulaire/definition/form-compute-parallel-structures.ts
index e05615dd7..d0a1df40b 100644
--- a/src/app/formulaire/definition/form-compute-parallel-structures.ts
+++ b/src/app/formulaire/definition/form-compute-parallel-structures.ts
@@ -1,12 +1,8 @@
-import { ComputeNode, ParallelStructure, Structure, ParamDefinition } from "jalhyd";
+import { Structure, ParamDefinition } from "jalhyd";
 
 import { FormComputeFixedVar } from "./form-compute-fixedvar";
 import { FormResultFixedVar } from "./form-result-fixedvar";
 import { FormulaireDefinition } from "./form-definition";
-import { NgParameter } from "../ngparam";
-import { FormulaireNode } from "../formulaire-node";
-import { FieldSet } from "../fieldset";
-import { FieldsetContainer } from "../fieldset-container";
 
 export class FormComputeParallelStructures extends FormComputeFixedVar {
 
@@ -14,26 +10,6 @@ export class FormComputeParallelStructures extends FormComputeFixedVar {
         super(formBase, formResult);
     }
 
-    /**
-     * @return dans le cas d'un paramètre d'ouvrage, le FieldSet parent d'un paramètre,
-     * le FieldsetContainer parent du FieldSet
-     * ainsi que l'indice du FieldSet parent dans le FieldsetContainer
-     */
-    private structureParents(p: NgParameter): [FieldsetContainer, FieldSet, number] {
-        const parent1: FormulaireNode = p.parent;
-        if (parent1 !== undefined) {
-            if (parent1 instanceof FieldSet) {
-                const parent2 = parent1.parent;
-                if (parent2 instanceof FieldsetContainer) {
-                    const fsIndex = parent1.indexAsKid();
-                    return [parent2, parent1, fsIndex];
-                }
-            }
-        }
-
-        return [undefined, undefined, -1];
-    }
-
     /**
      * construit un identifiant de type { uid: "abcdef", symbol: "X" }
      * avec "abcdef" l'index de l'ouvrage et "X" son paramètre
@@ -49,15 +25,4 @@ export class FormComputeParallelStructures extends FormComputeFixedVar {
             return super.getParameterRefid(p);
         }
     }
-
-    protected setParameterValue(node: ComputeNode, p: NgParameter, val: number) {
-        const [fsc, fs, i] = this.structureParents(p);
-        if (i === -1) {
-            super.setParameterValue(node, p, val);
-        } else {
-            const n: ParallelStructure = node as ParallelStructure;
-            const prm = n.structures[i].getParameter(p.symbol);
-            prm.v = val;
-        }
-    }
 }
-- 
GitLab


From e2f4144ae789839a18508a13a7fbecd5a4480b20 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Mon, 13 May 2019 16:09:56 +0200
Subject: [PATCH 27/44] =?UTF-8?q?Composant=20pour=20les=20r=C3=A9sultats?=
 =?UTF-8?q?=20de=20PAB=20(fixes)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/app/app.module.ts                         |   6 +-
 .../calculator-results.component.html         |   1 +
 .../calculator-results.component.ts           |  10 ++
 .../fixedvar-results.component.scss           |  21 +++
 .../fixedvar-results.component.ts             |  22 +--
 .../fixedvar-results/var-results.component.ts |  10 +-
 .../pab-results-table.component.html          |  32 ++++
 .../pab-results-table.component.scss          |  63 +++++++
 .../pab-results-table.component.ts            | 155 ++++++++++++++++++
 .../pab-results/pab-results.component.html    |  11 ++
 .../pab-results/pab-results.component.ts      | 126 ++++++++++++++
 .../definition/concrete/form-base.ts          |   2 +-
 .../definition/concrete/form-pab.ts           |   6 +-
 .../concrete/form-parallel-structures.ts      |   2 +-
 .../formulaire/definition/form-compute-pab.ts |  65 ++++++--
 .../definition/form-result-fixedvar.ts        |  10 +-
 .../formulaire/definition/form-result-pab.ts  |  33 ++++
 src/app/results/pab-results.ts                |  34 ++++
 src/locale/messages.en.json                   |   7 +
 src/locale/messages.fr.json                   |   7 +
 20 files changed, 569 insertions(+), 54 deletions(-)
 create mode 100644 src/app/components/fixedvar-results/fixedvar-results.component.scss
 create mode 100644 src/app/components/pab-results/pab-results-table.component.html
 create mode 100644 src/app/components/pab-results/pab-results-table.component.scss
 create mode 100644 src/app/components/pab-results/pab-results-table.component.ts
 create mode 100644 src/app/components/pab-results/pab-results.component.html
 create mode 100644 src/app/components/pab-results/pab-results.component.ts
 create mode 100644 src/app/formulaire/definition/form-result-pab.ts
 create mode 100644 src/app/results/pab-results.ts

diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 09827b43e..6b7d73fd1 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -63,6 +63,8 @@ import { CalcCanvasComponent } from "./components/canvas/canvas.component";
 import { SectionCanvasComponent } from "./components/section-canvas/section-canvas.component";
 import { RemousResultsComponent } from "./components/remous-results/remous-results.component";
 import { ResultsGraphComponent } from "./components/results-graph/results-graph.component";
+import { PabResultsComponent } from "./components/pab-results/pab-results.component";
+import { PabResultsTableComponent } from "./components/pab-results/pab-results-table.component";
 import { GraphTypeSelectComponent } from "./components/results-graph/graph-type.component";
 import { LogComponent } from "./components/log/log.component";
 import { CalculatorListComponent } from "./components/calculator-list/calculator-list.component";
@@ -72,6 +74,7 @@ import { FixedResultsComponent } from "./components/fixedvar-results/fixed-resul
 import { VarResultsComponent } from "./components/fixedvar-results/var-results.component";
 import { LogEntryComponent } from "./components/log-entry/log-entry.component";
 import { ParamLinkComponent } from "./components/param-link/param-link.component";
+import { SelectModelFieldLineComponent } from "./components/select-model-field-line/select-model-field-line.component";
 
 import { DialogConfirmEmptySessionComponent } from "./components/dialog-confirm-empty-session/dialog-confirm-empty-session.component";
 import { DialogConfirmCloseCalcComponent } from "./components/dialog-confirm-close-calc/dialog-confirm-close-calc.component";
@@ -89,7 +92,6 @@ import {
   JalhydModelValidationStepDirective
 } from "./directives/jalhyd-model-validation.directive";
 import { ImmediateErrorStateMatcher } from "./formulaire/immediate-error-state-matcher";
-import { SelectModelFieldLineComponent } from "./components/select-model-field-line/select-model-field-line.component";
 
 const appRoutes: Routes = [
   { path: "list", component: CalculatorListComponent },
@@ -168,6 +170,8 @@ const appRoutes: Routes = [
     LogComponent,
     LogEntryComponent,
     NgParamInputComponent,
+    PabResultsComponent,
+    PabResultsTableComponent,
     ParamComputedComponent,
     ParamFieldLineComponent,
     ParamLinkComponent,
diff --git a/src/app/components/calculator-results/calculator-results.component.html b/src/app/components/calculator-results/calculator-results.component.html
index 03c60dfa0..be99ed81b 100644
--- a/src/app/components/calculator-results/calculator-results.component.html
+++ b/src/app/components/calculator-results/calculator-results.component.html
@@ -1,5 +1,6 @@
 <div>
     <section-results></section-results>
     <remous-results></remous-results>
+    <pab-results></pab-results>
     <fixedvar-results></fixedvar-results>
 </div>
diff --git a/src/app/components/calculator-results/calculator-results.component.ts b/src/app/components/calculator-results/calculator-results.component.ts
index c6489fa69..514a86101 100644
--- a/src/app/components/calculator-results/calculator-results.component.ts
+++ b/src/app/components/calculator-results/calculator-results.component.ts
@@ -3,6 +3,7 @@ import { Component, ViewChild, Output, EventEmitter, AfterViewChecked } from "@a
 import { FixedVarResultsComponent } from "../../components/fixedvar-results/fixedvar-results.component";
 import { SectionResultsComponent } from "../../components/section-results/section-results.component";
 import { RemousResultsComponent } from "../../components/remous-results/remous-results.component";
+import { PabResultsComponent } from "../../components/pab-results/pab-results.component";
 import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
 
 @Component({
@@ -30,6 +31,12 @@ export class CalculatorResultsComponent implements AfterViewChecked {
     @ViewChild(RemousResultsComponent)
     private remousResultsComponent: RemousResultsComponent;
 
+    /**
+     * composant d'affichage des résultats des passes à bassins
+     */
+    @ViewChild(PabResultsComponent)
+    private pabResultsComponent: PabResultsComponent;
+
     /**
      * événement émis à la fin du dessin de la vue
      */
@@ -42,10 +49,12 @@ export class CalculatorResultsComponent implements AfterViewChecked {
             this.fixedVarResultsComponent.results = undefined;
             this.sectionResultsComponent.results = undefined;
             this.remousResultsComponent.results = undefined;
+            this.pabResultsComponent.results = undefined;
         } else {
             this.fixedVarResultsComponent.results = f.results;
             this.sectionResultsComponent.results = f.results;
             this.remousResultsComponent.results = f.results;
+            this.pabResultsComponent.results = f.results;
         }
     }
 
@@ -53,6 +62,7 @@ export class CalculatorResultsComponent implements AfterViewChecked {
         this.fixedVarResultsComponent.updateView();
         this.sectionResultsComponent.updateView();
         this.remousResultsComponent.updateView();
+        this.pabResultsComponent.updateView();
     }
 
     public ngAfterViewChecked() {
diff --git a/src/app/components/fixedvar-results/fixedvar-results.component.scss b/src/app/components/fixedvar-results/fixedvar-results.component.scss
new file mode 100644
index 000000000..6b464cc79
--- /dev/null
+++ b/src/app/components/fixedvar-results/fixedvar-results.component.scss
@@ -0,0 +1,21 @@
+
+.result_right {
+    text-align: right;
+}
+
+.result_center {
+    text-align: center;
+}
+
+.result_id_0 {
+    background-color: #f0f0f0;
+}
+
+.result_id_1 {
+    background-color: #ffffff;
+}
+
+.result_id_2 {
+    font-weight: bolder;
+    background-color: #00d0ff;
+}
diff --git a/src/app/components/fixedvar-results/fixedvar-results.component.ts b/src/app/components/fixedvar-results/fixedvar-results.component.ts
index 3392fb698..ca402293f 100644
--- a/src/app/components/fixedvar-results/fixedvar-results.component.ts
+++ b/src/app/components/fixedvar-results/fixedvar-results.component.ts
@@ -1,6 +1,5 @@
 import { Component, ViewChild, DoCheck } from "@angular/core";
 
-import { I18nService } from "../../services/internationalisation/internationalisation.service";
 import { LogComponent } from "../../components/log/log.component";
 import { FixedResults } from "../../results/fixed-results";
 import { VarResults } from "../../results/var-results";
@@ -15,24 +14,8 @@ import { VarResultsComponent } from "./var-results.component";
 @Component({
     selector: "fixedvar-results",
     templateUrl: "./fixedvar-results.component.html",
-    styles: [`
-    .result_right {
-        text-align: right;
-    }
-    .result_center {
-        text-align: center;
-    }
-    .result_id_0 {
-        background-color: #f0f0f0;
-    }
-    .result_id_1 {
-        background-color: #ffffff;
-    }
-    .result_id_2 {
-        font-weight: bolder;
-        background-color: #00d0ff;
-    }
-    `
+    styleUrls: [
+        "./fixedvar-results.component.scss"
     ]
 })
 export class FixedVarResultsComponent implements DoCheck {
@@ -66,7 +49,6 @@ export class FixedVarResultsComponent implements DoCheck {
     private resultsGraphComponent: ResultsGraphComponent;
 
     constructor(
-        private intlService: I18nService,
         private appSetupService: ApplicationSetupService,
     ) { }
 
diff --git a/src/app/components/fixedvar-results/var-results.component.ts b/src/app/components/fixedvar-results/var-results.component.ts
index 926e5b2a7..b8185119d 100644
--- a/src/app/components/fixedvar-results/var-results.component.ts
+++ b/src/app/components/fixedvar-results/var-results.component.ts
@@ -15,13 +15,13 @@ import * as XLSX from "xlsx";
 export class VarResultsComponent {
 
     /** résultats non mis en forme */
-    private _varResults: VarResults;
+    protected _varResults: VarResults;
 
     /** résultats mis en forme */
-    private _results: any[];
+    protected _results: any[];
 
     /** entêtes des colonnes (param à varier, à calculer + extraResults) */
-    private _headers: string[];
+    protected _headers: string[];
 
     /** tracks the fullscreen state */
     public get isFullscreen() {
@@ -31,8 +31,8 @@ export class VarResultsComponent {
     @ViewChild("tableContainer") table: ElementRef;
 
     constructor(
-        private appSetupService: ApplicationSetupService,
-        private intlService: I18nService
+        protected appSetupService: ApplicationSetupService,
+        protected intlService: I18nService
     ) { }
 
     public set results(r: VarResults) {
diff --git a/src/app/components/pab-results/pab-results-table.component.html b/src/app/components/pab-results/pab-results-table.component.html
new file mode 100644
index 000000000..bbdbf38db
--- /dev/null
+++ b/src/app/components/pab-results/pab-results-table.component.html
@@ -0,0 +1,32 @@
+<!-- @TODO copied from var-results.component.html > merge ?-->
+<div class="pab-results-table-container" #pabResultsTable *ngIf="hasResults" fxLayout="row wrap" fxLayoutAlign="center center">
+    <div fxFlex="1 1 100%">
+        <div class="pab-results-table-buttons">
+            <button mat-icon-button (click)="exportAsSpreadsheet()">
+                <mat-icon color="primary" svgIcon="file_excel" class="scaled09"></mat-icon>
+            </button>
+            <button mat-icon-button *ngIf="! isFullscreen" (click)="setFullscreen(pabResultsTable)">
+                <mat-icon color="primary" class="scaled12">fullscreen</mat-icon>
+            </button>
+            <button mat-icon-button *ngIf="isFullscreen" (click)="exitFullscreen()">
+                <mat-icon color="primary" class="scaled12">fullscreen_exit</mat-icon>
+            </button>
+        </div>
+
+        <div class="pab-results-table-scrollable-container" [ngClass]="{'full-height': isFullscreen}">
+            <!-- scrollable -->
+            <div class="pab-results-table-inner-container" #tableContainer>
+
+                <table mat-table [dataSource]="dataSet">
+                    <ng-container *ngFor="let h of headers; let i = index" [matColumnDef]="h">
+                        <th mat-header-cell *matHeaderCellDef>{{ h }}</th>
+                        <td mat-cell *matCellDef="let element">{{ element[i] }}</td>
+                    </ng-container>
+
+                    <tr mat-header-row *matHeaderRowDef="headers"></tr>
+                    <tr mat-row *matRowDef="let row; columns: headers;"></tr>
+                </table>
+            </div>
+        </div>
+    </div>
+</div>
diff --git a/src/app/components/pab-results/pab-results-table.component.scss b/src/app/components/pab-results/pab-results-table.component.scss
new file mode 100644
index 000000000..780da209d
--- /dev/null
+++ b/src/app/components/pab-results/pab-results-table.component.scss
@@ -0,0 +1,63 @@
+:host {
+    display: block;
+}
+
+.pab-results-table-container {
+    margin-top: 2em;
+    background-color: white;
+}
+
+.pab-results-table-buttons {
+    padding-right: 4px;
+    padding-top: 4px;
+    text-align: right;
+    background-color: white;
+
+    button {
+        margin-left: 3px;
+        width: auto;
+
+        mat-icon {
+            &.scaled12 {
+                transform: scale(1.2)
+            }
+            &.scaled09 {
+                transform: scale(0.9);
+            }
+        }
+    }
+}
+
+.pab-results-table-scrollable-container {
+    overflow-x: scroll;
+    border: solid #ccc 1px;
+
+    &.full-height {
+        height: calc(100vh - 40px); // rend le mode plein-écran scrollable verticalement, sinon ça dépasse
+    }
+}
+
+table.mat-table {
+
+    .mat-header-row {
+        height: 40px;
+    }
+
+    .mat-row {
+        height: 32px;
+
+        &:nth-child(odd) {
+            background-color: #f4f4f4;
+        }
+    }
+
+    ::ng-deep .mat-cell {
+        padding: 5px;
+    }
+
+    ::ng-deep .mat-header-cell {
+        font-size: 1em;
+        color: black;
+        padding: 5px;
+    }
+}
diff --git a/src/app/components/pab-results/pab-results-table.component.ts b/src/app/components/pab-results/pab-results-table.component.ts
new file mode 100644
index 000000000..da5e5ca79
--- /dev/null
+++ b/src/app/components/pab-results/pab-results-table.component.ts
@@ -0,0 +1,155 @@
+import { Component, ViewChild, ElementRef } from "@angular/core";
+
+import { PabResults } from "../../results/pab-results";
+
+import * as XLSX from "xlsx";
+import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
+
+@Component({
+    selector: "pab-results-table",
+    templateUrl: "./pab-results-table.component.html",
+    styleUrls: [
+        "./pab-results-table.component.scss"
+    ]
+})
+export class PabResultsTableComponent {
+
+    /** résultats non mis en forme */
+    private _pabResults: PabResults;
+
+    /** entêtes des colonnes */
+    private _headers: string[];
+
+    /** résultats mis en forme */
+    private _dataSet: any[];
+
+    /** tracks the fullscreen state */
+    public get isFullscreen() {
+        return (document["fullscreenElement"] !== null);
+    }
+
+    @ViewChild("tableContainer") table: ElementRef;
+
+    constructor(
+        protected appSetupService: ApplicationSetupService,
+        protected intlService: I18nService
+    ) { }
+
+    public set results(r: PabResults) {
+        this._pabResults = r;
+
+        // refresh headers here if language changed
+        this._headers = [
+            this.intlService.localizeText("INFO_EXTRARES_LIB_CLOISON"),
+            this.intlService.localizeText("INFO_EXTRARES_LIB_DH"),
+            this.intlService.localizeText("INFO_EXTRARES_LIB_ZR"),
+            this.intlService.localizeText("INFO_EXTRARES_LIB_Q"),
+            this.intlService.localizeText("INFO_EXTRARES_LIB_Z1_PAB"),
+            this.intlService.localizeText("INFO_EXTRARES_LIB_PV"),
+            this.intlService.localizeText("INFO_EXTRARES_LIB_TMOY"),
+            this.intlService.localizeText("INFO_EXTRARES_LIB_ZRB"),
+            this.intlService.localizeText("INFO_EXTRARES_LIB_QA")
+        ];
+
+        this._dataSet = [];
+        if (this._pabResults) {
+            const pr = this._pabResults;
+            const nDigits = this.appSetupService.displayDigits;
+            // line 1
+            this._dataSet.push([
+                "", "", "", "",
+                this._pabResults.cloisonsResults[0] ? this._pabResults.cloisonsResults[0].vCalc.toFixed(nDigits) : "X",
+                "", "", "", ""
+            ]);
+
+            // lines 2 - n-1
+            for (let i = 0; i < pr.cloisonsResults.length - 1; i++) {
+                if (pr.cloisonsResults[i]) {
+                    const ZRAM = pr.cloisonsResults[i].getExtraResult("ZRAM");
+                    const PV = pr.cloisonsResults[i].getExtraResult("PV");
+                    const Y = pr.cloisonsResults[i].getExtraResult("Y");
+                    const ZRB = pr.cloisonsResults[i].getExtraResult("ZRB");
+                    this._dataSet.push([
+                        i + 1,
+                        (pr.cloisonsResults[i].vCalc - pr.cloisonsResults[i + 1].vCalc).toFixed(nDigits),
+                        ZRAM ? ZRAM.toFixed(nDigits) : "X",
+                        pr.cloisonsQ[i].toFixed(nDigits),
+                        pr.cloisonsResults[i + 1].vCalc.toFixed(nDigits),
+                        PV ? PV.toFixed(nDigits) : "X",
+                        Y ? Y.toFixed(nDigits) : "X",
+                        ZRB ? ZRB.toFixed(nDigits) : "X",
+                        pr.bassinsQA[i]
+                    ]);
+                } else {
+                    // @TODO remove dummy data when "pab.children[0] is undefined" bug is resolved
+                    this._dataSet.push([ "1", "X", "X", "X", "X", "X", "X", "X", "X" ]);
+                }
+            }
+
+            // line n
+            const l = pr.cloisonsResults.length;
+            const nZRAM = pr.cloisonsResults[l - 1].getExtraResult("ZRAM");
+            const nPV = pr.cloisonsResults[l - 1].getExtraResult("PV");
+            const nY = pr.cloisonsResults[l - 1].getExtraResult("Y");
+            const nZRB = pr.cloisonsResults[l - 1].getExtraResult("ZRB");
+            this._dataSet.push([
+                l,
+                (pr.cloisonsResults[l - 1].vCalc - pr.cloisonAvalResults.vCalc).toFixed(nDigits),
+                nZRAM ? nZRAM.toFixed(nDigits) : "X",
+                pr.cloisonsQ[l - 1].toFixed(nDigits),
+                pr.cloisonAvalResults.vCalc.toFixed(nDigits),
+                nPV ? nPV.toFixed(nDigits) : "X",
+                nY ? nY.toFixed(nDigits) : "X",
+                nZRB ? nZRB.toFixed(nDigits) : "X",
+                pr.bassinsQA[l - 1]
+            ]);
+
+            // downstream line
+            const caZRAM = pr.cloisonAvalResults.getExtraResult("ZRAM");
+            this._dataSet.push([
+                this.intlService.localizeText("INFO_EXTRARES_LIB_AVAL"),
+                (pr.cloisonAvalResults.vCalc - pr.Z2).toFixed(nDigits),
+                caZRAM ? caZRAM.toFixed(nDigits) : "X",
+                pr.cloisonAvalQ.toFixed(nDigits),
+                pr.Z2.toFixed(nDigits),
+                "",
+                "",
+                "",
+                ""
+            ]);
+        }
+    }
+
+    public get hasResults(): boolean {
+        return this._pabResults && this._pabResults.hasResults;
+    }
+
+    public get headers() {
+        return this._headers;
+    }
+
+    /**
+     * Returns a combination of and results and extraResults for mat-table
+     */
+    public get dataSet() {
+        return this._dataSet;
+    }
+
+    public setFullscreen(element) {
+        element.requestFullscreen();
+    }
+
+    public exitFullscreen() {
+        document.exitFullscreen();
+    }
+
+    public exportAsSpreadsheet() {
+        // automagic <table> extraction
+        const ws: XLSX.WorkSheet = XLSX.utils.table_to_sheet(this.table.nativeElement);
+        const wb: XLSX.WorkBook = XLSX.utils.book_new();
+        XLSX.utils.book_append_sheet(wb, ws, "default");
+        // save and download
+        XLSX.writeFile(wb, "VariableResults.xlsx");
+    }
+}
diff --git a/src/app/components/pab-results/pab-results.component.html b/src/app/components/pab-results/pab-results.component.html
new file mode 100644
index 000000000..02aa6f94e
--- /dev/null
+++ b/src/app/components/pab-results/pab-results.component.html
@@ -0,0 +1,11 @@
+<div class="container">
+    <!-- journal -->
+    <log></log>
+
+    <!-- <results-graph *ngIf="showVarResults"></results-graph> -->
+
+    <div>
+        <!-- tableau de résultats -->
+        <pab-results-table [results]="pabResults"></pab-results-table>
+    </div>
+</div>
diff --git a/src/app/components/pab-results/pab-results.component.ts b/src/app/components/pab-results/pab-results.component.ts
new file mode 100644
index 000000000..d787ca36c
--- /dev/null
+++ b/src/app/components/pab-results/pab-results.component.ts
@@ -0,0 +1,126 @@
+import { Component, ViewChild, DoCheck } from "@angular/core";
+
+import { LogComponent } from "../../components/log/log.component";
+import { CalculatorResults } from "../../results/calculator-results";
+import { Result, cLog } from "jalhyd";
+import { NgParameter } from "../../formulaire/ngparam";
+import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
+import { PabResultsTableComponent } from "./pab-results-table.component";
+import { PabResults } from "../../results/pab-results";
+
+@Component({
+    selector: "pab-results",
+    templateUrl: "./pab-results.component.html",
+    styleUrls: [
+        "../fixedvar-results/fixedvar-results.component.scss"
+    ]
+})
+export class PabResultsComponent implements DoCheck {
+
+    /** résultats non mis en forme */
+    private _pabResults: PabResults;
+
+    /** true si les résultats doiventt être remis à jour */
+    private _doUpdate = false;
+
+    @ViewChild(PabResultsTableComponent)
+    private pabResultsTableComponent: PabResultsTableComponent;
+
+    /** composant journal */
+    @ViewChild(LogComponent)
+    private logComponent: LogComponent;
+
+    constructor(
+        private appSetupService: ApplicationSetupService,
+    ) { }
+
+    public set results(rs: CalculatorResults[]) {
+        if (rs.length > 0) {
+            this._pabResults = rs[0] as PabResults;
+        }
+        this.updateView();
+    }
+
+    public updateView() {
+        this.logComponent.log = undefined;
+        if (this.pabResultsTableComponent) {
+            this.pabResultsTableComponent.results = undefined;
+        }
+        // set _doUpdate flag so that results are rebuilt on the next Angular display cycle
+        this._doUpdate = false;
+        if (this._pabResults !== undefined) {
+            this._doUpdate = this._doUpdate || this._pabResults.hasResults || this._pabResults.hasLog;
+        }
+    }
+
+    public ngDoCheck() {
+        if (this._doUpdate) {
+            this._doUpdate = !this.updateResults();
+        }
+    }
+
+    private mergeLog(result: Result, log: cLog) {
+        if (result && result.hasLog) {
+            if (result.hasGlobalLog) {
+                log.addLog(result.globalLog);
+            } else {
+                log.addLog(result.log);
+            }
+        }
+    }
+
+    private get mergedGlobalLogs(): cLog {
+        const res = new cLog();
+        if (this._pabResults) {
+            this.mergeLog(this._pabResults.result, res);
+        }
+        return res;
+    }
+
+    /**
+     * met à jour l'affichage des résultats
+     * @returns true si les résultats ont pu être mis à jour
+     */
+    private updateResults() {
+        let pabUpdated: boolean;
+        if (this.hasResults) {
+            pabUpdated = this.pabResultsTableComponent !== undefined;
+            if (pabUpdated) {
+                this.pabResultsTableComponent.results = this._pabResults;
+            }
+        } else {
+            pabUpdated = true;
+        }
+
+        const logUpdated = this.logComponent !== undefined;
+        if (logUpdated) {
+            this.logComponent.log = this.mergedGlobalLogs;
+        }
+
+        return pabUpdated && logUpdated;
+    }
+
+    /**
+     * affichage de la table des résultats de passe à bassins
+     */
+    public get showPabResults(): boolean {
+        return this.hasResults;
+    }
+
+    public get pabResults() {
+        return this._pabResults;
+    }
+
+    public formattedLabel(p: NgParameter): string {
+        return CalculatorResults.paramLabel(p, false);
+    }
+
+    public formattedValue(p: NgParameter): string {
+        const nDigits = this.appSetupService.displayDigits;
+        return p.getValue().toFixed(nDigits);
+    }
+
+    public get hasResults(): boolean {
+        return this._pabResults && this._pabResults.hasResults;
+    }
+}
diff --git a/src/app/formulaire/definition/concrete/form-base.ts b/src/app/formulaire/definition/concrete/form-base.ts
index d033e5076..e54548b98 100644
--- a/src/app/formulaire/definition/concrete/form-base.ts
+++ b/src/app/formulaire/definition/concrete/form-base.ts
@@ -14,7 +14,7 @@ export class FormulaireBase extends FormulaireDefinition {
 
     constructor() {
         super();
-        this._formResult = new FormResultFixedVar(this, false);
+        this._formResult = new FormResultFixedVar(this);
         this._formCompute = new FormComputeFixedVar(this, (this._formResult as FormResultFixedVar));
     }
 
diff --git a/src/app/formulaire/definition/concrete/form-pab.ts b/src/app/formulaire/definition/concrete/form-pab.ts
index 71243ff67..18b63e068 100644
--- a/src/app/formulaire/definition/concrete/form-pab.ts
+++ b/src/app/formulaire/definition/concrete/form-pab.ts
@@ -1,6 +1,5 @@
 import { Nub, Props, Session, PabCloisons, Pab } from "jalhyd";
 
-import { FormResultFixedVar } from "../form-result-fixedvar";
 import { FieldsetContainer } from "../../fieldset-container";
 import { FieldSet } from "../../fieldset";
 import { SelectField } from "../../select-field";
@@ -9,6 +8,7 @@ import { FieldsetTemplate } from "../../fieldset-template";
 import { FormulaireNode } from "../../formulaire-node";
 import { FormulaireBase } from "./form-base";
 import { FormComputePab } from "../form-compute-pab";
+import { FormResultPab } from "../form-result-pab";
 
 /**
  * Formulaire pour les passes à bassins, inspiré du formulaire
@@ -24,11 +24,11 @@ export class FormulairePab extends FormulaireBase {
 
     constructor() {
         super();
-        this._formResult = new FormResultFixedVar(this, false);
+        this._formResult = new FormResultPab(this);
 
         // remove obsolete observer set by super()
         this.removeObserver(this._formCompute);
-        this._formCompute = new FormComputePab(this, (this._formResult as FormResultFixedVar));
+        this._formCompute = new FormComputePab(this, (this._formResult as FormResultPab));
     }
 
     public get modeleCloisonsSelectId(): string {
diff --git a/src/app/formulaire/definition/concrete/form-parallel-structures.ts b/src/app/formulaire/definition/concrete/form-parallel-structures.ts
index bc954c296..ada70cc18 100644
--- a/src/app/formulaire/definition/concrete/form-parallel-structures.ts
+++ b/src/app/formulaire/definition/concrete/form-parallel-structures.ts
@@ -19,7 +19,7 @@ export class FormulaireParallelStructure extends FormulaireBase {
 
     constructor() {
         super();
-        this._formResult = new FormResultFixedVar(this, false);
+        this._formResult = new FormResultFixedVar(this);
 
         // remove obsolete observer set by super()
         this.removeObserver(this._formCompute);
diff --git a/src/app/formulaire/definition/form-compute-pab.ts b/src/app/formulaire/definition/form-compute-pab.ts
index 888019181..a3e1d2d22 100644
--- a/src/app/formulaire/definition/form-compute-pab.ts
+++ b/src/app/formulaire/definition/form-compute-pab.ts
@@ -1,28 +1,57 @@
-import { ParamDefinition, PabCloisons, Pab } from "jalhyd";
+import { Result, Pab } from "jalhyd";
 
-import { FormComputeFixedVar } from "./form-compute-fixedvar";
-import { FormResultFixedVar } from "./form-result-fixedvar";
 import { FormulaireDefinition } from "./form-definition";
+import { FormResultPab } from "./form-result-pab";
+import { FormCompute } from "./form-compute";
+import { NgParameter, ParamRadioConfig } from "../ngparam";
 
-export class FormComputePab extends FormComputeFixedVar {
+export class FormComputePab extends FormCompute {
 
-    constructor(formBase: FormulaireDefinition, formResult: FormResultFixedVar) {
+    constructor(formBase: FormulaireDefinition, formResult: FormResultPab) {
         super(formBase, formResult);
     }
 
-    /**
-     * construit un identifiant de type { uid: "abcdef", symbol: "X" }
-     * avec "abcdef" l'index du bassin et "X" son paramètre
-     */
-    protected getParameterRefid(p: ParamDefinition): any {
-        const nub = p.parentComputeNode;
-        if (nub instanceof PabCloisons) {
-            return {
-                uid: nub.uid,
-                symbol: p.symbol
-            };
-        } else {
-            return super.getParameterRefid(p);
+    protected get formResult(): FormResultPab {
+        return this._formResult as FormResultPab;
+    }
+
+    protected compute() {
+        this.runNubCalc(this._formBase.currentNub);
+        this.reaffectResultComponents();
+    }
+
+    protected reaffectResultComponents() {
+        const pab: Pab = (this._formBase.currentNub as Pab);
+        const computedParam: NgParameter = this.getComputedParameter();
+
+        // résultat de calcul de la passe à bassins
+        const pabr = this.formResult.pabResults;
+        pabr.calculatedParameter = computedParam;
+        pabr.result = pab.result;
+        // résultat de chaque cloison
+        const cr: Result[] = [];
+        // valeur de Q pour chaque cloison
+        const cq: number[] = [];
+        // valeur de QA pour chaque bassin
+        const bqa: number[] = [];
+        console.log("pab.children[0].result >>", pab.children[0].result);
+        for (const c of pab.children) {
+            cr.push(c.result);
+            cq.push(c.prms.Q.v);
+            bqa.push(c.prms.QA.v);
         }
+        pabr.cloisonsResults = cr,
+        pabr.cloisonsQ = cq,
+        pabr.bassinsQA = bqa;
+        // résultat de la cloison aval
+        pabr.cloisonAvalResults = pab.downWall.result;
+        // débit de la cloison aval
+        pabr.cloisonAvalQ = pab.downWall.prms.Q.v;
+        // cote aval de la passe
+        pabr.Z2 = pab.prms.Z2.v;
+    }
+
+    private getComputedParameter(): NgParameter {
+        return this._formBase.getDisplayedParamFromState(ParamRadioConfig.CAL);
     }
 }
diff --git a/src/app/formulaire/definition/form-result-fixedvar.ts b/src/app/formulaire/definition/form-result-fixedvar.ts
index be7137587..53b9c455c 100644
--- a/src/app/formulaire/definition/form-result-fixedvar.ts
+++ b/src/app/formulaire/definition/form-result-fixedvar.ts
@@ -1,6 +1,6 @@
 import { FixedResults } from "../../results/fixed-results";
 import { GraphType, VarResults } from "../../results/var-results";
-import { ParamRadioConfig, NgParameter } from "../ngparam";
+import { ParamRadioConfig } from "../ngparam";
 import { FormResult } from "./form-result";
 import { FormulaireDefinition } from "./form-definition";
 import { CalculatorResults } from "../../results/calculator-results";
@@ -9,12 +9,12 @@ export class FormResultFixedVar extends FormResult {
     /**
      * résultats fixes/variables
      */
-    private _fixedResults: FixedResults;
-    private _varResults: VarResults;
+    protected _fixedResults: FixedResults;
+    protected _varResults: VarResults;
 
-    private _formBase: FormulaireDefinition;
+    protected _formBase: FormulaireDefinition;
 
-    constructor(base: FormulaireDefinition, private displaySymbol: boolean) {
+    constructor(base: FormulaireDefinition) {
         super();
         this._formBase = base;
         this._fixedResults = new FixedResults();
diff --git a/src/app/formulaire/definition/form-result-pab.ts b/src/app/formulaire/definition/form-result-pab.ts
new file mode 100644
index 000000000..5adc86f13
--- /dev/null
+++ b/src/app/formulaire/definition/form-result-pab.ts
@@ -0,0 +1,33 @@
+import { FormulaireDefinition } from "./form-definition";
+import { FormResult } from "./form-result";
+import { CalculatorResults } from "../../results/calculator-results";
+import { PabResults } from "../../results/pab-results";
+
+export class FormResultPab extends FormResult {
+
+    protected _formBase: FormulaireDefinition;
+
+    protected _pabResults: PabResults;
+
+    constructor(base: FormulaireDefinition) {
+        super();
+        this._formBase = base;
+        this._pabResults = new PabResults();
+    }
+
+    public get pabResults() {
+        return this._pabResults;
+    }
+
+    public resetResults() {
+        this._pabResults.reset();
+    }
+
+    public get results(): CalculatorResults[] {
+        return [ this._pabResults ];
+    }
+
+    public get hasResults(): boolean {
+        return this._pabResults.hasResults;
+    }
+}
diff --git a/src/app/results/pab-results.ts b/src/app/results/pab-results.ts
new file mode 100644
index 000000000..c30f8c1ec
--- /dev/null
+++ b/src/app/results/pab-results.ts
@@ -0,0 +1,34 @@
+import { CalculatedParamResults } from "./param-calc-results";
+import { Result } from "jalhyd";
+
+export class PabResults extends CalculatedParamResults {
+
+    /** résultats des modules Cloisons avant chaque bassin */
+    public cloisonsResults: Result[];
+
+    /** valeur de Q pour chaque module Cloisons */
+    public cloisonsQ: number[];
+
+    /** valeur de QA pour chaque bassin */
+    public bassinsQA: number[];
+
+    /** résultats du module ParallelStructure pour la cloison aval */
+    public cloisonAvalResults: Result;
+
+    /** débit de la cloison aval */
+    public cloisonAvalQ: number;
+
+    /** cote aval de l'ensemble de la passe */
+    public Z2: number;
+
+    public constructor() {
+        super();
+        this.reset();
+    }
+
+    public reset() {
+        super.reset();
+        this.cloisonsResults = undefined;
+        this.cloisonAvalResults = undefined;
+    }
+}
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index 67d14da0e..e7385b44c 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -83,9 +83,12 @@
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_1": "Partially submerged",
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_2": "Submerged",
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_3": "Zero flow",
+    "INFO_EXTRARES_LIB_AVAL": "Downstream",
     "INFO_EXTRARES_LIB_B": "Surface width (m)",
+    "INFO_EXTRARES_LIB_CLOISON": "Cross wall n°",
     "INFO_EXTRARES_LIB_CV": "Cv: Velocity coefficient",
     "INFO_EXTRARES_LIB_CVQT": "CV.QT: Corrected discharge (m³/s)",
+    "INFO_EXTRARES_LIB_DH": "Fall (m)",
     "INFO_EXTRARES_LIB_DHR": "DHR : Residual fall (m)",
     "INFO_EXTRARES_LIB_EC": "EC: Kinetic energy (m)",
     "INFO_EXTRARES_LIB_ENUM_MACRORUGOFLOWTYPE": "Flow type",
@@ -101,11 +104,13 @@
     "INFO_EXTRARES_LIB_OUVRAGE_Q_REGIME": "Regime",
     "INFO_EXTRARES_LIB_P": "Wetted perimeter (m)",
     "INFO_EXTRARES_LIB_PV": "Volumic dissipated power (W/m³)",
+    "INFO_EXTRARES_LIB_QA": "Attraction flow (m³/s)",
     "INFO_EXTRARES_LIB_Q": "Flow (m³/s)",
     "INFO_EXTRARES_LIB_Q_GUIDETECH": "Technical guide flow (m³/s)",
     "INFO_EXTRARES_LIB_R": "Hydraulic radius (m)",
     "INFO_EXTRARES_LIB_S": "Wetted area (m²)",
     "INFO_EXTRARES_LIB_TAU0": "Tractive force (Pa)",
+    "INFO_EXTRARES_LIB_TMOY": "Average depth (m)",
     "INFO_EXTRARES_LIB_TOR": "Supercritical water line",
     "INFO_EXTRARES_LIB_V": "Average speed (m/s)",
     "INFO_EXTRARES_LIB_VDEB": "Conveyance speed (m/s)",
@@ -116,6 +121,8 @@
     "INFO_EXTRARES_LIB_YF": "Subcritical depth (m)",
     "INFO_EXTRARES_LIB_YN": "Normal depth (m)",
     "INFO_EXTRARES_LIB_YT": "Supercritical depth (m)",
+    "INFO_EXTRARES_LIB_Z1_PAB": "Water level (m)",
+    "INFO_EXTRARES_LIB_ZR": "Bottom elevation (m)",
     "INFO_EXTRARES_LIB_ZRB": "Downstream basin bottom elevation (m)",
     "INFO_EXTRARES_LIB_ZF2": "Downstream bottom elevation (m)",
     "INFO_LECHAPTCALMON_TITRE": "Lechapt-Calmon",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index a7aaa7a93..61856081f 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -83,9 +83,12 @@
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_1": "Partiellement noyé",
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_2": "Noyé",
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_3": "Débit nul",
+    "INFO_EXTRARES_LIB_AVAL": "Aval",
     "INFO_EXTRARES_LIB_B": "Largeur au miroir (m)",
+    "INFO_EXTRARES_LIB_CLOISON": "Cloison n°",
     "INFO_EXTRARES_LIB_CV": "Cv: Coefficient de vitesse d'approche",
     "INFO_EXTRARES_LIB_CVQT": "CV.QT: Débit corrigé (m³/s)",
+    "INFO_EXTRARES_LIB_DH": "Chute (m)",
     "INFO_EXTRARES_LIB_DHR": "DHR: Chute résiduelle (m)",
     "INFO_EXTRARES_LIB_EC": "EC: Énergie cinétique (m)",
     "INFO_EXTRARES_LIB_ENUM_MACRORUGOFLOWTYPE": "Type d'écoulement",
@@ -103,9 +106,11 @@
     "INFO_EXTRARES_LIB_PV": "Puissance volumique dissipée (W/m³)",
     "INFO_EXTRARES_LIB_Q": "Débit (m³/s)",
     "INFO_EXTRARES_LIB_Q_GUIDETECH": "Débit Guide technique (m³/s)",
+    "INFO_EXTRARES_LIB_QA": "Débit d'attrait (m³/s)",
     "INFO_EXTRARES_LIB_R": "Rayon hydraulique (m)",
     "INFO_EXTRARES_LIB_S": "Surface mouillée (m²)",
     "INFO_EXTRARES_LIB_TAU0": "Force tractrice (Pa)",
+    "INFO_EXTRARES_LIB_TMOY": "Tirant d'eau moyen (m)",
     "INFO_EXTRARES_LIB_TOR": "Ligne d'eau torrentielle",
     "INFO_EXTRARES_LIB_V": "Vitesse moyenne (m/s)",
     "INFO_EXTRARES_LIB_VDEB": "Vitesse débitante (m/s)",
@@ -116,6 +121,8 @@
     "INFO_EXTRARES_LIB_YF": "Tirant d'eau fluvial (m)",
     "INFO_EXTRARES_LIB_YN": "Tirant d'eau normal (m)",
     "INFO_EXTRARES_LIB_YT": "Tirant d'eau torrentiel (m)",
+    "INFO_EXTRARES_LIB_Z1_PAB": "Niveau d'eau (m)",
+    "INFO_EXTRARES_LIB_ZR": "Cote de radier (m)",
     "INFO_EXTRARES_LIB_ZRB": "Cote de radier du bassin aval (m)",
     "INFO_EXTRARES_LIB_ZF2": "Cote de fond aval (m)",
     "INFO_LECHAPTCALMON_TITRE": "Lechapt-Calmon",
-- 
GitLab


From 3cb605f85245610751f2e393f93c94904993a2a3 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Tue, 14 May 2019 10:18:55 +0200
Subject: [PATCH 28/44] =?UTF-8?q?Correction=20bug:=20les=20ent=C3=AAtes=20?=
 =?UTF-8?q?de=20r=C3=A9sultats=20de=20PAB=20s'affichaient=20sur=20tous=20l?=
 =?UTF-8?q?es=20modules?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/app/components/pab-results/pab-results-table.component.ts | 4 ++--
 src/app/components/pab-results/pab-results.component.ts       | 3 ++-
 src/app/formulaire/definition/form-compute-pab.ts             | 1 -
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/app/components/pab-results/pab-results-table.component.ts b/src/app/components/pab-results/pab-results-table.component.ts
index da5e5ca79..96ada8304 100644
--- a/src/app/components/pab-results/pab-results-table.component.ts
+++ b/src/app/components/pab-results/pab-results-table.component.ts
@@ -53,7 +53,7 @@ export class PabResultsTableComponent {
         ];
 
         this._dataSet = [];
-        if (this._pabResults) {
+        if (this._pabResults && this._pabResults.cloisonsResults && this._pabResults.cloisonsResults.length > 0) {
             const pr = this._pabResults;
             const nDigits = this.appSetupService.displayDigits;
             // line 1
@@ -150,6 +150,6 @@ export class PabResultsTableComponent {
         const wb: XLSX.WorkBook = XLSX.utils.book_new();
         XLSX.utils.book_append_sheet(wb, ws, "default");
         // save and download
-        XLSX.writeFile(wb, "VariableResults.xlsx");
+        XLSX.writeFile(wb, "PABResults.xlsx");
     }
 }
diff --git a/src/app/components/pab-results/pab-results.component.ts b/src/app/components/pab-results/pab-results.component.ts
index d787ca36c..274ace58e 100644
--- a/src/app/components/pab-results/pab-results.component.ts
+++ b/src/app/components/pab-results/pab-results.component.ts
@@ -35,7 +35,8 @@ export class PabResultsComponent implements DoCheck {
     ) { }
 
     public set results(rs: CalculatorResults[]) {
-        if (rs.length > 0) {
+        this._pabResults = undefined;
+        if (rs.length > 0 && rs[0] instanceof PabResults) {
             this._pabResults = rs[0] as PabResults;
         }
         this.updateView();
diff --git a/src/app/formulaire/definition/form-compute-pab.ts b/src/app/formulaire/definition/form-compute-pab.ts
index a3e1d2d22..b1586759d 100644
--- a/src/app/formulaire/definition/form-compute-pab.ts
+++ b/src/app/formulaire/definition/form-compute-pab.ts
@@ -34,7 +34,6 @@ export class FormComputePab extends FormCompute {
         const cq: number[] = [];
         // valeur de QA pour chaque bassin
         const bqa: number[] = [];
-        console.log("pab.children[0].result >>", pab.children[0].result);
         for (const c of pab.children) {
             cr.push(c.result);
             cq.push(c.prms.Q.v);
-- 
GitLab


From c0e01ef920f98e34bd1379056949db9dec5ff66c Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Tue, 14 May 2019 10:56:09 +0200
Subject: [PATCH 29/44] =?UTF-8?q?M=C3=A0J=20affichage=20des=20r=C3=A9sulta?=
 =?UTF-8?q?ts=20d'une=20PAB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../pab-results-table.component.ts            | 47 +++++++++----------
 src/app/results/pab-results.ts                |  6 ++-
 2 files changed, 27 insertions(+), 26 deletions(-)

diff --git a/src/app/components/pab-results/pab-results-table.component.ts b/src/app/components/pab-results/pab-results-table.component.ts
index 96ada8304..a52b6acf5 100644
--- a/src/app/components/pab-results/pab-results-table.component.ts
+++ b/src/app/components/pab-results/pab-results-table.component.ts
@@ -65,26 +65,23 @@ export class PabResultsTableComponent {
 
             // lines 2 - n-1
             for (let i = 0; i < pr.cloisonsResults.length - 1; i++) {
-                if (pr.cloisonsResults[i]) {
-                    const ZRAM = pr.cloisonsResults[i].getExtraResult("ZRAM");
-                    const PV = pr.cloisonsResults[i].getExtraResult("PV");
-                    const Y = pr.cloisonsResults[i].getExtraResult("Y");
-                    const ZRB = pr.cloisonsResults[i].getExtraResult("ZRB");
-                    this._dataSet.push([
-                        i + 1,
-                        (pr.cloisonsResults[i].vCalc - pr.cloisonsResults[i + 1].vCalc).toFixed(nDigits),
-                        ZRAM ? ZRAM.toFixed(nDigits) : "X",
-                        pr.cloisonsQ[i].toFixed(nDigits),
-                        pr.cloisonsResults[i + 1].vCalc.toFixed(nDigits),
-                        PV ? PV.toFixed(nDigits) : "X",
-                        Y ? Y.toFixed(nDigits) : "X",
-                        ZRB ? ZRB.toFixed(nDigits) : "X",
-                        pr.bassinsQA[i]
-                    ]);
-                } else {
-                    // @TODO remove dummy data when "pab.children[0] is undefined" bug is resolved
-                    this._dataSet.push([ "1", "X", "X", "X", "X", "X", "X", "X", "X" ]);
-                }
+                // console.log("pr.cloisonsResults[i]", pr.cloisonsResults[i]);
+                // console.log("resultElement STRINGIFIÉ", JSON.stringify(pr.cloisonsResults[i].resultElement));
+                const ZRAM = pr.cloisonsResults[i].getExtraResult("ZRAM");
+                const PV = pr.cloisonsResults[i].getExtraResult("PV");
+                const Y = pr.cloisonsResults[i].getExtraResult("Y");
+                const ZRB = pr.cloisonsResults[i].getExtraResult("ZRB");
+                this._dataSet.push([
+                    i + 1,
+                    (pr.cloisonsResults[i].vCalc - pr.cloisonsResults[i + 1].vCalc).toFixed(nDigits),
+                    (ZRAM !== undefined) ? ZRAM.toFixed(nDigits) : "X",
+                    pr.cloisonsQ[i].toFixed(nDigits),
+                    pr.cloisonsResults[i + 1].vCalc.toFixed(nDigits),
+                    (PV !== undefined) ? PV.toFixed(nDigits) : "X",
+                    (Y !== undefined) ? Y.toFixed(nDigits) : "X",
+                    (ZRB !== undefined) ? ZRB.toFixed(nDigits) : "X",
+                    pr.bassinsQA[i]
+                ]);
             }
 
             // line n
@@ -96,12 +93,12 @@ export class PabResultsTableComponent {
             this._dataSet.push([
                 l,
                 (pr.cloisonsResults[l - 1].vCalc - pr.cloisonAvalResults.vCalc).toFixed(nDigits),
-                nZRAM ? nZRAM.toFixed(nDigits) : "X",
+                (nZRAM !== undefined) ? nZRAM.toFixed(nDigits) : "X",
                 pr.cloisonsQ[l - 1].toFixed(nDigits),
                 pr.cloisonAvalResults.vCalc.toFixed(nDigits),
-                nPV ? nPV.toFixed(nDigits) : "X",
-                nY ? nY.toFixed(nDigits) : "X",
-                nZRB ? nZRB.toFixed(nDigits) : "X",
+                (nPV !== undefined) ? nPV.toFixed(nDigits) : "X",
+                (nY !== undefined) ? nY.toFixed(nDigits) : "X",
+                (nZRB !== undefined) ? nZRB.toFixed(nDigits) : "X",
                 pr.bassinsQA[l - 1]
             ]);
 
@@ -110,7 +107,7 @@ export class PabResultsTableComponent {
             this._dataSet.push([
                 this.intlService.localizeText("INFO_EXTRARES_LIB_AVAL"),
                 (pr.cloisonAvalResults.vCalc - pr.Z2).toFixed(nDigits),
-                caZRAM ? caZRAM.toFixed(nDigits) : "X",
+                (caZRAM !== undefined) ? caZRAM.toFixed(nDigits) : "X",
                 pr.cloisonAvalQ.toFixed(nDigits),
                 pr.Z2.toFixed(nDigits),
                 "",
diff --git a/src/app/results/pab-results.ts b/src/app/results/pab-results.ts
index c30f8c1ec..358aee845 100644
--- a/src/app/results/pab-results.ts
+++ b/src/app/results/pab-results.ts
@@ -28,7 +28,11 @@ export class PabResults extends CalculatedParamResults {
 
     public reset() {
         super.reset();
-        this.cloisonsResults = undefined;
+        this.cloisonsResults = [];
+        this.cloisonsQ = [];
+        this.bassinsQA = [];
         this.cloisonAvalResults = undefined;
+        this.cloisonAvalQ = undefined;
+        this.Z2 = undefined;
     }
 }
-- 
GitLab


From ae1b8fe01b4433467359885dccd69c84db4d11e2 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Tue, 14 May 2019 15:11:09 +0200
Subject: [PATCH 30/44] Graphique pour la PAB
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

amélioration du composant ResultsGraph
nouvelle interface PlottableData
---
 .../pab-results-table.component.ts            |  37 ++--
 .../pab-results/pab-results.component.html    |   2 +-
 .../pab-results/pab-results.component.ts      |  35 +++-
 .../results-graph/graph-type.component.ts     |   2 +-
 .../results-graph/results-graph.component.ts  |  19 +-
 .../formulaire/definition/form-compute-pab.ts |  21 ++
 .../definition/form-result-fixedvar.ts        |   3 +-
 src/app/results/graph-type.ts                 |  19 ++
 src/app/results/pab-results.ts                |  35 +++-
 src/app/results/plottable-data.ts             |  30 +++
 src/app/results/plottable-pab-results.ts      | 193 ++++++++++++++++++
 src/app/results/var-results.ts                |  40 ++--
 12 files changed, 363 insertions(+), 73 deletions(-)
 create mode 100644 src/app/results/graph-type.ts
 create mode 100644 src/app/results/plottable-data.ts
 create mode 100644 src/app/results/plottable-pab-results.ts

diff --git a/src/app/components/pab-results/pab-results-table.component.ts b/src/app/components/pab-results/pab-results-table.component.ts
index a52b6acf5..d598ae9bc 100644
--- a/src/app/components/pab-results/pab-results-table.component.ts
+++ b/src/app/components/pab-results/pab-results-table.component.ts
@@ -39,27 +39,18 @@ export class PabResultsTableComponent {
     public set results(r: PabResults) {
         this._pabResults = r;
 
-        // refresh headers here if language changed
-        this._headers = [
-            this.intlService.localizeText("INFO_EXTRARES_LIB_CLOISON"),
-            this.intlService.localizeText("INFO_EXTRARES_LIB_DH"),
-            this.intlService.localizeText("INFO_EXTRARES_LIB_ZR"),
-            this.intlService.localizeText("INFO_EXTRARES_LIB_Q"),
-            this.intlService.localizeText("INFO_EXTRARES_LIB_Z1_PAB"),
-            this.intlService.localizeText("INFO_EXTRARES_LIB_PV"),
-            this.intlService.localizeText("INFO_EXTRARES_LIB_TMOY"),
-            this.intlService.localizeText("INFO_EXTRARES_LIB_ZRB"),
-            this.intlService.localizeText("INFO_EXTRARES_LIB_QA")
-        ];
-
         this._dataSet = [];
         if (this._pabResults && this._pabResults.cloisonsResults && this._pabResults.cloisonsResults.length > 0) {
             const pr = this._pabResults;
             const nDigits = this.appSetupService.displayDigits;
+
+            // refresh headers here if language changed
+            this._headers = pr.headers;
+
             // line 1
             this._dataSet.push([
                 "", "", "", "",
-                this._pabResults.cloisonsResults[0] ? this._pabResults.cloisonsResults[0].vCalc.toFixed(nDigits) : "X",
+                this._pabResults.cloisonsResults[0] ? this._pabResults.cloisonsResults[0].vCalc.toFixed(nDigits) : "",
                 "", "", "", ""
             ]);
 
@@ -74,12 +65,12 @@ export class PabResultsTableComponent {
                 this._dataSet.push([
                     i + 1,
                     (pr.cloisonsResults[i].vCalc - pr.cloisonsResults[i + 1].vCalc).toFixed(nDigits),
-                    (ZRAM !== undefined) ? ZRAM.toFixed(nDigits) : "X",
+                    (ZRAM !== undefined) ? ZRAM.toFixed(nDigits) : "",
                     pr.cloisonsQ[i].toFixed(nDigits),
                     pr.cloisonsResults[i + 1].vCalc.toFixed(nDigits),
-                    (PV !== undefined) ? PV.toFixed(nDigits) : "X",
-                    (Y !== undefined) ? Y.toFixed(nDigits) : "X",
-                    (ZRB !== undefined) ? ZRB.toFixed(nDigits) : "X",
+                    (PV !== undefined) ? PV.toFixed(nDigits) : "",
+                    (Y !== undefined) ? Y.toFixed(nDigits) : "",
+                    (ZRB !== undefined) ? ZRB.toFixed(nDigits) : "",
                     pr.bassinsQA[i]
                 ]);
             }
@@ -93,12 +84,12 @@ export class PabResultsTableComponent {
             this._dataSet.push([
                 l,
                 (pr.cloisonsResults[l - 1].vCalc - pr.cloisonAvalResults.vCalc).toFixed(nDigits),
-                (nZRAM !== undefined) ? nZRAM.toFixed(nDigits) : "X",
+                (nZRAM !== undefined) ? nZRAM.toFixed(nDigits) : "",
                 pr.cloisonsQ[l - 1].toFixed(nDigits),
                 pr.cloisonAvalResults.vCalc.toFixed(nDigits),
-                (nPV !== undefined) ? nPV.toFixed(nDigits) : "X",
-                (nY !== undefined) ? nY.toFixed(nDigits) : "X",
-                (nZRB !== undefined) ? nZRB.toFixed(nDigits) : "X",
+                (nPV !== undefined) ? nPV.toFixed(nDigits) : "",
+                (nY !== undefined) ? nY.toFixed(nDigits) : "",
+                (nZRB !== undefined) ? nZRB.toFixed(nDigits) : "",
                 pr.bassinsQA[l - 1]
             ]);
 
@@ -107,7 +98,7 @@ export class PabResultsTableComponent {
             this._dataSet.push([
                 this.intlService.localizeText("INFO_EXTRARES_LIB_AVAL"),
                 (pr.cloisonAvalResults.vCalc - pr.Z2).toFixed(nDigits),
-                (caZRAM !== undefined) ? caZRAM.toFixed(nDigits) : "X",
+                (caZRAM !== undefined) ? caZRAM.toFixed(nDigits) : "",
                 pr.cloisonAvalQ.toFixed(nDigits),
                 pr.Z2.toFixed(nDigits),
                 "",
diff --git a/src/app/components/pab-results/pab-results.component.html b/src/app/components/pab-results/pab-results.component.html
index 02aa6f94e..9fcc9927c 100644
--- a/src/app/components/pab-results/pab-results.component.html
+++ b/src/app/components/pab-results/pab-results.component.html
@@ -2,7 +2,7 @@
     <!-- journal -->
     <log></log>
 
-    <!-- <results-graph *ngIf="showVarResults"></results-graph> -->
+    <results-graph *ngIf="hasResults"></results-graph>
 
     <div>
         <!-- tableau de résultats -->
diff --git a/src/app/components/pab-results/pab-results.component.ts b/src/app/components/pab-results/pab-results.component.ts
index 274ace58e..99517c236 100644
--- a/src/app/components/pab-results/pab-results.component.ts
+++ b/src/app/components/pab-results/pab-results.component.ts
@@ -1,12 +1,16 @@
 import { Component, ViewChild, DoCheck } from "@angular/core";
 
+import { Result, cLog } from "jalhyd";
+
 import { LogComponent } from "../../components/log/log.component";
 import { CalculatorResults } from "../../results/calculator-results";
-import { Result, cLog } from "jalhyd";
 import { NgParameter } from "../../formulaire/ngparam";
 import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
 import { PabResultsTableComponent } from "./pab-results-table.component";
 import { PabResults } from "../../results/pab-results";
+import { ResultsGraphComponent } from "../results-graph/results-graph.component";
+import { PlottableData } from "../../results/plottable-data";
+import { PlottablePabResults } from "../../results/plottable-pab-results";
 
 @Component({
     selector: "pab-results",
@@ -30,6 +34,10 @@ export class PabResultsComponent implements DoCheck {
     @ViewChild(LogComponent)
     private logComponent: LogComponent;
 
+    /** graphique dans le cas d'un paramètre à varier */
+    @ViewChild(ResultsGraphComponent)
+    private resultsGraphComponent: ResultsGraphComponent;
+
     constructor(
         private appSetupService: ApplicationSetupService,
     ) { }
@@ -47,6 +55,9 @@ export class PabResultsComponent implements DoCheck {
         if (this.pabResultsTableComponent) {
             this.pabResultsTableComponent.results = undefined;
         }
+        if (this.resultsGraphComponent) {
+            this.resultsGraphComponent.results = undefined;
+        }
         // set _doUpdate flag so that results are rebuilt on the next Angular display cycle
         this._doUpdate = false;
         if (this._pabResults !== undefined) {
@@ -84,13 +95,20 @@ export class PabResultsComponent implements DoCheck {
      */
     private updateResults() {
         let pabUpdated: boolean;
+        let graphUpdated: boolean;
         if (this.hasResults) {
             pabUpdated = this.pabResultsTableComponent !== undefined;
             if (pabUpdated) {
                 this.pabResultsTableComponent.results = this._pabResults;
             }
+            graphUpdated = this.resultsGraphComponent !== undefined;
+            if (graphUpdated) {
+                this.resultsGraphComponent.results = this.plottableResults;
+                this.resultsGraphComponent.updateView();
+            }
         } else {
             pabUpdated = true;
+            graphUpdated = true;
         }
 
         const logUpdated = this.logComponent !== undefined;
@@ -98,14 +116,7 @@ export class PabResultsComponent implements DoCheck {
             this.logComponent.log = this.mergedGlobalLogs;
         }
 
-        return pabUpdated && logUpdated;
-    }
-
-    /**
-     * affichage de la table des résultats de passe à bassins
-     */
-    public get showPabResults(): boolean {
-        return this.hasResults;
+        return pabUpdated && logUpdated && graphUpdated;
     }
 
     public get pabResults() {
@@ -124,4 +135,10 @@ export class PabResultsComponent implements DoCheck {
     public get hasResults(): boolean {
         return this._pabResults && this._pabResults.hasResults;
     }
+
+    /** builds a set of PlottableData from PabResults, to feed the graph */
+    protected get plottableResults(): PlottableData {
+        const pr = new PlottablePabResults(this._pabResults);
+        return pr;
+    }
 }
diff --git a/src/app/components/results-graph/graph-type.component.ts b/src/app/components/results-graph/graph-type.component.ts
index f5194120b..78ef5d486 100644
--- a/src/app/components/results-graph/graph-type.component.ts
+++ b/src/app/components/results-graph/graph-type.component.ts
@@ -1,7 +1,7 @@
 import { Component } from "@angular/core";
 import { Observable, IObservable, Observer } from "jalhyd";
-import { GraphType } from "../../results/var-results";
 import { I18nService } from "../../services/internationalisation/internationalisation.service";
+import { GraphType } from "../../results/graph-type";
 
 @Component({
     selector: "graph-type",
diff --git a/src/app/components/results-graph/results-graph.component.ts b/src/app/components/results-graph/results-graph.component.ts
index 13757e3bd..d0731c27b 100644
--- a/src/app/components/results-graph/results-graph.component.ts
+++ b/src/app/components/results-graph/results-graph.component.ts
@@ -2,10 +2,11 @@ import { Component, ViewChild, AfterContentInit } from "@angular/core";
 
 import { Observer } from "jalhyd";
 
-import { VarResults, GraphType } from "../../results/var-results";
 import { GraphTypeSelectComponent } from "./graph-type.component";
 import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
 import { I18nService } from "../../services/internationalisation/internationalisation.service";
+import { PlottableData } from "../../results/plottable-data";
+import { GraphType } from "../../results/graph-type";
 
 @Component({
     selector: "results-graph",
@@ -15,7 +16,7 @@ import { I18nService } from "../../services/internationalisation/internationalis
     ]
 })
 export class ResultsGraphComponent implements AfterContentInit, Observer {
-    private _results: VarResults;
+    private _results: PlottableData;
 
     @ViewChild(GraphTypeSelectComponent)
     private _graphTypeComponent: GraphTypeSelectComponent;
@@ -53,7 +54,7 @@ export class ResultsGraphComponent implements AfterContentInit, Observer {
         const nDigits = this.appSetup.displayDigits;
     }
 
-    public set results(r: VarResults) {
+    public set results(r: PlottableData) {
         this._results = r;
         if (this._results && this._graphTypeComponent) {
             this._graphTypeComponent.selectedValue = r.graphType;
@@ -98,17 +99,7 @@ export class ResultsGraphComponent implements AfterContentInit, Observer {
      * Returns a human readable description of any param / result symbol
      */
     public getChartAxisLabel(symbol: string) {
-        // 1. calculated param ?
-        if (this._results.calculatedParameter && this._results.calculatedParameter.symbol === symbol) {
-            return this._results.calculatedParameterHeader;
-        } else
-        // 2. variated param ?
-        if (this._results.variatedParameter.symbol === symbol) {
-            return this._results.variableParamHeader;
-        } else {
-            // 3. Result element ?
-            return this.intlService.getExtraResLabel(symbol);
-        }
+        return this._results.getChartAxisLabel(symbol);
     }
 
     public get uitextSelectX() {
diff --git a/src/app/formulaire/definition/form-compute-pab.ts b/src/app/formulaire/definition/form-compute-pab.ts
index b1586759d..c5cfd59c2 100644
--- a/src/app/formulaire/definition/form-compute-pab.ts
+++ b/src/app/formulaire/definition/form-compute-pab.ts
@@ -15,6 +15,20 @@ export class FormComputePab extends FormCompute {
         return this._formResult as FormResultPab;
     }
 
+    private getVariatedParameter(): NgParameter {
+        const res = this._formBase.getDisplayedParamFromState(ParamRadioConfig.VAR);
+        if (res !== undefined) {
+            return res;
+        }
+
+        const pms = this._formBase.getDisplayedParamListFromState(ParamRadioConfig.LINK);
+        for (const p of pms) {
+            if (p.paramDefinition.hasMultipleValues) {
+                return p;
+            }
+        }
+    }
+
     protected compute() {
         this.runNubCalc(this._formBase.currentNub);
         this.reaffectResultComponents();
@@ -23,11 +37,13 @@ export class FormComputePab extends FormCompute {
     protected reaffectResultComponents() {
         const pab: Pab = (this._formBase.currentNub as Pab);
         const computedParam: NgParameter = this.getComputedParameter();
+        const varParam: NgParameter = this.getVariatedParameter();
 
         // résultat de calcul de la passe à bassins
         const pabr = this.formResult.pabResults;
         pabr.calculatedParameter = computedParam;
         pabr.result = pab.result;
+
         // résultat de chaque cloison
         const cr: Result[] = [];
         // valeur de Q pour chaque cloison
@@ -48,6 +64,11 @@ export class FormComputePab extends FormCompute {
         pabr.cloisonAvalQ = pab.downWall.prms.Q.v;
         // cote aval de la passe
         pabr.Z2 = pab.prms.Z2.v;
+
+        if (varParam) {
+            pabr.variatedParameter = varParam;
+            // pabr.update(false);
+        }
     }
 
     private getComputedParameter(): NgParameter {
diff --git a/src/app/formulaire/definition/form-result-fixedvar.ts b/src/app/formulaire/definition/form-result-fixedvar.ts
index 53b9c455c..6a79f021e 100644
--- a/src/app/formulaire/definition/form-result-fixedvar.ts
+++ b/src/app/formulaire/definition/form-result-fixedvar.ts
@@ -1,9 +1,10 @@
 import { FixedResults } from "../../results/fixed-results";
-import { GraphType, VarResults } from "../../results/var-results";
+import { VarResults } from "../../results/var-results";
 import { ParamRadioConfig } from "../ngparam";
 import { FormResult } from "./form-result";
 import { FormulaireDefinition } from "./form-definition";
 import { CalculatorResults } from "../../results/calculator-results";
+import { GraphType } from "../../results/graph-type";
 
 export class FormResultFixedVar extends FormResult {
     /**
diff --git a/src/app/results/graph-type.ts b/src/app/results/graph-type.ts
new file mode 100644
index 000000000..cf83fc76e
--- /dev/null
+++ b/src/app/results/graph-type.ts
@@ -0,0 +1,19 @@
+/**
+ * type de graphe
+ */
+export enum GraphType {
+    /**
+     * histogramme
+     */
+    Histogram,
+
+    /**
+     * points indépendants reliés par une courbe
+     */
+    HistoLine,
+
+    /**
+     * graphe XY classique
+     */
+    Scatter,
+}
diff --git a/src/app/results/pab-results.ts b/src/app/results/pab-results.ts
index 358aee845..e9b135981 100644
--- a/src/app/results/pab-results.ts
+++ b/src/app/results/pab-results.ts
@@ -1,6 +1,9 @@
-import { CalculatedParamResults } from "./param-calc-results";
 import { Result } from "jalhyd";
 
+import { CalculatedParamResults } from "./param-calc-results";
+import { NgParameter } from "../formulaire/ngparam";
+import { ServiceFactory } from "../services/service-factory";
+
 export class PabResults extends CalculatedParamResults {
 
     /** résultats des modules Cloisons avant chaque bassin */
@@ -21,9 +24,39 @@ export class PabResults extends CalculatedParamResults {
     /** cote aval de l'ensemble de la passe */
     public Z2: number;
 
+    /** paramètre varié */
+    public variatedParameter: NgParameter;
+
+    /** symboles des colonnes de résultat */
+    protected _columns: string[];
+
     public constructor() {
         super();
         this.reset();
+        // standard headers
+        this._columns = [
+            "CLOISON",
+            "DH",
+            "ZR",
+            "Q",
+            "Z1_PAB",
+            "PV",
+            "TMOY",
+            "ZRB",
+            "QA"
+        ];
+    }
+
+    /** headers symbols */
+    public get columns() {
+        return this._columns;
+    }
+
+    /** translated headers texts */
+    public get headers() {
+        return this._columns.map((h) => {
+            return ServiceFactory.instance.i18nService.localizeText("INFO_EXTRARES_LIB_" + h);
+        });
     }
 
     public reset() {
diff --git a/src/app/results/plottable-data.ts b/src/app/results/plottable-data.ts
new file mode 100644
index 000000000..609c388aa
--- /dev/null
+++ b/src/app/results/plottable-data.ts
@@ -0,0 +1,30 @@
+import { GraphType } from "./graph-type";
+
+/**
+ * Une interface pour nourrir un ResultsGraphComponent
+ */
+export interface PlottableData {
+
+    graphType: GraphType;
+    chartX: string;
+    chartY: string;
+
+    /**
+     * Returns the label to display, for an element of getAvailableChartAxis()
+     * (usually a variable symbol like "Q", "Z1"…)
+     * @param symbol parameter / result symbol (ex: "Q")
+     */
+    getChartAxisLabel(symbol: string);
+
+    /**
+     * Returns a list of plottable parameters / result elements, that can be defined
+     * as X or Y chart axis
+     */
+    getAvailableChartAxis(): string[];
+
+    /**
+     * Returns the series of values for the required variated parameter / result element
+     * @param symbol parameter / result symbol (ex: "Q")
+     */
+    getValuesSeries(symbol: string): any[];
+}
diff --git a/src/app/results/plottable-pab-results.ts b/src/app/results/plottable-pab-results.ts
new file mode 100644
index 000000000..9038608dd
--- /dev/null
+++ b/src/app/results/plottable-pab-results.ts
@@ -0,0 +1,193 @@
+import { PlottableData } from "./plottable-data";
+import { PabResults } from "./pab-results";
+import { GraphType } from "./graph-type";
+import { ServiceFactory } from "../services/service-factory";
+
+export class PlottablePabResults implements PlottableData {
+
+    public graphType: GraphType = GraphType.Scatter;
+    public chartX: string;
+    public chartY: string;
+
+    protected pabResults: PabResults;
+
+    public constructor(pabResults: PabResults) {
+        this.pabResults = pabResults;
+
+        // axes par défaut
+        console.log(">> axes par défaut");
+        this.chartX = "CLOISON";
+        this.chartY = "Z1_PAB";
+    }
+
+    /**
+     * Returns the label to display, for an element of getAvailableChartAxis()
+     * @param symbol parameter / result symbol (ex: "Q")
+     */
+    public getChartAxisLabel(symbol: string) {
+        return this.pabResults.headers[this.pabResults.columns.indexOf(symbol)];
+    }
+
+    /**
+     * Returns a list of plottable parameters / result elements, that can be defined
+     * as X or Y chart axis
+     */
+    public getAvailableChartAxis(): string[] {
+        return this.pabResults.columns;
+    }
+
+    /**
+     * Returns the series of values for the required symbol
+     * @param symbol parameter / result symbol (ex: "Q")
+     */
+    public getValuesSeries(symbol: string): any[] {
+        const data: string[] = [];
+        const pr = this.pabResults;
+        const nDigits = ServiceFactory.instance.applicationSetupService.displayDigits;
+        const l = this.pabResults.cloisonsResults.length;
+
+        switch (symbol) {
+            case "CLOISON":
+                data.push("");
+                for (let i = 0; i < l; i++) {
+                    data.push("" + (i + 1));
+                }
+                data.push(ServiceFactory.instance.i18nService.localizeText("INFO_EXTRARES_LIB_AVAL"));
+                break;
+
+            case "DH":
+                data.push("");
+                for (let i = 0; i < l - 1; i++) {
+                    data.push((pr.cloisonsResults[i].vCalc - pr.cloisonsResults[i + 1].vCalc).toFixed(nDigits));
+                }
+                data.push((pr.cloisonsResults[l - 1].vCalc - pr.cloisonAvalResults.vCalc).toFixed(nDigits));
+                data.push((pr.cloisonAvalResults.vCalc - pr.Z2).toFixed(nDigits));
+                break;
+
+            case "ZR":
+                data.push("");
+                for (let i = 0; i < l; i++) {
+                    const er = pr.cloisonsResults[i].getExtraResult("ZRAM");
+                    data.push((er !== undefined) ? er.toFixed(nDigits) : "");
+                }
+                const zrAval = pr.cloisonAvalResults.getExtraResult("ZRAM");
+                data.push((zrAval !== undefined) ? zrAval.toFixed(nDigits) : "");
+                break;
+
+            case "Q":
+                data.push("");
+                for (let i = 0; i < l; i++) {
+                    data.push(pr.cloisonsQ[i].toFixed(nDigits));
+                }
+                data.push(pr.cloisonAvalQ.toFixed(nDigits));
+                break;
+
+            case "Z1_PAB":
+                for (let i = 0; i < l - 1; i++) {
+                    data.push(pr.cloisonsResults[i].vCalc.toFixed(nDigits));
+                }
+                data.push(pr.cloisonAvalResults.vCalc.toFixed(nDigits));
+                data.push(pr.Z2.toFixed(nDigits));
+                break;
+
+            case "PV":
+                data.push("");
+                for (let i = 0; i < l; i++) {
+                    const er = pr.cloisonsResults[i].getExtraResult("PV");
+                    data.push((er !== undefined) ? er.toFixed(nDigits) : "");
+                }
+                data.push("");
+                break;
+
+            case "TMOY":
+                data.push("");
+                for (let i = 0; i < l; i++) {
+                    const er = pr.cloisonsResults[i].getExtraResult("Y");
+                    data.push((er !== undefined) ? er.toFixed(nDigits) : "");
+                }
+                data.push("");
+                break;
+
+            case "ZRB":
+                data.push("");
+                for (let i = 0; i < l; i++) {
+                    const er = pr.cloisonsResults[i].getExtraResult("ZRB");
+                    data.push((er !== undefined) ? er.toFixed(nDigits) : "");
+                }
+                data.push("");
+                break;
+
+            case "QA":
+                data.push("");
+                for (let i = 0; i < l; i++) {
+                    data.push(pr.bassinsQA[i].toFixed(nDigits));
+                }
+                data.push("");
+                break;
+        }
+
+        // line 1
+        /* this._dataSet.push([
+            "", "", "", "",
+            this._pabResults.cloisonsResults[0] ? this._pabResults.cloisonsResults[0].vCalc.toFixed(nDigits) : "X",
+            "", "", "", ""
+        ]);
+
+        // lines 2 - n-1
+        for (let i = 0; i < pr.cloisonsResults.length - 1; i++) {
+            // console.log("pr.cloisonsResults[i]", pr.cloisonsResults[i]);
+            // console.log("resultElement STRINGIFIÉ", JSON.stringify(pr.cloisonsResults[i].resultElement));
+            const ZRAM = pr.cloisonsResults[i].getExtraResult("ZRAM");
+            const PV = pr.cloisonsResults[i].getExtraResult("PV");
+            const Y = pr.cloisonsResults[i].getExtraResult("Y");
+            const ZRB = pr.cloisonsResults[i].getExtraResult("ZRB");
+            this._dataSet.push([
+                i + 1,
+                (pr.cloisonsResults[i].vCalc - pr.cloisonsResults[i + 1].vCalc).toFixed(nDigits),
+                (ZRAM !== undefined) ? ZRAM.toFixed(nDigits) : "X",
+                pr.cloisonsQ[i].toFixed(nDigits),
+                pr.cloisonsResults[i + 1].vCalc.toFixed(nDigits),
+                (PV !== undefined) ? PV.toFixed(nDigits) : "X",
+                (Y !== undefined) ? Y.toFixed(nDigits) : "X",
+                (ZRB !== undefined) ? ZRB.toFixed(nDigits) : "X",
+                pr.bassinsQA[i]
+            ]);
+        }
+
+        // line n
+        const l = pr.cloisonsResults.length;
+        const nZRAM = pr.cloisonsResults[l - 1].getExtraResult("ZRAM");
+        const nPV = pr.cloisonsResults[l - 1].getExtraResult("PV");
+        const nY = pr.cloisonsResults[l - 1].getExtraResult("Y");
+        const nZRB = pr.cloisonsResults[l - 1].getExtraResult("ZRB");
+        this._dataSet.push([
+            l,
+            (pr.cloisonsResults[l - 1].vCalc - pr.cloisonAvalResults.vCalc).toFixed(nDigits),
+            (nZRAM !== undefined) ? nZRAM.toFixed(nDigits) : "X",
+            pr.cloisonsQ[l - 1].toFixed(nDigits),
+            pr.cloisonAvalResults.vCalc.toFixed(nDigits),
+            (nPV !== undefined) ? nPV.toFixed(nDigits) : "X",
+            (nY !== undefined) ? nY.toFixed(nDigits) : "X",
+            (nZRB !== undefined) ? nZRB.toFixed(nDigits) : "X",
+            pr.bassinsQA[l - 1]
+        ]);
+
+        // downstream line
+        const caZRAM = pr.cloisonAvalResults.getExtraResult("ZRAM");
+        this._dataSet.push([
+            this.intlService.localizeText("INFO_EXTRARES_LIB_AVAL"),
+            (pr.cloisonAvalResults.vCalc - pr.Z2).toFixed(nDigits),
+            (caZRAM !== undefined) ? caZRAM.toFixed(nDigits) : "X",
+            pr.cloisonAvalQ.toFixed(nDigits),
+            pr.Z2.toFixed(nDigits),
+            "",
+            "",
+            "",
+            ""
+        ]); */
+
+
+
+        return data;
+    }
+}
diff --git a/src/app/results/var-results.ts b/src/app/results/var-results.ts
index da0be9d5f..8224154f1 100644
--- a/src/app/results/var-results.ts
+++ b/src/app/results/var-results.ts
@@ -3,29 +3,10 @@ import { CalculatedParamResults } from "./param-calc-results";
 import { NgParameter } from "../formulaire/ngparam";
 import { ResultElement } from "jalhyd";
 import { ServiceFactory } from "../services/service-factory";
+import { PlottableData } from "./plottable-data";
+import { GraphType } from "./graph-type";
 
-/**
- * type de graphe
- */
-export enum GraphType {
-    /**
-     * histogramme
-     */
-    Histogram,
-
-    /**
-     * points indépendants reliés par une courbe
-     */
-    HistoLine,
-
-    /**
-     * graphe XY classique
-     */
-    Scatter,
-}
-
-
-export class VarResults extends CalculatedParamResults {
+export class VarResults extends CalculatedParamResults implements PlottableData {
     /**
      * paramètre varié
      */
@@ -103,6 +84,20 @@ export class VarResults extends CalculatedParamResults {
         return this._extraResultHeaders;
     }
 
+    public getChartAxisLabel(symbol: string) {
+        // 1. calculated param ?
+        if (this.calculatedParameter && this.calculatedParameter.symbol === symbol) {
+            return this.calculatedParameterHeader;
+        } else
+        // 2. variated param ?
+        if (this.variatedParameter.symbol === symbol) {
+            return this.variableParamHeader;
+        } else {
+            // 3. Result element ?
+            return ServiceFactory.instance.i18nService.getExtraResLabel(symbol);
+        }
+    }
+
     /**
      * Returns the series of values for the required variated parameter / result element
      * @param symbol parameter / result symbol (ex: "Q")
@@ -139,7 +134,6 @@ export class VarResults extends CalculatedParamResults {
     /**
      * Returns a list of plottable parameters / result elements, that can be defined
      * as X or Y chart axis
-     * @param except exclude this symbol from the list
      */
     public getAvailableChartAxis(): string[] {
         const res: string[] = [];
-- 
GitLab


From 61cb07d2aa4ddb6d3b6e473705f0e125f6ee11d1 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Tue, 14 May 2019 15:30:20 +0200
Subject: [PATCH 31/44] =?UTF-8?q?Graphique=20de=20la=20PAB:=20r=C3=A9glage?=
 =?UTF-8?q?s=20conserv=C3=A9s?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../pab-results/pab-results.component.ts      | 11 ++-
 src/app/results/plottable-pab-results.ts      | 79 +++----------------
 src/app/results/var-results.ts                |  2 +-
 3 files changed, 20 insertions(+), 72 deletions(-)

diff --git a/src/app/components/pab-results/pab-results.component.ts b/src/app/components/pab-results/pab-results.component.ts
index 99517c236..e96534074 100644
--- a/src/app/components/pab-results/pab-results.component.ts
+++ b/src/app/components/pab-results/pab-results.component.ts
@@ -24,6 +24,9 @@ export class PabResultsComponent implements DoCheck {
     /** résultats non mis en forme */
     private _pabResults: PabResults;
 
+    /** résultats mis en forme pour le graphique */
+    private _plottableResults: PlottablePabResults;
+
     /** true si les résultats doiventt être remis à jour */
     private _doUpdate = false;
 
@@ -40,7 +43,9 @@ export class PabResultsComponent implements DoCheck {
 
     constructor(
         private appSetupService: ApplicationSetupService,
-    ) { }
+    ) {
+        this._plottableResults = new PlottablePabResults();
+    }
 
     public set results(rs: CalculatorResults[]) {
         this._pabResults = undefined;
@@ -138,7 +143,7 @@ export class PabResultsComponent implements DoCheck {
 
     /** builds a set of PlottableData from PabResults, to feed the graph */
     protected get plottableResults(): PlottableData {
-        const pr = new PlottablePabResults(this._pabResults);
-        return pr;
+        this._plottableResults.setPabResults(this.pabResults);
+        return this._plottableResults;
     }
 }
diff --git a/src/app/results/plottable-pab-results.ts b/src/app/results/plottable-pab-results.ts
index 9038608dd..628b79106 100644
--- a/src/app/results/plottable-pab-results.ts
+++ b/src/app/results/plottable-pab-results.ts
@@ -11,13 +11,18 @@ export class PlottablePabResults implements PlottableData {
 
     protected pabResults: PabResults;
 
-    public constructor(pabResults: PabResults) {
-        this.pabResults = pabResults;
-
+    public constructor(pabResults?: PabResults) {
+        if (pabResults) {
+            this.setPabResults(pabResults);
+        }
         // axes par défaut
-        console.log(">> axes par défaut");
-        this.chartX = "CLOISON";
-        this.chartY = "Z1_PAB";
+        this.chartX = this.chartX || "CLOISON";
+        this.chartY = this.chartY || "Z1_PAB";
+    }
+
+    /** reaffect pabResults, for ex. when objet was contructed with empty pabResults */
+    public setPabResults(pabResults: PabResults) {
+        this.pabResults = pabResults;
     }
 
     /**
@@ -126,68 +131,6 @@ export class PlottablePabResults implements PlottableData {
                 break;
         }
 
-        // line 1
-        /* this._dataSet.push([
-            "", "", "", "",
-            this._pabResults.cloisonsResults[0] ? this._pabResults.cloisonsResults[0].vCalc.toFixed(nDigits) : "X",
-            "", "", "", ""
-        ]);
-
-        // lines 2 - n-1
-        for (let i = 0; i < pr.cloisonsResults.length - 1; i++) {
-            // console.log("pr.cloisonsResults[i]", pr.cloisonsResults[i]);
-            // console.log("resultElement STRINGIFIÉ", JSON.stringify(pr.cloisonsResults[i].resultElement));
-            const ZRAM = pr.cloisonsResults[i].getExtraResult("ZRAM");
-            const PV = pr.cloisonsResults[i].getExtraResult("PV");
-            const Y = pr.cloisonsResults[i].getExtraResult("Y");
-            const ZRB = pr.cloisonsResults[i].getExtraResult("ZRB");
-            this._dataSet.push([
-                i + 1,
-                (pr.cloisonsResults[i].vCalc - pr.cloisonsResults[i + 1].vCalc).toFixed(nDigits),
-                (ZRAM !== undefined) ? ZRAM.toFixed(nDigits) : "X",
-                pr.cloisonsQ[i].toFixed(nDigits),
-                pr.cloisonsResults[i + 1].vCalc.toFixed(nDigits),
-                (PV !== undefined) ? PV.toFixed(nDigits) : "X",
-                (Y !== undefined) ? Y.toFixed(nDigits) : "X",
-                (ZRB !== undefined) ? ZRB.toFixed(nDigits) : "X",
-                pr.bassinsQA[i]
-            ]);
-        }
-
-        // line n
-        const l = pr.cloisonsResults.length;
-        const nZRAM = pr.cloisonsResults[l - 1].getExtraResult("ZRAM");
-        const nPV = pr.cloisonsResults[l - 1].getExtraResult("PV");
-        const nY = pr.cloisonsResults[l - 1].getExtraResult("Y");
-        const nZRB = pr.cloisonsResults[l - 1].getExtraResult("ZRB");
-        this._dataSet.push([
-            l,
-            (pr.cloisonsResults[l - 1].vCalc - pr.cloisonAvalResults.vCalc).toFixed(nDigits),
-            (nZRAM !== undefined) ? nZRAM.toFixed(nDigits) : "X",
-            pr.cloisonsQ[l - 1].toFixed(nDigits),
-            pr.cloisonAvalResults.vCalc.toFixed(nDigits),
-            (nPV !== undefined) ? nPV.toFixed(nDigits) : "X",
-            (nY !== undefined) ? nY.toFixed(nDigits) : "X",
-            (nZRB !== undefined) ? nZRB.toFixed(nDigits) : "X",
-            pr.bassinsQA[l - 1]
-        ]);
-
-        // downstream line
-        const caZRAM = pr.cloisonAvalResults.getExtraResult("ZRAM");
-        this._dataSet.push([
-            this.intlService.localizeText("INFO_EXTRARES_LIB_AVAL"),
-            (pr.cloisonAvalResults.vCalc - pr.Z2).toFixed(nDigits),
-            (caZRAM !== undefined) ? caZRAM.toFixed(nDigits) : "X",
-            pr.cloisonAvalQ.toFixed(nDigits),
-            pr.Z2.toFixed(nDigits),
-            "",
-            "",
-            "",
-            ""
-        ]); */
-
-
-
         return data;
     }
 }
diff --git a/src/app/results/var-results.ts b/src/app/results/var-results.ts
index 8224154f1..3a0356668 100644
--- a/src/app/results/var-results.ts
+++ b/src/app/results/var-results.ts
@@ -185,7 +185,7 @@ export class VarResults extends CalculatedParamResults implements PlottableData
             this._extraResultHeaders.push(intlService.getExtraResLabel(k));
         }
 
-        // when variable parameter changes, ensure the X / Y current values are still availble
+        // when variable parameter changes, ensure the X / Y current values are still available
         // (might be the previous variated parameter, that is not accessible anymore)
         const aca = this.getAvailableChartAxis();
         if (! aca.includes(this.chartX)) {
-- 
GitLab


From c884e939bccd387cbac9c08f2691081a2eebf7c5 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Tue, 14 May 2019 15:56:57 +0200
Subject: [PATCH 32/44] Ajout id bouton

---
 .../select-model-field-line.component.html                      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/app/components/select-model-field-line/select-model-field-line.component.html b/src/app/components/select-model-field-line/select-model-field-line.component.html
index 37b2b3295..f9153337a 100644
--- a/src/app/components/select-model-field-line/select-model-field-line.component.html
+++ b/src/app/components/select-model-field-line/select-model-field-line.component.html
@@ -16,7 +16,7 @@
         <button type="button" mat-icon-button *ngIf="selectedValue" [routerLink]="['/calculator/', modelUid]">
             <mat-icon>edit</mat-icon>
         </button>
-        <button type="button" mat-icon-button *ngIf="entries.length === 0" (click)="createModel()">
+        <button type="button" mat-icon-button *ngIf="entries.length === 0" (click)="createModel()" id="create-model">
             <mat-icon>add</mat-icon>
         </button>
     </div>
-- 
GitLab


From bb6b8bdd991de8a3cd668ac3b9ddf30ff1ec1c42 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Tue, 14 May 2019 15:57:02 +0200
Subject: [PATCH 33/44] =?UTF-8?q?M=C3=A0J=20test=20PAB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 e2e/calculate-all-params.e2e-spec.ts | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/e2e/calculate-all-params.e2e-spec.ts b/e2e/calculate-all-params.e2e-spec.ts
index ca53ff2ba..f83a8212a 100644
--- a/e2e/calculate-all-params.e2e-spec.ts
+++ b/e2e/calculate-all-params.e2e-spec.ts
@@ -1,5 +1,6 @@
 import { ListPage } from "./list.po";
 import { CalculatorPage } from "./calculator.po";
+import { Navbar } from "./navbar.po";
 
 /**
  * For all calculators, try to calculate every parameter: check that only one parameter
@@ -8,10 +9,12 @@ import { CalculatorPage } from "./calculator.po";
 describe("ngHyd − calculate all parameters of all calculators", () => {
   let listPage: ListPage;
   let calcPage: CalculatorPage;
+  let navBar: Navbar;
 
   beforeEach(() => {
     listPage = new ListPage();
     calcPage = new CalculatorPage();
+    navBar = new Navbar();
   });
 
   // get calculators list (IDs) @TODO read it from config !
@@ -23,6 +26,15 @@ describe("ngHyd − calculate all parameters of all calculators", () => {
       it("", async () => {
         // go to list page
         await listPage.navigateTo();
+
+        // special case for PAB: create a Cloisons and an Ouvrages before
+        if (ct === 15) {
+          await listPage.clickMenuEntryForCalcType(8);
+          await navBar.clickNewCalculatorButton();
+          await listPage.clickMenuEntryForCalcType(10);
+          await navBar.clickNewCalculatorButton();
+        }
+
         // click calculator button (instanciate)
         await listPage.clickMenuEntryForCalcType(ct);
         // get all parameters IDs
-- 
GitLab


From 639b6b6487553fe20c58adf96762f1f3dfeb69ad Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Tue, 14 May 2019 16:42:05 +0200
Subject: [PATCH 34/44] =?UTF-8?q?PAB:=20aggr=C3=A9gation=20des=20logs=20de?=
 =?UTF-8?q?s=20cloisons,=20protection=20des=20r=C3=A9sultats=20contre=20le?=
 =?UTF-8?q?s=20erreurs=20de=20calcul?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../pab-results-table.component.ts            | 117 ++++++++++--------
 .../pab-results/pab-results.component.ts      |   6 +
 src/app/results/pab-results.ts                |  15 +++
 src/app/results/plottable-pab-results.ts      |   8 ++
 4 files changed, 96 insertions(+), 50 deletions(-)

diff --git a/src/app/components/pab-results/pab-results-table.component.ts b/src/app/components/pab-results/pab-results-table.component.ts
index d598ae9bc..83603a493 100644
--- a/src/app/components/pab-results/pab-results-table.component.ts
+++ b/src/app/components/pab-results/pab-results-table.component.ts
@@ -40,7 +40,12 @@ export class PabResultsTableComponent {
         this._pabResults = r;
 
         this._dataSet = [];
-        if (this._pabResults && this._pabResults.cloisonsResults && this._pabResults.cloisonsResults.length > 0) {
+        if (
+            this._pabResults
+            && this._pabResults.cloisonsResults
+            && this._pabResults.cloisonsResults.length > 0
+            && ! this._pabResults.hasError()
+        ) {
             const pr = this._pabResults;
             const nDigits = this.appSetupService.displayDigits;
 
@@ -48,64 +53,76 @@ export class PabResultsTableComponent {
             this._headers = pr.headers;
 
             // line 1
-            this._dataSet.push([
-                "", "", "", "",
-                this._pabResults.cloisonsResults[0] ? this._pabResults.cloisonsResults[0].vCalc.toFixed(nDigits) : "",
-                "", "", "", ""
-            ]);
+            if (pr.cloisonsResults[0].vCalc) { // parfois le calcul des cloisons échoue
+                this._dataSet.push([
+                    "", "", "", "",
+                    pr.cloisonsResults[0] ? pr.cloisonsResults[0].vCalc.toFixed(nDigits) : "",
+                    "", "", "", ""
+                ]);
+            }
 
             // lines 2 - n-1
             for (let i = 0; i < pr.cloisonsResults.length - 1; i++) {
-                // console.log("pr.cloisonsResults[i]", pr.cloisonsResults[i]);
-                // console.log("resultElement STRINGIFIÉ", JSON.stringify(pr.cloisonsResults[i].resultElement));
-                const ZRAM = pr.cloisonsResults[i].getExtraResult("ZRAM");
-                const PV = pr.cloisonsResults[i].getExtraResult("PV");
-                const Y = pr.cloisonsResults[i].getExtraResult("Y");
-                const ZRB = pr.cloisonsResults[i].getExtraResult("ZRB");
-                this._dataSet.push([
-                    i + 1,
-                    (pr.cloisonsResults[i].vCalc - pr.cloisonsResults[i + 1].vCalc).toFixed(nDigits),
-                    (ZRAM !== undefined) ? ZRAM.toFixed(nDigits) : "",
-                    pr.cloisonsQ[i].toFixed(nDigits),
-                    pr.cloisonsResults[i + 1].vCalc.toFixed(nDigits),
-                    (PV !== undefined) ? PV.toFixed(nDigits) : "",
-                    (Y !== undefined) ? Y.toFixed(nDigits) : "",
-                    (ZRB !== undefined) ? ZRB.toFixed(nDigits) : "",
-                    pr.bassinsQA[i]
-                ]);
+                if (
+                    pr.cloisonsResults[i].vCalc
+                    && pr.cloisonsResults[i + 1].vCalc
+                ) {
+                    const ZRAM = pr.cloisonsResults[i].getExtraResult("ZRAM");
+                    const PV = pr.cloisonsResults[i].getExtraResult("PV");
+                    const Y = pr.cloisonsResults[i].getExtraResult("Y");
+                    const ZRB = pr.cloisonsResults[i].getExtraResult("ZRB");
+                    this._dataSet.push([
+                        i + 1,
+                        (pr.cloisonsResults[i].vCalc - pr.cloisonsResults[i + 1].vCalc).toFixed(nDigits),
+                        (ZRAM !== undefined) ? ZRAM.toFixed(nDigits) : "",
+                        pr.cloisonsQ[i].toFixed(nDigits),
+                        pr.cloisonsResults[i + 1].vCalc.toFixed(nDigits),
+                        (PV !== undefined) ? PV.toFixed(nDigits) : "",
+                        (Y !== undefined) ? Y.toFixed(nDigits) : "",
+                        (ZRB !== undefined) ? ZRB.toFixed(nDigits) : "",
+                        pr.bassinsQA[i]
+                    ]);
+                }
             }
 
             // line n
             const l = pr.cloisonsResults.length;
-            const nZRAM = pr.cloisonsResults[l - 1].getExtraResult("ZRAM");
-            const nPV = pr.cloisonsResults[l - 1].getExtraResult("PV");
-            const nY = pr.cloisonsResults[l - 1].getExtraResult("Y");
-            const nZRB = pr.cloisonsResults[l - 1].getExtraResult("ZRB");
-            this._dataSet.push([
-                l,
-                (pr.cloisonsResults[l - 1].vCalc - pr.cloisonAvalResults.vCalc).toFixed(nDigits),
-                (nZRAM !== undefined) ? nZRAM.toFixed(nDigits) : "",
-                pr.cloisonsQ[l - 1].toFixed(nDigits),
-                pr.cloisonAvalResults.vCalc.toFixed(nDigits),
-                (nPV !== undefined) ? nPV.toFixed(nDigits) : "",
-                (nY !== undefined) ? nY.toFixed(nDigits) : "",
-                (nZRB !== undefined) ? nZRB.toFixed(nDigits) : "",
-                pr.bassinsQA[l - 1]
-            ]);
+            if (
+                pr.cloisonsResults[l - 1].vCalc
+                && pr.cloisonAvalResults.vCalc
+            ) {
+                const nZRAM = pr.cloisonsResults[l - 1].getExtraResult("ZRAM");
+                const nPV = pr.cloisonsResults[l - 1].getExtraResult("PV");
+                const nY = pr.cloisonsResults[l - 1].getExtraResult("Y");
+                const nZRB = pr.cloisonsResults[l - 1].getExtraResult("ZRB");
+                this._dataSet.push([
+                    l,
+                    (pr.cloisonsResults[l - 1].vCalc - pr.cloisonAvalResults.vCalc).toFixed(nDigits),
+                    (nZRAM !== undefined) ? nZRAM.toFixed(nDigits) : "",
+                    pr.cloisonsQ[l - 1].toFixed(nDigits),
+                    pr.cloisonAvalResults.vCalc.toFixed(nDigits),
+                    (nPV !== undefined) ? nPV.toFixed(nDigits) : "",
+                    (nY !== undefined) ? nY.toFixed(nDigits) : "",
+                    (nZRB !== undefined) ? nZRB.toFixed(nDigits) : "",
+                    pr.bassinsQA[l - 1]
+                ]);
+            }
 
             // downstream line
-            const caZRAM = pr.cloisonAvalResults.getExtraResult("ZRAM");
-            this._dataSet.push([
-                this.intlService.localizeText("INFO_EXTRARES_LIB_AVAL"),
-                (pr.cloisonAvalResults.vCalc - pr.Z2).toFixed(nDigits),
-                (caZRAM !== undefined) ? caZRAM.toFixed(nDigits) : "",
-                pr.cloisonAvalQ.toFixed(nDigits),
-                pr.Z2.toFixed(nDigits),
-                "",
-                "",
-                "",
-                ""
-            ]);
+            if (pr.cloisonAvalResults.vCalc) {
+                const caZRAM = pr.cloisonAvalResults.getExtraResult("ZRAM");
+                this._dataSet.push([
+                    this.intlService.localizeText("INFO_EXTRARES_LIB_AVAL"),
+                    (pr.cloisonAvalResults.vCalc - pr.Z2).toFixed(nDigits),
+                    (caZRAM !== undefined) ? caZRAM.toFixed(nDigits) : "",
+                    pr.cloisonAvalQ.toFixed(nDigits),
+                    pr.Z2.toFixed(nDigits),
+                    "",
+                    "",
+                    "",
+                    ""
+                ]);
+            }
         }
     }
 
diff --git a/src/app/components/pab-results/pab-results.component.ts b/src/app/components/pab-results/pab-results.component.ts
index e96534074..d17318d76 100644
--- a/src/app/components/pab-results/pab-results.component.ts
+++ b/src/app/components/pab-results/pab-results.component.ts
@@ -90,6 +90,12 @@ export class PabResultsComponent implements DoCheck {
         const res = new cLog();
         if (this._pabResults) {
             this.mergeLog(this._pabResults.result, res);
+            // log des cloisons
+            for (const c of this._pabResults.cloisonsResults) {
+                this.mergeLog(c, res);
+            }
+            // log de la cloison aval
+            this.mergeLog(this._pabResults.cloisonAvalResults, res);
         }
         return res;
     }
diff --git a/src/app/results/pab-results.ts b/src/app/results/pab-results.ts
index e9b135981..9f381a010 100644
--- a/src/app/results/pab-results.ts
+++ b/src/app/results/pab-results.ts
@@ -68,4 +68,19 @@ export class PabResults extends CalculatedParamResults {
         this.cloisonAvalQ = undefined;
         this.Z2 = undefined;
     }
+
+    /** retourne true si au moins un calcul a échoué (le log a un code négatif) */
+    public hasError(): boolean {
+        let err = false;
+        // log principal
+        err = (err || this.result.hasErrorMessages);
+        // logs des cloisons
+        for (const c of this.cloisonsResults) {
+            err = (err || c.hasErrorMessages);
+        }
+        // log de la cloison aval
+        err = (err || this.cloisonAvalResults.hasErrorMessages);
+
+        return err;
+    }
 }
diff --git a/src/app/results/plottable-pab-results.ts b/src/app/results/plottable-pab-results.ts
index 628b79106..9122fcee6 100644
--- a/src/app/results/plottable-pab-results.ts
+++ b/src/app/results/plottable-pab-results.ts
@@ -51,6 +51,10 @@ export class PlottablePabResults implements PlottableData {
         const nDigits = ServiceFactory.instance.applicationSetupService.displayDigits;
         const l = this.pabResults.cloisonsResults.length;
 
+        if (this.hasError()) {
+            return [];
+        }
+
         switch (symbol) {
             case "CLOISON":
                 data.push("");
@@ -133,4 +137,8 @@ export class PlottablePabResults implements PlottableData {
 
         return data;
     }
+
+    protected hasError(): boolean {
+        return this.pabResults.hasError();
+    }
 }
-- 
GitLab


From f9c247e02c7a6e9efdb5abd5975754f3b337ada4 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Tue, 14 May 2019 17:33:04 +0200
Subject: [PATCH 35/44] Fix #199 : ID uniques pour les <input> des PabCloisons

---
 src/app/components/generic-input/generic-input.component.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/app/components/generic-input/generic-input.component.ts b/src/app/components/generic-input/generic-input.component.ts
index e75f2ed18..4b23185d6 100644
--- a/src/app/components/generic-input/generic-input.component.ts
+++ b/src/app/components/generic-input/generic-input.component.ts
@@ -1,7 +1,7 @@
 import { Input, Output, EventEmitter, ChangeDetectorRef, OnChanges, ViewChild } from "@angular/core";
 import { NgModel } from "@angular/forms";
 import { BaseComponent } from "../base/base.component";
-import { isNumeric, Structure } from "jalhyd";
+import { isNumeric, Structure, PabCloisons } from "jalhyd";
 import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
 import { NgParameter } from "../../formulaire/ngparam";
 import { I18nService } from "../../services/internationalisation/internationalisation.service";
@@ -46,7 +46,7 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC
                 // if inside a nested Structure, prefix with Structure position
                 // to disambiguate
                 const nub = param.paramDefinition.parentNub;
-                if (nub && nub instanceof Structure) {
+                if (nub && (nub instanceof Structure || nub instanceof PabCloisons)) {
                     id = nub.findPositionInParent() + "_" + id;
                 }
             }
-- 
GitLab


From 092388f1bb43d9e2f9dfc88caef5da04d5e955e2 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Tue, 14 May 2019 17:53:40 +0200
Subject: [PATCH 36/44] PAB: changement d'ordre des colonnes, renommage des
 variables

---
 .../pab-results-table.component.ts            | 59 +++++++++----------
 src/app/results/pab-results.ts                |  6 +-
 src/app/results/plottable-pab-results.ts      | 10 ++--
 src/locale/messages.en.json                   |  6 +-
 src/locale/messages.fr.json                   |  6 +-
 5 files changed, 42 insertions(+), 45 deletions(-)

diff --git a/src/app/components/pab-results/pab-results-table.component.ts b/src/app/components/pab-results/pab-results-table.component.ts
index 83603a493..b9064fcdc 100644
--- a/src/app/components/pab-results/pab-results-table.component.ts
+++ b/src/app/components/pab-results/pab-results-table.component.ts
@@ -55,9 +55,9 @@ export class PabResultsTableComponent {
             // line 1
             if (pr.cloisonsResults[0].vCalc) { // parfois le calcul des cloisons échoue
                 this._dataSet.push([
-                    "", "", "", "",
+                    "",
                     pr.cloisonsResults[0] ? pr.cloisonsResults[0].vCalc.toFixed(nDigits) : "",
-                    "", "", "", ""
+                    "", "", "", "", "", "", ""
                 ]);
             }
 
@@ -69,18 +69,18 @@ export class PabResultsTableComponent {
                 ) {
                     const ZRAM = pr.cloisonsResults[i].getExtraResult("ZRAM");
                     const PV = pr.cloisonsResults[i].getExtraResult("PV");
-                    const Y = pr.cloisonsResults[i].getExtraResult("Y");
+                    const YMOY = pr.cloisonsResults[i].getExtraResult("YMOY");
                     const ZRB = pr.cloisonsResults[i].getExtraResult("ZRB");
                     this._dataSet.push([
-                        i + 1,
-                        (pr.cloisonsResults[i].vCalc - pr.cloisonsResults[i + 1].vCalc).toFixed(nDigits),
-                        (ZRAM !== undefined) ? ZRAM.toFixed(nDigits) : "",
-                        pr.cloisonsQ[i].toFixed(nDigits),
-                        pr.cloisonsResults[i + 1].vCalc.toFixed(nDigits),
-                        (PV !== undefined) ? PV.toFixed(nDigits) : "",
-                        (Y !== undefined) ? Y.toFixed(nDigits) : "",
-                        (ZRB !== undefined) ? ZRB.toFixed(nDigits) : "",
-                        pr.bassinsQA[i]
+                        i + 1, // n° cloison
+                        pr.cloisonsResults[i + 1].vCalc.toFixed(nDigits), // Z
+                        (ZRAM !== undefined) ? ZRAM.toFixed(nDigits) : "", // ZRAM
+                        (pr.cloisonsResults[i].vCalc - pr.cloisonsResults[i + 1].vCalc).toFixed(nDigits), // DH
+                        pr.cloisonsQ[i].toFixed(nDigits), // Q
+                        (PV !== undefined) ? PV.toFixed(nDigits) : "", // PV
+                        (YMOY !== undefined) ? YMOY.toFixed(nDigits) : "", // YMOY
+                        (ZRB !== undefined) ? ZRB.toFixed(nDigits) : "", // ZRB
+                        pr.bassinsQA[i] // QA
                     ]);
                 }
             }
@@ -93,18 +93,18 @@ export class PabResultsTableComponent {
             ) {
                 const nZRAM = pr.cloisonsResults[l - 1].getExtraResult("ZRAM");
                 const nPV = pr.cloisonsResults[l - 1].getExtraResult("PV");
-                const nY = pr.cloisonsResults[l - 1].getExtraResult("Y");
+                const nY = pr.cloisonsResults[l - 1].getExtraResult("YMOY");
                 const nZRB = pr.cloisonsResults[l - 1].getExtraResult("ZRB");
                 this._dataSet.push([
-                    l,
-                    (pr.cloisonsResults[l - 1].vCalc - pr.cloisonAvalResults.vCalc).toFixed(nDigits),
-                    (nZRAM !== undefined) ? nZRAM.toFixed(nDigits) : "",
-                    pr.cloisonsQ[l - 1].toFixed(nDigits),
-                    pr.cloisonAvalResults.vCalc.toFixed(nDigits),
-                    (nPV !== undefined) ? nPV.toFixed(nDigits) : "",
-                    (nY !== undefined) ? nY.toFixed(nDigits) : "",
-                    (nZRB !== undefined) ? nZRB.toFixed(nDigits) : "",
-                    pr.bassinsQA[l - 1]
+                    l, // n° cloison
+                    pr.cloisonAvalResults.vCalc.toFixed(nDigits), // Z
+                    (nZRAM !== undefined) ? nZRAM.toFixed(nDigits) : "", // ZRAM
+                    (pr.cloisonsResults[l - 1].vCalc - pr.cloisonAvalResults.vCalc).toFixed(nDigits), // DH
+                    pr.cloisonsQ[l - 1].toFixed(nDigits), // Q
+                    (nPV !== undefined) ? nPV.toFixed(nDigits) : "", // PV
+                    (nY !== undefined) ? nY.toFixed(nDigits) : "", // YMOY
+                    (nZRB !== undefined) ? nZRB.toFixed(nDigits) : "", // ZRB
+                    pr.bassinsQA[l - 1] // QA
                 ]);
             }
 
@@ -112,15 +112,12 @@ export class PabResultsTableComponent {
             if (pr.cloisonAvalResults.vCalc) {
                 const caZRAM = pr.cloisonAvalResults.getExtraResult("ZRAM");
                 this._dataSet.push([
-                    this.intlService.localizeText("INFO_EXTRARES_LIB_AVAL"),
-                    (pr.cloisonAvalResults.vCalc - pr.Z2).toFixed(nDigits),
-                    (caZRAM !== undefined) ? caZRAM.toFixed(nDigits) : "",
-                    pr.cloisonAvalQ.toFixed(nDigits),
-                    pr.Z2.toFixed(nDigits),
-                    "",
-                    "",
-                    "",
-                    ""
+                    this.intlService.localizeText("INFO_EXTRARES_LIB_AVAL"), // n° cloison
+                    pr.Z2.toFixed(nDigits), // Z
+                    (caZRAM !== undefined) ? caZRAM.toFixed(nDigits) : "", // ZRAM
+                    (pr.cloisonAvalResults.vCalc - pr.Z2).toFixed(nDigits), // DH
+                    pr.cloisonAvalQ.toFixed(nDigits), // Q
+                    "", "", "", ""
                 ]);
             }
         }
diff --git a/src/app/results/pab-results.ts b/src/app/results/pab-results.ts
index 9f381a010..435220e7f 100644
--- a/src/app/results/pab-results.ts
+++ b/src/app/results/pab-results.ts
@@ -36,12 +36,12 @@ export class PabResults extends CalculatedParamResults {
         // standard headers
         this._columns = [
             "CLOISON",
+            "Z",
+            "ZRAM",
             "DH",
-            "ZR",
             "Q",
-            "Z1_PAB",
             "PV",
-            "TMOY",
+            "YMOY",
             "ZRB",
             "QA"
         ];
diff --git a/src/app/results/plottable-pab-results.ts b/src/app/results/plottable-pab-results.ts
index 9122fcee6..00df147c2 100644
--- a/src/app/results/plottable-pab-results.ts
+++ b/src/app/results/plottable-pab-results.ts
@@ -17,7 +17,7 @@ export class PlottablePabResults implements PlottableData {
         }
         // axes par défaut
         this.chartX = this.chartX || "CLOISON";
-        this.chartY = this.chartY || "Z1_PAB";
+        this.chartY = this.chartY || "Z";
     }
 
     /** reaffect pabResults, for ex. when objet was contructed with empty pabResults */
@@ -73,7 +73,7 @@ export class PlottablePabResults implements PlottableData {
                 data.push((pr.cloisonAvalResults.vCalc - pr.Z2).toFixed(nDigits));
                 break;
 
-            case "ZR":
+            case "ZRAM":
                 data.push("");
                 for (let i = 0; i < l; i++) {
                     const er = pr.cloisonsResults[i].getExtraResult("ZRAM");
@@ -91,7 +91,7 @@ export class PlottablePabResults implements PlottableData {
                 data.push(pr.cloisonAvalQ.toFixed(nDigits));
                 break;
 
-            case "Z1_PAB":
+            case "Z":
                 for (let i = 0; i < l - 1; i++) {
                     data.push(pr.cloisonsResults[i].vCalc.toFixed(nDigits));
                 }
@@ -108,10 +108,10 @@ export class PlottablePabResults implements PlottableData {
                 data.push("");
                 break;
 
-            case "TMOY":
+            case "YMOY":
                 data.push("");
                 for (let i = 0; i < l; i++) {
-                    const er = pr.cloisonsResults[i].getExtraResult("Y");
+                    const er = pr.cloisonsResults[i].getExtraResult("YMOY");
                     data.push((er !== undefined) ? er.toFixed(nDigits) : "");
                 }
                 data.push("");
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index e7385b44c..6c4617820 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -110,7 +110,6 @@
     "INFO_EXTRARES_LIB_R": "Hydraulic radius (m)",
     "INFO_EXTRARES_LIB_S": "Wetted area (m²)",
     "INFO_EXTRARES_LIB_TAU0": "Tractive force (Pa)",
-    "INFO_EXTRARES_LIB_TMOY": "Average depth (m)",
     "INFO_EXTRARES_LIB_TOR": "Supercritical water line",
     "INFO_EXTRARES_LIB_V": "Average speed (m/s)",
     "INFO_EXTRARES_LIB_VDEB": "Conveyance speed (m/s)",
@@ -119,10 +118,11 @@
     "INFO_EXTRARES_LIB_YC": "Critical depth (m)",
     "INFO_EXTRARES_LIB_YCO": "Conjugate depth (m)",
     "INFO_EXTRARES_LIB_YF": "Subcritical depth (m)",
+    "INFO_EXTRARES_LIB_YMOY": "Average depth (m)",
     "INFO_EXTRARES_LIB_YN": "Normal depth (m)",
     "INFO_EXTRARES_LIB_YT": "Supercritical depth (m)",
-    "INFO_EXTRARES_LIB_Z1_PAB": "Water level (m)",
-    "INFO_EXTRARES_LIB_ZR": "Bottom elevation (m)",
+    "INFO_EXTRARES_LIB_Z": "Water level (m)",
+    "INFO_EXTRARES_LIB_ZRAM": "Bottom elevation (m)",
     "INFO_EXTRARES_LIB_ZRB": "Downstream basin bottom elevation (m)",
     "INFO_EXTRARES_LIB_ZF2": "Downstream bottom elevation (m)",
     "INFO_LECHAPTCALMON_TITRE": "Lechapt-Calmon",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index 61856081f..0eec0a6c0 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -110,7 +110,6 @@
     "INFO_EXTRARES_LIB_R": "Rayon hydraulique (m)",
     "INFO_EXTRARES_LIB_S": "Surface mouillée (m²)",
     "INFO_EXTRARES_LIB_TAU0": "Force tractrice (Pa)",
-    "INFO_EXTRARES_LIB_TMOY": "Tirant d'eau moyen (m)",
     "INFO_EXTRARES_LIB_TOR": "Ligne d'eau torrentielle",
     "INFO_EXTRARES_LIB_V": "Vitesse moyenne (m/s)",
     "INFO_EXTRARES_LIB_VDEB": "Vitesse débitante (m/s)",
@@ -119,10 +118,11 @@
     "INFO_EXTRARES_LIB_YC": "Tirant d'eau critique (m)",
     "INFO_EXTRARES_LIB_YCO": "Tirant d'eau conjugué (m)",
     "INFO_EXTRARES_LIB_YF": "Tirant d'eau fluvial (m)",
+    "INFO_EXTRARES_LIB_YMOY": "Tirant d'eau moyen (m)",
     "INFO_EXTRARES_LIB_YN": "Tirant d'eau normal (m)",
     "INFO_EXTRARES_LIB_YT": "Tirant d'eau torrentiel (m)",
-    "INFO_EXTRARES_LIB_Z1_PAB": "Niveau d'eau (m)",
-    "INFO_EXTRARES_LIB_ZR": "Cote de radier (m)",
+    "INFO_EXTRARES_LIB_Z": "Niveau d'eau (m)",
+    "INFO_EXTRARES_LIB_ZRAM": "Cote de radier amont de la cloison (m)",
     "INFO_EXTRARES_LIB_ZRB": "Cote de radier du bassin aval (m)",
     "INFO_EXTRARES_LIB_ZF2": "Cote de fond aval (m)",
     "INFO_LECHAPTCALMON_TITRE": "Lechapt-Calmon",
-- 
GitLab


From f4ce50105391746936df33b74b26ed1d9e5e2d4a Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 16 May 2019 09:19:02 +0200
Subject: [PATCH 37/44] =?UTF-8?q?Liste=20des=20modules,=20carte=20"Passe?=
 =?UTF-8?q?=20=C3=A0=20bassins":=20changement=20de=20l'ordre=20des=20modul?=
 =?UTF-8?q?es?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/app/config.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/app/config.json b/src/app/config.json
index 19625acb4..76fe94639 100644
--- a/src/app/config.json
+++ b/src/app/config.json
@@ -13,7 +13,7 @@
                 "title": "Passe à poisson sur le Lez, entre Bollène et Suze",
                 "credits": "Hervé Capra / Irstea"
             },
-            "calculators": [ 15, 5, 6, 12, 13, 10, 9 ]
+            "calculators": [ 12, 13, 6, 5, 10, 15, 9 ]
         },
         {
             "name": "PASSE_NATURELLE",
-- 
GitLab


From b14bd4e5e0f04830e171dbe4d2dccc308c4442c0 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 16 May 2019 11:56:25 +0200
Subject: [PATCH 38/44] =?UTF-8?q?M=C3=A0J=20traductions=20:=20nouveaux=20c?=
 =?UTF-8?q?odes=20d'erreur?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/locale/messages.en.json | 2 ++
 src/locale/messages.fr.json | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index 6c4617820..5ba477488 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -6,6 +6,7 @@
     "ERROR_DICHO_INIT_DOMAIN": "Dichotomy: target %targetSymbol%=%targetValue% does not exist for variable %variableSymbol% valued in interval %variableInterval%",
     "ERROR_DICHO_INVALID_STEP_GROWTH": "Dichotomy (initial interval search): invalid null step growth",
     "ERROR_DICHO_NULL_STEP": "Dichotomy (initial interval search): invalid null step",
+    "ERROR_ELEVATION_ZI_LOWER_THAN_Z2": "Upstream elevation is lower than downstream elevation",
     "ERROR_INTERVAL_OUTSIDE": "Interval: value %value% is outside of %interval",
     "ERROR_INTERVAL_UNDEF": "Interval: invalid 'undefined' value",
     "ERROR_INVALID_AT_POSITION": "Position %s:",
@@ -19,6 +20,7 @@
     "ERROR_PARAMDEF_CALC_UNDEFINED": "calculability of '%symbol%' parameter is undefined",
     "ERROR_PARAMDEF_LINKED_VALUE_UNDEFINED": "value of '%symbol%' linked parameter is undefined",
     "ERROR_PARAMDEF_VALUE_FIXED": "value of '%symbol%' parameter cannot be changed",
+    "ERROR_PARAMDEF_VALUE_INTEGER": "value of '%symbol%' parameter must be integer",
     "ERROR_PARAMDEF_VALUE_INTERVAL": "parameter '%symbol%': value %value% is out of [%minValue%, %maxValue%] interval",
     "ERROR_PARAMDEF_VALUE_NULL": "value of '%symbol%' parameter cannot be 0",
     "ERROR_PARAMDEF_VALUE_POS": "value %value% of '%symbol%' parameter is invalid (cannot be <=0)",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index 0eec0a6c0..8eba3a450 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -6,6 +6,7 @@
     "ERROR_DICHO_INIT_DOMAIN": "Dichotomie&nbsp;: la valeur cible %targetSymbol%=%targetValue% n'existe pas pour la variable %variableSymbol% prise dans l'intervalle %variableInterval%",
     "ERROR_DICHO_INVALID_STEP_GROWTH": "Dichotomie&nbsp;: l'augmentation du pas pour la recherche de l'intervalle de départ est incorrecte (=0)",
     "ERROR_DICHO_NULL_STEP": "Dichotomie&nbsp;: le pas pour la recherche de l'intervalle de départ ne devrait pas être nul",
+    "ERROR_ELEVATION_ZI_LOWER_THAN_Z2": "La cote amont est plus basse que la cote aval",
     "ERROR_INTERVAL_OUTSIDE": "Interval&nbsp;: la valeur %value% est hors de l'intervalle %interval",
     "ERROR_INTERVAL_UNDEF": "Interval&nbsp;: valeur 'undefined' incorrecte",
     "ERROR_INVALID_AT_POSITION": "Position %s :",
@@ -19,6 +20,7 @@
     "ERROR_PARAMDEF_CALC_UNDEFINED": "La calculabilité du paramètre %symbol% n'est pas définie",
     "ERROR_PARAMDEF_LINKED_VALUE_UNDEFINED": "La valeur du paramètre lié %symbol% n'est pas définie",
     "ERROR_PARAMDEF_VALUE_FIXED": "La valeur du paramètre %symbol% ne peut pas être changée",
+    "ERROR_PARAMDEF_VALUE_INTEGER": "La valeur du paramètre %symbol% doit être entière",
     "ERROR_PARAMDEF_VALUE_INTERVAL": "Paramètre '%symbol%'&nbsp;: la valeur %value% est en dehors de l'intervalle [%minValue%, %maxValue%]",
     "ERROR_PARAMDEF_VALUE_NULL": "La valeur du paramètre '%symbol%' ne peut pas être nulle",
     "ERROR_PARAMDEF_VALUE_POS": "La valeur %value% du paramètre '%symbol%' est incorrecte (<=0)",
-- 
GitLab


From f01dc9224ab050be62adbde852a1ea2bfb67002f Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 16 May 2019 12:30:51 +0200
Subject: [PATCH 39/44] Fix #205

---
 src/app/components/generic-calculator/calc-name.component.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/app/components/generic-calculator/calc-name.component.html b/src/app/components/generic-calculator/calc-name.component.html
index 85c1f9c06..bd3e1b057 100644
--- a/src/app/components/generic-calculator/calc-name.component.html
+++ b/src/app/components/generic-calculator/calc-name.component.html
@@ -1,5 +1,5 @@
 <mat-form-field>
-    <input matInput #inputControl="ngModel" class="form-control" type="text"
+    <input matInput #inputControl="ngModel" class="form-control" type="text" (keydown.enter)="$event.preventDefault()"
         id="calc-name-input" [(ngModel)]="uiValue" [placeholder]="title" required>
     <mat-error>{{ errorMessage }}</mat-error>
 </mat-form-field>
-- 
GitLab


From 42eaf8a147c4508695261adae9845992d748de09 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 16 May 2019 14:13:23 +0200
Subject: [PATCH 40/44] Fix #209 suppression de "Pr" dans tous les modules,
 sauf Remous

---
 src/app/calculators/cloisons/cloisons.config.json    | 12 ------------
 .../calculators/cond_distri/cond_distri.config.json  | 11 -----------
 src/app/calculators/dever/dever.config.json          | 12 ------------
 .../lechapt-calmon/lechapt-calmon.config.json        | 11 -----------
 src/app/calculators/macrorugo/macrorugo.config.json  | 11 -----------
 .../pab-puissance/pab-puissance.config.json          | 11 -----------
 src/app/calculators/pab/pab.config.json              | 12 ------------
 .../parallel-structures.config.json                  | 12 ------------
 .../regime-uniforme/regime-uniforme.config.json      | 11 -----------
 .../section-param/section-param.config.json          | 11 -----------
 10 files changed, 114 deletions(-)

diff --git a/src/app/calculators/cloisons/cloisons.config.json b/src/app/calculators/cloisons/cloisons.config.json
index 59e6a9510..cb8cd8427 100644
--- a/src/app/calculators/cloisons/cloisons.config.json
+++ b/src/app/calculators/cloisons/cloisons.config.json
@@ -175,18 +175,6 @@
             "fs_ouvrage"
         ]
     },
-    {
-        "id": "fs_param_calc",
-        "type": "fieldset",
-        "calcType": "ParallelStructure",
-        "option": "fix",
-        "fields": [
-            {
-                "type": "input",
-                "id": "Pr"
-            }
-        ]
-    },
     {
         "type": "options",
         "ouvrageSelectId": "select_ouvrage",
diff --git a/src/app/calculators/cond_distri/cond_distri.config.json b/src/app/calculators/cond_distri/cond_distri.config.json
index 2ff24f844..7db50cfac 100644
--- a/src/app/calculators/cond_distri/cond_distri.config.json
+++ b/src/app/calculators/cond_distri/cond_distri.config.json
@@ -31,17 +31,6 @@
             }
         ]
     },
-    {
-        "id": "fs_param_calc",
-        "type": "fieldset",
-        "option": "fix",
-        "fields": [
-            {
-                "type": "input",
-                "id": "Pr"
-            }
-        ]
-    },
     {
         "type": "options",
         "idCal": "J",
diff --git a/src/app/calculators/dever/dever.config.json b/src/app/calculators/dever/dever.config.json
index 2780306f6..4bb14ec5e 100644
--- a/src/app/calculators/dever/dever.config.json
+++ b/src/app/calculators/dever/dever.config.json
@@ -206,18 +206,6 @@
             "fs_ouvrage"
         ]
     },
-    {
-        "id": "fs_param_calc",
-        "type": "fieldset",
-        "calcType": "ParallelStructure",
-        "option": "fix",
-        "fields": [
-            {
-                "type": "input",
-                "id": "Pr"
-            }
-        ]
-    },
     {
         "type": "options",
         "ouvrageSelectId": "select_ouvrage",
diff --git a/src/app/calculators/lechapt-calmon/lechapt-calmon.config.json b/src/app/calculators/lechapt-calmon/lechapt-calmon.config.json
index fd24f30d6..040f73352 100644
--- a/src/app/calculators/lechapt-calmon/lechapt-calmon.config.json
+++ b/src/app/calculators/lechapt-calmon/lechapt-calmon.config.json
@@ -225,17 +225,6 @@
             }
         ]
     },
-    {
-        "id": "fs_param_calc",
-        "type": "fieldset",
-        "option": "fix",
-        "fields": [
-            {
-                "type": "input",
-                "id": "Pr"
-            }
-        ]
-    },
     {
         "type": "options",
         "idCal": "J",
diff --git a/src/app/calculators/macrorugo/macrorugo.config.json b/src/app/calculators/macrorugo/macrorugo.config.json
index 1b693b635..253a4a984 100644
--- a/src/app/calculators/macrorugo/macrorugo.config.json
+++ b/src/app/calculators/macrorugo/macrorugo.config.json
@@ -75,17 +75,6 @@
             }
         ]
     },
-    {
-        "id": "fs_param_calc",
-        "type": "fieldset",
-        "option": "fix",
-        "fields": [
-            {
-                "type": "input",
-                "id": "Pr"
-            }
-        ]
-    },
     {
         "type": "options",
         "idCal": "Q",
diff --git a/src/app/calculators/pab-puissance/pab-puissance.config.json b/src/app/calculators/pab-puissance/pab-puissance.config.json
index 02be79b11..9e2e81595 100644
--- a/src/app/calculators/pab-puissance/pab-puissance.config.json
+++ b/src/app/calculators/pab-puissance/pab-puissance.config.json
@@ -26,17 +26,6 @@
             }
         ]
     },
-    {
-        "id": "fs_param_calc",
-        "type": "fieldset",
-        "option": "fix",
-        "fields": [
-            {
-                "type": "input",
-                "id": "Pr"
-            }
-        ]
-    },
     {
         "type": "options",
         "idCal": "PV",
diff --git a/src/app/calculators/pab/pab.config.json b/src/app/calculators/pab/pab.config.json
index 568202378..443720b8d 100644
--- a/src/app/calculators/pab/pab.config.json
+++ b/src/app/calculators/pab/pab.config.json
@@ -60,18 +60,6 @@
             }
         ]
     },
-    {
-        "id": "fs_param_calc",
-        "type": "fieldset",
-        "calcType": "Pab",
-        "option": "fix",
-        "fields": [
-            {
-                "type": "input",
-                "id": "Pr"
-            }
-        ]
-    },
     {
         "type": "options",
         "modeleCloisonsSelectId": "select_modele_cloisons",
diff --git a/src/app/calculators/parallel-structures/parallel-structures.config.json b/src/app/calculators/parallel-structures/parallel-structures.config.json
index ec632ecb8..966819227 100644
--- a/src/app/calculators/parallel-structures/parallel-structures.config.json
+++ b/src/app/calculators/parallel-structures/parallel-structures.config.json
@@ -351,18 +351,6 @@
             "fs_ouvrage"
         ]
     },
-    {
-        "id": "fs_param_calc",
-        "type": "fieldset",
-        "calcType": "ParallelStructure",
-        "option": "fix",
-        "fields": [
-            {
-                "type": "input",
-                "id": "Pr"
-            }
-        ]
-    },
     {
         "type": "options",
         "ouvrageSelectId": "select_ouvrage",
diff --git a/src/app/calculators/regime-uniforme/regime-uniforme.config.json b/src/app/calculators/regime-uniforme/regime-uniforme.config.json
index 4fa4f5f25..ddd0c099a 100644
--- a/src/app/calculators/regime-uniforme/regime-uniforme.config.json
+++ b/src/app/calculators/regime-uniforme/regime-uniforme.config.json
@@ -139,17 +139,6 @@
             }
         ]
     },
-    {
-        "id": "fs_param_calc",
-        "type": "fieldset",
-        "option": "fix",
-        "fields": [
-            {
-                "type": "input",
-                "id": "Pr"
-            }
-        ]
-    },
     {
         "type": "options",
         "defaultNodeType": "SectionRectangle",
diff --git a/src/app/calculators/section-param/section-param.config.json b/src/app/calculators/section-param/section-param.config.json
index 23d9f8f39..b9076cd68 100644
--- a/src/app/calculators/section-param/section-param.config.json
+++ b/src/app/calculators/section-param/section-param.config.json
@@ -139,17 +139,6 @@
             }
         ]
     },
-    {
-        "id": "fs_param_calc",
-        "type": "fieldset",
-        "option": "fix",
-        "fields": [
-            {
-                "type": "input",
-                "id": "Pr"
-            }
-        ]
-    },
     {
         "type": "options",
         "defaultNodeType": "SectionRectangle",
-- 
GitLab


From a011fe32079a4bf32dc5eed92c51ee7a12eb2ac2 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 16 May 2019 14:22:57 +0200
Subject: [PATCH 41/44] Fix #207 "nombre de bassins" => "nombre de chutes"

---
 src/app/calculators/pab-nombre/pab-nombre.en.json | 4 ++--
 src/app/calculators/pab-nombre/pab-nombre.fr.json | 4 ++--
 src/locale/messages.en.json                       | 4 ++--
 src/locale/messages.fr.json                       | 4 ++--
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/app/calculators/pab-nombre/pab-nombre.en.json b/src/app/calculators/pab-nombre/pab-nombre.en.json
index b4b5570ef..cbe229469 100644
--- a/src/app/calculators/pab-nombre/pab-nombre.en.json
+++ b/src/app/calculators/pab-nombre/pab-nombre.en.json
@@ -1,6 +1,6 @@
 {
-    "fs_nombre": "Fall and number of basins",
+    "fs_nombre": "Fall and number of falls",
     "DHT": "Total fall",
-    "N": "Number of basins",
+    "N": "Number of falls",
     "DH": "Fall between basins"
 }
\ No newline at end of file
diff --git a/src/app/calculators/pab-nombre/pab-nombre.fr.json b/src/app/calculators/pab-nombre/pab-nombre.fr.json
index ac914d8bd..491b42cc3 100644
--- a/src/app/calculators/pab-nombre/pab-nombre.fr.json
+++ b/src/app/calculators/pab-nombre/pab-nombre.fr.json
@@ -1,6 +1,6 @@
 {
-    "fs_nombre": "Chute et nombre de bassins",
+    "fs_nombre": "Chute et nombre de chutes",
     "DHT": "Chute totale",
-    "N": "Nombre de bassins",
+    "N": "Nombre de chutes",
     "DH": "Chute entre bassins"
 }
\ No newline at end of file
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index 5ba477488..e32af73d6 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -65,7 +65,7 @@
     "INFO_DIALOG_PAB_Q": "Flow (m³/s)",
     "INFO_DIALOG_PAB_Z1": "Upstream elevation (m)",
     "INFO_DIALOG_PAB_Z2": "Downstream elevation (m)",
-    "INFO_DIALOG_PAB_NB": "Number of basins",
+    "INFO_DIALOG_PAB_NB": "Number of falls",
     "INFO_EMPTY_SESSION_DIALOGUE_TEXT": "Warning ! All open calculators will be lost. Continue ?",
     "INFO_EMPTY_SESSION_DIALOGUE_TITRE": "New session",
     "INFO_EXTRARES_ENUM_MACRORUGOFLOWTYPE_0": "Emergent",
@@ -193,7 +193,7 @@
     "INFO_PABCHUTE_TITRE_COURT": "FL: fall",
     "INFO_PABDIMENSIONS_TITRE": "Fish ladder: dimensions",
     "INFO_PABDIMENSIONS_TITRE_COURT": "FL: dimensions",
-    "INFO_PABNOMBRE_TITRE": "Fish ladder : number of basins",
+    "INFO_PABNOMBRE_TITRE": "Fish ladder : number of falls",
     "INFO_PABNOMBRE_TITRE_COURT": "FL : number",
     "INFO_PABPUISSANCE_TITRE": "Fish ladder: dissipated power",
     "INFO_PABPUISSANCE_TITRE_COURT": "FL: diss. power",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index 8eba3a450..ca9c9d24e 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -66,7 +66,7 @@
     "INFO_DIALOG_PAB_Q": "Débit (m³/s)",
     "INFO_DIALOG_PAB_Z1": "Cote amont (m)",
     "INFO_DIALOG_PAB_Z2": "Cote aval (m)",
-    "INFO_DIALOG_PAB_NB": "Nombre de bassins",
+    "INFO_DIALOG_PAB_NB": "Nombre de chutes",
     "INFO_EMPTY_SESSION_DIALOGUE_TITRE": "Démarrer une nouvelle session",
     "INFO_EXTRARES_ENUM_MACRORUGOFLOWTYPE_0": "Émergent",
     "INFO_EXTRARES_ENUM_MACRORUGOFLOWTYPE_1": "Quasi-émergent",
@@ -193,7 +193,7 @@
     "INFO_PABCHUTE_TITRE_COURT": "PAB&nbsp;: chute",
     "INFO_PABDIMENSIONS_TITRE": "Passe à bassins&nbsp;: dimensions",
     "INFO_PABDIMENSIONS_TITRE_COURT": "PAB&nbsp;: dimensions",
-    "INFO_PABNOMBRE_TITRE": "Passe à bassins&nbsp;: nombre de bassins",
+    "INFO_PABNOMBRE_TITRE": "Passe à bassins&nbsp;: nombre de chutes",
     "INFO_PABNOMBRE_TITRE_COURT": "PAB&nbsp;: nombre",
     "INFO_PABPUISSANCE_TITRE": "Passe à bassins&nbsp;: puissance dissipée",
     "INFO_PABPUISSANCE_TITRE_COURT": "PAB&nbsp;: puissance",
-- 
GitLab


From 88cbaac0651f9914f379e437dd08fb47ff082445 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 16 May 2019 14:42:05 +0200
Subject: [PATCH 42/44] =?UTF-8?q?#211=20:=20suppression=20des=20mentions?=
 =?UTF-8?q?=20"r=C3=A9sultat=20de"=20dans=20les=20param=C3=A8tres=20liable?=
 =?UTF-8?q?s?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/locale/messages.en.json | 10 +++++-----
 src/locale/messages.fr.json | 10 +++++-----
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index e32af73d6..1f84ea625 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -156,12 +156,12 @@
     "INFO_LIB_ZRAM": "Upstream apron elevation",
     "INFO_LIB_ZT": "Triangle top elevation",
     "INFO_LINKED_VALUE_DEVICE": "%s (%s, device %s)",
-    "INFO_LINKED_VALUE_DEVICE_RESULT": "%s (result of %s, device %s)",
-    "INFO_LINKED_VALUE_EXTRA_RESULT": "%s (%s, extra result)",
-    "INFO_LINKED_VALUE_EXTRA_RESULT_OF": "%s (%s, extra result of %s)",
-    "INFO_LINKED_VALUE_RESULT": "%s (result of %s)",
+    "INFO_LINKED_VALUE_DEVICE_RESULT": "%s (%s, device %s)",
+    "INFO_LINKED_VALUE_EXTRA_RESULT": "%s (%s)",
+    "INFO_LINKED_VALUE_EXTRA_RESULT_OF": "%s (%s)",
+    "INFO_LINKED_VALUE_RESULT": "%s (%s)",
     "INFO_LINKED_VALUE_SECTION": "%s (%s, section)",
-    "INFO_LINKED_VALUE_SECTION_RESULT": "%s (result of %s, section)",
+    "INFO_LINKED_VALUE_SECTION_RESULT": "%s (%s, section)",
     "INFO_MACRORUGO_TITRE": "Rock-ramp fishpasses",
     "INFO_MACRORUGO_TITRE_COURT": "Rock-ramp",
     "INFO_MENU_EMPTY_SESSION_TITLE": "New session",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index ca9c9d24e..77620375a 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -156,12 +156,12 @@
     "INFO_LIB_ZRAM": "Cote du radier amont",
     "INFO_LIB_ZT": "Cote haute du triangle",
     "INFO_LINKED_VALUE_DEVICE": "%s (%s, ouvrage %s)",
-    "INFO_LINKED_VALUE_DEVICE_RESULT": "%s (résultat de %s, ouvrage %s)",
-    "INFO_LINKED_VALUE_EXTRA_RESULT": "%s (%s, résultat complémentaire)",
-    "INFO_LINKED_VALUE_EXTRA_RESULT_OF": "%s (%s, résultat complémentaire de %s)",
-    "INFO_LINKED_VALUE_RESULT": "%s (résultat de %s)",
+    "INFO_LINKED_VALUE_DEVICE_RESULT": "%s (%s, ouvrage %s)",
+    "INFO_LINKED_VALUE_EXTRA_RESULT": "%s (%s)",
+    "INFO_LINKED_VALUE_EXTRA_RESULT_OF": "%s (%s)",
+    "INFO_LINKED_VALUE_RESULT": "%s (%s)",
     "INFO_LINKED_VALUE_SECTION": "%s (%s, section)",
-    "INFO_LINKED_VALUE_SECTION_RESULT": "%s (résultat de %s, section)",
+    "INFO_LINKED_VALUE_SECTION_RESULT": "%s (%s, section)",
     "INFO_MACRORUGO_TITRE": "Passe à macro-rugosités",
     "INFO_MACRORUGO_TITRE_COURT": "Macro-rugo.",
     "INFO_MENU_EMPTY_SESSION_TITLE": "Nouvelle session",
-- 
GitLab


From b7d949a0c92e4cb5e3d35433b19fa97afe2c9b6d Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 16 May 2019 16:28:26 +0200
Subject: [PATCH 43/44] =?UTF-8?q?#211=20:=20afficher=20la=20valeur=20des?=
 =?UTF-8?q?=20param=C3=A8tres=20liables=20directement=20dans=20la=20liste?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../param-link/param-link.component.ts        |  36 ++++---
 src/app/formulaire/ngparam.ts                 | 101 ++++++++++--------
 2 files changed, 78 insertions(+), 59 deletions(-)

diff --git a/src/app/components/param-link/param-link.component.ts b/src/app/components/param-link/param-link.component.ts
index dd211d884..a7188d580 100644
--- a/src/app/components/param-link/param-link.component.ts
+++ b/src/app/components/param-link/param-link.component.ts
@@ -1,7 +1,7 @@
 import { Component, Input, Output, EventEmitter, OnChanges, OnDestroy } from "@angular/core";
 
 import { NgParameter } from "../../formulaire/ngparam";
-import { LinkedValue, ParamValueMode, Observer, Structure, acSection } from "jalhyd";
+import { LinkedValue, ParamValueMode, Observer, Structure, acSection, ParamDefinition } from "jalhyd";
 import { FormulaireService } from "../../services/formulaire/formulaire.service";
 import { I18nService } from "../../services/internationalisation/internationalisation.service";
 import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
@@ -153,37 +153,43 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
         const s = i.symbol; // nom associé au paramètre/à la valeur
         const c = i.meta["formTitle"]; // nom du module de calcul
 
+        // value(s) preview
+        let preview: string;
+        if (i.isResult() || i.isExtraResult()) {
+            preview = NgParameter.linkedValuePreview(i);
+        } else {
+            preview = NgParameter.preview(i.element as ParamDefinition);
+        }
+
         // 1. Paramètre / résultat d'un ouvrage dans un Nub de type ParallelStructure
         if (i.nub instanceof Structure) {
-            let p: number;
-            p = i.nub.findPositionInParent();
+            let pos: number;
+            pos = i.nub.findPositionInParent();
             if (i.isResult()) {
                 // résultat d'ouvrage
-                return sprintf(
+                return `${preview} - ` + sprintf(
                     this.intlService.localizeText("INFO_LINKED_VALUE_DEVICE_RESULT"),
-                    s, c, (p + 1)
+                    s, c, (pos + 1)
                 );
             } else {
                 // paramètre d'ouvrage
-                return sprintf(
+                return `${preview} - ` + sprintf(
                     this.intlService.localizeText("INFO_LINKED_VALUE_DEVICE"),
-                    s, c, (p + 1)
+                    s, c, (pos + 1)
                 );
             }
         } else
         // 2. Paramètre / résultat d'une section dans un Nub de type SectionNub
         if (i.nub instanceof acSection) {
-            let p: number;
-            p = i.nub.findPositionInParent();
             if (i.isResult()) {
                 // résultat de section
-                return sprintf(
+                return `${preview} - ` + sprintf(
                     this.intlService.localizeText("INFO_LINKED_VALUE_SECTION_RESULT"),
                     s, c
                 );
             } else {
                 // paramètre de section
-                return sprintf(
+                return `${preview} - ` + sprintf(
                     this.intlService.localizeText("INFO_LINKED_VALUE_SECTION"),
                     s, c
                 );
@@ -191,7 +197,7 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
         } else
         // 3. Résultat
         if (i.isResult()) {
-            return sprintf(
+            return `${preview} - ` + sprintf(
                 this.intlService.localizeText("INFO_LINKED_VALUE_RESULT"),
                 s, c
             );
@@ -200,19 +206,19 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
         if (i.isExtraResult()) {
             if (i.meta["result"]) {
                 // @TODO not used ?
-                return sprintf(
+                return `${preview} - ` + sprintf(
                     this.intlService.localizeText("INFO_LINKED_VALUE_EXTRA_RESULT_OF"),
                     s, c, i.meta["result"]
                 );
             } else {
-                return sprintf(
+                return `${preview} - ` + sprintf(
                     this.intlService.localizeText("INFO_LINKED_VALUE_EXTRA_RESULT"),
                     s, c
                 );
             }
         } else {
             // 5. Paramètre (cas général)
-            return `${s} (${c})`;
+            return `${preview} - ${s} (${c})`;
         }
     }
 
diff --git a/src/app/formulaire/ngparam.ts b/src/app/formulaire/ngparam.ts
index 294bbc53f..a3ec21b91 100644
--- a/src/app/formulaire/ngparam.ts
+++ b/src/app/formulaire/ngparam.ts
@@ -81,54 +81,67 @@ export class NgParameter extends InputField implements Observer {
                 break;
             case ParamValueMode.LINK:
                 if (p.isReferenceDefined()) {
-                    if (p.referencedValue.isParameter()) {
-                        const targetParam = (p.referencedValue.element as ParamDefinition);
-                        // calculated param ?
-                        if (targetParam.valueMode === ParamValueMode.CALCUL) {
-                            // was the result already computed ?
-                            // @WAARNING .result might be set but the computation might have failed (dichotomy for ex.)
-                            if (p.referencedValue.nub.result) {
-                                if (p.referencedValue.hasMultipleValues()) {
-                                    // like LIST mode
-                                    valuePreview = i18n.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES");
-                                    valuePreview += " " + p.referencedValue.nub.result.getCalculatedValues().map((v) => {
-                                        return v.toFixed(nDigits);
-                                    }).slice(0, 5).join("; ") + "…";
-                                } else {
-                                    const vCalc = p.referencedValue.nub.result.vCalc;
-                                    if (vCalc) {
-                                        valuePreview = String(vCalc.toFixed(nDigits));
-                                    } else {
-                                        // computation has been run but has failed
-                                        valuePreview = i18n.localizeText("INFO_PARAMFIELD_CALCULATION_FAILED");
-                                    }
-                                }
-                            } else {
-                                valuePreview = i18n.localizeText("INFO_PARAMFIELD_IN_CALCULATION");
-                            }
-                        } else {
-                            // recursive call
-                            valuePreview =  NgParameter.preview(targetParam);
-                        }
+                    valuePreview = NgParameter.linkedValuePreview(p.referencedValue);
+                }
+        }
+
+        return valuePreview;
+    }
+
+    /**
+     * Returns a text preview of the given linked value(s)
+     */
+    public static linkedValuePreview(ref: LinkedValue): string {
+        let valuePreview: string;
+        const i18n = ServiceFactory.instance.i18nService;
+        const nDigits = ServiceFactory.instance.applicationSetupService.displayDigits;
+
+        if (ref.isParameter()) {
+            const targetParam = (ref.element as ParamDefinition);
+            // calculated param ?
+            if (targetParam.valueMode === ParamValueMode.CALCUL) {
+                // was the result already computed ?
+                // @WARNING .result might be set but the computation might have failed (dichotomy for ex.)
+                if (ref.nub.result) {
+                    if (ref.hasMultipleValues()) {
+                        // like LIST mode
+                        valuePreview = i18n.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES");
+                        valuePreview += " " + ref.nub.result.getCalculatedValues().map((v) => {
+                            return v.toFixed(nDigits);
+                        }).slice(0, 5).join("; ") + "…";
                     } else {
-                        // was the result already computed ?
-                        try {
-                            const remoteValues = p.referencedValue.getParamValues();
-                            if (p.referencedValue.hasMultipleValues()) {
-                                // like LIST mode
-                                valuePreview = i18n.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES");
-                                valuePreview += " " + remoteValues.valueList.slice(0, 5).map((v) => {
-                                    return v.toFixed(nDigits);
-                                }).join("; ") + "…";
-                            } else {
-                                // like SINGLE mode
-                                valuePreview = String(remoteValues.currentValue.toFixed(nDigits));
-                            }
-                        } catch (e) {
-                            valuePreview = i18n.localizeText("INFO_PARAMFIELD_IN_CALCULATION");
+                        const vCalc = ref.nub.result.vCalc;
+                        if (vCalc) {
+                            valuePreview = String(vCalc.toFixed(nDigits));
+                        } else {
+                            // computation has been run but has failed
+                            valuePreview = i18n.localizeText("INFO_PARAMFIELD_CALCULATION_FAILED");
                         }
                     }
+                } else {
+                    valuePreview = i18n.localizeText("INFO_PARAMFIELD_IN_CALCULATION");
+                }
+            } else {
+                // recursive call
+                valuePreview =  NgParameter.preview(targetParam);
+            }
+        } else {
+            // was the result already computed ?
+            try {
+                const remoteValues = ref.getParamValues();
+                if (ref.hasMultipleValues()) {
+                    // like LIST mode
+                    valuePreview = i18n.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES");
+                    valuePreview += " " + remoteValues.valueList.slice(0, 5).map((v) => {
+                        return v.toFixed(nDigits);
+                    }).join("; ") + "…";
+                } else {
+                    // like SINGLE mode
+                    valuePreview = String(remoteValues.currentValue.toFixed(nDigits));
                 }
+            } catch (e) {
+                valuePreview = i18n.localizeText("INFO_PARAMFIELD_IN_CALCULATION");
+            }
         }
 
         return valuePreview;
-- 
GitLab


From ee824975c5ffbf5218844463d87c6e427bb6800e Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 16 May 2019 17:27:17 +0200
Subject: [PATCH 44/44] =?UTF-8?q?Param=C3=A8tres=20li=C3=A9s:=20aper=C3=A7?=
 =?UTF-8?q?u=20des=20valeurs=20plus=20compact,=20suppression=20de=20l'info?=
 =?UTF-8?q?bulle?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../param-link/param-link.component.html      |  6 ---
 .../param-link/param-link.component.scss      |  6 +--
 .../param-link/param-link.component.ts        |  2 +-
 src/app/formulaire/ngparam.ts                 | 43 +++++++++++--------
 4 files changed, 28 insertions(+), 29 deletions(-)

diff --git a/src/app/components/param-link/param-link.component.html b/src/app/components/param-link/param-link.component.html
index d24d7c0ed..aeba66357 100644
--- a/src/app/components/param-link/param-link.component.html
+++ b/src/app/components/param-link/param-link.component.html
@@ -18,9 +18,3 @@
         timelapse
     </mat-icon>
 </div>
-
-<mat-icon id="info-tooltip" #tooltip="matTooltip" [matTooltip]="tooltipText" matTooltipClass="linked-param-tooltip"
-    (click)="tooltip.toggle()"
-    (mouseenter)="$event.stopImmediatePropagation()" (mouseleave)="$event.stopImmediatePropagation()">
-    info_outline
-</mat-icon>
diff --git a/src/app/components/param-link/param-link.component.scss b/src/app/components/param-link/param-link.component.scss
index fc6224f86..412764067 100644
--- a/src/app/components/param-link/param-link.component.scss
+++ b/src/app/components/param-link/param-link.component.scss
@@ -7,8 +7,8 @@
     margin-top: 14px;
 
     mat-form-field {
-        width: calc(100% - 40px);
-        margin-right: 40px;
+        width: calc(100% - 16px);
+        margin-right: 16px;
 
         mat-select {
     
@@ -35,7 +35,7 @@
     .status-icons-container {
         position: absolute;
         top: -8px;
-        right: 8px;
+        right: 13px;
 
         > mat-icon {
             transform: scale(0.7);
diff --git a/src/app/components/param-link/param-link.component.ts b/src/app/components/param-link/param-link.component.ts
index a7188d580..5e59c9c50 100644
--- a/src/app/components/param-link/param-link.component.ts
+++ b/src/app/components/param-link/param-link.component.ts
@@ -158,7 +158,7 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
         if (i.isResult() || i.isExtraResult()) {
             preview = NgParameter.linkedValuePreview(i);
         } else {
-            preview = NgParameter.preview(i.element as ParamDefinition);
+            preview = NgParameter.preview(i.element as ParamDefinition, true);
         }
 
         // 1. Paramètre / résultat d'un ouvrage dans un Nub de type ParallelStructure
diff --git a/src/app/formulaire/ngparam.ts b/src/app/formulaire/ngparam.ts
index a3ec21b91..0154d6ae4 100644
--- a/src/app/formulaire/ngparam.ts
+++ b/src/app/formulaire/ngparam.ts
@@ -39,8 +39,9 @@ export class NgParameter extends InputField implements Observer {
 
     /**
      * Returns a text preview of the current value(s), depending on the value mode
+     * @param compact if true, will represent multiple values in a more compact way
      */
-    public static preview(p: ParamDefinition): string {
+    public static preview(p: ParamDefinition, compact: boolean = false): string {
         let valuePreview: string;
         const i18n = ServiceFactory.instance.i18nService;
         const nDigits = ServiceFactory.instance.applicationSetupService.displayDigits;
@@ -62,15 +63,23 @@ export class NgParameter extends InputField implements Observer {
                 if (step) {
                     step = step.toFixed(nDigits);
                 }
-                valuePreview = i18n.localizeText("INFO_PARAMFIELD_PARAMVARIER_MINMAXSTEP");
-                valuePreview = sprintf(valuePreview, min, max, step);
+                if (compact) {
+                    valuePreview = min + " … " + max;
+                } else {
+                    valuePreview = i18n.localizeText("INFO_PARAMFIELD_PARAMVARIER_MINMAXSTEP");
+                    valuePreview = sprintf(valuePreview, min, max, step);
+                }
                 break;
             case ParamValueMode.LISTE:
-                valuePreview = i18n.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES");
                 const vals = p.valueList || [];
-                valuePreview += " " + vals.slice(0, 5).map((v) => {
-                    return v.toFixed(nDigits);
-                }).join("; ") + "…";
+                if (compact) {
+                    valuePreview = vals[0].toFixed(nDigits) + " … " + vals[vals.length - 1].toFixed(nDigits);
+                } else {
+                    valuePreview = i18n.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES");
+                    valuePreview += " " + vals.slice(0, 5).map((v) => {
+                        return v.toFixed(nDigits);
+                    }).join("; ") + "…";
+                }
                 break;
             case ParamValueMode.CALCUL:
                 valuePreview = i18n.localizeText("INFO_PARAMFIELD_IN_CALCULATION");
@@ -104,11 +113,9 @@ export class NgParameter extends InputField implements Observer {
                 // @WARNING .result might be set but the computation might have failed (dichotomy for ex.)
                 if (ref.nub.result) {
                     if (ref.hasMultipleValues()) {
-                        // like LIST mode
-                        valuePreview = i18n.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES");
-                        valuePreview += " " + ref.nub.result.getCalculatedValues().map((v) => {
-                            return v.toFixed(nDigits);
-                        }).slice(0, 5).join("; ") + "…";
+                        // compact representation
+                        const cVal = ref.nub.result.getCalculatedValues();
+                        valuePreview = cVal[0].toFixed(nDigits) + " … " + cVal[cVal.length - 1].toFixed(nDigits);
                     } else {
                         const vCalc = ref.nub.result.vCalc;
                         if (vCalc) {
@@ -122,19 +129,17 @@ export class NgParameter extends InputField implements Observer {
                     valuePreview = i18n.localizeText("INFO_PARAMFIELD_IN_CALCULATION");
                 }
             } else {
-                // recursive call
-                valuePreview =  NgParameter.preview(targetParam);
+                // recursive call, compact mode
+                valuePreview =  NgParameter.preview(targetParam, true);
             }
         } else {
             // was the result already computed ?
             try {
                 const remoteValues = ref.getParamValues();
                 if (ref.hasMultipleValues()) {
-                    // like LIST mode
-                    valuePreview = i18n.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES");
-                    valuePreview += " " + remoteValues.valueList.slice(0, 5).map((v) => {
-                        return v.toFixed(nDigits);
-                    }).join("; ") + "…";
+                    // compact representation
+                    const cVal = remoteValues.valueList;
+                    valuePreview = cVal[0].toFixed(nDigits) + " … " + cVal[cVal.length - 1].toFixed(nDigits);
                 } else {
                     // like SINGLE mode
                     valuePreview = String(remoteValues.currentValue.toFixed(nDigits));
-- 
GitLab