APIs

Show:
"use strict";
/**
 * @module opcua.address_space.types
 */
var assert = require("node-opcua-assert");
var _ = require("underscore");
var util = require("util");

var BinaryStreamSizeCalculator = require("node-opcua-binary-stream").BinaryStreamSizeCalculator;
var hexDump = require("node-opcua-debug").hexDump;
var utils = require("node-opcua-utils");


var getFactory = require("./factories_factories").getFactory;
var _defaultTypeMap = require("./factories_builtin_types")._defaultTypeMap;
var get_base_schema = require("./factories_schema_helpers").get_base_schema;


/**
 * @class BaseUAObject
 * @constructor
 */
function BaseUAObject() {

}

/**
 * Encode the object to the binary stream.
 * @class BaseUAObject
 * @method encode
 * @param stream {BinaryStream}
 * @param options {BinaryStream}
 */
BaseUAObject.prototype.encode = function (/*stream,options*/) {

};

/**
 * Decode the object from the binary stream.
 * @class BaseUAObject
 * @method decode
 * @param stream {BinaryStream}
 * @param options {Object}
 */
BaseUAObject.prototype.decode = function (/*stream,options*/) {

};

/**
 * Calculate the required size to store this object in a binary stream.
 * @method binaryStoreSize
 * @return {Number}
 */
BaseUAObject.prototype.binaryStoreSize = function (options) {

    var stream = new BinaryStreamSizeCalculator();
    this.encode(stream, options);
    return stream.length;
};

/**
 * @method toString
 * @return {String}
 */
BaseUAObject.prototype.toString = function () {

    var self = this;

    if (self._schema && self._schema.hasOwnProperty("toString")) {

        return self._schema.toString.apply(self, arguments);

    } else {
        if (!self.explore) {
            console.log(util.inspect(self));
            return Object.prototype.toString.apply(self,arguments);
        }
        return self.explore.apply(self,arguments);
    }
};


/**
 *
 * verify that all object attributes values are valid according to schema
 * @method isValid
 * @return {Boolean}
 */
BaseUAObject.prototype.isValid = function () {
    assert(this._schema);
    if (this._schema.isValid) {
        return this._schema.isValid(this);
    } else {
        return true;
    }
};


function _decode_member_(value, field, stream, options) {

    var tracer = options.tracer;
    var cursor_before = stream.length;
    var fieldType = field.fieldType;

    if (field.category === "basic") {

        value = field.schema.decode(stream);
        tracer.trace("member", options.name, value, cursor_before, stream.length, fieldType);

    } else if (field.category === "enumeration") {

        value = field.schema.decode(stream);
        tracer.trace("member", options.name, value, cursor_before, stream.length, fieldType);

    } else {
        assert(field.category === "complex");
        assert(_.isFunction(field.schema));
        var Constructor = field.schema;
        value = new Constructor();
        value.decode_debug(stream, options);

    }
    return value;
}

/**
 * @method decode_debug
 *
 */
BaseUAObject.prototype.decode_debug = function (stream, options) {

    var tracer = options.tracer;
    var schema = this._schema;

    tracer.trace("start", options.name + "(" + schema.name + ")", stream.length, stream.length);
    var self = this;
    schema.fields.forEach(function (field) {

        var value = self[field.name];

        if (field.isArray) {

            var cursor_before = stream.length;
            var nb = stream.readUInt32();
            if (nb === 0xFFFFFFFF) {
                nb = 0;
            }
            options.name = field.name + [];

            tracer.trace("start_array", field.name, nb, cursor_before, stream.length);
            for (var i = 0; i < nb; i++) {
                tracer.trace("start_element", field.name, i);
                options.name = "element #" + i;

                _decode_member_(value, field, stream, options);

                tracer.trace("end_element", field.name, i);

            }
            tracer.trace("end_array", field.name, stream.length - 4);
        } else {

            options.name = field.name;
            _decode_member_(value, field, stream, options);

        }

    });

    tracer.trace("end", schema.name, stream.length, stream.length);
};


function r(str) {
    return (str + "                                ").substr(0, 30);
}

function apply_on_all_schema_fields(self, schema, data, callback , args) {

    assert(schema);
    var fields = schema.fields;
    var field, i, n = fields.length;
    for (i = 0; i < n; i++) {
        field = fields[i];
        callback(self, field, data , args);
    }
}

var _nb_elements = process.env.ARRAYLENGTH ? parseInt(process.env.ARRAYLENGTH) : 10;

function _array_ellypsis(value,field) {


    if (!value) {
        return "null []";
    } else {
        if (value.length===0) {
            return "[ /* empty*/ ]";
        }
        assert(_.isArray(value));
        var v = [];
        var m = Math.min(_nb_elements, value.length);
        for (var i = 0; i < m; i++) {
            var element = value[i];
            v.push( !utils.isNullOrUndefined(element) ? element.toString() : null);
        }
        return "[ " + v.join(",") + ( value.length > 10 ? " ... " : "") + "] (l=" + value.length + ")";
    }
}


var _exploreObject = function (self, field, data, args) {

    if (!self) {
        return;
    }
    assert(self);

    var fieldType = field.fieldType;
    var fieldName = field.name;
    var category = field.category;

    var padding = data.padding;

    var value = self[fieldName];

    var str;

    var fieldName_f = r(padding + fieldName, 30).yellow;
    var fieldType_f = ("/* " + r(fieldType, 10) + ( field.isArray ? "[]" : "  ") + " */").cyan;


    // compact version of very usual objects
    if (fieldType === "QualifiedName" && !field.isArray && value) {

        value = value.toString() || "<null>";
        str = fieldName_f + " " + fieldType_f + ": " + value.toString().green;
        data.lines.push(str);
        return;
    }
    if (fieldType === "LocalizedText" && !field.isArray && value) {
        value = value.toString() || "<null>";
        str = fieldName_f + " " + fieldType_f + ": " + value.toString().green;
        data.lines.push(str);
        return;
    }


    var _dump_simple_value = function (self, field, data, value, fieldType) {

        var str = "";
        if (value instanceof Buffer) {

            var _hexDump = hexDump(value);
            data.lines.push(fieldName_f + " " + fieldType_f);
            data.lines.push("BUFFER{" + _hexDump + "}");

        } else {


            if (field.isArray) {

                str = fieldName_f + " " + fieldType_f + ": " + _array_ellypsis(value,field);

            } else {
                if (fieldType === "IntegerId" || fieldType === "UInt32") {

                    value = "" + value + "               0x" + value.toString(16);

                } else if (fieldType === "DateTime" || fieldType === "UtcTime") {
                    value = (value && value.toISOString) ? value.toISOString() : value;
                } else if (typeof value === "object" && value !== null) {
                    value = value.toString.apply(value,args);
                }
                str = fieldName_f + " " + fieldType_f + ": " + ((value === null || value === undefined) ? "null".blue.bold : value.toString());
            }
            data.lines.push(str);
        }

    };

    switch (category) {

        case "enumeration":

            str = fieldName_f + " " + fieldType_f + ": " + value.key + " ( " + value.value + ")";
            data.lines.push(str);

            break;

        case "basic":
            _dump_simple_value(self, field, data, value, fieldType);
            break;

        case "complex":
            if (field.subtype) {

                // this is a synonymous
                fieldType = field.subType;
                _dump_simple_value(self, field, data, value, fieldType);

            } else {
                var _new_desc = getFactory(fieldType).prototype._schema;

                if (field.isArray) {
                    if (value === null) {
                        data.lines.push(fieldName_f + " " + fieldType_f + ": null []");
                    } else if (value.length === 0) {
                        data.lines.push(fieldName_f + " " + fieldType_f + ": [ /* empty */ ]");
                    } else {
                        data.lines.push(fieldName_f + " " + fieldType_f + ": [");

                        var i = 0;

                        var m = Math.min(_nb_elements, value.length);

                        for (i = 0; i < m; i++) {
                            var element = value[i];
                            data.lines.push(padding + "  { " + ("/*" + i + "*/").cyan);

                            var data1 = {padding: padding + "    ", lines: []};
                            apply_on_all_schema_fields(element, _new_desc, data1, _exploreObject , args);
                            data.lines = data.lines.concat(data1.lines);

                            data.lines.push(padding + "  }" + ((i === value.length - 1) ? "" : ","));
                        }
                        if (m < value.length) {
                            data.lines.push(padding + " ..... ( " + value.length + " elements )");
                        }
                        data.lines.push(padding + "]");

                    }

                } else {

                    data.lines.push(fieldName_f + " " + fieldType_f + ": {");

                    data1 = {padding: padding + "  ", lines: []};
                    apply_on_all_schema_fields(value, _new_desc, data1, _exploreObject,args);
                    data.lines = data.lines.concat(data1.lines);

                    data.lines.push(padding + "}");
                }
            }

            break;
        default:
            throw new Error("internal error: unknown kind_of_field");
    }
};


BaseUAObject.prototype.explore = function () {

    var self = this;
    var data = {padding: " ", lines: []};
    data.lines.push("{" + (" /*" + this._schema.name + "*/").cyan);
    apply_on_all_schema_fields(self, self._schema, data, _exploreObject,arguments);
    data.lines.push("};");
    return data.lines.join("\n");
};

function _visit_schema_chain(self, schema, options, func, extra_data) {
    assert(_.isFunction(func));

    // apply also construct to baseType schema first
    var base_schema = get_base_schema(schema);
    if (base_schema) {
        _visit_schema_chain(self, base_schema, options, func, extra_data);
    }
    func.call(self, schema, options, extra_data);
}

function _JSONify(schema, options) {

    /* jshint validthis: true */
    var self = this;
    schema.fields.forEach(function (field) {
        var f = self[field.name];
        if (f === null || f === undefined) {
            return;
        }

        var t = _defaultTypeMap[field.fieldType];

        function jsonify(value) {

            if (_.isFunction(field.toJSON)) {
                return field.toJSON(value);
            } else if (t && t.toJSON) {
                return t.toJSON(value);
            } else if (value.toJSON) {
                return value.toJSON();
            } else {
                return f;
            }

        }

        if (field.isArray) {
            options[field.name] = f.map(jsonify);
        } else {
            options[field.name] = jsonify(f);
        }
    });
}

BaseUAObject.prototype.toJSON = function () {

    var self = this;

    assert(this._schema);
    if (this._schema.toJSON) {
        return this._schema.toJSON.apply(this, arguments);
    } else {
        //xx return Object.toJSON.apply(this,arguments);
        assert(self._schema);
        var schema = self._schema;
        var options = {};
        _visit_schema_chain(self, schema, options, _JSONify);
        return options;
    }
};

BaseUAObject.prototype.clone = function (/*options,optionalfilter,extraInfo*/) {
    var self = this;

    var params = {};
    function construct_param(schema, options) {
        /* jshint validthis: true */
        var self = this;

        schema.fields.forEach(function (field) {

            var f = self[field.name];
            if (f === null || f === undefined) {
                return;
            }
            if (field.isArray) {
                options[field.name] = self[field.name];
            } else {
                options[field.name] = self[field.name];
            }
        });
    }
    construct_param.call(this,self._schema,params);

    return new self.constructor(params);

};


exports.BaseUAObject = BaseUAObject;