node-opcua-numeric-range 2.55.0 → 2.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -20
- package/dist/numeric_range.d.ts +3 -3
- package/dist/numeric_range.js +2 -5
- package/dist/numeric_range.js.map +1 -1
- package/package.json +9 -7
- package/source/index.ts +4 -4
- package/source/numeric_range.ts +778 -775
package/source/numeric_range.ts
CHANGED
|
@@ -1,775 +1,778 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module node-opcua-numeric-range
|
|
3
|
-
*/
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
// OPC.UA Part 4 7.21 Numerical Range
|
|
13
|
-
// The syntax for the string contains one of the following two constructs. The first construct is the string
|
|
14
|
-
// representation of an individual integer. For example, '6' is valid, but '6.0' and '3.2' are not. The
|
|
15
|
-
// minimum and maximum values that can be expressed are defined by the use of this parameter and
|
|
16
|
-
// not by this parameter type definition. The second construct is a range represented by two integers
|
|
17
|
-
// separated by the colon (':') character. The first integer shall always have a lower value than the
|
|
18
|
-
// second. For example, '5:7' is valid, while '7:5' and '5:5' are not. The minimum and maximum values
|
|
19
|
-
// that can be expressed by these integers are defined by the use of this parameter , and not by this
|
|
20
|
-
// parameter type definition. No other characters, including white - space characters, are permitted.
|
|
21
|
-
// Multi- dimensional arrays can be indexed by specifying a range for each dimension separated by a ','.
|
|
22
|
-
//
|
|
23
|
-
// For example, a 2x2 block in a 4x4 matrix could be selected with the range '1:2,0:1'. A single element
|
|
24
|
-
// in a multi - dimensional array can be selected by specifying a single number instead of a range.
|
|
25
|
-
// For example, '1,1' specifies selects the [1,1] element in a two dimensional array.
|
|
26
|
-
// Dimensions are specified in the order that they appear in the ArrayDimensions Attribute.
|
|
27
|
-
// All dimensions shall be specified for a NumericRange to be valid.
|
|
28
|
-
//
|
|
29
|
-
// All indexes start with 0. The maximum value for any index is one less than the length of the
|
|
30
|
-
// dimension.
|
|
31
|
-
|
|
32
|
-
const NUMERIC_RANGE_EMPTY_STRING = "NumericRange:<Empty>";
|
|
33
|
-
|
|
34
|
-
// BNF of NumericRange
|
|
35
|
-
// The following BNF describes the syntax of the NumericRange parameter type.
|
|
36
|
-
// <numeric-range> ::= <dimension> [',' <dimension>]
|
|
37
|
-
// <dimension> ::= <index> [':' <index>]
|
|
38
|
-
// <index> ::= <digit> [<digit>]
|
|
39
|
-
// <digit> ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' |9'
|
|
40
|
-
//
|
|
41
|
-
// tslint:disable:object-literal-shorthand
|
|
42
|
-
// tslint:disable:only-arrow-functions
|
|
43
|
-
export const schemaNumericRange = {
|
|
44
|
-
name: "NumericRange",
|
|
45
|
-
subType: "UAString",
|
|
46
|
-
|
|
47
|
-
defaultValue: () => {
|
|
48
|
-
return new NumericRange();
|
|
49
|
-
},
|
|
50
|
-
encode: encodeNumericRange,
|
|
51
|
-
|
|
52
|
-
decode: decodeNumericRange,
|
|
53
|
-
|
|
54
|
-
random: (): NumericRange => {
|
|
55
|
-
function r() {
|
|
56
|
-
return Math.ceil(Math.random() * 100);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const start = r();
|
|
60
|
-
const end = start + r();
|
|
61
|
-
return new NumericRange(start, end);
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
coerce: coerceNumericRange
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
registerBasicType(schemaNumericRange);
|
|
68
|
-
|
|
69
|
-
export enum NumericRangeType {
|
|
70
|
-
Empty = 0,
|
|
71
|
-
SingleValue = 1,
|
|
72
|
-
ArrayRange = 2,
|
|
73
|
-
MatrixRange = 3,
|
|
74
|
-
InvalidRange = 4
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// new Enum(["Empty", "SingleValue", "ArrayRange", "MatrixRange", "InvalidRange"]);
|
|
78
|
-
|
|
79
|
-
const regexNumericRange = /^[0-9:,]*$/;
|
|
80
|
-
|
|
81
|
-
function _valid_range(low: number, high: number): boolean {
|
|
82
|
-
return !(low >= high || low < 0 || high < 0);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
type NumericalRangeValueType = null | number | string | number[] | number[][];
|
|
86
|
-
|
|
87
|
-
export interface NumericalRangeSingleValue {
|
|
88
|
-
type: NumericRangeType.SingleValue;
|
|
89
|
-
value: number;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export interface NumericalRangeArrayRange {
|
|
93
|
-
type: NumericRangeType.ArrayRange;
|
|
94
|
-
value: number[];
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export interface NumericalRangeMatrixRange {
|
|
98
|
-
type: NumericRangeType.MatrixRange;
|
|
99
|
-
value: number[][];
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export interface NumericalRangeEmpty {
|
|
103
|
-
type: NumericRangeType.Empty;
|
|
104
|
-
value: null;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export interface NumericalRangeInvalid {
|
|
108
|
-
type: NumericRangeType.InvalidRange;
|
|
109
|
-
value: string;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export type NumericalRange0 =
|
|
113
|
-
| NumericalRangeSingleValue
|
|
114
|
-
| NumericalRangeArrayRange
|
|
115
|
-
| NumericalRangeMatrixRange
|
|
116
|
-
| NumericalRangeEmpty
|
|
117
|
-
| NumericalRangeInvalid;
|
|
118
|
-
|
|
119
|
-
export interface NumericalRange1 {
|
|
120
|
-
type: NumericRangeType;
|
|
121
|
-
value: NumericalRangeValueType;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function construct_numeric_range_bit_from_string(str: string): NumericalRange0 {
|
|
125
|
-
const values = str.split(":");
|
|
126
|
-
|
|
127
|
-
if (values.length === 1) {
|
|
128
|
-
return {
|
|
129
|
-
type: NumericRangeType.SingleValue,
|
|
130
|
-
value: parseInt(values[0], 10)
|
|
131
|
-
};
|
|
132
|
-
} else if (values.length === 2) {
|
|
133
|
-
const array = values.map((a) => parseInt(a, 10));
|
|
134
|
-
|
|
135
|
-
if (!_valid_range(array[0], array[1])) {
|
|
136
|
-
return {
|
|
137
|
-
type: NumericRangeType.InvalidRange,
|
|
138
|
-
value: str
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
return {
|
|
142
|
-
type: NumericRangeType.ArrayRange,
|
|
143
|
-
value: array
|
|
144
|
-
};
|
|
145
|
-
} else {
|
|
146
|
-
return {
|
|
147
|
-
type: NumericRangeType.InvalidRange,
|
|
148
|
-
value: str
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function _normalize(e: NumericalRange1): number | number[] {
|
|
154
|
-
if (e.type === NumericRangeType.SingleValue) {
|
|
155
|
-
const ee = e as NumericalRangeSingleValue;
|
|
156
|
-
return [ee.value, ee.value];
|
|
157
|
-
}
|
|
158
|
-
return e.value as number;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function construct_numeric_range_from_string(str: string): NumericalRange0 {
|
|
162
|
-
if (!regexNumericRange.test(str)) {
|
|
163
|
-
return {
|
|
164
|
-
type: NumericRangeType.InvalidRange,
|
|
165
|
-
value: str
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
/* detect multi dim range*/
|
|
169
|
-
const values = str.split(",");
|
|
170
|
-
|
|
171
|
-
if (values.length === 1) {
|
|
172
|
-
return construct_numeric_range_bit_from_string(values[0]);
|
|
173
|
-
} else if (values.length === 2) {
|
|
174
|
-
const elements = values.map(construct_numeric_range_bit_from_string);
|
|
175
|
-
let rowRange: any = elements[0];
|
|
176
|
-
let colRange: any = elements[1];
|
|
177
|
-
if (rowRange.type === NumericRangeType.InvalidRange || colRange.type === NumericRangeType.InvalidRange) {
|
|
178
|
-
return { type: NumericRangeType.InvalidRange, value: str };
|
|
179
|
-
}
|
|
180
|
-
rowRange = _normalize(rowRange);
|
|
181
|
-
colRange = _normalize(colRange);
|
|
182
|
-
return {
|
|
183
|
-
type: NumericRangeType.MatrixRange,
|
|
184
|
-
value: [rowRange, colRange]
|
|
185
|
-
};
|
|
186
|
-
} else {
|
|
187
|
-
// not supported yet
|
|
188
|
-
return { type: NumericRangeType.InvalidRange, value: str };
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function construct_from_string(value: string): NumericalRange0 {
|
|
193
|
-
return construct_numeric_range_from_string(value);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function _set_single_value(value: number | null): NumericalRange0 {
|
|
197
|
-
if (value === null || value < 0 || !isFinite(value)) {
|
|
198
|
-
return {
|
|
199
|
-
type: NumericRangeType.InvalidRange,
|
|
200
|
-
value: "" + value?.toString()
|
|
201
|
-
};
|
|
202
|
-
} else {
|
|
203
|
-
return {
|
|
204
|
-
type: NumericRangeType.SingleValue,
|
|
205
|
-
value: value
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function _check_range(numericalRange: NumericalRange0) {
|
|
211
|
-
switch (numericalRange.type) {
|
|
212
|
-
case NumericRangeType.ArrayRange:
|
|
213
|
-
return _valid_range(numericalRange.value[0], numericalRange.value[1]);
|
|
214
|
-
}
|
|
215
|
-
// istanbul ignore next
|
|
216
|
-
throw new Error("unsupported case");
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function _set_range_value(low: number, high: number): NumericalRangeSingleValue | NumericalRangeArrayRange | NumericalRangeInvalid {
|
|
220
|
-
if (low === high) {
|
|
221
|
-
return {
|
|
222
|
-
type: NumericRangeType.SingleValue,
|
|
223
|
-
value: low
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
const numericalRange: NumericalRangeArrayRange = {
|
|
227
|
-
type: NumericRangeType.ArrayRange,
|
|
228
|
-
value: [low, high]
|
|
229
|
-
};
|
|
230
|
-
if (!_check_range(numericalRange as NumericalRangeArrayRange)) {
|
|
231
|
-
return {
|
|
232
|
-
type: NumericRangeType.InvalidRange,
|
|
233
|
-
value: ""
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
return numericalRange;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function construct_from_values(value: number, secondValue?: number): NumericalRange0 {
|
|
240
|
-
if (secondValue === undefined) {
|
|
241
|
-
return _set_single_value(value);
|
|
242
|
-
} else {
|
|
243
|
-
if (!isFinite(secondValue)) {
|
|
244
|
-
throw new Error(" invalid second argument, expecting a number");
|
|
245
|
-
}
|
|
246
|
-
return _set_range_value(value, secondValue);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function _construct_from_array(value: number[], value2?: any): NumericalRange0 {
|
|
251
|
-
assert(value.length === 2);
|
|
252
|
-
|
|
253
|
-
// istanbul ignore next
|
|
254
|
-
if (!isFinite(value[0]) || !isFinite(value[1])) {
|
|
255
|
-
return { type: NumericRangeType.InvalidRange, value: "" + value };
|
|
256
|
-
}
|
|
257
|
-
let range1 = _set_range_value(value[0], value[1]);
|
|
258
|
-
if (!value2) {
|
|
259
|
-
return range1;
|
|
260
|
-
}
|
|
261
|
-
// we have a matrix
|
|
262
|
-
const nr2 = new NumericRange(value2);
|
|
263
|
-
// istanbul ignore next
|
|
264
|
-
if (
|
|
265
|
-
nr2.type === NumericRangeType.InvalidRange ||
|
|
266
|
-
nr2.type === NumericRangeType.MatrixRange ||
|
|
267
|
-
nr2.type === NumericRangeType.Empty
|
|
268
|
-
) {
|
|
269
|
-
return { type: NumericRangeType.InvalidRange, value: "" + value };
|
|
270
|
-
}
|
|
271
|
-
if (range1.type === NumericRangeType.SingleValue) {
|
|
272
|
-
range1 = {
|
|
273
|
-
type: NumericRangeType.ArrayRange,
|
|
274
|
-
value: [range1.value, range1.value]
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
if (nr2.type === NumericRangeType.SingleValue) {
|
|
278
|
-
nr2.type = NumericRangeType.ArrayRange;
|
|
279
|
-
nr2.value = [nr2.value as number, nr2.value as number];
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// istanbul ignore next
|
|
283
|
-
return {
|
|
284
|
-
type: NumericRangeType.MatrixRange,
|
|
285
|
-
value: [range1.value as number[], nr2.value as number[]]
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
export class NumericRange implements NumericalRange1 {
|
|
290
|
-
public static coerce = coerceNumericRange;
|
|
291
|
-
|
|
292
|
-
public static schema = schemaNumericRange;
|
|
293
|
-
// tslint:disable:variable-name
|
|
294
|
-
public static NumericRangeType = NumericRangeType;
|
|
295
|
-
|
|
296
|
-
public static readonly empty = new NumericRange() as NumericalRange0;
|
|
297
|
-
|
|
298
|
-
public static overlap(nr1?: NumericalRange0, nr2?: NumericalRange0) {
|
|
299
|
-
nr1 = nr1 || NumericRange.empty;
|
|
300
|
-
nr2 = nr2 || NumericRange.empty;
|
|
301
|
-
|
|
302
|
-
if (NumericRangeType.Empty === nr1.type || NumericRangeType.Empty === nr2.type) {
|
|
303
|
-
return true;
|
|
304
|
-
}
|
|
305
|
-
if (NumericRangeType.SingleValue === nr1.type && NumericRangeType.SingleValue === nr2.type) {
|
|
306
|
-
return nr1.value === nr2.value;
|
|
307
|
-
}
|
|
308
|
-
if (NumericRangeType.ArrayRange === nr1.type && NumericRangeType.ArrayRange === nr2.type) {
|
|
309
|
-
// +-----+ +------+ +---+ +------+
|
|
310
|
-
// +----+ +---+ +--------+ +---+
|
|
311
|
-
const l1 = nr1.value[0];
|
|
312
|
-
const h1 = nr1.value[1];
|
|
313
|
-
const l2 = nr2.value[0];
|
|
314
|
-
const h2 = nr2.value[1];
|
|
315
|
-
return _overlap(l1, h1, l2, h2);
|
|
316
|
-
}
|
|
317
|
-
// istanbul ignore next
|
|
318
|
-
assert(false, "NumericalRange#overlap : case not implemented yet "); // TODO
|
|
319
|
-
// istanbul ignore next
|
|
320
|
-
return false;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
public type: NumericRangeType;
|
|
324
|
-
public value: NumericalRangeValueType;
|
|
325
|
-
|
|
326
|
-
constructor();
|
|
327
|
-
// tslint:disable-next-line: unified-signatures
|
|
328
|
-
constructor(value: string | null);
|
|
329
|
-
// tslint:disable-next-line: unified-signatures
|
|
330
|
-
constructor(value: number, secondValue?: number);
|
|
331
|
-
// tslint:disable-next-line: unified-signatures
|
|
332
|
-
constructor(value: number[]);
|
|
333
|
-
// tslint:disable-next-line: unified-signatures
|
|
334
|
-
constructor(value: number[], secondValue: number[]);
|
|
335
|
-
constructor(value?: null | string | number | number[], secondValue?: number | number[]) {
|
|
336
|
-
this.type = NumericRangeType.InvalidRange;
|
|
337
|
-
this.value = null;
|
|
338
|
-
|
|
339
|
-
assert(!value || !(value instanceof NumericRange), "use coerce to create a NumericRange");
|
|
340
|
-
assert(!secondValue || typeof secondValue === "number" || Array.isArray(secondValue));
|
|
341
|
-
if (typeof value === "string") {
|
|
342
|
-
const a = construct_from_string(value as string);
|
|
343
|
-
this.type = a.type;
|
|
344
|
-
this.value = a.value;
|
|
345
|
-
} else if (
|
|
346
|
-
typeof value === "number" &&
|
|
347
|
-
isFinite(value) &&
|
|
348
|
-
(secondValue === undefined || (typeof secondValue === "number" && isFinite(secondValue)))
|
|
349
|
-
) {
|
|
350
|
-
const a = construct_from_values(value, secondValue);
|
|
351
|
-
this.type = a.type;
|
|
352
|
-
this.value = a.value;
|
|
353
|
-
} else if (Array.isArray(value)) {
|
|
354
|
-
const a = _construct_from_array(value, secondValue);
|
|
355
|
-
this.type = a.type;
|
|
356
|
-
this.value = a.value;
|
|
357
|
-
} else {
|
|
358
|
-
this.value = "<invalid>";
|
|
359
|
-
this.type = NumericRangeType.Empty;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// xx assert((this.type !== NumericRangeType.ArrayRange) || Array.isArray(this.value));
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
public isValid(): boolean {
|
|
366
|
-
if (this.type === NumericRangeType.ArrayRange) {
|
|
367
|
-
const value = this.value as number[];
|
|
368
|
-
if (value[0] < 0 || value[1] < 0) {
|
|
369
|
-
return false;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
if (this.type === NumericRangeType.SingleValue) {
|
|
373
|
-
const value = this.value as number;
|
|
374
|
-
// istanbul ignore next
|
|
375
|
-
if (value < 0) {
|
|
376
|
-
return false;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
return this.type !== NumericRangeType.InvalidRange;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
public isEmpty(): boolean {
|
|
383
|
-
return this.type === NumericRangeType.Empty;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
public isDefined(): boolean {
|
|
387
|
-
return this.type !== NumericRangeType.Empty && this.type !== NumericRangeType.InvalidRange;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
public toString(): string {
|
|
391
|
-
function array_range_to_string(values: number[]): string {
|
|
392
|
-
assert(Array.isArray(values));
|
|
393
|
-
if (values.length === 2 && values[0] === values[1]) {
|
|
394
|
-
return values[0].toString();
|
|
395
|
-
}
|
|
396
|
-
return values.map((value) => value.toString(10)).join(":");
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
function matrix_range_to_string(values: any) {
|
|
400
|
-
return values
|
|
401
|
-
.map((value: any) => {
|
|
402
|
-
return Array.isArray(value) ? array_range_to_string(value) : value.toString(10);
|
|
403
|
-
})
|
|
404
|
-
.join(",");
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
switch (this.type) {
|
|
408
|
-
case NumericRangeType.SingleValue:
|
|
409
|
-
return (this.value as any).toString(10);
|
|
410
|
-
|
|
411
|
-
case NumericRangeType.ArrayRange:
|
|
412
|
-
return array_range_to_string(this.value as number[]);
|
|
413
|
-
|
|
414
|
-
case NumericRangeType.Empty:
|
|
415
|
-
return NUMERIC_RANGE_EMPTY_STRING;
|
|
416
|
-
|
|
417
|
-
case NumericRangeType.MatrixRange:
|
|
418
|
-
return matrix_range_to_string(this.value);
|
|
419
|
-
|
|
420
|
-
default:
|
|
421
|
-
assert(this.type === NumericRangeType.InvalidRange);
|
|
422
|
-
return "NumericRange:<Invalid>";
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
public toJSON(): string {
|
|
427
|
-
return this.toString();
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
public toEncodeableString(): UAString {
|
|
431
|
-
switch (this.type) {
|
|
432
|
-
case NumericRangeType.SingleValue:
|
|
433
|
-
case NumericRangeType.ArrayRange:
|
|
434
|
-
case NumericRangeType.MatrixRange:
|
|
435
|
-
return this.toString();
|
|
436
|
-
case NumericRangeType.InvalidRange:
|
|
437
|
-
// istanbul ignore next
|
|
438
|
-
if (!(typeof this.value === "string")) {
|
|
439
|
-
throw new Error("Internal Error");
|
|
440
|
-
}
|
|
441
|
-
return this.value; // value contains the original strings which was detected invalid
|
|
442
|
-
default:
|
|
443
|
-
return null;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* @method extract_values
|
|
449
|
-
* @param array flat array containing values or string
|
|
450
|
-
* @param dimensions: of the matrix if data is a matrix
|
|
451
|
-
* @return {*}
|
|
452
|
-
*/
|
|
453
|
-
public extract_values<U, T extends ArrayLike<U>>(array: T, dimensions?: number[]): ExtractResult<T> {
|
|
454
|
-
const self = this as NumericalRange0;
|
|
455
|
-
|
|
456
|
-
if (!array) {
|
|
457
|
-
return {
|
|
458
|
-
array,
|
|
459
|
-
statusCode: this.type === NumericRangeType.Empty ? StatusCodes.Good : StatusCodes.BadIndexRangeNoData
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
let index;
|
|
464
|
-
let low_index;
|
|
465
|
-
let high_index;
|
|
466
|
-
let rowRange;
|
|
467
|
-
let colRange;
|
|
468
|
-
switch (self.type) {
|
|
469
|
-
case NumericRangeType.Empty:
|
|
470
|
-
return extract_empty(array, dimensions);
|
|
471
|
-
|
|
472
|
-
case NumericRangeType.SingleValue:
|
|
473
|
-
index = self.value;
|
|
474
|
-
return extract_single_value(array, index);
|
|
475
|
-
|
|
476
|
-
case NumericRangeType.ArrayRange:
|
|
477
|
-
low_index = self.value[0];
|
|
478
|
-
high_index = self.value[1];
|
|
479
|
-
return extract_array_range(array, low_index, high_index);
|
|
480
|
-
|
|
481
|
-
case NumericRangeType.MatrixRange:
|
|
482
|
-
rowRange = self.value[0];
|
|
483
|
-
colRange = self.value[1];
|
|
484
|
-
return extract_matrix_range(array, rowRange, colRange, dimensions);
|
|
485
|
-
|
|
486
|
-
default:
|
|
487
|
-
return { statusCode: StatusCodes.BadIndexRangeInvalid };
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
public set_values_matrix(
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
assert(
|
|
504
|
-
const
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
const
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
//
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
array:
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
return
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
const
|
|
686
|
-
|
|
687
|
-
const
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
const
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
let
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
if (value
|
|
768
|
-
return
|
|
769
|
-
}
|
|
770
|
-
if (value ===
|
|
771
|
-
return new NumericRange();
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @module node-opcua-numeric-range
|
|
3
|
+
*/
|
|
4
|
+
import { debuglog } from "util";
|
|
5
|
+
import { assert } from "node-opcua-assert";
|
|
6
|
+
|
|
7
|
+
import { decodeString, encodeString, UAString, UInt8 } from "node-opcua-basic-types";
|
|
8
|
+
import { BinaryStream, OutputBinaryStream } from "node-opcua-binary-stream";
|
|
9
|
+
import { registerBasicType } from "node-opcua-factory";
|
|
10
|
+
import { StatusCode, StatusCodes } from "node-opcua-status-code";
|
|
11
|
+
|
|
12
|
+
// OPC.UA Part 4 7.21 Numerical Range
|
|
13
|
+
// The syntax for the string contains one of the following two constructs. The first construct is the string
|
|
14
|
+
// representation of an individual integer. For example, '6' is valid, but '6.0' and '3.2' are not. The
|
|
15
|
+
// minimum and maximum values that can be expressed are defined by the use of this parameter and
|
|
16
|
+
// not by this parameter type definition. The second construct is a range represented by two integers
|
|
17
|
+
// separated by the colon (':') character. The first integer shall always have a lower value than the
|
|
18
|
+
// second. For example, '5:7' is valid, while '7:5' and '5:5' are not. The minimum and maximum values
|
|
19
|
+
// that can be expressed by these integers are defined by the use of this parameter , and not by this
|
|
20
|
+
// parameter type definition. No other characters, including white - space characters, are permitted.
|
|
21
|
+
// Multi- dimensional arrays can be indexed by specifying a range for each dimension separated by a ','.
|
|
22
|
+
//
|
|
23
|
+
// For example, a 2x2 block in a 4x4 matrix could be selected with the range '1:2,0:1'. A single element
|
|
24
|
+
// in a multi - dimensional array can be selected by specifying a single number instead of a range.
|
|
25
|
+
// For example, '1,1' specifies selects the [1,1] element in a two dimensional array.
|
|
26
|
+
// Dimensions are specified in the order that they appear in the ArrayDimensions Attribute.
|
|
27
|
+
// All dimensions shall be specified for a NumericRange to be valid.
|
|
28
|
+
//
|
|
29
|
+
// All indexes start with 0. The maximum value for any index is one less than the length of the
|
|
30
|
+
// dimension.
|
|
31
|
+
|
|
32
|
+
const NUMERIC_RANGE_EMPTY_STRING = "NumericRange:<Empty>";
|
|
33
|
+
|
|
34
|
+
// BNF of NumericRange
|
|
35
|
+
// The following BNF describes the syntax of the NumericRange parameter type.
|
|
36
|
+
// <numeric-range> ::= <dimension> [',' <dimension>]
|
|
37
|
+
// <dimension> ::= <index> [':' <index>]
|
|
38
|
+
// <index> ::= <digit> [<digit>]
|
|
39
|
+
// <digit> ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' |9'
|
|
40
|
+
//
|
|
41
|
+
// tslint:disable:object-literal-shorthand
|
|
42
|
+
// tslint:disable:only-arrow-functions
|
|
43
|
+
export const schemaNumericRange = {
|
|
44
|
+
name: "NumericRange",
|
|
45
|
+
subType: "UAString",
|
|
46
|
+
|
|
47
|
+
defaultValue: (): NumericRange => {
|
|
48
|
+
return new NumericRange();
|
|
49
|
+
},
|
|
50
|
+
encode: encodeNumericRange,
|
|
51
|
+
|
|
52
|
+
decode: decodeNumericRange,
|
|
53
|
+
|
|
54
|
+
random: (): NumericRange => {
|
|
55
|
+
function r() {
|
|
56
|
+
return Math.ceil(Math.random() * 100);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const start = r();
|
|
60
|
+
const end = start + r();
|
|
61
|
+
return new NumericRange(start, end);
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
coerce: coerceNumericRange
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
registerBasicType(schemaNumericRange);
|
|
68
|
+
|
|
69
|
+
export enum NumericRangeType {
|
|
70
|
+
Empty = 0,
|
|
71
|
+
SingleValue = 1,
|
|
72
|
+
ArrayRange = 2,
|
|
73
|
+
MatrixRange = 3,
|
|
74
|
+
InvalidRange = 4
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// new Enum(["Empty", "SingleValue", "ArrayRange", "MatrixRange", "InvalidRange"]);
|
|
78
|
+
|
|
79
|
+
const regexNumericRange = /^[0-9:,]*$/;
|
|
80
|
+
|
|
81
|
+
function _valid_range(low: number, high: number): boolean {
|
|
82
|
+
return !(low >= high || low < 0 || high < 0);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
type NumericalRangeValueType = null | number | string | number[] | number[][];
|
|
86
|
+
|
|
87
|
+
export interface NumericalRangeSingleValue {
|
|
88
|
+
type: NumericRangeType.SingleValue;
|
|
89
|
+
value: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface NumericalRangeArrayRange {
|
|
93
|
+
type: NumericRangeType.ArrayRange;
|
|
94
|
+
value: number[];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface NumericalRangeMatrixRange {
|
|
98
|
+
type: NumericRangeType.MatrixRange;
|
|
99
|
+
value: number[][];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface NumericalRangeEmpty {
|
|
103
|
+
type: NumericRangeType.Empty;
|
|
104
|
+
value: null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface NumericalRangeInvalid {
|
|
108
|
+
type: NumericRangeType.InvalidRange;
|
|
109
|
+
value: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export type NumericalRange0 =
|
|
113
|
+
| NumericalRangeSingleValue
|
|
114
|
+
| NumericalRangeArrayRange
|
|
115
|
+
| NumericalRangeMatrixRange
|
|
116
|
+
| NumericalRangeEmpty
|
|
117
|
+
| NumericalRangeInvalid;
|
|
118
|
+
|
|
119
|
+
export interface NumericalRange1 {
|
|
120
|
+
type: NumericRangeType;
|
|
121
|
+
value: NumericalRangeValueType;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function construct_numeric_range_bit_from_string(str: string): NumericalRange0 {
|
|
125
|
+
const values = str.split(":");
|
|
126
|
+
|
|
127
|
+
if (values.length === 1) {
|
|
128
|
+
return {
|
|
129
|
+
type: NumericRangeType.SingleValue,
|
|
130
|
+
value: parseInt(values[0], 10)
|
|
131
|
+
};
|
|
132
|
+
} else if (values.length === 2) {
|
|
133
|
+
const array = values.map((a) => parseInt(a, 10));
|
|
134
|
+
|
|
135
|
+
if (!_valid_range(array[0], array[1])) {
|
|
136
|
+
return {
|
|
137
|
+
type: NumericRangeType.InvalidRange,
|
|
138
|
+
value: str
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
type: NumericRangeType.ArrayRange,
|
|
143
|
+
value: array
|
|
144
|
+
};
|
|
145
|
+
} else {
|
|
146
|
+
return {
|
|
147
|
+
type: NumericRangeType.InvalidRange,
|
|
148
|
+
value: str
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function _normalize(e: NumericalRange1): number | number[] {
|
|
154
|
+
if (e.type === NumericRangeType.SingleValue) {
|
|
155
|
+
const ee = e as NumericalRangeSingleValue;
|
|
156
|
+
return [ee.value, ee.value];
|
|
157
|
+
}
|
|
158
|
+
return e.value as number;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function construct_numeric_range_from_string(str: string): NumericalRange0 {
|
|
162
|
+
if (!regexNumericRange.test(str)) {
|
|
163
|
+
return {
|
|
164
|
+
type: NumericRangeType.InvalidRange,
|
|
165
|
+
value: str
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/* detect multi dim range*/
|
|
169
|
+
const values = str.split(",");
|
|
170
|
+
|
|
171
|
+
if (values.length === 1) {
|
|
172
|
+
return construct_numeric_range_bit_from_string(values[0]);
|
|
173
|
+
} else if (values.length === 2) {
|
|
174
|
+
const elements = values.map(construct_numeric_range_bit_from_string);
|
|
175
|
+
let rowRange: any = elements[0];
|
|
176
|
+
let colRange: any = elements[1];
|
|
177
|
+
if (rowRange.type === NumericRangeType.InvalidRange || colRange.type === NumericRangeType.InvalidRange) {
|
|
178
|
+
return { type: NumericRangeType.InvalidRange, value: str };
|
|
179
|
+
}
|
|
180
|
+
rowRange = _normalize(rowRange);
|
|
181
|
+
colRange = _normalize(colRange);
|
|
182
|
+
return {
|
|
183
|
+
type: NumericRangeType.MatrixRange,
|
|
184
|
+
value: [rowRange, colRange]
|
|
185
|
+
};
|
|
186
|
+
} else {
|
|
187
|
+
// not supported yet
|
|
188
|
+
return { type: NumericRangeType.InvalidRange, value: str };
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function construct_from_string(value: string): NumericalRange0 {
|
|
193
|
+
return construct_numeric_range_from_string(value);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function _set_single_value(value: number | null): NumericalRange0 {
|
|
197
|
+
if (value === null || value < 0 || !isFinite(value)) {
|
|
198
|
+
return {
|
|
199
|
+
type: NumericRangeType.InvalidRange,
|
|
200
|
+
value: "" + value?.toString()
|
|
201
|
+
};
|
|
202
|
+
} else {
|
|
203
|
+
return {
|
|
204
|
+
type: NumericRangeType.SingleValue,
|
|
205
|
+
value: value
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function _check_range(numericalRange: NumericalRange0) {
|
|
211
|
+
switch (numericalRange.type) {
|
|
212
|
+
case NumericRangeType.ArrayRange:
|
|
213
|
+
return _valid_range(numericalRange.value[0], numericalRange.value[1]);
|
|
214
|
+
}
|
|
215
|
+
// istanbul ignore next
|
|
216
|
+
throw new Error("unsupported case");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function _set_range_value(low: number, high: number): NumericalRangeSingleValue | NumericalRangeArrayRange | NumericalRangeInvalid {
|
|
220
|
+
if (low === high) {
|
|
221
|
+
return {
|
|
222
|
+
type: NumericRangeType.SingleValue,
|
|
223
|
+
value: low
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
const numericalRange: NumericalRangeArrayRange = {
|
|
227
|
+
type: NumericRangeType.ArrayRange,
|
|
228
|
+
value: [low, high]
|
|
229
|
+
};
|
|
230
|
+
if (!_check_range(numericalRange as NumericalRangeArrayRange)) {
|
|
231
|
+
return {
|
|
232
|
+
type: NumericRangeType.InvalidRange,
|
|
233
|
+
value: ""
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
return numericalRange;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function construct_from_values(value: number, secondValue?: number): NumericalRange0 {
|
|
240
|
+
if (secondValue === undefined) {
|
|
241
|
+
return _set_single_value(value);
|
|
242
|
+
} else {
|
|
243
|
+
if (!isFinite(secondValue)) {
|
|
244
|
+
throw new Error(" invalid second argument, expecting a number");
|
|
245
|
+
}
|
|
246
|
+
return _set_range_value(value, secondValue);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function _construct_from_array(value: number[], value2?: any): NumericalRange0 {
|
|
251
|
+
assert(value.length === 2);
|
|
252
|
+
|
|
253
|
+
// istanbul ignore next
|
|
254
|
+
if (!isFinite(value[0]) || !isFinite(value[1])) {
|
|
255
|
+
return { type: NumericRangeType.InvalidRange, value: "" + value };
|
|
256
|
+
}
|
|
257
|
+
let range1 = _set_range_value(value[0], value[1]);
|
|
258
|
+
if (!value2) {
|
|
259
|
+
return range1;
|
|
260
|
+
}
|
|
261
|
+
// we have a matrix
|
|
262
|
+
const nr2 = new NumericRange(value2);
|
|
263
|
+
// istanbul ignore next
|
|
264
|
+
if (
|
|
265
|
+
nr2.type === NumericRangeType.InvalidRange ||
|
|
266
|
+
nr2.type === NumericRangeType.MatrixRange ||
|
|
267
|
+
nr2.type === NumericRangeType.Empty
|
|
268
|
+
) {
|
|
269
|
+
return { type: NumericRangeType.InvalidRange, value: "" + value };
|
|
270
|
+
}
|
|
271
|
+
if (range1.type === NumericRangeType.SingleValue) {
|
|
272
|
+
range1 = {
|
|
273
|
+
type: NumericRangeType.ArrayRange,
|
|
274
|
+
value: [range1.value, range1.value]
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
if (nr2.type === NumericRangeType.SingleValue) {
|
|
278
|
+
nr2.type = NumericRangeType.ArrayRange;
|
|
279
|
+
nr2.value = [nr2.value as number, nr2.value as number];
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// istanbul ignore next
|
|
283
|
+
return {
|
|
284
|
+
type: NumericRangeType.MatrixRange,
|
|
285
|
+
value: [range1.value as number[], nr2.value as number[]]
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export class NumericRange implements NumericalRange1 {
|
|
290
|
+
public static coerce = coerceNumericRange;
|
|
291
|
+
|
|
292
|
+
public static schema = schemaNumericRange;
|
|
293
|
+
// tslint:disable:variable-name
|
|
294
|
+
public static NumericRangeType = NumericRangeType;
|
|
295
|
+
|
|
296
|
+
public static readonly empty = new NumericRange() as NumericalRange0;
|
|
297
|
+
|
|
298
|
+
public static overlap(nr1?: NumericalRange0, nr2?: NumericalRange0): boolean {
|
|
299
|
+
nr1 = nr1 || NumericRange.empty;
|
|
300
|
+
nr2 = nr2 || NumericRange.empty;
|
|
301
|
+
|
|
302
|
+
if (NumericRangeType.Empty === nr1.type || NumericRangeType.Empty === nr2.type) {
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
if (NumericRangeType.SingleValue === nr1.type && NumericRangeType.SingleValue === nr2.type) {
|
|
306
|
+
return nr1.value === nr2.value;
|
|
307
|
+
}
|
|
308
|
+
if (NumericRangeType.ArrayRange === nr1.type && NumericRangeType.ArrayRange === nr2.type) {
|
|
309
|
+
// +-----+ +------+ +---+ +------+
|
|
310
|
+
// +----+ +---+ +--------+ +---+
|
|
311
|
+
const l1 = nr1.value[0];
|
|
312
|
+
const h1 = nr1.value[1];
|
|
313
|
+
const l2 = nr2.value[0];
|
|
314
|
+
const h2 = nr2.value[1];
|
|
315
|
+
return _overlap(l1, h1, l2, h2);
|
|
316
|
+
}
|
|
317
|
+
// istanbul ignore next
|
|
318
|
+
assert(false, "NumericalRange#overlap : case not implemented yet "); // TODO
|
|
319
|
+
// istanbul ignore next
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
public type: NumericRangeType;
|
|
324
|
+
public value: NumericalRangeValueType;
|
|
325
|
+
|
|
326
|
+
constructor();
|
|
327
|
+
// tslint:disable-next-line: unified-signatures
|
|
328
|
+
constructor(value: string | null);
|
|
329
|
+
// tslint:disable-next-line: unified-signatures
|
|
330
|
+
constructor(value: number, secondValue?: number);
|
|
331
|
+
// tslint:disable-next-line: unified-signatures
|
|
332
|
+
constructor(value: number[]);
|
|
333
|
+
// tslint:disable-next-line: unified-signatures
|
|
334
|
+
constructor(value: number[], secondValue: number[]);
|
|
335
|
+
constructor(value?: null | string | number | number[], secondValue?: number | number[]) {
|
|
336
|
+
this.type = NumericRangeType.InvalidRange;
|
|
337
|
+
this.value = null;
|
|
338
|
+
|
|
339
|
+
assert(!value || !(value instanceof NumericRange), "use coerce to create a NumericRange");
|
|
340
|
+
assert(!secondValue || typeof secondValue === "number" || Array.isArray(secondValue));
|
|
341
|
+
if (typeof value === "string") {
|
|
342
|
+
const a = construct_from_string(value as string);
|
|
343
|
+
this.type = a.type;
|
|
344
|
+
this.value = a.value;
|
|
345
|
+
} else if (
|
|
346
|
+
typeof value === "number" &&
|
|
347
|
+
isFinite(value) &&
|
|
348
|
+
(secondValue === undefined || (typeof secondValue === "number" && isFinite(secondValue)))
|
|
349
|
+
) {
|
|
350
|
+
const a = construct_from_values(value, secondValue);
|
|
351
|
+
this.type = a.type;
|
|
352
|
+
this.value = a.value;
|
|
353
|
+
} else if (Array.isArray(value)) {
|
|
354
|
+
const a = _construct_from_array(value, secondValue);
|
|
355
|
+
this.type = a.type;
|
|
356
|
+
this.value = a.value;
|
|
357
|
+
} else {
|
|
358
|
+
this.value = "<invalid>";
|
|
359
|
+
this.type = NumericRangeType.Empty;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// xx assert((this.type !== NumericRangeType.ArrayRange) || Array.isArray(this.value));
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
public isValid(): boolean {
|
|
366
|
+
if (this.type === NumericRangeType.ArrayRange) {
|
|
367
|
+
const value = this.value as number[];
|
|
368
|
+
if (value[0] < 0 || value[1] < 0) {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (this.type === NumericRangeType.SingleValue) {
|
|
373
|
+
const value = this.value as number;
|
|
374
|
+
// istanbul ignore next
|
|
375
|
+
if (value < 0) {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return this.type !== NumericRangeType.InvalidRange;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
public isEmpty(): boolean {
|
|
383
|
+
return this.type === NumericRangeType.Empty;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
public isDefined(): boolean {
|
|
387
|
+
return this.type !== NumericRangeType.Empty && this.type !== NumericRangeType.InvalidRange;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
public toString(): string {
|
|
391
|
+
function array_range_to_string(values: number[]): string {
|
|
392
|
+
assert(Array.isArray(values));
|
|
393
|
+
if (values.length === 2 && values[0] === values[1]) {
|
|
394
|
+
return values[0].toString();
|
|
395
|
+
}
|
|
396
|
+
return values.map((value) => value.toString(10)).join(":");
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function matrix_range_to_string(values: any) {
|
|
400
|
+
return values
|
|
401
|
+
.map((value: any) => {
|
|
402
|
+
return Array.isArray(value) ? array_range_to_string(value) : value.toString(10);
|
|
403
|
+
})
|
|
404
|
+
.join(",");
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
switch (this.type) {
|
|
408
|
+
case NumericRangeType.SingleValue:
|
|
409
|
+
return (this.value as any).toString(10);
|
|
410
|
+
|
|
411
|
+
case NumericRangeType.ArrayRange:
|
|
412
|
+
return array_range_to_string(this.value as number[]);
|
|
413
|
+
|
|
414
|
+
case NumericRangeType.Empty:
|
|
415
|
+
return NUMERIC_RANGE_EMPTY_STRING;
|
|
416
|
+
|
|
417
|
+
case NumericRangeType.MatrixRange:
|
|
418
|
+
return matrix_range_to_string(this.value);
|
|
419
|
+
|
|
420
|
+
default:
|
|
421
|
+
assert(this.type === NumericRangeType.InvalidRange);
|
|
422
|
+
return "NumericRange:<Invalid>";
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
public toJSON(): string {
|
|
427
|
+
return this.toString();
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
public toEncodeableString(): UAString {
|
|
431
|
+
switch (this.type) {
|
|
432
|
+
case NumericRangeType.SingleValue:
|
|
433
|
+
case NumericRangeType.ArrayRange:
|
|
434
|
+
case NumericRangeType.MatrixRange:
|
|
435
|
+
return this.toString();
|
|
436
|
+
case NumericRangeType.InvalidRange:
|
|
437
|
+
// istanbul ignore next
|
|
438
|
+
if (!(typeof this.value === "string")) {
|
|
439
|
+
throw new Error("Internal Error");
|
|
440
|
+
}
|
|
441
|
+
return this.value; // value contains the original strings which was detected invalid
|
|
442
|
+
default:
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* @method extract_values
|
|
449
|
+
* @param array flat array containing values or string
|
|
450
|
+
* @param dimensions: of the matrix if data is a matrix
|
|
451
|
+
* @return {*}
|
|
452
|
+
*/
|
|
453
|
+
public extract_values<U, T extends ArrayLike<U>>(array: T, dimensions?: number[]): ExtractResult<T> {
|
|
454
|
+
const self = this as NumericalRange0;
|
|
455
|
+
|
|
456
|
+
if (!array) {
|
|
457
|
+
return {
|
|
458
|
+
array,
|
|
459
|
+
statusCode: this.type === NumericRangeType.Empty ? StatusCodes.Good : StatusCodes.BadIndexRangeNoData
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
let index;
|
|
464
|
+
let low_index;
|
|
465
|
+
let high_index;
|
|
466
|
+
let rowRange;
|
|
467
|
+
let colRange;
|
|
468
|
+
switch (self.type) {
|
|
469
|
+
case NumericRangeType.Empty:
|
|
470
|
+
return extract_empty(array, dimensions);
|
|
471
|
+
|
|
472
|
+
case NumericRangeType.SingleValue:
|
|
473
|
+
index = self.value;
|
|
474
|
+
return extract_single_value(array, index);
|
|
475
|
+
|
|
476
|
+
case NumericRangeType.ArrayRange:
|
|
477
|
+
low_index = self.value[0];
|
|
478
|
+
high_index = self.value[1];
|
|
479
|
+
return extract_array_range(array, low_index, high_index);
|
|
480
|
+
|
|
481
|
+
case NumericRangeType.MatrixRange:
|
|
482
|
+
rowRange = self.value[0];
|
|
483
|
+
colRange = self.value[1];
|
|
484
|
+
return extract_matrix_range(array, rowRange, colRange, dimensions);
|
|
485
|
+
|
|
486
|
+
default:
|
|
487
|
+
return { statusCode: StatusCodes.BadIndexRangeInvalid };
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
public set_values_matrix(
|
|
492
|
+
sourceToAlter: { matrix: Buffer | []; dimensions: number[] },
|
|
493
|
+
newMatrix: Buffer | []
|
|
494
|
+
): { matrix: Buffer | []; statusCode: StatusCode } {
|
|
495
|
+
const { matrix, dimensions } = sourceToAlter;
|
|
496
|
+
const self = this as NumericalRange0;
|
|
497
|
+
assert(dimensions, "expecting valid dimensions here");
|
|
498
|
+
if (self.type !== NumericRangeType.MatrixRange) {
|
|
499
|
+
// istanbul ignore next
|
|
500
|
+
return { matrix, statusCode: StatusCodes.BadTypeMismatch };
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
assert(dimensions.length === 2);
|
|
504
|
+
const nbRows = dimensions[0];
|
|
505
|
+
const nbCols = dimensions[1];
|
|
506
|
+
assert(sourceToAlter.matrix.length === nbRows * nbCols);
|
|
507
|
+
const [rowStart, rowEnd] = self.value[0];
|
|
508
|
+
const [colStart, colEnd] = self.value[1];
|
|
509
|
+
|
|
510
|
+
const nbRowInNew = rowEnd - rowStart + 1;
|
|
511
|
+
const nbColInNew = colEnd - colStart + 1;
|
|
512
|
+
if (nbRowInNew * nbColInNew !== newMatrix.length) {
|
|
513
|
+
return { matrix, statusCode: StatusCodes.BadTypeMismatch };
|
|
514
|
+
}
|
|
515
|
+
// check if the sub-matrix is in th range of the initial matrix
|
|
516
|
+
if (rowEnd >= nbRows || colEnd >= nbCols) {
|
|
517
|
+
// debugLog("out of band range => ", { rowEnd, nbRows, colEnd, nbCols });
|
|
518
|
+
return { matrix, statusCode: StatusCodes.BadTypeMismatch };
|
|
519
|
+
}
|
|
520
|
+
for (let row = rowStart; row <= rowEnd; row++) {
|
|
521
|
+
const ri = row - rowStart;
|
|
522
|
+
for (let col = colStart; col <= colEnd; col++) {
|
|
523
|
+
const ci = col - colStart;
|
|
524
|
+
matrix[row * nbCols + col] = newMatrix[ri * nbColInNew + ci];
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return {
|
|
528
|
+
matrix,
|
|
529
|
+
statusCode: StatusCodes.Good
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
public set_values(arrayToAlter: Buffer | [], newValues: Buffer | []): { array: Buffer | []; statusCode: StatusCode } {
|
|
533
|
+
assert_array_or_buffer(arrayToAlter);
|
|
534
|
+
assert_array_or_buffer(newValues);
|
|
535
|
+
|
|
536
|
+
const self = this as NumericalRange0;
|
|
537
|
+
|
|
538
|
+
let low_index;
|
|
539
|
+
let high_index;
|
|
540
|
+
switch (self.type) {
|
|
541
|
+
case NumericRangeType.Empty:
|
|
542
|
+
low_index = 0;
|
|
543
|
+
high_index = arrayToAlter.length - 1;
|
|
544
|
+
break;
|
|
545
|
+
case NumericRangeType.SingleValue:
|
|
546
|
+
low_index = self.value;
|
|
547
|
+
high_index = self.value;
|
|
548
|
+
break;
|
|
549
|
+
case NumericRangeType.ArrayRange:
|
|
550
|
+
low_index = self.value[0];
|
|
551
|
+
high_index = self.value[1];
|
|
552
|
+
break;
|
|
553
|
+
case NumericRangeType.MatrixRange:
|
|
554
|
+
// for the time being MatrixRange is not supported
|
|
555
|
+
return { array: arrayToAlter, statusCode: StatusCodes.BadIndexRangeNoData };
|
|
556
|
+
default:
|
|
557
|
+
return { array: [], statusCode: StatusCodes.BadIndexRangeInvalid };
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (high_index >= arrayToAlter.length || low_index >= arrayToAlter.length) {
|
|
561
|
+
return { array: [], statusCode: StatusCodes.BadIndexRangeNoData };
|
|
562
|
+
}
|
|
563
|
+
// istanbul ignore next
|
|
564
|
+
if (this.type !== NumericRangeType.Empty && newValues.length !== high_index - low_index + 1) {
|
|
565
|
+
return { array: [], statusCode: StatusCodes.BadIndexRangeInvalid };
|
|
566
|
+
}
|
|
567
|
+
const insertInPlace = Array.isArray(arrayToAlter)
|
|
568
|
+
? insertInPlaceStandardArray
|
|
569
|
+
: arrayToAlter instanceof Buffer
|
|
570
|
+
? insertInPlaceBuffer
|
|
571
|
+
: insertInPlaceTypedArray;
|
|
572
|
+
return {
|
|
573
|
+
array: insertInPlace(arrayToAlter, low_index, high_index, newValues),
|
|
574
|
+
statusCode: StatusCodes.Good
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function slice<U, T extends ArrayLike<U>>(arr: T, start: number, end: number): T {
|
|
580
|
+
if (start === 0 && end === arr.length) {
|
|
581
|
+
return arr;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
let res;
|
|
585
|
+
if ((arr as any).buffer instanceof ArrayBuffer) {
|
|
586
|
+
res = (arr as any).subarray(start, end);
|
|
587
|
+
} else {
|
|
588
|
+
assert(typeof (arr as any).slice === "function");
|
|
589
|
+
assert(arr instanceof Buffer || arr instanceof Array || typeof arr === "string");
|
|
590
|
+
res = (arr as any).slice(start, end);
|
|
591
|
+
}
|
|
592
|
+
if (res instanceof Uint8Array && arr instanceof Buffer) {
|
|
593
|
+
// note in io-js 3.00 onward standard Buffer are implemented differently and
|
|
594
|
+
// provides a buffer member and a subarray method, in fact in io-js 3.0
|
|
595
|
+
// it seems that Buffer acts as a Uint8Array. in this very special case
|
|
596
|
+
// we need to make sure that we end up with a Buffer object and not a Uint8Array.
|
|
597
|
+
res = Buffer.from(res);
|
|
598
|
+
}
|
|
599
|
+
return res;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
export interface ExtractResult<T> {
|
|
603
|
+
array?: T;
|
|
604
|
+
statusCode: StatusCode;
|
|
605
|
+
dimensions?: number[];
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function extract_empty<U, T extends ArrayLike<U>>(array: T, dimensions: any): ExtractResult<T> {
|
|
609
|
+
return {
|
|
610
|
+
array: slice(array, 0, array.length),
|
|
611
|
+
dimensions,
|
|
612
|
+
statusCode: StatusCodes.Good
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function extract_single_value<U, T extends ArrayLike<U>>(array: T, index: number): ExtractResult<T> {
|
|
617
|
+
if (index >= array.length) {
|
|
618
|
+
if (typeof array === "string") {
|
|
619
|
+
return { array: "" as any as T, statusCode: StatusCodes.BadIndexRangeNoData };
|
|
620
|
+
}
|
|
621
|
+
return { array: [] as any as T, statusCode: StatusCodes.BadIndexRangeNoData };
|
|
622
|
+
}
|
|
623
|
+
return {
|
|
624
|
+
array: slice(array, index, index + 1),
|
|
625
|
+
statusCode: StatusCodes.Good
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
function extract_array_range<U, T extends ArrayLike<U>>(array: T, low_index: number, high_index: number): ExtractResult<T> {
|
|
630
|
+
assert(isFinite(low_index) && isFinite(high_index));
|
|
631
|
+
assert(low_index >= 0);
|
|
632
|
+
assert(low_index <= high_index);
|
|
633
|
+
if (low_index >= array.length) {
|
|
634
|
+
if (typeof array === "string") {
|
|
635
|
+
return { array: "" as any as T, statusCode: StatusCodes.BadIndexRangeNoData };
|
|
636
|
+
}
|
|
637
|
+
return { array: [] as any as T, statusCode: StatusCodes.BadIndexRangeNoData };
|
|
638
|
+
}
|
|
639
|
+
// clamp high index
|
|
640
|
+
high_index = Math.min(high_index, array.length - 1);
|
|
641
|
+
|
|
642
|
+
return {
|
|
643
|
+
array: slice(array, low_index, high_index + 1),
|
|
644
|
+
statusCode: StatusCodes.Good
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function isArrayLike(value: any): boolean {
|
|
649
|
+
return typeof value.length === "number" || Object.prototype.hasOwnProperty.call(value, "length");
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function extract_matrix_range<U, T extends ArrayLike<U>>(
|
|
653
|
+
array: T,
|
|
654
|
+
rowRange: number[],
|
|
655
|
+
colRange: number[],
|
|
656
|
+
dimension?: number[]
|
|
657
|
+
): ExtractResult<T> {
|
|
658
|
+
assert(Array.isArray(rowRange) && Array.isArray(colRange));
|
|
659
|
+
|
|
660
|
+
if (array.length === 0) {
|
|
661
|
+
return {
|
|
662
|
+
array: [] as any as T,
|
|
663
|
+
statusCode: StatusCodes.BadIndexRangeNoData
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
if (isArrayLike((array as any)[0]) && !dimension) {
|
|
667
|
+
// like extracting data from a one dimensional array of strings or byteStrings...
|
|
668
|
+
const result = extract_array_range(array, rowRange[0], rowRange[1]);
|
|
669
|
+
for (let i = 0; i < result.array!.length; i++) {
|
|
670
|
+
const e = (result.array! as any)[i];
|
|
671
|
+
(result.array as any)[i] = extract_array_range(e, colRange[0], colRange[1]).array;
|
|
672
|
+
}
|
|
673
|
+
return result;
|
|
674
|
+
}
|
|
675
|
+
if (!dimension) {
|
|
676
|
+
return {
|
|
677
|
+
array: [] as any as T,
|
|
678
|
+
statusCode: StatusCodes.BadIndexRangeNoData
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
assert(dimension, "expecting dimension to know the shape of the matrix represented by the flat array");
|
|
683
|
+
|
|
684
|
+
//
|
|
685
|
+
const rowLow = rowRange[0];
|
|
686
|
+
const rowHigh = rowRange[1];
|
|
687
|
+
const colLow = colRange[0];
|
|
688
|
+
const colHigh = colRange[1];
|
|
689
|
+
|
|
690
|
+
const nbRow = dimension[0];
|
|
691
|
+
const nbCol = dimension[1];
|
|
692
|
+
|
|
693
|
+
const nbRowDest = rowHigh - rowLow + 1;
|
|
694
|
+
const nbColDest = colHigh - colLow + 1;
|
|
695
|
+
|
|
696
|
+
// construct an array of the same type with the appropriate length to
|
|
697
|
+
// store the extracted matrix.
|
|
698
|
+
const tmp = new (array as any).constructor(nbColDest * nbRowDest);
|
|
699
|
+
|
|
700
|
+
let row;
|
|
701
|
+
let col;
|
|
702
|
+
let r;
|
|
703
|
+
let c;
|
|
704
|
+
r = 0;
|
|
705
|
+
for (row = rowLow; row <= rowHigh; row++) {
|
|
706
|
+
c = 0;
|
|
707
|
+
for (col = colLow; col <= colHigh; col++) {
|
|
708
|
+
const srcIndex = row * nbCol + col;
|
|
709
|
+
const destIndex = r * nbColDest + c;
|
|
710
|
+
tmp[destIndex] = (array as any)[srcIndex];
|
|
711
|
+
c++;
|
|
712
|
+
}
|
|
713
|
+
r += 1;
|
|
714
|
+
}
|
|
715
|
+
return {
|
|
716
|
+
array: tmp,
|
|
717
|
+
dimensions: [nbRowDest, nbColDest],
|
|
718
|
+
statusCode: StatusCodes.Good
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function assert_array_or_buffer(array: any) {
|
|
723
|
+
assert(Array.isArray(array) || array.buffer instanceof ArrayBuffer || array instanceof Buffer);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
function insertInPlaceStandardArray(arrayToAlter: any, low: number, high: number, newValues: any): any {
|
|
727
|
+
const args = [low, high - low + 1].concat(newValues);
|
|
728
|
+
arrayToAlter.splice(...args);
|
|
729
|
+
return arrayToAlter;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
function insertInPlaceTypedArray(arrayToAlter: any, low: number, high: number, newValues: any): any {
|
|
733
|
+
if (low === 0 && high === arrayToAlter.length - 1) {
|
|
734
|
+
return new arrayToAlter.constructor(newValues);
|
|
735
|
+
}
|
|
736
|
+
assert(newValues.length === high - low + 1);
|
|
737
|
+
arrayToAlter.subarray(low, high + 1).set(newValues);
|
|
738
|
+
return arrayToAlter;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function insertInPlaceBuffer(bufferToAlter: any, low: number, high: number, newValues: any): any {
|
|
742
|
+
if (low === 0 && high === bufferToAlter.length - 1) {
|
|
743
|
+
return Buffer.from(newValues);
|
|
744
|
+
}
|
|
745
|
+
assert(newValues.length === high - low + 1);
|
|
746
|
+
for (let i = 0; i < newValues.length; i++) {
|
|
747
|
+
bufferToAlter[i + low] = newValues[i];
|
|
748
|
+
}
|
|
749
|
+
return bufferToAlter;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
function _overlap(l1: number, h1: number, l2: number, h2: number): boolean {
|
|
753
|
+
return Math.max(l1, l2) <= Math.min(h1, h2);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
export function encodeNumericRange(numericRange: NumericRange, stream: OutputBinaryStream): void {
|
|
757
|
+
assert(numericRange instanceof NumericRange);
|
|
758
|
+
encodeString(numericRange.toEncodeableString(), stream);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
export function decodeNumericRange(stream: BinaryStream, _value?: NumericRange): NumericRange {
|
|
762
|
+
const str = decodeString(stream)!;
|
|
763
|
+
return new NumericRange(str);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
function coerceNumericRange(value: any | string | NumericRange | null | number[]): NumericRange {
|
|
767
|
+
if (value instanceof NumericRange) {
|
|
768
|
+
return value;
|
|
769
|
+
}
|
|
770
|
+
if (value === null || value === undefined) {
|
|
771
|
+
return new NumericRange();
|
|
772
|
+
}
|
|
773
|
+
if (value === NUMERIC_RANGE_EMPTY_STRING) {
|
|
774
|
+
return new NumericRange();
|
|
775
|
+
}
|
|
776
|
+
assert(typeof value === "string" || Array.isArray(value));
|
|
777
|
+
return new NumericRange(value);
|
|
778
|
+
}
|