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.
@@ -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
- resolveOpaqueOnAddressSpace,
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, coerceTimestampsToReturn, apply_timestamps_no_copy } from "node-opcua-data-value";
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 { HistoryReadDetails, HistoryReadRequest, HistoryReadResult, HistoryReadValueId } from "node-opcua-service-history";
69
- import { StatusCode, StatusCodes, CallbackT, StatusCodeCallback } from "node-opcua-status-code";
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
- AggregateConfiguration
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?: ISessionContext
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(nodesToBrowse, context);
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
- public browsePath(browsePath: BrowsePath): BrowsePathResult {
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,