From c5902b140fb0c4b7de4d959677b13f6310e373da Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Tue, 9 Jun 2020 12:25:47 +0200
Subject: [PATCH] PreBarrage: change form on graph click, edit walls

---
 src/app/calculators/pbbassin/en.json          |   6 +
 src/app/calculators/pbbassin/fr.json          |   6 +
 src/app/calculators/pbcloison/en.json         |   6 +
 src/app/calculators/pbcloison/fr.json         |   6 +
 src/app/calculators/prebarrage/config.json    |  41 ++----
 src/app/calculators/prebarrage/fr.json        |  13 +-
 .../dialog-new-pb-cloison.component.ts        |   2 -
 .../calculator.component.ts                   |   8 +-
 .../pb-schema/pb-schema.component.ts          | 135 +++++++++++++++---
 .../formulaire/definition/form-definition.ts  |   1 +
 .../definition/form-parallel-structures.ts    |   2 +-
 .../formulaire/definition/form-pb-cloison.ts  |  74 ++++++++++
 .../formulaire/definition/form-prebarrage.ts  | 115 +++++++++------
 .../formulaire/elements/formulaire-node.ts    |   2 +-
 src/app/formulaire/elements/pb-schema.ts      |   5 +
 15 files changed, 316 insertions(+), 106 deletions(-)
 create mode 100644 src/app/calculators/pbbassin/en.json
 create mode 100644 src/app/calculators/pbbassin/fr.json
 create mode 100644 src/app/calculators/pbcloison/en.json
 create mode 100644 src/app/calculators/pbcloison/fr.json
 create mode 100644 src/app/formulaire/definition/form-pb-cloison.ts

diff --git a/src/app/calculators/pbbassin/en.json b/src/app/calculators/pbbassin/en.json
new file mode 100644
index 000000000..6566e3fd8
--- /dev/null
+++ b/src/app/calculators/pbbassin/en.json
@@ -0,0 +1,6 @@
+{
+    "fs_basin_params": "Basin parameters",
+
+    "S": "Surface",
+    "ZF": "Bottom elevation"
+}
diff --git a/src/app/calculators/pbbassin/fr.json b/src/app/calculators/pbbassin/fr.json
new file mode 100644
index 000000000..71d54fd77
--- /dev/null
+++ b/src/app/calculators/pbbassin/fr.json
@@ -0,0 +1,6 @@
+{
+    "fs_basin_params": "Paramètres du bassin",
+
+    "S": "Surface",
+    "ZF": "Cote de fond"
+}
diff --git a/src/app/calculators/pbcloison/en.json b/src/app/calculators/pbcloison/en.json
new file mode 100644
index 000000000..bcd280c3a
--- /dev/null
+++ b/src/app/calculators/pbcloison/en.json
@@ -0,0 +1,6 @@
+{
+    "fs_wall_params": "Wall parameters",
+
+    "select_upstream_basin": "Upstream basin",
+    "select_downstream_basin": "Downstream basin"
+}
diff --git a/src/app/calculators/pbcloison/fr.json b/src/app/calculators/pbcloison/fr.json
new file mode 100644
index 000000000..4480513b3
--- /dev/null
+++ b/src/app/calculators/pbcloison/fr.json
@@ -0,0 +1,6 @@
+{
+    "fs_wall_params": "Paramètres de la cloison",
+
+    "select_upstream_basin": "Bassin amont",
+    "select_downstream_basin": "Bassin aval"
+}
diff --git a/src/app/calculators/prebarrage/config.json b/src/app/calculators/prebarrage/config.json
index 6c72eac2d..547346740 100644
--- a/src/app/calculators/prebarrage/config.json
+++ b/src/app/calculators/prebarrage/config.json
@@ -30,21 +30,8 @@
                 "id": "fs_basin_params",
                 "type": "fieldset",
                 "fields": [
-                    {
-                        "id": "select_upstream",
-                        "type": "select_reference",
-                        "reference": "nub",
-                        "source": "upstream_stuff"
-                    },
-                    {
-                        "id": "select_downstream",
-                        "type": "select_reference",
-                        "reference": "nub",
-                        "source": "downstream_stuff"
-                    },
-                    "Q",
-                    "Z1",
-                    "Z2"
+                    "S",
+                    "ZF"
                 ]
             },
             {
@@ -101,28 +88,30 @@
                 "type": "fieldset",
                 "fields": [
                     {
-                        "id": "select_upstream",
+                        "id": "select_upstream_basin",
                         "type": "select_reference",
                         "reference": "nub",
-                        "source": "upstream_stuff"
+                        "source": "upstream_basin"
                     },
                     {
-                        "id": "select_downstream",
+                        "id": "select_downstream_basin",
                         "type": "select_reference",
                         "reference": "nub",
-                        "source": "downstream_stuff"
-                    },
-                    {
-                        "id": "devices_container",
-                        "type": "template_container",
-                        "templates": [
-                            "fs_wall_device"
-                        ]
+                        "source": "downstream_basin"
                     }
                 ]
             },
+            {
+                "id": "struct_container",
+                "type": "template_container",
+                "templates": [
+                    "fs_wall_device"
+                ]
+            },
             {
                 "type": "options",
+                "upstreamBasinSelectId": "select_upstream_basin",
+                "downstreamBasinSelectId": "select_downstream_basin",
                 "selectIds": [ ]
             }
         ]
diff --git a/src/app/calculators/prebarrage/fr.json b/src/app/calculators/prebarrage/fr.json
index b770ea7b8..287b79352 100644
--- a/src/app/calculators/prebarrage/fr.json
+++ b/src/app/calculators/prebarrage/fr.json
@@ -1,10 +1,7 @@
 {
-    "fs_params": "Édition du bassin / de la cloison",
+    "fs_river_params": "Paramètres de la rivière",
 
-    "Ytarget": "Valeur du paramètre cible",
-    "Xinit": "Valeur initiale du paramètre recherché",
-    "X": "Valeur du paramètre recherché",
-
-    "select_upstream": "Bassin amont",
-    "select_downstream": "Bassin aval"
-}
\ No newline at end of file
+    "Q": "Débit",
+    "Z1": "Cote de l'eau amont",
+    "Z2": "Cote de l'eau aval"
+}
diff --git a/src/app/components/dialog-new-pb-cloison/dialog-new-pb-cloison.component.ts b/src/app/components/dialog-new-pb-cloison/dialog-new-pb-cloison.component.ts
index d562f03d3..7798238d6 100644
--- a/src/app/components/dialog-new-pb-cloison/dialog-new-pb-cloison.component.ts
+++ b/src/app/components/dialog-new-pb-cloison/dialog-new-pb-cloison.component.ts
@@ -57,13 +57,11 @@ export class DialogNewPbCloisonComponent implements OnInit {
      * @param downstream if true, inverts the test
      */
     public basinIsSelectable(index: number, downstream: boolean = false): boolean {
-        let ok = true;
         if (downstream) {
             return (this.upstreamIndex === 0 || index > this.upstreamIndex)
         } else {
             return (this.downstreamIndex === 0 || index < this.downstreamIndex)
         }
-        return ok;
     }
 
     public basinDescription(i: number, fallback: string): string {
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index a06206020..9c26baa5d 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -49,6 +49,7 @@ import { NgParameter } from "../../formulaire/elements/ngparam";
 import { FormulaireFixedVar } from "../../formulaire/definition/form-fixedvar";
 import { PbSchema } from "../../formulaire/elements/pb-schema";
 import { PbSchemaComponent } from "../pb-schema/pb-schema.component";
+import { FormulairePrebarrage } from "../../formulaire/definition/form-prebarrage";
 
 import { HotkeysService, Hotkey } from "angular2-hotkeys";
 
@@ -373,6 +374,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
         this._formulaire.resetResults([]);
         this.appComponent.showProgressBar = true;
         this._computeClicked = true;
+        this.showPBInputData = false;
         // send resetForm to clear log
         this._formulaire.notifyObservers({
             "action": "resetForm",
@@ -588,9 +590,9 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
 
     /** réception d'un événement de clic sur un nœud du schéma de PréBarrage */
     public onPBNodeSelected(event: any) {
-        console.log("node selected", event.node ? event.node.constructor.name : "zubi");
-        this.showPBInputData = true;
-        // show proper form depending on what was clicked
+        this.showPBInputData = true; // @TODO pas forcément ! (résultats de l'élément cliqué)
+        // show proper form (actually subform elements) depending on what was clicked
+        (this._formulaire as FormulairePrebarrage).nodeSelected(event.node);
     }
 
     public openHelp() {
diff --git a/src/app/components/pb-schema/pb-schema.component.ts b/src/app/components/pb-schema/pb-schema.component.ts
index 6728c7829..7249b6929 100644
--- a/src/app/components/pb-schema/pb-schema.component.ts
+++ b/src/app/components/pb-schema/pb-schema.component.ts
@@ -1,18 +1,16 @@
-import { Component, Input, Output, EventEmitter, OnInit, AfterViewInit, ViewChild, Inject, forwardRef } from "@angular/core";
-import { MatDialog } from '@angular/material/dialog';
+import { Component, Input, Output, EventEmitter, OnInit, AfterViewInit, ViewChild, Inject, forwardRef, AfterContentInit } from "@angular/core";
+import { MatDialog } from "@angular/material/dialog";
 
 import {
-    PreBarrage, PbBassin, PbBassinParams, PbCloison
+    PreBarrage, PbBassin, PbBassinParams, PbCloison, CreateStructure, LoiDebit, Structure, Observer, IObservable
  } from "jalhyd";
 
 import * as mermaid from "mermaid";
 
 import { I18nService } from "../../services/internationalisation.service";
-import { ApplicationSetupService } from "../../services/app-setup.service";
-import { NotificationsService } from "../../services/notifications.service";
 import { PbSchema } from "../../formulaire/elements/pb-schema";
 import { DialogNewPbCloisonComponent } from "../dialog-new-pb-cloison/dialog-new-pb-cloison.component";
-import { GenericCalculatorComponent } from '../generic-calculator/calculator.component';
+import { GenericCalculatorComponent } from "../generic-calculator/calculator.component";
 
 /**
  * The interactive schema for calculator type "PreBarrage" (component)
@@ -24,7 +22,7 @@ import { GenericCalculatorComponent } from '../generic-calculator/calculator.com
         "./pb-schema.component.scss"
     ]
 })
-export class PbSchemaComponent implements AfterViewInit, OnInit {
+export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnInit, Observer {
 
     @Input()
     private pbSchema: PbSchema;
@@ -65,9 +63,7 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
     public constructor(
         @Inject(forwardRef(() => GenericCalculatorComponent)) private calculatorComponent: GenericCalculatorComponent,
         private i18nService: I18nService,
-        private newPbCloisonDialog: MatDialog,
-        private appSetupService: ApplicationSetupService,
-        private notifService: NotificationsService
+        private newPbCloisonDialog: MatDialog
     ) { }
 
     public get selectedItem(): any {
@@ -110,6 +106,8 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
     public ngAfterViewInit(): void {
         this.refreshEventListeners();
         this.updateValidity();
+        // subscribe to "refresh" event passed indirectly by FormulairePbCloison (change upstream/downstream basin)
+        this.pbSchema.addObserver(this);
     }
 
     /** Add click listener on every node and link in the graph */
@@ -122,6 +120,19 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
         });
     }
 
+    // debug
+    private createStructure(l: LoiDebit, v: number[]): Structure {
+        const s: Structure = CreateStructure(l);
+        s.prms.Q.singleValue = v[0];
+        s.prms.ZDV.singleValue = v[1];
+        s.prms.Z1.singleValue = v[2];
+        s.prms.Z2.singleValue = v[3];
+        s.getParameter("L").singleValue = v[4];
+        s.getParameter("CdGR").singleValue = v[5];
+        s.prms.W.singleValue = v[6];
+        return s;
+    }
+
     /**
      * Builds a Mermaid graph text definition, using Nodes
      * to represent basins as well as walls; sorts connexions
@@ -161,17 +172,89 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
             this.model.addChild(new PbBassin(new PbBassinParams(32.10, 94.25)));
             this.model.addChild(new PbBassin(new PbBassinParams(35.00, 94.10)));
             this.model.addChild(new PbCloison(undefined, this.model.children[0] as PbBassin));
+            // Session.getInstance().createNub(p, this.currentNub as ParallelStructure) as Structure
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 95.30, 0, 0, 0.4, 1.04 ])
+            );
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 96.25, 0, 0, 4.40, 1.04 ])
+            );
+            //  Wall between upstream and basin 2
             this.model.addChild(new PbCloison(undefined, this.model.children[1] as PbBassin));
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 96.00, 0, 0, 1.00, 1.04 ])
+            );
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 96.25, 0, 0, 5.00, 0.91 ])
+            );
+            // Wall between upstream and basin 5
             this.model.addChild(new PbCloison(undefined, this.model.children[4] as PbBassin));
+             this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 96.25, 0, 0, 3.50, 0.99 ])
+            );
+            // Wall between upstream and basin 6
             this.model.addChild(new PbCloison(undefined, this.model.children[5] as PbBassin));
+             this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 96.25, 0, 0, 3.60, 0.99 ])
+            );
+            // Wall between basin 1 & 3
             this.model.addChild(new PbCloison(this.model.children[0] as PbBassin, this.model.children[2] as PbBassin));
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 95.00, 0, 0, 0.40, 1.04 ])
+            );
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 96.25, 0, 0, 5.20, 0.99 ])
+            );
+            // Wall between basin 2 & 3
             this.model.addChild(new PbCloison(this.model.children[1] as PbBassin, this.model.children[2] as PbBassin));
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 95.85, 0, 0, 4.38, 0.91 ])
+            );
+            // Wall between basin 2 & 4
             this.model.addChild(new PbCloison(this.model.children[1] as PbBassin, this.model.children[3] as PbBassin));
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 95.85, 0, 0, 3.00, 0.99 ])
+            );
+            // Wall between basin 2 & 5
             this.model.addChild(new PbCloison(this.model.children[1] as PbBassin, this.model.children[4] as PbBassin));
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 95.50, 0, 0, 1.00, 1.04 ])
+            );
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 95.75, 0, 0, 3.00, 0.99 ])
+            );
+            // Wall between basin 3 & 4
             this.model.addChild(new PbCloison(this.model.children[2] as PbBassin, this.model.children[3] as PbBassin));
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 94.70, 0, 0, 0.40, 1.04 ])
+            );
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 95.65, 0, 0, 5.74, 0.99 ])
+            );
+            // Wall between basin 4 & 5
             this.model.addChild(new PbCloison(this.model.children[3] as PbBassin, this.model.children[4] as PbBassin));
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 94.40, 0, 0, 0.40, 1.04 ])
+            );
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 95.35, 0, 0, 6.00, 0.99 ])
+            );
+            // Wall between basin 5 & 6
             this.model.addChild(new PbCloison(this.model.children[4] as PbBassin, this.model.children[5] as PbBassin));
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 94.25, 0, 0, 0.70, 1.04 ])
+            );
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 95.05, 0, 0, 9.50, 0.99 ])
+            );
+            // Wall between basin 6 & downstream
             this.model.addChild(new PbCloison(this.model.children[5] as PbBassin, undefined));
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 94.10, 0, 0, 0.95, 1.04 ])
+            );
+            this.model.children[this.model.children.length - 1].addChild(
+                this.createStructure(LoiDebit.WeirCunge80, [ 0, 94.75, 0, 0, 10.20, 0.99 ])
+            );
         }
 
         const sortedWalls: PbCloison[] = [];
@@ -239,7 +322,7 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
         const sommeA = a.bassinAmont.findPositionInParent() + a.bassinAval.findPositionInParent();
         const sommeB = b.bassinAmont.findPositionInParent() + b.bassinAval.findPositionInParent();
         return (sommeA <= sommeB ? -1 : 1);
-    };
+    }
 
     private selectNode(item: any) {
         // highlight clicked element
@@ -249,13 +332,9 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
         if ([ this.upstreamId, this.downstreamId ].includes(item.id)) {
             this._selectedItem = undefined;
         } else {
-            for (const b of this.model.children) {
-                if (b.uid === item.id) {
-                    this._selectedItem = b;
-                }
-            }
+            this._selectedItem = this.model.findChild(item.id);
         }
-        // show proper form, hide results
+        // show proper form and hide results
         this.nodeSelected.emit({
             node: this._selectedItem
         });
@@ -315,7 +394,9 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
     private findBasinPosition(basin: PbBassin): number {
         let i = 0;
         for (const b of this.model.bassins) {
-            if (b === basin) break;
+            if (b === basin) {
+                break;
+            }
             i++;
         }
         return i;
@@ -347,10 +428,10 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
 
     /** Copies a wall */
     public onCopyClick() {
-        const wall = this._selectedItem as PbCloison
+        const wall = this._selectedItem as PbCloison;
         const wallCopy = new PbCloison(wall.bassinAmont, wall.bassinAval);
         this.model.addChild(wallCopy);
-        this.unselect();
+        this.unselect(); // @TODO select new wall ?
         this.refresh();
     }
 
@@ -361,7 +442,7 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
     /** Adds a new lone basin */
     public onAddBasinClick() {
         this.model.addChild(new PbBassin(new PbBassinParams(20, 99)));
-        this.unselect();
+        this.unselect(); // @TODO select new basin ?
         this.refresh();
     }
 
@@ -424,6 +505,18 @@ export class PbSchemaComponent implements AfterViewInit, OnInit {
     private unselect() {
         this._selectedItem = undefined;
         this.clearHighlightedItems();
+        this.nodeSelected.emit({}); // nothing selected
+    }
+
+    // interface Observer
+
+    public update(sender: IObservable, data: any) {
+        if (sender instanceof PbSchema) {
+            if (data.action === "refresh") {
+                this.unselect();
+                this.refresh();
+            }
+        }
     }
 
 }
diff --git a/src/app/formulaire/definition/form-definition.ts b/src/app/formulaire/definition/form-definition.ts
index a4a0732ea..c481e906e 100644
--- a/src/app/formulaire/definition/form-definition.ts
+++ b/src/app/formulaire/definition/form-definition.ts
@@ -269,6 +269,7 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
                     break;
 
                 case "template_container":
+                    console.log("parsing template container !", conf);
                     this.parse_template_container(conf, templates);
                     break;
 
diff --git a/src/app/formulaire/definition/form-parallel-structures.ts b/src/app/formulaire/definition/form-parallel-structures.ts
index eaffb9f33..f9e2a99e1 100644
--- a/src/app/formulaire/definition/form-parallel-structures.ts
+++ b/src/app/formulaire/definition/form-parallel-structures.ts
@@ -98,7 +98,7 @@ export class FormulaireParallelStructure extends FormulaireRepeatableFieldset {
     }
 
     protected get fieldsetContainer(): FieldsetContainer {
-        const n = this.getFormulaireNodeById("struct_container");
+        const n = this.getFormulaireNodeById("struct_container"); // @TODO make it generic, do not force ID !
         if (n === undefined || !(n instanceof FieldsetContainer)) {
             throw new Error("l'élément 'struct_container' n'est pas du type FieldsetContainer");
         }
diff --git a/src/app/formulaire/definition/form-pb-cloison.ts b/src/app/formulaire/definition/form-pb-cloison.ts
new file mode 100644
index 000000000..5ea0bc823
--- /dev/null
+++ b/src/app/formulaire/definition/form-pb-cloison.ts
@@ -0,0 +1,74 @@
+import { FormulaireParallelStructure } from "./form-parallel-structures";
+import { SelectFieldNub } from "../elements/select-field-nub";
+
+import { IObservable, Nub, PbCloison, PbBassin } from "jalhyd";
+
+export class FormulairePbCloison extends FormulaireParallelStructure {
+
+    /** id of select configuring upstream basin Nub */
+    private _upstreamBasinSelectId: string;
+
+    /** id of select configuring downstream basin Nub */
+    private _downstreamBasinSelectId: string;
+
+    protected parseOptions(json: {}) {
+        super.parseOptions(json);
+        this._upstreamBasinSelectId = this.getOption(json, "upstreamBasinSelectId");
+        this._downstreamBasinSelectId = this.getOption(json, "downstreamBasinSelectId");
+    }
+
+    protected completeParse(json: {}, firstNotif: boolean = true) {
+        super.completeParse(json);
+        if (this._upstreamBasinSelectId) {
+            const sel = this.getFormulaireNodeById(this._upstreamBasinSelectId);
+            if (sel) {
+                sel.addObserver(this);
+                if (firstNotif) {
+                    // force 1st observation
+                    (sel as SelectFieldNub).notifySelectValueChanged();
+                }
+            }
+        }
+        if (this._downstreamBasinSelectId) {
+            const sel = this.getFormulaireNodeById(this._downstreamBasinSelectId);
+            if (sel) {
+                sel.addObserver(this);
+                if (firstNotif) {
+                    // force 1st observation
+                    (sel as SelectFieldNub).notifySelectValueChanged();
+                }
+            }
+        }
+    }
+
+    // interface Observer
+
+    public update(sender: IObservable, data: any) {
+        // copied from FormDefinition, to avoid calling super.update()
+        if (sender instanceof Nub) {
+            switch (data.action) {
+                case "resultUpdated":
+                    // forward Nub results update notification to FormCompute objects
+                    this.reaffectResultComponents();
+                    break;
+            }
+        }
+        // copied from FormFixedVar, to avoid calling super.update()
+        if (data.action === "propertyChange") {
+            this.reset();
+        }
+
+        if (sender instanceof SelectFieldNub) {
+            const nub = this._currentNub as PbCloison;
+            const pb = nub.parent;
+            // empty "" data.value.value should return undefined, which is good for amont/aval
+            const newBasin = pb.findChild(data.value.value) as PbBassin;
+            if (sender.id === this._upstreamBasinSelectId) {
+                nub.bassinAmont = newBasin;
+            } else if (sender.id === this._downstreamBasinSelectId) {
+                nub.bassinAval = newBasin;
+            }
+            this.notifyObservers({ action: "updateBasin" }, this);
+        }
+    }
+}
diff --git a/src/app/formulaire/definition/form-prebarrage.ts b/src/app/formulaire/definition/form-prebarrage.ts
index d15c13b37..95d4ee703 100644
--- a/src/app/formulaire/definition/form-prebarrage.ts
+++ b/src/app/formulaire/definition/form-prebarrage.ts
@@ -1,8 +1,10 @@
-import { CalculatorType } from "jalhyd";
+import { CalculatorType, PbBassin, PbCloison, IObservable } from "jalhyd";
 
 import { FormulaireFixedVar } from "./form-fixedvar";
-import { FormulaireParallelStructure } from "./form-parallel-structures";
 import { PbSchema } from "../elements/pb-schema";
+import { FormulaireDefinition } from "./form-definition";
+import { ServiceFactory } from "../../services/service-factory";
+import { FormulairePbCloison } from "./form-pb-cloison";
 
 /**
  * Formulaire pour les PréBarrage
@@ -15,27 +17,16 @@ export class FormulairePrebarrage extends FormulaireFixedVar {
     /** child form for basins dimensions */
     private basinForm: FormulaireFixedVar;
 
+    /** configuration for re-creating basin form every time needed */
+    private basinFormConfig: string;
+
     /** child form for walls (repeatable devices) */
-    private wallForm: FormulaireParallelStructure;
-
-    public constructor() {
-        super();
-        console.log("Construction du FormPreBarrage !!");
-        this.riverForm = new FormulaireFixedVar();
-        this.riverForm.defaultProperties["calcType"] = CalculatorType.PreBarrage;
-        this.basinForm = new FormulaireFixedVar();
-        this.basinForm.defaultProperties["calcType"] = CalculatorType.PbBassin;
-        this.wallForm = new FormulaireParallelStructure();
-        this.wallForm.defaultProperties["calcType"] = CalculatorType.PbCloison;
-    }
+    private wallForm: FormulairePbCloison;
 
-    protected parseOptions(json: {}) {
-        // super.parseOptions(json);
-        // @TODO parse children forms configs
-    }
+    /** configuration for re-creating wall form every time needed */
+    private wallFormConfig: string;
 
     public parseConfig(json?: {}) {
-        console.log("> parse confaïgue");
         if (json !== undefined) {
             this._jsonConfig = json;
         }
@@ -64,25 +55,28 @@ export class FormulairePrebarrage extends FormulaireFixedVar {
     }
 
     private parse_subform(json: {}) {
-        console.log("parse sf", json);
         switch (json["id"]) {
             case "subform_river":
+                // parse it and build it once then keep it (it always has the same Nub: PreBarrage)
+                this.riverForm = new FormulaireFixedVar();
+                this.riverForm.defaultProperties["calcType"] = CalculatorType.PreBarrage;
                 this.riverForm.currentNub = this.currentNub;
+                this.riverForm.preparseConfig(json["config"]);
                 this.riverForm.parseConfig(json["config"]);
+                this.kids.push(this.riverForm);
+                // show default form
+                this.showFormElements(this.riverForm);
                 break;
             case "subform_basin":
-                // this.basinForm.currentNub = this.currentNub;
-                this.basinForm.parseConfig(json["config"]);
+                // only store config to create it multiple times on demand
+                this.basinFormConfig = json["config"];
                 break;
             case "subform_wall":
-                // this.wallForm.currentNub = this.currentNub;
-                this.wallForm.parseConfig(json["config"]);
+                // same as above
+                console.log("===> storing wall form config", json["config"]);
+                this.wallFormConfig = json["config"];
                 break;
         }
-        // console.log(">> triggering parsing of new subform", subform.constructor.name, subform.uid);
-        // subform.parseConfig(json);
-        // this.kids.push(subform);
-        // f.defaultProperties["calcType"] = ct;
     }
 
     private parse_pb_schema(json: {}) {
@@ -91,26 +85,59 @@ export class FormulairePrebarrage extends FormulaireFixedVar {
         this.kids.push(sch);
     }
 
-    protected completeParse(json: {}, firstNotif: boolean = true) {
-        // super.completeParse(json);
-        // @TODO parse children forms configs
+    public nodeSelected(node: PbBassin | PbCloison) {
+        // show only the relevant form
+        if (node === undefined) {
+            this.showFormElements(this.riverForm);
+
+        } else if (node instanceof PbBassin) {
+            this.basinForm = new FormulaireFixedVar();
+            this.basinForm.defaultProperties["calcType"] = CalculatorType.PbBassin;
+            this.basinForm.currentNub = node;
+            this.basinForm.preparseConfig(this.basinFormConfig);
+            this.basinForm.parseConfig(this.basinFormConfig);
+            ServiceFactory.instance.formulaireService.loadUpdateFormulaireLocalisation(this.basinForm);
+            this.showFormElements(this.basinForm);
+
+        } else if (node instanceof PbCloison) {
+            this.wallForm = new FormulairePbCloison();
+            this.wallForm.defaultProperties["calcType"] = CalculatorType.PbCloison;
+            this.wallForm.currentNub = node;
+            this.wallForm.preparseConfig(this.wallFormConfig);
+            this.wallForm.parseConfig(this.wallFormConfig);
+            this.wallForm.addObserver(this); // subscribe to upstream/downstream basin change
+            ServiceFactory.instance.formulaireService.loadUpdateFormulaireLocalisation(this.wallForm);
+            this.showFormElements(this.wallForm);
+        }
+    }
+
+    /**
+     * Adds all elements of given Formulaire f to the current form, right
+     * after the PbSchema, replacing any other element already present
+     * @param f Formulaire to display
+     */
+    private showFormElements(f: FormulaireDefinition) {
+        // clear all kids except PbSchema
+        this._kids = [ this.kids[0] ];
+        for (const e of f.kids) {
+            this.kids.push(e);
+        }
+    }
+
+    private refreshSchema() {
+        const pbs = this.kids[0] as PbSchema;
+        pbs.refresh();
     }
 
     // interface Observer
 
-    /* public update(sender: IObservable, data: any) {
-        // copied from FormDefinition, to avoid calling super.update()
-        if (sender instanceof Nub) {
-            switch (data.action) {
-                case "resultUpdated":
-                    // forward Nub results update notification to FormCompute objects
-                    this.reaffectResultComponents();
-                    break;
+    public update(sender: IObservable, data: any) {
+        super.update(sender, data);
+        if (sender instanceof FormulairePbCloison) {
+            // console.log("HOH PUTAIN CE BIG QUATTRO HOYOYOYOYOY", this.kids[0].constructor.name);
+            if (data.action === "updateBasin") {
+                this.refreshSchema();
             }
         }
-        // copied from FormFixedVar, to avoid calling super.update()
-        if (data.action === "propertyChange") {
-            this.reset();
-        }
-    } */
+    }
 }
diff --git a/src/app/formulaire/elements/formulaire-node.ts b/src/app/formulaire/elements/formulaire-node.ts
index 9a2e5e3c4..74eb6567d 100644
--- a/src/app/formulaire/elements/formulaire-node.ts
+++ b/src/app/formulaire/elements/formulaire-node.ts
@@ -22,7 +22,7 @@ export abstract class FormulaireNode implements IObservable {
     private _parentNode: FormulaireNode;
 
     /** enfants (utilisé entre autres pour FormulaireDefinition, FieldSet et FieldsetContainer) */
-    private _kids: FormulaireNode[];
+    protected _kids: FormulaireNode[];
 
     /** implémentation par délégation de IObservable */
     private _observable: Observable;
diff --git a/src/app/formulaire/elements/pb-schema.ts b/src/app/formulaire/elements/pb-schema.ts
index 5be43f62f..94a7d452a 100644
--- a/src/app/formulaire/elements/pb-schema.ts
+++ b/src/app/formulaire/elements/pb-schema.ts
@@ -22,4 +22,9 @@ export class PbSchema extends FormulaireElement {
             return this.parentForm.currentNub as PreBarrage;
         }
     }
+
+    /** Asks PbSchemaComponent to redraw the schema */
+    public refresh() {
+        this.notifyObservers({ action: "refresh" }, this);
+    }
 }
-- 
GitLab