node-opcua-address-space 2.186.17 → 2.187.1
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/api/address_space_public.ts +16 -0
- package/api/helpers/resolve_opaque_on_address_space.ts +13 -1
- package/api/index.ts +14 -3
- package/api/loader/store_record_applier.ts +267 -0
- package/api/session_context.ts +116 -27
- package/dist/api/address_space_public.d.ts +14 -0
- package/dist/api/address_space_public.d.ts.map +1 -1
- package/dist/api/address_space_public.js +3 -0
- package/dist/api/address_space_public.js.map +1 -1
- package/dist/api/helpers/resolve_opaque_on_address_space.d.ts +6 -0
- package/dist/api/helpers/resolve_opaque_on_address_space.d.ts.map +1 -1
- package/dist/api/helpers/resolve_opaque_on_address_space.js +12 -1
- package/dist/api/helpers/resolve_opaque_on_address_space.js.map +1 -1
- package/dist/api/index.d.ts +4 -3
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +4 -3
- package/dist/api/index.js.map +1 -1
- package/dist/api/loader/store_record_applier.d.ts +49 -0
- package/dist/api/loader/store_record_applier.d.ts.map +1 -0
- package/dist/api/loader/store_record_applier.js +235 -0
- package/dist/api/loader/store_record_applier.js.map +1 -0
- package/dist/api/session_context.d.ts +10 -0
- package/dist/api/session_context.d.ts.map +1 -1
- package/dist/api/session_context.js +100 -27
- package/dist/api/session_context.js.map +1 -1
- package/dist/impl/address_space.d.ts +2 -0
- package/dist/impl/address_space.d.ts.map +1 -1
- package/dist/impl/address_space.js +12 -2
- package/dist/impl/address_space.js.map +1 -1
- package/dist/impl/base_node_impl.d.ts +12 -0
- package/dist/impl/base_node_impl.d.ts.map +1 -1
- package/dist/impl/base_node_impl.js +35 -29
- package/dist/impl/base_node_impl.js.map +1 -1
- package/dist/impl/base_node_private.d.ts +5 -5
- package/dist/impl/base_node_private.d.ts.map +1 -1
- package/dist/impl/base_node_private.js +11 -5
- package/dist/impl/base_node_private.js.map +1 -1
- package/dist/impl/extension_object_array_node.d.ts +4 -2
- package/dist/impl/extension_object_array_node.d.ts.map +1 -1
- package/dist/impl/extension_object_array_node.js +5 -2
- package/dist/impl/extension_object_array_node.js.map +1 -1
- package/dist/impl/historical_access/address_space_historical_data_node.d.ts +26 -2
- package/dist/impl/historical_access/address_space_historical_data_node.d.ts.map +1 -1
- package/dist/impl/historical_access/address_space_historical_data_node.js +39 -0
- package/dist/impl/historical_access/address_space_historical_data_node.js.map +1 -1
- package/dist/impl/namespace_impl.d.ts.map +1 -1
- package/dist/impl/namespace_impl.js +3 -1
- package/dist/impl/namespace_impl.js.map +1 -1
- package/dist/impl/nodeid_manager.d.ts.map +1 -1
- package/dist/impl/nodeid_manager.js +10 -7
- package/dist/impl/nodeid_manager.js.map +1 -1
- package/dist/impl/nodeset_tools/construct_namespace_dependency.d.ts.map +1 -1
- package/dist/impl/nodeset_tools/construct_namespace_dependency.js +28 -0
- package/dist/impl/nodeset_tools/construct_namespace_dependency.js.map +1 -1
- package/dist/impl/reference_impl.d.ts +2 -1
- package/dist/impl/reference_impl.d.ts.map +1 -1
- package/dist/impl/reference_impl.js +6 -5
- package/dist/impl/reference_impl.js.map +1 -1
- package/dist/impl/reference_index.d.ts +22 -0
- package/dist/impl/reference_index.d.ts.map +1 -0
- package/dist/impl/reference_index.js +104 -0
- package/dist/impl/reference_index.js.map +1 -0
- package/dist/impl/ua_variable_impl.d.ts +10 -0
- package/dist/impl/ua_variable_impl.d.ts.map +1 -1
- package/dist/impl/ua_variable_impl.js +152 -106
- package/dist/impl/ua_variable_impl.js.map +1 -1
- package/dist/tsconfig_base.tsbuildinfo +1 -1
- package/distNodeJS/generate_address_space.d.ts +6 -1
- package/distNodeJS/generate_address_space.d.ts.map +1 -1
- package/distNodeJS/generate_address_space.js +33 -1
- package/distNodeJS/generate_address_space.js.map +1 -1
- package/impl/address_space.ts +16 -2
- package/impl/base_node_impl.ts +52 -30
- package/impl/base_node_private.ts +16 -10
- package/impl/extension_object_array_node.ts +12 -3
- package/impl/historical_access/address_space_historical_data_node.ts +64 -1
- package/impl/namespace_impl.ts +3 -1
- package/impl/nodeid_manager.ts +10 -7
- package/impl/nodeset_tools/construct_namespace_dependency.ts +23 -1
- package/impl/reference_impl.ts +6 -5
- package/impl/reference_index.ts +116 -0
- package/impl/ua_variable_impl.ts +203 -115
- package/package.json +34 -33
- package/source_nodejs/generate_address_space.ts +46 -1
package/impl/ua_variable_impl.ts
CHANGED
|
@@ -135,10 +135,6 @@ function adjust_samplingInterval(minimumSamplingInterval: number): number {
|
|
|
135
135
|
return minimumSamplingInterval;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
function is_Variant(v: unknown): boolean {
|
|
139
|
-
return v instanceof Variant;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
138
|
function is_StatusCode(v: unknown): boolean {
|
|
143
139
|
return !!(
|
|
144
140
|
v?.constructor &&
|
|
@@ -149,11 +145,8 @@ function is_StatusCode(v: unknown): boolean {
|
|
|
149
145
|
);
|
|
150
146
|
}
|
|
151
147
|
|
|
152
|
-
function
|
|
153
|
-
|
|
154
|
-
// /@@assert(v.isValid());
|
|
155
|
-
}
|
|
156
|
-
return is_Variant(v) || is_StatusCode(v);
|
|
148
|
+
function isThenable<T>(v: T | PromiseLike<T>): v is PromiseLike<T> {
|
|
149
|
+
return typeof (v as PromiseLike<T> | null | undefined)?.then === "function";
|
|
157
150
|
}
|
|
158
151
|
|
|
159
152
|
function default_func(this: UAVariable, dataValue1: DataValue, callback1: CallbackT<StatusCode>) {
|
|
@@ -213,46 +206,60 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
213
206
|
implements UAVariable<T>, UAVariableImplExtArray
|
|
214
207
|
{
|
|
215
208
|
// -------------- UAvaraibleImplExArray
|
|
216
|
-
$$variableType?: UAVariableType;
|
|
217
|
-
$$dataType?: UADataType
|
|
218
|
-
$$getElementBrowseName?: (extObject: ExtensionObject, index: number | number[]) => QualifiedName;
|
|
219
|
-
$$extensionObjectArray?: ExtensionObject[];
|
|
220
|
-
$$indexPropertyName?: string;
|
|
209
|
+
declare $$variableType?: UAVariableType;
|
|
210
|
+
declare $$dataType?: UADataType;
|
|
211
|
+
declare $$getElementBrowseName?: (extObject: ExtensionObject, index: number | number[]) => QualifiedName;
|
|
212
|
+
declare $$extensionObjectArray?: ExtensionObject[];
|
|
213
|
+
declare $$indexPropertyName?: string;
|
|
221
214
|
// --------------
|
|
222
215
|
|
|
223
216
|
public readonly nodeClass = NodeClass.Variable;
|
|
224
217
|
|
|
225
218
|
public dataType: NodeId;
|
|
226
|
-
private _basicDataType?: DataType;
|
|
219
|
+
declare private _basicDataType?: DataType;
|
|
227
220
|
|
|
228
221
|
// narrowing this to ExtensionObject breaks declaration-merging in ua_two_state_variable.ts and elsewhere
|
|
229
222
|
// (concrete *T variants bind $extensionObject to a different, more specific shape) — see the matching
|
|
230
223
|
// comment on UAVariable.$extensionObject in node-opcua-address-space-base for the documented precedent.
|
|
231
224
|
// biome-ignore lint/suspicious/noExplicitAny: see comment above
|
|
232
|
-
public $extensionObject?: any;
|
|
233
|
-
public $set_ExtensionObject?: (
|
|
225
|
+
declare public $extensionObject?: any;
|
|
226
|
+
declare public $set_ExtensionObject?: (
|
|
227
|
+
newValue: ExtensionObject,
|
|
228
|
+
sourceTimestamp: PreciseClock,
|
|
229
|
+
cache: Set<UAVariableImpl>
|
|
230
|
+
) => void;
|
|
234
231
|
|
|
235
|
-
public $historicalDataConfiguration?: UAHistoricalDataConfiguration;
|
|
236
|
-
public varHistorian?: IVariableHistorian;
|
|
232
|
+
declare public $historicalDataConfiguration?: UAHistoricalDataConfiguration;
|
|
233
|
+
declare public varHistorian?: IVariableHistorian;
|
|
237
234
|
|
|
238
235
|
/**
|
|
239
236
|
* @internal @private
|
|
240
237
|
*/
|
|
241
238
|
public $dataValue: DataValue;
|
|
242
239
|
public accessLevel: number;
|
|
243
|
-
public userAccessLevel?: number;
|
|
240
|
+
declare public userAccessLevel?: number;
|
|
244
241
|
public valueRank: number;
|
|
245
242
|
public minimumSamplingInterval: number;
|
|
246
243
|
public historizing: boolean;
|
|
247
244
|
public semantic_version: number;
|
|
248
245
|
public arrayDimensions: null | number[];
|
|
249
246
|
|
|
250
|
-
public _timestamped_get_func?: TimestampGetFunc | null;
|
|
251
|
-
public _timestamped_set_func?: VariableDataValueSetterWithCallback | null;
|
|
252
|
-
public _get_func?: GetFunc | null;
|
|
253
|
-
public _set_func?: ((value: Variant, callback: (err: Error | null, statusCode?: StatusCode) => void) => void) | null;
|
|
254
|
-
public refreshFunc?: (callback: CallbackT<DataValue>) => void;
|
|
255
|
-
|
|
247
|
+
declare public _timestamped_get_func?: TimestampGetFunc | null;
|
|
248
|
+
declare public _timestamped_set_func?: VariableDataValueSetterWithCallback | null;
|
|
249
|
+
declare public _get_func?: GetFunc | null;
|
|
250
|
+
declare public _set_func?: ((value: Variant, callback: (err: Error | null, statusCode?: StatusCode) => void) => void) | null;
|
|
251
|
+
declare public refreshFunc?: (callback: CallbackT<DataValue>) => void;
|
|
252
|
+
/**
|
|
253
|
+
* true when refreshFunc only wraps the synchronous getter of a Variable bound with a plain
|
|
254
|
+
* `get`. readValue evaluates that getter itself: refreshing such a Variable before reading
|
|
255
|
+
* it, as the Read service does for the others, evaluates the getter twice for nothing.
|
|
256
|
+
*/
|
|
257
|
+
declare public _refreshFuncWrapsGetter?: boolean;
|
|
258
|
+
declare public __waiting_callbacks?: CallbackT<DataValue>[];
|
|
259
|
+
// the variant types already found to fit the DataType below (bit k: DataType k), set on the first write
|
|
260
|
+
declare private _validatedForDataType?: NodeId;
|
|
261
|
+
declare private _validatedVariantTypes?: number;
|
|
262
|
+
declare private __satisfy_waiting_callbacks?: (err: Error | null, dataValue?: DataValue) => void;
|
|
256
263
|
|
|
257
264
|
get typeDefinitionObj(): UAVariableType {
|
|
258
265
|
/* c8 ignore next */
|
|
@@ -318,7 +325,6 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
318
325
|
}
|
|
319
326
|
private checkPermissionPrivate(context: ISessionContext, permission: PermissionType): boolean {
|
|
320
327
|
if (!context) return true;
|
|
321
|
-
assert(context instanceof SessionContext);
|
|
322
328
|
if (context.checkPermission) {
|
|
323
329
|
if (!(context.checkPermission instanceof Function)) {
|
|
324
330
|
errorLog("context checkPermission is not a function");
|
|
@@ -437,7 +443,14 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
437
443
|
if (!this.checkPermissionPrivate(context, PermissionType.Read)) {
|
|
438
444
|
return makeNowDataValue({ statusCode: StatusCodes.BadUserAccessDenied });
|
|
439
445
|
}
|
|
440
|
-
|
|
446
|
+
// isUserReadable() would evaluate isReadable() and the Read permission a second time, and the
|
|
447
|
+
// permission lookup walks the namespace metadata. Both just passed above, so only the access
|
|
448
|
+
// level is left, unless a node replaced isUserReadable (see makeNotReadableIfEnabledFlagIsFalse).
|
|
449
|
+
const userReadable =
|
|
450
|
+
this.isUserReadable === UAVariableImpl.prototype.isUserReadable
|
|
451
|
+
? this.checkAccessLevelPrivate(context, AccessLevelFlag.CurrentRead)
|
|
452
|
+
: this.isUserReadable(context);
|
|
453
|
+
if (!userReadable) {
|
|
441
454
|
return makeNowDataValue({ statusCode: StatusCodes.BadNotReadable });
|
|
442
455
|
}
|
|
443
456
|
if (!isValidDataEncoding(dataEncoding)) {
|
|
@@ -1273,6 +1286,7 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1273
1286
|
this._get_func = null;
|
|
1274
1287
|
this._set_func = null;
|
|
1275
1288
|
this.refreshFunc = undefined;
|
|
1289
|
+
this._refreshFuncWrapsGetter = false;
|
|
1276
1290
|
this._historyRead = UAVariableImpl.prototype._historyRead;
|
|
1277
1291
|
}
|
|
1278
1292
|
|
|
@@ -1303,12 +1317,22 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1303
1317
|
public readValueAsync(context: ISessionContext | null, callback: CallbackT<DataValue>): void;
|
|
1304
1318
|
public readValueAsync(context: ISessionContext | null): Promise<DataValue>;
|
|
1305
1319
|
public readValueAsync(context: ISessionContext | null, callback?: CallbackT<DataValue>): Promise<DataValue> | undefined {
|
|
1306
|
-
|
|
1320
|
+
// written out rather than wrapped with thenify-ex: every sample of a monitored item comes
|
|
1321
|
+
// through here with a callback, and the generic wrapper forwarded it with apply(arguments)
|
|
1322
|
+
if (callback) {
|
|
1323
|
+
this._readValueAsync(context, callback);
|
|
1324
|
+
return undefined;
|
|
1325
|
+
}
|
|
1326
|
+
return new Promise<DataValue>((resolve, reject) => {
|
|
1327
|
+
this._readValueAsync(context, (err, dataValue) => (err ? reject(err) : resolve(dataValue as DataValue)));
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1307
1330
|
|
|
1331
|
+
private _readValueAsync(context: ISessionContext | null, callback: CallbackT<DataValue>): void {
|
|
1308
1332
|
context = context || SessionContext.defaultContext;
|
|
1309
1333
|
|
|
1310
1334
|
this.__waiting_callbacks = this.__waiting_callbacks || [];
|
|
1311
|
-
this.__waiting_callbacks.push(callback
|
|
1335
|
+
this.__waiting_callbacks.push(callback);
|
|
1312
1336
|
|
|
1313
1337
|
const _readValueAsync_in_progress = this.__waiting_callbacks.length >= 2;
|
|
1314
1338
|
if (_readValueAsync_in_progress) {
|
|
@@ -1320,19 +1344,10 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1320
1344
|
return;
|
|
1321
1345
|
}
|
|
1322
1346
|
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
};
|
|
1328
|
-
|
|
1329
|
-
let func: (innerCallback: (err: Error | null, dataValue: DataValue) => void) => void;
|
|
1330
|
-
|
|
1331
|
-
// stamped like the same gates of readValue(): the requested timestamps are expected
|
|
1332
|
-
// on a Bad result too (FEAT-34)
|
|
1333
|
-
const answerStatusOnly = (statusCode: StatusCode) => (innerCallback: (err: Error | null, dataValue: DataValue) => void) =>
|
|
1334
|
-
innerCallback(null, makeNowDataValue({ statusCode }));
|
|
1335
|
-
|
|
1347
|
+
// runs once per sample of every monitored item, so nothing here allocates a closure or
|
|
1348
|
+
// binds a function: the denial (if any) is a StatusCode, and the callback that releases
|
|
1349
|
+
// the waiting readers is made once per variable
|
|
1350
|
+
let denied: StatusCode | null = null;
|
|
1336
1351
|
if (context.isAccessRestricted(this)) {
|
|
1337
1352
|
// same gate as readValue(): a variable bound with a simple get()
|
|
1338
1353
|
// also gets a refreshFunc, sending every read down the asyncRefresh
|
|
@@ -1340,30 +1355,48 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1340
1355
|
// through readValue(). Without this check here, a node hardened
|
|
1341
1356
|
// with AccessRestrictions(EncryptionRequired) answered its value
|
|
1342
1357
|
// over an unencrypted channel.
|
|
1343
|
-
|
|
1358
|
+
denied = StatusCodes.BadSecurityModeInsufficient;
|
|
1344
1359
|
} else if (!this.isReadable(context)) {
|
|
1345
|
-
|
|
1360
|
+
denied = StatusCodes.BadNotReadable;
|
|
1346
1361
|
} else if (!this.checkPermissionPrivate(context, PermissionType.Read)) {
|
|
1347
|
-
|
|
1348
|
-
} else if (
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1362
|
+
denied = StatusCodes.BadUserAccessDenied;
|
|
1363
|
+
} else if (
|
|
1364
|
+
// as in readValue(): the Read permission has just passed, only the access level is
|
|
1365
|
+
// left, unless a node replaced isUserReadable (see makeNotReadableIfEnabledFlagIsFalse)
|
|
1366
|
+
this.isUserReadable === UAVariableImpl.prototype.isUserReadable
|
|
1367
|
+
? !this.checkAccessLevelPrivate(context, AccessLevelFlag.CurrentRead)
|
|
1368
|
+
: !this.isUserReadable(context)
|
|
1369
|
+
) {
|
|
1370
|
+
denied = StatusCodes.BadNotReadable;
|
|
1353
1371
|
}
|
|
1354
1372
|
|
|
1355
|
-
|
|
1373
|
+
this.__satisfy_waiting_callbacks ??= (err: Error | null, dataValue?: DataValue) => {
|
|
1356
1374
|
// now call all pending callbacks
|
|
1357
1375
|
const callbacks = this.__waiting_callbacks || [];
|
|
1358
1376
|
this.__waiting_callbacks = [];
|
|
1359
|
-
const _n = callbacks.length;
|
|
1360
1377
|
for (const callback1 of callbacks) {
|
|
1361
1378
|
callback1.call(this, err, dataValue);
|
|
1362
1379
|
}
|
|
1363
1380
|
};
|
|
1381
|
+
const satisfy_callbacks = this.__satisfy_waiting_callbacks;
|
|
1364
1382
|
|
|
1365
1383
|
try {
|
|
1366
|
-
|
|
1384
|
+
if (denied) {
|
|
1385
|
+
// stamped like the same gates of readValue(): the requested timestamps are expected
|
|
1386
|
+
// on a Bad result too (FEAT-34)
|
|
1387
|
+
satisfy_callbacks(null, makeNowDataValue({ statusCode: denied }));
|
|
1388
|
+
} else if (typeof this.refreshFunc === "function") {
|
|
1389
|
+
const clock = getCurrentClock();
|
|
1390
|
+
if (this.asyncRefresh === wrappedAsyncRefresh) {
|
|
1391
|
+
// the thenified wrapper adds nothing when a callback is given
|
|
1392
|
+
asyncRefreshImpl.call(this, clock, satisfy_callbacks);
|
|
1393
|
+
} else {
|
|
1394
|
+
this.asyncRefresh(clock, satisfy_callbacks);
|
|
1395
|
+
}
|
|
1396
|
+
} else {
|
|
1397
|
+
assert(this.$dataValue instanceof DataValue);
|
|
1398
|
+
satisfy_callbacks(null, this.readValue(context));
|
|
1399
|
+
}
|
|
1367
1400
|
} catch (err) {
|
|
1368
1401
|
// c8 ignore next
|
|
1369
1402
|
if (doDebug) {
|
|
@@ -1374,7 +1407,6 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1374
1407
|
}
|
|
1375
1408
|
satisfy_callbacks(err as Error);
|
|
1376
1409
|
}
|
|
1377
|
-
return undefined;
|
|
1378
1410
|
}
|
|
1379
1411
|
|
|
1380
1412
|
public getWriteMask(): number {
|
|
@@ -1879,7 +1911,27 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1879
1911
|
}
|
|
1880
1912
|
|
|
1881
1913
|
public _validate_DataType(variantDataType: DataType): boolean {
|
|
1882
|
-
|
|
1914
|
+
// every write of a Variable checks the same variant type against the same DataType: a check that
|
|
1915
|
+
// passed is remembered while the DataType stays the same; a refusal is always checked again
|
|
1916
|
+
const bit = variantDataType >= 0 && variantDataType < 31 ? 1 << variantDataType : 0;
|
|
1917
|
+
if (bit && this._validatedForDataType === this.dataType && ((this._validatedVariantTypes ?? 0) & bit) !== 0) {
|
|
1918
|
+
return true;
|
|
1919
|
+
}
|
|
1920
|
+
const valid = validateDataTypeCorrectness(
|
|
1921
|
+
this.addressSpace,
|
|
1922
|
+
this.dataType,
|
|
1923
|
+
variantDataType,
|
|
1924
|
+
/* allow Nulls */ false,
|
|
1925
|
+
this.nodeId
|
|
1926
|
+
);
|
|
1927
|
+
if (valid && bit) {
|
|
1928
|
+
if (this._validatedForDataType !== this.dataType) {
|
|
1929
|
+
this._validatedForDataType = this.dataType;
|
|
1930
|
+
this._validatedVariantTypes = 0;
|
|
1931
|
+
}
|
|
1932
|
+
this._validatedVariantTypes = (this._validatedVariantTypes ?? 0) | bit;
|
|
1933
|
+
}
|
|
1934
|
+
return valid;
|
|
1883
1935
|
}
|
|
1884
1936
|
|
|
1885
1937
|
public _internal_set_value(value: Variant): void {
|
|
@@ -1936,9 +1988,19 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1936
1988
|
*/
|
|
1937
1989
|
public _inner_replace_dataValue(dataValue: DataValue, indexRange?: NumericRange | null) {
|
|
1938
1990
|
assert(this.$dataValue.value instanceof Variant);
|
|
1939
|
-
const old_dataValue = this.$dataValue.clone();
|
|
1940
1991
|
|
|
1941
|
-
|
|
1992
|
+
const replacesArrayInPlace = !!this.$$extensionObjectArray && dataValue.value.arrayType !== VariantArrayType.Scalar;
|
|
1993
|
+
const replacesExtensionObjectInPlace =
|
|
1994
|
+
this._basicDataType === DataType.ExtensionObject &&
|
|
1995
|
+
this.valueRank === -1 &&
|
|
1996
|
+
!!this.$set_ExtensionObject &&
|
|
1997
|
+
dataValue.value.arrayType === VariantArrayType.Scalar;
|
|
1998
|
+
// the previous value is needed intact for the comparison below. Only the two branches that
|
|
1999
|
+
// write into $dataValue in place need a copy of it: the common case swaps in the new object
|
|
2000
|
+
// and leaves the old one untouched
|
|
2001
|
+
const old_dataValue = replacesArrayInPlace || replacesExtensionObjectInPlace ? this.$dataValue.clone() : this.$dataValue;
|
|
2002
|
+
|
|
2003
|
+
if (replacesArrayInPlace) {
|
|
1942
2004
|
// we have a bounded array or matrix
|
|
1943
2005
|
assert(Array.isArray(dataValue.value.value));
|
|
1944
2006
|
if (this.$$extensionObjectArray !== this.$dataValue.value.value) {
|
|
@@ -1952,12 +2014,7 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1952
2014
|
this.$dataValue.serverPicoseconds = dataValue.serverPicoseconds;
|
|
1953
2015
|
this.$dataValue.sourceTimestamp = dataValue.sourceTimestamp;
|
|
1954
2016
|
this.$dataValue.sourcePicoseconds = dataValue.sourcePicoseconds;
|
|
1955
|
-
} else if (
|
|
1956
|
-
this._basicDataType === DataType.ExtensionObject &&
|
|
1957
|
-
this.valueRank === -1 &&
|
|
1958
|
-
this.$set_ExtensionObject &&
|
|
1959
|
-
dataValue.value.arrayType === VariantArrayType.Scalar
|
|
1960
|
-
) {
|
|
2017
|
+
} else if (replacesExtensionObjectInPlace && this.$set_ExtensionObject) {
|
|
1961
2018
|
// the entire extension object is changed.
|
|
1962
2019
|
this.$dataValue.statusCode = this.$dataValue.statusCode || StatusCodes.Good;
|
|
1963
2020
|
const preciseClock = coerceClock(this.$dataValue.sourceTimestamp, this.$dataValue.sourcePicoseconds);
|
|
@@ -1966,14 +2023,16 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1966
2023
|
this.$dataValue = dataValue;
|
|
1967
2024
|
this.$dataValue.statusCode = this.$dataValue.statusCode || StatusCodes.Good;
|
|
1968
2025
|
}
|
|
1969
|
-
// repair missing timestamps
|
|
1970
|
-
|
|
2026
|
+
// repair missing timestamps (the clock is only read when one is missing: this runs on
|
|
2027
|
+
// every stored value, and most arrive with both timestamps)
|
|
2028
|
+
let now: Date | undefined;
|
|
1971
2029
|
if (!dataValue.serverTimestamp) {
|
|
2030
|
+
now = new Date();
|
|
1972
2031
|
this.$dataValue.serverTimestamp = old_dataValue.serverTimestamp || now;
|
|
1973
2032
|
this.$dataValue.serverPicoseconds = old_dataValue.serverPicoseconds || 0;
|
|
1974
2033
|
}
|
|
1975
2034
|
if (!dataValue.sourceTimestamp) {
|
|
1976
|
-
this.$dataValue.sourceTimestamp = old_dataValue.sourceTimestamp || now;
|
|
2035
|
+
this.$dataValue.sourceTimestamp = old_dataValue.sourceTimestamp || (now ?? new Date());
|
|
1977
2036
|
this.$dataValue.sourcePicoseconds = old_dataValue.sourcePicoseconds || 0;
|
|
1978
2037
|
}
|
|
1979
2038
|
|
|
@@ -1997,7 +2056,7 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1997
2056
|
this.touchValue(preciseClock);
|
|
1998
2057
|
propagateTouchValueDownward(this, preciseClock, cache);
|
|
1999
2058
|
}
|
|
2000
|
-
} else {
|
|
2059
|
+
} else if ((this as UAVariable).listenerCount("value_changed") > 0) {
|
|
2001
2060
|
(this as UAVariable).emit("value_changed", this.$dataValue.clone(), indexRange);
|
|
2002
2061
|
}
|
|
2003
2062
|
}
|
|
@@ -2105,11 +2164,15 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
2105
2164
|
import type { IStructuredTypeSchema } from "node-opcua-factory";
|
|
2106
2165
|
import { withCallback } from "thenify-ex";
|
|
2107
2166
|
|
|
2108
|
-
|
|
2167
|
+
// readValueAsync calls the unwrapped asyncRefresh directly: binding the thenified wrapper on
|
|
2168
|
+
// every sample cost more than the refresh itself
|
|
2169
|
+
const asyncRefreshImpl: (this: UAVariableImpl, oldestDate: PreciseClock, callback: CallbackT<DataValue>) => void =
|
|
2170
|
+
UAVariableImpl.prototype.asyncRefresh;
|
|
2171
|
+
UAVariableImpl.prototype.asyncRefresh = withCallback(asyncRefreshImpl);
|
|
2172
|
+
const wrappedAsyncRefresh = UAVariableImpl.prototype.asyncRefresh;
|
|
2109
2173
|
UAVariableImpl.prototype.writeValue = withCallback(UAVariableImpl.prototype.writeValue);
|
|
2110
2174
|
UAVariableImpl.prototype.writeAttribute = withCallback(UAVariableImpl.prototype.writeAttribute);
|
|
2111
2175
|
UAVariableImpl.prototype.historyRead = withCallback(UAVariableImpl.prototype.historyRead);
|
|
2112
|
-
UAVariableImpl.prototype.readValueAsync = withCallback(UAVariableImpl.prototype.readValueAsync);
|
|
2113
2176
|
|
|
2114
2177
|
export interface UAVariableImplExtArray {
|
|
2115
2178
|
$$variableType?: UAVariableType;
|
|
@@ -2230,26 +2293,24 @@ type f3<T, D, R> = (this: T, data: D, callback: (err: Error | null, r?: R) => vo
|
|
|
2230
2293
|
function turn_sync_to_async<T, D, R>(f: f1<T, D, R> | f2<T, D, R> | f3<T, D, R>, numberOfArgs: number): f3<T, D, R> {
|
|
2231
2294
|
if (f.length <= numberOfArgs) {
|
|
2232
2295
|
return function (this: T, data: D, callback: (err: Error | null, r?: R) => void) {
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
callback(err as Error);
|
|
2252
|
-
});
|
|
2296
|
+
const ff2 = f as f2<T, D, R>;
|
|
2297
|
+
let r: R | Promise<R>;
|
|
2298
|
+
try {
|
|
2299
|
+
r = ff2.call(this, data);
|
|
2300
|
+
} catch (err) {
|
|
2301
|
+
callback(err as Error);
|
|
2302
|
+
return;
|
|
2303
|
+
}
|
|
2304
|
+
if (r instanceof Promise) {
|
|
2305
|
+
r.then(
|
|
2306
|
+
(value) => callback(null, value),
|
|
2307
|
+
(err) => callback(err as Error)
|
|
2308
|
+
);
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2311
|
+
// a synchronous function answers at once: a Write of such Variables completes without waiting
|
|
2312
|
+
// a turn per value, under one permission cache for the whole request
|
|
2313
|
+
callback(null, r);
|
|
2253
2314
|
};
|
|
2254
2315
|
} else {
|
|
2255
2316
|
assert(f.length === numberOfArgs + 1);
|
|
@@ -2307,12 +2368,26 @@ function _Variable_bind_with_timestamped_get(
|
|
|
2307
2368
|
assert(!this._timestamped_get_func);
|
|
2308
2369
|
|
|
2309
2370
|
const async_refresh_func = (callback: (err: Error | null, dataValue?: DataValue) => void) => {
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2371
|
+
const reportError = (err: unknown) => {
|
|
2372
|
+
errorLog("asyncRefresh error: Variable is ", this.nodeId.toString(), this.browseName.toString());
|
|
2373
|
+
callback(err as Error);
|
|
2374
|
+
};
|
|
2375
|
+
// a getter that throws is reported by asyncRefresh, which calls this function
|
|
2376
|
+
const dataValueOrPromise = (this._timestamped_get_func as () => DataValue | Promise<DataValue>).call(this);
|
|
2377
|
+
if (isThenable(dataValueOrPromise)) {
|
|
2378
|
+
Promise.resolve(dataValueOrPromise)
|
|
2379
|
+
.then((dataValue) => callback(null, dataValue))
|
|
2380
|
+
.catch(reportError);
|
|
2381
|
+
return;
|
|
2382
|
+
}
|
|
2383
|
+
// the getter is synchronous (it always is for a Variable bound with a plain `get`):
|
|
2384
|
+
// answer at once. A promise here costs every Read of the Variable an allocation and
|
|
2385
|
+
// a turn of the microtask queue, for a value that is already known.
|
|
2386
|
+
try {
|
|
2387
|
+
callback(null, dataValueOrPromise);
|
|
2388
|
+
} catch (err) {
|
|
2389
|
+
reportError(err);
|
|
2390
|
+
}
|
|
2316
2391
|
};
|
|
2317
2392
|
const pThis = this as UAVariable;
|
|
2318
2393
|
if (options.timestamped_get.length === 0) {
|
|
@@ -2356,21 +2431,10 @@ function _Variable_bind_with_simple_get(this: UAVariableImpl, options: GetterOpt
|
|
|
2356
2431
|
const timestamped_get_func_from__Variable_bind_with_simple_get = () => {
|
|
2357
2432
|
const value: Variant | StatusCode = (this._get_func as GetFunc)();
|
|
2358
2433
|
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
" : the getter must return a Variant or a StatusCode" + "\nvalue_check.constructor.name ",
|
|
2364
|
-
value ? value.constructor.name : "null"
|
|
2365
|
-
);
|
|
2366
|
-
throw new Error(
|
|
2367
|
-
" bindVariable : the value getter function returns a invalid result ( expecting a Variant or a StatusCode !!!"
|
|
2368
|
-
);
|
|
2369
|
-
}
|
|
2370
|
-
if (is_StatusCode(value)) {
|
|
2371
|
-
return new DataValue({ statusCode: value as StatusCode });
|
|
2372
|
-
} else {
|
|
2373
|
-
if (!this.$dataValue?.statusCode.isGoodish() || !sameVariant(this.$dataValue.value, value as Variant)) {
|
|
2434
|
+
// a getter returns a Variant on every read but a failing one: test for it first, as
|
|
2435
|
+
// is_StatusCode ends in comparisons of constructor names
|
|
2436
|
+
if (value instanceof Variant) {
|
|
2437
|
+
if (!this.$dataValue?.statusCode.isGoodish() || !sameVariant(this.$dataValue.value, value)) {
|
|
2374
2438
|
// rebuilding artificially timestamps with current clock as they are not provided
|
|
2375
2439
|
// by the underlying getter function
|
|
2376
2440
|
const { timestamp: sourceTimestamp, picoseconds: sourcePicoseconds } = getCurrentClock();
|
|
@@ -2382,12 +2446,26 @@ function _Variable_bind_with_simple_get(this: UAVariableImpl, options: GetterOpt
|
|
|
2382
2446
|
}
|
|
2383
2447
|
return this.$dataValue;
|
|
2384
2448
|
}
|
|
2449
|
+
|
|
2450
|
+
/* c8 ignore next */
|
|
2451
|
+
if (!is_StatusCode(value)) {
|
|
2452
|
+
errorLog(
|
|
2453
|
+
chalk.red(" Bind variable error: "),
|
|
2454
|
+
" : the getter must return a Variant or a StatusCode" + "\nvalue_check.constructor.name ",
|
|
2455
|
+
value ? value.constructor.name : "null"
|
|
2456
|
+
);
|
|
2457
|
+
throw new Error(
|
|
2458
|
+
" bindVariable : the value getter function returns a invalid result ( expecting a Variant or a StatusCode !!!"
|
|
2459
|
+
);
|
|
2460
|
+
}
|
|
2461
|
+
return new DataValue({ statusCode: value as StatusCode });
|
|
2385
2462
|
};
|
|
2386
2463
|
|
|
2387
2464
|
_Variable_bind_with_timestamped_get.call(this, {
|
|
2388
2465
|
get: undefined,
|
|
2389
2466
|
timestamped_get: timestamped_get_func_from__Variable_bind_with_simple_get
|
|
2390
2467
|
});
|
|
2468
|
+
this._refreshFuncWrapsGetter = true;
|
|
2391
2469
|
}
|
|
2392
2470
|
|
|
2393
2471
|
type SimpleSetOptions = { set?: SetFunc; timestamped_set: undefined };
|
|
@@ -2440,8 +2518,18 @@ function _Variable_bind_with_timestamped_set(
|
|
|
2440
2518
|
"timestamped_set must have 2 parameters timestamped_set: function(dataValue,callback){} or one paramater timestamped_set: function(dataValue): Promise<StatusCode>{}"
|
|
2441
2519
|
);
|
|
2442
2520
|
assert(!options.set, "should not specify set when timestamped_set_func exists ");
|
|
2443
|
-
|
|
2521
|
+
// the callback form of a setter is a stateless wrapper around it (it takes the variable as
|
|
2522
|
+
// `this`): one per setter function, not one per variable. Every writable variable without a
|
|
2523
|
+
// setter of its own binds the same default, and a closure per variable was 110 bytes each
|
|
2524
|
+
let setter = callbackSetters.get(options.timestamped_set);
|
|
2525
|
+
if (!setter) {
|
|
2526
|
+
setter = convertToCallbackFunction1<StatusCode, DataValue, UAVariable>(options.timestamped_set);
|
|
2527
|
+
callbackSetters.set(options.timestamped_set, setter);
|
|
2528
|
+
}
|
|
2529
|
+
this._timestamped_set_func = setter;
|
|
2444
2530
|
}
|
|
2531
|
+
/** the callback form of each setter function met so far, see _Variable_bind_with_timestamped_set */
|
|
2532
|
+
const callbackSetters = new WeakMap<TimestampSetFunc, VariableDataValueSetterWithCallback>();
|
|
2445
2533
|
|
|
2446
2534
|
interface SetterOptions {
|
|
2447
2535
|
set?: SetFunc;
|
|
@@ -2528,9 +2616,9 @@ export class UAVariableImplT<T, DT extends DataType> extends UAVariableImpl impl
|
|
|
2528
2616
|
context: ISessionContext | null,
|
|
2529
2617
|
callback?: CallbackT<DataValueT<T, DT>>
|
|
2530
2618
|
): Promise<DataValueT<T, DT>> | undefined {
|
|
2531
|
-
// forward the arguments we were actually given
|
|
2532
|
-
//
|
|
2533
|
-
//
|
|
2619
|
+
// forward the arguments we were actually given: the base method picks the promise or the
|
|
2620
|
+
// callback form from the callback it receives (writeValue below, still thenified, picks
|
|
2621
|
+
// it from the argument count, where an explicit undefined callback would be passed on)
|
|
2534
2622
|
if (callback === undefined) {
|
|
2535
2623
|
return super.readValueAsync(context) as Promise<DataValueT<T, DT>>;
|
|
2536
2624
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-opcua-address-space",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.187.1",
|
|
5
5
|
"description": "pure nodejs OPCUA SDK - module address-space",
|
|
6
6
|
"main": "./dist/api/index.js",
|
|
7
7
|
"types": "./dist/api/index.d.ts",
|
|
@@ -23,48 +23,49 @@
|
|
|
23
23
|
"@types/semver": "7.8.0",
|
|
24
24
|
"chalk": "6.0.1",
|
|
25
25
|
"dequeue": "^1.0.5",
|
|
26
|
-
"node-opcua-address-space-base": "2.
|
|
26
|
+
"node-opcua-address-space-base": "2.187.0",
|
|
27
|
+
"node-opcua-address-space-store": "2.187.1",
|
|
27
28
|
"node-opcua-assert": "2.186.7",
|
|
28
|
-
"node-opcua-basic-types": "2.
|
|
29
|
-
"node-opcua-binary-stream": "2.
|
|
30
|
-
"node-opcua-client-dynamic-extension-object": "2.
|
|
29
|
+
"node-opcua-basic-types": "2.187.0",
|
|
30
|
+
"node-opcua-binary-stream": "2.187.0",
|
|
31
|
+
"node-opcua-client-dynamic-extension-object": "2.187.0",
|
|
31
32
|
"node-opcua-constants": "2.185.0",
|
|
32
|
-
"node-opcua-crypto": "6.
|
|
33
|
-
"node-opcua-data-access": "2.
|
|
34
|
-
"node-opcua-data-model": "2.
|
|
35
|
-
"node-opcua-data-value": "2.
|
|
36
|
-
"node-opcua-date-time": "2.
|
|
33
|
+
"node-opcua-crypto": "6.2.0",
|
|
34
|
+
"node-opcua-data-access": "2.187.0",
|
|
35
|
+
"node-opcua-data-model": "2.187.0",
|
|
36
|
+
"node-opcua-data-value": "2.187.0",
|
|
37
|
+
"node-opcua-date-time": "2.187.0",
|
|
37
38
|
"node-opcua-debug": "2.186.7",
|
|
38
|
-
"node-opcua-enum": "2.
|
|
39
|
-
"node-opcua-extension-object": "2.
|
|
40
|
-
"node-opcua-factory": "2.
|
|
41
|
-
"node-opcua-nodeid": "2.
|
|
42
|
-
"node-opcua-nodeset-ua": "2.
|
|
43
|
-
"node-opcua-numeric-range": "2.
|
|
39
|
+
"node-opcua-enum": "2.187.0",
|
|
40
|
+
"node-opcua-extension-object": "2.187.0",
|
|
41
|
+
"node-opcua-factory": "2.187.0",
|
|
42
|
+
"node-opcua-nodeid": "2.187.0",
|
|
43
|
+
"node-opcua-nodeset-ua": "2.187.0",
|
|
44
|
+
"node-opcua-numeric-range": "2.187.0",
|
|
44
45
|
"node-opcua-object-registry": "2.186.7",
|
|
45
|
-
"node-opcua-pseudo-session": "2.
|
|
46
|
-
"node-opcua-service-browse": "2.
|
|
47
|
-
"node-opcua-service-call": "2.
|
|
48
|
-
"node-opcua-service-history": "2.
|
|
49
|
-
"node-opcua-service-translate-browse-path": "2.
|
|
50
|
-
"node-opcua-service-write": "2.
|
|
51
|
-
"node-opcua-status-code": "2.
|
|
52
|
-
"node-opcua-types": "2.
|
|
53
|
-
"node-opcua-utils": "2.
|
|
54
|
-
"node-opcua-variant": "2.
|
|
55
|
-
"node-opcua-xml2json": "2.
|
|
46
|
+
"node-opcua-pseudo-session": "2.187.0",
|
|
47
|
+
"node-opcua-service-browse": "2.187.0",
|
|
48
|
+
"node-opcua-service-call": "2.187.0",
|
|
49
|
+
"node-opcua-service-history": "2.187.0",
|
|
50
|
+
"node-opcua-service-translate-browse-path": "2.187.0",
|
|
51
|
+
"node-opcua-service-write": "2.187.0",
|
|
52
|
+
"node-opcua-status-code": "2.187.0",
|
|
53
|
+
"node-opcua-types": "2.187.0",
|
|
54
|
+
"node-opcua-utils": "2.187.0",
|
|
55
|
+
"node-opcua-variant": "2.187.0",
|
|
56
|
+
"node-opcua-xml2json": "2.187.0",
|
|
56
57
|
"semver": "^7.8.5",
|
|
57
58
|
"thenify-ex": "4.4.0",
|
|
58
59
|
"xml-writer": "^1.7.0"
|
|
59
60
|
},
|
|
60
61
|
"devDependencies": {
|
|
61
62
|
"humanize": "0.0.9",
|
|
62
|
-
"node-opcua-benchmarker": "2.
|
|
63
|
+
"node-opcua-benchmarker": "2.187.0",
|
|
63
64
|
"node-opcua-leak-detector": "2.186.7",
|
|
64
|
-
"node-opcua-nodesets": "2.
|
|
65
|
-
"node-opcua-packet-analyzer": "2.
|
|
66
|
-
"node-opcua-schemas": "2.
|
|
67
|
-
"node-opcua-service-filter": "2.
|
|
65
|
+
"node-opcua-nodesets": "2.187.0",
|
|
66
|
+
"node-opcua-packet-analyzer": "2.187.0",
|
|
67
|
+
"node-opcua-schemas": "2.187.0",
|
|
68
|
+
"node-opcua-service-filter": "2.187.0",
|
|
68
69
|
"node-opcua-test-fixtures": "2.184.0",
|
|
69
70
|
"node-opcua-test-helpers": "2.184.0"
|
|
70
71
|
},
|
|
@@ -83,7 +84,7 @@
|
|
|
83
84
|
"internet of things"
|
|
84
85
|
],
|
|
85
86
|
"homepage": "http://node-opcua.github.io/",
|
|
86
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "706db482c6f77cd5c7c1f98f9f7cf1c5841a0161",
|
|
87
88
|
"files": [
|
|
88
89
|
"dist",
|
|
89
90
|
"distHelpers",
|