node-opcua-pseudo-session
Version:
pure nodejs OPCUA SDK - module pseudo-session
127 lines (120 loc) • 5.89 kB
text/typescript
/**
* @module node-opcua-pseudo-session
*/
import { assert } from "node-opcua-assert";
import { AttributeIds } from "node-opcua-basic-types";
import { VariableIds } from "node-opcua-constants";
import { make_debugLog } from "node-opcua-debug";
import { resolveNodeId } from "node-opcua-nodeid";
import { BrowseDescriptionOptions, BrowseResult, ReferenceDescription } from "node-opcua-service-browse";
import { StatusCodes } from "node-opcua-status-code";
import {
IBasicSessionReadAsyncMultiple,
IBasicSessionBrowseAsyncMultiple,
IBasicSessionBrowseNextAsyncMultiple,
BrowseDescriptionLike
} from "./basic_session_interface";
const debugLog = make_debugLog(__filename);
async function readLimits(session: IBasicSessionReadAsyncMultiple) {
const dataValues = await session.read([
{ nodeId: VariableIds.Server_ServerCapabilities_MaxBrowseContinuationPoints, attributeId: AttributeIds.Value },
{ nodeId: VariableIds.Server_ServerCapabilities_OperationLimits_MaxNodesPerBrowse, attributeId: AttributeIds.Value }
]);
const maxBrowseContinuationPoints = (dataValues[0].value.value as number) || 10;
const maxNodesPerBrowse = (dataValues[1].value.value as number) || 10;
return { maxBrowseContinuationPoints, maxNodesPerBrowse };
}
function coerceToBrowseDescription(nodeToBrowse: BrowseDescriptionLike): BrowseDescriptionOptions {
if (typeof nodeToBrowse === "string") {
return <BrowseDescriptionOptions>{
nodeId: resolveNodeId(nodeToBrowse)
};
} else {
return nodeToBrowse as BrowseDescriptionOptions;
}
}
export type ISessionForBrowseAll = IBasicSessionBrowseAsyncMultiple & IBasicSessionBrowseNextAsyncMultiple & IBasicSessionReadAsyncMultiple;
export async function browseAll(session: ISessionForBrowseAll, nodeToBrowse: BrowseDescriptionLike): Promise<BrowseResult>;
export async function browseAll(session: ISessionForBrowseAll, nodesToBrowse: BrowseDescriptionLike[]): Promise<BrowseResult[]>;
export async function browseAll(
session: ISessionForBrowseAll,
nodesToBrowse: BrowseDescriptionLike[] | BrowseDescriptionLike
): Promise<BrowseResult | BrowseResult[]> {
if (!(nodesToBrowse instanceof Array)) {
return (await browseAll(session, [nodesToBrowse]))[0];
}
const { maxBrowseContinuationPoints, maxNodesPerBrowse } = await readLimits(session);
const maxNodesToBrowse = Math.min(maxNodesPerBrowse, maxBrowseContinuationPoints);
const tmp = nodesToBrowse.map(coerceToBrowseDescription);
let nodesToBrowse1 = tmp.splice(0, maxNodesToBrowse);
let browseResults: BrowseResult[] = [];
while (nodesToBrowse1.length) {
const partialBrowseResult = await browseAll2(session, nodesToBrowse1);
browseResults = [...browseResults, ...partialBrowseResult];
nodesToBrowse1 = tmp.splice(0, maxNodesToBrowse);
}
assert(browseResults.length === nodesToBrowse.length, "browseResults must have same length as nodesToBrowse");
return browseResults;
}
export async function browseAll2(
session: IBasicSessionBrowseAsyncMultiple & IBasicSessionBrowseNextAsyncMultiple,
nodesToBrowse: BrowseDescriptionOptions[]
): Promise<BrowseResult[]> {
if (nodesToBrowse.length === 0) {
return [];
}
const browseResults = await session.browse(nodesToBrowse);
const browseToRedo = [];
const browseToContinue: { references: ReferenceDescription[]; continuationPoint: Buffer }[] = [];
for (let i = 0; i < browseResults.length; i++) {
const result = browseResults[i];
if (
result.statusCode.equals(StatusCodes.BadNoContinuationPoints) ||
result.statusCode.equals(StatusCodes.BadContinuationPointInvalid)
) {
// there was not enough continuation points
debugLog("There is not enough browse continuation points");
// we will have to re-inject this browse to a new browse command
browseToRedo.push({ index: i, nodeToBrowse: nodesToBrowse[i] });
continue;
}
const continuationPoint = result.continuationPoint;
(result as any).continuationPoint = undefined;
if (continuationPoint && continuationPoint.length > 0) {
browseToContinue.push({ references: result.references || [], continuationPoint });
}
}
// resolve continuationPoints
while (browseToContinue.length) {
const tmp = [...browseToContinue];
const continuationPoints = tmp.map((e) => e.continuationPoint);
browseToContinue.splice(0);
const browseNextResults = await session.browseNext(continuationPoints, false);
assert(
continuationPoints.length === browseNextResults.length,
"browseNextResults length should eql continuationPoints.length"
);
for (let i = 0; i < browseNextResults.length; i++) {
const browseResult = browseNextResults[i];
const references = tmp[i].references || [];
if (browseResult.references && browseResult.references.length) {
references.push(...browseResult.references);
}
const continuationPoint = browseResult.continuationPoint;
if (continuationPoint) {
browseToContinue.push({ references, continuationPoint });
}
}
}
// resolve to redo
if (browseToRedo.length && nodesToBrowse.length !== browseToRedo.length) {
const nodesToBrowse2 = browseToRedo.map((e) => e.nodeToBrowse);
const results2 = await browseAll2(session, nodesToBrowse2);
for (let i = 0; i < browseResults.length; i++) {
browseResults[browseToRedo[i].index] = results2[i];
}
browseToRedo.splice(0);
}
browseResults.forEach((b) => ((b as any).continuationPoint = undefined));
return browseResults;
}