UNPKG

jalhyd

Version:

JaLHyd, a Javascript Library for Hydraulics

1,094 lines 44.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ParamDefinition = exports.ExtensionStrategy = exports.ParamFamily = exports.ParamCalculability = void 0; const internal_modules_1 = require("../internal_modules"); const internal_modules_2 = require("../internal_modules"); const internal_modules_3 = require("../internal_modules"); const internal_modules_4 = require("../internal_modules"); const internal_modules_5 = require("../internal_modules"); const internal_modules_6 = require("../internal_modules"); const internal_modules_7 = require("../internal_modules"); const internal_modules_8 = require("../internal_modules"); const internal_modules_9 = require("../internal_modules"); const internal_modules_10 = require("../internal_modules"); /** * Calculabilité du paramètre */ var ParamCalculability; (function (ParamCalculability) { /** parameter may have any value, may not vary, may not be calculated */ ParamCalculability[ParamCalculability["FIXED"] = 0] = "FIXED"; /** parameter may have any value, may vary, may not be calculated */ ParamCalculability[ParamCalculability["FREE"] = 1] = "FREE"; /** parameter may have any value, may vary, may be calculated analytically */ ParamCalculability[ParamCalculability["EQUATION"] = 2] = "EQUATION"; /** parameter may have any value, may vary, may be calculated using numerical method */ ParamCalculability[ParamCalculability["DICHO"] = 3] = "DICHO"; })(ParamCalculability = exports.ParamCalculability || (exports.ParamCalculability = {})); /** * Parameter family: defines linkability with other parameters/results */ var ParamFamily; (function (ParamFamily) { ParamFamily[ParamFamily["ANY"] = 0] = "ANY"; ParamFamily[ParamFamily["LENGTHS"] = 1] = "LENGTHS"; ParamFamily[ParamFamily["WIDTHS"] = 2] = "WIDTHS"; ParamFamily[ParamFamily["SLOPES"] = 3] = "SLOPES"; ParamFamily[ParamFamily["HEIGHTS"] = 4] = "HEIGHTS"; ParamFamily[ParamFamily["BASINFALLS"] = 5] = "BASINFALLS"; ParamFamily[ParamFamily["TOTALFALLS"] = 6] = "TOTALFALLS"; ParamFamily[ParamFamily["ELEVATIONS"] = 7] = "ELEVATIONS"; ParamFamily[ParamFamily["VOLUMES"] = 8] = "VOLUMES"; ParamFamily[ParamFamily["FLOWS"] = 9] = "FLOWS"; ParamFamily[ParamFamily["DIAMETERS"] = 10] = "DIAMETERS"; ParamFamily[ParamFamily["SPEEDS"] = 11] = "SPEEDS"; ParamFamily[ParamFamily["STRICKLERS"] = 12] = "STRICKLERS"; ParamFamily[ParamFamily["BLOCKCONCENTRATION"] = 13] = "BLOCKCONCENTRATION"; // concentrations de blocs })(ParamFamily = exports.ParamFamily || (exports.ParamFamily = {})); /** * Strategy to apply when multiple parameters are variating, and * values series have to be extended for sizes to match */ var ExtensionStrategy; (function (ExtensionStrategy) { ExtensionStrategy[ExtensionStrategy["REPEAT_LAST"] = 0] = "REPEAT_LAST"; ExtensionStrategy[ExtensionStrategy["RECYCLE"] = 1] = "RECYCLE"; // repeat the whole series from the beginning // autres propositions : // PAD_LEFT // pad with zeroes at the beginning ? // PAD_RIGHT // pad with zeroes at the end ? // INTERPOLATE // insert regular steps between first and last value })(ExtensionStrategy = exports.ExtensionStrategy || (exports.ExtensionStrategy = {})); /** * Paramètre avec symbole, famille, domaine de définition, calculabilité, * pointant éventuellement vers un autre paramètre / résultat */ class ParamDefinition { /** * @param nullParams true if provided value must be ignored (@see nghyd/enableEmptyFieldsOnFormInit) */ constructor(parent, symb, d, unit, val, family, visible = true, nullParams = false) { /** le paramètre doit-il être exposé (par ex: affiché par l'interface graphique) ? */ this.visible = true; this._parent = parent; this._symbol = symb; this._unit = unit; this._observable = new internal_modules_7.Observable(); this._paramValues = new internal_modules_10.ParamValues(); // set single value and copy it to sandbox value and initValue if (!nullParams) { this._paramValues.singleValue = val; this.v = val; } this.initValue = val; // if (!nullParams && this._paramValues.singleValue === undefined) { // throw new Error("a value must be provided for " + symb + " parameter"); // } this._calc = ParamCalculability.FREE; this._family = family; this.visible = visible; this.valueMode = internal_modules_9.ParamValueMode.SINGLE; this.extensionStrategy = ExtensionStrategy.REPEAT_LAST; this.setDomain(d); this.checkValueAgainstDomain(val); } /** * set parent a-posteriori; used by nghyd when populating forms */ // public set parent(parent: ParamsEquation) { // this._parent = parent; // } /** * identifiant unique de la forme * (uid du JalhydObject (en général un Nub) + "_" + symbole du paramètre) * ex: 123_Q */ get uid() { return this.nubUid + "_" + this._symbol; } get unit() { return this._unit; } setUnit(u) { this._unit = u; } get initValue() { return this._initValue; } /** * Make sure that this value is never undefined, null or NaN, so that * there is always an initial value for "DICHO" calculations, even * when param value was set to undefined before */ set initValue(v) { if (v !== undefined && !isNaN(v) && v !== null) { this._initValue = v; } else { if (this._initValue === undefined || isNaN(this._initValue) || this._initValue === null) { this._initValue = Math.random(); } } } /** * @see getParentComputeNode() */ get parentComputeNode() { return this.getParentComputeNode(); } /** * pointer to the ComputeNode (usually Nub) that uses the ParamsEquation that * uses this ParamDefinition; for Section params, if returnEnclosingNubForSections * is true, this will return the Nub enclosing the Section, otherwise the section itself */ getParentComputeNode(returnEnclosingNubForSections = true) { let parentCN; if (this._parent) { // ComputeNode utilisant le ParamsEquation parentCN = this._parent.parent; // Section: go up to enclosing Nub if any (might not have any yet when unserializing sessions) if (parentCN instanceof internal_modules_4.acSection && returnEnclosingNubForSections && parentCN.parent) { parentCN = parentCN.parent; } } else { // fail silently (the story of my life) // throw new Error("ParamDefinition.parentComputeNode : parameter has no parent !"); } return parentCN; } /** * Returns the parent compute node as Nub if it is a Nub; returns * undefined otherwise */ get parentNub() { if (this.parentComputeNode && this.parentComputeNode instanceof internal_modules_3.Nub) { return this.parentComputeNode; } else { return undefined; } } /** * Identifiant unique du Nub parent */ get nubUid() { return this.parentComputeNode.uid; } /** * Type de module du Nub parent */ get nubCalcType() { let parentCalcType; if (this.parentComputeNode instanceof internal_modules_3.Nub) { parentCalcType = this.parentComputeNode.calcType; } else { throw new Error("ParamDefinition.nubCalcType : parameter has no parent !"); } return parentCalcType; } get symbol() { return this._symbol; } setDomain(d) { if (d instanceof internal_modules_8.ParamDomain) { this._domain = d; } else { this._domain = new internal_modules_8.ParamDomain(d); } } get domain() { return this._domain; } get interval() { return this._domain.interval; } get extensionStrategy() { return this._extensionStrategy; } set extensionStrategy(strategy) { this._extensionStrategy = strategy; // synchronise with underlying local ParamValues (for iterator), except for links if ([internal_modules_9.ParamValueMode.SINGLE, internal_modules_9.ParamValueMode.MINMAX, internal_modules_9.ParamValueMode.LISTE].includes(this.valueMode)) { if (this._paramValues) { this._paramValues.extensionStrategy = strategy; } } } get valueMode() { return this._paramValues.valueMode; } /** * Easy setter that propagates the change to other parameters * (default behaviour); use setValueMode(..., false) to prevent * possible infinite loops */ set valueMode(newMode) { this.setValueMode(newMode); } /** * Sets the value mode and asks the Nub to ensure there is only one parameter * in CALC mode * * If propagateToCalculatedParam is true, any param that goes from CALC mode * to any other mode will trigger a reset of the default calculated param at * Nub level * * Propagates modes other than CALC and LINK to the underlying * ParamValues (local instance) */ setValueMode(newMode, propagateToCalculatedParam = true) { const oldMode = this.valueMode; // ignore idempotent calls if (oldMode === newMode) { return; } this.propagateValueMode(oldMode, newMode, propagateToCalculatedParam); // set new mode this._paramValues.valueMode = newMode; } propagateValueMode(oldMode, newMode, propagateToCalculatedParam = true) { if (oldMode === internal_modules_9.ParamValueMode.CALCUL) { if (propagateToCalculatedParam) { const parentNode = this.parentComputeNode; // Set default calculated parameter, only if previous CALC param was // manually set to something else than CALC if (parentNode instanceof internal_modules_3.Nub) { parentNode.resetDefaultCalculatedParam(this); } } } else if (newMode === internal_modules_9.ParamValueMode.CALCUL) { const parentNode = this.parentComputeNode; // set old CALC param to SINGLE mode if (parentNode instanceof internal_modules_3.Nub) { parentNode.unsetCalculatedParam(this); } } } /** * Returns true if this parameter is the calculatedParam of * its parent Nub */ get isCalculated() { if (this.parentNub) { return (this.parentNub.calculatedParam === this); // should be the same object } return false; } /** * Sets this parameter as the one to be calculated */ setCalculated() { if (this.parentNub) { this.parentNub.calculatedParam = this; } } /** * Current values set associated to this parameter; in LINK mode, points * to a remote set of values. */ get paramValues() { if (this.valueMode === internal_modules_9.ParamValueMode.LINK && this.isReferenceDefined()) { return this._referencedValue.getParamValues(); } else { return this._paramValues; } } get referencedValue() { return this._referencedValue; } get family() { return this._family; } get calculability() { if (this._calc === undefined) { const e = new internal_modules_6.Message(internal_modules_6.MessageCode.ERROR_PARAMDEF_CALC_UNDEFINED); e.extraVar.symbol = this.symbol; throw e; } return this._calc; } set calculability(c) { this._calc = c; } /** * Returns true if current value (not singleValue !) is defined */ get hasCurrentValue() { return (this.v !== undefined); } /** * Returns true if held value (not currentValue !) is defined, * depending on the value mode */ get isDefined() { let defined = false; switch (this.valueMode) { case internal_modules_9.ParamValueMode.SINGLE: defined = (this.singleValue !== undefined); break; case internal_modules_9.ParamValueMode.MINMAX: defined = (this.paramValues.min !== undefined && this.paramValues.max !== undefined && this.paramValues.step !== undefined); break; case internal_modules_9.ParamValueMode.LISTE: defined = (this.valueList && this.valueList.length > 0 && this.valueList[0] !== undefined); break; case internal_modules_9.ParamValueMode.CALCUL: if (this.parentNub && this.parentNub.result && this.parentNub.result.resultElements.length > 0) { const res = this.parentNub.result; defined = (res.vCalc !== undefined); } break; case internal_modules_9.ParamValueMode.LINK: defined = this.referencedValue.isDefined(); } return defined; } // -- values getters / setters; in LINK mode, reads / writes from / to the target values get singleValue() { this.checkValueMode([internal_modules_9.ParamValueMode.SINGLE, internal_modules_9.ParamValueMode.CALCUL, internal_modules_9.ParamValueMode.LINK]); return this.paramValues.singleValue; } set singleValue(v) { this.checkValueMode([internal_modules_9.ParamValueMode.SINGLE, internal_modules_9.ParamValueMode.CALCUL, internal_modules_9.ParamValueMode.LINK]); this.checkValueAgainstDomain(v); this.paramValues.singleValue = v; this.initValue = v; this.notifyValueModified(this); } get min() { this.checkValueMode([internal_modules_9.ParamValueMode.MINMAX, internal_modules_9.ParamValueMode.LINK]); return this.paramValues.min; } set min(v) { this.checkValueMode([internal_modules_9.ParamValueMode.MINMAX]); this.paramValues.min = v; } get max() { this.checkValueMode([internal_modules_9.ParamValueMode.MINMAX, internal_modules_9.ParamValueMode.LINK]); return this.paramValues.max; } set max(v) { this.checkValueMode([internal_modules_9.ParamValueMode.MINMAX]); this.paramValues.max = v; } get step() { this.checkValueMode([internal_modules_9.ParamValueMode.MINMAX, internal_modules_9.ParamValueMode.LINK]); return this.paramValues.step; } set step(v) { this.checkValueMode([internal_modules_9.ParamValueMode.MINMAX]); this.paramValues.step = v; } /** * Generates a reference step value, given the current (local) values for min / max */ get stepRefValue() { this.checkValueMode([internal_modules_9.ParamValueMode.MINMAX]); return new internal_modules_5.Interval(1e-9, this._paramValues.max - this._paramValues.min); } get valueList() { this.checkValueMode([internal_modules_9.ParamValueMode.LISTE, internal_modules_9.ParamValueMode.LINK]); return this.paramValues.valueList; } set valueList(l) { this.checkValueMode([internal_modules_9.ParamValueMode.LISTE]); this.paramValues.valueList = l; } count() { return this.paramValues.valuesIterator.count(); } /** * Copies values of p into the current parameter, whatever the valueMode is; if * p has linked values, the target values will be copied and the links won't be kept * @param p a reference ParamDefinition or ParamValues to copy values from */ copyValuesFrom(p) { this.valueMode = p.valueMode; switch (p.valueMode) { case internal_modules_9.ParamValueMode.MINMAX: this.min = p.min; this.max = p.max; this.step = p.step; break; case internal_modules_9.ParamValueMode.LISTE: this.valueList = p.valueList; break; case internal_modules_9.ParamValueMode.LINK: if (p instanceof ParamDefinition) { this.copyValuesFrom(p.referencedValue.getParamValues()); } break; case internal_modules_9.ParamValueMode.CALCUL: case internal_modules_9.ParamValueMode.SINGLE: default: this.singleValue = p.singleValue; } } // for INumberiterator interface get currentValue() { // magically follows links return this.paramValues.currentValue; } /** * Get single value */ getValue() { return this.paramValues.singleValue; } /** * Returns values as a number list, for LISTE and MINMAX modes; * in MINMAX mode, infers the list from min/max/step values * @param extendTo if given, will extend the values list to this number of values */ getInferredValuesList(extendTo) { this.checkValueMode([internal_modules_9.ParamValueMode.LISTE, internal_modules_9.ParamValueMode.MINMAX, internal_modules_9.ParamValueMode.LINK]); return this.paramValues.getInferredValuesList(false, extendTo, true); } /** * Magic method to define current values into Paramvalues set; defines the mode * accordingly by detecting values nature */ setValues(o, max, step) { const oldMode = this.valueMode; this.paramValues.setValues(o, max, step); this.propagateValueMode(oldMode, this.valueMode, true); } /** * Sets the current value of the parameter's own values set, then * notifies all observers */ setValue(val, sender) { this.valueMode = internal_modules_9.ParamValueMode.SINGLE; this.checkValueAgainstDomain(val); this.paramValues.singleValue = val; } /** * Same as setValue() but does not force SINGLE value mode; used for * updating initial value of a DICHO calculated parameter */ setInitValue(val, sender) { this.checkValueAgainstDomain(val); this.initValue = val; } /** * Validates the given numeric value against the definition domain; throws * an error if value is outside the domain */ checkValueAgainstDomain(v) { const sDomain = internal_modules_8.ParamDomainValue[this._domain.domain]; switch (this._domain.domain) { case internal_modules_8.ParamDomainValue.ANY: break; case internal_modules_8.ParamDomainValue.POS: if (v <= 0) { const f = new internal_modules_6.Message(internal_modules_6.MessageCode.ERROR_PARAMDEF_VALUE_POS); f.extraVar.symbol = this.symbol; f.extraVar.value = v; throw f; } break; case internal_modules_8.ParamDomainValue.POS_NULL: if (v < 0) { const f = new internal_modules_6.Message(internal_modules_6.MessageCode.ERROR_PARAMDEF_VALUE_POSNULL); f.extraVar.symbol = this.symbol; f.extraVar.value = v; throw f; } break; case internal_modules_8.ParamDomainValue.NOT_NULL: if (v === 0) { const f = new internal_modules_6.Message(internal_modules_6.MessageCode.ERROR_PARAMDEF_VALUE_NULL); f.extraVar.symbol = this.symbol; throw f; } break; case internal_modules_8.ParamDomainValue.INTERVAL: const min = this._domain.minValue; const max = this._domain.maxValue; if (v < min || v > max) { const f = new internal_modules_6.Message(internal_modules_6.MessageCode.ERROR_PARAMDEF_VALUE_INTERVAL); f.extraVar.symbol = this.symbol; f.extraVar.value = v; f.extraVar.minValue = min; f.extraVar.maxValue = max; throw f; } break; case internal_modules_8.ParamDomainValue.INTEGER: if (!Number.isInteger(v)) { const f = new internal_modules_6.Message(internal_modules_6.MessageCode.ERROR_PARAMDEF_VALUE_INTEGER); f.extraVar.symbol = this.symbol; f.extraVar.value = v; throw f; } break; default: const e = new internal_modules_6.Message(internal_modules_6.MessageCode.ERROR_PARAMDOMAIN_INVALID); e.extraVar.symbol = this.symbol; e.extraVar.domain = sDomain; throw e; } } checkMin(min) { return this.isMinMaxDomainValid(min) && (min < this.max); } checkMax(max) { return this.isMinMaxDomainValid(max) && (this.min < max); } checkMinMaxStep(step) { return this.isMinMaxValid && this.stepRefValue.intervalHasValue(step); } /** * Return true if single value is valid regarding the domain constraints */ get isValueValid() { try { const v = this.paramValues.singleValue; this.checkValueAgainstDomain(v); return true; } catch (e) { return false; } } get isMinMaxValid() { return this.checkMinMax(this.min, this.max); } /** * Return true if current value is valid regarding the range constraints : min / max / step */ get isRangeValid() { switch (this.valueMode) { case internal_modules_9.ParamValueMode.LISTE: return this.isListValid; case internal_modules_9.ParamValueMode.MINMAX: return this.checkMinMaxStep(this.step); } throw new Error(`ParamDefinition.isRangeValid() : valeur ${internal_modules_9.ParamValueMode[this.valueMode]}` + `de ParamValueMode non prise en compte`); } /** * Root method to determine if a field value is valid, regarding the model constraints. * * Does not take care of input validation (ie: valid number, not empty...) because an * invalid input should never be set as a value; input validation is up to the GUI. * * In LINK mode : * - if target is a parameter, checks validity of the target value against the local model constraints * - if target is a result : * - is result is already computed, checks validity of the target result against the local model constraints * - if it is not, checks the "computability" of the target Nub (ie. validity of the Nub) to allow * triggering chain computation */ get isValid() { switch (this.valueMode) { case internal_modules_9.ParamValueMode.SINGLE: return this.isValueValid; case internal_modules_9.ParamValueMode.MINMAX: case internal_modules_9.ParamValueMode.LISTE: return this.isRangeValid; case internal_modules_9.ParamValueMode.CALCUL: return true; case internal_modules_9.ParamValueMode.LINK: if (!this.isReferenceDefined()) { return false; } // valuesIterator covers both target param and target result cases const iterator = this.valuesIterator; if (iterator) { try { for (const v of iterator) { this.checkValueAgainstDomain(v); } return true; } catch (e) { return false; } } else { // undefined iterator means target results are not computed yet // check target Nub computability return this.referencedValue.nub.isComputable(); } } throw new Error(`ParamDefinition.isValid() : valeur de ParamValueMode '${internal_modules_9.ParamValueMode[this.valueMode]}' inconnue`); } /** * Sets the current parameter to LINK mode, pointing to the given * symbol (might be a Parameter (computed or not) or an ExtraResult) * of the given Nub * * Prefer @see defineReferenceFromLinkedValue() whenever possible * * @param nub * @param symbol */ defineReference(nub, symbol) { // prevent loops - beware that all Nubs must be registered in the // current session or this test will always fail if (!this.isAcceptableReference(nub, symbol)) { throw new Error(`defineReference() : link target ${nub.uid}.${symbol} is not an available linkable value`); } // clear current reference this.undefineReference(); // find what the symbol points to if (nub) { // 1. extra result ? // - start here to avoid extra results being presented as // parameters by the iterator internally used by nub.getParameter() // - extra results with no family are linkable only if linking parameter family is ANY if (Object.keys(nub.resultsFamilies).includes(symbol) && (nub.resultsFamilies[symbol] !== undefined || this._family === ParamFamily.ANY)) { this._referencedValue = new internal_modules_2.LinkedValue(nub, undefined, symbol); } else { // 2. is it a parameter (possibly in CALC mode) ? const p = nub.getParameter(symbol); if (p) { this._referencedValue = new internal_modules_2.LinkedValue(nub, p, symbol); } } } if (this._referencedValue) { // set value mode this.valueMode = internal_modules_9.ParamValueMode.LINK; } else { throw new Error(`defineReference - could not find target for ${nub.uid}.${symbol}`); } } /** * Asks the Session for available linkabke values for the current parameter, * and returns true if given { nub / symbol } pair is part of them */ isAcceptableReference(nub, symbol) { const linkableValues = internal_modules_1.Session.getInstance().getLinkableValues(this); for (const lv of linkableValues) { if (lv.isTargetOf(nub, symbol)) { return true; } } return false; } defineReferenceFromLinkedValue(target) { this._referencedValue = target; this.valueMode = internal_modules_9.ParamValueMode.LINK; } undefineReference() { this._referencedValue = undefined; } isReferenceDefined() { return (this._referencedValue !== undefined); } /** * Returns an object representation of the Parameter's current state * @param nubUidsInSession UIDs of Nubs that will be saved in session along with this one; * useful to determine if linked parameters must be kept as links or have their value * copied (if target is not in UIDs list); if undefined, wil consider all Nubs as * available (ie. will not break links) */ objectRepresentation(nubUidsInSession) { // parameter representation const paramRep = { symbol: this.symbol, mode: internal_modules_9.ParamValueMode[this.valueMode] }; // adjust parameter representation depending on value mode switch (this.valueMode) { case internal_modules_9.ParamValueMode.SINGLE: paramRep.value = this.singleValue; break; case internal_modules_9.ParamValueMode.CALCUL: // save initial value if param is calculated using dichotomy if (this.calculability === ParamCalculability.DICHO) { paramRep.value = this.singleValue; } break; case internal_modules_9.ParamValueMode.MINMAX: paramRep.min = this.min; paramRep.max = this.max; paramRep.step = this.step; paramRep.extensionStrategy = this.extensionStrategy; break; case internal_modules_9.ParamValueMode.LISTE: paramRep.values = this.valueList; paramRep.extensionStrategy = this.extensionStrategy; break; case internal_modules_9.ParamValueMode.LINK: if (nubUidsInSession === undefined || nubUidsInSession.includes(this._referencedValue.nub.uid)) { // target Nub is available in session, link won't get broken paramRep.targetNub = this._referencedValue.nub.uid; paramRep.targetParam = this._referencedValue.symbol; } else { // target Nub will be lost, copy value(s) to keep consistency const targetPV = this._referencedValue.getParamValues(true); paramRep.mode = internal_modules_9.ParamValueMode[targetPV.valueMode]; switch (targetPV.valueMode) { case internal_modules_9.ParamValueMode.SINGLE: paramRep.value = targetPV.singleValue; break; case internal_modules_9.ParamValueMode.MINMAX: paramRep.min = targetPV.min; paramRep.max = targetPV.max; paramRep.step = targetPV.step; paramRep.extensionStrategy = targetPV.extensionStrategy; break; case internal_modules_9.ParamValueMode.LISTE: paramRep.values = targetPV.valueList; paramRep.extensionStrategy = targetPV.extensionStrategy; break; // should never be in LINK or CALC mode (see LinkedValue methods) } } break; } return paramRep; } /** * Fills the current Parameter, provided an object representation * @param obj object representation of a Parameter state * @returns object { * calculated: boolean true if loaded parameter is in CALC mode * hasErrors: boolean true if errors were encountered during loading * } */ loadObjectRepresentation(obj, setCalcMode = true) { const ret = { calculated: false, hasErrors: false }; // set mode const mode = internal_modules_9.ParamValueMode[obj.mode]; // get enum index for string value // when loading parent Nub, setting mode to CALC would prevent ensuring // consistency when setting calculatedParam afterwards if (mode !== internal_modules_9.ParamValueMode.CALCUL || setCalcMode) { try { this.setValueMode(mode, setCalcMode); } catch (err) { // silent fail : impossible to determine if this is an error, because // at this time, it is possible that no candidate for calculatedParam can // be found, since Nub children are not loaded yet (see nghyd#263) /* ret.hasErrors = true; console.error("loadObjectRepresentation: set valueMode error"); */ } } // set value(s) switch (mode) { case internal_modules_9.ParamValueMode.SINGLE: this.singleValue = obj.value; break; case internal_modules_9.ParamValueMode.MINMAX: this.min = obj.min; this.max = obj.max; this.step = obj.step; if (obj.extensionStrategy !== undefined) { this.extensionStrategy = obj.extensionStrategy; } break; case internal_modules_9.ParamValueMode.LISTE: this.valueList = obj.values; if (obj.extensionStrategy !== undefined) { this.extensionStrategy = obj.extensionStrategy; } break; case internal_modules_9.ParamValueMode.CALCUL: // although calculated param is set at Nub level (see below), // it is detected as "the only parameter in CALC mode" if (obj.value !== undefined) { // dichotomy params have an initial value, others don't this.singleValue = obj.value; } ret.calculated = true; break; case internal_modules_9.ParamValueMode.LINK: // formulaire dont le Nub est la cible du lien const destNub = internal_modules_1.Session.getInstance().findNubByUid(obj.targetNub); if (destNub) { try { this.defineReference(destNub, obj.targetParam); } catch (err) { // silent fail : impossible to determine if this is an error, because // fixLinks() might solve it later } } // si la cible du lien n'existe pas, Session.fixLinks() est censé s'en occuper break; default: throw new Error(`session file : invalid value mode '${obj.valueMode}' in param object ${obj.symbol}`); } return ret; } /** * Returns true if both the Nub UID and the symbol are identical */ equals(p) { return (this.nubUid === p.nubUid && this.symbol === p.symbol); } /** * Returns true if this parameter's value can safely be linked to the * given parameter "src" (ie. without leading to circular dependencies) */ isLinkableTo(src) { // did we loop back to the candidate parameter ? if (this.equals(src)) { // prevent infinite recursion return false; } if (this.valueMode === internal_modules_9.ParamValueMode.CALCUL) { // if my Nub doesn't depend on your result, // and my Nub doesn't already have a parameter linked to you, // you may depend on its result (me) ! if (this._parent && this._parent.parent) { const myNub = this.parentComputeNode; const ok = (!myNub.dependsOnNubResult(src.parentNub) && !myNub.dependsOnParameter(src)); return ok; } } if (this.valueMode === internal_modules_9.ParamValueMode.LINK && this.isReferenceDefined()) { // does the link point to an extra result ? if (this.referencedValue.isExtraResult()) { // if the target Nub doesn't depend on your result, you may depend on its extra result (me) ! return (!this.referencedValue.nub.dependsOnNubResult(src.parentNub)); } else { // the link points to a parameter, computed or not // recursively follow links, unless it loops back to the original parameter return this.referencedValue.element.isLinkableTo(src); } } // SINGLE / MINMAX / LISTE parameters can always be linked without danger return true; } /** * Returns true if this parameter is ultimately (after followink links chain) linked * to a result or extra result of any of the given nubs. * If followLinksChain is false, will limit theexploration to the first target level only */ isLinkedToResultOfNubs(nubs, followLinksChain = true) { let linked = false; if (this.valueMode === internal_modules_9.ParamValueMode.LINK && this.isReferenceDefined()) { const targetNubUid = this.referencedValue.nub.uid; // is target a result ? if (this.referencedValue.isResult() || this.referencedValue.isExtraResult()) { for (const y of nubs) { if (y.uid === targetNubUid) { // dependence found ! linked = true; } else if (this.referencedValue.nub.dependsOnNubResult(y)) { // indirect result dependence found ! linked = true; } } } else { // target is a parameter // recursion ? if (followLinksChain) { linked = this.referencedValue.element.isLinkedToResultOfNubs(nubs); } } } return linked; } /** * Returns true if the current parameter is directly linked to the * Nub having UID "uid", its parent or any of its children * @param uid * @param symbol symbol of the target parameter whose value change triggered this method; * if current Parameter targets this symbol, Nub will be considered dependent * @param includeValuesLinks if true, even if this Parameter targets a non-calculated non-modified * parameter, Nub will be considered dependent @see jalhyd#98 */ dependsOnNubFamily(uid, symbol, includeValuesLinks = false) { let linked = false; if (this.valueMode === internal_modules_9.ParamValueMode.LINK && this.isReferenceDefined()) { const ref = this._referencedValue; // direct, parent or children reference ? if ((ref.nub.uid === uid) || (ref.nub.parent && ref.nub.parent.uid === uid) || (ref.nub.getChildren().map((c) => c.uid).includes(uid))) { linked = ((symbol !== undefined && symbol === ref.symbol) || ref.isCalculated() || includeValuesLinks); } // else no recursion, checking level 1 only } return linked; } // interface IterableValues /** * Transparent proxy to own values iterator or targetted values iterator (in LINK mode) */ get valuesIterator() { if (this.valueMode === internal_modules_9.ParamValueMode.LINK && this.isReferenceDefined()) { try { return this._referencedValue.getParamValues().valuesIterator; } catch (e) { // values are not computed yet return undefined; } } else { return this.paramValues.valuesIterator; } } getExtendedValuesIterator(size) { return this.paramValues.getValuesIterator(false, size, true); } /** * Returns true if there are more than 1 value associated to this parameter; * might be its own values (MINMAX / LISTE mode), or targetted values (LINK mode) */ get hasMultipleValues() { if (this.valueMode === internal_modules_9.ParamValueMode.LINK && this.isReferenceDefined()) { return this._referencedValue.hasMultipleValues(); } else { if (this.valueMode === internal_modules_9.ParamValueMode.CALCUL) { return this.parentNub.resultHasMultipleValues(); } else { return this.paramValues.hasMultipleValues; } } } initValuesIterator(reverse = false) { return this.paramValues.getValuesIterator(reverse, undefined, true); } get hasNext() { return this.paramValues.hasNext; } next() { return this.paramValues.next(); } /** trick method - use .next() instead, unless you are explicitely in jalhyd#222 case */ nextValue() { return this.next(); } [Symbol.iterator]() { return this.paramValues; } // interface IObservable /** * ajoute un observateur à la liste */ addObserver(o) { this._observable.addObserver(o); } /** * supprime un observateur de la liste */ removeObserver(o) { this._observable.removeObserver(o); } /** * notifie un événement aux observateurs */ notifyObservers(data, sender) { this._observable.notifyObservers(data, sender); } /** * variable calculable par l'équation ? */ isAnalytical() { return this.calculability === ParamCalculability.EQUATION; } /** * Get the highest nub in the child nub hierarchy */ get originNub() { let originNub = this.parentNub; while (true) { if (originNub.parent === undefined) { return originNub; } originNub = originNub.parent; } } /** * Renvoie la valeur actuelle du paramètre (valeur courante ou résultat si calculé) */ get V() { // paramètre en calcul ou résultat complémentaire if (this.parentNub.result !== undefined) { if (this.parentNub.result.resultElement.ok) { if (this.parentNub.result.resultElement.values[this.symbol] !== undefined) { return this.parentNub.result.resultElement.values[this.symbol]; } } } // sinon, valeur courante return this.currentValue; } clone() { const res = new ParamDefinition(this._parent, this._symbol, this.domain.clone(), this._unit, undefined, this._family, this.visible); res._paramValues = this._paramValues.clone(); res.v = this.v; res._initValue = this._initValue; res._calc = this._calc; res._extensionStrategy = this._extensionStrategy; res._referencedValue = this._referencedValue; return res; } /** * notification envoyée après la modification de la valeur du paramètre */ notifyValueModified(sender) { this.notifyObservers({ action: "paramdefinitionAfterValue" }, sender); } /** * vérifie si un min/max est valide par rapport au domaine de définition */ isMinMaxDomainValid(v) { if (v === undefined) { return false; } if (this.valueMode === internal_modules_9.ParamValueMode.MINMAX) { try { this.checkValueAgainstDomain(v); } catch (e) { return false; } } return true; } checkMinMax(min, max) { return this.isMinMaxDomainValid(min) && this.isMinMaxDomainValid(max) && (min < max); } get isListValid() { if (this.paramValues.valueList === undefined) { return false; } for (const v of this.paramValues.valueList) { try { this.checkValueAgainstDomain(v); } catch (e) { return false; } } return true; } /** * Throws an error if this.paramValues._valueMode is not the expected value mode. * In LINK mode, the tested valueMode is the mode of the ultimately targetted * set of values (may never be LINK) */ checkValueMode(expected) { if (!expected.includes(this.valueMode)) { throw new Error(`ParamDefinition : mode de valeurs ${internal_modules_9.ParamValueMode[this.valueMode]} incorrect pour le paramètre ${this.symbol}`); } } } exports.ParamDefinition = ParamDefinition; //# sourceMappingURL=param-definition.js.map