calculator.component.ts 23.41 KiB
import { Component, OnInit, DoCheck, OnDestroy, ViewChild, ViewChildren,
         QueryList, AfterViewChecked, ElementRef, Inject, forwardRef } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { Observer, Session, Cloisons, Pab, ParamValueMode, CalculatorType, Bief, SectionParametree, acSection, round, SessionSettings } from "jalhyd";
import { AppComponent } from "../../app.component";
import { FormulaireService } from "../../services/formulaire.service";
import { I18nService } from "../../services/internationalisation.service";
import { FieldSet } from "../../formulaire/fieldset";
import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
import { CalculatorResultsComponent } from "../../components/calculator-results/calculator-results.component";
import { Subscription } from "rxjs";
import { FieldSetComponent } from "../field-set/field-set.component";
import { CalculatorNameComponent } from "./calc-name.component";
import { FormulaireElement } from "../../formulaire/formulaire-element";
import { FieldsetContainer } from "../../formulaire/fieldset-container";
import { FieldsetContainerComponent } from "../fieldset-container/fieldset-container.component";
import { PabTableComponent } from "../pab-table/pab-table.component";
import { ServiceFactory } from "../../services/service-factory";
import { MatDialog } from "@angular/material";
import { DialogConfirmCloseCalcComponent } from "../dialog-confirm-close-calc/dialog-confirm-close-calc.component";
import { DialogGeneratePABComponent } from "../dialog-generate-pab/dialog-generate-pab.component";
import { PabTable } from "../../formulaire/pab-table";
import { HotkeysService, Hotkey } from "angular2-hotkeys";
@Component({
    selector: "hydrocalc",
    templateUrl: "./calculator.component.html",
    styleUrls: ["./calculator.component.scss"]
export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChecked, OnDestroy, Observer {
    /**
     * liste des FieldSetComponent
    @ViewChildren(FieldSetComponent)
    private _fieldsetComponents: QueryList<FieldSetComponent>;
    /**
     * liste des FieldsetContainerComponent
    @ViewChildren(FieldsetContainerComponent)
    private _fieldsetContainerComponents: QueryList<FieldsetContainerComponent>;
    /**
     * PabTableComponent if any
    @ViewChild(PabTableComponent, { static: false })
    private _pabTableComponent: PabTableComponent;
    /**
     * composant d'affichage des résultats
    @ViewChild(CalculatorResultsComponent, { static: true })
    private resultsComponent: CalculatorResultsComponent;
    /**
     * composant "nom du module de calcul"
    @ViewChild(CalculatorNameComponent, { static: true })
    private _calculatorNameComponent: CalculatorNameComponent;
    /**
     * formulaire affiché
    private _formulaire: FormulaireDefinition;
    private _subscription: Subscription; // pour souscrire aux changements d'URL envoyés par le routeur
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
/** * true si on a cliqué sur le bouton de lancement de calcul */ private _computeClicked: boolean; /** * flag de validité gloable de la saisie * NB : la validité du bouton "calculer" dépend de la validité de la saisie dans l'UI et de celle indiquée par le formulaire. * La validité de l'UI comprend la forme (pas de chaîne alpha dans les champs numériques, etc..). * La validité formulaire comprend le domaine de définition des valeurs saisies. */ private _isUIValid = false; /** * flag disabled du bouton "calculer" */ public isCalculateDisabled = true; /** * flag (+info) indiquant un événement radio à traiter * nécessaire avec l'introduction du mode de valeur LINK des paramètres car quand on modifie le mode à LINK, les possibles * paramètres liables ne sont pas encore connus */ private _pendingRadioClick = false; private _pendingRadioClickInfo: any; // services private intlService: I18nService; private formulaireService: FormulaireService; public get ID() { if (this._formulaire) { return this._formulaire.uid; } else { return "calculator_1"; } } constructor( @Inject(forwardRef(() => AppComponent)) private appComponent: AppComponent, private route: ActivatedRoute, private router: Router, private confirmCloseCalcDialog: MatDialog, private generatePABDialog: MatDialog, private _elementRef: ElementRef, private hotkeysService: HotkeysService ) { this.intlService = ServiceFactory.instance.i18nService; this.formulaireService = ServiceFactory.instance.formulaireService; // hotkeys listeners this.hotkeysService.add(new Hotkey("alt+w", AppComponent.onHotkey(this.closeCalculator, this))); this.hotkeysService.add(new Hotkey("alt+d", AppComponent.onHotkey(this.cloneCalculator, this))); this.hotkeysService.add(new Hotkey("alt+enter", AppComponent.onHotkey(this.doCompute, this))); this.hotkeysService.add(new Hotkey("alt+1", AppComponent.onHotkey(() => { this.appComponent.scrollToQuicknav("input"); }, this))); this.hotkeysService.add(new Hotkey("alt+2", AppComponent.onHotkey(() => { this.appComponent.scrollToQuicknav("results"); }, this))); this.hotkeysService.add(new Hotkey("alt+3", AppComponent.onHotkey(() => { this.appComponent.scrollToQuicknav("charts"); }, this))); } public get formElements(): FormulaireElement[] { if (this._formulaire === undefined) { return []; }
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
return this._formulaire.kids as FormulaireElement[]; } /** * détermine si un FormulaireElement est du type FieldSet */ public isFieldset(fe: any): boolean { return fe instanceof FieldSet; } /** * détermine si un FormulaireElement est du type FieldsetContainer */ public isFieldsetContainer(fe: any): boolean { return fe instanceof FieldsetContainer; } /** * détermine si un FormulaireElement est du type PabTable */ public isPabTable(fe: any): boolean { return fe instanceof PabTable; } public get hasForm() { return this._formulaire !== undefined; } public get hasResults(): boolean { if (this.hasForm) { return this._formulaire.hasResults; } return false; } public get uitextTitre() { if (this.hasForm) { return this.formulaireService.getLocalisedTitleFromCalculatorType(this._formulaire.calculatorType); } } public get uitextCalculer() { return this.intlService.localizeText("INFO_CALCULATOR_CALCULER"); } public get uitextCalculatorName() { return this.intlService.localizeText("INFO_CALCULATOR_CALC_NAME"); } public get uitextResultsTitle() { return this.intlService.localizeText("INFO_CALCULATOR_RESULTS_TITLE"); } public get uitextGeneratePAB() { return this.intlService.localizeText("INFO_CALCULATOR_RESULTS_GENERATE_PAB"); } public get uitextGenerateSPAmont() { return this.intlService.localizeText("INFO_CALCULATOR_RESULTS_GENERATE_SP_AMONT"); } public get uitextGenerateSPAval() { return this.intlService.localizeText("INFO_CALCULATOR_RESULTS_GENERATE_SP_AVAL"); } public get uitextOpenHelp() { return this.intlService.localizeText("INFO_CALCULATOR_OPEN_HELP"); } public get uitextCloneCalculator() {
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
return this.intlService.localizeText("INFO_CALCULATOR_CLONE"); } public get uitextSaveCalculator() { return this.intlService.localizeText("INFO_CALCULATOR_SAVE"); } public get uitextCloseCalculator() { return this.intlService.localizeText("INFO_CALCULATOR_CLOSE"); } public get uitextUsedBy() { return this.intlService.localizeText("INFO_CALCULATOR_USED_BY"); } public get quicknavItems() { const elts = [ "input", "results" ]; if (this.isWide && this.hasResults) { elts.push("charts"); } return elts; } /** * Triggered at calculator instanciation */ ngOnInit() { this.formulaireService.addObserver(this); this.subscribeRouter(); } ngDoCheck() { this.isCalculateDisabled = ! this._isUIValid; } ngOnDestroy() { this.unsubscribeRouter(); this.formulaireService.removeObserver(this); this.intlService.removeObserver(this); } private subscribeRouter() { // récupération des paramètres passés avec la route (URL) this._subscription = this.route.params.subscribe(params => { const uid: string = params["uid"]; this.formulaireService.setCurrentForm(uid); }); } private unsubscribeRouter() { if (this._subscription) { this._subscription.unsubscribe(); } } /* * gestion des événements clic sur les radios */ public onRadioClick(info: any) { if (info.param.valueMode === ParamValueMode.LINK) { this.updateLinkedParameters(); // only when switching to LINK mode } this._pendingRadioClick = true; this._pendingRadioClickInfo = info; } public ngAfterViewChecked() { if (this._pendingRadioClick) { this._pendingRadioClick = false; this._formulaire.onRadioClick(this._pendingRadioClickInfo);
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
this._pendingRadioClickInfo = undefined; } } public onCloseForm() { this.formulaireService.requestCloseForm(this._formulaire.uid); } /** * met à jour les paramètres liés */ private updateLinkedParameters() { this._fieldsetComponents.forEach(fsc => fsc.updateLinkedParameters()); this._fieldsetContainerComponents.forEach(fscc => fscc.updateLinkedParameters()); } public doCompute() { this._formulaire.resetResults([]); this.appComponent.showProgressBar = true; this._computeClicked = true; setTimeout(() => { this._formulaire.doCompute(); this.appComponent.showProgressBar = false; }, 100); // 100ms is important, a lower value may make the progress bar never appear :/ } public onCalcResultsViewChecked() { if (this._computeClicked) { this._computeClicked = false; this.scrollToResults(); } } /** open calculator identified by given UID */ public toCalc(id: string) { this.appComponent.toCalc(id); } /** list of calculators having a link pointing to the current calculator */ public get calculatorsUsingThisOne(): any { const sources = Session.getInstance().getDependingNubs(this._formulaire.currentNub.uid, undefined, true); return sources.map((s) => { return { label: this.formulaireService.getFormulaireFromNubId(s.uid).calculatorName, uid: s.uid }; }); } /** * Moves the view to the start of the "Results" section */ private scrollToResults() { // try to scroll to quicknav "results" first try { this.appComponent.scrollToQuicknav("results"); } catch (e) { const element = document.getElementById ("fake-results-anchor"); if (element) { element.scrollIntoView(); } } } private setForm(f: FormulaireDefinition) { if (this._formulaire !== undefined) { this._formulaire.removeObserver(this); } this._formulaire = f; if (this._formulaire !== undefined) {
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
this._formulaire.addObserver(this); } } private updateFormulaireResults(uid: string) { if (this._formulaire.uid === uid) { this.resultsComponent.updateView(); } } // interface Observer update(sender: any, data: any): void { if (sender instanceof FormulaireService) { switch (data["action"]) { case "currentFormChanged": const uid: string = data["formId"]; this.setForm(this.formulaireService.getFormulaireFromId(uid)); this.resultsComponent.formulaire = this._formulaire; this._calculatorNameComponent.model = this._formulaire; // reload localisation in all cases this.formulaireService.loadUpdateFormulaireLocalisation(this._formulaire); break; } } else if (sender instanceof FormulaireDefinition) { let f: FormulaireDefinition; switch (data["action"]) { case "resetForm": // réinitialisation du formulaire case "resultsUpdated": f = sender as FormulaireDefinition; this.updateFormulaireResults(f.uid); break; } } } /** * appelé après le 1er affichage du composant */ protected afterFirstViewChecked() { this.updateUIValidity(); } /** * calcul de la validité globale de la vue */ private updateUIValidity() { this._isUIValid = false; if (this._fieldsetComponents !== undefined) { this._isUIValid = this._fieldsetComponents.reduce( // callback ( // accumulator (valeur précédente du résultat) acc, // currentValue (élément courant dans le tableau) fieldset, // currentIndex (indice courant dans le tableau) currIndex, // array (tableau parcouru) array ) => { return acc && fieldset.isValid; } // valeur initiale , this._fieldsetComponents.length > 0); } if (this._fieldsetContainerComponents !== undefined) { this._isUIValid = this._isUIValid && this._fieldsetContainerComponents.reduce<boolean>(
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
// callback ( // accumulator (valeur précédente du résultat) acc, // currentValue (élément courant dans le tableau) fieldsetContainer, // currentIndex (indice courant dans le tableau) currIndex, // array (tableau parcouru) array ): boolean => { return acc && fieldsetContainer.isValid; } // valeur initiale , true); } if (this._pabTableComponent !== undefined) { this._isUIValid = this._isUIValid && this._pabTableComponent.isValid; } } public getElementStyleDisplay(id: string) { const isDisplayed: boolean = this._formulaire.isDisplayed(id); return isDisplayed ? "block" : "none"; } /** * réception d'un événement de validité d'un FormElement */ public onElementValid() { this.updateUIValidity(); } /** * réception d'un événement de changement de valeur d'un input */ public onInputChange(event: any) { this._formulaire.resetResults([], (event ? event.symbol : undefined)); // to refresh log components, that are fed manually (!) this.resultsComponent.updateView(); } /** * Passe d'un champ de saisie à l'autre lors de l'appui sur * TAB, sans passer par les autres éléments d'interface */ public onTabPressed(event: any) { // event source input id const srcId = event.originalEvent.target.id; // find all available inputs const qs = "ngparam-input input.form-control"; let inputs: Node[] = Array.from(this._elementRef.nativeElement.querySelectorAll(qs)); // find calculated symbol if any, to exclude it from inputs list let calcSymbol = ""; if (this._formulaire.currentNub.calculatedParam) { calcSymbol = this._formulaire.currentNub.calculatedParam.symbol; } inputs = inputs.filter((i: any) => { return i.id !== calcSymbol; }); // find position among inputs list const currentIndex = inputs.findIndex((i: any) => { return i.id === srcId; }); // focus previous / next input
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
let newIndex = currentIndex; if (event.shift) { newIndex = (newIndex === 0) ? (inputs.length - 1) : (newIndex - 1); } else { newIndex = (newIndex + 1) % inputs.length; } const elt = (inputs[newIndex] as HTMLInputElement); elt.focus(); elt.select(); } public openHelp() { window.open("assets/docs-fr/calculators/" + this._formulaire.helpLink, "_blank"); } /** * flag d'affichage du bouton d'aide */ public get enableHelpButton() { return this._formulaire && this._formulaire.helpLink; } /** return true if current Nub type is ct */ public is(ct: CalculatorType): boolean { return ( this._formulaire && this._formulaire.currentNub && this._formulaire.currentNub.calcType === ct ); } // for "one wide column" layout public get isWide() { return (this.isPAB || this.isMRC); } // true if current Nub is PAB public get isPAB() { return this.is(CalculatorType.Pab); } // true if current Nub is MacroRugoCompound public get isMRC() { return this.is(CalculatorType.MacroRugoCompound); } // true if current Nub is Jet public get isJet() { return this.is(CalculatorType.Jet); } // for "generate PAB" button public get isPABCloisons() { return this.is(CalculatorType.Cloisons); } // true if current Nub is Bief public get isBief() { return this.is(CalculatorType.Bief); } /** * Returns true if no parameter is varying */ private allParamsAreFixed() { let ret = true; for (const p of this._formulaire.currentNub.parameterIterator) { if (p.valueMode === ParamValueMode.LINK) { ret = ret && (! p.hasMultipleValues); } else {
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
// avoid calling hasMultipleValues here, because changing parameter mode in GUI // switches valueMode before setting min/max/step or valuesList, and iterator // checker fails to count values that do not exist yet ret = ret && (! [ ParamValueMode.LISTE, ParamValueMode.MINMAX ].includes(p.valueMode)); } } return ret; } // PAB generation is enabled only if : // - no parameter is varying // - Cloisons is calculated first public get generatePABEnabled(): boolean { return this.allParamsAreFixed() && this.hasResults; } public get uitextGeneratePabTitle() { if (! this.hasResults) { return this.intlService.localizeText("INFO_CALCULER_D_ABORD"); } if (! this.allParamsAreFixed) { return this.intlService.localizeText("INFO_PAB_PARAMETRES_FIXES"); } return ""; } /** * Génère une passe à bassins à partir du modèle de cloisons en cours */ public generatePAB() { // création du dialogue de génération d'une passe à bassin const cloisons = (this._formulaire.currentNub as Cloisons); const dialogRef = this.generatePABDialog.open( DialogGeneratePABComponent, { data: { chute: cloisons.prms.DH.V, debit: cloisons.prms.Q.V, coteAmont: cloisons.prms.Z1.V }, disableClose: false } ); dialogRef.afterClosed().subscribe(result => { if (result) { if (result.generate) { this.formulaireService.createFormulaire(CalculatorType.Pab).then((f: FormulaireDefinition) => { const pab = (f.currentNub as Pab); const params = pab.prms; // paramètres hydrauliques params.Q.singleValue = result.debit; params.Z1.singleValue = result.coteAmont; params.Z2.singleValue = result.coteAval; // création des bassins pab.deleteChild(0); pab.addCloisonsFromModel(this._formulaire.currentNub as Cloisons, result.nbBassins); // go to new PAB this.router.navigate(["/calculator", f.uid]); }); } } }); } /** * Génère une SectionParametree à partir des cotes amont du module Bief en cours */ public generateSPAmont() { const bief = (this._formulaire.currentNub as Bief); this.generateSP(round(bief.prms.Z1.singleValue - bief.prms.ZF1.singleValue, 3));
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
} /** * Génère une SectionParametree à partir des cotes aval du module Bief en cours */ public generateSPAval() { const bief = (this._formulaire.currentNub as Bief); this.generateSP(round(bief.prms.Z2.singleValue - bief.prms.ZF2.singleValue, 3)); } /** * Génère une SectionParametree à partir du module Bief en cours * @param Y tirant d'eau */ private generateSP(Y: number) { const bief = (this._formulaire.currentNub as Bief); const pente = round( (bief.prms.ZF1.singleValue - bief.prms.ZF2.singleValue) / bief.prms.Long.singleValue, 5 // ça ou autre chose… ); const serialisedSection = bief.section.serialise(); const sectionCopy = Session.getInstance().unserialiseSingleNub(serialisedSection, false).nub; const secParam = new SectionParametree(sectionCopy as acSection); Session.getInstance().registerNub(secParam); this.formulaireService.createFormulaire(CalculatorType.SectionParametree, secParam) .then((f: FormulaireDefinition) => { const sp = (f.currentNub as SectionParametree); sp.section.prms.Y.singleValue = Y; sp.section.prms.If.singleValue = pente; // calculate f.doCompute(); // go to new SP this.router.navigate(["/calculator", f.uid]).then(); } ); } public saveCalculator() { this.formulaireService.saveForm(this._formulaire); } public closeCalculator() { const dialogRef = this.confirmCloseCalcDialog.open( DialogConfirmCloseCalcComponent, { data: { uid: this._formulaire.currentNub.uid }, disableClose: true } ); dialogRef.afterClosed().subscribe(result => { if (result) { this.onCloseForm(); } }); } /** * Duplicates the current calculator form */ public cloneCalculator() { const serialisedNub: string = this._formulaire.currentNub.serialise({ title: this._formulaire.calculatorName }); const nubPointer = Session.getInstance().unserialiseSingleNub(serialisedNub); this.formulaireService.createFormulaire(nubPointer.nub.calcType, nubPointer.nub, nubPointer.meta.title).then((f) => { this.router.navigate(["/calculator", f.uid]); }); } }
701