APIs

Show:
  1. "use strict";
  2. /**
  3. * @module opcua.client
  4. */
  5.  
  6.  
  7. const async = require("async");
  8. const assert = require("node-opcua-assert").assert;
  9. const _ = require("underscore");
  10. const util = require("util");
  11. const EventEmitter = require("events").EventEmitter;
  12.  
  13.  
  14. const read_service = require("node-opcua-service-read");
  15. const call_service = require("node-opcua-service-call");
  16.  
  17. const AttributeIds = require("node-opcua-data-model").AttributeIds;
  18. const AccessLevelFlag = require("node-opcua-data-model").AccessLevelFlag;
  19. const makeResultMask = require("node-opcua-data-model").makeResultMask;
  20. const BrowseDirection = require("node-opcua-data-model").BrowseDirection;
  21. const NodeClass = require("node-opcua-data-model").NodeClass;
  22. const makeNodeClassMask = require("node-opcua-data-model").makeNodeClassMask;
  23.  
  24. const ReferenceTypeIds = require("node-opcua-constants").ReferenceTypeIds;
  25. const ObjectTypeIds = require("node-opcua-constants").ObjectTypeIds;
  26.  
  27. const NodeId = require("node-opcua-nodeid").NodeId;
  28. const makeNodeId = require("node-opcua-nodeid").makeNodeId;
  29. const coerceNodeId = require("node-opcua-nodeid").coerceNodeId;
  30.  
  31. const StatusCodes = require("node-opcua-status-code").StatusCodes;
  32.  
  33. const lowerFirstLetter = require("node-opcua-utils").lowerFirstLetter;
  34.  
  35. const Variant = require("node-opcua-variant").Variant;
  36. const VariantArrayType = require("node-opcua-variant").VariantArrayType;
  37. const DataType = require("node-opcua-variant").DataType;
  38.  
  39.  
  40. const ClientSession = require("node-opcua-client").ClientSession;
  41. const ClientSubscription = require("node-opcua-client").ClientSubscription;
  42.  
  43. const resultMask = makeResultMask("ReferenceType | IsForward | BrowseName | NodeClass | TypeDefinition");
  44.  
  45. function makeRefId(referenceTypeName) {
  46. const nodeId = makeNodeId(ReferenceTypeIds[referenceTypeName] || ObjectTypeIds[referenceTypeName]);
  47.  
  48. // istanbul ignore next
  49. if (nodeId.isEmpty()) {
  50. throw new Error("makeRefId: cannot find ReferenceTypeName + ", referenceTypeName);
  51. }
  52. return nodeId;
  53. }
  54.  
  55. exports.makeRefId = makeRefId;
  56.  
  57. /**
  58. * @method convertNodeIdToDataTypeAsync
  59. *
  60. * @param session {ClientSession}
  61. * @param dataTypeId {NodeId}
  62. * @param callback {Function}
  63. * @param callback.err {Error|null}
  64. * @param callback.dataType {DataType}
  65. *
  66. * @example
  67. *
  68. * var dataTypeId ="ns=0;i=11"; // Double
  69. * convertNodeIdToDataTypeAsync(session,dataTypeId,function(err,dataType) {
  70. * assert(!err && dataType === DataType.Double);
  71. * });
  72. *
  73. * var dataTypeId ="ns=0;i=290"; // Duration => SubTypeOf Double
  74. * convertNodeIdToDataTypeAsync(session,dataTypeId,function(err,dataType) {
  75. * assert(!err && dataType === DataType.Double);
  76. * });
  77. *
  78. * see also AddressSpace#findCorrespondingBasicDataType
  79. */
  80. function convertNodeIdToDataTypeAsync(session, dataTypeId, callback) {
  81.  
  82. const nodeToRead = {
  83. nodeId: dataTypeId,
  84. attributeId: AttributeIds.BrowseName
  85. };
  86.  
  87. session.read(nodeToRead, function (err, dataValue) {
  88.  
  89. // istanbul ignore next
  90. if (err) {
  91. return setImmediate(function(){callback(err);});
  92. }
  93.  
  94. let dataType;
  95. // istanbul ignore next
  96. if (dataValue.statusCode !== StatusCodes.Good) {
  97. //Xx console.log("convertNodeIdToDataTypeAsync: Cannot read browse name for nodeID ".red + dataTypeId.toString());
  98. dataType = DataType.Null;
  99. return setImmediate(function(){callback(null, dataType);});
  100. }
  101.  
  102. const dataTypeName = dataValue.value.value;
  103.  
  104. if (dataTypeId.namespace === 0 && DataType.get(dataTypeId.value)) {
  105. dataType = DataType.get(dataTypeId.value);
  106. return setImmediate(function(){callback(null, dataType);});
  107. }
  108.  
  109. /// example => Duration (i=290) => Double (i=11)
  110. // read subTypeOf
  111. const nodeToBrowse = {
  112. // BrowseDescription
  113. referenceTypeId: makeRefId("HasSubtype"),
  114. //xx nodeClassMask: makeNodeClassMask("ObjectType"),
  115. includeSubtypes: false,
  116. browseDirection: BrowseDirection.Inverse,
  117. nodeId: dataTypeId,
  118. resultMask: resultMask
  119. };
  120. session.browse(nodeToBrowse, function (err, browseResult) {
  121. // istanbul ignore next
  122. if (err) {
  123. return callback(err);
  124. }
  125.  
  126. const references = browseResult.references;
  127.  
  128. if (!references || references.length !== 1) {
  129. return callback(new Error("cannot find SuperType of " + dataTypeName.toString()));
  130. }
  131. const nodeId = references[0].nodeId;
  132. return convertNodeIdToDataTypeAsync(session, nodeId, callback);
  133. });
  134. });
  135. }
  136.  
  137. function convertNodeIdToDataType(dataTypeId) {
  138. return dataTypeId._dataType;
  139. }
  140.  
  141. function ProxyBaseNode(proxyManager, nodeId) {
  142.  
  143. const self = this;
  144. /**
  145. * the object nodeId
  146. * @property nodeId
  147. * @type {NodeId}
  148. */
  149. self.nodeId = nodeId;
  150.  
  151. self.proxyManager = proxyManager;
  152. assert(self.proxyManager.session, "expecting valid session");
  153. Object.defineProperty(self, "proxyManager", {
  154. enumerable: false,
  155. writable: true
  156. });
  157. /**
  158. * the object's components
  159. * @property $components
  160. * @type {Array<ProxyBaseNode>}
  161. */
  162. self.$components = [];
  163.  
  164. /**
  165. * the object's properties
  166. * @property $properties
  167. * @type {Array<ProxyBaseNode>}
  168. */
  169. self.$properties = [];
  170.  
  171. /**
  172. * the object's properties
  173. * @property $methods
  174. * @type {Array<ProxyBaseNode>}
  175. */
  176. self.$methods = [];
  177.  
  178. /**
  179. * the Folder's elements
  180. * @property $organizes
  181. * @type {Array<ProxyBaseNode>}
  182. */
  183. self.$organizes = [];
  184.  
  185. /**
  186. * the object's description
  187. * @property description
  188. * @type {String}
  189. */
  190. self.description = "";
  191. /**
  192. * the object's browseName
  193. * @property browseName
  194. * @type {String}
  195. */
  196. self.browseName = "";
  197. /**
  198. * the object's NodeClass
  199. * @property nodeClass
  200. * @type {NodeClass}
  201. */
  202. self.nodeClass = null;
  203. }
  204.  
  205. util.inherits(ProxyBaseNode, EventEmitter);
  206.  
  207.  
  208. /**
  209. * get a updated Value of the Variable , by using a ReadRequest
  210. * @method readValue
  211. * @param callback {Function}
  212. * @param callback.err {Error|null}
  213. * @param callback.dataValue {DataValue}
  214. */
  215. ProxyBaseNode.prototype.readValue = function (callback) {
  216.  
  217. const self = this;
  218. assert(self.proxyManager);
  219.  
  220. const session = self.proxyManager.session;
  221. assert(session);
  222.  
  223. const nodeToRead = {
  224. nodeId: self.nodeId,
  225. attributeId: AttributeIds.Value
  226. };
  227. self.proxyManager.session.read(nodeToRead, function (err, dataValue) {
  228.  
  229. // istanbul ignore next
  230. if (err) {
  231. return callback(err);
  232. }
  233. const data = dataValue.value;
  234. callback(null, data);
  235.  
  236. });
  237. };
  238.  
  239.  
  240. /**
  241. * set the Value of the Variable, by using a WriteRequest
  242. * @method writeValue
  243. * @param dataValue {DataValue}
  244. * @param callback {Function}
  245. * @param callback.err {Error|null}
  246. */
  247. ProxyBaseNode.prototype.writeValue = function (dataValue, callback) {
  248. const self = this;
  249. assert(self.proxyManager);
  250.  
  251. const session = self.proxyManager.session;
  252. assert(session);
  253.  
  254. const nodeToWrite = {
  255. nodeId: self.nodeId,
  256. attributeId: AttributeIds.Value,
  257. value: dataValue
  258. };
  259. self.proxyManager.session.write(nodeToWrite, function (err, statusCode) {
  260. // istanbul ignore next
  261. if (err) {
  262. return callback(err);
  263. }
  264. if (statusCode !== StatusCodes.Good) {
  265. callback(new Error(statusCode.toString()));
  266. } else {
  267. callback(null);
  268. }
  269. });
  270. };
  271.  
  272. ProxyBaseNode.prototype.toString = function () {
  273.  
  274. const str = [];
  275. const self = this;
  276. str.push(" ProxyObject ");
  277. str.push(" browseName : " + self.browseName.toString());
  278. str.push(" typeDefinition : " + self.typeDefinition.toString());
  279. str.push(" $components# : " + self.$components.length.toString());
  280. str.push(" $properties# : " + self.$properties.length.toString());
  281.  
  282. return str.join("\n");
  283. };
  284.  
  285. function ProxyVariable(session, nodeId) {
  286. ProxyBaseNode.apply(this, arguments);
  287. }
  288.  
  289. util.inherits(ProxyVariable, ProxyBaseNode);
  290.  
  291. const ProxyObject = ProxyVariable;
  292.  
  293. function ObjectExplorer(options) {
  294. const self = this;
  295. self.proxyManager = options.proxyManager;
  296. self.name = options.name;
  297. self.nodeId = options.nodeId;
  298. self.parent = options.parent;
  299. }
  300.  
  301. ObjectExplorer.prototype.$resolve = function (callback) {
  302.  
  303. const self = this;
  304.  
  305. self.proxyManager.getObject(self.nodeId, function (err, childObj) {
  306.  
  307. // istanbul ignore next
  308. if (err) {
  309. return callback(err);
  310. }
  311.  
  312. self.parent[self.name] = childObj;
  313. self.parent.$components.push(childObj);
  314.  
  315. callback(null)
  316. });
  317. };
  318.  
  319. function readUAStructure(proxyManager, obj, callback) {
  320.  
  321. const session = proxyManager.session;
  322.  
  323. // 0 Object
  324. // 1 Variable
  325. // 2 Method
  326. const nodeId = obj.nodeId;
  327. const nodesToBrowse = [
  328.  
  329. // Components (except Methods)
  330. {
  331. // BrowseDescription
  332. referenceTypeId: makeRefId("HasComponent"),
  333. nodeClassMask: makeNodeClassMask("Object | Variable"), // we don't want Method here
  334. includeSubtypes: true,
  335. browseDirection: BrowseDirection.Forward,
  336. nodeId: nodeId,
  337. resultMask: resultMask
  338. },
  339. // Properties
  340. {
  341. // BrowseDescription
  342. referenceTypeId: makeRefId("HasProperty"),
  343. //nodeClassMask: makeNodeClassMask("Variable"),
  344. includeSubtypes: true,
  345. browseDirection: BrowseDirection.Forward,
  346. nodeId: nodeId,
  347. resultMask: resultMask
  348. },
  349.  
  350. // Methods
  351. {
  352. // BrowseDescription
  353. referenceTypeId: makeRefId("HasComponent"),
  354. nodeClassMask: makeNodeClassMask("Method"),
  355. includeSubtypes: true,
  356. browseDirection: BrowseDirection.Forward,
  357. nodeId: nodeId,
  358. resultMask: resultMask
  359. },
  360. // TypeDefinition
  361. {
  362. // BrowseDescription
  363. referenceTypeId: makeRefId("HasTypeDefinition"),
  364. includeSubtypes: true,
  365. browseDirection: BrowseDirection.Both,
  366. nodeId: nodeId,
  367. resultMask: resultMask
  368.  
  369. },
  370. // FromState
  371. {
  372. // BrowseDescription
  373. referenceTypeId: makeRefId("FromState"),
  374. includeSubtypes: true,
  375. browseDirection: BrowseDirection.Forward,
  376. nodeId: nodeId,
  377. resultMask: resultMask
  378. },
  379. // ToState
  380. {
  381. // BrowseDescription
  382. referenceTypeId: makeRefId("ToState"),
  383. includeSubtypes: true,
  384. browseDirection: BrowseDirection.Forward,
  385. nodeId: nodeId,
  386. resultMask: resultMask
  387. },
  388. // (for folders ) Organizes
  389. {
  390. // BrowseDescription
  391. referenceTypeId: makeRefId("Organizes"),
  392. includeSubtypes: true,
  393. browseDirection: BrowseDirection.Forward,
  394. nodeId: nodeId,
  395. resultMask: resultMask
  396. }
  397. ];
  398.  
  399.  
  400. /**
  401. * @method add_method
  402. * construct a callable method
  403. *
  404. * @param obj
  405. * @param reference
  406. * @param callback
  407. * @private
  408. */
  409. function add_method(obj, reference, callback) {
  410.  
  411. const name = lowerFirstLetter(reference.browseName.name);
  412.  
  413. obj[name] = function functionCaller(inputArgs, callback) {
  414.  
  415. assert(_.isFunction(callback));
  416. // convert input arguments into Variants
  417. const inputArgsDef = obj[name].inputArguments || [];
  418.  
  419. const inputArguments = inputArgsDef.map(function (arg) {
  420.  
  421. const dataType = convertNodeIdToDataType(arg.dataType);
  422.  
  423. const arrayType = (arg.valueRank === 1) ? VariantArrayType.Array : VariantArrayType.Scalar;
  424.  
  425. //xx console.log("xxx ",arg.toString());
  426. const propName = lowerFirstLetter(arg.name);
  427.  
  428. const value = inputArgs[propName];
  429. if (value === undefined) {
  430. throw new Error("expecting input argument " + propName);
  431. }
  432. if (arrayType === VariantArrayType.Array) {
  433. if (!_.isArray(value)) {
  434. throw new Error("expecting value to be an Array or a TypedArray");
  435. }
  436. }
  437.  
  438. return new Variant({arrayType: arrayType, dataType: dataType, value: value});
  439. });
  440.  
  441. const methodToCall = new call_service.CallMethodRequest({
  442. objectId: obj.nodeId,
  443. methodId: reference.nodeId,
  444. inputArguments: inputArguments
  445. });
  446.  
  447. //xx console.log(" calling ",methodToCall.toString());
  448.  
  449. session.call(methodToCall, function (err, callResult) {
  450.  
  451. // istanbul ignore next
  452. if (err) {
  453. return callback(err);
  454. }
  455. if (callResult.statusCode !== StatusCodes.Good) {
  456. return callback(new Error("Error " + callResult.statusCode.toString()));
  457. }
  458. callResult.outputArguments = callResult.outputArguments || [];
  459.  
  460. obj[name].outputArguments = obj[name].outputArguments || [];
  461.  
  462. assert(callResult.outputArguments.length === obj[name].outputArguments.length);
  463. const outputArgs = {};
  464.  
  465. const outputArgsDef = obj[name].outputArguments;
  466.  
  467. _.zip(outputArgsDef, callResult.outputArguments).forEach(function (pair) {
  468. const arg = pair[0];
  469. const variant = pair[1];
  470.  
  471. const propName = lowerFirstLetter(arg.name);
  472. outputArgs[propName] = variant.value;
  473.  
  474. });
  475. callback(err, outputArgs);
  476.  
  477. });
  478. };
  479.  
  480.  
  481. function extractDataType(arg, callback) {
  482.  
  483. if (arg.dataType && arg.dataType._dataType) {
  484. return setImmediate(callback); // already converted
  485. }
  486.  
  487. convertNodeIdToDataTypeAsync(session, arg.dataType, function (err, dataType) {
  488. if (!err) {
  489. assert(dataType.hasOwnProperty("value"));
  490. arg.dataType._dataType = dataType;
  491. }
  492. callback(err);
  493. });
  494. }
  495.  
  496. const methodObj = {
  497. nodeId: reference.nodeId,
  498. executableFlag: false,
  499. browseName: name,
  500. func: obj[name]
  501. };
  502. obj.$methods[name] = methodObj;
  503.  
  504. async.parallel([
  505. function(callback) {
  506. session.getArgumentDefinition(reference.nodeId, function (err, args) {
  507. // istanbul ignore next
  508. if (err) {
  509. return setImmediate(function() { callback(err); });
  510. }
  511. const inputArguments = args.inputArguments;
  512. const outputArguments = args.outputArguments;
  513.  
  514. obj[name].inputArguments = inputArguments;
  515. obj[name].outputArguments = outputArguments;
  516.  
  517. async.series([
  518. function (callback) {
  519. async.each(obj[name].inputArguments, extractDataType, callback);
  520. },
  521. function (callback) {
  522. async.each(obj[name].outputArguments, extractDataType, callback);
  523. }
  524. ], callback)
  525. });
  526. },
  527. function(callback) {
  528. proxyManager._monitor_execution_flag(methodObj, function () {
  529. callback();
  530. });
  531. }
  532. ],callback);
  533.  
  534. }
  535.  
  536. function add_component(obj, reference, callback) {
  537.  
  538. const name = lowerFirstLetter(reference.browseName.name || "");
  539.  
  540. proxyManager.getObject(reference.nodeId, function (err, childObj) {
  541.  
  542. // istanbul ignore else
  543. if (!err) {
  544. childObj = new ObjectExplorer({
  545. proxyManager: proxyManager,
  546. nodeId: reference.nodeId,
  547. name: name,
  548. parent: obj
  549. });
  550. obj[name] = childObj;
  551. obj.$components.push(childObj);
  552.  
  553. childObj.$resolve(callback);
  554. } else {
  555. callback(err);
  556. }
  557. });
  558. }
  559.  
  560. function addFolderElement(obj, reference, callback) {
  561.  
  562. const name = lowerFirstLetter(reference.browseName.name || "");
  563.  
  564. const childObj = new ObjectExplorer({
  565. proxyManager: proxyManager,
  566. nodeId: reference.nodeId,
  567. name: name,
  568. parent: obj
  569. });
  570. obj[name] = childObj;
  571. obj.$organizes.push(childObj);
  572. childObj.$resolve(callback);
  573. }
  574.  
  575. function add_property(obj, reference, callback) {
  576.  
  577. const name = lowerFirstLetter(reference.browseName.name || "");
  578.  
  579. obj[name] = new ProxyVariable(proxyManager, reference.nodeId, reference);
  580. obj.$properties[name] = obj[name];
  581.  
  582. setImmediate(callback);
  583. }
  584.  
  585. function add_typeDefinition(obj, references, callback) {
  586. references = references || [];
  587. if (references.length !== 1) {
  588. //xx console.log(" cannot find type definition", references.length);
  589. return setImmediate(callback);
  590. }
  591. const reference = references[0];
  592. assert(!obj.typeDefinition, "type definition can only be set once");
  593. obj.typeDefinition = reference.browseName.name || "";
  594. setImmediate(callback);
  595. }
  596.  
  597. function addFromState(obj, reference, callback) {
  598. proxyManager.getObject(reference.nodeId, function (err, childObj) {
  599. obj.$fromState = childObj;
  600. callback(err);
  601. });
  602. }
  603.  
  604. function addToState(obj, reference, callback) {
  605. proxyManager.getObject(reference.nodeId, function (err, childObj) {
  606. obj.$toState = childObj;
  607. callback(err);
  608. });
  609. }
  610.  
  611.  
  612. session.browse(nodesToBrowse, function (err, browseResults) {
  613. function t(references) {
  614. return references.map(function (r) {
  615. return r.browseName.name + " " + r.nodeId.toString();
  616. });
  617. }
  618.  
  619. // istanbul ignore next
  620. if (err) {
  621. return callback(err);
  622. }
  623.  
  624. //xx console.log("Components", t(results[0].references));
  625. //xx console.log("Properties", t(results[1].references));
  626. //xx console.log("Methods", t(results[2].references));
  627. async.parallel([
  628.  
  629. function (callback) {
  630. async.map(browseResults[0].references, add_component.bind(null, obj), callback);
  631. },
  632.  
  633. function (callback) {
  634. async.map(browseResults[1].references, add_property.bind(null, obj), callback);
  635. },
  636.  
  637. // now enrich our object with nice callable async methods
  638. function (callback) {
  639. async.map(browseResults[2].references, add_method.bind(null, obj), callback);
  640. },
  641.  
  642. // now set typeDefinition
  643. function (callback) {
  644. add_typeDefinition.bind(null, obj)(browseResults[3].references, callback);
  645. },
  646.  
  647. //
  648. function (callback) { // FromState
  649. // fromState
  650. const reference = browseResults[4].references ? browseResults[4].references[0] : null;
  651. // fromState
  652. if (reference) {
  653. return addFromState(obj, reference, callback);
  654. }
  655. callback();
  656. },
  657. function (callback) { // ToState
  658. const reference = browseResults[5].references ? browseResults[5].references[0] : null;
  659. // fromState
  660. if (reference) {
  661. return addToState(obj, reference, callback);
  662. }
  663. callback();
  664. },
  665. function (callback) { // Organizes
  666. async.map(browseResults[6].references, addFolderElement.bind(null, obj), callback);
  667. }
  668.  
  669. ], callback);
  670.  
  671. });
  672. }
  673.  
  674. /**
  675. * @method getObject
  676. *
  677. * @param proxyManager
  678. * @param nodeId
  679. * @param options
  680. * @param callback {Function}
  681. * @param callback.err
  682. * @param callback.clientObject
  683. *
  684. */
  685. function getObject(proxyManager, nodeId, options, callback) {
  686.  
  687. const session = proxyManager.session;
  688.  
  689. nodeId = coerceNodeId(nodeId);
  690.  
  691. if (nodeId.isEmpty()) {
  692. return setImmediate(function() {
  693. callback(new Error(" Invalid empty node in getObject"));
  694. });
  695. }
  696.  
  697. const nodesToRead = [
  698. {
  699. nodeId: nodeId,
  700. attributeId: AttributeIds.BrowseName
  701. },
  702. {
  703. nodeId: nodeId,
  704. attributeId: AttributeIds.Description
  705. },
  706. {
  707. nodeId: nodeId,
  708. attributeId: AttributeIds.NodeClass
  709. }
  710. ];
  711.  
  712. function read_accessLevels(clientObject, callback) {
  713. const nodesToRead = [
  714. {
  715. nodeId: nodeId,
  716. attributeId: AttributeIds.Value
  717. },
  718. {
  719. nodeId: nodeId,
  720. attributeId: AttributeIds.UserAccessLevel
  721. },
  722. {
  723. nodeId: nodeId,
  724. attributeId: AttributeIds.AccessLevel
  725. }
  726. ];
  727. session.read(nodesToRead, 1, function (err, dataValues) {
  728. if (dataValues[0].statusCode === StatusCodes.Good) {
  729. clientObject.dataValue = AccessLevelFlag.get(dataValues[0].value);
  730. }
  731. if (dataValues[1].statusCode === StatusCodes.Good) {
  732. //xx console.log("AccessLevel ", results[3].value.toString())
  733. clientObject.userAccessLevel = AccessLevelFlag.get(dataValues[1].value.value);
  734. }
  735. if (dataValues[2].statusCode === StatusCodes.Good) {
  736. clientObject.accessLevel = AccessLevelFlag.get(dataValues[2].value.value);
  737. }
  738. callback(err);
  739. });
  740. }
  741.  
  742. let clientObject;
  743.  
  744. async.series([
  745.  
  746. function (callback) {
  747. // readAttributes like browseName and references
  748. session.read(nodesToRead, 1, function (err, dataValues) {
  749.  
  750. if (!err) {
  751.  
  752. if (dataValues[0].statusCode === StatusCodes.BadNodeIdUnknown) {
  753. //xx console.log(" INVALID NODE ", nodeId.toString());
  754. return callback(new Error("Invalid Node " + nodeId.toString()));
  755. }
  756.  
  757. clientObject = new ProxyObject(proxyManager, nodeId);
  758.  
  759. ///x console.log("xxxx ,s",results.map(function(a){ return a.toString();}));
  760.  
  761. clientObject.browseName = dataValues[0].value.value;
  762. clientObject.description = (dataValues[1].value ? dataValues[1].value.value : "");
  763. clientObject.nodeClass = NodeClass.get(dataValues[2].value.value);
  764. //xx console.log("xxx nodeClass = ",clientObject.nodeClass.toString());
  765.  
  766. if (clientObject.nodeClass === NodeClass.Variable) {
  767. return read_accessLevels(clientObject, callback);
  768. }
  769. }
  770. callback(err);
  771. });
  772. },
  773. function (callback) {
  774.  
  775. // install monitored item
  776. if (clientObject.nodeClass === NodeClass.Variable) {
  777. //xx console.log("xxxx -> ???", clientObject.nodeId.toString(), clientObject.nodeClass.toString());
  778. return proxyManager._monitor_value(clientObject, callback);
  779. }
  780. callback();
  781. },
  782.  
  783.  
  784. function (callback) {
  785.  
  786. readUAStructure(proxyManager, clientObject, callback);
  787. }
  788.  
  789. //
  790. ], function (err) {
  791.  
  792. // istanbul ignore next
  793. if (err) {
  794. return callback(err);
  795. }
  796.  
  797. callback(null, clientObject);
  798. });
  799. }
  800.  
  801.  
  802. /**
  803. * @class UAProxyManager
  804. * @param session
  805. * @constructor
  806. */
  807. function UAProxyManager(session) {
  808.  
  809. const self = this;
  810. self.session = session;
  811. assert(session instanceof ClientSession);
  812. self._map = {};
  813. // create a subscription
  814.  
  815. }
  816.  
  817.  
  818. /**
  819. * @class ClientSession
  820. * @method createSubscription2
  821. * @param createSubscriptionRequest
  822. * @param callback
  823. * @param callback.err
  824. * @param callback.subscription
  825. *
  826. *
  827. * subscription.on("error', function(err){ ... });
  828. * subscription.on("terminate',function(err){ ... });
  829. * var monitoredItem = subscription.monitor(itemToMonitor,monitoringParameters,requestedParameters,callback);
  830. * monitoredItem.on("changed",function( dataValue) {...});
  831. *
  832. */
  833. ClientSession.prototype.createSubscription2 = function (createSubscriptionRequest, callback) {
  834. assert(_.isFunction(callback));
  835. const self = this;
  836.  
  837. const subscription = new ClientSubscription(self, createSubscriptionRequest);
  838. subscription.on("error", function (err) {
  839.  
  840. });
  841. subscription.on("started", function () {
  842. callback(null, subscription);
  843. });
  844. };
  845.  
  846. /**
  847. * @class UAProxyManager
  848. * @method start
  849. * @param callback
  850. * @async
  851. */
  852. UAProxyManager.prototype.start = function (callback) {
  853. const self = this;
  854.  
  855. const createSubscriptionRequest = {
  856. requestedPublishingInterval: 100,
  857. requestedLifetimeCount: 6000,
  858. requestedMaxKeepAliveCount: 100,
  859. maxNotificationsPerPublish: 1000,
  860. publishingEnabled: true,
  861. priority: 10
  862. };
  863.  
  864. self.session.createSubscription2(createSubscriptionRequest, function (err, subscription) {
  865. self.subscription = subscription;
  866. self.subscription.on("terminated", function () {
  867. self.subscription = null;
  868. });
  869. callback(err);
  870. });
  871. };
  872.  
  873. /**
  874. * @method stop
  875. * @param callback
  876. * @async
  877. */
  878. UAProxyManager.prototype.stop = function (callback) {
  879. const self = this;
  880. if (self.subscription) {
  881. self.subscription.terminate(function() {
  882. self.subscription = null;
  883. callback();
  884. });
  885.  
  886. } else {
  887. callback(new Error("UAProxyManager already stopped ?"))
  888. }
  889. };
  890.  
  891. /**
  892. * @method getObject
  893. * @param nodeId
  894. * @param callback
  895. * @async
  896. */
  897. UAProxyManager.prototype.getObject = function (nodeId, callback) {
  898.  
  899. let options = {};
  900. const self = this;
  901.  
  902. setImmediate(function () {
  903. options = options || {};
  904. options.depth = options.depth || 1;
  905.  
  906. const key = nodeId.toString();
  907.  
  908. if (self._map.hasOwnProperty(key)) {
  909. return callback(null, self._map[key]);
  910. }
  911.  
  912. getObject(self, nodeId, options, function (err, obj) {
  913.  
  914. if (!err) {
  915. self._map[key] = obj;
  916. }
  917. callback(err, obj);
  918. });
  919. });
  920. };
  921.  
  922.  
  923. UAProxyManager.prototype._monitor_value = function (proxyObject, callback) {
  924.  
  925. assert(_.isFunction(callback));
  926. //xx if (proxyObject.nodeId.toString() !== "ns=0;i=2257") { return;}
  927.  
  928. const self = this;
  929.  
  930. if (!self.subscription) {
  931. // some server do not provide subscription support, do not treat this as an error.
  932. return callback(null); // new Error("No subscription"));
  933. }
  934.  
  935. const itemToMonitor = { // ReadValueId
  936. nodeId: proxyObject.nodeId,
  937. attributeId: AttributeIds.Value
  938. };
  939. const monitoringParameters = { // MonitoringParameters
  940. samplingInterval: 0, /* event-based */
  941. discardOldest: true,
  942. queueSize: 10
  943. };
  944. const requestedParameters = read_service.TimestampsToReturn.Both;
  945.  
  946. const monitoredItem = self.subscription.monitor(itemToMonitor, monitoringParameters, requestedParameters, callback);
  947.  
  948. // console.log("xxxxxx installing monitored Item",monitoredItem.itemToMonitor.nodeId.toString(),monitoredItem.itemToMonitor.attributeId);
  949. //xx proxyObject.monitoredItem = monitoredItem;
  950. Object.defineProperty(proxyObject, "__monitoredItem", {value: monitoredItem, enumerable: false});
  951.  
  952. proxyObject.__monitoredItem.on("changed", function (dataValue) {
  953. proxyObject.dataValue = dataValue;
  954. proxyObject.emit("value_changed", dataValue);
  955. //xx console.log("xxx Value Changed ".red,proxyObject.nodeId.toString() , proxyObject.browseName,proxyObject.dataValue.toString());
  956. });
  957.  
  958. };
  959.  
  960. UAProxyManager.prototype._monitor_execution_flag = function (proxyObject, callback) {
  961.  
  962. // note : proxyObject must wrap a method
  963. assert(_.isFunction(callback));
  964. assert(proxyObject.nodeId instanceof NodeId);
  965.  
  966. const self = this;
  967.  
  968. if (!self.subscription) {
  969. // some server do not provide subscription support, do not treat this as an error.
  970. return callback(null); // new Error("No subscription"));
  971. }
  972.  
  973. const itemToMonitor = { // ReadValueId
  974. nodeId: proxyObject.nodeId,
  975. attributeId: AttributeIds.Executable
  976. };
  977.  
  978. const monitoringParameters = { // MonitoringParameters
  979. samplingInterval: 0, /* event-based */
  980. discardOldest: true,
  981. queueSize: 10
  982. };
  983. const requestedParameters = read_service.TimestampsToReturn.None;
  984.  
  985. const monitoredItem = self.subscription.monitor(itemToMonitor, monitoringParameters, requestedParameters, callback);
  986.  
  987. Object.defineProperty(proxyObject, "__monitoredItem_execution_flag", {value: monitoredItem, enumerable: false});
  988.  
  989. proxyObject.__monitoredItem_execution_flag.on("changed", function (dataValue) {
  990. proxyObject.executableFlag = dataValue.value.value;
  991. //xx console.log(" execution flag = ", proxyObject.executableFlag , proxyObject.browseName , proxyObject.nodeId.toString());
  992. //xx proxyObject.emit("execution_flag_changed",proxyObject.executableFlag);
  993. });
  994. };
  995.  
  996. exports.UAProxyManager = UAProxyManager;
  997.  
  998. const thenify = require("thenify");
  999. UAProxyManager.prototype.start = thenify.withCallback(UAProxyManager.prototype.start);
  1000. UAProxyManager.prototype.stop = thenify.withCallback(UAProxyManager.prototype.stop);
  1001.  
  1002. UAProxyManager.prototype.getObject = thenify.withCallback(UAProxyManager.prototype.getObject);
  1003. ProxyBaseNode.prototype.readValue = thenify.withCallback(ProxyBaseNode.prototype.readValue);
  1004. ProxyBaseNode.prototype.writeValue = thenify.withCallback(ProxyBaseNode.prototype.writeValue);
  1005.