APIs

Show:
"use strict";

/**
 * @module opcua.address_space
 */

const assert = require("node-opcua-assert").assert;
const util = require("util");

const BaseNode = require("./base_node").BaseNode;
const NodeClass = require("node-opcua-data-model").NodeClass;
const AttributeIds = require("node-opcua-data-model").AttributeIds;
const DataValue =  require("node-opcua-data-value").DataValue;
const DataType = require("node-opcua-variant").DataType;
const StatusCodes = require("node-opcua-status-code").StatusCodes;

const SessionContext = require("./session_context").SessionContext;

const coerceLocalizedText = require("node-opcua-data-model").coerceLocalizedText;

let ReferenceTypeCounter=0;


/**
 * @class ReferenceType
 * @extends  BaseNode
 * @param options
 * @constructor
 */
function ReferenceType(options) {
    BaseNode.apply(this, arguments);

    this.isAbstract = util.isNullOrUndefined(options.isAbstract) ? false : !!options.isAbstract;
    this.symmetric = util.isNullOrUndefined(options.symmetric) ? false : !!options.symmetric;
    this.inverseName = coerceLocalizedText(options.inverseName);
    ReferenceTypeCounter +=1;
}

util.inherits(ReferenceType, BaseNode);
ReferenceType.prototype.nodeClass = NodeClass.ReferenceType;

/**
 *
 * @method readAttribute
 * @param attributeId {AttributeIds}
 * @param [indexRange {NumericalRange}]
 * @param [dataEncoding {String}]
 * @return {DataValue}
 */
ReferenceType.prototype.readAttribute = function (context, attributeId, indexRange, dataEncoding) {

    assert(context instanceof SessionContext);

    const options = {};
    switch (attributeId) {
        case AttributeIds.IsAbstract:
            options.value = {dataType: DataType.Boolean, value: !!this.isAbstract};
            options.statusCode = StatusCodes.Good;
            break;
        case AttributeIds.Symmetric:
            options.value = {dataType: DataType.Boolean, value: !!this.symmetric};
            options.statusCode = StatusCodes.Good;
            break;
        case AttributeIds.InverseName: // LocalizedText
            options.value = {dataType: DataType.LocalizedText, value: this.inverseName};
            options.statusCode = StatusCodes.Good;
            break;
        default:
            return BaseNode.prototype.readAttribute.call(this, context, attributeId);
    }
    return new DataValue(options);
};


const tools = require("./tool_isSupertypeOf");
/**
 * returns true if self is  a super type of baseType
 * @method isSupertypeOf
 * @param baseType {ReferenceType}
 * @return {Boolean}  true if self is a Subtype of baseType
 *
 *
 * @example
 *
 *
 */
ReferenceType.prototype.isSupertypeOf = tools.construct_isSupertypeOf(ReferenceType);

ReferenceType.prototype._slow_isSupertypeOf = tools.construct_slow_isSupertypeOf(ReferenceType);

ReferenceType.prototype.toString = function () {
    let str = "";
    str += this.isAbstract ? "A" : " ";
    str += this.symmetric ? "S" : " ";
    str += " " + this.browseName.toString() + "/" + this.inverseName.text + " ";
    str += this.nodeId.toString();
    return str;
};


function findAllSubTypes(referenceType) {

    const addressSpace = referenceType.addressSpace;
    const possibleReferenceTypes = [];

    const hasSubtypeReferenceType = addressSpace.findReferenceType("HasSubtype");
    function _findAllSubType(referenceType) {
        possibleReferenceTypes.push(referenceType);
        assert(referenceType.nodeClass === NodeClass.ReferenceType);
        const references = referenceType.findReferences(hasSubtypeReferenceType, true);
        for(let _r of references) {
            const subType = addressSpace.findNode(_r.nodeId);
            _findAllSubType(subType);
        }
    }
    _findAllSubType(referenceType);

    return possibleReferenceTypes;
}

/**
 * returns a array of all ReferenceTypes in the addressSpace that are self or a subType of self
 * @method getAllSubtypes
 * @return {ReferenceType[]}
 */
ReferenceType.prototype.getAllSubtypes = function() {

    const _cache = BaseNode._getCache(this);

    if (!_cache._allSubTypesVersion || _cache._allSubTypesVersion < ReferenceTypeCounter) {

        _cache._allSubTypes = null;
    }
    if (!_cache._allSubTypes) {
        _cache._allSubTypes = findAllSubTypes(this);
        _cache._allSubTypesVersion = ReferenceTypeCounter;
    }
    //xx console.log("XXX getAllSubtypes of ",this.browseName.toString()," => ", _.values(_cache._allSubTypes).map(x=>x.browseName.toString()).join(" "));
    return _cache._allSubTypes;
};


function _get_idx(referenceType)  {

    const possibleReferenceTypes = referenceType.getAllSubtypes();
    // create a index of reference type with browseName as key for faster search
    const keys = {};
    possibleReferenceTypes.forEach(function(refType){
        keys[refType.nodeId.toString()]= refType;
    });

    return keys;
}

/**
 * getSubtypeIndex
 * @returns {null|*}
 * @private
 */
ReferenceType.prototype.getSubtypeIndex = function() {
    const _cache = BaseNode._getCache(this);
    if (_cache._subtype_idxVersion < ReferenceTypeCounter) {
        // the cache need to be invalidated
        _cache._subtype_idx = null;
    } else {
    }
    if (!_cache._subtype_idx) {
        _cache._subtype_idx = _get_idx(this);
        _cache._subtype_idxVersion = ReferenceTypeCounter;
    }
    return _cache._subtype_idx;
};

ReferenceType.prototype.is = function(referenceTypeString) {
    assert(typeof referenceTypeString === "string");
    const referenceType = this.addressSpace.findReferenceType(referenceTypeString);
    assert(referenceType&& referenceType instanceof ReferenceType);
    this.getSubtypeIndex().hasOwnProperty(referenceType.referenceType.toString());
};

ReferenceType.prototype.install_extra_properties = function () {
    //
};

exports.ReferenceType = ReferenceType;