From 806510e3cae9cfd5f47c6fe4e5782d7cdffd71fe Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Tue, 30 Jun 2020 17:13:11 +0200
Subject: [PATCH] [WIP] PreBarrage results

---
 src/app/app.module.ts                         |   4 +
 src/app/calculators/prebarrage/fr.json        |  15 +-
 .../calculator-results.component.html         |   1 +
 .../calculator-results.component.ts           |  44 ++-
 .../fixed-results.component.html              |   1 -
 .../fixed-results.component.ts                |   1 +
 .../calculator.component.ts                   |   6 +-
 .../pb-results-table.component.html           |  30 ++
 .../pb-results-table.component.scss           |  52 +++
 .../pb-results/pb-results-table.component.ts  | 143 ++++++++
 .../pb-results/pb-results.component.html      |  23 ++
 .../pb-results/pb-results.component.scss      |   4 +
 .../pb-results/pb-results.component.ts        | 324 ++++++++++++++++++
 .../formulaire/definition/form-prebarrage.ts  |  95 ++++-
 src/app/results/prebarrage-results.ts         | 111 ++++++
 src/locale/messages.en.json                   |   1 +
 src/locale/messages.fr.json                   |   1 +
 17 files changed, 836 insertions(+), 20 deletions(-)
 create mode 100644 src/app/components/pb-results/pb-results-table.component.html
 create mode 100644 src/app/components/pb-results/pb-results-table.component.scss
 create mode 100644 src/app/components/pb-results/pb-results-table.component.ts
 create mode 100644 src/app/components/pb-results/pb-results.component.html
 create mode 100644 src/app/components/pb-results/pb-results.component.scss
 create mode 100644 src/app/components/pb-results/pb-results.component.ts
 create mode 100644 src/app/results/prebarrage-results.ts

diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index a6f375b49..d9b61bed6 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -74,6 +74,8 @@ import { ResultsChartComponent } from "./components/results-chart/results-chart.
 import { PabResultsComponent } from "./components/pab-results/pab-results.component";
 import { VerificateurResultsComponent } from "./components/verificateur-results/verificateur-results.component";
 import { PabResultsTableComponent } from "./components/pab-results/pab-results-table.component";
+import { PbResultsComponent } from "./components/pb-results/pb-results.component";
+import { PbResultsTableComponent } from "./components/pb-results/pb-results-table.component";
 import { ChartTypeSelectComponent } from "./components/results-chart/chart-type.component";
 import { CalculatorListComponent } from "./components/calculator-list/calculator-list.component";
 import { ApplicationSetupComponent } from "./components/app-setup/app-setup.component";
@@ -224,6 +226,8 @@ const appRoutes: Routes = [
     PabResultsComponent,
     PabResultsTableComponent,
     PabTableComponent,
+    PbResultsComponent,
+    PbResultsTableComponent,
     PbSchemaComponent,
     VariableResultsSelectorComponent,
     MacrorugoCompoundResultsComponent,
diff --git a/src/app/calculators/prebarrage/fr.json b/src/app/calculators/prebarrage/fr.json
index 287b79352..7b3f06d22 100644
--- a/src/app/calculators/prebarrage/fr.json
+++ b/src/app/calculators/prebarrage/fr.json
@@ -3,5 +3,18 @@
 
     "Q": "Débit",
     "Z1": "Cote de l'eau amont",
-    "Z2": "Cote de l'eau aval"
+    "Z2": "Cote de l'eau aval",
+
+    "Z": "Cote de l'eau",
+    "S": "Surface",
+    "ZF": "Cote de fond",
+    "PV": "Puissance dissipée",
+    "YMOY": "Profondeur moyenne",
+
+    "UNIT_Q": "m³/s",
+    "UNIT_S": "m²",
+    "UNIT_Z": "m",
+    "UNIT_ZF": "m",
+    "UNIT_YMOY": "m",
+    "UNIT_PV": "W/m³"
 }
diff --git a/src/app/components/calculator-results/calculator-results.component.html b/src/app/components/calculator-results/calculator-results.component.html
index 319b3eab3..4befd3308 100644
--- a/src/app/components/calculator-results/calculator-results.component.html
+++ b/src/app/components/calculator-results/calculator-results.component.html
@@ -2,6 +2,7 @@
     <section-results [hidden]="! isSP"></section-results>
     <remous-results [hidden]="! isRemous"></remous-results>
     <pab-results [hidden]="! isPAB"></pab-results>
+    <pb-results [hidden]="! isPB"></pb-results>
     <verificateur-results [hidden]="! isVerificateur"></verificateur-results>
     <macrorugo-compound-results [hidden]="! isMRC"></macrorugo-compound-results>
     <jet-results [hidden]="! isJet"></jet-results>
diff --git a/src/app/components/calculator-results/calculator-results.component.ts b/src/app/components/calculator-results/calculator-results.component.ts
index 900cf877a..c8c6b712f 100644
--- a/src/app/components/calculator-results/calculator-results.component.ts
+++ b/src/app/components/calculator-results/calculator-results.component.ts
@@ -11,6 +11,7 @@ import { GenericCalculatorComponent } from "../generic-calculator/calculator.com
 import { VerificateurResultsComponent } from "../verificateur-results/verificateur-results.component";
 
 import { CalculatorType } from "jalhyd";
+import { PbResultsComponent } from "../pb-results/pb-results.component";
 
 @Component({
     selector: "calc-results",
@@ -50,6 +51,12 @@ export class CalculatorResultsComponent implements AfterViewChecked {
     @ViewChild(VerificateurResultsComponent, { static: true })
     private verificateurResultsComponent: VerificateurResultsComponent;
 
+    /*
+     * composant d'affichage des résultats des prébarrages
+     */
+    @ViewChild(PbResultsComponent, { static: true })
+    private pbResultsComponent: PbResultsComponent;
+
     /**
      * composant d'affichage des résultats des passes à macrorugosités complexes
      */
@@ -77,31 +84,38 @@ export class CalculatorResultsComponent implements AfterViewChecked {
         this._formulaire = f;
         if (this._formulaire === undefined) {
             this.fixedVarResultsComponent.results = undefined;
+            this.sectionResultsComponent.results = undefined;
+            this.pbResultsComponent.results = undefined;
             this.jetResultsComponent.results = undefined;
             this.mrcResultsComponent.results = undefined;
             this.pabResultsComponent.results = undefined;
             this.remousResultsComponent.results = undefined;
-            this.sectionResultsComponent.results = undefined;
             this.verificateurResultsComponent.results = undefined;
         } else {
-            if (this.showGenericResults) { this.fixedVarResultsComponent.results = f.results; }
-            if (this.isJet) { this.jetResultsComponent.results = f.results; }
-            if (this.isMRC) { this.mrcResultsComponent.results = f.results; }
-            if (this.isPAB) { this.pabResultsComponent.results = f.results; }
-            if (this.isRemous) { this.remousResultsComponent.results = f.results; }
-            if (this.isSP) { this.sectionResultsComponent.results = f.results; }
-            if (this.isVerificateur) { this.verificateurResultsComponent.results = f.results; }
+            this.sectionResultsComponent.results = f.results;
+            this.remousResultsComponent.results = f.results;
+            this.pabResultsComponent.results = f.results;
+            this.pbResultsComponent.results = f.results;
+            this.mrcResultsComponent.results = f.results;
+            // FixedVar and Jet are mutually incompatible (the 2nd extend the 1st)
+            if (this.isJet) {
+                this.jetResultsComponent.results = f.results;
+                this.fixedVarResultsComponent.results = undefined;
+            } else {
+                this.fixedVarResultsComponent.results = f.results;
+                this.jetResultsComponent.results = undefined;
+            }
         }
     }
 
     public updateView() {
-        if (this.showGenericResults) { this.fixedVarResultsComponent.updateView(); }
-        if (this.isJet) { this.jetResultsComponent.updateView(); }
-        if (this.isMRC) { this.mrcResultsComponent.updateView(); }
-        if (this.isPAB) { this.pabResultsComponent.updateView(); }
-        if (this.isRemous) { this.remousResultsComponent.updateView(); }
-        if (this.isSP) { this.sectionResultsComponent.updateView(); }
-        if (this.isVerificateur) { this.verificateurResultsComponent.updateView(); }
+        this.fixedVarResultsComponent.updateView();
+        this.sectionResultsComponent.updateView();
+        this.remousResultsComponent.updateView();
+        this.pabResultsComponent.updateView();
+        this.pbResultsComponent.updateView();
+        this.mrcResultsComponent.updateView();
+        this.jetResultsComponent.updateView();
     }
 
     public ngAfterViewChecked() {
diff --git a/src/app/components/fixedvar-results/fixed-results.component.html b/src/app/components/fixedvar-results/fixed-results.component.html
index d9bca9625..849bf9d4e 100644
--- a/src/app/components/fixedvar-results/fixed-results.component.html
+++ b/src/app/components/fixedvar-results/fixed-results.component.html
@@ -5,7 +5,6 @@
             <mat-icon color="primary">file_download</mat-icon>
         </button>
     </div>
-
     <!-- table des résultats fixés -->
     <div class="fixed-results-inner-container" #tableContainer>
         <table mat-table [dataSource]="dataSet" [trackBy]="tbIndex">
diff --git a/src/app/components/fixedvar-results/fixed-results.component.ts b/src/app/components/fixedvar-results/fixed-results.component.ts
index 32660b368..004c46c9c 100644
--- a/src/app/components/fixedvar-results/fixed-results.component.ts
+++ b/src/app/components/fixedvar-results/fixed-results.component.ts
@@ -36,6 +36,7 @@ export class FixedResultsComponent extends ResultsComponentDirective {
     }
 
     public set results(r: FixedResults) {
+        console.log("->->->->-> je suis FRC et j'ai reçu des résultats", r === undefined ? "keud" : r.result);
         this._fixedResults = r;
     }
 
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index 9c26baa5d..6c2119744 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -590,9 +590,11 @@ 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) {
-        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
+        // show proper form (actually subform elements) or proper results,
+        // depending on what was clicked
         (this._formulaire as FormulairePrebarrage).nodeSelected(event.node);
+        // refresh results component
+        this.resultsComponent.updateView();
     }
 
     public openHelp() {
diff --git a/src/app/components/pb-results/pb-results-table.component.html b/src/app/components/pb-results/pb-results-table.component.html
new file mode 100644
index 000000000..75f0451ff
--- /dev/null
+++ b/src/app/components/pb-results/pb-results-table.component.html
@@ -0,0 +1,30 @@
+<div class="pb-results-table-container" #pbResultsTable fxLayout="row wrap" fxLayoutAlign="center center">
+    <div fxFlex="1 1 100%">
+        <div class="pb-results-table-buttons">
+            <button mat-icon-button (click)="exportAsSpreadsheet()" [title]="uitextExportAsSpreadsheet">
+                <mat-icon color="primary">file_download</mat-icon>
+            </button>
+            <button mat-icon-button *ngIf="! isFullscreen" (click)="setFullscreen(pbResultsTable)" [title]="uitextEnterFSTitle">
+                <mat-icon color="primary" class="scaled12">fullscreen</mat-icon>
+            </button>
+            <button mat-icon-button *ngIf="isFullscreen" (click)="exitFullscreen()" [title]="uitextExitFSTitle">
+                <mat-icon color="primary" class="scaled12">fullscreen_exit</mat-icon>
+            </button>
+        </div>
+
+        <div class="pb-results-table-scrollable-container" [ngClass]="{'full-height': isFullscreen}">
+            <!-- scrollable -->
+            <div class="pb-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 [innerHTML]="h"></th>
+                        <td mat-cell *matCellDef="let element" [innerHTML]="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/pb-results/pb-results-table.component.scss b/src/app/components/pb-results/pb-results-table.component.scss
new file mode 100644
index 000000000..9e49c5ca0
--- /dev/null
+++ b/src/app/components/pb-results/pb-results-table.component.scss
@@ -0,0 +1,52 @@
+/** @see additional styles in src/styles.css */
+
+:host {
+    display: block;
+    margin-bottom: 1.5em;
+}
+
+.pb-results-table-container {
+    background-color: white;
+}
+
+.pb-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)
+            }
+        }
+    }
+}
+
+.pb-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;
+        }
+    }
+}
diff --git a/src/app/components/pb-results/pb-results-table.component.ts b/src/app/components/pb-results/pb-results-table.component.ts
new file mode 100644
index 000000000..b1ab9838b
--- /dev/null
+++ b/src/app/components/pb-results/pb-results-table.component.ts
@@ -0,0 +1,143 @@
+import { Component, ViewChild, ElementRef } from "@angular/core";
+
+import { PreBarrage, PbBassin } from "jalhyd";
+
+import { I18nService } from "../../services/internationalisation.service";
+import { ResultsComponentDirective } from "../fixedvar-results/results.component";
+import { AppComponent } from "../../app.component";
+import { fv } from "../../util";
+import { PrebarrageResults } from "../../results/prebarrage-results";
+
+@Component({
+    selector: "pb-results-table",
+    templateUrl: "./pb-results-table.component.html",
+    styleUrls: [
+        "./pb-results-table.component.scss"
+    ]
+})
+export class PbResultsTableComponent extends ResultsComponentDirective {
+
+    /** résultats non mis en forme */
+    private _pbResults: PrebarrageResults;
+
+    /** entêtes des colonnes */
+    private _headers: string[];
+
+    /** résultats mis en forme */
+    private _dataSet: any[];
+
+    @ViewChild("tableContainer")
+    table: ElementRef;
+
+    constructor(
+        protected intlService: I18nService
+    ) {
+        super();
+    }
+
+    /* private getJetTypes(re: Result, vi: number): string {
+        // jet type for each device
+        const devices = re.sourceNub.getChildren();
+        const jetTypes: string[] = devices.map((device) => {
+            const jt = device.result.resultElements[vi].getValue("ENUM_StructureJetType");
+            let jetType = this.intlService.localizeText("INFO_ENUM_STRUCTUREJETTYPE_" + jt);
+            if (devices.length > 1) {
+                // evil HTML injection in table cell (simpler)
+                jetType = this.intlService.localizeText("INFO_LIB_FS_OUVRAGE") + " n°"
+                    + (device.findPositionInParent() + 1) + ": " + jetType;
+            }
+            return jetType;
+        });
+        return `<div class="inner-cell-line">` + jetTypes.join(`, </div><div class="inner-cell-line">`) + `</div>`;
+    } */
+
+    public set results(r: PrebarrageResults) {
+        this._pbResults = r;
+        console.log("Set results dans PBRTC !", r);
+
+        this._dataSet = [];
+        if (
+            this._pbResults
+            && this._pbResults.bassinsResults
+            && this._pbResults.bassinsResults.length > 0
+            && ! this._pbResults.hasOnlyErrors()
+        ) {
+            const pr = this._pbResults;
+            const pb = pr.result.sourceNub as PreBarrage;
+            // when a parameter is variating, index of the variating parameter
+            // values to build the data from
+            const vi = pr.variableIndex;
+
+            // refresh headers here if language changed
+            this._headers = pr.headers;
+
+            // upstream line
+            if (pr.result.resultElements[vi].vCalc) { // le calcul peut avoir échoué
+                this._dataSet.push([
+                    this.intlService.localizeText("INFO_LIB_AMONT"),
+                    "", "",
+                    fv(pb.prms.Z1.V),
+                    "", "",
+                    fv(pb.prms.Q.V)
+                ]);
+            }
+
+            // basins 1 - n
+            for (let i = 0; i < pr.bassinsResults.length; i++) {
+                if (
+                    Object.keys(pr.bassinsResults[i].resultElements[vi].values).length > 0 // no vCalc in this case
+                ) {
+                    const rb = pr.bassinsResults[i].resultElements[vi].values;
+                    const basin = pr.bassinsResults[i].sourceNub as PbBassin;
+                    this._dataSet.push([
+                        i + 1, // n° cloison
+                        fv(basin.prms.S.V),
+                        fv(basin.prms.ZF.V),
+                        fv(rb.Z),
+                        fv(rb.PV),
+                        fv(rb.YMOY),
+                        fv(rb.Q)
+                    ]);
+                }
+            }
+
+            // downstream line
+            if (pr.result.resultElements[vi].vCalc) { // le calcul peut avoir échoué
+                this._dataSet.push([
+                    this.intlService.localizeText("INFO_LIB_AVAL"),
+                    "", "",
+                    fv(pb.prms.Z2.V),
+                    "", "",
+                    fv(pb.prms.Q.V)
+                ]);
+            }
+        }
+    }
+
+    public get headers() {
+        return this._headers;
+    }
+
+    /**
+     * Returns a combination of parameters and results for mat-table
+     */
+    public get dataSet() {
+        return this._dataSet;
+    }
+
+    public exportAsSpreadsheet() {
+        AppComponent.exportAsSpreadsheet(this.table.nativeElement);
+    }
+
+    public get uitextExportAsSpreadsheet() {
+        return this.intlService.localizeText("INFO_RESULTS_EXPORT_AS_SPREADSHEET");
+    }
+
+    public get uitextEnterFSTitle() {
+        return this.intlService.localizeText("INFO_CHART_BUTTON_TITLE_ENTER_FS");
+    }
+
+    public get uitextExitFSTitle() {
+        return this.intlService.localizeText("INFO_CHART_BUTTON_TITLE_EXIT_FS");
+    }
+}
diff --git a/src/app/components/pb-results/pb-results.component.html b/src/app/components/pb-results/pb-results.component.html
new file mode 100644
index 000000000..db8aa6d91
--- /dev/null
+++ b/src/app/components/pb-results/pb-results.component.html
@@ -0,0 +1,23 @@
+<div class="container">
+
+    <log #generalLog [logTitle]="uitextGeneralLogTitle">log général</log>
+
+    <variable-results-selector [results]="pbResults" (indexChange)="variableIndexChanged()">
+    </variable-results-selector>
+
+    <log #iterationLog></log>
+
+    <!-- tableau de résultats des bassins -->
+    <pb-results-table *ngIf="showBasinsResults" [results]="pbResults"></pb-results-table>
+
+    <!-- <quicknav *ngIf="hasDisplayableResults" [items]="[ 'input', 'results', 'charts' ]"
+        [currentItem]="'charts'" [align]="'left'"></quicknav> -->
+
+    <!-- <div id="pb-graphs-container" class="container" fxLayout="row wrap" fxLayoutAlign="space-around start">
+        <pb-profile-chart *ngIf="hasDisplayableResults" fxFlex.gt-xs="1 0 400px" fxFlex.lt-sm="1 0 300px">
+        </pb-profile-chart>
+        <results-chart *ngIf="hasDisplayableResults" fxFlex.gt-xs="1 0 400px" fxFlex.lt-sm="1 0 300px">
+        </results-chart>
+    </div> -->
+
+</div>
diff --git a/src/app/components/pb-results/pb-results.component.scss b/src/app/components/pb-results/pb-results.component.scss
new file mode 100644
index 000000000..446313ba6
--- /dev/null
+++ b/src/app/components/pb-results/pb-results.component.scss
@@ -0,0 +1,4 @@
+results-chart {
+    margin-left: 1em;
+    margin-right: 1em;
+}
diff --git a/src/app/components/pb-results/pb-results.component.ts b/src/app/components/pb-results/pb-results.component.ts
new file mode 100644
index 000000000..5a031da13
--- /dev/null
+++ b/src/app/components/pb-results/pb-results.component.ts
@@ -0,0 +1,324 @@
+import { Component, ViewChild, DoCheck } from "@angular/core";
+
+import { Result, cLog, Message, MessageCode, MessageSeverity } from "jalhyd";
+
+import { LogComponent } from "../log/log.component";
+import { CalculatorResults } from "../../results/calculator-results";
+import { PbResultsTableComponent } from "./pb-results-table.component";
+import { PrebarrageResults } from "../../results/prebarrage-results";
+import { VariableResultsSelectorComponent } from "../variable-results-selector/variable-results-selector.component";
+import { I18nService } from "../../services/internationalisation.service";
+import { PabProfileChartComponent } from "../pab-profile-chart/pab-profile-chart.component";
+import { FixedResults } from "../../results/fixed-results";
+
+@Component({
+    selector: "pb-results",
+    templateUrl: "./pb-results.component.html",
+    styleUrls: [
+        "./pb-results.component.scss"
+    ]
+})
+export class PbResultsComponent implements DoCheck {
+
+    /** résultats des bassins, non mis en forme */
+    private _pbResults: PrebarrageResults;
+
+    /** true si les résultats doiventt être remis à jour */
+    private _doUpdate = false;
+
+    @ViewChild(PbResultsTableComponent)
+    private pbResultsTableComponent: PbResultsTableComponent;
+
+    @ViewChild(VariableResultsSelectorComponent)
+    private variableResultsSelectorComponent: VariableResultsSelectorComponent;
+
+    @ViewChild("generalLog")
+    private generalLogComponent: LogComponent;
+
+    @ViewChild("iterationLog")
+    private iterationLogComponent: LogComponent;
+
+    /* @ViewChild(PabProfileChartComponent)
+    private profileChartComponent: PabProfileChartComponent; */
+
+    constructor(
+        private i18nService: I18nService,
+    ) { }
+
+    public set results(rs: CalculatorResults[]) {
+        this._pbResults = undefined;
+        for (const r of rs) {
+            if (r instanceof PrebarrageResults) {
+                this._pbResults = r as PrebarrageResults;
+            }
+        }
+        this.updateView();
+    }
+
+    public get pbResults() {
+        return this._pbResults;
+    }
+
+    public get hasResults(): boolean {
+        return this._pbResults && this._pbResults.hasResults;
+    }
+
+    public get showBasinsResults(): boolean {
+        let ret = this._pbResults && this._pbResults.hasResults;
+        if (
+            this._pbResults
+            && this._pbResults.variatedParameters
+            && this._pbResults.variatedParameters.length > 0
+        ) {
+            ret = ret
+            && this._pbResults.variableIndex !== undefined
+            && this._pbResults.result.resultElements[this._pbResults.variableIndex] !== undefined
+            && this._pbResults.result.resultElements[this._pbResults.variableIndex].ok;
+        }
+        return ret;
+    }
+
+    /**
+     * update results table and chart when the variable index changed (event sent by
+     * PabVariableResultsSelectorComponent); variable index is already set in
+     * pabResults at this time
+     */
+    public variableIndexChanged() {
+        this.updateView();
+    }
+
+    public updateView() {
+        if (this.iterationLogComponent) {
+            this.iterationLogComponent.log = undefined;
+        }
+        if (this.generalLogComponent) {
+            this.generalLogComponent.log = undefined;
+        }
+        if (this.pbResultsTableComponent) {
+            this.pbResultsTableComponent.results = undefined;
+        }
+        if (this.variableResultsSelectorComponent) {
+            this.variableResultsSelectorComponent.results = undefined;
+        }
+        /* if (this.profileChartComponent) {
+            this.profileChartComponent.results = undefined;
+        } */
+        // set _doUpdate flag so that results are rebuilt on the next Angular display cycle
+        this._doUpdate = false;
+        if (this._pbResults !== undefined) {
+            this._doUpdate = this._doUpdate || this._pbResults.hasResults || this._pbResults.hasLog;
+        }
+    }
+
+    public ngDoCheck() {
+        if (this._doUpdate) {
+            this._doUpdate = !this.updateResults();
+        }
+    }
+
+    /* private mergeGlobalLog(result: Result, log: cLog) {
+        if (result) {
+            if (result.hasGlobalLog()) {
+                log.addLog(result.globalLog);
+            }
+            // if no parameter is varying, 1st element log is considered "global"
+            if (this.pbResults.variatedParameters.length === 0) {
+                if (result.hasResultElements() && result.resultElement.hasLog()) {
+                    log.addLog(result.log);
+                }
+            }
+        }
+    } */
+
+    /**
+     * Returns the number of errors, warnings, infos among children logs
+     */
+    /* private logStats(): any {
+        const ret = {
+            info: 0,
+            warning: 0,
+            error: 0
+        };
+        if (this._pbResults.result && this._pbResults.result.hasLog()) {
+            for (const re of this._pbResults.result.resultElements) {
+                if (re.hasLog()) {
+                    for (const m of re.log.messages) {
+                        const s = m.getSeverity();
+                        switch (s) {
+                            case MessageSeverity.INFO:
+                                ret.info ++;
+                                break;
+                            case MessageSeverity.WARNING:
+                                ret.warning ++;
+                                break;
+                            case MessageSeverity.ERROR:
+                                ret.error ++;
+                                break;
+                        }
+                    }
+                }
+            }
+        }
+        for (const cr of this._pbResults.cloisonsResults) {
+            if (cr && cr.hasLog()) {
+                for (const re of cr.resultElements) {
+                    if (re.hasLog()) {
+                        for (const m of re.log.messages) {
+                            const s = m.getSeverity();
+                            switch (s) {
+                                case MessageSeverity.INFO:
+                                    ret.info ++;
+                                    break;
+                                case MessageSeverity.WARNING:
+                                    ret.warning ++;
+                                    break;
+                                case MessageSeverity.ERROR:
+                                    ret.error ++;
+                                    break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        if (this._pbResults.cloisonAvalResults && this._pbResults.cloisonAvalResults.hasLog()) {
+            for (const re of this._pbResults.cloisonAvalResults.resultElements) {
+                if (re.hasLog()) {
+                    for (const m of re.log.messages) {
+                        const s = m.getSeverity();
+                        switch (s) {
+                            case MessageSeverity.INFO:
+                                ret.info ++;
+                                break;
+                            case MessageSeverity.WARNING:
+                                ret.warning ++;
+                                break;
+                            case MessageSeverity.ERROR:
+                                ret.error ++;
+                                break;
+                        }
+                    }
+                }
+            }
+        }
+        return ret;
+    } */
+
+    /*
+     * Retourne les logs à afficher dans le composant de log global, au dessus
+     * du sélecteur d'itération : messages globaux et / ou résumé des messages
+     * spécifiques à chaque ResultElement
+     */
+    /* private get globalLog(): cLog {
+        const l = new cLog();
+        if (this._pbResults && this.pbResults.variatedParameters.length > 0) {
+            this.mergeGlobalLog(this._pbResults.result, l);
+            // un problème avec la PAB en général / les cloisons, à une étape quelconque ?
+            if (
+                (this.pbResults.hasLog)
+                && l.messages.length === 0 // existing global messages make generic message below useless
+            ) {
+                const logStats = this.logStats();
+                const m = new Message(MessageCode.WARNING_PROBLEMS_ENCOUNTERED);
+                m.extraVar.info = "" + logStats.info; // to avoid displaying fixed number of digits
+                m.extraVar.warning = "" + logStats.warning;
+                m.extraVar.error = "" + logStats.error;
+                l.add(m);
+                // l.add(new Message(MessageCode.WARNING_PROBLEMS_ENCOUNTERED));
+            }
+        } // sinon pas de log global (aucun paramètre ne varie)
+        return l;
+    } */
+
+    /**
+     * Retourne les logs à afficher dans le composant de log global, au dessus
+     * du sélecteur d'itération : messages globaux et / ou résumé des messages
+     * spécifiques à chaque ResultElement
+     */
+    /* private get iterationLog(): cLog {
+        const l = new cLog();
+        if (this._pbResults) {
+            if (this.pbResults.variatedParameters.length > 0) {
+                // A. si un paramètre varie
+                const vi = this._pbResults.variableIndex;
+                // log de la PAB pour l'itération en cours
+                if (
+                    this._pbResults.result
+                    && this._pbResults.result.hasResultElements()
+                    && this._pbResults.result.resultElements[vi]
+                    && this._pbResults.result.resultElements[vi].hasLog()
+                ) {
+                    l.addLog(this._pbResults.result.resultElements[vi].log);
+                }
+                // logs des enfants pour l'itération en cours
+                for (const cr of this._pbResults.cloisonsResults) {
+                    if (cr && cr.hasResultElements() && cr.resultElements[vi].hasLog()) {
+                        l.addLog(cr.resultElements[vi].log);
+                    }
+                }
+                if (this._pbResults.cloisonAvalResults && this._pbResults.cloisonAvalResults.resultElements[vi].hasLog()) {
+                    l.addLog(this._pbResults.cloisonAvalResults.resultElements[vi].log);
+                }
+            } else {
+                // B. si aucun paramètre ne varie
+                this.mergeGlobalLog(this._pbResults.result, l); // faut bien mettre le log global quelque part
+                // logs des enfants
+                for (const cr of this._pbResults.cloisonsResults) {
+                    if (cr && cr.hasResultElements() && cr.resultElement.hasLog()) {
+                        l.addLog(cr.resultElement.log);
+                    }
+                }
+                if (this._pbResults.cloisonAvalResults && this._pbResults.cloisonAvalResults.resultElement.hasLog()) {
+                    l.addLog(this._pbResults.cloisonAvalResults.resultElement.log);
+                }
+            }
+        }
+        return l;
+    } */
+
+    /**
+     * met à jour l'affichage des résultats
+     * @returns true si les résultats ont pu être mis à jour
+     */
+    private updateResults() {
+        console.log("------------ update results -----------");
+        let pabUpdated: boolean;
+        let profileChartUpdated: boolean;
+        let selectorUpdated: boolean;
+
+        // results or not, there might be a log
+        const logUpdated = (this.iterationLogComponent !== undefined || this.generalLogComponent !== undefined); // gne ?
+        if (logUpdated) {
+            // order of logs is important !
+            /* this.iterationLogComponent.log = this.iterationLog;
+            this.generalLogComponent.log = this.globalLog; */
+        }
+
+        if (this.hasResults) {
+            pabUpdated = this.pbResultsTableComponent !== undefined;
+            if (pabUpdated) {
+                this.pbResultsTableComponent.results = this._pbResults;
+            }
+            selectorUpdated = this.variableResultsSelectorComponent !== undefined;
+            if (selectorUpdated) {
+                this.variableResultsSelectorComponent.results = this._pbResults;
+            }
+            /* profileChartUpdated = this.profileChartComponent !== undefined;
+            if (profileChartUpdated) {
+                this.profileChartComponent.results = this._pbResults;
+                this.profileChartComponent.updateView();
+            } */
+        } else {
+            pabUpdated = true;
+            profileChartUpdated = true;
+            selectorUpdated = true;
+        }
+
+        return pabUpdated && logUpdated && profileChartUpdated && selectorUpdated;
+    }
+
+    public get uitextGeneralLogTitle(): string {
+        return this.i18nService.localizeText("INFO_TITREJOURNAL_GLOBAL");
+    }
+
+}
diff --git a/src/app/formulaire/definition/form-prebarrage.ts b/src/app/formulaire/definition/form-prebarrage.ts
index 0199451c3..8cd692893 100644
--- a/src/app/formulaire/definition/form-prebarrage.ts
+++ b/src/app/formulaire/definition/form-prebarrage.ts
@@ -1,4 +1,4 @@
-import { CalculatorType, PbBassin, PbCloison, IObservable } from "jalhyd";
+import { CalculatorType, PbBassin, PbCloison, IObservable, PreBarrage } from "jalhyd";
 
 import { FormulaireFixedVar } from "./form-fixedvar";
 import { PbSchema } from "../elements/pb-schema";
@@ -6,6 +6,9 @@ import { FormulaireDefinition } from "./form-definition";
 import { ServiceFactory } from "../../services/service-factory";
 import { FormulairePbCloison } from "./form-pb-cloison";
 import { FieldsetContainer } from "../elements/fieldset-container";
+import { CalculatorResults } from "../../results/calculator-results";
+import { PrebarrageResults } from "../../results/prebarrage-results";
+import { NgParameter } from "../elements/ngparam";
 
 /**
  * Formulaire pour les PréBarrage
@@ -24,6 +27,34 @@ export class FormulairePrebarrage extends FormulaireFixedVar {
     /** configuration for re-creating wall form every time needed */
     private wallFormConfig: string;
 
+    protected _selectedItem: PbBassin | PbCloison;
+
+    protected _pbResults: PrebarrageResults;
+
+    constructor() {
+        super();
+        this._pbResults = new PrebarrageResults();
+    }
+
+    /* public get pbNub(): PreBarrage {
+        return this.currentNub as PreBarrage;
+    } */
+
+    public get pbResults(): PrebarrageResults {
+        return this._pbResults;
+    }
+
+    public get results(): CalculatorResults[] {
+        // ensure help links are propagated
+        this._pbResults.helpLinks = this.helpLinks;
+        console.log("RENVOI", [ this.fixedResults, this._pbResults ]);
+        return [ this.fixedResults, this._pbResults ];
+    }
+
+    public get hasResults(): boolean {
+        return this._pbResults.hasResults;
+    }
+
     public parseConfig(json?: {}) {
         if (json !== undefined) {
             this._jsonConfig = json;
@@ -82,7 +113,15 @@ export class FormulairePrebarrage extends FormulaireFixedVar {
         this.kids.push(sch);
     }
 
+    /**
+     * Déclenché par CalculatorComponent lorsque le schéma de prébarrage
+     * envoie un événement "nodeSelected" (on a cliqué sur un nœud)
+     */
     public nodeSelected(node: PbBassin | PbCloison) {
+        // store for results formatting
+        this._selectedItem = node;
+        this.reaffectResultComponents();
+
         // show only the relevant form
         if (node === undefined) {
             this.showFormElements(this.riverForm);
@@ -156,4 +195,58 @@ export class FormulairePrebarrage extends FormulaireFixedVar {
             }
         }
     }
+
+    protected compute() {
+        this.runNubCalc(this.currentNub);
+        this.refreshFieldsets(); // important: before reaffectResultComponents() or it will break results components localization
+        // reset variable index to avoid trying to access an index > 0 when nothing varies
+        const pabr = this.pbResults;
+        pabr.variableIndex = 0;
+
+        this.reaffectResultComponents();
+    }
+
+    protected reaffectResultComponents() {
+        const pb: PreBarrage = (this.currentNub as PreBarrage);
+        const computedParam: NgParameter = this.getComputedParameter();
+        const varParams: NgParameter[] = this.getVariatedParameters();
+
+        // pour le sélecteur d'itérations
+        if (varParams) {
+            this.pbResults.variatedParameters = varParams;
+        }
+
+        // résultats selon l'objet sélectionné sur le schéma
+        if (this._selectedItem !== undefined && this._selectedItem instanceof PbCloison) {
+            // cacher les résultats de bassins
+            this.pbResults.reset();
+
+            // afficher les résultats de cloison
+            console.log("RRC => cloison !", this._selectedItem.uid, this._selectedItem.result);
+            this.fixedResults.result = this._selectedItem.result;
+            if (computedParam !== undefined) {
+                this.fixedResults.calculatedParameter = computedParam;
+            }
+        } else {
+            // cacher les résultats de la cloison
+            this.fixedResults.reset();
+            this.addFixedParameters();
+
+            // afficher les résultats des bassins
+            this.pbResults.reset();
+            // résultat général du Nub (amont, aval, débit)
+            this.pbResults.result = pb.result;
+            this.pbResults.calculatedParameter = computedParam;
+            // résultat de chaque bassin
+            for (const b of pb.bassins) {
+                this.pbResults.bassinsResults.push(b.result);
+            }
+        }
+    }
+
+    public resetResults() {
+        super.resetResults();
+        this._fixedResults.reset();
+        this._pbResults.reset();
+    }
 }
diff --git a/src/app/results/prebarrage-results.ts b/src/app/results/prebarrage-results.ts
new file mode 100644
index 000000000..cea988714
--- /dev/null
+++ b/src/app/results/prebarrage-results.ts
@@ -0,0 +1,111 @@
+import { Result } from "jalhyd";
+
+import { ServiceFactory } from "../services/service-factory";
+import { MultiDimensionResults } from "./multidimension-results";
+
+export class PrebarrageResults extends MultiDimensionResults {
+
+    /** résultats des bassins, dans l'ordre */
+    public bassinsResults: Result[];
+
+    /** symboles des colonnes de résultat */
+    protected _columns: string[];
+
+    public constructor() {
+        super();
+        this.reset();
+        // standard headers
+        this._columns = [
+            "BASSIN",
+            "S",
+            "ZF",
+            "Z",
+            "PV",
+            "YMOY",
+            "Q"
+        ];
+    }
+
+    /** headers symbols */
+    public get columns() {
+        return this._columns;
+    }
+
+    /** translated headers texts */
+    public get headers() {
+        return this._columns.map((h) => {
+            // calculator type for translation
+            const sn = this.result.sourceNub;
+            let ct = sn.calcType;
+            if (sn.parent) {
+                ct = sn.parent.calcType;
+            }
+            let label = ServiceFactory.instance.formulaireService.expandVariableNameAndUnit(ct , h);
+            label += this.getHelpLink(h);
+            return label;
+        });
+    }
+
+    public reset() {
+        super.reset();
+        this.bassinsResults = [];
+        this.result = undefined;
+    }
+
+    /**
+     * Returns true if at least one log message is present in the PAB result or any
+     * of the children results
+     */
+    public get hasLog(): boolean {
+        if (this.bassinsResults) {
+            for (const cr of this.bassinsResults) {
+                if (cr && cr.hasLog()) {
+                    return true;
+                }
+            }
+        }
+        return (this.result && this.result.hasLog());
+    }
+
+    // do not test result.ok else log messages will prevent partial results from being displayed
+    public get hasResults(): boolean {
+        return this.result !== undefined && ! this.result.hasOnlyErrors;
+    }
+
+    /** 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 bassins
+        for (const c of this.bassinsResults) {
+            err = (err || c.hasErrorMessages());
+        }
+
+        return err;
+    }
+
+    /** retourne true si le calcul à l'itération i a échoué */
+    public iterationHasError(i: number): boolean {
+        let err = this.result.resultElements[i].hasErrorMessages();
+        // logs des bassins
+        for (const c of this.bassinsResults) {
+            err = (err || c.resultElements[i].hasErrorMessages());
+        }
+
+        return err;
+    }
+
+    /** retourne true si tous les calculs ont échoué */
+    public hasOnlyErrors(): boolean {
+        let err = true;
+        // log principal
+        err = (err && this.result.hasOnlyErrors);
+        // logs des bassins
+        for (const c of this.bassinsResults) {
+            err = (err && c.hasOnlyErrors);
+        }
+
+        return err;
+    }
+}
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index b74de9221..c2c32a603 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -258,6 +258,7 @@
     "INFO_LIB_AMONT": "Upstream",
     "INFO_LIB_AVAL": "Downstream",
     "INFO_LIB_B": "Surface width",
+    "INFO_LIB_BASSIN": "Basin",
     "INFO_LIB_BB": "Pool width",
     "INFO_LIB_BETA": "Beta coefficient",
     "INFO_LIB_BT": "Half opening of the triangle",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index 3047fac2a..1dd7eb5cd 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -258,6 +258,7 @@
     "INFO_LIB_AMONT": "Amont",
     "INFO_LIB_AVAL": "Aval",
     "INFO_LIB_B": "Largeur au miroir",
+    "INFO_LIB_BASSIN": "Bassin",
     "INFO_LIB_BB": "Largeur du bassin",
     "INFO_LIB_BETA": "Coefficient béta",
     "INFO_LIB_BT": "Demi-ouverture du triangle",
-- 
GitLab