APIs

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

const colors = require("colors");

const BinaryStream = require("node-opcua-binary-stream").BinaryStream;
const Enum = require("node-opcua-enum");
const hexDump = require("node-opcua-debug").hexDump;
const buffer_ellipsis = require("node-opcua-utils").buffer_ellipsis;

const ec = require("node-opcua-basic-types");


const spaces = "                                                                                                                                                                             ";
function f(n, width) {
    const s = n.toString();
    return (s + "      ").substr(0, Math.max(s.length, width));
}

function display_encoding_mask(padding, encoding_mask, encoding_info) {
    assert(encoding_info instanceof Enum);
    let bits = [];
    encoding_info.enums.forEach(function (enumValue) {
        const mask = enumValue.value;
        const bit = Math.log(mask) / Math.log(2);
        bits = [".", ".", ".", ".", ".", ".", ".", ".", "."];
        bits[bit] = ((encoding_mask & mask) === mask) ? "Y" : "n";

        console.log(padding + " ", bits.join(""), " <- has " + enumValue.key);
    });
    // DataValueEncodingByte
}

function hex_block(start, end, buffer) {
    const n = end - start;
    const strBuf = buffer_ellipsis(buffer);
    return "s:".cyan + f(start, 4) + " e:".cyan + f(end, 4) + " n:".cyan + f(n, 4) + " " + strBuf.yellow;
}


function make_tracer(buffer, padding, offset) {

    padding = padding || 0;
    offset = offset || 0;

    const pad = function () {
        return "                                                       ".substr(0, padding);
    };

    function display(str, hex_info) {
        hex_info = hex_info || "";

        // account for ESC codes for colors
        const nbColorAttributes = _.filter(str, function (c) {
            return c === "\u001b";
        }).length;
        const extra = nbColorAttributes * 5;
        console.log((pad() + str + spaces).substr(0, 132 + extra) + "|" + hex_info);
    }

    function display_encodeable(value, buffer, start, end) {
        const ext_buf = buffer.slice(start, end);
        const stream = new BinaryStream(ext_buf);
        const nodeId = ec.decodeNodeId(stream);
        const encodingMask = ec.decodeByte(stream); // 1 bin 2: xml
        const length = ec.decodeUInt32(stream);

        display("     ExpandedNodId =".green + " " + nodeId);
        display("     encoding mask =".green + " " + encodingMask);
        display("            length =".green + " " + length);
        packet_analyzer(ext_buf.slice(stream.length), value.encodingDefaultBinary, padding + 2, start + stream.length);

    }

    const options = {

        tracer: {

            dump: function (title, value) {
                display(title + "  " + value.toString().green);
            },

            encoding_byte: function (encoding_mask, valueEnum, start, end) {
                const b = buffer.slice(start, end);
                display("  012345678", hex_block(start, end, b));
                display_encoding_mask(pad(), encoding_mask, valueEnum);
            },

            trace: function (operation, name, value, start, end, fieldType) {

                const b = buffer.slice(start, end);
                let _hexDump="";

                switch (operation) {

                    case "start":
                        padding += 2;
                        display(name.toString());
                        break;

                    case "end":
                        padding -= 2;
                        break;

                    case "start_array":
                        display("." + name + " (length = " + value + ") " + "[", hex_block(start, end, b));
                        padding += 2;
                        break;

                    case "end_array":
                        padding -= 2;
                        display("] // " + name);
                        break;

                    case "start_element":
                        display(" #" + value + " {");
                        padding += 2;
                        break;

                    case "end_element":
                        padding -= 2;
                        display(" } // # " + value);
                        break;

                    case "member":
                        display("." + name + " : " + fieldType);

                        _hexDump = "";
                        if (value instanceof Buffer) {
                            _hexDump = hexDump(value);
                            console.log(_hexDump);
                            value = "<BUFFER>";
                        }
                        display((" " + value).green, hex_block(start, end, b));

                        if (value && value.encode) {
                            if (fieldType === "ExtensionObject") {
                                display_encodeable(value, buffer, start, end);
                            } else {
                                let str = value.toString() || "<empty>";
                                display(str.green);
                            }
                        }
                        break;
                }
            }
        }
    };

    return options;
}

const factories =require("node-opcua-factory");
/**
 * @method packet_analyzer
 * @param {Buffer} buffer
 * @param id
 * @param {Integer} padding
 * @param {Integer} offset
 * @param {Object} custom_options
 * @param {Object} custom_options.factory
 * @param {Function} custom_options.factory.constructObject
 */
function packet_analyzer(buffer, id, padding, offset, custom_options) {

   //xx var factories = custom_options.factory;

    const stream = new BinaryStream(buffer);

    let objMessage;
    if (!id) {

        id = ec.decodeExpandedNodeId(stream);

    } else if (typeof id === "object" && id._schema) {
        objMessage = id;
    }

    try {
        objMessage = objMessage || factories.constructObject(id);
    }
    catch (err) {
        console.log(id);
        console.log(err);
        console.log("Cannot read decodeExpandedNodeId  on stream " + stream._buffer.toString("hex"));
    }

    let options = make_tracer(buffer, padding, offset);
    options.name = "message";

    options = _.extend(options, custom_options);
    try {
        objMessage.decode_debug(stream, options);
    }
    catch (err) {
        console.log(" Error in ", err);
        console.log(" Error in ", err.stack);
        console.log(" objMessage ", util.inspect(objMessage, {color: true}));
    }
}


function analyze_object_binary_encoding(obj,options) {

    assert(obj);

    const size = obj.binaryStoreSize();
    console.log("-------------------------------------------------");
    console.log(" size = ", size);
    const stream = new BinaryStream(size);
    obj.encode(stream);
    stream.rewind();
    console.log("-------------------------------------------------");
    if (stream._buffer.length < 256) {
        console.log(hexDump(stream._buffer));
    }
    packet_analyzer(stream._buffer, obj.encodingDefaultBinary);

}
exports.analyze_object_binary_encoding = analyze_object_binary_encoding;

exports.packet_analyzer = packet_analyzer;