jalhyd
Version:
JaLHyd, a Javascript Library for Hydraulics
1,184 lines • 67.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Nub = 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");
const internal_modules_11 = require("./internal_modules");
const internal_modules_12 = require("./internal_modules");
const internal_modules_13 = require("./internal_modules");
class NubIterator {
constructor(n) {
this._nubs = [];
this.fill(n);
this._index = 0;
}
fill(n) {
this._nubs.push(n);
for (const c of n.getChildren()) {
this.fill(c);
}
}
// interface IterableIterator
next() {
const i = this._index;
if (this._index < this._nubs.length) {
this._index = i + 1;
return {
done: false,
value: this._nubs[i]
};
}
else {
return {
done: true,
value: undefined
};
}
}
[Symbol.iterator]() {
return this;
}
}
/**
* Classe abstraite de Noeud de calcul dans une session :
* classe de base pour tous les calculs
*/
class Nub extends internal_modules_1.ComputeNode {
constructor(prms, dbg = false) {
super(prms, dbg);
/** paramétrage de la dichotomie */
this.dichoStartIntervalMaxSteps = 100;
/** properties describing the Nub type */
this._props = new internal_modules_8.Props();
/** a rough indication of calculation progress, between 0 and 100 */
this._progress = 0;
/** allows notifying of progress every X milliseconds only */
this.previousNotificationTimestamp = 0;
this.deleteAllChildren();
this._observable = new internal_modules_11.Observable();
this._defaultCalculatedParam = this.getFirstAnalyticalParameter();
this.resetDefaultCalculatedParam();
}
/**
* Find longest series, BUT: if any varying parameter is a calculation result,
* its values can't be extended (could be, but not at the moment), so
* the global size in this case will be the min of sizes of linked variated results
* @param variated list of variated parameter/valuesList pairs to examinate
*/
static findVariatedSize(variated) {
if (variated.length > 0) {
let minLinkedResultParam;
let minLinkedResultsSize = Infinity;
let longest = 0;
for (let i = 0; i < variated.length; i++) {
if (variated[i].param.valueMode === internal_modules_6.ParamValueMode.LINK
&& variated[i].param.referencedValue.isCalculated()) {
if (variated[i].values.valuesIterator.count() < minLinkedResultsSize) {
minLinkedResultParam = variated[i];
minLinkedResultsSize = minLinkedResultParam.values.valuesIterator.count();
}
}
if (variated[i].values.valuesIterator.count() > variated[longest].values.valuesIterator.count()) {
longest = i;
}
}
return {
size: variated[longest].values.valuesIterator.count(),
longest,
minLinkedResultParam
};
}
else {
return {
size: 0,
longest: undefined,
minLinkedResultParam: undefined
};
}
}
static concatPrms(p1, p2) {
const p3 = p1;
for (const p of p2) {
p3.push(p);
}
return p3;
}
get parent() {
return this._parent;
}
setParent(p) {
this._parent = p;
}
get intlType() {
return this._intlType;
}
get result() {
return this._result;
}
/**
* Local setter to set result element of Equation() / Solve() / … as current
* ResultElement, instead of overwriting the whole Result object
* (used by CalcSerie with varying parameters)
*/
set currentResultElement(r) {
if (!this._result) {
this.initNewResultElement();
}
this._result.resultElement = r.resultElement;
}
set properties(props) {
this.setProperties(props);
}
/**
* return ParamsEquation of all children recursively
*/
get childrenPrms() {
const prms = [];
if (this._children.length) { // if called within constructor, default class member value is not set yet
for (const child of this._children) {
prms.push(child.prms);
if (child.getChildren()) {
if (child.getChildren().length) {
Nub.concatPrms(prms, child.childrenPrms);
}
}
}
}
return prms;
}
/**
* Returns an array with the calculable parameters
*/
get calculableParameters() {
const calcPrms = [];
for (const p of this.parameterIterator) {
if ([internal_modules_5.ParamCalculability.DICHO, internal_modules_5.ParamCalculability.EQUATION].includes(p.calculability)) {
calcPrms.push(p);
}
}
return calcPrms;
}
/**
* Returns an iterator over :
* - own parameters (this._prms)
* - children parameters (this._children[*]._prms)
*/
get parameterIterator() {
const prms = [];
prms.push(this._prms);
if (this._children) {
Nub.concatPrms(prms, this.childrenPrms);
}
return new internal_modules_7.ParamsEquationArrayIterator(prms);
}
get progress() {
return this._progress;
}
/**
* Updates the progress percentage and notifies observers,
* at most once per 300ms
*/
set progress(v) {
this._progress = v;
const currentTime = new Date().getTime();
if ((currentTime - this.previousNotificationTimestamp) > 30
|| v === 100) {
// console.log(">> notifying !");
this.notifyProgressUpdated();
this.previousNotificationTimestamp = currentTime;
}
}
get calcType() {
return this.getPropValue("calcType");
}
/**
* set Nub calculator type.
* give children the opportunity to react to assignment
* @see Structure
*/
setCalculatorType(ct) {
this.setPropValue("calcType", ct);
}
get calculatedParam() {
return this._calculatedParam;
}
/**
* Sets p as the parameter to be computed; sets it to CALC mode
*/
set calculatedParam(p) {
this._calculatedParam = p;
this._calculatedParam.valueMode = internal_modules_6.ParamValueMode.CALCUL;
}
/**
* Returns a parameter descriptor compatible with Calc() methods,
* ie. a symbol string for a Nub's main parameter, or an object
* of the form { uid: , symbol: } for a Nub's sub-Nub parameter
* (ex: Structure parameter in ParallelStructure)
*/
get calculatedParamDescriptor() {
if (this.uid === this._calculatedParam.nubUid) {
return this._calculatedParam.symbol;
}
else {
return {
uid: this._calculatedParam.nubUid,
symbol: this._calculatedParam.symbol
};
}
}
// move code out of setter to ease inheritance
setProperties(props, resetProps = false) {
// copy observers
const observers = this._props.getObservers();
// empty props
if (resetProps) {
this._props.reset();
}
// restore observers
for (const obs of observers) {
this._props.addObserver(obs);
}
// set new props values
let error;
for (const p of props.keys) {
// try properties one by one so that if an error is thrown,
// remaining properties are still copied
let oldValue;
try {
// keep old value to restore it in case of an error, or else it breaks fixTargets()
oldValue = this.getPropValue(p); // should always be undefined but who knows
this.setPropValue(p, props.getPropValue(p));
}
catch (e) {
error = e;
// no one will notice ^^
this.setPropValue(p, oldValue);
}
}
// throw caught error if any
if (error) {
throw error;
}
}
/**
* Finds the previous calculated parameter and sets its mode to SINGLE
*/
unsetCalculatedParam(except) {
for (const p of this.parameterIterator) {
if (p.valueMode === internal_modules_6.ParamValueMode.CALCUL
&& p !== except) {
p.setValueMode(internal_modules_6.ParamValueMode.SINGLE, false);
}
}
}
/**
* @param excluded parameter to exclude from search
* @returns true if one of the parameters (other than excluded if provided) is in calculated mode
*/
hasCalculedParam(excluded) {
for (const p of this._prms) {
if (p.valueMode == internal_modules_6.ParamValueMode.CALCUL && (p !== excluded || excluded === undefined)) {
return true;
}
}
return false;
}
/**
* Tries to reset the calculated parameter, successively, to :
* - the default one if it is in SINGLE mode
* - the first SINGLE calculable parameter other than requirer
* - the first MINMAX/LISTE calculable parameter other than requirer
*
* If no default calculated parameter is defined, does nothing.
*
* @param force checking of non pre-existing calculated parameter
*/
resetDefaultCalculatedParam(requirer, force = true) {
if (this._defaultCalculatedParam) {
// if forced, first check there is not already another parameter
// in calculated mode (eg. in a sibling nub) other than requirer (if provided)
let doReset = true;
if (!force) {
// children (except for Pab: in this case, child parameters
// can be calculated and must not prevent reset)
if (!(this instanceof internal_modules_3.Pab)) {
for (const kid of this.getChildren()) {
doReset = doReset && !kid.hasCalculedParam(requirer);
}
}
doReset = doReset && !this.hasCalculedParam(requirer);
}
if (doReset) {
// if default calculated param is not eligible to CALC mode
if (requirer === this._defaultCalculatedParam
|| this._defaultCalculatedParam.valueMode === internal_modules_6.ParamValueMode.LINK || this._defaultCalculatedParam.valueMode === internal_modules_6.ParamValueMode.LISTE || this._defaultCalculatedParam.valueMode === internal_modules_6.ParamValueMode.MINMAX) {
// first SINGLE calculable parameter if any
const newCalculatedParam = this.findFirstCalculableParameter(requirer);
if (newCalculatedParam) {
this.calculatedParam = newCalculatedParam;
}
else {
// @TODO throws when a new linkable nub is added, and all parameters are already linked !
throw Error("resetDefaultCalculatedParam : could not find any SINGLE/MINMAX/LISTE parameter");
}
}
else {
// default one
this.calculatedParam = this._defaultCalculatedParam;
}
}
}
else {
// do nothing (ex: Section Paramétrée)
}
}
/**
* Returns the first visible calculable parameter other than otherThan,
* that is set to SINGLE, MINMAX or LISTE mode (there might be none)
*/
findFirstCalculableParameter(otherThan) {
for (const p of this.parameterIterator) {
if ([internal_modules_5.ParamCalculability.EQUATION, internal_modules_5.ParamCalculability.DICHO].includes(p.calculability)
&& p.visible
&& p !== otherThan
&& [internal_modules_6.ParamValueMode.SINGLE, internal_modules_6.ParamValueMode.MINMAX, internal_modules_6.ParamValueMode.LISTE].includes(p.valueMode)) {
return p;
}
}
return undefined;
}
/**
* Calculate and put in cache the symbol of first parameter calculable analytically
*/
get firstAnalyticalPrmSymbol() {
if (this._firstAnalyticalPrmSymbol === undefined) {
this._firstAnalyticalPrmSymbol = this.getFirstAnalyticalParameter().symbol;
}
return this._firstAnalyticalPrmSymbol;
}
/**
* Run Equation with first analytical parameter to compute
* Returns the result in number form
*/
EquationFirstAnalyticalParameter() {
const res = this.Equation(this.firstAnalyticalPrmSymbol);
if (!res.ok) {
this._result = res;
throw new Error(this.constructor.name + ".EquationFirstAnalyticalParameter(): fail");
}
return res.vCalc;
}
/**
* Calcul d'une équation quelle que soit l'inconnue à calculer; déclenche le calcul en
* chaîne des modules en amont si nécessaire
* @param sVarCalc nom de la variable à calculer
* @param rInit valeur initiale de la variable à calculer dans le cas de la dichotomie
*/
Calc(sVarCalc, rInit) {
let computedVar;
// overload calculated param at execution time ?
if (sVarCalc) {
computedVar = this.getParameter(sVarCalc);
}
else {
computedVar = this.calculatedParam;
}
if (rInit === undefined) {
rInit = computedVar.initValue;
}
if (computedVar.isAnalytical()) {
this.currentResultElement = this.Equation(sVarCalc);
this._result.symbol = sVarCalc;
return this._result;
}
const resSolve = this.Solve(sVarCalc, rInit);
if (!resSolve.ok) {
this.currentResultElement = resSolve;
this._result.symbol = sVarCalc;
return this._result;
}
const sAnalyticalPrm = this.getFirstAnalyticalParameter().symbol;
computedVar.v = resSolve.vCalc;
const res = this.Equation(sAnalyticalPrm);
res.vCalc = resSolve.vCalc;
this.currentResultElement = res;
this._result.symbol = sVarCalc;
this.notifyResultUpdated();
return this._result;
}
/**
* Calculates required Nubs so that all input data is available;
* uses 50% of the progress
* @returns true if everything went OK, false otherwise
*/
triggerChainCalculation() {
const requiredNubs1stLevel = this.getRequiredNubs();
if (requiredNubs1stLevel.length > 0) {
const progressStep = Nub.progressPercentageAccordedToChainCalculation / requiredNubs1stLevel.length;
for (const rn of requiredNubs1stLevel) {
const r = rn.CalcSerie(undefined, false);
if (r.hasGlobalError() || r.hasOnlyErrors) {
// something has failed in chain
return {
ok: false,
message: new internal_modules_10.Message(internal_modules_10.MessageCode.ERROR_IN_CALC_CHAIN)
};
}
else if (this.resultHasMultipleValues()
&& (r.hasErrorMessages() // some steps failed
// or upstream Nub has already triggered a warning message; pass it on
|| r.globalLog.contains(internal_modules_10.MessageCode.WARNING_ERROR_IN_CALC_CHAIN_STEPS))) {
// if a parameter varies, errors might have occurred for
// certain steps (but not all steps)
return {
ok: true,
message: new internal_modules_10.Message(internal_modules_10.MessageCode.WARNING_ERROR_IN_CALC_CHAIN_STEPS)
};
}
this.progress += progressStep;
}
// round progress to accorded percentage
this.progress = Nub.progressPercentageAccordedToChainCalculation;
}
return {
ok: true,
message: undefined
};
}
/**
* Returns a list of parameters that are fixed, either because their valueMode
* is SINGLE, or because their valueMode is LINK and the reference is
* defined and fixed. Does not take calculated parameters into account.
*/
findFixedParams() {
const fixed = [];
for (const p of this.parameterIterator) {
if (p.visible
&& (p.valueMode === internal_modules_6.ParamValueMode.SINGLE
|| (p.valueMode === internal_modules_6.ParamValueMode.LINK
&& p.isReferenceDefined()
&& !p.referencedValue.hasMultipleValues()))) {
fixed.push(p);
}
}
return fixed;
}
/**
* Returns a list of parameters that are variating, either because their valueMode
* is LISTE or MINMAX, or because their valueMode is LINK and the reference is
* defined and variated. Does not take calculated parameters into account.
*/
findVariatedParams() {
const variated = [];
for (const p of this.parameterIterator) {
if (p.valueMode === internal_modules_6.ParamValueMode.LISTE
|| p.valueMode === internal_modules_6.ParamValueMode.MINMAX
|| (p.valueMode === internal_modules_6.ParamValueMode.LINK
&& p.isReferenceDefined()
&& p.referencedValue.hasMultipleValues())) {
variated.push({
param: p,
// extract variated values from variated Parameters
// (in LINK mode, proxies to target data)
values: p.paramValues
});
}
}
return variated;
}
/**
* compute log stats (# of error, warning, info) on all result elements for all nub (source and source's children)
*/
resultElementsLogStats(stats) {
stats = this._result.resultElementsLogStats(0, stats);
let sn = 1;
const it = this.directChildNubIterator();
let icn = it.next();
while (!icn.done) {
stats = icn.value.result.resultElementsLogStats(sn++, stats);
icn = it.next();
}
return stats;
}
/**
* abstract of iterations logs
*/
generateAbstractLogMessages() {
const stats = this.resultElementsLogStats();
// String()ify numbers below, to avoid decimals formatting on screen (ex: "3.000 errors encoutered...")
if (stats.error > 0) {
if (stats.error > 1) {
this._result.globalLog.add(new internal_modules_10.Message(internal_modules_10.MessageCode.WARNING_ERRORS_ABSTRACT_PLUR, { nb: String(stats.error) }));
}
else {
this._result.globalLog.add(new internal_modules_10.Message(internal_modules_10.MessageCode.WARNING_ERRORS_ABSTRACT, { nb: String(stats.error) }) // should always be "1"
);
}
}
if (stats.warning > 0) {
this._result.globalLog.add(new internal_modules_10.Message(internal_modules_10.MessageCode.WARNING_WARNINGS_ABSTRACT, { nb: String(stats.warning) }));
}
}
/**
* Effectue une série de calculs sur un paramètre; déclenche le calcul en chaîne
* des modules en amont si nécessaire
* @param rInit solution approximative du paramètre
* @param resetDepending true pour resetResult() sur les Nubs dépendants également
*/
CalcSerie(rInit, resetDepending = true) {
// prepare calculation
let extraLogMessage; // potential chain calculation warning to add to result at the end
this.progress = 0;
this.resetResult([], resetDepending);
const ccRes = this.triggerChainCalculation();
if (ccRes.ok) {
// might still have a warning log
if (ccRes.message !== undefined) {
extraLogMessage = ccRes.message;
}
}
else {
// something went wrong in the chain
this._result = new internal_modules_12.Result(undefined, this);
this._result.globalLog.add(ccRes.message);
return this._result;
}
this.copySingleValuesToSandboxValues();
// variated parameters caracteristics
const variated = this.findVariatedParams();
// find calculated parameter
const computedSymbol = this.findCalculatedParameter();
if (rInit === undefined && this.calculatedParam) {
rInit = this.calculatedParam.v;
}
// reinit Result and children Results
this.reinitResult();
if (variated.length === 0) { // no parameter is varying
// prepare a new slot to store results
this.initNewResultElement();
this.doCalc(computedSymbol, rInit); // résultat dans this.currentResult (resultElement le plus récent)
this.progress = 100;
}
else { // at least one parameter is varying
const fvsRes = Nub.findVariatedSize(variated);
let size = fvsRes.size;
const longest = fvsRes.longest;
// if at least one linked variated result was found
if (fvsRes.minLinkedResultParam !== undefined) {
// if the size limited by linked variated results is shorter
// than the size of the longest variating element, limit it
if (fvsRes.minLinkedResultParam.values.valuesIterator.count() < size) {
size = fvsRes.minLinkedResultParam.values.valuesIterator.count();
// and add a warning
const m = new internal_modules_10.Message(internal_modules_10.MessageCode.WARNING_VARIATED_LENGTH_LIMITED_BY_LINKED_RESULT);
m.extraVar.symbol = fvsRes.minLinkedResultParam.param.symbol;
m.extraVar.size = size;
this._result.globalLog.add(m);
}
}
// grant the remaining percentage of the progressbar to local calculation
// (should be 50% if any chain calculation occurred, 100% otherwise)
let progressStep;
const remainingProgress = 100 - this.progress;
progressStep = remainingProgress / size;
// (re)init all iterators
for (const v of variated) {
// copy iterators, for linked params @see bug #222
if (v === variated[longest]) {
v.iterator = v.values.initValuesIterator(false, undefined, true);
}
else {
v.iterator = v.values.initValuesIterator(false, size, true);
}
}
// iterate over longest series (in fact any series would do)
let l = 0; // extra counter if size is limited by linked variated results
while (variated[longest].values.hasNext && l < size) {
// get next value for all variating parameters
for (const v of variated) {
const currentIteratorValue = v.iterator.nextValue();
v.param.v = currentIteratorValue.value;
}
// prepare a new slot to store results
this.initNewResultElement();
// calculate
this.doCalc(computedSymbol, rInit); // résultat dans this.currentResult (resultElement le plus récent)
if (this._result.resultElement.ok) {
rInit = this._result.resultElement.vCalc;
}
// update progress
this.progress += progressStep;
l++;
}
// round progress to 100%
this.progress = 100;
this.generateAbstractLogMessages();
}
if (computedSymbol !== undefined) {
const realSymbol = (typeof computedSymbol === "string") ? computedSymbol : computedSymbol.symbol;
this._result.symbol = realSymbol;
}
this.notifyResultUpdated();
if (extraLogMessage !== undefined) {
this._result.globalLog.add(extraLogMessage);
}
return this._result;
}
addChild(child, after) {
if (after !== undefined) {
this._children.splice(after + 1, 0, child);
}
else {
this._children.push(child);
}
// add reference to parent collection (this)
child.setParent(this);
// postprocessing
this.adjustChildParameters(child);
}
getChildren() {
return this._children;
}
/**
* @returns iterator on direct child nubs (may include extra nubs, see Pab nub)
*/
directChildNubIterator() {
return this._children[Symbol.iterator]();
}
/**
* @returns iterator on all child nubs (recursively)
*/
get allChildNubIterator() {
return new NubIterator(this);
}
/**
* Returns true if all parameters are valid; used to check validity of
* parameters linked to Nub results
*/
isComputable() {
let valid = true;
for (const p of this.prms) {
valid = valid && p.isValid;
}
return valid;
}
/**
* @returns true if nub is an orifice structure
*/
get isOrifice() {
if (this._prms.hasParameter("h1") && this._prms.hasParameter("ZDV")) {
return !this.prms.get("h1").visible && !this.prms.get("ZDV").visible;
}
return false;
}
/**
* Liste des valeurs (paramètre, résultat, résultat complémentaire) liables à un paramètre
* @param src paramètre auquel lier d'autres valeurs
*/
getLinkableValues(src) {
let res = [];
const symbol = src.symbol;
// If parameter comes from the same Nub, its parent or any of its children,
// no linking is possible at all.
// Different Structures in the same parent can get linked to each other.
if (!this.isParentOrChildOf(src.nubUid)
&& ( // check grand-parent for PreBarrage special case @TODO find a generic way
// to perform both tests (synthesise .nubUid and .originNub.uid)
!(this.calcType === internal_modules_1.CalculatorType.PreBarrage)
|| !this.isParentOrChildOf(src.originNub.uid))) {
// 1. own parameters
for (const p of this._prms) {
// if symbol and Nub type are identical
if ((p.symbol === symbol && this.calcType === src.nubCalcType && p.visible)
// or if this is a Section, and src direct parent is a Section too, and symbol is identical
|| (p.symbol === symbol
&& this instanceof internal_modules_3.acSection && src.getParentComputeNode(false) instanceof internal_modules_3.acSection
&& p.visible // different types of sections hide certain parameters
)
// or if family is identical
|| (p.family !== undefined && p.family === src.family && p.visible)
// or if one of the families is ANY and target parameter is visible
|| ((src.family === internal_modules_5.ParamFamily.ANY || p.family === internal_modules_5.ParamFamily.ANY)
&& p.visible)) {
// if variability doesn't cause any problem (a non-variable
// parameter cannot be linked to a variating one)
if (src.calculability !== internal_modules_5.ParamCalculability.FIXED || !p.hasMultipleValues) {
// if it is safe to link p's value to src
if (p.isLinkableTo(src)) {
// if p is a CALC param of a Structure other than "Q"
// (structures always have Q as CALC param and cannot have another)
// or a CALC param of a Section, that is not sibling of the target
// (to prevent circular dependencies among ParallelStructures),
// expose its parent
if (((this instanceof internal_modules_3.Structure && p.symbol !== "Q" && !this.isSiblingOf(src.nubUid))
|| this instanceof internal_modules_3.acSection)
&& (p.valueMode === internal_modules_6.ParamValueMode.CALCUL)) {
if (this.parent._prms.hasParameter(symbol)) {
// trick to expose p as a result of the parent Nub
res.push(new internal_modules_4.LinkedValue(this.parent, p, p.symbol));
}
else {
// in case parent does not hold parameter, inform about real owning nub (this)
res.push(new internal_modules_4.LinkedValue(this.parent, p, p.symbol, undefined, this));
}
}
else {
// do not suggest parameters that are already linked to another one
if (p.valueMode !== internal_modules_6.ParamValueMode.LINK) {
res.push(new internal_modules_4.LinkedValue(this, p, p.symbol));
}
}
}
}
}
}
// 2. extra results
if (this._resultsFamilies) {
// if I don't depend on your result, you may depend on mine !
if (!this.dependsOnNubResult(src.parentNub)) {
const erk = Object.keys(this._resultsFamilies);
// browse extra results
for (const erSymbol of erk) {
const erFamily = this._resultsFamilies[erSymbol];
// if family is identical or ANY, and variability doesn't cause any problem
if ((( // both families cannot be undefined
erFamily === src.family
&& erFamily !== undefined)
|| erFamily === internal_modules_5.ParamFamily.ANY
|| src.family === internal_modules_5.ParamFamily.ANY)
&& (src.calculability !== internal_modules_5.ParamCalculability.FIXED
|| !this.resultHasMultipleValues())) {
res.push(new internal_modules_4.LinkedValue(this, undefined, erSymbol));
}
}
}
}
}
// 3. children Nubs, except for PAB and MRC and PreBarrage
if (!(this instanceof internal_modules_3.MacrorugoCompound)
&& ( // meta-except, if source param in a PAB's Cloison (should only apply to QA)
!(this instanceof internal_modules_3.Pab)
|| (src.originNub instanceof internal_modules_3.Pab
&& src.symbol === "QA"))
&& this.calcType !== internal_modules_1.CalculatorType.PreBarrage) {
for (const cn of this.getChildren()) {
res = res.concat(cn.getLinkableValues(src));
}
}
return res;
}
/**
* Returns true if the given Nub UID is either of :
* - the current Nub UID
* - the current Nub's parent Nub UID
* - the UID of any of the current Nub's children
*/
isParentOrChildOf(uid) {
if (this.uid === uid) {
return true;
}
if (this._parent && this._parent.uid === uid) {
return true;
}
for (const c of this.getChildren()) {
if (c.uid === uid) {
return true;
}
}
return false;
}
/**
* Returns true if the given Nub UID :
* - is the current Nub UID
* - is the UID of any of the current Nub's siblings (children of the same parent)
*/
isSiblingOf(uid) {
if (this.uid === uid) {
return true;
}
if (this._parent) {
for (const c of this._parent.getChildren()) {
if (c.uid === uid) {
return true;
}
}
return true;
}
return false;
}
/**
* Returns all Nubs whose results are required by the given one,
* without following links (stops when it finds a Nub that has to
* be calculated). Used to trigger chain calculation.
*/
getRequiredNubs(visited = []) {
let requiredNubs = [];
// prevent loops
if (!visited.includes(this.uid)) {
visited.push(this.uid);
// inspect all target Nubs
for (const p of this.parameterIterator) {
if (p.valueMode === internal_modules_6.ParamValueMode.LINK && p.isReferenceDefined()) {
const nub = p.referencedValue.nub;
// a Nub is required if I depend on its result
if (this.dependsOnNubResult(nub, false)) {
requiredNubs.push(nub);
}
}
}
}
// 1. deduplicate
requiredNubs = requiredNubs.filter((element, index, self) => self.findIndex((e) => e.uid === element.uid) === index);
// 2. only keep requiredNubs that are not dependencies of others
const realRequiredNubs = [];
for (const rn of requiredNubs) {
let keep = true;
// test all combinations
for (const rn2 of requiredNubs) {
if (rn.uid !== rn2.uid) {
keep = (keep
&& !rn2.dependsOnNubResult(rn, true));
}
}
if (keep) {
realRequiredNubs.push(rn);
}
}
return realRequiredNubs;
}
/**
* Returns all Nubs whose results are required by the given one,
* following links. Used by Solveur.
*/
getRequiredNubsDeep(visited = []) {
let requiredNubs = this.getRequiredNubs(visited);
for (const rn of requiredNubs) {
requiredNubs = requiredNubs.concat(rn.getRequiredNubsDeep(visited));
}
requiredNubs = requiredNubs.filter((item, index) => requiredNubs.indexOf(item) === index // deduplicate
);
return requiredNubs;
}
/**
* Returns all Nubs whose parameters or results are targetted
* by the given one.
* (used for dependencies checking at session saving time)
*/
getTargettedNubs(visited = []) {
const targettedNubs = [];
// prevent loops
if (!visited.includes(this.uid)) {
visited.push(this.uid);
// inspect all target Nubs
for (const p of this.parameterIterator) {
if (p.valueMode === internal_modules_6.ParamValueMode.LINK && p.isReferenceDefined()) {
targettedNubs.push(p.referencedValue.nub);
}
}
}
return targettedNubs;
}
/**
* Returns true if
* - this Nub
* - any of this Nub's children
* - this Nub's parent
* depends on
* - the result of the given Nub
* - the result of any of the given Nub's children
* [ note: but NOT the result of the given Nub's parent, or the given Nub might
* be recalculated independently, which is not wanted ]
*
* (ie. "my family depends on the result of your family")
*
* If followLinksChain is false, will limit the exploration to the first target level only
*/
dependsOnNubResult(nub, followLinksChain = true) {
let thisFamily = [this];
if (this._parent) {
thisFamily = thisFamily.concat(this._parent);
}
thisFamily = thisFamily.concat(this.getChildren());
let yourFamily = [];
const you = nub;
if (you) {
yourFamily = [you].concat(you.getChildren());
}
let depends = false;
outerloop: for (const t of thisFamily) {
// look for a parameter linked to the result of any Nub of yourFamily
for (const p of t.prms) {
if (p.valueMode === internal_modules_6.ParamValueMode.LINK) {
if (p.isLinkedToResultOfNubs(yourFamily, followLinksChain)) {
// dependence found !
depends = true;
break outerloop;
}
}
}
}
return depends;
}
/**
* Returns true if this nub (parent and children included)
* directly requires (1st level only)
* the given parameter
*/
dependsOnParameter(src) {
let thisFamily = [this];
if (this._parent) {
thisFamily = thisFamily.concat(this._parent);
}
thisFamily = thisFamily.concat(this.getChildren());
let depends = false;
outerloop: for (const t of thisFamily) {
// look for a parameter of thisFamily that is directly linked to src
for (const p of t.prms) {
if (p.valueMode === internal_modules_6.ParamValueMode.LINK
&& p.isReferenceDefined()
&& p.referencedValue.isParameter()) {
if (p.referencedValue.nub.uid === src.nubUid
&& p.referencedValue.symbol === src.symbol) {
// dependence found !
depends = true;
break outerloop;
}
}
}
}
return depends;
}
/**
* Returns true if the computation of the current Nub (parent and children
* included) directly requires (1st level only), anything (parameter or result)
* from the given Nub 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 Nub targets this symbol, it will be considered dependent
* @param includeValuesLinks if true, even if this Nub targets only non-calculated non-modified
* parameters, it will be considered dependent @see jalhyd#98
* @param includeOtherDependencies if true, then:
* - if this Nub is a Solveur, also returns true if ${uid} is either the X or the Ytarget's parent
* Nub of this Solveur
* - if this Nub is a Verificateur, also returns true if ${uid} is either a selected Custom
* Species or the Pass to check of this Verificateur
*/
resultDependsOnNub(uid, visited = [], symbol, includeValuesLinks = false, includeOtherDependencies = false) {
var _a, _b;
if (uid !== this.uid && !visited.includes(this.uid)) {
visited.push(this.uid);
// does any of our parameters depend on the target Nub ?
for (const p of this._prms) {
if (p.valueMode === internal_modules_6.ParamValueMode.LINK) {
if (p.dependsOnNubFamily(uid, symbol, includeValuesLinks)) {
return true;
}
}
}
// does any of our parent's parameters depend on the target Nub ?
if (this._parent) {
if (this._parent.resultDependsOnNub(uid, visited, symbol, includeValuesLinks, includeOtherDependencies)) {
return true;
}
}
// does any of our children' parameters depend on the target Nub ?
for (const c of this.getChildren()) {
if (c.resultDependsOnNub(uid, visited, symbol, includeValuesLinks, includeOtherDependencies)) {
return true;
}
}
// other dependencies
if (includeOtherDependencies) {
// is this a Solveur
if (this instanceof internal_modules_3.Solveur) {
return ((((_a = this.searchedParameter) === null || _a === void 0 ? void 0 : _a.nubUid) === uid)
|| (((_b = this.nubToCalculate) === null || _b === void 0 ? void 0 : _b.uid) === uid));
}
if (this instanceof internal_modules_3.Verificateur) {
return ((this.nubToVerify !== undefined && this.nubToVerify.uid === uid)
|| (this.speciesList.includes(uid)));
}
if (this instanceof internal_modules_3.MacrorugoRemous) {
return ((this.nubMacroRugo !== undefined && this.getPropValue("nubMacroRugo") === uid));
}
}
}
return false;
}
/**
* Returns true if the computation of the current Nub has multiple values
* (whether it is already computed or not), by detecting if any parameter,
* linked or not, is variated
*/
resultHasMultipleValues() {
for (const p of this.parameterIterator) {
if (p.valueMode === internal_modules_6.ParamValueMode.MINMAX || p.valueMode === internal_modules_6.ParamValueMode.LISTE) {
return true;
}
else if (p.valueMode === internal_modules_6.ParamValueMode.LINK && p.isReferenceDefined()) {
// indirect recursivity
if (p.referencedValue.hasMultipleValues()) {
return true;
}
}
}
return false;
}
/**
* Sets the current result to undefined, as well as the results
* of all depending Nubs; also invalidates all fake ParamValues
* held by LinkedValues pointing to this result
* @param resetDepending true pour resetResult() sur les Nubs dépendants également
*/
resetResult(visited = [], resetDepending = true) {
this._result = undefined;
visited.push(this.uid);
const dependingNubs = internal_modules_3.Session.getInstance().getDependingNubs(this.uid); // @TODO include Solveur / Verificateur links ?
for (const dn of dependingNubs) {
if (resetDepending && !visited.includes(dn.uid)) {
dn.resetResult(visited);
}
dn.resetLinkedParamValues(this);
}
}
/**
* For all parameters pointing to the result of the given Nub,
* invalidates fake ParamValues held by pointed LinkedValue
*/
resetLinkedParamValues(nub) {
for (const p of this.parameterIterator) {
if (p.valueMode === internal_modules_6.ParamValueMode.LINK && p.isReferenceDefined()) {
if ((p.referencedValue.isResult() || p.referencedValue.isExtraResult())
&& p.referencedValue.nub.uid === nub.uid) {
p.referencedValue.invalidateParamValues();
}
}
}
}
/**
* Duplicates the current Nub, but does not register it in the session
*/
clone() {
const serialised = this.serialise();
const clone = internal_modules_3.Session.getInstance().unserialiseSingleNub(serialised, false);
return clone.nub;
}
/**
* Returns a JSON representation of the Nub's current state
* @param extra extra key-value pairs, for ex. calculator title in GUI
* @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)
*/
serialise(extra, nubUidsInSession) {
return JSON.stringify(this.objectRepresentation(extra, nubUidsInSession));
}
/**
* Returns an object representation of the Nub's current state
* @param extra extra key-value pairs, for ex. calculator title in GUI
* @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)
*/
objectRepresentation(extra, nubUidsInSession) {
let ret = {
uid: this.uid,
props: this.invertedPropertiesEnumAndValues(),
};
if (extra) {
ret = Object.assign(Object.assign({}, ret), { meta: extra }); // merge properties
}
ret = Object.assign(Object.assign({}, ret), { children: [], parameters: [] }); // extend here to make "parameters" the down-most key
// iterate over local parameters
const localParametersIterator = new internal_modules_7.ParamsEquationArrayIterator([this._prms]);
for (const p of localParametersIterator) {
if (p.visible) {
ret.parameters.push(p.objectRepresentation(nubUidsInSession));
}
}
// iterate over children Nubs
for (const child of this._children) {
ret.children.push(child.objectRepresentation(undefined, nubUidsInSession));
}
return ret;
}
/**
* Fills the current Nub with parameter values, provided an object representation
* @param obj object representation of a Nub content (parameters)
* @returns the calculated parameter found, if any - used by child Nub to notify
* its parent of the calculated parameter to set
*/
loadObjectRepresentation(obj) {
// return value
const ret = {
p: undefined,
hasErrors: false,
changedUids: {}
};
// set parameter modes and values
if (obj.parameters && Array.isArray(obj.parameters)) {
// 1st pass: find calculated param
// (if calculated param is not the default one, and default one is processed
// before new one, prevents changing the former's mode from setting the 1st
// param of the Nub to calculated, resetting the mode that had been loaded)
for (const p of obj.parameters) {
if (internal_modules_6.ParamValueMode[p.mode] === internal_modules_6.ParamValueMode.CALCUL) {
this.loadParam(p, ret);
}
}
// define calculated param at Nub level
if (ret.p) {
this.calculatedParam = ret.p;
}
// set parameter modes and values - 2nd pass: set non-calculated params
for (const p of obj.parameters) {
if (internal_modules_6.ParamValueMode[p.mode] !== internal_modules_6.ParamValueMode.CALCUL) {
this.loadParam(p, ret);
}
}
}
// iterate over children if any
if (obj.children && Array.isArray(obj.children)) {
for (const s of obj.children) {
if (Object.keys(s.props).length > 0) {
// decode properties
const props = internal_modules_8.Props.invertEnumKeysAndValuesInProperties(s.props, true);
// create the Nub
const subNub = internal_modules_3.Session.getInstance().createNub(new internal_modules_8.Props(props), this);
// try to keep the original ID
if (!internal_modules_3.Session.getInstance().uidAlreadyUsed(s.uid)) {
subNub.setUid(s.uid);
}
else {
ret.changedUids[s.uid] = subNub.uid;
}
const childRet = subNub.loadObjectRepresentation(s);
// add Structure to parent
this.addChild(subNub);
// set calculated parameter for child ?
if (childRet.p) {
this.calculatedParam = childRet.p;
}
// forward errors
if (childRet.hasErrors) {
ret.hasErrors = true;
}
}
}
}
return ret;
}
/**
* Load parameter state from JSON and amend "ret" status variable;
* to be used by loadObjectRepresentation() only
*/
loadParam(p, ret) {
const param = this.getParamet