diff --git a/spec/iterator/param_equation.spec.ts b/spec/iterator/param_equation.spec.ts index b8b881f063981292fdc837ce7af13eaccaf6bdab..40660ada8280255a156208939a42079e52c3da92 100644 --- a/spec/iterator/param_equation.spec.ts +++ b/spec/iterator/param_equation.spec.ts @@ -1,29 +1,54 @@ -/// <reference path="../../node_modules/@types/jasmine/index.d.ts" /> +/** + * IMPORTANT ! + * Décommenter temporairement la ligne suivante (import { } from "./mock_jasmine") + * Pour exécuter ce code dans le débugger. + * Faire de même avec le fichier test_func.ts + */ +// import { describe, expect, it, xdescribe, xit } from "../mock_jasmine"; import { ConduiteDistribParams, ConduiteDistrib } from "../../src/cond_distri"; +import { ParallelStructureParams } from "../../src/structure/parallel_structure_params"; +import { ParallelStructure } from "../../src/structure/parallel_structure"; +import { Structure } from "../../src/structure/structure"; +import { CreateStructure } from "../../src/structure/factory_structure"; +import { StructureType, LoiDebit } from "../../src/structure/structure_props"; +import { IParamDefinitionIterator } from "../../src/param/params-equation"; -describe("iterator ConduiteDistribParams : ", () => { - it("test 1", () => { - const cdp: ConduiteDistribParams = new ConduiteDistribParams(1, 2, 3, 4, 5) +function checkParams(pdi: IParamDefinitionIterator, symbols: string[], values: number[]) { + let n = 0; + for (const p of pdi) { + expect(p.symbol === symbols[n]); + if (n < values.length) + expect(p.v === values[n]); + n++; + } + expect(n === symbols.length).toBeTruthy(); +} - let n = 0; - for (let p of cdp) { - n++; - expect(p.v == n); - } - expect(n === 5); +describe("iterator : ", () => { + it("ConduiteDistribParams", () => { + const peq: ConduiteDistribParams = new ConduiteDistribParams(1, 2, 3, 4, 5); + const symbs = ["Q", "D", "J", "Lg", "Nu"]; + const vals = [1, 2, 3, 4, 5]; + checkParams(peq.iterator, symbs, vals); }); - it("test 2", () => { - const cdp: ConduiteDistribParams = new ConduiteDistribParams(1, 2, 3, 4, 5) + it("ParallelStructureParams 1", () => { + const peq: ParallelStructureParams = new ParallelStructureParams(1, 2, 3); + const symbs = ["Q", "Z1", "Z2"]; + const vals = [1, 2, 3]; + checkParams(peq.iterator, symbs, vals); + }); - const symbs = ["Q", "D", "J", "Lg", "Nu"]; - let n = 0; - for (const p of cdp.iterator) { - expect(p.v == n + 1); - expect(p.symbol == symbs[n]); - n++; - } - expect(n === 5).toBeTruthy(); + it("ParallelStructureParams 2", () => { + const psp: ParallelStructureParams = new ParallelStructureParams(1, 2, 3); + const pst = new ParallelStructure(psp); + + const st: Structure = CreateStructure(StructureType.SeuilRectangulaire, LoiDebit.Cem88d); + pst.addStructure(st); + + const symbs = ["Q", "Z1", "Z2", "Cd", "h1", "h2", "L", "Q", "W", "Z1", "Z2", "ZDV"]; + const vals = [1, 2, 3]; + checkParams(pst.parameterIterator, symbs, vals); }); }); diff --git a/spec/iterator/paramvalues_iterator.spec.ts b/spec/iterator/paramvalues_iterator.spec.ts index b92b76790b3116f423b8638b990bc6cb30ee7d47..238b64b503e76072787e6bb8e807fa1fb52fefe3 100644 --- a/spec/iterator/paramvalues_iterator.spec.ts +++ b/spec/iterator/paramvalues_iterator.spec.ts @@ -1,5 +1,3 @@ -/// <reference path="../../node_modules/@types/jasmine/index.d.ts" /> - import { ParamValues, ParamValueMode, ParamValueIterator } from "../../src/param/param-values" function checkNumberList(it: ParamValueIterator, exp: number[]) { @@ -14,7 +12,7 @@ function checkNumberList(it: ParamValueIterator, exp: number[]) { describe('paramvalues iterator : ', () => { it("vide (1)", () => { const p = new ParamValues(); - expect(() => p.getValuesIterator()).toThrow(new Error("ParamValueIterator : mode de génération de valeurs CALCUL incorrect")); + expect(() => p.getValuesIterator()).toThrow(new Error("ParamValues : mode de valeurs undefined incorrect")); }); it("vide (2)", () => { diff --git a/spec/structure/parallel_structure.spec.ts b/spec/structure/parallel_structure.spec.ts index 1f2d7d9b9ccbbd7204fe4e28ee0774d245b85bb1..78f1ca3edfe8a8443f4a7ebc9288c8fa7910d15a 100644 --- a/spec/structure/parallel_structure.spec.ts +++ b/spec/structure/parallel_structure.spec.ts @@ -7,7 +7,8 @@ // import { describe, expect, it, xdescribe, xit } from "../mock_jasmine"; import { ParamCalculability } from "../../src/param/param-definition"; -import { CreateStructure, loiAdmissibles, LoiDebit, StructureType } from "../../src/structure/factory_structure"; +import { CreateStructure } from "../../src/structure/factory_structure"; +import { LoiDebit, StructureType, loiAdmissibles } from "../../src/structure/structure_props"; import { ParallelStructure } from "../../src/structure/parallel_structure"; import { ParallelStructureParams } from "../../src/structure/parallel_structure_params"; import { Structure } from "../../src/structure/structure"; diff --git a/spec/structure/structure_kivi.spec.ts b/spec/structure/structure_kivi.spec.ts index 1db9a2b5335eb3dca76b5eb6a1d369e64eed6bd9..1286b02b87ea3183cbb4ea328586eccb8d205414 100644 --- a/spec/structure/structure_kivi.spec.ts +++ b/spec/structure/structure_kivi.spec.ts @@ -7,7 +7,8 @@ // import { describe, expect, it, xdescribe, xit } from "../mock_jasmine"; import { StructureFlowMode, StructureFlowRegime, MessageCode } from "../../src"; -import { CreateStructure, LoiDebit, StructureType } from "../../src/structure/factory_structure"; +import { CreateStructure } from "../../src/structure/factory_structure"; +import { LoiDebit, StructureType } from "../../src/structure/structure_props"; import { StructureKivi } from "../../src/structure/structure_kivi"; import { StructureKiviParams } from "../../src/structure/structure_kivi_params"; import { testStructure } from "./structure_test"; diff --git a/src/base.ts b/src/base.ts index 95f88b73cec1aa1c9a65082e35b2e22592df4ff9..7ab2bd593ae09221a212033076f395370f355f3a 100644 --- a/src/base.ts +++ b/src/base.ts @@ -1,10 +1,17 @@ /** * Gestion des messages de debogage dans la console - * @note Etendre cette classe pour toutes les classes à debugguer + * @note Implémenter cette interface pour toutes les classes à debugguer * Dans le constructeur, utiliser super(true) pour debugger la classe, super(false) sinon. */ + +export interface IDebug { + debug(s: any): void; + + readonly DBG: boolean; +} + // tslint:disable-next-line:max-classes-per-file -export abstract class Debug { +export class Debug { /** * @param _DBG Flag de débuggage */ diff --git a/src/compute-node.ts b/src/compute-node.ts index acc5e5b432f2a038355e24a2a00ba525bf24c1d6..88ff3470faae9b9fbb1d386699967982268c1966 100644 --- a/src/compute-node.ts +++ b/src/compute-node.ts @@ -1,7 +1,8 @@ -import { Debug } from "./base"; -import { ParamsEquation } from "./param/params-equation"; +import { Debug, IDebug } from "./base"; +import { ParamsEquation, IParamDefinitionIterator } from "./param/params-equation"; import { ParamDefinition } from "./param/param-definition"; import { ParamValueMode } from "./param/param-values"; +import { JalhydObject } from "./jalhyd_object"; /** * type de calculette @@ -22,20 +23,21 @@ export enum ComputeNodeType { // types de sections SectionTrapeze, SectionRectangle, SectionCercle, SectionPuissance, // types d'ouvrages hydrauliques - StructureRectangle, - // ouvrages hydrauliques : Kindsvater-Carter & Villemonte - StructureKIVI + StructureRectangle } /** * noeud de calcul */ // tslint:disable-next-line:max-classes-per-file -export abstract class ComputeNode extends Debug { +export abstract class ComputeNode extends JalhydObject implements IDebug { protected _prms: ParamsEquation; + private _debug: Debug; + constructor(prms: ParamsEquation, dbg: boolean = false) { - super(dbg); + super(); + this._debug = new Debug(dbg); this._prms = prms; if (!this._prms.calculabilityDefined) { this._prms.resetParametersCalculability(); @@ -44,24 +46,37 @@ export abstract class ComputeNode extends Debug { this._prms.DefineCalculability(); } + public get parameters(): ParamsEquation { + return this._prms; + } + public getParameter(name: string): ParamDefinition { - return this._prms.getParameter(name); + for (const p of this.parameterIterator) + if (p.symbol === name) + return p; + return undefined; } public getFirstAnalyticalParameter(): ParamDefinition { - return this._prms.getFirstAnalyticalParameter(); + for (const p of this.parameterIterator) + if (p.isAnalytical()) + return p; + return undefined; } - public initParametersValueMode(computedParam: ParamDefinition, variatedParam?: ParamDefinition, variatedMode?: ParamValueMode) { - for (const k in this._prms.map) { - if (k == computedParam.symbol) - this._prms.map[k].paramValues.valueMode = ParamValueMode.CALCUL; - else if (variatedParam && k == variatedParam.symbol) - this._prms.map[k].paramValues.valueMode = variatedMode; - else - this._prms.map[k].paramValues.valueMode = ParamValueMode.SINGLE; - } + public get parameterIterator(): IParamDefinitionIterator { + return this._prms.iterator; } protected abstract setParametersCalculability(): void; + + // interface IDebug + + debug(s: any) { + this._debug.debug(s); + } + + public get DBG(): boolean { + return this._debug.DBG; + } } diff --git a/src/dichotomie.ts b/src/dichotomie.ts index 568fdc977acef3867fb907c88d0f33111cb2166e..43a29c63b06331eb24da29326bf3979b2647aa47 100644 --- a/src/dichotomie.ts +++ b/src/dichotomie.ts @@ -118,6 +118,13 @@ export class Dichotomie extends Debug { */ private _startIntervalMaxSteps = 100; + /** + * nombre d'itérations maxi pour la recherche dichotomique + */ + private _maxIterations: number = 100; + + private _currentIterations: number; + /** * nom du paramètre calculé analytiquement par Equation() */ @@ -139,6 +146,10 @@ export class Dichotomie extends Debug { } } + public set maxIterations(n: number) { + this._maxIterations = Math.max(n, 1); + } + /** * Valeur inconnue à rechercher */ @@ -147,7 +158,7 @@ export class Dichotomie extends Debug { } private set vX(vCalc: number) { - this._paramX.v = vCalc; + this._paramX.setValue(vCalc); } /** @@ -188,6 +199,7 @@ export class Dichotomie extends Debug { const r = this.getStartInterval(rTarget, rInit); if (r.ok) { const interv: SearchInterval = r.intSearch; + this._currentIterations = 0; // Dichotomie return this.dichoSearch(rTarget, rTol, interv.min, interv.max, interv.targetLower, interv.targetUpper); } else { @@ -371,6 +383,13 @@ export class Dichotomie extends Debug { */ // tslint:disable-next-line:variable-name private dichoSearch(Ytarg: number, rTol: number, Xmin: number, Xmax: number, Ymin: number, Ymax: number): Result { + this._currentIterations++; + if (this._currentIterations > this._maxIterations) { + const r = new Result(); + r.globalLog.add(new Message(MessageCode.ERROR_DICHO_CONVERGE)); + return r; + } + this.debug("dichoSearch : yTarget " + Ytarg + " X " + Xmin + "->" + Xmax + " tol " + rTol); // tslint:disable-next-line:variable-name diff --git a/src/index.ts b/src/index.ts index 4cc0584dd590e9af70101e7e5ded51e27a3653dd..f904a4e2888d429cf7ea21fe4a2ed3d091308033 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,13 +5,15 @@ export * from "./param/param-domain"; export * from "./param/params-equation"; export * from "./param/param-values"; export * from "./compute-node"; -export * from "./parameters"; export * from "./nub"; +export * from "./nub_factory"; +export * from "./session_nub"; export * from "./cond_distri"; export * from "./dichotomie"; export * from "./lechaptcalmon"; export * from "./regime_uniforme"; export * from "./remous"; +export * from "./section/section_nub"; export * from "./section/section_type"; export * from "./section/section_trapez"; export * from "./section/section_rectang"; @@ -25,6 +27,7 @@ export * from "./util/result"; export * from "./util/resultelement"; export * from "./util/pair"; export * from "./util/interval"; +export * from "./util/observer"; export * from "./pab/pab_dimension"; export * from "./pab/pab_puissance"; export * from "./util/iterator"; @@ -34,4 +37,5 @@ export * from "./structure/parallel_structure_params"; export * from "./structure/structure"; export * from "./structure/structure_params"; export * from "./structure/factory_structure"; +export * from "./structure/structure_props"; export * from "./jalhyd_object"; diff --git a/src/nub.ts b/src/nub.ts index 6ed251bdaa9a0f77a2468aeefcfe08fcf9e1c39b..26f285efb765b6246d56e01cd459fdd35a023056 100644 --- a/src/nub.ts +++ b/src/nub.ts @@ -39,10 +39,12 @@ export abstract class Nub extends ComputeNode { * @param rPrec précision de calcul */ public Calc(sVarCalc: string, rInit?: number, rPrec: number = 0.001): Result { + const computedVar = this.getParameter(sVarCalc); + if (rInit === undefined) { - rInit = this._prms.map[sVarCalc].v; + rInit = computedVar.v; } - if (this._prms.map[sVarCalc].isAnalytical()) { + if (computedVar.isAnalytical()) { this._result = this.Equation(sVarCalc); return this._result; } @@ -53,21 +55,26 @@ export abstract class Nub extends ComputeNode { return this._result; } const sAnalyticalPrm: string = this.getFirstAnalyticalParameter().symbol; - this._prms.map[sVarCalc].v = resSolve.vCalc; + computedVar.setValue(resSolve.vCalc); const res: Result = this.Equation(sAnalyticalPrm); res.vCalc = resSolve.vCalc; this._result = res; return res; } - public CalcSerie(rPrec: number = 0.001, rInit?: number): Result { + /** + * effectue une série de calculs sur un paramètre + * @param rPrec précision de calcul + * @param rInit solution approximative du paramètre + * @param sDonnee éventuel symbole du paramètre à calculer + */ + public CalcSerie(rPrec: number = 0.001, rInit?: number, sDonnee?: string): Result { const res = new Result(); this._result = res; let variatedParam: ParamDefinition; let computedParam: ParamDefinition; - for (const k in this._prms.map) { - const p: ParamDefinition = this._prms.map[k]; + for (const p of this.parameterIterator) { switch (p.valueMode) { case ParamValueMode.LISTE: @@ -79,32 +86,41 @@ export abstract class Nub extends ComputeNode { break; case ParamValueMode.CALCUL: - if (computedParam == undefined) - computedParam = p; - else - throw new Error(`CalcSerie() : il y plusieurs paramètres à calculer (au moins ${computedParam.symbol} et ${p.symbol})`); + if (sDonnee == undefined) { + if (computedParam == undefined) + computedParam = p; + else + throw new Error(`CalcSerie() : il y plusieurs paramètres à calculer (au moins ${computedParam.symbol} et ${p.symbol})`); + } break; } } - if (computedParam == undefined) - throw new Error(`CalcSerie() : aucun paramètre à calculer`); + if (sDonnee) + var computedSymbol: string = sDonnee; + else { + if (computedParam == undefined) + throw new Error(`CalcSerie() : aucun paramètre à calculer`); + computedSymbol = computedParam.symbol; + } if (rInit === undefined) - rInit = this._prms.map[computedParam.symbol].v; + rInit = computedParam.v; if (variatedParam == undefined) - this.Calc(computedParam.symbol, rInit, rPrec); // résultat dans this._result + this._result = this.Calc(computedSymbol, rInit, rPrec); // résultat dans this._result else { const res = new Result(); variatedParam.paramValues.initIterator(); while (variatedParam.paramValues.hasNext) { variatedParam.paramValues.next; - this.Calc(computedParam.symbol, rInit, rPrec); // résultat dans this._result - res.addResultElement(this._result.resultElement); - res.addLog(this._result.log); - if (this._result.ok) + this.Calc(computedSymbol, rInit, rPrec); // résultat dans this._result + if (this._result.ok) { + res.addResultElement(this._result.resultElement); + res.addLog(this._result.log); rInit = this._result.resultElement.vCalc; + } + res.globalLog.addLog(this._result.globalLog); } this._result = res; } @@ -121,7 +137,7 @@ export abstract class Nub extends ComputeNode { private Solve(sVarCalc: string, rInit: number, rPrec: number): Result { const dicho: Dichotomie = new Dichotomie(this, sVarCalc, this.DBG); dicho.startIntervalMaxSteps = this._dichoStartIntervalMaxSteps; - const target = this._prms.getFirstAnalyticalParameter(); + const target = this.getFirstAnalyticalParameter(); return dicho.Dichotomie(target.v, rPrec, rInit); } diff --git a/src/nub_factory.ts b/src/nub_factory.ts new file mode 100644 index 0000000000000000000000000000000000000000..56a6a1287fde232880e405d22be2e21d9bf32455 --- /dev/null +++ b/src/nub_factory.ts @@ -0,0 +1,275 @@ +import { ComputeNodeType, CalculatorType } from "./compute-node" +import { Nub } from "./nub" +import { SessionNub, Props } from "./session_nub" +import { ConduiteDistribParams, ConduiteDistrib } from "./cond_distri"; +import { LechaptCalmonParams, LechaptCalmon } from "./lechaptcalmon"; + +import { acSection } from "./section/section_type"; +import { ParamsSectionTrapez, cSnTrapez } from "./section/section_trapez"; +import { ParamsSectionRectang, cSnRectang } from "./section/section_rectang"; +import { ParamsSectionCirc, cSnCirc } from "./section/section_circulaire"; +import { ParamsSectionPuiss, cSnPuiss } from "./section/section_puissance"; +import { SectionParametree } from "./section/section_nub"; +import { RegimeUniforme } from "./regime_uniforme"; +import { CourbeRemousParams, MethodeResolution, CourbeRemous } from "./remous"; +import { PabDimensionParams, PabDimension } from "./pab/pab_dimension"; +import { PabPuissance, PabPuissanceParams } from "./pab/pab_puissance"; +import { CreateStructure } from "./structure/factory_structure"; +import { StructureType, LoiDebit } from "./structure/structure_props"; +import { Structure } from "./structure/structure"; +import { ParallelStructure } from "./structure/parallel_structure"; +import { ParallelStructureParams } from "./structure/parallel_structure_params"; +import { RectangularStructureParams } from "./structure/structure_cem88d"; + +export class NubFactory { + private _defaultPrecision: number = 0.001; + + private static _instance: NubFactory; // instance pour le pattern singleton + + private _session: SessionNub[]; + + private constructor() { + this._session = []; + } + + public static getInstance() { + if (NubFactory._instance == undefined) + NubFactory._instance = new NubFactory(); + return NubFactory._instance; + } + + public setDefaultPrecision(p: number) { + this._defaultPrecision = p; + } + + private newSessionNub(p: Props | {}): SessionNub { + const params = p instanceof Props ? p : new Props(p); + const nub = this.createNub(params); + return new SessionNub(nub, params); + } + + /** + * créé un Nub et l'ajoute à la session + */ + public createSessionNub(p: Props | {}): SessionNub { + const res = this.newSessionNub(p); + this._session.push(res); + return res; + } + + public findSessionNub(params: Props | {}): SessionNub { + for (const n of this._session) + if (n.hasProperties(params)) + return n; + return undefined; + } + + private replaceStructureNub(oldNub: Nub, newNub: Nub) { + const b1 = oldNub instanceof Structure; + const b2 = newNub instanceof Structure; + if ((b1 && !b2) || (!b1 && b2)) + throw new Error(`NubFactory.replaceStructureNub() : arguments incorrects (${typeof (oldNub)}/${typeof (newNub)}`); + if (b1 && b2) { + for (const sn of this._session) + if (sn.nub instanceof ParallelStructure) { + const psn = sn.nub as ParallelStructure; + let i = 0; + for (const st of psn.structures) { + if (st.uid == oldNub.uid) { + psn.replaceStructure(i, newNub as Structure); + return; + } + i++; + } + } + throw new Error(`NubFactory.replaceStructureNub() : la structure (uid ${oldNub.uid}) à remplacer n'a pas été trouvée`); + } + } + + /** + * remplace un SessionNub par un nouveau + * @param sn SessionNub à remplacer + * @param params propriété du nouveau SessionNub + */ + public replaceSessionNub(sn: SessionNub, params: Props): SessionNub { + let i = 0; + for (const n of this._session) { + if (n.uid == sn.uid) { + const newNub = this.newSessionNub(params); + this._session[i] = newNub; + this.replaceStructureNub(sn.nub, newNub.nub); + return newNub; + } + i++; + } + + // pas trouvé + return undefined; + } + + /** + * créé un Nub + * @param calcType type de Nub + * @param nodeType sous type de Nub + * @param params paramètres supplémentaires spécifiques + */ + private createNub(params: Props): Nub { + const calcType: CalculatorType = params.getPropValue("calcType"); + const nodeType: ComputeNodeType = params.getPropValue("nodeType"); + + switch (calcType) { + case CalculatorType.ConduiteDistributrice: + { + const prms = new ConduiteDistribParams(3, // débit Q + 1.2, // diamètre D + 0.6, // perte de charge J + 100, // Longueur de la conduite Lg + 1e-6, // Viscosité dynamique Nu + ); + + return new ConduiteDistrib(prms); + } + + case CalculatorType.LechaptCalmon: + { + const prms = new LechaptCalmonParams(3, // débit + 1.2, // diamètre + 0.6, /// perte de charge + 100, // longueur du toyo + 1.863, // paramètre L du matériau + 2, // paramètre M du matériau + 5.33// paramètre N du matériau + ); + return new LechaptCalmon(prms); + } + + case CalculatorType.SectionParametree: + return new SectionParametree(this.createSection(nodeType)); + + case CalculatorType.RegimeUniforme: + const sect: acSection = this.createSection(nodeType); + const ru = new RegimeUniforme(sect); + return ru; + + + case CalculatorType.CourbeRemous: + { + const sect: acSection = this.createSection(nodeType); + const prms = new CourbeRemousParams(sect, 0.15, // Yamont = tirant amont + 0.4, // Yaval = tirant aval + 100, // Long= Longueur du bief + 5, // Dx=Pas d'espace + MethodeResolution.EulerExplicite + ); + return new CourbeRemous(prms); + } + + case CalculatorType.PabDimensions: + { + const prms = new PabDimensionParams( + 2, // Longueur L + 1, // Largeur W + 0.5, // Tirant d'eau Y + 2 // Volume V + ); + return new PabDimension(prms); + } + + case CalculatorType.PabPuissance: + { + const prms = new PabPuissanceParams( + 0.3, // Chute entre bassins DH (m) + 0.1, // Débit Q (m3/s) + 0.5, // Volume V (m3) + 588.6 // Puissance dissipée Pv (W/m3) + ); + return new PabPuissance(prms); + } + + case CalculatorType.Structure: + const structType: StructureType = params.getPropValue("structureType"); + const loiDebit: LoiDebit = params.getPropValue("loiDebit"); + return CreateStructure(structType, loiDebit); + + case CalculatorType.ParallelStructure: + { + const prms = new ParallelStructureParams(0.5, // Q + 102, // Z1 + 101.5 // Z2 + ); + return new ParallelStructure(prms); + } + + default: + throw new Error(`NubFactory.createNub() : calculatrice '${CalculatorType[calcType]}' / noeud de calcul '${ComputeNodeType[nodeType]}' non pris en charge`); + } + } + + private createSection(nt: ComputeNodeType): acSection { + switch (nt) { + case ComputeNodeType.None: // pour les paramètres communs, n'importe quelle section convient + case ComputeNodeType.SectionTrapeze: + { + const prms = new ParamsSectionTrapez(2.5, // largeur de fond + 0.56, // fruit + 0.8, // tirant d'eau + 40, // Ks=Strickler + 1.2, // Q=Débit + 0.001, // If=pente du fond + this._defaultPrecision, // précision + 1, // YB= hauteur de berge + ); + + return new cSnTrapez(prms); + } + + + case ComputeNodeType.SectionRectangle: + { + const prms = new ParamsSectionRectang(0.8, // tirant d'eau + 2.5, // largeur de fond + 40, // Ks=Strickler + 1.2, // Q=Débit + 0.001, // If=pente du fond + this._defaultPrecision, // précision + 1 // YB=hauteur de berge + ); + return new cSnRectang(prms); + } + + case ComputeNodeType.SectionCercle: + { + const prms = new ParamsSectionCirc(2, // diamètre + 0.8, // tirant d'eau + 40, // Ks=Strickler + 1.2, // Q=Débit + 0.001, // If=pente du fond + this._defaultPrecision, // précision + 1, // YB= hauteur de berge + ); + return new cSnCirc(prms); + } + + case ComputeNodeType.SectionPuissance: + { + const prms = new ParamsSectionPuiss(0.5, // coefficient + 0.8, // tirant d'eau + 4, // largeur de berge + 40, // Ks=Strickler + 1.2, // Q=Débit + 0.001, // If=pente du fond + this._defaultPrecision, // précision + 1, // YB= hauteur de berge + ); + return new cSnPuiss(prms); + } + + default: + throw new Error(`type de section ${ComputeNodeType[nt]} non pris en charge`); + } + } + + // public get sessionNubIterator(): IterableIterator<SessionNub> { + // return this._session[Symbol.iterator](); // crée un itérateur à partir d'un tableau + // } +} \ No newline at end of file diff --git a/src/param/param-base.ts b/src/param/param-base.ts index b5ee3c26c9f58f70d47e8a1af193afe98f7f80cd..6731c4566b1389a5a2526233db51ab429c055d64 100644 --- a/src/param/param-base.ts +++ b/src/param/param-base.ts @@ -31,7 +31,9 @@ export class BaseParam extends JalhydObject { this._symbol = symb; this._paramValues = new ParamValues(); - this._paramValues.singleValue = val; + this._paramValues.setSingleValue(val); + if (val !== undefined) + this._paramValues.valueMode = ParamValueMode.SINGLE; if (d instanceof ParamDomain) { this._domain = d; @@ -78,9 +80,7 @@ export class BaseParam extends JalhydObject { public setValue(val: number) { this.checkValue(val); - this._paramValues.singleValue = val; - - // console.log("setting param " + this._symbol + " id=" + this._id + " to " + val); // A VIRER + this._paramValues.setSingleValue(val); } public get uncheckedValue(): number { diff --git a/src/param/param-values.ts b/src/param/param-values.ts index 62d132ba666f730aec92977eab55e5a1f088b545..3793c823327e4d88396f1afce8de97590e1f546f 100644 --- a/src/param/param-values.ts +++ b/src/param/param-values.ts @@ -211,23 +211,22 @@ export class ParamValues { constructor() { this._singleValue = new DefinedNumber(); - this.valueMode = ParamValueMode.CALCUL; } public setValues(o: number | any, max?: number, step?: number) { if (typeof (o) === "number") { if (max == undefined) { - this.valueMode = ParamValueMode.SINGLE; + this._valueMode = ParamValueMode.SINGLE; this._singleValue.value = o as number; } else { - this.valueMode = ParamValueMode.MINMAX; + this._valueMode = ParamValueMode.MINMAX; this._minValue = o as number; this._maxValue = max; this._stepValue = step; } } else if (Array.isArray(o)) { - this.valueMode = ParamValueMode.LISTE; + this._valueMode = ParamValueMode.LISTE; this._valueList = o; } else @@ -286,9 +285,8 @@ export class ParamValues { return this._singleValue.uncheckedValue; } - public set singleValue(v: number) { + public setSingleValue(v: number) { this._singleValue.value = v; - this.valueMode = ParamValueMode.SINGLE; } public get isDefined() { @@ -301,7 +299,7 @@ export class ParamValues { public set min(v: number) { this._minValue = v; - this.valueMode = ParamValueMode.MINMAX; + this._valueMode = ParamValueMode.MINMAX; } public get max() { @@ -310,7 +308,7 @@ export class ParamValues { public set max(v: number) { this._maxValue = v; - this.valueMode = ParamValueMode.MINMAX; + this._valueMode = ParamValueMode.MINMAX; } public get stepRefValue(): Pair { @@ -325,7 +323,7 @@ export class ParamValues { public set step(v: number) { this._stepValue = v; - this.valueMode = ParamValueMode.MINMAX; + this._valueMode = ParamValueMode.MINMAX; } public get valueList() { @@ -335,7 +333,7 @@ export class ParamValues { public set valueList(l: number[]) { this._valueList = l; - this.valueMode = ParamValueMode.LISTE; + this._valueMode = ParamValueMode.LISTE; } /** @@ -378,17 +376,4 @@ export class ParamValues { this._singleValue.value = this._iterator.next().value; return this._singleValue.value; } - - /** - * copie des membres - */ - public copyMembers(n: ParamValues) { - n._valueMode = this.valueMode; - n._singleValue = new DefinedNumber(this._singleValue.value); - n._minValue = this._minValue; - n._maxValue = this._maxValue; - n._stepValue = this._stepValue; - if (this._valueList != undefined) - n._valueList = this._valueList.slice(0); // copie - } } diff --git a/src/param/params-equation.ts b/src/param/params-equation.ts index 94ea6bcd25ed57b07cd2d2cb1843f5797fc45568..e84f4525aa3d7bde134836dc269a3034fc65b846 100644 --- a/src/param/params-equation.ts +++ b/src/param/params-equation.ts @@ -2,6 +2,79 @@ import { MapIterator } from "../util/iterator" import { ParamDefinition } from "./param-definition"; +export interface IParamDefinitionIterator extends IterableIterator<ParamDefinition> { +} + +/** + * itérateur sur les paramètres d'une seule instance de ParamsEquation + */ +export class ParamDefinitionIterator implements IParamDefinitionIterator { + private _mapIterator: MapIterator<ParamDefinition>; + + constructor(_params: ParamsEquation) { + this._mapIterator = new MapIterator(_params.map); + } + + public next(): IteratorResult<ParamDefinition> { + return this._mapIterator.next(); + } + + public [Symbol.iterator](): IterableIterator<ParamDefinition> { + return this; + } +} + +/** + * itérateur sur les paramètres d'un tableau de de ParamsEquation + */ +export class ParamsEquationArrayIterator implements IParamDefinitionIterator { + private _paramsEqs: ParamsEquation[]; + + private _index: number = 0; + + private _currentMapIterator: MapIterator<ParamDefinition>; + + constructor(p: ParamsEquation[]) { + this._paramsEqs = p; + } + + private nextIterator() { + if (this._index < this._paramsEqs.length) + this._currentMapIterator = new MapIterator(this._paramsEqs[this._index++].map); + else + this._currentMapIterator = undefined; + } + + private get done(): IteratorResult<ParamDefinition> { + return { + done: true, + value: undefined + }; + } + + public next(): IteratorResult<ParamDefinition> { + if (this._currentMapIterator == undefined) + this.nextIterator(); + + if (this._currentMapIterator) { + var res = this._currentMapIterator.next(); + if (res.done) { + this.nextIterator(); + if (this._currentMapIterator) + res = this._currentMapIterator.next(); + } + } + else + res = this.done; + + return res; + } + + public [Symbol.iterator](): IterableIterator<ParamDefinition> { + return this; + } +} + /** * liste des paramètres d'une équation */ @@ -24,67 +97,27 @@ export abstract class ParamsEquation implements Iterable<ParamDefinition> { } public hasParameter(name: string): boolean { - for (const ps in this._paramMap) { - if (this._paramMap.hasOwnProperty(ps)) { - const p: ParamDefinition = this._paramMap[ps]; - if (p.symbol === name) { - return true; - } - } - } - + for (const p of this) + if (p.symbol === name) + return true; return false; } - public getParameter(name: string): ParamDefinition { - for (const ps in this._paramMap) { - if (this._paramMap.hasOwnProperty(ps)) { - const p: ParamDefinition = this._paramMap[ps]; - if (p.symbol === name) { - return p; - } - } - } - - throw new Error("ParamsEquation.getParameter() : invalid parameter name " + name); - } - - public getFirstAnalyticalParameter(): ParamDefinition { - for (const ps in this._paramMap) { - if (this._paramMap.hasOwnProperty(ps)) { - const p: ParamDefinition = this._paramMap[ps]; - if (p.isAnalytical()) { - return p; - } - } - } - return undefined; - } - public get map(): { [key: string]: ParamDefinition } { return this._paramMap; } public resetParametersCalculability() { - for (const ps in this._paramMap) { - if (this._paramMap.hasOwnProperty(ps)) { - const p: ParamDefinition = this._paramMap[ps]; - p.calculability = undefined - } - } + for (const p of this) + p.calculability = undefined } public checkParametersCalculability() { const res = []; - for (const ps in this._paramMap) { - if (this._paramMap.hasOwnProperty(ps)) { - const p: ParamDefinition = this._paramMap[ps]; - if (p.calculability === undefined) { - res.push(p.symbol); - } - } - } + for (const p of this) + if (p.calculability === undefined) + res.push(p.symbol); if (res.length > 0) { throw new Error("Calculability of parameter(s) " + res.toString() + " has not been defined"); @@ -95,8 +128,8 @@ export abstract class ParamsEquation implements Iterable<ParamDefinition> { return this.iterator; } - public get iterator() { - return new MapIterator(this._paramMap); + public get iterator(): IParamDefinitionIterator { + return new ParamDefinitionIterator(this); } protected addParamDefinition(p: ParamDefinition) { @@ -106,10 +139,7 @@ export abstract class ParamsEquation implements Iterable<ParamDefinition> { } protected addParamDefinitions(ps: ParamsEquation) { - for (const pi in ps._paramMap) { - if (ps._paramMap.hasOwnProperty(pi)) { - this.addParamDefinition(ps._paramMap[pi]); - } - } + for (const p of ps) + this.addParamDefinition(p); } } diff --git a/src/parameters.ts b/src/parameters.ts deleted file mode 100644 index 88275fa9623a42e5d2d063966277e464fbf23b2e..0000000000000000000000000000000000000000 --- a/src/parameters.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { ComputeNodeType, CalculatorType } from "./compute-node" -import { ParamsEquation } from "./param/params-equation"; -import { ParamDefinition } from "./param/param-definition"; -import { ConduiteDistribParams, ConduiteDistrib } from "./cond_distri"; -import { LechaptCalmonParams, LechaptCalmon } from "./lechaptcalmon"; -import { acSection } from "./section/section_type"; -import { ParamsSectionTrapez, cSnTrapez } from "./section/section_trapez"; -import { ParamsSectionRectang, cSnRectang } from "./section/section_rectang"; -import { ParamsSectionCirc, cSnCirc } from "./section/section_circulaire"; -import { ParamsSectionPuiss, cSnPuiss } from "./section/section_puissance"; -import { RegimeUniforme } from "./regime_uniforme"; -import { CourbeRemous, CourbeRemousParams } from "./remous"; -import { PabDimensionParams, PabDimension } from "./pab/pab_dimension"; -import { PabPuissanceParams, PabPuissance } from "./pab/pab_puissance"; -import { HashTable } from "./util/hashtable"; -import { ParallelStructureParams } from "./structure/parallel_structure_params"; -import { ParallelStructure } from "./structure/parallel_structure"; -import { RectangularStructureParams } from "./structure/rectangular_structure_params"; -import { StructureCem88d } from "./structure/structure_cem88d"; -import { StructureKiviParams } from "./structure/structure_kivi_params"; -import { StructureKivi } from "./structure/structure_kivi"; - - -export class ComputeNodeParameters { - private static _instance: ComputeNodeParameters; - - private _nodes: HashTable; - - private constructor() { - this._nodes = new HashTable(); - } - - public static getInstance() { - if (ComputeNodeParameters._instance == undefined) - ComputeNodeParameters._instance = new ComputeNodeParameters(); - return ComputeNodeParameters._instance; - } - - private createSection(nt: ComputeNodeType): acSection { - switch (nt) { - case ComputeNodeType.None: // pour les paramètres communs, n'importe quelle section convient - case ComputeNodeType.SectionTrapeze: - { - let cn = new ParamsSectionTrapez(1, 0.5, undefined, undefined, - 1, undefined, 0.1, 1); - let n = new cSnTrapez(cn); // pour initialiser la calculabilité des paramètres - return n; - } - - case ComputeNodeType.SectionRectangle: - { - let cn = new ParamsSectionRectang(undefined, 1, undefined, 1, - undefined, 0.1, 1); - let n = new cSnRectang(cn); // pour initialiser la calculabilité des paramètres - return n; - } - - case ComputeNodeType.SectionCercle: - { - let cn = new ParamsSectionCirc(1, undefined, undefined, 1, - undefined, 0.1, 1); - let n = new cSnCirc(cn); // pour initialiser la calculabilité des paramètres - return n; - } - - case ComputeNodeType.SectionPuissance: - { - let cn = new ParamsSectionPuiss(0.5, undefined, 1, undefined, - 1, undefined, 0.1, 1); - let n = new cSnPuiss(cn); // pour initialiser la calculabilité des paramètres - return n; - } - - default: - throw new Error(`type de section ${ComputeNodeType[nt]} non pris en charge`); - } - } - - private createComputeNodeParameters(calcType: CalculatorType, nodeType: ComputeNodeType): ParamsEquation { - switch (calcType) { - case CalculatorType.ConduiteDistributrice: - { - const cn = new ConduiteDistribParams(undefined, undefined, undefined, undefined, undefined); - const n = new ConduiteDistrib(cn); // pour initialiser la calculabilité des paramètres - return cn; - } - - case CalculatorType.LechaptCalmon: - { - const cn = new LechaptCalmonParams(undefined, undefined, undefined, undefined, undefined, undefined, undefined); - const n = new LechaptCalmon(cn); // pour initialiser la calculabilité des paramètres - return cn; - } - - case CalculatorType.SectionParametree: - { - const sect: acSection = this.createSection(nodeType); - return sect.prms; - } - - case CalculatorType.RegimeUniforme: - const sect: acSection = this.createSection(nodeType); - const ru = new RegimeUniforme(sect); // pour initialiser la calculabilité des paramètres - return sect.prms; - - - case CalculatorType.CourbeRemous: - { - const sect: acSection = this.createSection(nodeType); - const crp = new CourbeRemousParams(sect, undefined, undefined, undefined, undefined, undefined); - const ru = new CourbeRemous(crp); // pour initialiser la calculabilité des paramètres - return crp; - } - - case CalculatorType.PabDimensions: - { - let cn = new PabDimensionParams(undefined, undefined, undefined); - let n = new PabDimension(cn); // pour initialiser la calculabilité des paramètres - return cn; - } - - case CalculatorType.PabPuissance: - { - let cn = new PabPuissanceParams(undefined, undefined, undefined); - let n = new PabPuissance(cn); // pour initialiser la calculabilité des paramètres - return cn; - } - - case CalculatorType.Structure: - switch (nodeType) { - case ComputeNodeType.None: // pour les paramètres communs, n'importe quelle structure convient - case ComputeNodeType.StructureRectangle: - let cn = new RectangularStructureParams(undefined, 0.5, 2, 1, undefined, undefined); - let n = new StructureCem88d(cn); - return cn; - - case ComputeNodeType.StructureKIVI: - { - let cn = new StructureKiviParams(undefined, 1, 2, 1, undefined, undefined, undefined, undefined); - let n = new StructureKivi(cn); - return cn; - } - - default: - throw new Error(`ComputeNodeParameters.createComputeNodeParameters() : calculatrice '${CalculatorType[calcType]}' / noeud de calcul '${ComputeNodeType[nodeType]}' non pris en charge`); - } - - case CalculatorType.ParallelStructure: - { - let cn = new ParallelStructureParams(undefined, undefined, undefined); - let n = new ParallelStructure(cn); - return cn; - } - - default: - throw new Error(`ComputeNodeParameters.createComputeNodeParameters() : calculatrice '${CalculatorType[calcType]}' / noeud de calcul '${ComputeNodeType[nodeType]}' non pris en charge`); - } - } - - public getComputeNodeParameter(calcType: CalculatorType, nodeType: ComputeNodeType, symbol: string): ParamDefinition { - const key = { calcType, nodeType }; - let params = this._nodes.get(key); - if (params == undefined) { - params = this.createComputeNodeParameters(calcType, nodeType); - this._nodes.put(key, params); - } - - return params.map[symbol]; - } -} diff --git a/src/remous.ts b/src/remous.ts index 3e02d648b599003609f2de6d1cdec9dd54496e1d..6c3ff39a118f985843978792639fb19ce0bcff79 100644 --- a/src/remous.ts +++ b/src/remous.ts @@ -432,42 +432,8 @@ export class CourbeRemous extends Nub { this.debug("abscisses "); this.logArray(trX); - // Calcul de la variable à calculer - - const tRes: { [key: number]: number } = {}; - if (val_a_cal !== undefined && (nFlu !== 0 || nTor !== 0)) { - for (const rX of trX) { - let rY: number; - const hasFlu: boolean = crbFlu[rX] !== undefined; - const hasTor: boolean = crbTor[rX] !== undefined; - - if (hasFlu && !hasTor) { - rY = crbFlu[rX]; - } - - if (hasTor) { - if (!hasFlu || (hasFlu && crbFlu[rX] === crbTor[rX])) { - rY = crbTor[rX]; - } - } - - if (rY !== undefined) { - // tRes[+rX] = this.Sn.Calc(val_a_cal, rY); - const rVar = this.Sn.Calc(val_a_cal, rY); - if (!rVar.ok) { - res.addLog(rVar.log); - return res; - } - tRes[rX] = rVar.vCalc; - this.debug("X=" + rX + " Calc(" + val_a_cal + ", Y=" + rY + ")=" + tRes[rX]); - } - } + // compilation des résultats - this.debug("extra param " + val_a_cal); - this.logObject(tRes); - } - - const hasRes = Object.keys(tRes).length > 0; for (const x of trX) { let ligneDeau; @@ -488,13 +454,25 @@ export class CourbeRemous extends Nub { re.addExtraResult("tor", crbTor[x]); } - if (hasRes && tRes[x]) { - re.addExtraResult("tRes", tRes[x]); - } - res.addResultElement(re); } + // Calcul de la variable à calculer + + const tRes: { [key: number]: number } = {}; + if (val_a_cal) { + for (const re of res.resultElements) { + const rY = re.vCalc; + if (rY !== undefined) { + const rVar = this.Sn.Calc(val_a_cal, rY); + if (!rVar.ok) + res.addLog(rVar.log); + else + re.addExtraResult("tRes", rVar.vCalc); + } + } + } + return res; } diff --git a/src/section/section_nub.ts b/src/section/section_nub.ts new file mode 100644 index 0000000000000000000000000000000000000000..ad9068b19cc2d69e6dfe96a1886ebbfcd788aaf6 --- /dev/null +++ b/src/section/section_nub.ts @@ -0,0 +1,228 @@ +import { Nub } from "../nub"; +import { acSection } from "./section_type"; +import { Result } from "../util/result"; +import { ParamDefinition, ParamCalculability } from "../param/param-definition"; +import { ParamDomain, ParamDomainValue } from "../param/param-domain"; +import { ParamValueMode } from "../param/param-values"; +import { ResultElement } from "../util/resultelement"; + +/** + * Nub sur les sections paramétrées + */ +export class SectionParametree extends Nub { + private _section: acSection; + + private _sectionVars: { [key: string]: ParamDefinition }; + + constructor(sect: acSection, dbg: boolean = false) { + super(sect.prms, dbg); + this._section = sect; + this.initSectionVars(); + } + + private initSectionVars() { + this._sectionVars = {}; + this.initSectionVar("Hs"); + this.initSectionVar("Hsc"); + this.initSectionVar("B"); + this.initSectionVar("P"); + this.initSectionVar("S"); + this.initSectionVar("R"); + this.initSectionVar("V"); + this.initSectionVar("Fr"); + this.initSectionVar("Yc"); + this.initSectionVar("Yn"); + this.initSectionVar("Yf"); + this.initSectionVar("Yt"); + this.initSectionVar("Yco"); + this.initSectionVar("J"); + this.initSectionVar("Imp"); + this.initSectionVar("Tau0"); + this.initSectionVar("I-J"); + } + + private initSectionVar(symbol: string) { + this._sectionVars[symbol] = this.createSectionVar(symbol); + } + + private createSectionVar(symbol: string): ParamDefinition { + switch (symbol) { + case "Hs": + case "Hsc": + case "B": + case "P": + case "S": + case "R": + case "V": + case "Fr": + case "Yc": + case "Yn": + case "Yf": + case "Yt": + case "Yco": + case "J": + case "Imp": + case "Tau0": + var dom = new ParamDomain(ParamDomainValue.POS_NULL); + break; + + case "I-J": + var dom = new ParamDomain(ParamDomainValue.ANY); + break; + + default: + throw new Error(`SectionParametree.createSectionVar() : symbole ${symbol} non pris en charge`); + } + + const res = new ParamDefinition(symbol, dom); + res.calculability = ParamCalculability.EQUATION; + return res; + } + + public get section(): acSection { + return this._section; + } + + public getParameter(name: string): ParamDefinition { + let res = super.getParameter(name); + if (res === undefined) { + // pas trouvé -> il s'agit peut être d'une variable dans le genre Hs, B, P, ... + res = this._sectionVars[name]; + + if (!res) + throw new Error(`SectionParametree.getParameter() : nom de paramètre ${name} incorrect`); + } + return res; + } + + public getFirstAnalyticalParameter(): ParamDefinition { + let res = super.getFirstAnalyticalParameter(); + if (res) + return res; + + for (const k in this._sectionVars) { + const p = this._sectionVars[k]; + if (p.isAnalytical) + return p; + } + return undefined; + } + + /** + * paramétrage de la calculabilité des paramètres + */ + protected setParametersCalculability() { + } + + Equation(sVarCalc: string): Result { + switch (sVarCalc) { + case "Hs": + case "Hsc": + case "B": + case "P": + case "S": + case "R": + case "V": + case "Fr": + case "Yc": + case "Yn": + case "Yf": + case "Yt": + case "Yco": + case "J": + case "Imp": + case "Tau0": + case "I-J": + return this._section.Calc(sVarCalc, this.getParameter("Y").v); + + default: + throw new Error(`SectionParam.Equation() : calcul sur ${sVarCalc} non implémenté`); + } + } + + private hasVariatedParameter(): boolean { + for (const p of this.parameterIterator) { + switch (p.valueMode) { + case ParamValueMode.LISTE: + case ParamValueMode.MINMAX: + return true; + } + } + return false; + } + + private addExtraResultFromVar(varCalc: string, Y: number, re: ResultElement) { + const r: Result = this._section.Calc(varCalc, Y); + if (r.ok) + re.addExtraResult(varCalc, r.vCalc); + else + re.log.addLog(r.log); + } + + public CalcSerie(rPrec: number = 0.001, rInit?: number, sDonnee?: string): Result { + //paramètre à varier ? + + if (this.hasVariatedParameter()) + return super.CalcSerie(rPrec, rInit, sDonnee); + + // sinon, on stocke toutes les valeurs des variables à calcul dans les résultats supplémentaires + + const Y = this.getParameter("Y").v; // tirant d'eau original (doit être fourni à acSection.Calc() sous peine d'être modifié par les appels successifs car c'est en même temps un paramètre et une variable temporaire) + const result = new Result(); + const re = new ResultElement(); + result.addResultElement(re); + + // charge spécifique + this.addExtraResultFromVar("Hs", Y, re); + + // charge critique + this.addExtraResultFromVar("Hsc", Y, re); + + // largeur au miroir + this.addExtraResultFromVar("B", Y, re); + + // périmètre hydraulique + this.addExtraResultFromVar("P", Y, re); + + // surface hydraulique + this.addExtraResultFromVar("S", Y, re); + + // rayon hydraulique + this.addExtraResultFromVar("R", Y, re); + + // vitesse moyenne + this.addExtraResultFromVar("V", Y, re); + + // nombre de Froude + this.addExtraResultFromVar("Fr", Y, re); + + // tirant d'eau critique + this.addExtraResultFromVar("Yc", Y, re); + + // tirant d'eau normal + this.addExtraResultFromVar("Yn", Y, re); + + // tirant d'eau fluvial + this.addExtraResultFromVar("Yf", Y, re); + + // tirant d'eau torrentiel + this.addExtraResultFromVar("Yt", Y, re); + + // tirant d'eau conjugué + this.addExtraResultFromVar("Yco", Y, re); + + // perte de charge + this.addExtraResultFromVar("J", Y, re); + + // Variation linéaire de l'énergie spécifique + this.addExtraResultFromVar("I-J", Y, re); + + // impulsion hydraulique + this.addExtraResultFromVar("Imp", Y, re); + + // contrainte de cisaillement + this.addExtraResultFromVar("Tau0", Y, re); + + return result; + } +} diff --git a/src/session_nub.ts b/src/session_nub.ts new file mode 100644 index 0000000000000000000000000000000000000000..d1f320d1505063d53fcfcd41ea0d9d2c8aaa32cf --- /dev/null +++ b/src/session_nub.ts @@ -0,0 +1,128 @@ +import { Nub } from "./nub"; +import { IObservable, Observer, Observable } from "./util/observer"; + +/** + * gestion d'un ensemble de propriétés (clé/valeur) + */ +export class Props implements IObservable { + // implémentation de IObservable par délégation + private _observable: Observable; + + constructor(private _props: any = {}) { + this._observable = new Observable(); + } + + public hasProperties(props: Props | {}): boolean { + const keys = Object.keys(this._props); + const p = props instanceof Props ? props.props : props; + + // if (keys.length != Object.keys(p).length) + // return false; + + for (const k of keys) + if (this._props[k] !== p[k]) + return false; + + return true; + } + + public getPropValue(key: string): any { + return this._props[key]; + } + + private notifyPropChange(prop: string, val: any, sender: any) { + this.notifyObservers({ + "action": "propertyChange", + "name": prop, + "value": val + }, sender) + } + + public setPropValue(key: string, val: any, sender?: any): boolean { + const oldValue = this._props[key]; + const changed = oldValue !== val; + if (changed) { + this._props[key] = val; + this.notifyPropChange(key, val, sender); + } + return changed; + } + + public get props() { + return this._props; + } + + public clone(): Props { + const res = new Props(); + for (const k in this._props) + res._props[k] = this._props[k]; + return res; + } + + public toString(): string { + let res = "["; + for (const k in this._props) { + if (res != "[") + res += ", "; + res += `${k}:${this.props[k]}`; + } + res += "]" + return res; + } + + // interface IObservable + + /** + * ajoute un observateur à la liste + */ + public addObserver(o: Observer) { + this._observable.addObserver(o); + } + + /** + * supprime un observateur de la liste + */ + public removeObserver(o: Observer) { + this._observable.removeObserver(o); + } + + /** + * notifie un événement aux observateurs + */ + public notifyObservers(data: any, sender?: any) { + this._observable.notifyObservers(data, sender); + } +} + +/** + * Nub utilisé dans une session + */ +export class SessionNub { + private _props: Props; + + constructor(private _nub: Nub, props: Props | {}) { + if (this._nub == undefined) + throw new Error(`NgNub.constructor() : argument invalide`); + + if (props instanceof Props) + this._props = props.clone(); + else + this._props = new Props(props); + } + + public get nub() { + return this._nub; + } + + public get uid(): number { + return this._nub.uid; + } + + public get properties() { + return this._props; + } + + public hasProperties(p: Props | {}): boolean { + return this._props.hasProperties(p); + } +} diff --git a/src/structure/factory_structure.ts b/src/structure/factory_structure.ts index 787daa1f7999c71e0143ca7aea8ba98c11a47d0e..abda575bea35b86a4986a4b28f3ef1e73465997f 100644 --- a/src/structure/factory_structure.ts +++ b/src/structure/factory_structure.ts @@ -8,40 +8,7 @@ import { StructureKiviParams } from "./structure_kivi_params"; import { StructureOrificeFree } from "./structure_orifice_free"; import { StructureOrificeSubmerged } from "./structure_orifice_submerged"; import { StructureWeirFree } from "./structure_weir_free"; - -export enum StructureType { - SeuilRectangulaire, VanneRectangulaire - // VanneCirculaire, - // VanneTrapezoidale, SeuilTrapezoidal -} - -export enum LoiDebit { - // loi de débit Déversoir / Orifice Cemagref 1988 - Cem88d, - // loi de débit Déversoir / Vanne de fond Cemagref 1988 - Cem88v, - // loi de débit Cunge 1980 - Cunge80, - // loi de débit pour vanne dénoyée - OrificeFree, - // loi de débit pour vanne noyée - OrificeSubmerged, - // loi de débit pour seuil dénoyé - WeirFree, - // Loi Kindsvater-Carter et Villemonte - KIVI -} - -export const loiAdmissibles: { [key: string]: LoiDebit[] } = { - SeuilRectangulaire: [ - LoiDebit.Cem88d, LoiDebit.Cem88v, LoiDebit.Cunge80, LoiDebit.WeirFree, - LoiDebit.KIVI - ], - VanneRectangulaire: [ - LoiDebit.Cem88d, LoiDebit.Cem88v, LoiDebit.Cunge80, LoiDebit.OrificeFree, - LoiDebit.OrificeSubmerged - ] -}; +import { StructureType, LoiDebit, StructureProperties } from "./structure_props"; export function CreateStructure(structureType: StructureType, loiDebit: LoiDebit, dbg: boolean = false): Structure { const rectStructPrms: RectangularStructureParams = new RectangularStructureParams( @@ -57,14 +24,14 @@ export function CreateStructure(structureType: StructureType, loiDebit: LoiDebit case StructureType.VanneRectangulaire: rectStructPrms.W.v = 0.5; rectStructPrms.Cd.v = 0.6; // Cd pour une vanne rectangulaire - if (!(loiAdmissibles.VanneRectangulaire.includes(loiDebit))) { + if (!(StructureProperties.isCompatibleValues(StructureType.VanneRectangulaire, loiDebit))) { throw new Error( `la loi de débit ${LoiDebit[loiDebit]} n'est pas admissible pour les vannes rectangulaires` ); } break; case StructureType.SeuilRectangulaire: - if (!loiAdmissibles.SeuilRectangulaire.includes(loiDebit)) { + if (!(StructureProperties.isCompatibleValues(StructureType.SeuilRectangulaire, loiDebit))) { throw new Error( `la loi de débit ${LoiDebit[loiDebit]} n'est pas admissible pour les seuils rectangulaires` ); @@ -96,6 +63,7 @@ export function CreateStructure(structureType: StructureType, loiDebit: LoiDebit case LoiDebit.WeirFree: return new StructureWeirFree(rectStructPrms, dbg); + case LoiDebit.KIVI: const structKiviPrm: StructureKiviParams = new StructureKiviParams( 8.516, // Q @@ -107,8 +75,8 @@ export function CreateStructure(structureType: StructureType, loiDebit: LoiDebit 0.001, // béta 100); // ZRAM : cote Radier Amont return new StructureKivi(structKiviPrm, dbg); + default: throw new Error(`type de LoiDebit ${LoiDebit[loiDebit]} non pris en charge`); - } } diff --git a/src/structure/parallel_structure.ts b/src/structure/parallel_structure.ts index 8d2b46ab60bd5cadee49503d3ea5067e9fdaf7a7..d4dc9749f93e70512ae7b5b4500f012226feaa5b 100644 --- a/src/structure/parallel_structure.ts +++ b/src/structure/parallel_structure.ts @@ -4,6 +4,7 @@ import { Message } from "../util/message"; import { Result } from "../util/result"; import { ParallelStructureParams } from "./parallel_structure_params"; import { Structure } from "./structure"; +import { IParamDefinitionIterator, ParamsEquation, ParamsEquationArrayIterator } from "../param/params-equation"; /** * Interface pour mémoriser le n° d'ouvrage et le paramètre à calculer @@ -46,6 +47,14 @@ export class ParallelStructure extends Nub { this.updateStructuresH1H2(); } + public get parameterIterator(): IParamDefinitionIterator { + const prms: ParamsEquation[] = []; + prms.push(this._prms); + for (const st of this.structures) + prms.push(st.parameters); + return new ParamsEquationArrayIterator(prms); + } + /** * Ajout d'une structure en parallèle * @param structure La structure à rajouter @@ -54,6 +63,19 @@ export class ParallelStructure extends Nub { this.structures.push(structure); } + /** + * remplace une structure hydraulique + * @param index indice de la structure dans le tableau + * @param structure nouvelle structure + */ + public replaceStructure(index: number, structure: Structure) { + if (index > -1 && index < this.structures.length) { + this.structures[index] = structure; + } else { + throw new Error(`ParallelStructure.replaceStructure invalid index ${index}`); + } + } + /** * Supprime une structure hydraulique * @param index numéro de la structure dans le tableau @@ -117,7 +139,7 @@ export class ParallelStructure extends Nub { case "Q": res = super.Calc(sVarCalc, rInit, rPrec); if (res.ok) { - this.prms.map[sVarCalc].v = res.vCalc; + this.getParameter(sVarCalc).setValue(res.vCalc); } break; default: @@ -125,7 +147,7 @@ export class ParallelStructure extends Nub { const sVC = this.getStructureVarCalc(sVarCalc); res = this.CalcStructPrm(sVC, rInit, rPrec); if (res.ok) { - this.structures[sVC.index].prms.map[sVC.prm].v = res.vCalc; + this.structures[sVC.index].getParameter(sVC.prm).setValue(res.vCalc); } } if (res.ok) { @@ -137,6 +159,7 @@ export class ParallelStructure extends Nub { } } } + this._result = res; return res; } @@ -167,8 +190,12 @@ export class ParallelStructure extends Nub { private getStructureVarCalc(sVarCalc: string): IStructureVarCalc { let sIndex: string; let sPrm: string; + if (sVarCalc.indexOf(".") == -1) + throw new Error(`getStructureVarCalc() : erreur d'analyse de ${sVarCalc}, (pas de la forme n.X)`); [sIndex, sPrm] = sVarCalc.split("."); const i = parseInt(sIndex, 10); + if (i === NaN) + throw new Error(`getStructureVarCalc() : erreur d'analyse de ${sVarCalc} (${sIndex} n'est pas un nombre)`); return { index: i, prm: sPrm }; } @@ -180,7 +207,7 @@ export class ParallelStructure extends Nub { */ private CalcStructPrm(sVC: IStructureVarCalc, rInit?: number, rPrec: number = 0.001): Result { // Le débit restant sur la structure en calcul est : - this.structures[sVC.index].prms.Q.v = this.prms.Q.v - this.CalcQ(sVC.index).vCalc; + this.structures[sVC.index].prms.Q.setValue(this.prms.Q.v - this.CalcQ(sVC.index).vCalc); // Calcul du paramètre de la structure en calcul return this.structures[sVC.index].Calc(sVC.prm, rInit, rPrec); diff --git a/src/structure/structure.ts b/src/structure/structure.ts index 5e73d099bbd97798118b5e4f05855a926f4732d4..69ce4fea56d0962496d4b83b722be91fc48254d4 100644 --- a/src/structure/structure.ts +++ b/src/structure/structure.ts @@ -77,7 +77,7 @@ export abstract class Structure extends Nub { public Calc(sVarCalc: string, rInit?: number, rPrec: number = 0.001): Result { // Gestion de l'exception de calcul de W sur les seuils if (rInit === undefined) { - rInit = this._prms.map[sVarCalc].v; + rInit = this.getParameter(sVarCalc).v; } if (sVarCalc === "W" && rInit === Infinity) { throw new Error("Structure:Calc : Calcul de W impossible sur un seuil"); diff --git a/src/structure/structure_props.ts b/src/structure/structure_props.ts new file mode 100644 index 0000000000000000000000000000000000000000..7c7d63a4b41962f54cd52afee7203785cadc1ac5 --- /dev/null +++ b/src/structure/structure_props.ts @@ -0,0 +1,63 @@ +export enum StructureType { + SeuilRectangulaire, VanneRectangulaire + // VanneCirculaire, + // VanneTrapezoidale, SeuilTrapezoidal +} + +export enum LoiDebit { + // loi de débit Déversoir / Orifice Cemagref 1988 + Cem88d, + // loi de débit Déversoir / Vanne de fond Cemagref 1988 + Cem88v, + // loi de débit Cunge 1980 + Cunge80, + // loi de débit pour vanne dénoyée + OrificeFree, + // loi de débit pour vanne noyée + OrificeSubmerged, + // loi de débit pour seuil dénoyé + WeirFree, + // Loi Kindsvater-Carter et Villemonte + KIVI +} + +export const loiAdmissibles: { [key: string]: LoiDebit[] } = { + SeuilRectangulaire: [ + LoiDebit.Cem88d, LoiDebit.Cem88v, LoiDebit.Cunge80, LoiDebit.WeirFree, + LoiDebit.KIVI + ], + VanneRectangulaire: [ + LoiDebit.Cem88d, LoiDebit.Cem88v, LoiDebit.Cunge80, LoiDebit.OrificeFree, + LoiDebit.OrificeSubmerged + ] +}; + +export class StructureProperties { + /** + * @return true si les valeurs de StructureType et LoiDebit sont compatibles + */ + public static isCompatibleValues(struct: StructureType, loi: LoiDebit): boolean { + return loiAdmissibles[StructureType[struct]].includes(loi); + } + + /** + * @return la 1ère valeur de StructureType compatible avec la loi de débit + */ + public static findCompatibleStructure(loi: LoiDebit): StructureType { + for (const st in loiAdmissibles) { + const lds: LoiDebit[] = loiAdmissibles[st]; + for (const ld of lds) + if (ld === loi) + return (<any>StructureType)[st]; + } + return undefined; + } + + /** + * @return la 1ère valeur de LoiDebit compatible avec le type de structure + */ + public static findCompatibleLoiDebit(struct: StructureType): LoiDebit { + const sst: string = StructureType[struct]; + return loiAdmissibles[sst][0]; + } +} \ No newline at end of file diff --git a/src/util/message.ts b/src/util/message.ts index 0a08e11ef7623473d6a073b1a78898b1eddb368c..f30b1fa04e624689e28e67de5de562b39eebf792 100644 --- a/src/util/message.ts +++ b/src/util/message.ts @@ -2,233 +2,231 @@ export enum MessageCode { /** * pas de pb ! */ - ERROR_OK = 0, + ERROR_OK, /** * la dichotomie n'a pas pu trouver automatiquement d'intervalle de départ * car la valeur initiale de la variable est trop haute */ - ERROR_DICHO_INITVALUE_HIGH = -1, + ERROR_DICHO_INITVALUE_HIGH, /** * la dichotomie n'a pas pu trouver automatiquement d'intervalle de départ * car la valeur initiale de la variable est trop basse */ - ERROR_DICHO_INITVALUE_LOW = -2, + ERROR_DICHO_INITVALUE_LOW, /** * la dichotomie n'a pas pu trouver automatiquement d'intervalle de départ * car la valeur cible de la fonction n'existe pas pour des valeurs de la * variable dans son domaine de définition, cad il n'existe pas de solution */ - ERROR_DICHO_INIT_DOMAIN = -3, + ERROR_DICHO_INIT_DOMAIN, /** * la dichotomie n'a pas pu converger */ - ERROR_DICHO_CONVERGE = -4, + ERROR_DICHO_CONVERGE, /** * le pas pour la recherche de l'intervalle de départ est =0 */ - ERROR_DICHO_NULL_STEP = -5, + ERROR_DICHO_NULL_STEP, /** * l'augmentation du pas pour la recherche de l'intervalle de départ est incorrecte (=0) */ - ERROR_DICHO_INVALID_STEP_GROWTH = -6, + ERROR_DICHO_INVALID_STEP_GROWTH, /** * impossible de déterminer le sens de variation de la fonction */ - ERROR_DICHO_FUNCTION_VARIATION = -7, + ERROR_DICHO_FUNCTION_VARIATION, /** * les bornes de l'intervalle d'un ParamDomain sont incorrectes */ - ERROR_PARAMDOMAIN_INTERVAL_BOUNDS = -100, + ERROR_PARAMDOMAIN_INTERVAL_BOUNDS, /** * la valeur du ParamDomain est incorrecte */ - ERROR_PARAMDOMAIN_INVALID = -101, + ERROR_PARAMDOMAIN_INVALID, /** * la calculabilité d'un ParamDefinition est non définie */ - ERROR_PARAMDEF_CALC_UNDEFINED = -200, + ERROR_PARAMDEF_CALC_UNDEFINED, /** * la valeur d'un ParamDefinition est non définie */ - ERROR_PARAMDEF_VALUE_UNDEFINED = -201, + ERROR_PARAMDEF_VALUE_UNDEFINED, /** * la valeur d'un ParamDefinition ne peut pas être changée */ - ERROR_PARAMDEF_VALUE_FIXED = -202, + ERROR_PARAMDEF_VALUE_FIXED, /** * la valeur d'un ParamDefinition ne peut pas être > 0 */ - ERROR_PARAMDEF_VALUE_POS = -203, + ERROR_PARAMDEF_VALUE_POS, /** * la valeur d'un ParamDefinition ne peut pas être >= 0 */ - ERROR_PARAMDEF_VALUE_POSNULL = -204, + ERROR_PARAMDEF_VALUE_POSNULL, /** * la valeur d'un ParamDefinition ne peut pas être = 0 */ - ERROR_PARAMDEF_VALUE_NULL = -205, + ERROR_PARAMDEF_VALUE_NULL, /** * la valeur d'un ParamDefinition est en dehors de son intervalle autorisé */ - ERROR_PARAMDEF_VALUE_INTERVAL = -206, + ERROR_PARAMDEF_VALUE_INTERVAL, /** * la valeur passée à une méthode de la classe Interval est undefined */ - ERROR_INTERVAL_UNDEF = -300, + ERROR_INTERVAL_UNDEF, /** * la valeur passée à une méthode de la classe Interval est hors de l'intervalle défini */ - ERROR_INTERVAL_OUTSIDE = -301, + ERROR_INTERVAL_OUTSIDE, /** * internationalisation : langue non prise en charge */ - ERROR_LANG_UNSUPPORTED = -400, + ERROR_LANG_UNSUPPORTED, /** * courbes de remous : Arrêt du calcul : hauteur critique atteinte à l'abscisse x */ - WARNING_REMOUS_ARRET_CRITIQUE = -500, + WARNING_REMOUS_ARRET_CRITIQUE, /** * courbe de remous : Condition limite aval >= Hauteur critique : calcul de la partie fluviale à partir de l'aval */ - INFO_REMOUS_CALCUL_FLUVIAL = -501, + INFO_REMOUS_CALCUL_FLUVIAL, /** * courbe de remous : Condition limite amont <= Hauteur critique : calcul de la partie torrentielle à partir de l'amont */ - INFO_REMOUS_CALCUL_TORRENTIEL = -502, + INFO_REMOUS_CALCUL_TORRENTIEL, /** * courbe de remous : ressaut hydraulique détecté à l'amont/aval de l'abscisse x */ - INFO_REMOUS_RESSAUT_DEHORS = -503, + INFO_REMOUS_RESSAUT_DEHORS, /** * courbe de remous : Largeur au niveau des berges */ - INFO_REMOUS_LARGEUR_BERGE = -504, + INFO_REMOUS_LARGEUR_BERGE, /** * courbe de remous : Tirant d'eau critique */ - INFO_REMOUS_H_CRITIQUE = -505, + INFO_REMOUS_H_CRITIQUE, /** * courbe de remous : Tirant d'eau normal */ - INFO_REMOUS_H_NORMALE = -506, + INFO_REMOUS_H_NORMALE, /** * courbe de remous : Ressaut hydraulique détecté entre les abscisses Xmin et Xmax m */ - INFO_REMOUS_RESSAUT_HYDRO = -507, + INFO_REMOUS_RESSAUT_HYDRO, /** * courbe de remous : La pente de la ligne d'eau est trop forte à l'abscisse x m */ - ERROR_REMOUS_PENTE_FORTE = -508, + ERROR_REMOUS_PENTE_FORTE, /** * courbe de remous : Condition limite aval < Hauteur critique : pas de calcul possible depuis l'aval */ - ERROR_REMOUS_PAS_CALCUL_DEPUIS_AVAL = -509, + ERROR_REMOUS_PAS_CALCUL_DEPUIS_AVAL, /** * courbe de remous : Condition limite amont > Hauteur critique : pas de calcul possible depuis l'amont */ - ERROR_REMOUS_PAS_CALCUL_DEPUIS_AMONT = -510, + ERROR_REMOUS_PAS_CALCUL_DEPUIS_AMONT, /** * section : Non convergence du calcul de la hauteur critique (Méthode de Newton) */ - ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCRITIQUE = -600, + ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCRITIQUE, /** * section : Non convergence du calcul de la hauteur normale (Méthode de Newton) */ - ERROR_SECTION_NON_CONVERGENCE_NEWTON_HNORMALE = -601, + ERROR_SECTION_NON_CONVERGENCE_NEWTON_HNORMALE, /** * section : Non convergence du calcul de la hauteur conjuguée (Méthode de Newton) */ - ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCONJUG = -602, + ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCONJUG, /** * section : Non convergence du calcul de la hauteur correspondante (Méthode de Newton) pour le calcul de la hauteur fluviale */ - ERROR_SECTION_NON_CONVERGENCE_NEWTON_HFLU = -603, + ERROR_SECTION_NON_CONVERGENCE_NEWTON_HFLU, /** * section : Non convergence du calcul de la hauteur correspondante (Méthode de Newton) pour le calcul de la hauteur torrentielle */ - ERROR_SECTION_NON_CONVERGENCE_NEWTON_HTOR = -604, + ERROR_SECTION_NON_CONVERGENCE_NEWTON_HTOR, /** * section : La pente est négative ou nulle, la hauteur normale est infinie */ - ERROR_SECTION_PENTE_NEG_NULLE_HNORMALE_INF = -605, + ERROR_SECTION_PENTE_NEG_NULLE_HNORMALE_INF, /** * section : calcul impossible à cause d'un périmètre nul */ - ERROR_SECTION_PERIMETRE_NUL = -606, + ERROR_SECTION_PERIMETRE_NUL, /** * section : calcul impossible à cause d'un rayon nul */ - ERROR_SECTION_RAYON_NUL = -607, + ERROR_SECTION_RAYON_NUL, /** * section : calcul impossible à cause d'une surface nulle */ - ERROR_SECTION_SURFACE_NULLE = -608, + ERROR_SECTION_SURFACE_NULLE, /** * newton : pas de convergence */ - ERROR_NEWTON_NON_CONVERGENCE = -700, + ERROR_NEWTON_NON_CONVERGENCE, /** * newton : dérivée nulle */ - ERROR_NEWTON_DERIVEE_NULLE = -701, + ERROR_NEWTON_DERIVEE_NULLE, /** * Le paramètre "Cote de radier" ne peut pas être calculé avec cette loi de débit */ - ERROR_STRUCTURE_ZDV_PAS_CALCULABLE = -800, - + ERROR_STRUCTURE_ZDV_PAS_CALCULABLE, /** * StructureKivi : La pelle du seuil doit mesurer au moins 0,1 m. Le coefficient béta est forcé à 0. */ - WARNING_STRUCTUREKIVI_PELLE_TROP_FAIBLE = 100, + WARNING_STRUCTUREKIVI_PELLE_TROP_FAIBLE, /** * StructureKivi : h/p ne doit pas être supérieur à 2,5. h/p est forcé à 2,5. */ - WARNING_STRUCTUREKIVI_HP_TROP_ELEVE = 101, - + WARNING_STRUCTUREKIVI_HP_TROP_ELEVE, } /** diff --git a/src/util/observer.ts b/src/util/observer.ts new file mode 100644 index 0000000000000000000000000000000000000000..6a17beb902450e1e8f7da5c3d21023a49fd03fca --- /dev/null +++ b/src/util/observer.ts @@ -0,0 +1,53 @@ +export interface Observer { + update(sender: any, data: any): void; +} + +export interface IObservable { + /** + * ajoute un observateur à la liste + */ + addObserver(o: Observer): void; + + /** + * supprime un observateur de la liste + */ + removeObserver(o: Observer): void; + + /** + * notifie un événement aux observateurs + */ + notifyObservers(data: any, sender?: any): void; +} + +export class Observable implements IObservable { + private _observers: Observer[]; + + constructor() { + this._observers = []; + } + + /** + * ajoute un observateur à la liste + */ + public addObserver(o: Observer) { + if (this._observers.indexOf(o) == -1) + this._observers.push(o); + } + + /** + * supprime un observateur de la liste + */ + public removeObserver(o: Observer) { + this._observers = this._observers.filter(a => a !== o); + } + + /** + * notifie un événement aux observateurs + */ + public notifyObservers(data: any, sender?: any) { + if (sender == undefined) + sender = this; + for (let o of this._observers) + o.update(sender, data); + } +} diff --git a/src/util/result.ts b/src/util/result.ts index 70d7026bd3facc0ab83ee721318852ca0b80a653..baca22343183e8b927f8720cbf30af0529a69980 100644 --- a/src/util/result.ts +++ b/src/util/result.ts @@ -36,6 +36,20 @@ export class Result { return this._globalLog; } + /** + * @return true si il y a au moins un message dans le log global + */ + public get hasGlobalLog(): boolean { + return this.globalLog.messages.length > 0; + } + + /** + * @return true si il y a au moins un message dans le log global ou dans les ResultElement + */ + public get hasLog(): boolean { + return (this.nbResultElements > 0 && this.log.messages.length > 0) || this.hasGlobalLog; + } + /** * Retourne le résultat du premier ResultElement */