From e697fb810e3bfcdd63e6f43667ab0bd8867fa9ef Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Wed, 10 Apr 2019 15:36:09 +0200
Subject: [PATCH] =?UTF-8?q?Adaptation=20=C3=A0=20jalhyd#79?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 e2e/clone-calc.e2e-spec.ts                    |  17 +-
 e2e/compute-reset-chained-links.e2e-spec.ts   |  10 +-
 e2e/link-parallel-devices.e2e-spec.ts         |  47 ++++++
 e2e/load-linked-params.e2e-spec.ts            |  26 ---
 .../base-param-input.component.ts             |   4 +-
 .../generic-calculator/calc-name.component.ts |   2 +-
 .../calculator.component.ts                   |   1 -
 .../param-field-line.component.html           |   6 +-
 .../param-field-line.component.ts             |  24 ++-
 .../param-link/param-link.component.ts        |  11 +-
 .../remous-results.component.ts               |   2 +-
 .../definition/concrete/form-base.ts          |   8 -
 .../concrete/form-regime-uniforme.ts          |   1 -
 .../definition/form-compute-fixedvar.ts       |   2 +-
 .../form-compute-parallel-structures.ts       |   6 +-
 .../form-compute-section-parametree.ts        |   2 +-
 src/app/formulaire/definition/form-compute.ts |  14 +-
 .../definition/form-def-fixedvar.ts           | 154 +-----------------
 .../definition/form-def-paramcalc.ts          |  88 ----------
 .../formulaire/definition/form-definition.ts  |   2 +-
 .../definition/form-result-fixedvar.ts        |   2 -
 src/app/formulaire/ngparam.ts                 |  93 +++++------
 22 files changed, 151 insertions(+), 371 deletions(-)
 create mode 100644 e2e/link-parallel-devices.e2e-spec.ts

diff --git a/e2e/clone-calc.e2e-spec.ts b/e2e/clone-calc.e2e-spec.ts
index dab35d149..b768d34f0 100644
--- a/e2e/clone-calc.e2e-spec.ts
+++ b/e2e/clone-calc.e2e-spec.ts
@@ -72,22 +72,7 @@ describe("ngHyd − clone a calculator", () => {
       const displayedVal = await calcPage.getInputById(k).getAttribute("value");
       expect(displayedVal).toBe("" + v);
     });
-  });
-
-  it("cloning a parallel-structures calculator should work", async () => {
-    await startPage.navigateTo();
-
-    // create source module to clone
-    await navbar.clickNewCalculatorButton();
-    await listPage.clickMenuEntryForCalcType(8); // Lois d'ouvrages
-    await browser.sleep(500);
-
-    // otherwise clickCloneCalcButton() fails with "Element is not clickable at point"
-    // await browser.executeScript("window.scrollTo(0, 0);");
-    await calcPage.clickCloneCalcButton();
-    await browser.sleep(500);
 
-    // check existence of the cloned module
-    expect(await navbar.getAllCalculatorTabs().count()).toBe(2);
+    // @TODO check linked value (see above)
   });
 });
diff --git a/e2e/compute-reset-chained-links.e2e-spec.ts b/e2e/compute-reset-chained-links.e2e-spec.ts
index 6399c764c..dc72e70c0 100644
--- a/e2e/compute-reset-chained-links.e2e-spec.ts
+++ b/e2e/compute-reset-chained-links.e2e-spec.ts
@@ -36,7 +36,7 @@ describe("ngHyd − compute then reset chained results", () => {
     await browser.sleep(500);
     expect(await navbar.getAllCalculatorTabs().count()).toBe(3);
 
-    // 1. get top most module
+    // 1. get down-most module
     await navbar.clickCalculatorTabForUid("dWs5bm");
 
     // check that "compute" button is active
@@ -46,7 +46,7 @@ describe("ngHyd − compute then reset chained results", () => {
     // click "compute" button
     await calcButton.click();
 
-    // only top-most module should have results
+    // only down-most module should have results
     let hasResults = await calcPage.hasResults();
     // other two should not
     await navbar.clickCalculatorTabForUid("OGFzOH");
@@ -56,7 +56,7 @@ describe("ngHyd − compute then reset chained results", () => {
     hasResults = await calcPage.hasResults();
     expect(hasResults).toBe(false);
 
-    // 2. get bottom-most module
+    // 2. get up-most module
     await navbar.clickCalculatorTabForUid("OGFzOH");
 
     // modify any input (for ex. "Ks")
@@ -82,7 +82,7 @@ describe("ngHyd − compute then reset chained results", () => {
     await browser.sleep(500);
     expect(await navbar.getAllCalculatorTabs().count()).toBe(3);
 
-    // 1. get top most module (PAB Dimensions)
+    // 1. get down-most module (PAB Dimensions)
     await navbar.clickCalculatorTabForUid("bGZqcz");
 
     // check that "compute" button is active
@@ -99,7 +99,7 @@ describe("ngHyd − compute then reset chained results", () => {
       expect(hasResults).toBe(true);
     }
 
-    // 2. get bottom-most module (Macro-rugo)
+    // 2. get up-most module (Macro-rugo)
     await navbar.clickCalculatorTabForUid("dnRiY2");
 
     // modify any input (for ex. "Ks")
diff --git a/e2e/link-parallel-devices.e2e-spec.ts b/e2e/link-parallel-devices.e2e-spec.ts
new file mode 100644
index 000000000..f5c73aa4a
--- /dev/null
+++ b/e2e/link-parallel-devices.e2e-spec.ts
@@ -0,0 +1,47 @@
+import { AppPage } from "./app.po";
+import { ListPage } from "./list.po";
+import { CalculatorPage } from "./calculator.po";
+
+/**
+ * Load a session containing 4 calculators, having multiple linked parameters
+ * from one to another
+ * @TODO les valeurs des Select sont comparées au français, pas très générique :/
+ */
+describe("ngHyd − load session with multiple linked parameters − ", () => {
+  let startPage: AppPage;
+  let listPage: ListPage;
+  let calcPage: CalculatorPage;
+
+  function init() {
+      startPage = new AppPage();
+      calcPage = new CalculatorPage();
+      listPage = new ListPage();
+  }
+  beforeEach(init);
+
+  it("when creating parallel structures, devices should be linkable to one another", async () => {
+
+    // 1. check Lois d'ouvrages
+    await startPage.navigateTo();
+    await listPage.clickMenuEntryForCalcType(8);
+    await calcPage.getAddStructureButton().click();
+    const nb1 = await calcPage.getAllLinkButtons().count();
+    expect(nb1).toBe(8); // link buttons on children but not on parent
+
+    // 2. check Passe à bassin: Cloisons
+    await startPage.navigateTo();
+    await listPage.clickMenuEntryForCalcType(10);
+    await calcPage.getAddStructureButton().click();
+    const nb2 = await calcPage.getAllLinkButtons().count();
+    expect(nb2).toBe(4); // link buttons on children but not on parent
+
+
+    // 3. check Lois de déversoirs dénoyés
+    await startPage.navigateTo();
+    await listPage.clickMenuEntryForCalcType(9);
+    await calcPage.getAddStructureButton().click();
+    const nb3 = await calcPage.getAllLinkButtons().count();
+    expect(nb3).toBe(6); // link buttons on children but not on parent
+
+  });
+});
diff --git a/e2e/load-linked-params.e2e-spec.ts b/e2e/load-linked-params.e2e-spec.ts
index bac4ab8ee..2b67e12b6 100644
--- a/e2e/load-linked-params.e2e-spec.ts
+++ b/e2e/load-linked-params.e2e-spec.ts
@@ -97,30 +97,4 @@ describe("ngHyd − load session with multiple linked parameters − ", () => {
     expect(lo_brv).toEqual("LargeurBerge (Sec. param.)");
   });
 
-  it("when creating parallel structures, devices should be linkabke to one another", async () => {
-
-    // 1. check Lois d'ouvrages
-    await startPage.navigateTo();
-    await listPage.clickMenuEntryForCalcType(8);
-    await calcPage.getAddStructureButton().click();
-    const nb1 = await calcPage.getAllLinkButtons().count();
-    expect(nb1).toBe(8); // link buttons on children but not on parent
-
-    // 2. check Passe à bassin: Cloisons
-    await startPage.navigateTo();
-    await listPage.clickMenuEntryForCalcType(10);
-    await calcPage.getAddStructureButton().click();
-    const nb2 = await calcPage.getAllLinkButtons().count();
-    expect(nb2).toBe(4); // link buttons on children but not on parent
-
-
-    // 3. check Lois de déversoirs dénoyés
-    await startPage.navigateTo();
-    await listPage.clickMenuEntryForCalcType(9);
-    await calcPage.getAddStructureButton().click();
-    const nb3 = await calcPage.getAllLinkButtons().count();
-    expect(nb3).toBe(6); // link buttons on children but not on parent
-
-  });
-
 });
diff --git a/src/app/components/base-param-input/base-param-input.component.ts b/src/app/components/base-param-input/base-param-input.component.ts
index 9b69dd05e..4c31d115f 100644
--- a/src/app/components/base-param-input/base-param-input.component.ts
+++ b/src/app/components/base-param-input/base-param-input.component.ts
@@ -34,7 +34,7 @@ export class NgBaseParam extends Observable {
     }
 
     public checkValue(val: number) {
-        return this._param.checkValue(val);
+        return this._param.checkValueAgainstDomain(val);
     }
 
     public checkMin(min: number): boolean {
@@ -71,7 +71,7 @@ export class NgBaseParam extends Observable {
             msg = ServiceFactory.instance.i18nService.localizeText("ERROR_PARAM_NULL");
         } else {
             try {
-                this._param.checkValue(v);
+                this._param.checkValueAgainstDomain(v);
                 valid = true;
             } catch (e) {
                 if (e instanceof Message) {
diff --git a/src/app/components/generic-calculator/calc-name.component.ts b/src/app/components/generic-calculator/calc-name.component.ts
index 309526b77..b4c109b6f 100644
--- a/src/app/components/generic-calculator/calc-name.component.ts
+++ b/src/app/components/generic-calculator/calc-name.component.ts
@@ -50,7 +50,7 @@ export class CalculatorNameComponent extends GenericInputComponent {
         let msg: string;
 
         if (ui === undefined || ui.length < 1) {
-            msg = "Veuillez entrer un nom tralala";
+            msg = "Veuillez entrer un nom";
         } else {
             valid = true;
         }
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index 2635777d6..31842b969 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -204,7 +204,6 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
             this._formulaire.onRadioClick(this._pendingRadioClickInfo);
             this._pendingRadioClickInfo = undefined;
         }
-        // @TODO call this._isUIValid here ?
     }
 
     public onCloseForm() {
diff --git a/src/app/components/param-field-line/param-field-line.component.html b/src/app/components/param-field-line/param-field-line.component.html
index 973ec8c81..acea333d2 100644
--- a/src/app/components/param-field-line/param-field-line.component.html
+++ b/src/app/components/param-field-line/param-field-line.component.html
@@ -20,13 +20,13 @@
         <mat-button-toggle-group *ngIf="hasRadioFix() || hasRadioVar() || hasRadioCal() || hasRadioLink()">
 
             <mat-button-toggle class="radio_fix" value="radio_fix" 
-                (click)="onRadioClick('fix')" [checked]="isRadioFixChecked">
+                (click)="onRadioClick('fix')" [checked]="isRadioFixChecked" [disabled]="! canExitCalcMode()">
                 <span fxHide.xxs>{{ uitextParamFixe }}</span>
                 <span fxHide.gt-xxs>F</span>
             </mat-button-toggle>
         
             <mat-button-toggle class="radio_var" value="radio_var" *ngIf="hasRadioVar()"
-                (click)="onRadioClick('var')" [checked]="isRadioVarChecked">
+                (click)="onRadioClick('var')" [checked]="isRadioVarChecked" [disabled]="! canExitCalcMode()">
                 <span fxHide.xxs>{{ uitextParamVarier }}</span>
                 <span fxHide.gt-xxs>V</span>
             </mat-button-toggle>
@@ -38,7 +38,7 @@
             </mat-button-toggle>
         
             <mat-button-toggle class="radio_link" value="radio_link" *ngIf="hasRadioLink()"
-                (click)="onRadioClick('link')" [checked]="isRadioLinkChecked">
+                (click)="onRadioClick('link')" [checked]="isRadioLinkChecked" [disabled]="! canExitCalcMode()">
                 <span fxHide.xxs>{{ uitextParamLie }}</span>
                 <span fxHide.gt-xxs>L</span>
             </mat-button-toggle>
diff --git a/src/app/components/param-field-line/param-field-line.component.ts b/src/app/components/param-field-line/param-field-line.component.ts
index 47a79d393..06a78419f 100644
--- a/src/app/components/param-field-line/param-field-line.component.ts
+++ b/src/app/components/param-field-line/param-field-line.component.ts
@@ -4,7 +4,7 @@ import { I18nService } from "../../services/internationalisation/internationalis
 import { NgParameter, ParamRadioConfig } from "../../formulaire/ngparam";
 import { NgParamInputComponent } from "../ngparam-input/ngparam-input.component";
 import { ServiceFactory } from "../../services/service-factory";
-import { ParamValueMode, CalculatorType, ParallelStructure } from "jalhyd";
+import { ParamValueMode, ParallelStructure, Nub } from "jalhyd";
 import { FormulaireService } from "../../services/formulaire/formulaire.service";
 import { ParamLinkComponent } from "../param-link/param-link.component";
 import { ParamComputedComponent } from "../param-computed/param-computed.component";
@@ -214,14 +214,28 @@ export class ParamFieldLineComponent implements OnChanges {
         return false;
     }
 
-    private onRadioClick(option: string) {
+    /**
+     * Returns true if the current parameter is in CALC mode but no SINGLE
+     * parameter is available to take its place
+     */
+    public canExitCalcMode() {
+        let ret = true;
+        if (this.param.paramDefinition.isCalculated) {
+            const nub = this.param.paramDefinition.parentNub;
+            const p = nub.findFirstSingleParameter(this.param.paramDefinition);
+            ret = (p !== undefined);
+        }
+        return ret;
+    }
+
+    public onRadioClick(option: string) {
         const oldValue = this.param.valueMode;
         switch (option) {
             case "fix":
                 this.param.valueMode = ParamValueMode.SINGLE;
                 // reset the value to avoid "undefined" after exiting CALC or LINK mode
                 // @TODO not always necessary; find out why
-                this.param.setValue(this, this.param.paramDefinition.paramValues.singleValue);
+                this.param.setValue(this, this.param.paramDefinition.singleValue);
                 break;
 
             case "var":
@@ -237,7 +251,7 @@ export class ParamFieldLineComponent implements OnChanges {
                 break;
 
             case "cal":
-                this.param.valueMode = ParamValueMode.CALCUL;
+                this.param.setCalculated(); // sets mode to CALCUL and more
                 break;
 
             case "link":
@@ -278,7 +292,7 @@ export class ParamFieldLineComponent implements OnChanges {
     /**
      * réception d'un événement de validité de ParamValuesComponent
      */
-    private onParamValuesValid(event: boolean) {
+    public onParamValuesValid(event: boolean) {
         this._isRangeValid = event;
         this.emitValidity();
     }
diff --git a/src/app/components/param-link/param-link.component.ts b/src/app/components/param-link/param-link.component.ts
index 4d3efa4e0..7c3e8887e 100644
--- a/src/app/components/param-link/param-link.component.ts
+++ b/src/app/components/param-link/param-link.component.ts
@@ -172,7 +172,16 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
      * événement de changement de la valeur du modèle
      */
     private emitModelChanged() {
-        this.change.emit({ "action": "model", "value": this.currentLinkedParam.getValue() });
+        let value;
+        try {
+            value = this.currentLinkedParam.getValue();
+        } catch (e) {
+            // console.log("undefined target value (pending calculation)");
+        }
+        this.change.emit({
+            "action": "model",
+            "value": value
+        });
     }
 
     public updateParamList() {
diff --git a/src/app/components/remous-results/remous-results.component.ts b/src/app/components/remous-results/remous-results.component.ts
index d62a814e0..a59ea0c96 100644
--- a/src/app/components/remous-results/remous-results.component.ts
+++ b/src/app/components/remous-results/remous-results.component.ts
@@ -350,7 +350,7 @@ export class RemousResultsComponent implements DoCheck {
     }
 
     private get abscisseIterator(): INumberIterator {
-        return this._remousResults.varResults.variatedParameter.paramDefinition.paramValues.getValuesIterator();
+        return this._remousResults.varResults.variatedParameter.paramDefinition.valuesIterator;
     }
 
     private connectRessaut(lineFlu: LineData, lineTor: LineData) {
diff --git a/src/app/formulaire/definition/concrete/form-base.ts b/src/app/formulaire/definition/concrete/form-base.ts
index edc9f93c2..f1afa17ea 100644
--- a/src/app/formulaire/definition/concrete/form-base.ts
+++ b/src/app/formulaire/definition/concrete/form-base.ts
@@ -26,14 +26,6 @@ export class FormulaireBase extends FormulaireDefinition {
         this._formParamCalc.parseOptions(json);
     }
 
-    /**
-     * gestion du clic sur les radios "paramètre fixé, à varier, à calculer"
-     */
-    public onRadioClick(info: string) {
-        super.onRadioClick(info);
-        this._formParamCalc.onRadioClick(info);
-    }
-
     /**
      * Resets the form results, the results panel on screen, the model
      * results, and does the same for all depending modules
diff --git a/src/app/formulaire/definition/concrete/form-regime-uniforme.ts b/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
index 503bdaba7..3d846e31d 100644
--- a/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
+++ b/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
@@ -41,7 +41,6 @@ export class FormulaireRegimeUniforme extends FormulaireBase implements Observer
             this.replaceCurrentNub(sender.properties);
             for (const fs of this.allFieldsets) {
                 fs.setNub(this._currentNub);
-                this._formParamCalc.setDefault();
                 // treat the fieldset as new to re-subscribe to Nub properties change events
                 this.afterParseFieldset(fs);
             }
diff --git a/src/app/formulaire/definition/form-compute-fixedvar.ts b/src/app/formulaire/definition/form-compute-fixedvar.ts
index 873812579..205e9e9dc 100644
--- a/src/app/formulaire/definition/form-compute-fixedvar.ts
+++ b/src/app/formulaire/definition/form-compute-fixedvar.ts
@@ -46,7 +46,7 @@ export class FormComputeFixedVar extends FormCompute {
         const nub: Nub = this._formBase.currentNub;
         const computedParam: NgParameter = this.getComputedParameter();
 
-        const res: Result = this.runNubCalc(nub, computedParam);
+        const res: Result = this.runNubCalc(nub);
 
         this.reaffectResultComponents();
     }
diff --git a/src/app/formulaire/definition/form-compute-parallel-structures.ts b/src/app/formulaire/definition/form-compute-parallel-structures.ts
index 580a52f08..8f169e5e3 100644
--- a/src/app/formulaire/definition/form-compute-parallel-structures.ts
+++ b/src/app/formulaire/definition/form-compute-parallel-structures.ts
@@ -1,4 +1,4 @@
-import { ComputeNode, ParallelStructure, Structure } from "jalhyd";
+import { ComputeNode, ParallelStructure, Structure, ParamDefinition } from "jalhyd";
 
 import { FormComputeFixedVar } from "./form-compute-fixedvar";
 import { FormResultFixedVar } from "./form-result-fixedvar";
@@ -41,8 +41,8 @@ export class FormComputeParallelStructures extends FormComputeFixedVar {
      * construit un identifiant de type { uid: "abcdef", symbol: "X" }
      * avec "abcdef" l'index de l'ouvrage et "X" son paramètre
      */
-    protected getParameterRefid(p: NgParameter): any {
-        const nub = p.paramDefinition.parentComputeNode;
+    protected getParameterRefid(p: ParamDefinition): any {
+        const nub = p.parentComputeNode;
         if (nub instanceof Structure) {
             return {
                 uid: nub.uid,
diff --git a/src/app/formulaire/definition/form-compute-section-parametree.ts b/src/app/formulaire/definition/form-compute-section-parametree.ts
index 068dce9d2..4c78233a0 100644
--- a/src/app/formulaire/definition/form-compute-section-parametree.ts
+++ b/src/app/formulaire/definition/form-compute-section-parametree.ts
@@ -51,7 +51,7 @@ export class FormComputeSectionParametree extends FormCompute {
         const computedParam: NgParameter = this.createParameter(computedParamInfo.symbol, this._formBase);
         this._varResults.calculatedParameter = computedParam;
 
-        this._varResults.result = this.runNubCalc(sectNub, computedParam);
+        this._varResults.result = this.runNubCalc(sectNub);
         this._varResults.update(false);
     }
 
diff --git a/src/app/formulaire/definition/form-compute.ts b/src/app/formulaire/definition/form-compute.ts
index a9fb302a1..5f077f0c2 100644
--- a/src/app/formulaire/definition/form-compute.ts
+++ b/src/app/formulaire/definition/form-compute.ts
@@ -1,4 +1,4 @@
-import { Nub, Result, ParamDomainValue, Observer } from "jalhyd";
+import { Nub, Result, ParamDomainValue, Observer, ParamDefinition } from "jalhyd";
 
 import { FormResult } from "./form-result";
 import { FormulaireDefinition } from "./form-definition";
@@ -21,7 +21,7 @@ export abstract class FormCompute implements Observer {
      * retourne un identifiant du paramètre dans le formulaire
      * surchargé dans le cas des ouvrages //
      */
-    protected getParameterRefid(p: NgParameter) {
+    protected getParameterRefid(p: ParamDefinition) {
         return p.symbol;
     }
 
@@ -36,11 +36,17 @@ export abstract class FormCompute implements Observer {
      * Lance le calcul d'un paramètre en déterminant une valeur initiale.
      * Si nécessaire déclenche un calcul en chaîne des modules en amont.
      */
-    protected runNubCalc(nub: Nub, computedParam: NgParameter): Result {
+    protected runNubCalc(nub: Nub, computedParam?: ParamDefinition): Result {
         let init: number;
+
+        // by default, use Nub's calculatedParam
+        if (computedParam === undefined) {
+            computedParam = nub.calculatedParam;
+        }
+
         // require chain computation; redundant with Nub.CalcSerie but required
         // to get initial value here...
-        const computedParamValue = computedParam.getValue(true);
+        const computedParamValue = computedParam.getValue();
 
         switch (computedParam.domain.domain) {
             case ParamDomainValue.ANY:
diff --git a/src/app/formulaire/definition/form-def-fixedvar.ts b/src/app/formulaire/definition/form-def-fixedvar.ts
index 469bda41b..efad7a577 100644
--- a/src/app/formulaire/definition/form-def-fixedvar.ts
+++ b/src/app/formulaire/definition/form-def-fixedvar.ts
@@ -1,166 +1,14 @@
-import { ParamValueMode } from "jalhyd";
-
-import { ParamRadioConfig, NgParameter } from "../ngparam";
 import { FormulaireDefinition } from "./form-definition";
 
 /**
  * gestion des formulaires avec "paramètre fixé" et "paramètre à varier"
  */
 export class FormDefFixedVar {
+
     protected _formBase: FormulaireDefinition;
 
     constructor(base: FormulaireDefinition) {
         this._formBase = base;
     }
 
-    /**
-     * remet les radios de tous les paramètres à FIX sauf "me" et ceux (celui) à l'état "except"
-     */
-    protected resetOtherRadio(me: NgParameter, except?: ParamRadioConfig) {
-        for (const p of this._formBase.allFormElements) {
-            if (p instanceof NgParameter) {
-                if (p !== me && p.radioState !== except
-                    && p.radioState !== ParamRadioConfig.LINK
-                    && p.radioConfig !== ParamRadioConfig.FIX) {
-
-                    p.valueMode = ParamValueMode.SINGLE;
-                }
-            }
-        }
-    }
-
-    /**
-     * gère un changement de mode pour un paramètre
-     * règles :
-     *   - 1 seul paramètre CAL à la fois
-     *   - 1 seul paramètre multivalué (VAR) à la fois (directement ou par liaison)
-     *   - plusieurs paramètres FIX à la fois possible
-     *   - plusieurs paramètres LINK à la fois possible si les 2 1ères règles sont respectées
-     *
-     * analyse :
-     * ancien état    nouvel état    action(s)
-     * FIX            VAR            action1
-     * FIX            CAL            action2
-     * FIX            LINK           si paramètre lié FIX : aucune
-     *                               si paramètre lié VAR : action1
-     *                               si paramètre lié CAL : si valeur unique : aucune
-     *                                                      si valeur multiple : action1
-     *                               si paramètre lié LINK : recommencer ce cas avec le paramètre lié
-     *
-     * VAR            FIX            aucune
-     * VAR            CAL            action2
-     * VAR            LINK           si paramètre lié FIX : aucune
-     *                               si paramètre lié VAR : aucune
-     *                               si paramètre lié CAL : si valeur unique : aucune
-     *                                                      si valeur multiple : aucune
-     *                               si paramètre lié LINK : recommencer ce cas avec le paramètre lié
-     *
-     * CAL            FIX            action5
-     * CAL            VAR            action3 + action5
-     * CAL            LINK           si paramètre lié FIX : aucune
-     *                               si paramètre lié VAR : action3 + action4|action5
-     *                               si paramètre lié CAL : action3 + action4|action5
-     *                               si paramètre lié LINK : recommencer ce cas avec le paramètre lié
-     *
-     * action1 : reset (à FIX) de tous les autres paramètres que celui modifié sauf celui à CAL
-     * action2 : reset (à FIX) de tous les autres paramètres que celui modifié sauf celui/ceux à VAR
-     * action3 : reset (à FIX) de tous les autres paramètres que celui modifié
-     * action4 : mettre le paramètre désigné par la conf comme "par défault" à CAL
-     * action5 : mettre le 1er paramètre du module de calcul à CAL
-     */
-    protected processRadioStateChange(sourceParam: NgParameter, oldState: ParamValueMode) {
-        switch (oldState) {
-            case ParamValueMode.SINGLE:  // ancien état
-                switch (sourceParam.valueMode) {
-                    case ParamValueMode.MINMAX:  // nouvel état
-                    case ParamValueMode.LISTE:
-                        this.resetOtherRadio(sourceParam, ParamRadioConfig.CAL);
-                        break;
-
-                    case ParamValueMode.CALCUL:  // nouvel état
-                        this.resetOtherRadio(sourceParam, ParamRadioConfig.VAR);
-                        break;
-
-                    case ParamValueMode.LINK:  // nouvel état
-                        if (sourceParam.paramDefinition.isReferenceDefined() && sourceParam.paramDefinition.hasMultipleValues) {
-                            this.resetOtherRadio(sourceParam, ParamRadioConfig.CAL);
-                        } // @TODO vérifier tous les cas problématiques (liens en chaîne ?)
-                        break;
-                }
-                break;
-
-            case ParamValueMode.LISTE:  // ancien état
-            case ParamValueMode.MINMAX:
-                switch (sourceParam.valueMode) {
-                    case ParamValueMode.CALCUL:  // nouvel état
-                        this.resetOtherRadio(sourceParam, ParamRadioConfig.VAR);
-                        break;
-
-                    case ParamValueMode.LINK:  // nouvel état
-                        // mode du paramètre référencé
-                        /* const refParamValues = sourceParam.paramDefinition.referencedParamValues;
-                        if (refParamValues.valueMode === ParamValueMode.LINK) {
-                            throw new Error(`références de paramètre en chaîne non pris en charge`);
-                        } */
-                        // @TODO vérifier tous les cas problématiques (liens en chaîne ?)
-                        break;
-                }
-                break;
-
-            case ParamValueMode.LINK:  // ancien état
-                switch (sourceParam.valueMode) {
-                    case ParamValueMode.MINMAX:  // nouvel état
-                    case ParamValueMode.LISTE:
-                        this.resetOtherRadio(sourceParam, ParamRadioConfig.CAL);
-                        break;
-
-                    case ParamValueMode.CALCUL:  // nouvel état
-                        this.resetOtherRadio(sourceParam, ParamRadioConfig.VAR);
-                        break;
-                }
-                break;
-        }
-    }
-
-    /**
-     * modifie les boutons radio "fix", "var", "cal" de tous les paramètres
-     * en fonction de la modification de l'état d'un des paramètres
-     * @param uid id numérique unique du paramètre source
-     * @param option nouvel état "fix", "var" ou "cal" du paramètre source
-     */
-    protected resetRadiosAndResults(sourceParam: NgParameter, oldState: ParamValueMode) {
-        this.processRadioStateChange(sourceParam, oldState);
-
-        // on vérifie qu'il y a au moins un paramètre "à calculer" et sinon, on prend le 1er qui est à "fixé"
-        if (this._formBase.getDisplayedParamFromState(ParamRadioConfig.CAL) === undefined) {
-            let newCal: NgParameter;
-
-            for (const p of this._formBase.allFormElements) {
-                if (p instanceof NgParameter) {
-                    // change all radio button groups except the one that sent the event
-                    if (p.radioConfig === ParamRadioConfig.CAL && p.radioState === ParamRadioConfig.FIX && p !== sourceParam) {
-                        newCal = p;
-                        break;
-                    }
-                }
-                if (newCal) {
-                    break;
-                }
-            }
-            // if the current calculated parameter was set to another mode, set a new param
-            // to calculated mode (there must always be exactly one)
-            if (newCal) {
-                newCal.valueMode = ParamValueMode.CALCUL;
-            }
-        }
-    }
-
-    /**
-     * gestion des événements clic sur les radios
-     */
-    public onRadioClick(info: any) {
-        const param: NgParameter = info.param; // paramètre source de l'événement radio
-        const old: ParamValueMode = info.oldValueMode; // ancien état (radio)
-        this.resetRadiosAndResults(param, old);
-    }
 }
diff --git a/src/app/formulaire/definition/form-def-paramcalc.ts b/src/app/formulaire/definition/form-def-paramcalc.ts
index 230a3257c..c16a0397e 100644
--- a/src/app/formulaire/definition/form-def-paramcalc.ts
+++ b/src/app/formulaire/definition/form-def-paramcalc.ts
@@ -1,6 +1,3 @@
-import { ParamValueMode, ParamDefinition } from "jalhyd";
-
-import { NgParameter } from "../ngparam";
 import { FormulaireDefinition } from "./form-definition";
 import { FormDefFixedVar } from "./form-def-fixedvar";
 
@@ -8,97 +5,12 @@ import { FormDefFixedVar } from "./form-def-fixedvar";
  * gestion des formulaires avec "paramètre à calculer" (conduite distributrice, Lechapt-Calmon, régime uniforme, passes à bassin)
  */
 export class FormDefParamToCalculate extends FormDefFixedVar {
-    /**
-     * symbole du paramètre à calculer par défaut (cf config "idCal")
-     */
-    private _defaultCalculatedParam: string;
 
     constructor(base: FormulaireDefinition) {
         super(base);
     }
 
     public parseOptions(json: {}) {
-        this._defaultCalculatedParam = undefined;
-        // browse config file to find "options" chapter
-        for (const k in json) {
-            const o = json[k];
-            if (o.type === "options") {
-                // id du paramètre à calculer par défaut
-                this._defaultCalculatedParam = o["idCal"];
-                // this._formBase
-                if (this._defaultCalculatedParam && ! this.findCalculatedParam()) {
-                    const p = this.setDefault();
-                    p.isDefault = true;
-                }
-            }
-        }
-    }
-
-    /**
-     * Find the parameter that is set to CALC mode
-     */
-    private findCalculatedParam() {
-        for (const p of this._formBase.currentNub.parameterIterator) {
-            if (p.valueMode === ParamValueMode.CALCUL) {
-                return p;
-            }
-        }
-    }
-
-    /**
-     * met le paramètre par défaut à CAL sauf si c'est "except"
-     * @param except paramètre à ne pas remettre à CAL
-     */
-    public setDefault(except?: NgParameter): NgParameter {
-        const defaultParamCal: NgParameter = this._formBase.getParamFromSymbol(this._defaultCalculatedParam);
-        if (except === undefined || defaultParamCal.uid !== except.uid) {
-            defaultParamCal.valueMode = ParamValueMode.CALCUL;
-        }
-        return defaultParamCal;
     }
 
-    /**
-     * @see FormDefFixedVar.processRadioStateChange pour l'analyse
-     */
-    protected processRadioStateChange(sourceParam: NgParameter, oldState: ParamValueMode) {
-        super.processRadioStateChange(sourceParam, oldState);
-
-        switch (oldState) {
-            case ParamValueMode.CALCUL:  // ancien état
-                switch (sourceParam.valueMode) {
-                    case ParamValueMode.SINGLE:  // nouvel état
-                        this.setDefault(sourceParam);
-                        break;
-
-                    case ParamValueMode.MINMAX:  // nouvel état
-                    case ParamValueMode.LISTE:
-                        super.resetOtherRadio(sourceParam);
-                        this.setDefault(sourceParam);
-                        break;
-
-                    case ParamValueMode.LINK:  // nouvel état
-                        if (sourceParam.paramDefinition.hasMultipleValues) {
-                            super.resetOtherRadio(sourceParam);
-                            this.setDefault(sourceParam);
-                        } else {
-                            // mode du paramètre référencé
-                            const refValue = sourceParam.paramDefinition.referencedValue;
-                            if (refValue && refValue.isParameter()) {
-                                switch ((refValue.element as ParamDefinition).valueMode) {
-                                    case ParamValueMode.MINMAX:
-                                    case ParamValueMode.LISTE:
-                                    case ParamValueMode.CALCUL:
-                                        super.resetOtherRadio(sourceParam);
-                                        this.setDefault(sourceParam);
-                                        break;
-
-                                    case ParamValueMode.LINK:
-                                        throw new Error(`références de paramètre en chaîne non pris en charge`); // cas à traiter
-                                }
-                            }
-                        }
-                        break;
-                }
-        }
-    }
 }
diff --git a/src/app/formulaire/definition/form-definition.ts b/src/app/formulaire/definition/form-definition.ts
index be1369a87..0c43a6bf9 100644
--- a/src/app/formulaire/definition/form-definition.ts
+++ b/src/app/formulaire/definition/form-definition.ts
@@ -438,7 +438,7 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
      * gestion d'un clic sur les radios
      */
     public onRadioClick(info: any) {
-        // if mdoe changed, reset form results
+        // if mode changed, reset form results
         if (info.oldValueMode !== info.param.valueMode) {
             this.reset();
         }
diff --git a/src/app/formulaire/definition/form-result-fixedvar.ts b/src/app/formulaire/definition/form-result-fixedvar.ts
index 291a6373a..be7137587 100644
--- a/src/app/formulaire/definition/form-result-fixedvar.ts
+++ b/src/app/formulaire/definition/form-result-fixedvar.ts
@@ -1,5 +1,3 @@
-import { ResultElement, cLog, ParamValueMode } from "jalhyd";
-
 import { FixedResults } from "../../results/fixed-results";
 import { GraphType, VarResults } from "../../results/var-results";
 import { ParamRadioConfig, NgParameter } from "../ngparam";
diff --git a/src/app/formulaire/ngparam.ts b/src/app/formulaire/ngparam.ts
index 9d9ce7c9c..3f9f58711 100644
--- a/src/app/formulaire/ngparam.ts
+++ b/src/app/formulaire/ngparam.ts
@@ -1,5 +1,5 @@
 import { Interval, ParamDefinition, ParamDomain, ParamValueMode, INumberIterator,
-    Nub, Observer, asObservable, ParamCalculability, LinkedValue } from "jalhyd";
+    Observer, asObservable, ParamCalculability, LinkedValue } from "jalhyd";
 
 import { sprintf } from "sprintf-js";
 
@@ -11,24 +11,16 @@ import { ServiceFactory } from "../services/service-factory";
 import { FormulaireNode } from "./formulaire-node";
 
 export enum ParamRadioConfig {
-    /**
-     * pas de radio, paramètre modifiable à la main uniquement
-     */
+    /** pas de radio, paramètre modifiable à la main uniquement */
     FIX,
 
-    /**
-     * boutons radio "paramètre fixé" et "paramètre à varier"
-     */
+    /** boutons radio "paramètre fixé" et "paramètre à varier" */
     VAR,
 
-    /**
-     * boutons radio "paramètre fixé", "paramètre à varier" et "paramètre à calculer"
-     */
+    /** boutons radio "paramètre fixé", "paramètre à varier" et "paramètre à calculer" */
     CAL,
 
-    /**
-     * boutons radio "paramètre fixé", "paramètre à varier" et "paramètre à calculer", "paramètre lié"
-     */
+    /** boutons radio "paramètre fixé", "paramètre à varier" et "paramètre à calculer", "paramètre lié" */
     LINK
 }
 
@@ -37,17 +29,13 @@ export enum ParamRadioConfig {
  */
 export class NgParameter extends InputField implements Observer {
 
-    constructor(private _paramDef: ParamDefinition, parent: FormulaireNode) {
-        super(parent);
-    }
     public unit: string;
     public radioConfig: ParamRadioConfig;
 
-    /**
-     * true si ce paramètre est celui par défaut dans un formulaire
-     * (cf. fichier de conf des modules de calcul, objet "options", champ "idCal")
-     */
-    public isDefault = false; // archi bug du langage ! si on relit cette propriété sans l'avoir modifiée entre-temps, elle vaut undefined !
+    constructor(private _paramDef: ParamDefinition, parent: FormulaireNode) {
+        super(parent);
+        this.radioConfig = this.radioState;
+    }
 
     /**
      * Returns a text preview of the current value(s), depending on the value mode
@@ -62,9 +50,9 @@ export class NgParameter extends InputField implements Observer {
                 valuePreview = String(p.getValue().toFixed(nDigits));
                 break;
             case ParamValueMode.MINMAX:
-                let min: any = p.paramValues.min;
-                let max: any = p.paramValues.max;
-                let step: any = p.paramValues.step;
+                let min: any = p.min;
+                let max: any = p.max;
+                let step: any = p.step;
                 if (min) {
                     min = min.toFixed(nDigits);
                 }
@@ -79,7 +67,7 @@ export class NgParameter extends InputField implements Observer {
                 break;
             case ParamValueMode.LISTE:
                 valuePreview = i18n.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES");
-                const vals = p.paramValues.valueList || [];
+                const vals = p.valueList || [];
                 valuePreview += " " + vals.slice(0, 5).map((v) => {
                     return v.toFixed(nDigits);
                 }).join("; ") + "…";
@@ -161,10 +149,6 @@ export class NgParameter extends InputField implements Observer {
         this._confId = id;
     }
 
-    private get _paramValues() {
-        return this._paramDef.paramValues;
-    }
-
     public get paramDefinition() {
         return this._paramDef;
     }
@@ -216,23 +200,23 @@ export class NgParameter extends InputField implements Observer {
     }
 
     public get minValue() {
-        return this._paramValues.min;
+        return this._paramDef.min;
     }
 
     public get maxValue() {
-        return this._paramValues.max;
+        return this._paramDef.max;
     }
 
     public get stepRefValue(): Interval {
-        return this._paramValues.stepRefValue;
+        return this._paramDef.stepRefValue;
     }
 
     public get stepValue() {
-        return this._paramValues.step;
+        return this._paramDef.step;
     }
 
     public get valueList() {
-        return this._paramValues.valueList;
+        return this._paramDef.valueList;
     }
 
     public get isValid() {
@@ -274,11 +258,19 @@ export class NgParameter extends InputField implements Observer {
         throw new Error("invalid parameter radio configuration " + s);
     }
 
+    /**
+     * Sets this parameter as the one to be computed
+     */
+    public setCalculated() {
+        this.paramDefinition.setCalculated();
+    }
+
     /**
      * Asks the ParamDefinition for its current value
+     * @TODO replace with singleValue to avoid displaying computation results ?
      */
-    public getValue(triggerChainComputation: boolean = false) {
-        return this._paramDef.getValue(triggerChainComputation);
+    public getValue() {
+        return this._paramDef.getValue();
     }
 
     /**
@@ -307,32 +299,32 @@ export class NgParameter extends InputField implements Observer {
     }
 
     public setMinValue(sender: any, v: number) {
-        const changed = (this._paramValues.min !== v);
-        this._paramValues.min = v;
+        const changed = (this._paramDef.min !== v);
+        this._paramDef.min = v;
         if (changed) {
             this.notifyValueModified(sender);
         }
     }
 
     public setMaxValue(sender: any, v: number) {
-        const changed = (this._paramValues.max !== v);
-        this._paramValues.max = v;
+        const changed = (this._paramDef.max !== v);
+        this._paramDef.max = v;
         if (changed) {
             this.notifyValueModified(sender);
         }
     }
 
     public setStepValue(sender: any, v: number) {
-        const changed = (this._paramValues.step !== v);
-        this._paramValues.step = v;
+        const changed = (this._paramDef.step !== v);
+        this._paramDef.step = v;
         if (changed) {
             this.notifyValueModified(sender);
         }
     }
 
     public setValueList(sender: any, l: number[]) {
-        const changed = (JSON.stringify(this._paramValues.valueList) !== JSON.stringify(l));
-        this._paramValues.valueList = l;
+        const changed = (JSON.stringify(this._paramDef.valueList) !== JSON.stringify(l));
+        this._paramDef.valueList = l;
         if (changed) {
             this.notifyValueModified(sender);
         }
@@ -376,15 +368,22 @@ export class NgParameter extends InputField implements Observer {
                 o.addObserver(this); // pour être prévenu des changements de valeur de l'object référencé
             }
 
+            let value;
+            try {
+                value = this.getValue();
+            } catch (e) {
+                // console.log("undefined target value (pending calculation)");
+            }
+
             this.notifyObservers({
                 "action": "valueLinkChange",
-                "value": this.getValue()
+                "value": value
             });
         }
     }
 
     public checkValue(val: number) {
-        this._paramDef.checkValue(val);
+        this._paramDef.checkValueAgainstDomain(val);
     }
 
     public checkList(l: number[]) {
@@ -415,8 +414,6 @@ export class NgParameter extends InputField implements Observer {
             this.setValue(this, +val);
         }
         this.radioConfig = NgParameter.getRadioConfig(radioConfig);
-        // tslint:disable-next-line:max-line-length
-        this.isDefault = false; // malgré le fait qu'il soit initialisé dans la déclaration de la classe NgParam à false, quand on relit sa valeur, il vaut undefined (merci Microsoft)
     }
 
     public verifiesDependency(d: Dependency): boolean {
-- 
GitLab