jalhyd
Version:
JaLHyd, a Javascript Library for Hydraulics
1,094 lines • 44.3 kB
JavaScript
"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