diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8509d10d659747afc9c1163d6986006598ee6060..15ede0e0912e30acb3ce2d1644734dd059cb6b76 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -100,6 +100,7 @@ import { DialogEditPabComponent } from "./components/dialog-edit-pab/dialog-edit import { DialogEditParamComputedComponent } from "./components/dialog-edit-param-computed/dialog-edit-param-computed.component"; import { DialogEditParamValuesComponent } from "./components/dialog-edit-param-values/dialog-edit-param-values.component"; import { DialogGeneratePABComponent } from "./components/dialog-generate-pab/dialog-generate-pab.component"; +import { DialogGeneratePARSimulationComponent } from "./components/dialog-generate-par-simulation/dialog-generate-par-simulation.component"; import { DialogLoadSessionComponent } from "./components/dialog-load-session/dialog-load-session.component"; import { DialogLogEntriesDetailsComponent } from "./components/dialog-log-entries-details/dialog-log-entries-details.component"; import { DialogSaveSessionComponent } from "./components/dialog-save-session/dialog-save-session.component"; @@ -189,6 +190,7 @@ const appRoutes: Routes = [ DialogEditParamComputedComponent, DialogEditParamValuesComponent, DialogGeneratePABComponent, + DialogGeneratePARSimulationComponent, DialogLoadSessionComponent, DialogLogEntriesDetailsComponent, DialogSaveSessionComponent, @@ -240,6 +242,7 @@ const appRoutes: Routes = [ DialogEditParamComputedComponent, DialogEditParamValuesComponent, DialogGeneratePABComponent, + DialogGeneratePARSimulationComponent, DialogSaveSessionComponent, DialogLoadSessionComponent, DialogLogEntriesDetailsComponent diff --git a/src/app/calculators/par/config.json b/src/app/calculators/par/config.json index 7834b616ef114843bdc5805a69b9a9f085112a58..3d592b2bdb1502724508b8a1accb8706ef8c91aa 100644 --- a/src/app/calculators/par/config.json +++ b/src/app/calculators/par/config.json @@ -4,7 +4,10 @@ "type": "fieldset", "fields": [ "Q", - "Z1", + { + "id": "Z1", + "allowEmpty": true + }, { "id": "Z2", "allowEmpty": true diff --git a/src/app/components/dialog-generate-par-simulation/dialog-generate-par-simulation.component.html b/src/app/components/dialog-generate-par-simulation/dialog-generate-par-simulation.component.html new file mode 100644 index 0000000000000000000000000000000000000000..3f42734c8ba1676103d58b21cb70f5c59d028367 --- /dev/null +++ b/src/app/components/dialog-generate-par-simulation/dialog-generate-par-simulation.component.html @@ -0,0 +1,28 @@ +<h1 mat-dialog-title [innerHTML]="uitextGeneratePARSimulation"></h1> + +<form id="form-generate-par-simulation"> + + <div mat-dialog-content> + <div id="generate-par-sim-desc"> + {{ uitextDescription }} + </div> + <mat-form-field> + <mat-select id="select-combination" [placeholder]="label" [(value)]="selectedValue"> + <mat-option *ngFor="let e of entries" [value]="e" [title]="entryLabel(e)"> + {{ entryLabel(e) }} + </mat-option> + </mat-select> + </mat-form-field> + </div> + + <div mat-dialog-actions [attr.align]="'end'"> + <button mat-raised-button color="primary" [mat-dialog-close]="false" cdkFocusInitial> + {{ uitextCancel }} + </button> + <button mat-raised-button type="submit" color="warn" (click)="generatePARSimulation()" id="do-generate" + [disabled]=""> + {{ uitextGenerate }} + </button> + </div> + +</form> diff --git a/src/app/components/dialog-generate-par-simulation/dialog-generate-par-simulation.component.scss b/src/app/components/dialog-generate-par-simulation/dialog-generate-par-simulation.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..6fc6108653aeea19fa6a67d5741c50a28d4ad0ad --- /dev/null +++ b/src/app/components/dialog-generate-par-simulation/dialog-generate-par-simulation.component.scss @@ -0,0 +1,11 @@ +#form-generate-par-simulation { + max-width: 500px; +} + +mat-form-field { + width: 100%; +} + +#generate-par-sim-desc { + margin-bottom: 1em; +} diff --git a/src/app/components/dialog-generate-par-simulation/dialog-generate-par-simulation.component.ts b/src/app/components/dialog-generate-par-simulation/dialog-generate-par-simulation.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..dcb9569028dc7588dd3db80e1514ab95b0bdbe19 --- /dev/null +++ b/src/app/components/dialog-generate-par-simulation/dialog-generate-par-simulation.component.ts @@ -0,0 +1,109 @@ +import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; +import { Inject, Component } from "@angular/core"; + +import { I18nService } from "../../services/internationalisation.service"; +import { MultiDimensionResults } from "../../results/multidimension-results"; + +import { fv, longestVarNgParam } from "../../util"; + +@Component({ + selector: "dialog-generate-par-simulation", + templateUrl: "dialog-generate-par-simulation.component.html", + styleUrls: ["dialog-generate-par-simulation.component.scss"] +}) +export class DialogGeneratePARSimulationComponent { + + public selectedValue: number; + + /** résultats de la ParCalage */ + private _results: MultiDimensionResults; + + /** size of the longest variable value */ + private size = 0; + + /** inferred extended values list for each variating parameter */ + private varValues = []; + + constructor( + public dialogRef: MatDialogRef<DialogGeneratePARSimulationComponent>, + private intlService: I18nService, + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this._results = data.results; + this.selectedValue = 0; + + if (this._results) { + // pre-extract variable parameters values + this.varValues = []; + // find longest list + const lvp = longestVarNgParam(this._results.variatedParameters); + this.size = lvp.size; + // get extended values lists for each variable parameter + for (const v of this._results.variatedParameters) { + const vv = []; + const iter = v.getExtendedValuesIterator(this.size); + while (iter.hasNext) { + const nv = iter.next(); + vv.push(fv(nv.value)); + } + this.varValues.push(vv); + } + } + } + + public generatePARSimulation() { + this.dialogRef.close({ + generate: true, + selected: this.selectedValue, + size: this.size + }); + } + + public get uitextDescription() { + return this.intlService.localizeText("INFO_DIALOG_PARSIM_DESC"); + } + + public get uitextGeneratePARSimulation() { + return this.intlService.localizeText("INFO_CALCULATOR_RESULTS_GENERATE_PAR_SIMULATION"); + } + + public get uitextGenerate() { + return this.intlService.localizeText("INFO_OPTION_GENERATE"); + } + + public get uitextCancel() { + return this.intlService.localizeText("INFO_OPTION_CANCEL"); + } + + public get entries(): number[] { + const ret: number[] = []; + for (let i = 0; i < this.size; i++) { + ret.push(i); + } + return ret; + } + + protected entryLabel(index: number): string { + const kv = []; + for (let i = 0; i < this.varValues.length; i++) { + const vv = this.varValues[i]; + const vp = this._results.variatedParameters[i]; + let symbol = vp.symbol; + // is vp a parameter of a child Nub ? + if ( + vp.paramDefinition.parentNub + && vp.paramDefinition.parentNub !== vp.paramDefinition.originNub + ) { + const pos = vp.paramDefinition.parentNub.findPositionInParent() + 1; + symbol = this.intlService.localizeText("INFO_LIB_RADIER_N_COURT") + pos + "_" + symbol; + } + kv.push(`${symbol} = ${vv[index]}`); + } + return kv.join(", "); + } + + public get label() { + return this.intlService.localizeText("INFO_PARAMFIELD_BOUNDARY_CONDITIONS"); + } + +} diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts index 6e9adab4247b3de1d10832835a83c3653edb5521..53894e408f430719db52b1e6456e30c0cda5c023 100644 --- a/src/app/components/generic-calculator/calculator.component.ts +++ b/src/app/components/generic-calculator/calculator.component.ts @@ -40,7 +40,10 @@ import { PabTableComponent } from "../pab-table/pab-table.component"; import { MatDialog } from "@angular/material/dialog"; import { DialogConfirmCloseCalcComponent } from "../dialog-confirm-close-calc/dialog-confirm-close-calc.component"; import { DialogGeneratePABComponent } from "../dialog-generate-pab/dialog-generate-pab.component"; +import { DialogGeneratePARSimulationComponent } from "../dialog-generate-par-simulation/dialog-generate-par-simulation.component"; import { PabTable } from "../../formulaire/elements/pab-table"; +import { MultiDimensionResults } from "../../results/multidimension-results"; +import { NgParameter } from "../../formulaire/elements/ngparam"; import { HotkeysService, Hotkey } from "angular2-hotkeys"; @@ -124,6 +127,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe private router: Router, private confirmCloseCalcDialog: MatDialog, private generatePABDialog: MatDialog, + private generatePARSimulationDialog: MatDialog, private _elementRef: ElementRef, private hotkeysService: HotkeysService, private appSetupService: ApplicationSetupService, @@ -849,58 +853,109 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe return ( this.hasResults && ! parCalage.result.hasErrorMessages() - && this.allParamsAreFixed([ "Z1", "Q" ]) // only Z1 and Q may vary + && parCalage.prms.Z1.isDefined + && parCalage.prms.Z2.isDefined ); } public get uitextGenerateParSimulationTitle(): string { - if (! this.allParamsAreFixed([ "Z1", "Q" ])) { - return this.intlService.localizeText("INFO_ONLY_Z1_Q_MAY_VARY"); - } const parCalage = (this._formulaire.currentNub as Par); if (! this.hasResults || parCalage.result.hasErrorMessages()) { return this.intlService.localizeText("INFO_CALCULATE_FIRST"); } + if ( + parCalage.prms.Z1.isDefined + || parCalage.prms.Z2.isDefined + ) { + return this.intlService.localizeText("INFO_Z1_Z2_MUST_BE_DEFINED"); + } return ""; } /** - * Génère une simulation de passe à ralentisseurs à partir du calage en cours + * Génère une simulation de passe à ralentisseurs à partir du calage en cours; si + * au moins un paramètre varie, propose de choisir parmi les combinaisons de valeurs */ public generatePARSimulation() { const parCalage = (this._formulaire.currentNub as Par); const pcal = parCalage.prms; - // copy base params + let pres: { [key: string]: number } = parCalage.result.values; + + const varParams: NgParameter[] = this._formulaire.getVariatedParameters(); + if (varParams.length > 0) { + // open popup to choose combination of varying parameters + const mdParResults = new MultiDimensionResults(); + mdParResults.variatedParameters = varParams; + const dialogRef = this.generatePARSimulationDialog.open( + DialogGeneratePARSimulationComponent, + { + data: { + results: mdParResults + }, + disableClose: false + } + ); + dialogRef.afterClosed().subscribe(result => { + if (result && result.generate) { + const i = result.selected; + const s = result.size; // longest variating series, ie. number of iterations + pres = parCalage.result.resultElements[i].values; + // generate set of fixed values from chosen iteration i + this.doGenerateParSimWithValues({ + Q: pcal.Q.isCalculated ? pres.Q : (pcal.Q.hasMultipleValues ? pcal.Q.getInferredValuesList(s)[i] : pcal.Q.V), + Z1: pcal.Z1.hasMultipleValues ? pcal.Z1.getInferredValuesList(s)[i] : pcal.Z1.V, + Z2: pcal.Z2.hasMultipleValues ? pcal.Z2.getInferredValuesList(s)[i] : pcal.Z2.V, + S: pcal.S.hasMultipleValues ? pcal.S.getInferredValuesList(s)[i] : pcal.S.V, + P: pcal.P.hasMultipleValues ? pcal.P.getInferredValuesList(s)[i] : pcal.P.V, + L: pcal.L.isCalculated ? pres.L : (pcal.L.hasMultipleValues ? pcal.L.getInferredValuesList(s)[i] : pcal.L.V), + N: pcal.N.hasMultipleValues ? pcal.N.getInferredValuesList(s)[i] : pcal.N.V, + M: pcal.M.hasMultipleValues ? pcal.M.getInferredValuesList(s)[i] : pcal.M.V, + Nb: pres.Nb, + ZR1: pres.ZR1, + ZD1: pres.ZD1, + ZR2: pres.ZR2, + ZD2: pres.ZD2, + a: pres.a + }); + } + }); + } else { + // no parameter is varyng, generate directly + this.doGenerateParSimWithValues({ + Q: pcal.Q.V, + Z1: pcal.Z1.singleValue, + Z2: pcal.Z2.singleValue, + S: pcal.S.singleValue, + P: pcal.P.singleValue, + L: pcal.L.V, + N: pcal.Q.singleValue, + M: pcal.Q.singleValue, + Nb: pres.Nb, + ZR1: pres.ZR1, + ZD1: pres.ZD1, + ZR2: pres.ZR2, + ZD2: pres.ZD2, + a: pres.a + }); + } + } + + /** + * Creates a new Formulaire with a ParSimulation Nub, using given + * values as parameters + */ + protected doGenerateParSimWithValues(v: any) { + const parCalage = (this._formulaire.currentNub as Par); const psim = new ParSimulationParams( - undefined, // Q - undefined, // Z1 - pcal.Z2.singleValue, pcal.S.singleValue, pcal.P.singleValue, - undefined, // Nb - undefined, // ZR1 - pcal.ZD1.singleValue, - undefined, // ZR2 - undefined, // ZD2 - pcal.L.singleValue, pcal.a.singleValue, pcal.N.singleValue, pcal.M.singleValue + round(v.Q, 3), round(v.Z1, 3), round(v.Z2, 3), + round(v.S, 3), round(v.P, 3), round(v.Nb, 3), + round(v.ZR1, 3), round(v.ZD1, 3), round(v.ZR2, 3), + round(v.ZD2, 3), round(v.L, 3), round(v.a, 3), + round(v.N, 3), round(v.M, 3) ); const parSimulation = new ParSimulation(psim); - Session.getInstance().registerNub(parSimulation); - - // copy other params parSimulation.parType = parCalage.parType; - // Z1 and Q, that might be variating - psim.Z1.copyValuesFrom(pcal.Z1); - psim.Q.copyValuesFrom(pcal.Q); - // P, Nb, ZR1 - if (pcal.P.singleValue === undefined) { - psim.P.singleValue = round(parCalage.result.values.P, 3); - } - psim.Nb.singleValue = parCalage.result.values.Nb; - psim.ZR1.singleValue = round(parCalage.result.values.ZR1, 3); - // ZR2, ZD2 - if (pcal.Z2.singleValue !== undefined) { - psim.ZD2.singleValue = round(parCalage.result.values.ZD2, 3); - psim.ZR2.singleValue = round(parCalage.result.values.ZR2, 3); - } + Session.getInstance().registerNub(parSimulation); this.formulaireService.createFormulaire(CalculatorType.ParSimulation, parSimulation) .then((f: FormulaireDefinition) => { diff --git a/src/app/formulaire/definition/form-definition.ts b/src/app/formulaire/definition/form-definition.ts index 4c55719f1a82e22c8d56bb45eda119bf37d97ca5..3cb94b538c3290d99fdaa048f7fd3b7becb7e392 100644 --- a/src/app/formulaire/definition/form-definition.ts +++ b/src/app/formulaire/definition/form-definition.ts @@ -516,7 +516,7 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs return ngparam; } - protected getVariatedParameters(): NgParameter[] { + public getVariatedParameters(): NgParameter[] { let res: NgParameter[] = []; // find variated local parameters res = this.getDisplayedParamListFromState(ParamRadioConfig.VAR); diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json index 5b27a9971b0ea199d3e26bcffdf65b26e6a53cee..490aee9cbd7d3238e3b4b3ccefff4439391820bc 100644 --- a/src/locale/messages.en.json +++ b/src/locale/messages.en.json @@ -173,6 +173,7 @@ "INFO_CHILD_TYPE_MACRORUGO_PLUR": "aprons", "INFO_CHILD_TYPE_PUISSANCE": "power", "INFO_CHILD_TYPE_PUISSANCE_PLUR": "powers", + "INFO_DIALOG_PARSIM_DESC": "Choose a combination of values to generate the simulation", "INFO_FIELDSET_ADD": "Add", "INFO_FIELDSET_COPY": "Copy", "INFO_FIELDSET_REMOVE": "Remove", @@ -333,7 +334,9 @@ "INFO_MACRORUGOCOMPOUND_TITRE": "Compound rock-ramp fishpasses", "INFO_MACRORUGOCOMPOUND_TITRE_COURT": "Compound RR", "INFO_MACRORUGOCOMPOUND_LINCL": "Lateral inclination (m/m): ", + "INFO_ONLY_Q_MAY_VARY": "Only flow may vary", "INFO_ONLY_Z1_Q_MAY_VARY": "Only upstream elevation and flow may vary", + "INFO_Z1_Z2_MUST_BE_DEFINED": "Upstream and downstream elevations must be defined", "INFO_PENTE_TITRE": "Slope", "INFO_PENTE_TITRE_COURT": "Slope", "INFO_MENU_EMPTY_SESSION_TITLE": "New session", diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json index e1cd5c8334487b70d8f2537d6328468ff968340f..ff2ea795a7175546af4ebce95ad6b3f7361eb036 100644 --- a/src/locale/messages.fr.json +++ b/src/locale/messages.fr.json @@ -173,6 +173,7 @@ "INFO_CHILD_TYPE_MACRORUGO_PLUR": "radiers", "INFO_CHILD_TYPE_PUISSANCE": "puissance", "INFO_CHILD_TYPE_PUISSANCE_PLUR": "puissances", + "INFO_DIALOG_PARSIM_DESC": "Choisir une combinaison de valeurs pour générer la simulation", "INFO_FIELDSET_ADD": "Ajouter", "INFO_FIELDSET_COPY": "Copier", "INFO_FIELDSET_REMOVE": "Supprimer", @@ -334,7 +335,9 @@ "INFO_MACRORUGOCOMPOUND_TITRE": "Passe à macro-rugosités complexe", "INFO_MACRORUGOCOMPOUND_TITRE_COURT": "M-Rugo complexe", "INFO_MACRORUGOCOMPOUND_LINCL": "Dévers latéral (m/m) :", + "INFO_ONLY_Q_MAY_VARY": "Seul le débit peut varier", "INFO_ONLY_Z1_Q_MAY_VARY": "Seuls la cote amont et le débit peuvent varier", + "INFO_Z1_Z2_MUST_BE_DEFINED": "Les cotes amont et aval doivent être définies", "INFO_PENTE_TITRE": "Pente", "INFO_PENTE_TITRE_COURT": "Pente", "INFO_MENU_EMPTY_SESSION_TITLE": "Nouvelle session",