node-opcua-address-space 2.164.2 → 2.165.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/dist/source/loader/load_nodeset2.d.ts +6 -4
- package/dist/source/loader/load_nodeset2.js +100 -59
- package/dist/source/loader/load_nodeset2.js.map +1 -1
- package/dist/source/session_context.d.ts +24 -0
- package/dist/source/session_context.js +34 -0
- package/dist/source/session_context.js.map +1 -1
- package/dist/src/address_space.d.ts +13 -1
- package/dist/src/address_space.js +20 -0
- package/dist/src/address_space.js.map +1 -1
- package/dist/src/address_space_private.d.ts +2 -1
- package/dist/src/base_node_impl.d.ts +17 -17
- package/dist/src/base_node_impl.js +78 -58
- package/dist/src/base_node_impl.js.map +1 -1
- package/dist/src/ua_data_type_impl.d.ts +7 -11
- package/dist/src/ua_data_type_impl.js +30 -26
- package/dist/src/ua_data_type_impl.js.map +1 -1
- package/dist/src/ua_method_impl.js +71 -28
- package/dist/src/ua_method_impl.js.map +1 -1
- package/dist/src/ua_variable_impl_ext_obj.js +54 -2
- package/dist/src/ua_variable_impl_ext_obj.js.map +1 -1
- package/dist/tsconfig_base.tsbuildinfo +1 -1
- package/package.json +34 -34
- package/source/loader/load_nodeset2.ts +118 -95
- package/source/session_context.ts +52 -2
- package/src/address_space.ts +24 -1
- package/src/address_space_private.ts +4 -1
- package/src/base_node_impl.ts +142 -135
- package/src/ua_data_type_impl.ts +52 -81
- package/src/ua_method_impl.ts +82 -39
- package/src/ua_variable_impl_ext_obj.ts +46 -2
package/src/ua_data_type_impl.ts
CHANGED
|
@@ -2,63 +2,41 @@
|
|
|
2
2
|
* @module node-opcua-address-space
|
|
3
3
|
*/
|
|
4
4
|
import chalk from "chalk";
|
|
5
|
-
|
|
5
|
+
import type { BaseNode, ISessionContext, UADataType, UAObject, UAVariable } from "node-opcua-address-space-base";
|
|
6
6
|
import { assert } from "node-opcua-assert";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
7
|
+
import { coerceInt64, coerceInt64toInt32, type Int64 } from "node-opcua-basic-types";
|
|
8
|
+
import { DataTypeIds } from "node-opcua-constants";
|
|
9
|
+
import { AttributeIds, type LocalizedText, NodeClass, type QualifiedNameLike } from "node-opcua-data-model";
|
|
10
|
+
import { DataValue, type DataValueLike } from "node-opcua-data-value";
|
|
11
|
+
import { checkDebugFlag, make_debugLog } from "node-opcua-debug";
|
|
12
|
+
import { ExpandedNodeId, NodeId, resolveNodeId } from "node-opcua-nodeid";
|
|
11
13
|
import { NumericRange } from "node-opcua-numeric-range";
|
|
12
14
|
import { StatusCodes } from "node-opcua-status-code";
|
|
13
15
|
import {
|
|
14
|
-
DataTypeDefinition,
|
|
16
|
+
type DataTypeDefinition,
|
|
15
17
|
EnumDefinition,
|
|
16
|
-
EnumFieldOptions,
|
|
18
|
+
type EnumFieldOptions,
|
|
19
|
+
type EnumValueType,
|
|
17
20
|
StructureDefinition,
|
|
18
|
-
StructureFieldOptions,
|
|
21
|
+
type StructureFieldOptions,
|
|
19
22
|
StructureType
|
|
20
23
|
} from "node-opcua-types";
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
} from "node-opcua-variant";
|
|
24
|
-
import {
|
|
25
|
-
UAObject,
|
|
26
|
-
ISessionContext,
|
|
27
|
-
UADataType,
|
|
28
|
-
UAVariable,
|
|
29
|
-
BaseNode
|
|
30
|
-
} from "node-opcua-address-space-base";
|
|
31
|
-
import {
|
|
32
|
-
DataTypeIds
|
|
33
|
-
} from "node-opcua-constants";
|
|
34
|
-
import {
|
|
35
|
-
Int64,
|
|
36
|
-
coerceInt64,
|
|
37
|
-
coerceInt64toInt32
|
|
38
|
-
} from "node-opcua-basic-types";
|
|
24
|
+
import { DataType } from "node-opcua-variant";
|
|
25
|
+
import type { ExtensionObjectConstructorFuncWithSchema } from "../source/interfaces/extension_object_constructor";
|
|
39
26
|
import { SessionContext } from "../source/session_context";
|
|
40
|
-
import {
|
|
41
|
-
import { BaseNodeImpl, InternalBaseNodeOptions } from "./base_node_impl";
|
|
27
|
+
import { BaseNodeImpl, type InternalBaseNodeOptions } from "./base_node_impl";
|
|
42
28
|
import {
|
|
29
|
+
BaseNode_getCache,
|
|
43
30
|
BaseNode_References_toString,
|
|
44
31
|
BaseNode_toString,
|
|
45
32
|
ToStringBuilder,
|
|
46
|
-
ToStringOption
|
|
33
|
+
type ToStringOption
|
|
47
34
|
} from "./base_node_private";
|
|
48
|
-
import { construct_isSubtypeOf } from "./tool_isSubtypeOf";
|
|
49
|
-
import { get_subtypeOf } from "./tool_isSubtypeOf";
|
|
50
|
-
import { get_subtypeOfObj } from "./tool_isSubtypeOf";
|
|
51
|
-
import { BaseNode_getCache } from "./base_node_private";
|
|
52
|
-
import { checkDebugFlag, make_debugLog } from "node-opcua-debug";
|
|
53
|
-
|
|
35
|
+
import { construct_isSubtypeOf, get_subtypeOf, get_subtypeOfObj } from "./tool_isSubtypeOf";
|
|
54
36
|
|
|
55
37
|
const debugLog = make_debugLog("DATA_TYPE");
|
|
56
38
|
const doDebug = checkDebugFlag("DATA_TYPE");
|
|
57
39
|
|
|
58
|
-
export interface UADataTypeImpl {
|
|
59
|
-
_extensionObjectConstructor: ExtensionObjectConstructorFuncWithSchema;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
40
|
|
|
63
41
|
export interface StructureFieldOptionsEx extends StructureFieldOptions {
|
|
64
42
|
allowSubTypes: boolean;
|
|
@@ -80,6 +58,7 @@ export interface UADataTypeOptions extends InternalBaseNodeOptions {
|
|
|
80
58
|
}
|
|
81
59
|
|
|
82
60
|
export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
|
|
61
|
+
public _extensionObjectConstructor!: ExtensionObjectConstructorFuncWithSchema;
|
|
83
62
|
public readonly nodeClass = NodeClass.DataType;
|
|
84
63
|
public readonly definitionName: string = "";
|
|
85
64
|
public readonly symbolicName: string;
|
|
@@ -100,7 +79,7 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
|
|
|
100
79
|
}
|
|
101
80
|
|
|
102
81
|
public get subtypeOfObj(): UADataType | null {
|
|
103
|
-
return get_subtypeOfObj.call(this) as
|
|
82
|
+
return get_subtypeOfObj.call(this) as unknown as UADataType;
|
|
104
83
|
}
|
|
105
84
|
|
|
106
85
|
/** @deprecated */
|
|
@@ -108,10 +87,8 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
|
|
|
108
87
|
public isSubtypeOf = construct_isSubtypeOf<UADataType>(UADataTypeImpl);
|
|
109
88
|
|
|
110
89
|
public readonly isAbstract: boolean;
|
|
111
|
-
|
|
112
|
-
private
|
|
113
|
-
private enumStrings?: any;
|
|
114
|
-
private enumValues?: any;
|
|
90
|
+
private enumStrings?: UAVariable;
|
|
91
|
+
private enumValues?: UAVariable;
|
|
115
92
|
private $partialDefinition?: StructureFieldOptionsEx[] | EnumFieldOptions[];
|
|
116
93
|
private $fullDefinition?: StructureDefinition | EnumDefinition;
|
|
117
94
|
|
|
@@ -119,10 +96,9 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
|
|
|
119
96
|
super(options);
|
|
120
97
|
if (options.partialDefinition) {
|
|
121
98
|
this.$partialDefinition = options.partialDefinition;
|
|
122
|
-
this.$isUnion = options.isUnion;
|
|
123
99
|
}
|
|
124
100
|
this.isAbstract = options.isAbstract === undefined || options.isAbstract === null ? false : options.isAbstract;
|
|
125
|
-
this.symbolicName = options.symbolicName || this.browseName.name
|
|
101
|
+
this.symbolicName = options.symbolicName || this.browseName.name || "";
|
|
126
102
|
}
|
|
127
103
|
|
|
128
104
|
public get basicDataType(): DataType {
|
|
@@ -165,10 +141,10 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
|
|
|
165
141
|
public getEncodingDefinition(encoding_name: string): string | null {
|
|
166
142
|
const encodingNode = this.getEncodingNode(encoding_name);
|
|
167
143
|
if (!encodingNode) {
|
|
168
|
-
throw new Error(
|
|
144
|
+
throw new Error(`Cannot find Encoding for ${encoding_name}`);
|
|
169
145
|
}
|
|
170
146
|
const indexRange = new NumericRange();
|
|
171
|
-
const descriptionNodeRef = encodingNode.findReferences("HasDescription")[0]
|
|
147
|
+
const descriptionNodeRef = encodingNode.findReferences("HasDescription")[0];
|
|
172
148
|
const descriptionNode = this.addressSpace.findNode(descriptionNodeRef.nodeId) as UAVariable;
|
|
173
149
|
if (!descriptionNode) {
|
|
174
150
|
return null;
|
|
@@ -180,7 +156,7 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
|
|
|
180
156
|
public getEncodingNode(encoding_name: string): UAObject | null {
|
|
181
157
|
const _cache = BaseNode_getCache(this);
|
|
182
158
|
_cache._encoding = _cache._encoding || new Map();
|
|
183
|
-
const key = encoding_name
|
|
159
|
+
const key = `${encoding_name}Node`;
|
|
184
160
|
if (!_cache._encoding.has(key)) {
|
|
185
161
|
assert(encoding_name === "Default Binary" || encoding_name === "Default XML" || encoding_name === "Default JSON");
|
|
186
162
|
// could be binary or xml
|
|
@@ -188,11 +164,11 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
|
|
|
188
164
|
const addressSpace = this.addressSpace;
|
|
189
165
|
const encoding = refs
|
|
190
166
|
.map((ref) => addressSpace.findNode(ref.nodeId))
|
|
191
|
-
.filter((obj:
|
|
192
|
-
.filter((obj
|
|
167
|
+
.filter((obj): obj is BaseNode => obj !== null)
|
|
168
|
+
.filter((obj) => obj.browseName.toString() === encoding_name);
|
|
193
169
|
const node = encoding.length === 0 ? null : (encoding[0] as UAObject);
|
|
194
170
|
_cache._encoding.set(key, node);
|
|
195
|
-
return node
|
|
171
|
+
return node;
|
|
196
172
|
}
|
|
197
173
|
return _cache._encoding.get(key) || null;
|
|
198
174
|
}
|
|
@@ -247,7 +223,7 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
|
|
|
247
223
|
if (this.enumStrings) {
|
|
248
224
|
const enumStrings = this.enumStrings.readValue().value.value;
|
|
249
225
|
assert(Array.isArray(enumStrings));
|
|
250
|
-
definition = enumStrings.map((e:
|
|
226
|
+
definition = enumStrings.map((e: LocalizedText, index: number) => {
|
|
251
227
|
return {
|
|
252
228
|
name: e.text,
|
|
253
229
|
value: index
|
|
@@ -257,7 +233,7 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
|
|
|
257
233
|
assert(this.enumValues, "must have a enumValues property");
|
|
258
234
|
const enumValues = this.enumValues.readValue().value.value;
|
|
259
235
|
assert(Array.isArray(enumValues));
|
|
260
|
-
definition = enumValues.map((e:
|
|
236
|
+
definition = enumValues.map((e: EnumValueType) => {
|
|
261
237
|
return {
|
|
262
238
|
name: e.displayName.text,
|
|
263
239
|
value: coerceInt64toInt32(e.value)
|
|
@@ -317,9 +293,8 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
|
|
|
317
293
|
|
|
318
294
|
// https://reference.opcfoundation.org/v105/Core/docs/Part3/8.49/#Table34
|
|
319
295
|
if (isStructure) {
|
|
320
|
-
|
|
321
296
|
let dataTypeNode: UADataTypeImpl | null = this;
|
|
322
|
-
const allPartialDefinitions:
|
|
297
|
+
const allPartialDefinitions: StructureFieldOptionsEx[][] = [];
|
|
323
298
|
while (dataTypeNode && !isRootDataType(dataTypeNode)) {
|
|
324
299
|
if (dataTypeNode.$partialDefinition) {
|
|
325
300
|
allPartialDefinitions.push(dataTypeNode.$partialDefinition as StructureFieldOptionsEx[]);
|
|
@@ -337,14 +312,7 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
|
|
|
337
312
|
|
|
338
313
|
const defaultEncodingId = this.binaryEncodingNodeId || this.xmlEncodingNodeId || new NodeId();
|
|
339
314
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
this.$fullDefinition = makeStructureDefinition(
|
|
343
|
-
basicDataType,
|
|
344
|
-
defaultEncodingId,
|
|
345
|
-
definitionFields,
|
|
346
|
-
isUnion
|
|
347
|
-
);
|
|
315
|
+
this.$fullDefinition = makeStructureDefinition(basicDataType, defaultEncodingId, definitionFields, isUnion);
|
|
348
316
|
} else if (isEnumeration) {
|
|
349
317
|
const allPartialDefinitions: StructureFieldOptions[][] = [];
|
|
350
318
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
@@ -363,7 +331,7 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
|
|
|
363
331
|
this.$fullDefinition = makeEnumDefinition(definitionFields);
|
|
364
332
|
}
|
|
365
333
|
|
|
366
|
-
return this.$fullDefinition
|
|
334
|
+
return this.$fullDefinition as DataTypeDefinition;
|
|
367
335
|
}
|
|
368
336
|
|
|
369
337
|
public getDefinition(): DataTypeDefinition {
|
|
@@ -395,14 +363,14 @@ function dataTypeDefinition_toString(this: UADataTypeImpl, options: ToStringOpti
|
|
|
395
363
|
const output = definition.toString();
|
|
396
364
|
options.add(options.padding + chalk.yellow(" Definition : "));
|
|
397
365
|
for (const str of output.split("\n")) {
|
|
398
|
-
options.add(options.padding + chalk.yellow(
|
|
366
|
+
options.add(options.padding + chalk.yellow(` : ${str}`));
|
|
399
367
|
}
|
|
400
368
|
}
|
|
401
369
|
|
|
402
370
|
export function DataType_toString(this: UADataTypeImpl, options: ToStringOption): void {
|
|
403
371
|
BaseNode_toString.call(this, options);
|
|
404
|
-
options.add(options.padding + chalk.yellow(
|
|
405
|
-
options.add(options.padding + chalk.yellow(
|
|
372
|
+
options.add(options.padding + chalk.yellow(` isAbstract : ${this.isAbstract}`));
|
|
373
|
+
options.add(options.padding + chalk.yellow(` definitionName : ${this.definitionName}`));
|
|
406
374
|
|
|
407
375
|
options.add(
|
|
408
376
|
options.padding +
|
|
@@ -450,7 +418,6 @@ function makeStructureDefinition(
|
|
|
450
418
|
fields: StructureFieldOptionsEx[],
|
|
451
419
|
isUnion: boolean
|
|
452
420
|
): StructureDefinition {
|
|
453
|
-
|
|
454
421
|
const hasSubtypedFields = fields.filter((field) => field.allowSubTypes).length > 0;
|
|
455
422
|
|
|
456
423
|
if (hasSubtypedFields && doDebug) {
|
|
@@ -463,18 +430,15 @@ function makeStructureDefinition(
|
|
|
463
430
|
}
|
|
464
431
|
const hasOptionalFields = fields.filter((field) => field.isOptional).length > 0;
|
|
465
432
|
|
|
466
|
-
|
|
467
|
-
|
|
468
433
|
const structureType = isUnion
|
|
469
|
-
?
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
)
|
|
434
|
+
? hasSubtypedFields
|
|
435
|
+
? StructureType.UnionWithSubtypedValues
|
|
436
|
+
: StructureType.Union
|
|
473
437
|
: hasOptionalFields
|
|
474
438
|
? StructureType.StructureWithOptionalFields
|
|
475
|
-
:
|
|
476
|
-
|
|
477
|
-
|
|
439
|
+
: hasSubtypedFields
|
|
440
|
+
? StructureType.StructureWithSubtypedValues
|
|
441
|
+
: StructureType.Structure;
|
|
478
442
|
|
|
479
443
|
// note: https://reference.opcfoundation.org/Core/Part3/v105/docs/8.51
|
|
480
444
|
// field.isOptional has a special behavior depending on the structure type
|
|
@@ -490,8 +454,7 @@ function makeStructureDefinition(
|
|
|
490
454
|
// indicate if the data type field allows subtyping.Subtyping is allowed when set to TRUE.
|
|
491
455
|
|
|
492
456
|
const isUnionOrStructureWithSubtypedValues =
|
|
493
|
-
structureType === StructureType.UnionWithSubtypedValues ||
|
|
494
|
-
structureType === StructureType.StructureWithSubtypedValues;
|
|
457
|
+
structureType === StructureType.UnionWithSubtypedValues || structureType === StructureType.StructureWithSubtypedValues;
|
|
495
458
|
|
|
496
459
|
if (isUnionOrStructureWithSubtypedValues) {
|
|
497
460
|
for (const field of fields) {
|
|
@@ -503,10 +466,18 @@ function makeStructureDefinition(
|
|
|
503
466
|
}
|
|
504
467
|
}
|
|
505
468
|
|
|
469
|
+
// Normalize: default missing field dataType to BaseDataType (i=24)
|
|
470
|
+
// per OPC UA spec, a missing DataType should reference BaseDataType,
|
|
471
|
+
// not Null NodeId (i=0)
|
|
472
|
+
const baseDataTypeNodeId = resolveNodeId(DataTypeIds.BaseDataType);
|
|
473
|
+
|
|
506
474
|
const sd = new StructureDefinition({
|
|
507
475
|
baseDataType,
|
|
508
476
|
defaultEncodingId,
|
|
509
|
-
fields
|
|
477
|
+
fields: fields.map((f) => ({
|
|
478
|
+
...f,
|
|
479
|
+
dataType: f.dataType ?? baseDataTypeNodeId
|
|
480
|
+
})),
|
|
510
481
|
structureType
|
|
511
482
|
});
|
|
512
483
|
return sd;
|
package/src/ua_method_impl.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module node-opcua-address-space
|
|
3
3
|
*/
|
|
4
|
-
import { callbackify
|
|
4
|
+
import { callbackify } from "util";
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
import { assert } from "node-opcua-assert";
|
|
7
7
|
|
|
@@ -12,7 +12,7 @@ import { make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug"
|
|
|
12
12
|
import { NodeId } from "node-opcua-nodeid";
|
|
13
13
|
import { NumericRange } from "node-opcua-numeric-range";
|
|
14
14
|
import { Argument } from "node-opcua-service-call";
|
|
15
|
-
import { CallbackT, StatusCodes } from "node-opcua-status-code";
|
|
15
|
+
import { CallbackT, StatusCode, StatusCodes } from "node-opcua-status-code";
|
|
16
16
|
import { CallMethodResultOptions, PermissionType } from "node-opcua-types";
|
|
17
17
|
import { Variant } from "node-opcua-variant";
|
|
18
18
|
import { DataType, VariantLike } from "node-opcua-variant";
|
|
@@ -209,47 +209,90 @@ export class UAMethodImpl extends BaseNodeImpl implements UAMethod {
|
|
|
209
209
|
|
|
210
210
|
context.object = object;
|
|
211
211
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
212
|
+
// ── method call interceptors ──
|
|
213
|
+
const addressSpace = this.addressSpace as AddressSpacePrivate;
|
|
214
|
+
const interceptors = addressSpace._methodCallInterceptors;
|
|
215
|
+
const self = this;
|
|
216
|
+
const coercedInputArguments = inputArguments as Variant[];
|
|
217
|
+
const callObject = object;
|
|
218
|
+
|
|
219
|
+
const runInterceptorsAndExecute = async () => {
|
|
220
|
+
// Run interceptors sequentially
|
|
221
|
+
for (const interceptor of interceptors) {
|
|
222
|
+
let status: StatusCode;
|
|
223
|
+
try {
|
|
224
|
+
status = await interceptor(context, callObject, self, coercedInputArguments);
|
|
225
|
+
} catch (err) {
|
|
226
|
+
if (err instanceof Error) {
|
|
227
|
+
warningLog(chalk.red("ERR in method interceptor"), err.message);
|
|
221
228
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
callMethodResult
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
// const outputArgsDef = this.getOutputArguments();
|
|
237
|
-
|
|
238
|
-
// xx assert(outputArgsDef.length === callMethodResponse.outputArguments.length,
|
|
239
|
-
// xx "_asyncExecutionFunction did not provide the expected number of output arguments");
|
|
240
|
-
// to be continued ...
|
|
229
|
+
const callMethodResponse = { statusCode: StatusCodes.BadInternalError };
|
|
230
|
+
callback!(err as Error, callMethodResponse);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (status !== StatusCodes.Good) {
|
|
234
|
+
const callMethodResult: CallMethodResultOptions = {
|
|
235
|
+
statusCode: status,
|
|
236
|
+
outputArguments: [],
|
|
237
|
+
inputArgumentResults: coercedInputArguments?.map(() => status) || [],
|
|
238
|
+
inputArgumentDiagnosticInfos
|
|
239
|
+
};
|
|
240
|
+
return callback!(null, callMethodResult);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
241
243
|
|
|
242
|
-
|
|
244
|
+
// All interceptors passed — execute the method body
|
|
245
|
+
try {
|
|
246
|
+
self._asyncExecutionFunction!.call(
|
|
247
|
+
self as UAMethodImpl,
|
|
248
|
+
coercedInputArguments,
|
|
249
|
+
context,
|
|
250
|
+
(err: Error | null, callMethodResult?: CallMethodResultOptions) => {
|
|
251
|
+
if (err) {
|
|
252
|
+
debugLog(err.message);
|
|
253
|
+
debugLog(err);
|
|
254
|
+
}
|
|
255
|
+
callMethodResult = callMethodResult || {};
|
|
256
|
+
|
|
257
|
+
callMethodResult.statusCode = callMethodResult.statusCode || StatusCodes.Good;
|
|
258
|
+
callMethodResult.outputArguments = callMethodResult.outputArguments || [];
|
|
259
|
+
|
|
260
|
+
callMethodResult.inputArgumentResults =
|
|
261
|
+
callMethodResult.inputArgumentResults?.length === coercedInputArguments?.length
|
|
262
|
+
? callMethodResult.inputArgumentResults
|
|
263
|
+
: coercedInputArguments?.map(() => StatusCodes.Good);
|
|
264
|
+
callMethodResult.inputArgumentDiagnosticInfos =
|
|
265
|
+
callMethodResult.inputArgumentDiagnosticInfos || inputArgumentDiagnosticInfos;
|
|
266
|
+
|
|
267
|
+
// ── afterCall event ──
|
|
268
|
+
try {
|
|
269
|
+
self.emit("afterCall", context, coercedInputArguments, callMethodResult);
|
|
270
|
+
} catch (afterCallErr) {
|
|
271
|
+
if (afterCallErr instanceof Error) {
|
|
272
|
+
warningLog(chalk.red("ERR in afterCall listener"), afterCallErr.message);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
callback!(err, callMethodResult);
|
|
277
|
+
}
|
|
278
|
+
);
|
|
279
|
+
} catch (err) {
|
|
280
|
+
if (err instanceof Error) {
|
|
281
|
+
warningLog(chalk.red("ERR in method handler"), err.message);
|
|
282
|
+
warningLog(err.stack);
|
|
243
283
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
284
|
+
const callMethodResponse = { statusCode: StatusCodes.BadInternalError };
|
|
285
|
+
callback!(err as Error, callMethodResponse);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
runInterceptorsAndExecute().catch((err) => {
|
|
290
|
+
if (err instanceof Error) {
|
|
291
|
+
warningLog(chalk.red("ERR in method interceptor"), err.message);
|
|
249
292
|
}
|
|
250
293
|
const callMethodResponse = { statusCode: StatusCodes.BadInternalError };
|
|
251
|
-
callback(err as Error, callMethodResponse);
|
|
252
|
-
}
|
|
294
|
+
callback!(err as Error, callMethodResponse);
|
|
295
|
+
});
|
|
253
296
|
}
|
|
254
297
|
|
|
255
298
|
public clone(options: CloneOptions, optionalFilter?: CloneFilter, extraInfo?: CloneExtraInfo): UAMethod {
|
|
@@ -272,7 +315,7 @@ export class UAMethodImpl extends BaseNodeImpl implements UAMethod {
|
|
|
272
315
|
UAMethodImpl,
|
|
273
316
|
options,
|
|
274
317
|
optionalFilter || defaultCloneFilter,
|
|
275
|
-
extraInfo
|
|
318
|
+
extraInfo
|
|
276
319
|
) as UAMethodImpl;
|
|
277
320
|
|
|
278
321
|
clonedMethod._asyncExecutionFunction = this._asyncExecutionFunction;
|
|
@@ -153,13 +153,57 @@ export function setExtensionObjectPartialValue(node: UAVariableImpl, partialObje
|
|
|
153
153
|
|
|
154
154
|
const extensionObject = node.$extensionObject;
|
|
155
155
|
if (!extensionObject) {
|
|
156
|
-
throw new Error(
|
|
156
|
+
throw new Error(`setExtensionObjectValue node has no extension object ${node.browseName.toString()}`);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Returns true if the value is a structure-like object that should
|
|
160
|
+
* be recursed into during a partial update.
|
|
161
|
+
*
|
|
162
|
+
* For the existing extension object (extObject[prop]):
|
|
163
|
+
* - Proxied sub-extension objects → recurse (they have $isProxy)
|
|
164
|
+
* For the incoming partial object (partialObject1[prop]):
|
|
165
|
+
* - Plain objects {} → recurse (from constructExtensionObject or literals)
|
|
166
|
+
*
|
|
167
|
+
* Everything else (Date, Buffer, NodeId, QualifiedName, LocalizedText,
|
|
168
|
+
* DiagnosticInfo, arrays, etc.) is a terminal value and should be
|
|
169
|
+
* assigned directly — even if it has a `schema` property.
|
|
170
|
+
*/
|
|
171
|
+
function _shouldRecurseIntoExisting(value: any): boolean {
|
|
172
|
+
if (value === null || value === undefined) return false;
|
|
173
|
+
if (typeof value !== "object") return false;
|
|
174
|
+
if (Array.isArray(value)) return false;
|
|
175
|
+
// proxied sub-structures: installed by bindExtensionObject for nested structs
|
|
176
|
+
if (isProxy(value)) return true;
|
|
177
|
+
// plain objects (unlikely on the existing side but safe fallback)
|
|
178
|
+
const proto = Object.getPrototypeOf(value);
|
|
179
|
+
if (proto === Object.prototype || proto === null) return true;
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function _shouldRecurseIntoNew(value: any): boolean {
|
|
184
|
+
if (value === null || value === undefined) return false;
|
|
185
|
+
if (typeof value !== "object") return false;
|
|
186
|
+
if (Array.isArray(value)) return false;
|
|
187
|
+
// plain objects: partial update literals like { field1: v1 }
|
|
188
|
+
const proto = Object.getPrototypeOf(value);
|
|
189
|
+
if (proto === Object.prototype || proto === null) return true;
|
|
190
|
+
// extension objects from constructExtensionObject have a schema
|
|
191
|
+
// but so do QualifiedName/LocalizedText — we disambiguate by
|
|
192
|
+
// checking the *existing* side with isProxy, not here.
|
|
193
|
+
// Instead, only recurse if it's a schema'd type AND has
|
|
194
|
+
// Extension-Object-like characteristics (constructor with schema.name)
|
|
195
|
+
if (value.schema !== undefined && value.constructor?.name !== "QualifiedName"
|
|
196
|
+
&& value.constructor?.name !== "LocalizedText"
|
|
197
|
+
&& value.constructor?.name !== "DiagnosticInfo") {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
return false;
|
|
157
201
|
}
|
|
158
202
|
|
|
159
203
|
function _update_extension_object(extObject: any, partialObject1: any) {
|
|
160
204
|
const keys = Object.keys(partialObject1);
|
|
161
205
|
for (const prop of keys) {
|
|
162
|
-
if (extObject[prop]
|
|
206
|
+
if (_shouldRecurseIntoExisting(extObject[prop]) && _shouldRecurseIntoNew(partialObject1[prop])) {
|
|
163
207
|
_update_extension_object(extObject[prop], partialObject1[prop]);
|
|
164
208
|
} else {
|
|
165
209
|
if (isProxy(extObject)) {
|