"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;