import * as chalk from "chalk" ; import * as async from "async" ; import { NodeIdLike , resolveNodeId } from "node-opcua-nodeid" ; import { assert } from "node-opcua-assert" ; import { ReferenceDescription } from "node-opcua-service-browse" ; import { ErrorCallback } from "node-opcua-status-code" ; import { checkDebugFlag , make_debugLog , make_warningLog } from "node-opcua-debug" ; import { NodeClass } from "node-opcua-data-model" ; import { lowerFirstLetter } from "node-opcua-utils" ; import { NodeCrawlerBase , ObjectMap , Pojo , UserData } from "./node_crawler_base" ; import { CacheNode , CacheNodeVariable , CacheNodeVariableType } from "./cache_node" ; import { TaskReconstruction , EmptyCallback , removeCycle } from "./private" ; const debugLog = make_debugLog ( __filename ) ; const warningLog = make_warningLog ( __filename ) ; const doDebug = checkDebugFlag ( __filename ) ; type Queue = async . QueueObject < TaskReconstruction > ; export class NodeCrawler extends NodeCrawlerBase { /**
     *
     */ public read ( nodeId ) ; public read ( nodeId , callback ) ; public read ( nodeId , callback ? ) { if ( ! callback ) { throw new Error ( "Invalid Error" ) ; } try { nodeId = resolveNodeId ( nodeId ) ; } catch ( err ) { callback ( err as Error ) ; return ; } const key = nodeId . toString ( ) ; if ( Object . prototype . hasOwnProperty . call ( this . _objMap , key ) ) { const object = this . _objMap [ key ] ; return callback ( null , object ) ; } const userData = { onBrowse : NodeCrawlerBase . follow } ; this . crawl ( nodeId , userData , ( err ) => { if ( err ) { return callback ( err ) ; } if ( Object . prototype . hasOwnProperty . call ( this . _objectCache , key ) ) { const cacheNode = this . _objectCache [ key ] ; assert ( cacheNode . browseName . name !== "pending" ) ; this . simplify_object ( this . _objMap , cacheNode , callback ) ; } else { callback ( new Error ( "Cannot find nodeId" + key ) ) ; } } ) ; } private simplify_object ( objMap , object , finalCallback ) { assert ( typeof finalCallback === "function" ) ; const queue = async . queue ( ( task , innerCallback ) => { setImmediate ( ( ) => { assert ( typeof task . func === "function" ) ; task . func ( task , innerCallback ) ; } ) ; } , 1 ) ; this . _add_for_reconstruction ( queue , objMap , object , ( ) => { } ) ; const key1 = object . nodeId . toString ( ) ; queue . drain ( ( ) => { const object1 = this . _objMap [ key1 ] ; removeCycle ( object1 , finalCallback ) ; } ) ; } private _add_for_reconstruction ( queue , objMap , object , extraFunc ) { if ( ! object || ! object . nodeId ) { return ; } assert ( typeof extraFunc === "function" ) ; assert ( typeof object . nodeId . toString ( ) === "string" ) ; const task = { data : object , func : ( data , callback ) => { this . _reconstruct_manageable_object ( queue , objMap , object , ( err , obj ? ) => { extraFunc ( err , obj ) ; callback ( err || undefined ) ; } ) ; } } ; queue . push ( task ) ; } private _reconstruct_manageable_object ( queue , objMap , object , callback ) { assert ( typeof callback === "function" ) ; assert ( object ) ; assert ( object . nodeId ) ; const key2 = object . nodeId . toString ( ) ; if ( Object . prototype . hasOwnProperty . call ( objMap , key2 ) ) { return callback ( null , objMap [ key2 ] ) ; } const obj = { browseName : object . browseName . name , nodeId : object . nodeId . toString ( ) } ; if ( object . nodeClass ) { obj . nodeClass = object . nodeClass . toString ( ) ; } if ( object instanceof CacheNodeVariable || object instanceof CacheNodeVariableType ) { if ( object . dataType ) { obj . dataType = object . dataType . toJSON ( ) ; } if ( object . dataValue ) { obj . dataValue = object . dataValue . toJSON ( ) ; } } objMap [ key2 ] = obj ; const referenceMap = obj ; object . references = object . references || [ ] ; object . references . map ( ( ref ) => { assert ( ref ) ; const refIndex = ref . referenceTypeId . toString ( ) ; const referenceType = this . _objectCache [ refIndex ] ; if ( ! referenceType ) { warningLog ( chalk . red ( "Unknown reference type " + refIndex ) ) ; } const reference = this . _objectCache [ ref . nodeId . toString ( ) ] ; if ( ! reference ) { warningLog ( ref . nodeId . toString ( ) , "bn=" , ref . browseName . toString ( ) , "class =" , NodeClass [ ref . nodeClass ] , ref . typeDefinition . toString ( ) ) ; warningLog ( "Crawler: Cannot find reference" , ref . nodeId . toString ( ) , "in cache" ) ; warningLog ( "contact Sterfive's professional support for help to resolve" ) ; } if ( reference ) { reference . nodeClass = ( ref as any ) . $nodeClass ; } if ( referenceType ) { const refName = lowerFirstLetter ( referenceType ?. browseName ?. name || "" ) ; if ( refName === "hasTypeDefinition" ) { obj . typeDefinition = reference ?. browseName . name ; } else { if ( ! referenceMap [ refName ] ) { referenceMap [ refName ] = [ ] ; } this . _add_for_reconstruction ( queue , objMap , reference , ( err , mObject ? ) => { if ( ! err ) { referenceMap [ refName ] . push ( mObject ) ; } } ) ; } } } ) ; callback ( null , obj ) ; } } const thenify = require ( "thenify" ) ; NodeCrawler . prototype . read = thenify . withCallback ( NodeCrawler . prototype . read ) ;