node-opcua-server 2.105.1 → 2.107.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/addressSpace_accessor.d.ts +29 -0
- package/dist/addressSpace_accessor.js +346 -0
- package/dist/addressSpace_accessor.js.map +1 -0
- package/dist/base_server.js +1 -1
- package/dist/base_server.js.map +1 -1
- package/dist/i_address_space_accessor.d.ts +11 -0
- package/dist/i_address_space_accessor.js +3 -0
- package/dist/i_address_space_accessor.js.map +1 -0
- package/dist/monitored_item.js +1 -1
- package/dist/monitored_item.js.map +1 -1
- package/dist/opcua_server.d.ts +1 -1
- package/dist/opcua_server.js +46 -46
- package/dist/opcua_server.js.map +1 -1
- package/dist/register_server_manager.js +1 -1
- package/dist/register_server_manager.js.map +1 -1
- package/dist/server_engine.d.ts +15 -103
- package/dist/server_engine.js +36 -370
- package/dist/server_engine.js.map +1 -1
- package/dist/server_publish_engine.js +1 -1
- package/dist/server_publish_engine.js.map +1 -1
- package/dist/server_session.js +1 -1
- package/dist/server_session.js.map +1 -1
- package/dist/server_subscription.d.ts +1 -1
- package/dist/server_subscription.js +7 -5
- package/dist/server_subscription.js.map +1 -1
- package/package.json +26 -26
- package/source/addressSpace_accessor.ts +393 -0
- package/source/i_address_space_accessor.ts +12 -0
- package/source/monitored_item.ts +2 -2
- package/source/opcua_server.ts +70 -83
- package/source/register_server_manager.ts +0 -1
- package/source/server_engine.ts +40 -512
- package/source/server_subscription.ts +2 -2
package/source/server_engine.ts
CHANGED
|
@@ -10,14 +10,11 @@ import { BinaryStream } from "node-opcua-binary-stream";
|
|
|
10
10
|
import {
|
|
11
11
|
addElement,
|
|
12
12
|
AddressSpace,
|
|
13
|
-
BaseNode,
|
|
14
13
|
bindExtObjArrayNode,
|
|
15
|
-
ensureDatatypeExtractedWithCallback,
|
|
16
14
|
ensureObjectIsSecure,
|
|
17
15
|
MethodFunctor,
|
|
18
16
|
removeElement,
|
|
19
17
|
SessionContext,
|
|
20
|
-
UADataType,
|
|
21
18
|
UADynamicVariableArray,
|
|
22
19
|
UAMethod,
|
|
23
20
|
UAObject,
|
|
@@ -28,13 +25,9 @@ import {
|
|
|
28
25
|
BindVariableOptions,
|
|
29
26
|
ISessionContext,
|
|
30
27
|
DTServerStatus,
|
|
31
|
-
|
|
32
|
-
ContinuationData,
|
|
33
|
-
IServerBase,
|
|
34
|
-
UARole
|
|
35
|
-
} from "node-opcua-address-space";
|
|
28
|
+
IServerBase} from "node-opcua-address-space";
|
|
36
29
|
import { generateAddressSpace } from "node-opcua-address-space/nodeJS";
|
|
37
|
-
import { DataValue
|
|
30
|
+
import { DataValue } from "node-opcua-data-value";
|
|
38
31
|
import {
|
|
39
32
|
ServerDiagnosticsSummaryDataType,
|
|
40
33
|
ServerState,
|
|
@@ -43,40 +36,30 @@ import {
|
|
|
43
36
|
} from "node-opcua-common";
|
|
44
37
|
import {
|
|
45
38
|
AttributeIds,
|
|
46
|
-
BrowseDirection,
|
|
47
39
|
coerceLocalizedText,
|
|
48
40
|
LocalizedTextLike,
|
|
49
41
|
makeAccessLevelFlag,
|
|
50
|
-
NodeClass
|
|
51
|
-
QualifiedName
|
|
52
|
-
} from "node-opcua-data-model";
|
|
42
|
+
NodeClass} from "node-opcua-data-model";
|
|
53
43
|
import { coerceNodeId, makeNodeId, NodeId, NodeIdLike, NodeIdType, resolveNodeId } from "node-opcua-nodeid";
|
|
54
44
|
import { BrowseResult } from "node-opcua-service-browse";
|
|
55
|
-
import { ReadRequest, TimestampsToReturn } from "node-opcua-service-read";
|
|
56
45
|
import { UInt32 } from "node-opcua-basic-types";
|
|
57
46
|
import { CreateSubscriptionRequestLike } from "node-opcua-client";
|
|
58
|
-
import { ExtraDataTypeManager } from "node-opcua-client-dynamic-extension-object";
|
|
59
47
|
import { DataTypeIds, MethodIds, ObjectIds, VariableIds } from "node-opcua-constants";
|
|
60
48
|
import { getCurrentClock, minOPCUADate } from "node-opcua-date-time";
|
|
61
49
|
import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog, traceFromThisProjectOnly } from "node-opcua-debug";
|
|
62
50
|
import { nodesets } from "node-opcua-nodesets";
|
|
63
|
-
import { NumericRange } from "node-opcua-numeric-range";
|
|
64
51
|
import { ObjectRegistry } from "node-opcua-object-registry";
|
|
65
52
|
import { CallMethodResult } from "node-opcua-service-call";
|
|
66
53
|
import { TransferResult } from "node-opcua-service-subscription";
|
|
67
54
|
import { ApplicationDescription } from "node-opcua-service-endpoints";
|
|
68
|
-
import {
|
|
69
|
-
import { StatusCode, StatusCodes, CallbackT
|
|
55
|
+
import { HistoryReadRequest, HistoryReadResult, HistoryReadValueId } from "node-opcua-service-history";
|
|
56
|
+
import { StatusCode, StatusCodes, CallbackT } from "node-opcua-status-code";
|
|
70
57
|
import {
|
|
71
58
|
BrowseDescription,
|
|
72
59
|
BrowsePath,
|
|
73
60
|
BrowsePathResult,
|
|
74
61
|
BuildInfo,
|
|
75
62
|
BuildInfoOptions,
|
|
76
|
-
ReadAtTimeDetails,
|
|
77
|
-
ReadEventDetails,
|
|
78
|
-
ReadProcessedDetails,
|
|
79
|
-
ReadRawModifiedDetails,
|
|
80
63
|
SessionDiagnosticsDataType,
|
|
81
64
|
SessionSecurityDiagnosticsDataType,
|
|
82
65
|
WriteValue,
|
|
@@ -84,7 +67,9 @@ import {
|
|
|
84
67
|
TimeZoneDataType,
|
|
85
68
|
ProgramDiagnosticDataType,
|
|
86
69
|
CallMethodResultOptions,
|
|
87
|
-
|
|
70
|
+
ReadRequestOptions,
|
|
71
|
+
BrowseDescriptionOptions,
|
|
72
|
+
CallMethodRequest
|
|
88
73
|
} from "node-opcua-types";
|
|
89
74
|
import { DataType, isValidVariant, Variant, VariantArrayType } from "node-opcua-variant";
|
|
90
75
|
|
|
@@ -97,6 +82,8 @@ import { ServerSession } from "./server_session";
|
|
|
97
82
|
import { Subscription } from "./server_subscription";
|
|
98
83
|
import { sessionsCompatibleForTransfer } from "./sessions_compatible_for_transfer";
|
|
99
84
|
import { OPCUAServerOptions } from "./opcua_server";
|
|
85
|
+
import { IAddressSpaceAccessor } from "./i_address_space_accessor";
|
|
86
|
+
import { AddressSpaceAccessor } from "./addressSpace_accessor";
|
|
100
87
|
|
|
101
88
|
const debugLog = make_debugLog(__filename);
|
|
102
89
|
const errorLog = make_errorLog(__filename);
|
|
@@ -303,44 +290,6 @@ function _get_next_subscriptionId() {
|
|
|
303
290
|
return next_subscriptionId++;
|
|
304
291
|
}
|
|
305
292
|
|
|
306
|
-
function checkReadProcessedDetails(historyReadDetails: ReadProcessedDetails): StatusCode {
|
|
307
|
-
if (!historyReadDetails.aggregateConfiguration) {
|
|
308
|
-
historyReadDetails.aggregateConfiguration = new AggregateConfiguration({
|
|
309
|
-
useServerCapabilitiesDefaults: true
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
if (historyReadDetails.aggregateConfiguration.useServerCapabilitiesDefaults) {
|
|
313
|
-
return StatusCodes.Good;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// The PercentDataGood and PercentDataBad shall follow the following relationship
|
|
317
|
-
// PercentDataGood ≥ (100 – PercentDataBad).
|
|
318
|
-
// If they are equal the result of the PercentDataGood calculation is used.
|
|
319
|
-
// If the values entered for PercentDataGood and PercentDataBad do not result in a valid calculation
|
|
320
|
-
// (e.g. Bad = 80; Good = 0) the result will have a StatusCode of Bad_AggregateInvalidInputs.
|
|
321
|
-
if (
|
|
322
|
-
historyReadDetails.aggregateConfiguration.percentDataGood <
|
|
323
|
-
100 - historyReadDetails.aggregateConfiguration.percentDataBad
|
|
324
|
-
) {
|
|
325
|
-
return StatusCodes.BadAggregateInvalidInputs;
|
|
326
|
-
}
|
|
327
|
-
// The StatusCode Bad_AggregateInvalidInputs will be returned if the value of PercentDataGood
|
|
328
|
-
// or PercentDataBad exceed 100.
|
|
329
|
-
if (
|
|
330
|
-
historyReadDetails.aggregateConfiguration.percentDataGood > 100 ||
|
|
331
|
-
historyReadDetails.aggregateConfiguration.percentDataGood < 0
|
|
332
|
-
) {
|
|
333
|
-
return StatusCodes.BadAggregateInvalidInputs;
|
|
334
|
-
}
|
|
335
|
-
if (
|
|
336
|
-
historyReadDetails.aggregateConfiguration.percentDataBad > 100 ||
|
|
337
|
-
historyReadDetails.aggregateConfiguration.percentDataBad < 0
|
|
338
|
-
) {
|
|
339
|
-
return StatusCodes.BadAggregateInvalidInputs;
|
|
340
|
-
}
|
|
341
|
-
return StatusCodes.Good;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
293
|
export type StringGetter = () => string;
|
|
345
294
|
|
|
346
295
|
export interface ServerEngineOptions {
|
|
@@ -365,10 +314,15 @@ export interface CreateSessionOption {
|
|
|
365
314
|
export type ClosingReason = "Timeout" | "Terminated" | "CloseSession" | "Forcing";
|
|
366
315
|
|
|
367
316
|
export type ServerEngineShutdownTask = (this: ServerEngine) => void | Promise<void>;
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
|
|
368
322
|
/**
|
|
369
323
|
*
|
|
370
324
|
*/
|
|
371
|
-
export class ServerEngine extends EventEmitter {
|
|
325
|
+
export class ServerEngine extends EventEmitter implements IAddressSpaceAccessor {
|
|
372
326
|
public static readonly registry = new ObjectRegistry();
|
|
373
327
|
|
|
374
328
|
public isAuditing: boolean;
|
|
@@ -379,6 +333,7 @@ export class ServerEngine extends EventEmitter {
|
|
|
379
333
|
public clientDescription?: ApplicationDescription;
|
|
380
334
|
|
|
381
335
|
public addressSpace: AddressSpace | null;
|
|
336
|
+
public addressSpaceAccessor: IAddressSpaceAccessor | null = null;
|
|
382
337
|
|
|
383
338
|
// pseudo private
|
|
384
339
|
public _internalState: "creating" | "initializing" | "initialized" | "shutdown" | "disposed";
|
|
@@ -775,6 +730,8 @@ export class ServerEngine extends EventEmitter {
|
|
|
775
730
|
|
|
776
731
|
this.addressSpace = AddressSpace.create();
|
|
777
732
|
|
|
733
|
+
this.addressSpaceAccessor = new AddressSpaceAccessor(this.addressSpace);
|
|
734
|
+
|
|
778
735
|
// register namespace 1 (our namespace);
|
|
779
736
|
const serverNamespace = this.addressSpace.registerNamespace(this.serverNamespaceUrn);
|
|
780
737
|
assert(serverNamespace.index === 1);
|
|
@@ -1325,24 +1282,9 @@ export class ServerEngine extends EventEmitter {
|
|
|
1325
1282
|
});
|
|
1326
1283
|
}
|
|
1327
1284
|
|
|
1328
|
-
/**
|
|
1329
|
-
*
|
|
1330
|
-
* @method browseSingleNode
|
|
1331
|
-
* @param nodeId {NodeId|String} : the nodeid of the element to browse
|
|
1332
|
-
* @param browseDescription
|
|
1333
|
-
* @param browseDescription.browseDirection {BrowseDirection} :
|
|
1334
|
-
* @param browseDescription.referenceTypeId {String|NodeId}
|
|
1335
|
-
* @param [context]
|
|
1336
|
-
* @return the browse result
|
|
1337
|
-
*/
|
|
1338
|
-
public browseSingleNode(nodeId: NodeIdLike, browseDescription: BrowseDescription, context?: ISessionContext): BrowseResult {
|
|
1339
|
-
const addressSpace = this.addressSpace!;
|
|
1340
|
-
return addressSpace.browseSingleNode(nodeId, browseDescription, context);
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
1285
|
public async browseWithAutomaticExpansion(
|
|
1344
1286
|
nodesToBrowse: BrowseDescription[],
|
|
1345
|
-
context
|
|
1287
|
+
context: ISessionContext
|
|
1346
1288
|
): Promise<BrowseResult[]> {
|
|
1347
1289
|
// do expansion first
|
|
1348
1290
|
for (const browseDescription of nodesToBrowse) {
|
|
@@ -1363,325 +1305,22 @@ export class ServerEngine extends EventEmitter {
|
|
|
1363
1305
|
}
|
|
1364
1306
|
}
|
|
1365
1307
|
}
|
|
1366
|
-
return this.browse(
|
|
1308
|
+
return await this.browse(context, nodesToBrowse);
|
|
1367
1309
|
}
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
*/
|
|
1371
|
-
public browse(nodesToBrowse: BrowseDescription[], context?: ISessionContext): BrowseResult[] {
|
|
1372
|
-
const results: BrowseResult[] = [];
|
|
1373
|
-
for (const browseDescription of nodesToBrowse) {
|
|
1374
|
-
const nodeId = resolveNodeId(browseDescription.nodeId);
|
|
1375
|
-
const r = this.browseSingleNode(nodeId, browseDescription, context);
|
|
1376
|
-
results.push(r);
|
|
1377
|
-
}
|
|
1378
|
-
return results;
|
|
1310
|
+
public async browse(context: ISessionContext, nodesToBrowse: BrowseDescriptionOptions[]): Promise<BrowseResult[]> {
|
|
1311
|
+
return this.addressSpaceAccessor!.browse(context, nodesToBrowse);
|
|
1379
1312
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
*
|
|
1383
|
-
* @method readSingleNode
|
|
1384
|
-
* @param context
|
|
1385
|
-
* @param nodeId
|
|
1386
|
-
* @param attributeId
|
|
1387
|
-
* @param [timestampsToReturn=TimestampsToReturn.Neither]
|
|
1388
|
-
* @return DataValue
|
|
1389
|
-
*/
|
|
1390
|
-
public readSingleNode(
|
|
1391
|
-
context: ISessionContext,
|
|
1392
|
-
nodeId: NodeId | string,
|
|
1393
|
-
attributeId: AttributeIds,
|
|
1394
|
-
timestampsToReturn?: TimestampsToReturn
|
|
1395
|
-
): DataValue {
|
|
1396
|
-
context.currentTime = getCurrentClock();
|
|
1397
|
-
return this._readSingleNode(
|
|
1398
|
-
context,
|
|
1399
|
-
new ReadValueId({
|
|
1400
|
-
attributeId,
|
|
1401
|
-
nodeId: resolveNodeId(nodeId)
|
|
1402
|
-
}),
|
|
1403
|
-
timestampsToReturn
|
|
1404
|
-
);
|
|
1313
|
+
public async read(context: ISessionContext, readRequest: ReadRequestOptions): Promise<DataValue[]> {
|
|
1314
|
+
return this.addressSpaceAccessor!.read(context, readRequest);
|
|
1405
1315
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
*
|
|
1409
|
-
*
|
|
1410
|
-
* Maximum age of the value to be read in milliseconds. The age of the value is based on the difference between
|
|
1411
|
-
* the ServerTimestamp and the time when the Server starts processing the request. For example if the Client
|
|
1412
|
-
* specifies a maxAge of 500 milliseconds and it takes 100 milliseconds until the Server starts processing
|
|
1413
|
-
* the request, the age of the returned value could be 600 milliseconds prior to the time it was requested.
|
|
1414
|
-
* If the Server has one or more values of an Attribute that are within the maximum age, it can return any one
|
|
1415
|
-
* of the values or it can read a new value from the data source. The number of values of an Attribute that
|
|
1416
|
-
* a Server has depends on the number of MonitoredItems that are defined for the Attribute. In any case,
|
|
1417
|
-
* the Client can make no assumption about which copy of the data will be returned.
|
|
1418
|
-
* If the Server does not have a value that is within the maximum age, it shall attempt to read a new value
|
|
1419
|
-
* from the data source.
|
|
1420
|
-
* If the Server cannot meet the requested maxAge, it returns its 'best effort' value rather than rejecting the
|
|
1421
|
-
* request.
|
|
1422
|
-
* This may occur when the time it takes the Server to process and return the new data value after it has been
|
|
1423
|
-
* accessed is greater than the specified maximum age.
|
|
1424
|
-
* If maxAge is set to 0, the Server shall attempt to read a new value from the data source.
|
|
1425
|
-
* If maxAge is set to the max Int32 value or greater, the Server shall attempt to get a cached value.
|
|
1426
|
-
* Negative values are invalid for maxAge.
|
|
1427
|
-
*
|
|
1428
|
-
* @return an array of DataValue
|
|
1429
|
-
*/
|
|
1430
|
-
public read(context: ISessionContext, readRequest: ReadRequest): DataValue[] {
|
|
1431
|
-
assert(context instanceof SessionContext);
|
|
1432
|
-
assert(readRequest instanceof ReadRequest);
|
|
1433
|
-
assert(readRequest.maxAge >= 0);
|
|
1434
|
-
|
|
1435
|
-
const timestampsToReturn = readRequest.timestampsToReturn;
|
|
1436
|
-
|
|
1437
|
-
const nodesToRead = readRequest.nodesToRead || [];
|
|
1438
|
-
assert(Array.isArray(nodesToRead));
|
|
1439
|
-
|
|
1440
|
-
context.currentTime = getCurrentClock();
|
|
1441
|
-
|
|
1442
|
-
const dataValues: DataValue[] = [];
|
|
1443
|
-
for (const readValueId of nodesToRead) {
|
|
1444
|
-
const dataValue = this._readSingleNode(context, readValueId, timestampsToReturn);
|
|
1445
|
-
if (timestampsToReturn === TimestampsToReturn.Server) {
|
|
1446
|
-
dataValue.sourceTimestamp = null;
|
|
1447
|
-
dataValue.sourcePicoseconds = 0;
|
|
1448
|
-
}
|
|
1449
|
-
if (
|
|
1450
|
-
(timestampsToReturn === TimestampsToReturn.Both || timestampsToReturn === TimestampsToReturn.Server) &&
|
|
1451
|
-
(!dataValue.serverTimestamp || dataValue.serverTimestamp.getTime() === minOPCUADate.getTime())
|
|
1452
|
-
) {
|
|
1453
|
-
dataValue.serverTimestamp = context.currentTime.timestamp;
|
|
1454
|
-
dataValue.serverPicoseconds = 0; // context.currentTime.picoseconds;
|
|
1455
|
-
}
|
|
1456
|
-
dataValues.push(dataValue);
|
|
1457
|
-
}
|
|
1458
|
-
return dataValues;
|
|
1459
|
-
}
|
|
1460
|
-
|
|
1461
|
-
/**
|
|
1462
|
-
*
|
|
1463
|
-
* @method writeSingleNode
|
|
1464
|
-
* @param context
|
|
1465
|
-
* @param writeValue
|
|
1466
|
-
* @param callback
|
|
1467
|
-
* @param callback.err
|
|
1468
|
-
* @param callback.statusCode
|
|
1469
|
-
* @async
|
|
1470
|
-
*/
|
|
1471
|
-
public writeSingleNode(
|
|
1472
|
-
context: ISessionContext,
|
|
1473
|
-
writeValue: WriteValue,
|
|
1474
|
-
callback: (err: Error | null, statusCode?: StatusCode) => void
|
|
1475
|
-
): void {
|
|
1476
|
-
assert(context instanceof SessionContext);
|
|
1477
|
-
assert(typeof callback === "function");
|
|
1478
|
-
assert(writeValue.schema.name === "WriteValue");
|
|
1479
|
-
assert(writeValue.value instanceof DataValue);
|
|
1480
|
-
|
|
1481
|
-
if (writeValue.value.value === null) {
|
|
1482
|
-
return callback(null, StatusCodes.BadTypeMismatch);
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
assert(writeValue.value.value instanceof Variant);
|
|
1486
|
-
|
|
1487
|
-
const nodeId = writeValue.nodeId;
|
|
1488
|
-
|
|
1489
|
-
const obj = this.__findNode(nodeId) as UAVariable;
|
|
1490
|
-
if (!obj) {
|
|
1491
|
-
return callback(null, StatusCodes.BadNodeIdUnknown);
|
|
1492
|
-
} else {
|
|
1493
|
-
obj.writeAttribute(context, writeValue, callback);
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1496
|
-
|
|
1497
|
-
/**
|
|
1498
|
-
* write a collection of nodes
|
|
1499
|
-
* @method write
|
|
1500
|
-
* @param context
|
|
1501
|
-
* @param nodesToWrite
|
|
1502
|
-
* @param callback
|
|
1503
|
-
* @param callback.err
|
|
1504
|
-
* @param callback.results
|
|
1505
|
-
* @async
|
|
1506
|
-
*/
|
|
1507
|
-
public write(
|
|
1508
|
-
context: ISessionContext,
|
|
1509
|
-
nodesToWrite: WriteValue[],
|
|
1510
|
-
callback: (err: Error | null, statusCodes?: StatusCode[]) => void
|
|
1511
|
-
): void {
|
|
1512
|
-
assert(context instanceof SessionContext);
|
|
1513
|
-
assert(typeof callback === "function");
|
|
1514
|
-
|
|
1515
|
-
context.currentTime = getCurrentClock();
|
|
1516
|
-
|
|
1517
|
-
ensureDatatypeExtractedWithCallback(
|
|
1518
|
-
this.addressSpace!,
|
|
1519
|
-
(err2: Error | null, extraDataTypeManager?: ExtraDataTypeManager) => {
|
|
1520
|
-
if (err2) {
|
|
1521
|
-
return callback(err2);
|
|
1522
|
-
}
|
|
1523
|
-
const performWrite = (writeValue: WriteValue, inner_callback: StatusCodeCallback) => {
|
|
1524
|
-
resolveOpaqueOnAddressSpace(this.addressSpace!, writeValue.value.value!)
|
|
1525
|
-
.then(() => {
|
|
1526
|
-
this.writeSingleNode(context, writeValue, inner_callback);
|
|
1527
|
-
})
|
|
1528
|
-
.catch(inner_callback);
|
|
1529
|
-
};
|
|
1530
|
-
// tslint:disable:array-type
|
|
1531
|
-
async.map(nodesToWrite, performWrite, (err?: Error | null, statusCodes?: (StatusCode | undefined)[]) => {
|
|
1532
|
-
assert(Array.isArray(statusCodes));
|
|
1533
|
-
callback(err!, statusCodes as StatusCode[]);
|
|
1534
|
-
});
|
|
1535
|
-
}
|
|
1536
|
-
);
|
|
1316
|
+
public async write(context: ISessionContext, nodesToWrite: WriteValue[]): Promise<StatusCode[]> {
|
|
1317
|
+
return await this.addressSpaceAccessor!.write(context, nodesToWrite);
|
|
1537
1318
|
}
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
*
|
|
1541
|
-
*/
|
|
1542
|
-
public historyReadSingleNode(
|
|
1543
|
-
context: ISessionContext,
|
|
1544
|
-
nodeId: NodeId,
|
|
1545
|
-
attributeId: AttributeIds,
|
|
1546
|
-
historyReadDetails: ReadRawModifiedDetails | ReadEventDetails | ReadProcessedDetails | ReadAtTimeDetails,
|
|
1547
|
-
timestampsToReturn: TimestampsToReturn,
|
|
1548
|
-
continuationData: ContinuationData,
|
|
1549
|
-
callback: (err: Error | null, results?: HistoryReadResult) => void
|
|
1550
|
-
): void {
|
|
1551
|
-
if (timestampsToReturn === TimestampsToReturn.Invalid) {
|
|
1552
|
-
callback(
|
|
1553
|
-
null,
|
|
1554
|
-
new HistoryReadResult({
|
|
1555
|
-
statusCode: StatusCodes.BadTimestampsToReturnInvalid
|
|
1556
|
-
})
|
|
1557
|
-
);
|
|
1558
|
-
return;
|
|
1559
|
-
}
|
|
1560
|
-
assert(context instanceof SessionContext);
|
|
1561
|
-
this._historyReadSingleNode(
|
|
1562
|
-
context,
|
|
1563
|
-
new HistoryReadValueId({
|
|
1564
|
-
nodeId
|
|
1565
|
-
}),
|
|
1566
|
-
historyReadDetails,
|
|
1567
|
-
timestampsToReturn,
|
|
1568
|
-
continuationData,
|
|
1569
|
-
callback
|
|
1570
|
-
);
|
|
1319
|
+
public async call(context: ISessionContext, methodsToCall: CallMethodRequest[]): Promise<CallMethodResultOptions[]> {
|
|
1320
|
+
return await this.addressSpaceAccessor!.call(context, methodsToCall);
|
|
1571
1321
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
*
|
|
1575
|
-
* @method historyRead
|
|
1576
|
-
* @param context {SessionContext}
|
|
1577
|
-
* @param historyReadRequest {HistoryReadRequest}
|
|
1578
|
-
* @param historyReadRequest.requestHeader {RequestHeader}
|
|
1579
|
-
* @param historyReadRequest.historyReadDetails {HistoryReadDetails}
|
|
1580
|
-
* @param historyReadRequest.timestampsToReturn {TimestampsToReturn}
|
|
1581
|
-
* @param historyReadRequest.releaseContinuationPoints {Boolean}
|
|
1582
|
-
* @param historyReadRequest.nodesToRead {HistoryReadValueId[]}
|
|
1583
|
-
* @param callback
|
|
1584
|
-
* @param callback.err
|
|
1585
|
-
* @param callback.results {HistoryReadResult[]}
|
|
1586
|
-
*/
|
|
1587
|
-
public historyRead(
|
|
1588
|
-
context: ISessionContext,
|
|
1589
|
-
historyReadRequest: HistoryReadRequest,
|
|
1590
|
-
callback: (err: Error | null, results: HistoryReadResult[]) => void
|
|
1591
|
-
): void {
|
|
1592
|
-
assert(context instanceof SessionContext);
|
|
1593
|
-
assert(historyReadRequest instanceof HistoryReadRequest);
|
|
1594
|
-
assert(typeof callback === "function");
|
|
1595
|
-
|
|
1596
|
-
const timestampsToReturn = historyReadRequest.timestampsToReturn;
|
|
1597
|
-
const historyReadDetails = historyReadRequest.historyReadDetails! as HistoryReadDetails;
|
|
1598
|
-
const releaseContinuationPoints = historyReadRequest.releaseContinuationPoints;
|
|
1599
|
-
assert(historyReadDetails instanceof HistoryReadDetails);
|
|
1600
|
-
// ReadAnnotationDataDetails | ReadAtTimeDetails | ReadEventDetails | ReadProcessedDetails | ReadRawModifiedDetails;
|
|
1601
|
-
|
|
1602
|
-
const nodesToRead = historyReadRequest.nodesToRead || ([] as HistoryReadValueId[]);
|
|
1603
|
-
assert(Array.isArray(nodesToRead));
|
|
1604
|
-
|
|
1605
|
-
// special cases with ReadProcessedDetails
|
|
1606
|
-
interface M {
|
|
1607
|
-
nodeToRead: HistoryReadValueId;
|
|
1608
|
-
processDetail: ReadProcessedDetails;
|
|
1609
|
-
index: number;
|
|
1610
|
-
}
|
|
1611
|
-
|
|
1612
|
-
const _q = async (m: M): Promise<HistoryReadResult> => {
|
|
1613
|
-
return new Promise((resolve) => {
|
|
1614
|
-
const continuationPoint = m.nodeToRead.continuationPoint;
|
|
1615
|
-
this._historyReadSingleNode(
|
|
1616
|
-
context,
|
|
1617
|
-
m.nodeToRead,
|
|
1618
|
-
m.processDetail,
|
|
1619
|
-
timestampsToReturn,
|
|
1620
|
-
{ continuationPoint, releaseContinuationPoints /**, index = ??? */ },
|
|
1621
|
-
(err: Error | null, result?: any) => {
|
|
1622
|
-
if (err && !result) {
|
|
1623
|
-
errorLog("Internal error", err.message);
|
|
1624
|
-
result = new HistoryReadResult({ statusCode: StatusCodes.BadInternalError });
|
|
1625
|
-
}
|
|
1626
|
-
resolve(result);
|
|
1627
|
-
}
|
|
1628
|
-
);
|
|
1629
|
-
});
|
|
1630
|
-
};
|
|
1631
|
-
|
|
1632
|
-
if (historyReadDetails instanceof ReadProcessedDetails) {
|
|
1633
|
-
//
|
|
1634
|
-
if (!historyReadDetails.aggregateType || historyReadDetails.aggregateType.length !== nodesToRead.length) {
|
|
1635
|
-
return callback(null, [new HistoryReadResult({ statusCode: StatusCodes.BadInvalidArgument })]);
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
// chkec parameters
|
|
1639
|
-
const parameterStatus = checkReadProcessedDetails(historyReadDetails);
|
|
1640
|
-
if (parameterStatus !== StatusCodes.Good) {
|
|
1641
|
-
return callback(null, [new HistoryReadResult({ statusCode: parameterStatus })]);
|
|
1642
|
-
}
|
|
1643
|
-
const promises: Promise<HistoryReadResult>[] = [];
|
|
1644
|
-
let index = 0;
|
|
1645
|
-
for (const nodeToRead of nodesToRead) {
|
|
1646
|
-
const aggregateType = historyReadDetails.aggregateType[index];
|
|
1647
|
-
const processDetail = new ReadProcessedDetails({ ...historyReadDetails, aggregateType: [aggregateType] });
|
|
1648
|
-
promises.push(_q({ nodeToRead, processDetail, index }));
|
|
1649
|
-
index++;
|
|
1650
|
-
}
|
|
1651
|
-
Promise.all(promises).then((results: HistoryReadResult[]) => {
|
|
1652
|
-
callback(null, results);
|
|
1653
|
-
});
|
|
1654
|
-
return;
|
|
1655
|
-
}
|
|
1656
|
-
|
|
1657
|
-
const _r = async (nodeToRead: HistoryReadValueId, index: number) => {
|
|
1658
|
-
const continuationPoint = nodeToRead.continuationPoint;
|
|
1659
|
-
return new Promise<HistoryReadResult>((resolve, reject) => {
|
|
1660
|
-
this._historyReadSingleNode(
|
|
1661
|
-
context,
|
|
1662
|
-
nodeToRead,
|
|
1663
|
-
historyReadDetails,
|
|
1664
|
-
timestampsToReturn,
|
|
1665
|
-
{ continuationPoint, releaseContinuationPoints, index },
|
|
1666
|
-
(err: Error | null, result?: any) => {
|
|
1667
|
-
if (err && !result) {
|
|
1668
|
-
result = new HistoryReadResult({ statusCode: StatusCodes.BadInternalError });
|
|
1669
|
-
}
|
|
1670
|
-
resolve(result);
|
|
1671
|
-
// it's not guaranteed that the historical read process is really asynchronous
|
|
1672
|
-
}
|
|
1673
|
-
);
|
|
1674
|
-
});
|
|
1675
|
-
};
|
|
1676
|
-
const promises: Promise<HistoryReadResult>[] = [];
|
|
1677
|
-
let index = 0;
|
|
1678
|
-
for (const nodeToRead of nodesToRead) {
|
|
1679
|
-
promises.push(_r(nodeToRead, index));
|
|
1680
|
-
index++;
|
|
1681
|
-
}
|
|
1682
|
-
Promise.all(promises).then((results: HistoryReadResult[]) => {
|
|
1683
|
-
callback(null, results);
|
|
1684
|
-
});
|
|
1322
|
+
public async historyRead(context: ISessionContext, historyReadRequest: HistoryReadRequest): Promise<HistoryReadResult[]> {
|
|
1323
|
+
return this.addressSpaceAccessor!.historyRead(context, historyReadRequest);
|
|
1685
1324
|
}
|
|
1686
1325
|
|
|
1687
1326
|
public getOldestInactiveSession(): ServerSession | null {
|
|
@@ -1980,9 +1619,15 @@ export class ServerEngine extends EventEmitter {
|
|
|
1980
1619
|
return session;
|
|
1981
1620
|
}
|
|
1982
1621
|
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1622
|
+
public async translateBrowsePaths(browsePaths: BrowsePath[]): Promise<BrowsePathResult[]> {
|
|
1623
|
+
const browsePathResults: BrowsePathResult[] = [];
|
|
1624
|
+
for (const browsePath of browsePaths) {
|
|
1625
|
+
const result = await this.translateBrowsePath(browsePath);
|
|
1626
|
+
browsePathResults.push(result);
|
|
1627
|
+
}
|
|
1628
|
+
return browsePathResults;
|
|
1629
|
+
}
|
|
1630
|
+
public async translateBrowsePath(browsePath: BrowsePath): Promise<BrowsePathResult> {
|
|
1986
1631
|
return this.addressSpace!.browsePath(browsePath);
|
|
1987
1632
|
}
|
|
1988
1633
|
|
|
@@ -2139,123 +1784,6 @@ export class ServerEngine extends EventEmitter {
|
|
|
2139
1784
|
return subscription;
|
|
2140
1785
|
}
|
|
2141
1786
|
|
|
2142
|
-
private __findNode(nodeId: NodeId): BaseNode | null {
|
|
2143
|
-
if (nodeId.namespace >= (this.addressSpace?.getNamespaceArray().length || 0)) {
|
|
2144
|
-
return null;
|
|
2145
|
-
}
|
|
2146
|
-
const namespace = this.addressSpace!.getNamespace(nodeId.namespace)!;
|
|
2147
|
-
return namespace.findNode2(nodeId)!;
|
|
2148
|
-
}
|
|
2149
|
-
|
|
2150
|
-
private _readSingleNode(context: ISessionContext, nodeToRead: ReadValueId, timestampsToReturn?: TimestampsToReturn): DataValue {
|
|
2151
|
-
assert(context instanceof SessionContext);
|
|
2152
|
-
const nodeId: NodeId = nodeToRead.nodeId!;
|
|
2153
|
-
const attributeId: AttributeIds = nodeToRead.attributeId!;
|
|
2154
|
-
const indexRange: NumericRange = nodeToRead.indexRange!;
|
|
2155
|
-
const dataEncoding: QualifiedName = nodeToRead.dataEncoding;
|
|
2156
|
-
|
|
2157
|
-
if (timestampsToReturn === TimestampsToReturn.Invalid) {
|
|
2158
|
-
return new DataValue({ statusCode: StatusCodes.BadTimestampsToReturnInvalid });
|
|
2159
|
-
}
|
|
2160
|
-
|
|
2161
|
-
timestampsToReturn = coerceTimestampsToReturn(timestampsToReturn);
|
|
2162
|
-
|
|
2163
|
-
const obj = this.__findNode(nodeId!);
|
|
2164
|
-
|
|
2165
|
-
let dataValue;
|
|
2166
|
-
if (!obj) {
|
|
2167
|
-
// may be return BadNodeIdUnknown in dataValue instead ?
|
|
2168
|
-
// Object Not Found
|
|
2169
|
-
return new DataValue({ statusCode: StatusCodes.BadNodeIdUnknown });
|
|
2170
|
-
} else {
|
|
2171
|
-
// check access
|
|
2172
|
-
// BadUserAccessDenied
|
|
2173
|
-
// BadNotReadable
|
|
2174
|
-
// invalid attributes : BadNodeAttributesInvalid
|
|
2175
|
-
// invalid range : BadIndexRangeInvalid
|
|
2176
|
-
dataValue = obj.readAttribute(context, attributeId, indexRange, dataEncoding);
|
|
2177
|
-
dataValue = apply_timestamps_no_copy(dataValue, timestampsToReturn, attributeId);
|
|
2178
|
-
|
|
2179
|
-
return dataValue;
|
|
2180
|
-
}
|
|
2181
|
-
}
|
|
2182
|
-
|
|
2183
|
-
private _historyReadSingleNode(
|
|
2184
|
-
context: ISessionContext,
|
|
2185
|
-
nodeToRead: HistoryReadValueId,
|
|
2186
|
-
historyReadDetails: HistoryReadDetails,
|
|
2187
|
-
timestampsToReturn: TimestampsToReturn,
|
|
2188
|
-
continuationData: ContinuationData,
|
|
2189
|
-
callback: CallbackT<HistoryReadResult>
|
|
2190
|
-
): void {
|
|
2191
|
-
assert(context instanceof SessionContext);
|
|
2192
|
-
assert(typeof callback === "function");
|
|
2193
|
-
|
|
2194
|
-
const nodeId = nodeToRead.nodeId;
|
|
2195
|
-
const indexRange = nodeToRead.indexRange;
|
|
2196
|
-
const dataEncoding = nodeToRead.dataEncoding;
|
|
2197
|
-
const continuationPoint = nodeToRead.continuationPoint;
|
|
2198
|
-
|
|
2199
|
-
timestampsToReturn = coerceTimestampsToReturn(timestampsToReturn);
|
|
2200
|
-
if (timestampsToReturn === TimestampsToReturn.Invalid) {
|
|
2201
|
-
return callback(null, new HistoryReadResult({ statusCode: StatusCodes.BadTimestampsToReturnInvalid }));
|
|
2202
|
-
}
|
|
2203
|
-
|
|
2204
|
-
const obj = this.__findNode(nodeId) as UAVariable;
|
|
2205
|
-
|
|
2206
|
-
if (!obj) {
|
|
2207
|
-
// may be return BadNodeIdUnknown in dataValue instead ?
|
|
2208
|
-
// Object Not Found
|
|
2209
|
-
callback(null, new HistoryReadResult({ statusCode: StatusCodes.BadNodeIdUnknown }));
|
|
2210
|
-
return;
|
|
2211
|
-
} else {
|
|
2212
|
-
// istanbul ignore next
|
|
2213
|
-
if (!obj.historyRead) {
|
|
2214
|
-
// note : Object and View may also support historyRead to provide Event historical data
|
|
2215
|
-
// todo implement historyRead for Object and View
|
|
2216
|
-
const msg =
|
|
2217
|
-
" this node doesn't provide historyRead! probably not a UAVariable\n " +
|
|
2218
|
-
obj.nodeId.toString() +
|
|
2219
|
-
" " +
|
|
2220
|
-
obj.browseName.toString() +
|
|
2221
|
-
"\n" +
|
|
2222
|
-
"with " +
|
|
2223
|
-
nodeToRead.toString() +
|
|
2224
|
-
"\n" +
|
|
2225
|
-
"HistoryReadDetails " +
|
|
2226
|
-
historyReadDetails.toString();
|
|
2227
|
-
if (doDebug) {
|
|
2228
|
-
debugLog(chalk.cyan("ServerEngine#_historyReadSingleNode "), chalk.white.bold(msg));
|
|
2229
|
-
}
|
|
2230
|
-
const err = new Error(msg);
|
|
2231
|
-
// object has no historyRead method
|
|
2232
|
-
setImmediate(callback.bind(null, err));
|
|
2233
|
-
return;
|
|
2234
|
-
}
|
|
2235
|
-
// check access
|
|
2236
|
-
// BadUserAccessDenied
|
|
2237
|
-
// BadNotReadable
|
|
2238
|
-
// invalid attributes : BadNodeAttributesInvalid
|
|
2239
|
-
// invalid range : BadIndexRangeInvalid
|
|
2240
|
-
obj.historyRead(
|
|
2241
|
-
context,
|
|
2242
|
-
historyReadDetails,
|
|
2243
|
-
indexRange,
|
|
2244
|
-
dataEncoding,
|
|
2245
|
-
continuationData,
|
|
2246
|
-
(err: Error | null, result?: HistoryReadResult) => {
|
|
2247
|
-
if (err || !result) {
|
|
2248
|
-
return callback(err);
|
|
2249
|
-
}
|
|
2250
|
-
assert(result!.statusCode instanceof StatusCode);
|
|
2251
|
-
assert(result!.isValid());
|
|
2252
|
-
// result = apply_timestamps(result, timestampsToReturn, attributeId);
|
|
2253
|
-
callback(err, result);
|
|
2254
|
-
}
|
|
2255
|
-
);
|
|
2256
|
-
}
|
|
2257
|
-
}
|
|
2258
|
-
|
|
2259
1787
|
/**
|
|
2260
1788
|
*/
|
|
2261
1789
|
private __internal_bindMethod(nodeId: NodeId, func: MethodFunctor) {
|
|
@@ -1086,11 +1086,11 @@ export class Subscription extends EventEmitter {
|
|
|
1086
1086
|
this._createMonitoredItemStep3(monitoredItem, monitoredItemCreateRequest);
|
|
1087
1087
|
}
|
|
1088
1088
|
|
|
1089
|
-
public createMonitoredItem(
|
|
1089
|
+
public async createMonitoredItem(
|
|
1090
1090
|
addressSpace: AddressSpace,
|
|
1091
1091
|
timestampsToReturn: TimestampsToReturn,
|
|
1092
1092
|
monitoredItemCreateRequest: MonitoredItemCreateRequest
|
|
1093
|
-
): MonitoredItemCreateResult {
|
|
1093
|
+
): Promise<MonitoredItemCreateResult> {
|
|
1094
1094
|
const { monitoredItem, createResult } = this.preCreateMonitoredItem(
|
|
1095
1095
|
addressSpace,
|
|
1096
1096
|
timestampsToReturn,
|