Skip to content
Snippets Groups Projects
Commit eeb7e0fa authored by Mathias Chouet's avatar Mathias Chouet
Browse files

Merge branch '126-fonctionnalite-plusieurs-parametres-qui-varient-voir-jalhyd-62' into 'master'

Resolve "Fonctionnalité : plusieurs paramètres qui varient (Voir Jalhyd#62)"

Closes #126

See merge request !46
parents 7355826e 2cadfcac
No related branches found
No related tags found
1 merge request!46Resolve "Fonctionnalité : plusieurs paramètres qui varient (Voir Jalhyd#62)"
Showing
with 295 additions and 111 deletions
33-ajout-du-calcul-d-une-passe-a-bassin
62-fonctionnalite-plusieurs-parametres-qui-varient-nghyd-126
\ No newline at end of file
......@@ -105,6 +105,15 @@
</form>
</div>
<mat-form-field>
<mat-select [placeholder]="uitextExtensionStrategy" [(value)]="selectedExtensionStrategy"
data-testid="variable-extension-strategy-select">
<mat-option *ngFor="let e of extensionStrategies" [value]="e.value">
{{ e.label }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div mat-dialog-content *ngIf="viewChart">
......
......@@ -3,7 +3,7 @@ import { Inject, Component, OnInit } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { I18nService } from "../../services/internationalisation/internationalisation.service";
import { NgParameter } from "../../formulaire/ngparam";
import { ParamValueMode } from "jalhyd";
import { ParamValueMode, ExtensionStrategy } from "jalhyd";
import { sprintf } from "sprintf-js";
import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
......@@ -23,6 +23,9 @@ export class DialogEditParamValuesComponent implements OnInit {
/** available decimal separators */
public decimalSeparators: { label: string; value: string; }[];
/** available extension strategies */
public extensionStrategies: { value: ExtensionStrategy; label: string; }[];
/** current decimal separator */
public decimalSeparator: string;
......@@ -76,6 +79,16 @@ export class DialogEditParamValuesComponent implements OnInit {
}
];
this.decimalSeparator = this.decimalSeparators[0].value;
this.extensionStrategies = [
{
value: ExtensionStrategy.REPEAT_LAST,
label: this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_EXT_STRATEGY_REPEAT_LAST")
},
{
value: ExtensionStrategy.RECYCLE,
label: this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_EXT_STRATEGY_RECYCLE")
}
];
// chart configuration
const nDigits = this.appSetupService.displayDigits;
......@@ -104,6 +117,11 @@ export class DialogEditParamValuesComponent implements OnInit {
}
}]
},
elements: {
line: {
tension: 0
}
},
tooltips: {
callbacks: {
label: function(tooltipItem) {
......@@ -166,6 +184,14 @@ export class DialogEditParamValuesComponent implements OnInit {
this.param.valueMode = v;
}
public get selectedExtensionStrategy() {
return this.param.paramDefinition.extensionStrategy;
}
public set selectedExtensionStrategy(es) {
this.param.paramDefinition.extensionStrategy = es;
}
public get isMinMax() {
return this.param.valueMode === ParamValueMode.MINMAX;
}
......@@ -407,6 +433,10 @@ export class DialogEditParamValuesComponent implements OnInit {
return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_IMPORT_FICHIER");
}
public get uitextExtensionStrategy() {
return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_EXT_STRATEGY");
}
public ngOnInit() {
this.initVariableValues();
}
......
......@@ -15,6 +15,9 @@ import { ResultsComponent } from "./results.component";
})
export class VarResultsComponent extends ResultsComponent {
/** size of the longest variated parameter */
public size: number;
/** résultats non mis en forme */
protected _varResults: VarResults;
......@@ -33,54 +36,56 @@ export class VarResultsComponent extends ResultsComponent {
super();
}
/** Refreshes results and builds the dataset */
public set results(r: VarResults) {
this._varResults = r;
this._results = [];
this._headers = [];
const nDigits = this.appSetupService.displayDigits;
if (this._varResults) {
const nDigits = this.appSetupService.displayDigits;
let i = 0;
for (const x of this._varResults.variatedParameter.valuesIterator) {
const pval = x.toFixed(nDigits);
this._results.push({ "param": pval, "result": this._varResults.resultElements[i] });
i++;
// A. build headers
for (let i = 0; i < this._varResults.variatedParameters.length; i++) {
this._headers.push(this._varResults.variableParamHeaders[i]);
}
this._headers.push(this._varResults.variableParamHeader);
if (this._varResults.calculatedParameterHeader) {
this._headers.push(this._varResults.calculatedParameterHeader);
}
this._headers = this._headers.concat(this._varResults.extraResultHeaders);
}
}
public get hasResults(): boolean {
return this._varResults && this._varResults.hasResults;
}
public get headers() {
return this._headers;
}
// B. pre-extract variable parameters valueslet longest = 0;
const varValues = [];
// find longest list
this.size = 0;
for (let i = 0; i < this._varResults.variatedParameters.length; i++) {
const vs = this._varResults.variatedParameters[i].valuesIterator.count();
if (vs > this.size) {
this.size = vs;
}
}
// get extended values lists for each variable parameter
for (const v of this._varResults.variatedParameters) {
const vv = [];
const iter = v.getExtendedValuesIterator(this.size);
while (iter.hasNext) {
const nv = iter.next();
vv.push(nv.value.toFixed(nDigits));
}
varValues.push(vv);
}
/**
* Returns a combination of and results and extraResults for mat-table
*/
public get dataSet() {
const data = [];
const nDigits = this.appSetupService.displayDigits;
if (this._results) {
for (let i = 0; i < this._results.length; i++) {
const r = this._results[i];
// C. build dataset
for (let i = 0; i < this._varResults.resultElements.length; i++) {
const re: ResultElement = this._varResults.resultElements[i];
if (re) {
// for each computation step, build ordered list of : variable param value; result; extra results
// build ordered list of : variable params values; result; extra results
const list = [];
// 1. variable param value
const list = [ r.param ];
// 1. variable params values for this computation step
for (const vv of varValues) {
list.push(vv[i]);
}
// 2. result
if (re.vCalc) { // sometimes does no exist (ex: Section Parametree)
......@@ -97,11 +102,25 @@ export class VarResultsComponent extends ResultsComponent {
}
}
data.push(list);
this._results.push(list);
}
}
}
return data;
}
public get hasResults(): boolean {
return this._varResults && this._varResults.hasResults;
}
public get headers() {
return this._headers;
}
/**
* Returns a combination of results and extraResults for mat-table
*/
public get dataSet() {
return this._results;
}
public exportAsSpreadsheet() {
......
......@@ -197,7 +197,7 @@ export class ParamFieldLineComponent implements OnChanges {
let ret = true;
if (this.param.paramDefinition.isCalculated) {
const nub = this.param.paramDefinition.parentNub;
const p = nub.findFirstSingleParameter(this.param.paramDefinition);
const p = nub.findFirstCalculableParameter(this.param.paramDefinition);
ret = (p !== undefined);
}
return ret;
......
......@@ -368,7 +368,7 @@ export class RemousResultsComponent extends ResultsComponent implements DoCheck
}
private get abscisseIterator(): INumberIterator {
return this._remousResults.varResults.variatedParameter.paramDefinition.valuesIterator;
return this._remousResults.varResults.variatedParameters[0].paramDefinition.valuesIterator;
}
private connectRessaut(lineFlu: LineData, lineTor: LineData) {
......
......@@ -104,7 +104,7 @@ export class ResultsGraphComponent extends ResultsComponent implements AfterCont
/**
* Returns a human readable description of any param / result symbol
*/
public getChartAxisLabel(symbol: string) {
public getChartAxisLabel(symbol: string): string {
return this._results.getChartAxisLabel(symbol);
}
......@@ -135,6 +135,19 @@ export class ResultsGraphComponent extends ResultsComponent implements AfterCont
this._graphTypeComponent.addObserver(this);
}
/**
* Calls getChartAxisLabel() and removes the symbol prefix
* (cannot rebuild a clean label here)
*/
private axisLabelWithoutSymbol(symbol: string) {
let l = this._results.getChartAxisLabel(symbol);
const i = l.indexOf(": ");
if (i !== -1) {
l = l.substring(i + 2);
}
return l;
}
/** forces Angular to rebuild the chart @see bug #137 */
private forceRebuild() {
this.displayChart = false;
......@@ -172,20 +185,36 @@ export class ResultsGraphComponent extends ResultsComponent implements AfterCont
},
scaleLabel: {
display: true,
labelString: this.chartX
labelString: this.axisLabelWithoutSymbol(this.chartX)
}
}],
yAxes: [{
scaleLabel: {
display: true,
labelString: this.chartY
labelString: this.axisLabelWithoutSymbol(this.chartY)
}
}]
};
const that = this;
this.graph_options["tooltips"] = {
displayColors: false,
callbacks: {
label: function(tooltipItem, data) {
return Number(tooltipItem.yLabel).toFixed(nDigits);
title: (tooltipItems, data) => {
return this.chartY + " = " + Number(tooltipItems[0].yLabel).toFixed(nDigits);
},
label: (tooltipItem, data) => {
const lines: string[] = [];
for (const v of that._results.getVariatingParametersSymbols()) {
const series = that._results.getValuesSeries(v);
const line = v + " = " + series[tooltipItem.index].toFixed(nDigits);
if (v === this.chartX) {
lines.unshift("");
lines.unshift(line);
} else {
lines.push(line);
}
}
return lines;
}
}
};
......@@ -224,7 +253,7 @@ export class ResultsGraphComponent extends ResultsComponent implements AfterCont
},
scaleLabel: {
display: true,
labelString: this.chartX
labelString: this.axisLabelWithoutSymbol(this.chartX)
}
}],
yAxes: [{
......@@ -235,14 +264,30 @@ export class ResultsGraphComponent extends ResultsComponent implements AfterCont
},
scaleLabel: {
display: true,
labelString: this.chartY
labelString: this.axisLabelWithoutSymbol(this.chartY)
}
}]
};
const that = this;
this.graph_options["tooltips"] = {
displayColors: false,
callbacks: {
label: function(tooltipItem, data) {
return "(" + Number(tooltipItem.xLabel).toFixed(nDigits) + ", " + Number(tooltipItem.yLabel).toFixed(nDigits) + ")";
title: (tooltipItems, data) => {
return this.chartY + " = " + Number(tooltipItems[0].yLabel).toFixed(nDigits);
},
label: (tooltipItem, data) => {
const lines: string[] = [];
for (const v of that._results.getVariatingParametersSymbols()) {
const series = that._results.getValuesSeries(v);
const line = v + " = " + series[tooltipItem.index].toFixed(nDigits);
if (v === this.chartX) {
lines.unshift("");
lines.unshift(line);
} else {
lines.push(line);
}
}
return lines;
}
}
};
......
import { Nub, Result, ComputeNode } from "jalhyd";
import { Nub } from "jalhyd";
import { FormCompute } from "./form-compute";
import { NgParameter, ParamRadioConfig } from "../ngparam";
......@@ -14,18 +14,18 @@ export class FormComputeFixedVar extends FormCompute {
return this._formResult as FormResultFixedVar;
}
private getVariatedParameter(): NgParameter {
const res = this._formBase.getDisplayedParamFromState(ParamRadioConfig.VAR);
if (res !== undefined) {
return res;
}
const pms = this._formBase.getDisplayedParamListFromState(ParamRadioConfig.LINK);
for (const p of pms) {
if (p.paramDefinition.hasMultipleValues) {
return p;
private getVariatedParameters(): NgParameter[] {
let res: NgParameter[] = [];
res = this._formBase.getDisplayedParamListFromState(ParamRadioConfig.VAR);
if (res.length === 0) {
const pms = this._formBase.getDisplayedParamListFromState(ParamRadioConfig.LINK);
for (const p of pms) {
if (p.paramDefinition.hasMultipleValues) {
res.push(p);
}
}
}
return res;
}
private getComputedParameter(): NgParameter {
......@@ -42,15 +42,15 @@ export class FormComputeFixedVar extends FormCompute {
const computedParam: NgParameter = this.getComputedParameter();
this.formResult.resetResults(); // to avoid adding fixed parameters more than once (see below)
this.formResult.addFixedParameters();
const varParam: NgParameter = this.getVariatedParameter();
const varParams: NgParameter[] = this.getVariatedParameters();
if (varParam === undefined) {
if (varParams.length === 0) {
// pas de paramètre à varier
this.formResult.fixedResults.result = nub.result;
this.formResult.fixedResults.calculatedParameter = computedParam;
} else {
// il y a un paramètre à varier
this.formResult.varResults.variatedParameter = varParam;
this.formResult.varResults.variatedParameters = varParams;
this.formResult.varResults.calculatedParameter = computedParam;
this.formResult.varResults.result = nub.result;
......
......@@ -39,10 +39,10 @@ export class FormComputeSectionParametree extends FormCompute {
const sect: acSection = sectNub.section;
this._sectionResults.section = sect;
const varParam = this._formSection.getSectionVariatedParameter();
if (varParam) {
const varParams = this._formSection.getSectionVariatedParameters();
if (varParams.length > 0) {
// résultats variés avec tous les résultats complémentaires
this._varResults.variatedParameter = varParam;
this._varResults.variatedParameters = varParams;
this._varResults.result = sectNub.result;
this._varResults.update(false);
} else {
......
......@@ -18,8 +18,8 @@ export class FormDefSection {
return this._sectionSourceId !== undefined;
}
public getSectionVariatedParameter(): NgParameter {
return this._formBase.getDisplayedParamFromState(ParamRadioConfig.VAR);
public getSectionVariatedParameters(): NgParameter[] {
return this._formBase.getDisplayedParamListFromState(ParamRadioConfig.VAR);
}
public getSectionComputedParam(): { symbol: string, label: string } {
......
......@@ -237,6 +237,10 @@ export class NgParameter extends InputField implements Observer {
return this._paramDef.valueList;
}
public get inferredValuesList() {
return this._paramDef.getInferredValuesList();
}
public get isValid() {
if (this.radioState === undefined) {
return false;
......@@ -296,6 +300,10 @@ export class NgParameter extends InputField implements Observer {
}
}
public getExtendedValuesIterator(size: number): INumberIterator {
return this._paramDef.getExtendedValuesIterator(size);
}
/**
* notification envoyée après la modification de la valeur du paramètre
*/
......
......@@ -14,7 +14,13 @@ export interface PlottableData {
* (usually a variable symbol like "Q", "Z1"…)
* @param symbol parameter / result symbol (ex: "Q")
*/
getChartAxisLabel(symbol: string);
getChartAxisLabel(symbol: string): string;
/**
* Returns the translated name of the given symbol (usually an extraResult)
* if available, with its unit, but without the symbol itself
*/
expandLabelFromSymbol(symbol: string): string;
/**
* Returns a list of plottable parameters / result elements, that can be defined
......@@ -27,4 +33,10 @@ export interface PlottableData {
* @param symbol parameter / result symbol (ex: "Q")
*/
getValuesSeries(symbol: string): any[];
/**
* Returns the list of variating parameters
* (used by tooltip functions)
*/
getVariatingParametersSymbols(): string[];
}
......@@ -29,10 +29,14 @@ export class PlottablePabResults implements PlottableData {
* Returns the label to display, for an element of getAvailableChartAxis()
* @param symbol parameter / result symbol (ex: "Q")
*/
public getChartAxisLabel(symbol: string) {
public getChartAxisLabel(symbol: string): string {
return this.pabResults.headers[this.pabResults.columns.indexOf(symbol)];
}
public expandLabelFromSymbol(symbol: string): string {
return symbol;
}
/**
* Returns a list of plottable parameters / result elements, that can be defined
* as X or Y chart axis
......@@ -41,6 +45,11 @@ export class PlottablePabResults implements PlottableData {
return this.pabResults.columns;
}
// just to implement interface
public getVariatingParametersSymbols(): string[] {
return [];
}
/**
* Returns the series of values for the required symbol
* @param symbol parameter / result symbol (ex: "Q")
......
......@@ -137,7 +137,7 @@ export class RemousResults extends CalculatorResults {
this._log.addLog(this._result.globalLog);
this._varResults = new VarResults();
this._varResults.variatedParameter = new NgParameter(this._xValues, undefined);
this._varResults.variatedParameters = [ new NgParameter(this._xValues, undefined) ];
this._varResults.calculatedParameter
= new NgParameter(new ParamDefinition(null, "Ligne d'eau", ParamDomainValue.POS_NULL), undefined);
this._varResults.result = this._result;
......
......@@ -8,14 +8,14 @@ import { GraphType } from "./graph-type";
export class VarResults extends CalculatedParamResults implements PlottableData {
/**
* paramètre varié
* paramètres variés
*/
private _variatedParam: NgParameter;
private _variatedParams: NgParameter[];
/**
* titre de la 1ère colonne des résultats variés
* titre des colonnes des résultats variés
*/
private _variableParamHeader: string;
private _variableParamHeaders: string[];
/**
* clés des résultats complémentaires
......@@ -41,6 +41,12 @@ export class VarResults extends CalculatedParamResults implements PlottableData
*/
public chartY: string;
/** size of the longest variated parameter */
public size: number;
/** index of the longest variated parameter */
public longest: number;
/**
* tableau des ordonnées du graphe des résultats variés
*/
......@@ -53,23 +59,26 @@ export class VarResults extends CalculatedParamResults implements PlottableData
public reset() {
super.reset();
this._variableParamHeader = undefined;
this._variableParamHeaders = [];
this._extraResultHeaders = [];
this.extraResultKeys = [];
this._yValues = [];
this.longest = 0;
}
public get variatedParameter(): NgParameter {
return this._variatedParam;
public get variatedParameters(): NgParameter[] {
return this._variatedParams;
}
public set variatedParameter(p: NgParameter) {
this._variatedParam = p;
this._variableParamHeader = CalculatorResults.paramLabel(this._variatedParam, true);
public set variatedParameters(p: NgParameter[]) {
this._variatedParams = p;
this._variableParamHeaders = this._variatedParams.map((v) => {
return CalculatorResults.paramLabel(v, true);
});
}
public get variableParamHeader() {
return this._variableParamHeader;
public get variableParamHeaders() {
return this._variableParamHeaders;
}
public get yValues() {
......@@ -84,24 +93,34 @@ export class VarResults extends CalculatedParamResults implements PlottableData
return this._extraResultHeaders;
}
public getChartAxisLabel(symbol: string) {
public getChartAxisLabel(symbol: string): string {
// 1. calculated param ?
if (this.calculatedParameter && this.calculatedParameter.symbol === symbol) {
return this.calculatedParameterHeader;
} else
}
// 2. variated param ?
if (this.variatedParameter.symbol === symbol) {
return this.variableParamHeader;
} else {
// 3. Result element ?
// calculator type for translation
const sn = this.result.sourceNub;
let ct = sn.calcType;
if (sn.parent) {
ct = sn.parent.calcType;
for (let i = 0; i < this.variatedParameters.length; i++) {
if (this._variatedParams[i].symbol === symbol) {
return this.variableParamHeaders[i];
}
return ServiceFactory.instance.formulaireService.expandVariableNameAndUnit(ct, symbol);
}
// 3. Result element
return this.expandLabelFromSymbol(symbol);
}
/**
* Returns the translated name of the given symbol (usually an extraResult) with
* its unit, but without the symbol itself
*/
public expandLabelFromSymbol(symbol: string): string {
// calculator type for translation
const sn = this.result.sourceNub;
let ct = sn.calcType;
if (sn.parent) {
ct = sn.parent.calcType;
}
return ServiceFactory.instance.formulaireService.expandVariableNameAndUnit(ct, symbol);
}
/**
......@@ -117,19 +136,21 @@ export class VarResults extends CalculatedParamResults implements PlottableData
series.push(r.vCalc);
}
}
} else
}
// 2. variated param ?
if (this.variatedParameter.symbol === symbol) {
for (const v of this.variatedParameter.valuesIterator) {
series.push(v);
for (let i = 0; i < this.variatedParameters.length; i++) {
if (this._variatedParams[i].symbol === symbol) {
const iter = this.variatedParameters[i].getExtendedValuesIterator(this.size);
for (const v of iter) {
series.push(v);
}
}
} else {
// 3. Result element ?
for (const r of this.result.resultElements) { // re:ResultElement
for (const k in r.extraResults) {
if (k === symbol) {
series.push(r.extraResults[k]);
}
}
// 3. Result element ?
for (const r of this.result.resultElements) { // re:ResultElement
for (const k in r.extraResults) {
if (k === symbol) {
series.push(r.extraResults[k]);
}
}
}
......@@ -146,16 +167,42 @@ export class VarResults extends CalculatedParamResults implements PlottableData
if (this.calculatedParameter) {
res.push(this.calculatedParameter.symbol);
}
res.push(this.variatedParameter.symbol);
for (const v of this._variatedParams) {
res.push(v.symbol);
}
for (const erk of this.extraResultKeys) {
res.push(erk);
}
return res;
}
/**
* Returns the list of variating parameters
* (used by tooltip functions)
*/
public getVariatingParametersSymbols(): string[] {
return this._variatedParams.map((vp) => {
return vp.symbol;
});
}
public update(displaySymbol: boolean) {
if (this._variableParamHeader === undefined) {
this._variableParamHeader = CalculatorResults.paramLabel(this.variatedParameter, displaySymbol);
if (this._variableParamHeaders.length === 0) {
this._variableParamHeaders = this._variatedParams.map((v) => {
return CalculatorResults.paramLabel(v, true);
});
}
// liste la plus longue
this.size = 0;
let i = 0;
for (const v of this._variatedParams) {
const s = v.valuesIterator.count();
if (s > this.size) {
this.size = s;
this.longest = i;
}
i++;
}
// valeurs du paramètre à calculer
......@@ -181,7 +228,7 @@ export class VarResults extends CalculatedParamResults implements PlottableData
} else if (this.extraResultKeys.length > 0) {
defaultY = this.extraResultKeys[0];
}
this.chartX = this.chartX || this.variatedParameter.symbol;
this.chartX = this.chartX || this.variatedParameters[this.longest].symbol;
this.chartY = defaultY;
// calculator type for translation
......@@ -202,10 +249,10 @@ export class VarResults extends CalculatedParamResults implements PlottableData
// (might be the previous variated parameter, that is not accessible anymore)
const aca = this.getAvailableChartAxis();
if (! aca.includes(this.chartX)) {
this.chartX = this.variatedParameter.symbol;
this.chartX = this.variatedParameters[0].symbol;
}
if (! aca.includes(this.chartY)) {
this.chartY = this.variatedParameter.symbol;
this.chartY = this.variatedParameters[0].symbol;
}
}
}
......@@ -212,6 +212,9 @@
"INFO_PARAMFIELD_PARAMFIXE": "Fixed",
"INFO_PARAMFIELD_PARAMLIE_LABEL": "Linked parameter",
"INFO_PARAMFIELD_PARAMLIE": "Link",
"INFO_PARAMFIELD_PARAMVARIER_EXT_STRATEGY": "Values list extension strategy",
"INFO_PARAMFIELD_PARAMVARIER_EXT_STRATEGY_REPEAT_LAST": "Repeat last value",
"INFO_PARAMFIELD_PARAMVARIER_EXT_STRATEGY_RECYCLE": "Recycle values",
"INFO_PARAMFIELD_PARAMVARIER_IMPORT_FICHIER": "Import file",
"INFO_PARAMFIELD_PARAMVARIER_MINMAXSTEP": "min: %s, max: %s, step: %s",
"INFO_PARAMFIELD_PARAMVARIER_MODE": "Mode",
......
......@@ -212,6 +212,9 @@
"INFO_PARAMFIELD_PARAMFIXE": "fixé",
"INFO_PARAMFIELD_PARAMLIE_LABEL": "Paramètre lié",
"INFO_PARAMFIELD_PARAMLIE": "lié",
"INFO_PARAMFIELD_PARAMVARIER_EXT_STRATEGY": "Stratégie d'extension de la liste de valeurs",
"INFO_PARAMFIELD_PARAMVARIER_EXT_STRATEGY_REPEAT_LAST": "Répéter la dernière valeur",
"INFO_PARAMFIELD_PARAMVARIER_EXT_STRATEGY_RECYCLE": "Réutiliser les valeurs",
"INFO_PARAMFIELD_PARAMVARIER_IMPORT_FICHIER": "Importer un fichier",
"INFO_PARAMFIELD_PARAMVARIER_MINMAXSTEP": "min : %s, max : %s, pas : %s",
"INFO_PARAMFIELD_PARAMVARIER_MODE": "Mode",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment