From 94c0a1c776b4821d82f81e55e50cabf612c4f31d Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Tue, 11 Feb 2020 10:56:18 +0100
Subject: [PATCH 1/2] Fix #363 enhanced Solveur

---
 jalhyd_branch                                 |  2 +-
 src/app/calculators/solveur/config.json       |  9 ++
 src/app/calculators/solveur/en.json           |  1 +
 src/app/calculators/solveur/fr.json           |  1 +
 src/app/formulaire/definition/form-solveur.ts | 83 +++++++++++++++----
 src/app/formulaire/elements/fieldset.ts       |  2 +-
 .../formulaire/elements/select-field-nub.ts   | 22 +++--
 .../elements/select-field-parameter.ts        |  8 +-
 .../elements/select-field-reference.ts        | 12 +--
 src/app/formulaire/elements/select-field.ts   | 43 ++++++++--
 10 files changed, 143 insertions(+), 40 deletions(-)

diff --git a/jalhyd_branch b/jalhyd_branch
index feb22275f..b2cd21bbf 100644
--- a/jalhyd_branch
+++ b/jalhyd_branch
@@ -1 +1 @@
-197-mise-a-jour-vers-typescript-3-7
+188-solveur-pouvoir-cibler-un-resultat-complementaire-eventuellement-sur-un-seul-nub
diff --git a/src/app/calculators/solveur/config.json b/src/app/calculators/solveur/config.json
index 40b0bbb8c..fa1da444f 100644
--- a/src/app/calculators/solveur/config.json
+++ b/src/app/calculators/solveur/config.json
@@ -9,6 +9,13 @@
                 "reference": "nub",
                 "source": "solveur_target"
             },
+            {
+                "id": "select_target_result",
+                "type": "select",
+                "property": "targettedResult",
+                "source": "solveur_targetted_result",
+                "default": ""
+            },
             "Ytarget"
         ]
     },
@@ -27,7 +34,9 @@
     },
     {
         "type": "options",
+        "selectIds": [ "select_target_result" ],
         "targetNubSelectId": "select_target_nub",
+        "targettedResultSelectId": "select_target_result",
         "searchedParamSelectId": "select_searched_param",
         "_help": "solveur.html"
     }
diff --git a/src/app/calculators/solveur/en.json b/src/app/calculators/solveur/en.json
index df14b3764..a8e5ea027 100644
--- a/src/app/calculators/solveur/en.json
+++ b/src/app/calculators/solveur/en.json
@@ -7,5 +7,6 @@
     "X": "Value for searched parameter",
 
     "select_target_nub": "Module and parameter to calculate",
+    "select_target_result": "Targetted result",
     "select_searched_param": "Searched parameter"
 }
\ No newline at end of file
diff --git a/src/app/calculators/solveur/fr.json b/src/app/calculators/solveur/fr.json
index 1439bd8da..9c2c639bb 100644
--- a/src/app/calculators/solveur/fr.json
+++ b/src/app/calculators/solveur/fr.json
@@ -7,5 +7,6 @@
     "X": "Valeur du paramètre recherché",
 
     "select_target_nub": "Module et paramètre à calculer",
+    "select_target_result": "Résultat ciblé",
     "select_searched_param": "Paramètre recherché"
 }
\ No newline at end of file
diff --git a/src/app/formulaire/definition/form-solveur.ts b/src/app/formulaire/definition/form-solveur.ts
index 2057ce084..b6987a5d5 100644
--- a/src/app/formulaire/definition/form-solveur.ts
+++ b/src/app/formulaire/definition/form-solveur.ts
@@ -1,9 +1,11 @@
-import { IObservable, ParamDefinition } from "jalhyd";
+import { IObservable, ParamDefinition, Nub } from "jalhyd";
 
 import { SelectFieldNub } from "../elements/select-field-nub";
 import { SelectFieldParameter } from "../elements/select-field-parameter";
 import { NgParameter } from "../elements/ngparam";
 import { FormulaireFixedVar } from "./form-fixedvar";
+import { SelectField } from "../elements/select-field";
+import { FieldSet } from "../elements/fieldset";
 
 /**
  * Formulaire pour les Solveurs
@@ -13,31 +15,49 @@ export class FormulaireSolveur extends FormulaireFixedVar {
     /** id of select configuring target Nub */
     private _targetNubSelectId: string;
 
+    /** id of select configuring targetted result */
+    private _targettedResultSelectId: string;
+
     /** id of select configuring searched param */
     private _searchedParamSelectId: string;
 
     protected parseOptions(json: {}) {
         super.parseOptions(json);
         this._targetNubSelectId = this.getOption(json, "targetNubSelectId");
+        this._targettedResultSelectId = this.getOption(json, "targettedResultSelectId");
         this._searchedParamSelectId = this.getOption(json, "searchedParamSelectId");
     }
 
-    protected completeParse(json: {}) {
+    protected completeParse(json: {}, firstNotif: boolean = true) {
         super.completeParse(json);
         if (this._targetNubSelectId) {
             const sel = this.getFormulaireNodeById(this._targetNubSelectId);
             if (sel) {
                 sel.addObserver(this);
-                // force 1st observation
-                (sel as SelectFieldNub).notifySelectValueChanged();
+                if (firstNotif) {
+                    // force 1st observation
+                    (sel as SelectFieldNub).notifySelectValueChanged();
+                }
+            }
+        }
+        if (this._targettedResultSelectId) {
+            const sel = this.getFormulaireNodeById(this._targettedResultSelectId);
+            if (sel) {
+                sel.addObserver(this);
+                if (firstNotif) {
+                    // force 1st observation
+                    (sel as SelectField).notifyValueChanged();
+                }
             }
         }
         if (this._searchedParamSelectId) {
             const sel = this.getFormulaireNodeById(this._searchedParamSelectId);
             if (sel) {
                 sel.addObserver(this);
-                // force 1st observation
-                (sel as SelectFieldNub).notifySelectValueChanged();
+                if (firstNotif) {
+                    // force 1st observation
+                    (sel as SelectFieldNub).notifySelectValueChanged();
+                }
             }
         }
 
@@ -46,7 +66,20 @@ export class FormulaireSolveur extends FormulaireFixedVar {
     // interface Observer
 
     public update(sender: IObservable, data: any) {
-        super.update(sender, data);
+        // 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) {
             if (data.action === "select") {
                 // update Solveur property: Nub to calculate
@@ -56,17 +89,17 @@ export class FormulaireSolveur extends FormulaireFixedVar {
                     // nubToCalculate is updated anyway; here, just inhibit the error
                     this._currentNub.properties.setPropValue("nubToCalculate", data.value.value);
                 } catch (e) { }
-                // refresh parameters selector
-                const sel = this.getFormulaireNodeById(this._searchedParamSelectId) as SelectFieldParameter;
-                if (sel) {
-                    sel.updateEntries();
-                    // reflect changes in GUI
-                    const inputYtarget = this.getFormulaireNodeById("Ytarget") as NgParameter;
-                    inputYtarget.notifyValueModified(this);
+                // refresh targetted result selector
+                const trSel = this.getFormulaireNodeById(this._targettedResultSelectId) as SelectField;
+                if (trSel) {
+                    (trSel.parent as FieldSet).updateFields();
+                    // trick to re-set observers
+                    this.completeParse({}, false);
                 }
+                // refresh parameters selector
+                this.refreshParameterEntries();
             }
-        }
-        if (sender instanceof SelectFieldParameter) {
+        } else if (sender instanceof SelectFieldParameter) {
             if (data.action === "select") {
                 // update Solveur property: searched Parameter
                 try {
@@ -80,6 +113,24 @@ export class FormulaireSolveur extends FormulaireFixedVar {
                 const inputXinit = this.getFormulaireNodeById("Xinit") as NgParameter;
                 inputXinit.notifyValueModified(this);
             }
+        } else if (sender instanceof SelectField) {
+            if (sender.id === "select_target_result") {
+                // refresh parameters selector
+                this.refreshParameterEntries();
+            }
+        }
+    }
+
+    /**
+     * Re-populate searched parameter selector with fresh entries
+     */
+    private refreshParameterEntries() {
+        const pSel = this.getFormulaireNodeById(this._searchedParamSelectId) as SelectFieldParameter;
+        if (pSel) {
+            pSel.updateEntries();
+            // reflect changes in GUI
+            const inputYtarget = this.getFormulaireNodeById("Ytarget") as NgParameter;
+            inputYtarget.notifyValueModified(this);
         }
     }
 }
diff --git a/src/app/formulaire/elements/fieldset.ts b/src/app/formulaire/elements/fieldset.ts
index 0472fdc52..c6e49fa9b 100644
--- a/src/app/formulaire/elements/fieldset.ts
+++ b/src/app/formulaire/elements/fieldset.ts
@@ -381,7 +381,7 @@ export class FieldSet extends FormulaireElement implements Observer {
                                 if (senderId === sId) {
                                     // find select element in parent form
                                     const fe = this.parentForm.getFieldById(sId);
-                                    if (fe) {
+                                    if (fe && data.value !== undefined) {
                                         const prop = (fe as SelectField).associatedProperty;
                                         this.setPropValue(prop, data.value.value);
                                     }
diff --git a/src/app/formulaire/elements/select-field-nub.ts b/src/app/formulaire/elements/select-field-nub.ts
index 52053385b..2ddd07337 100644
--- a/src/app/formulaire/elements/select-field-nub.ts
+++ b/src/app/formulaire/elements/select-field-nub.ts
@@ -25,18 +25,24 @@ export class SelectFieldNub extends SelectFieldReference {
      */
     protected populate() {
         switch (this._source) {
-            case "solveur_target": // Solveur, paramètre cible (à calculer)
+            case "solveur_target": // Solveur, module cible (à calculer)
                 // find all Nubs having at least one link to another Nub's result
                 const fs = ServiceFactory.instance.formulaireService;
-                const downstreamNubs = Session.getInstance().getDownstreamNubs();
-                for (const dn of downstreamNubs) {
-                    const calc = fs.getFormulaireFromId(dn.uid).calculatorName;
+                const candidateNubs =
+                    Session.getInstance().getDownstreamNubs().concat(
+                        Session.getInstance().getUpstreamNubsHavingExtraResults()
+                    ).filter(
+                        (element, index, self) => self.findIndex((e) => e.uid === element.uid) === index
+                    );
+                for (const cn of candidateNubs) {
+                    const calc = fs.getFormulaireFromId(cn.uid).calculatorName;
                     let label = calc;
-                    if (dn.calculatedParam !== undefined) {
-                        const varName = fs.expandVariableName(dn.calcType, dn.calculatedParam.symbol);
-                        label += ` / ${varName} (${dn.calculatedParam.symbol})`;
+                    // calculated param
+                    if (cn.calculatedParam !== undefined) {
+                        const varName = fs.expandVariableName(cn.calcType, cn.calculatedParam.symbol);
+                        label += ` / ${varName} (${cn.calculatedParam.symbol})`;
                     }
-                    this.addEntry(new SelectEntry(this._entriesBaseId + dn.uid, dn.uid, decodeHtml(label)));
+                    this.addEntry(new SelectEntry(this._entriesBaseId + cn.uid, cn.uid, decodeHtml(label)));
                 }
                 break;
         }
diff --git a/src/app/formulaire/elements/select-field-parameter.ts b/src/app/formulaire/elements/select-field-parameter.ts
index c643edd71..c5a9d7291 100644
--- a/src/app/formulaire/elements/select-field-parameter.ts
+++ b/src/app/formulaire/elements/select-field-parameter.ts
@@ -29,8 +29,12 @@ export class SelectFieldParameter extends SelectFieldReference {
                 // find all non-calculated, non-linked parameters of all Nubs that
                 // the current "target" Nub depends on (if any)
                 const fs = ServiceFactory.instance.formulaireService;
-                const ntc: Nub = (this.parentForm.currentNub as Solveur).nubToCalculate;
-                const searchableParams = Solveur.getDependingNubsSearchableParams(ntc);
+                const solv = this.parentForm.currentNub as Solveur;
+                const ntc: Nub = solv.nubToCalculate;
+                const searchableParams = Solveur.getDependingNubsSearchableParams(
+                    ntc,
+                    solv.targettedResult !== undefined && solv.targettedResult !== ""
+                );
                 for (const p of searchableParams) {
                     if (p.visible) {
                         const calc = fs.getFormulaireFromId(p.originNub.uid).calculatorName;
diff --git a/src/app/formulaire/elements/select-field-reference.ts b/src/app/formulaire/elements/select-field-reference.ts
index 3a0d8396c..4cbe101ed 100644
--- a/src/app/formulaire/elements/select-field-reference.ts
+++ b/src/app/formulaire/elements/select-field-reference.ts
@@ -43,13 +43,13 @@ export abstract class SelectFieldReference extends SelectField {
         this.clearEntries();
         // populate
         this.populate();
-        // keep previously selected entry if possible
-        if (pse && pse.id) {
-            this.setValueFromId(pse.id);
+        // if no entry is available anymore, unset value
+        if (this.entries.length === 0) {
+            super.setValue(undefined);
         } else {
-            // if no entry is available anymore, unset value
-            if (this.entries.length === 0) {
-                super.setValue(undefined);
+            // keep previously selected entry if possible
+            if (pse && pse.id) {
+                this.setValueFromId(pse.id);
             } else {
                 this.setDefaultValue();
             }
diff --git a/src/app/formulaire/elements/select-field.ts b/src/app/formulaire/elements/select-field.ts
index cfd54a582..2fe1e85ea 100644
--- a/src/app/formulaire/elements/select-field.ts
+++ b/src/app/formulaire/elements/select-field.ts
@@ -1,11 +1,11 @@
 import {
-    acSection,
     CourbeRemous,
     Nub,
     ParallelStructure,
     StructureType,
     LoiDebit,
-    Session
+    Session,
+    Solveur
  } from "jalhyd";
 
 import { Field } from "./field";
@@ -13,6 +13,7 @@ import { SelectEntry } from "./select-entry";
 import { StringMap } from "../../stringmap";
 import { FormulaireNode } from "./formulaire-node";
 import { FormulaireDefinition } from "../definition/form-definition";
+import { ServiceFactory } from "../../services/service-factory";
 
 export class SelectField extends Field {
 
@@ -105,13 +106,17 @@ export class SelectField extends Field {
     public setValue(v: SelectEntry) {
         if (this._selectedEntry !== v) {
             this._selectedEntry = v;
-            this.notifyObservers({
-                "action": "select",
-                "value": v
-            }, this);
+            this.notifyValueChanged();
         }
     }
 
+    public notifyValueChanged() {
+        this.notifyObservers({
+            "action": "select",
+            "value": this._selectedEntry
+        }, this);
+    }
+
     public getLabel() {
         if (this._selectedEntry) {
             return this._selectedEntry.label;
@@ -147,6 +152,32 @@ export class SelectField extends Field {
                 }
                 break;
 
+            // driven by string[], not enum
+            case "solveur_targetted_result":
+                // 1. calculated param
+                const ntc = (nub as Solveur).nubToCalculate;
+                if (ntc !== undefined) {
+                    const varName = ServiceFactory.instance.formulaireService.expandVariableName(ntc.calcType, ntc.calculatedParam.symbol);
+                    this.addEntry(new SelectEntry(
+                        this._entriesBaseId + "none",
+                        "",
+                        `${varName} (${ntc.calculatedParam.symbol})`
+                    ));
+                }
+                // 2. extra results
+                if (ntc !== undefined && ntc.resultsFamilies !== undefined) {
+                    for (const er of Object.keys(ntc.resultsFamilies)) {
+                        const varName = ServiceFactory.instance.formulaireService.expandVariableName(ntc.calcType, er);
+                        const e: SelectEntry = new SelectEntry(
+                            this._entriesBaseId + er,
+                            er,
+                            `${varName} (${er})`
+                        );
+                        this.addEntry(e);
+                    }
+                }
+                break;
+
             // possible values depend on CalcType
             case "device_structure_type":
                 for (const st in (nub as ParallelStructure).getLoisAdmissibles()) {
-- 
GitLab


From fd74b42c8a3de4173100f22894cc652caf5b18a4 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 20 Feb 2020 14:35:05 +0100
Subject: [PATCH 2/2] Translation for jalhyd#198

---
 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 89299e905..a4862ce4d 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -60,6 +60,7 @@
     "ERROR_SECTION_PENTE_NEG_NULLE_HNORMALE_INF": "The slope is negative or zero, the normal depth is infinite",
     "ERROR_SECTION_SURFACE_NULLE": "Section: calculation is impossible when surface is null",
     "ERROR_SOMETHING_FAILED_IN_CHILD": "Calculation of child module #%number% failed",
+    "ERROR_SOLVEUR_NO_VARIATED_PARAMS_ALLOWED": "Solver cannot be used with a modules chain containing variated parameters",
     "ERROR_STRUCTURE_Q_TROP_ELEVE": "The flow passing through the other devices is too high: the requested parameter is not calculable.",
     "INFO_CALCULATOR_CALC_NAME": "Calculator name",
     "INFO_CALCULATOR_CALCULER": "Compute",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index b9c0993ee..87b9c928e 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -60,6 +60,7 @@
     "ERROR_SECTION_PENTE_NEG_NULLE_HNORMALE_INF": "La pente est négative ou nulle, la hauteur normale est infinie",
     "ERROR_SECTION_SURFACE_NULLE": "Section : calcul impossible à cause d'une surface nulle",
     "ERROR_SOMETHING_FAILED_IN_CHILD": "Le calcul du module enfant n°%number% a échoué",
+    "ERROR_SOLVEUR_NO_VARIATED_PARAMS_ALLOWED": "Le solveur ne peut pas être utilisé avec une chaîne de modules contenant des paramètres variés",
     "ERROR_STRUCTURE_Q_TROP_ELEVE": "Le débit passant par les autres ouvrages est trop élevé: le paramètre demandé n'est pas calculable.",
     "INFO_CALCULATOR_CALC_NAME": "Nom du module de calcul",
     "INFO_CALCULATOR_CALCULER": "Calculer",
-- 
GitLab