node-opcua-variant
Version:
pure nodejs OPCUA SDK - module -variant
838 lines • 34.7 kB
JavaScript
"use strict";
/**
* @module node-opcua-variant
*/
Object.defineProperty(exports, "__esModule", { value: true });
const _ = require("underscore");
const node_opcua_assert_1 = require("node-opcua-assert");
const node_opcua_basic_types_1 = require("node-opcua-basic-types");
const node_opcua_data_model_1 = require("node-opcua-data-model");
const node_opcua_factory_1 = require("node-opcua-factory");
const utils = require("node-opcua-utils");
const DataType_enum_1 = require("./DataType_enum");
const VariantArrayType_enum_1 = require("./VariantArrayType_enum");
// tslint:disable:no-bitwise
const schemaVariant = node_opcua_factory_1.buildStructuredType({
baseType: "BaseUAObject",
fields: [{
defaultValue: () => DataType_enum_1.DataType.Null,
documentation: "the variant type.",
fieldType: "DataType",
name: "dataType",
}, {
defaultValue: VariantArrayType_enum_1.VariantArrayType.Scalar,
fieldType: "VariantArrayType",
name: "arrayType",
}, {
defaultValue: null,
fieldType: "Any",
name: "value",
}, {
defaultValue: null,
documentation: "the matrix dimensions",
fieldType: "UInt32",
isArray: true,
name: "dimensions",
}],
name: "Variant",
});
function _coerceVariant(variantLike) {
const value = (variantLike instanceof Variant) ? variantLike : new Variant(variantLike);
node_opcua_assert_1.assert(value instanceof Variant);
return value;
}
class Variant extends node_opcua_factory_1.BaseUAObject {
constructor(options) {
super();
options = constructHook(options);
options = options ? options : {};
this.dataType = DataType_enum_1.DataType.Null;
this.arrayType = VariantArrayType_enum_1.VariantArrayType.Scalar;
const schema = schemaVariant;
/**
* the variant type.
* @property dataType
* @type {DataType}
* @default 0
*/
this.setDataType(node_opcua_factory_1.initialize_field(schema.fields[0], options.dataType));
/**
* @property arrayType
* @type {VariantArrayType}
* @default 0
*/
this.setArrayType(node_opcua_factory_1.initialize_field(schema.fields[1], options.arrayType));
/**
* @property value
* @type {Any}
* @default null
*/
this.value = node_opcua_factory_1.initialize_field(schema.fields[2], options.value);
/**
* the matrix dimensions
* @property dimensions
* @type {UInt32[]}
* @default null
*/
this.dimensions = node_opcua_factory_1.initialize_field_array(schema.fields[3], options.dimensions);
}
// Define Enumeration setters
setDataType(value) {
const coercedValue = DataType_enum_1._enumerationDataType.get(value);
/* istanbul ignore next */
if (coercedValue === undefined || coercedValue === null) {
throw new Error("value cannot be coerced to DataType: " + value);
}
this.dataType = coercedValue.value;
}
setArrayType(value) {
const coercedValue = VariantArrayType_enum_1._enumerationVariantArrayType.get(value);
/* istanbul ignore next */
if (coercedValue === undefined || coercedValue === null) {
throw new Error("value cannot be coerced to VariantArrayType: " + value);
}
this.arrayType = coercedValue.value;
}
encode(stream) {
encodeVariant(this, stream);
}
decode(stream) {
internalDecodeVariant(this, stream);
}
decodeDebug(stream, options) {
decodeDebugVariant(this, stream, options);
}
toString() {
return variantToString(this);
}
isValid() {
return isValidVariant(this.arrayType, this.dataType, this.value, this.dimensions);
}
clone() {
return new Variant(this);
}
}
Variant.schema = schemaVariant;
Variant.coerce = _coerceVariant;
exports.Variant = Variant;
Variant.prototype.schema = schemaVariant;
function variantToString(self, options) {
function toString(value) {
switch (self.dataType) {
case DataType_enum_1.DataType.Null:
return "<null>";
case DataType_enum_1.DataType.ByteString:
return value ? "0x" + value.toString("hex") : "<null>";
case DataType_enum_1.DataType.Boolean:
return value.toString();
case DataType_enum_1.DataType.DateTime:
return value ? (value.toISOString ? value.toISOString() : value.toString()) : "<null>";
default:
return value ? value.toString(options) : "0";
}
}
function f(value) {
if (value === undefined || (value === null && typeof value === "object")) {
return "<null>";
}
return toString(value);
}
let data = VariantArrayType_enum_1.VariantArrayType[self.arrayType];
if (self.dimensions && self.dimensions.length > 0) {
data += "[ " + self.dimensions.join(",") + " ]";
}
data += "<" + DataType_enum_1.DataType[self.dataType] + ">";
if (self.arrayType === VariantArrayType_enum_1.VariantArrayType.Scalar) {
data += ", value: " + f(self.value);
}
else if ((self.arrayType === VariantArrayType_enum_1.VariantArrayType.Array) || (self.arrayType === VariantArrayType_enum_1.VariantArrayType.Matrix)) {
if (!self.value) {
data += ", null";
}
else {
const a = [];
node_opcua_assert_1.assert(_.isArray(self.value) || (self.value.buffer instanceof ArrayBuffer));
for (let i = 0; i < Math.min(10, self.value.length); i++) {
a[i] = self.value[i];
}
if (self.value.length > 10) {
a.push("...");
}
data += ", l= " + self.value.length + ", value=[" + a.map(f).join(",") + "]";
}
}
return "Variant(" + data + ")";
}
/***
* @private
*/
exports.VARIANT_ARRAY_MASK = 0x80;
/***
* @private
*/
exports.VARIANT_ARRAY_DIMENSIONS_MASK = 0x40;
/***
* @private
*/
exports.VARIANT_TYPE_MASK = 0x3f;
/***
* @private
*/
function encodeVariant(variant, stream) {
let encodingByte = variant.dataType;
if (variant.arrayType === VariantArrayType_enum_1.VariantArrayType.Array || variant.arrayType === VariantArrayType_enum_1.VariantArrayType.Matrix) {
encodingByte |= exports.VARIANT_ARRAY_MASK;
}
if (variant.dimensions) {
node_opcua_assert_1.assert(variant.arrayType === VariantArrayType_enum_1.VariantArrayType.Matrix);
node_opcua_assert_1.assert(variant.dimensions.length >= 1);
encodingByte |= exports.VARIANT_ARRAY_DIMENSIONS_MASK;
}
node_opcua_basic_types_1.encodeUInt8(encodingByte, stream);
if (variant.arrayType === VariantArrayType_enum_1.VariantArrayType.Array || variant.arrayType === VariantArrayType_enum_1.VariantArrayType.Matrix) {
encodeVariantArray(variant.dataType, stream, variant.value);
}
else {
const encode = get_encoder(variant.dataType);
encode(variant.value, stream);
}
if ((encodingByte & exports.VARIANT_ARRAY_DIMENSIONS_MASK) === exports.VARIANT_ARRAY_DIMENSIONS_MASK && variant.dimensions) {
encodeDimension(variant.dimensions, stream);
}
}
exports.encodeVariant = encodeVariant;
/***
* @private
*/
function decodeDebugVariant(self, stream, options) {
const tracer = options.tracer;
const encodingByte = node_opcua_basic_types_1.decodeUInt8(stream);
const isArray = ((encodingByte & exports.VARIANT_ARRAY_MASK) === exports.VARIANT_ARRAY_MASK);
const hasDimension = ((encodingByte & exports.VARIANT_ARRAY_DIMENSIONS_MASK) === exports.VARIANT_ARRAY_DIMENSIONS_MASK);
self.dataType = (encodingByte & exports.VARIANT_TYPE_MASK);
tracer.dump("dataType: ", self.dataType);
tracer.dump("isArray: ", isArray ? "true" : "false");
tracer.dump("dimension: ", hasDimension);
const decode = node_opcua_factory_1.findBuiltInType(DataType_enum_1.DataType[self.dataType]).decode;
/* istanbul ignore next */
if (!decode) {
throw new Error("Variant.decode : cannot find decoder for type " + DataType_enum_1.DataType[self.dataType]);
}
const cursorBefore = stream.length;
if (isArray) {
self.arrayType = hasDimension ? VariantArrayType_enum_1.VariantArrayType.Matrix : VariantArrayType_enum_1.VariantArrayType.Array;
_decodeVariantArrayDebug(stream, decode, tracer, self.dataType);
}
else {
self.arrayType = VariantArrayType_enum_1.VariantArrayType.Scalar;
self.value = decode(stream);
tracer.trace("member", "Variant", self.value, cursorBefore, stream.length, DataType_enum_1.DataType[self.dataType]);
}
// ArrayDimensions
// Int32[]
// The length of each dimension.
// This field is only present if the array dimensions flag is set in the encoding mask.
// The lower rank dimensions appear first in the array.
// All dimensions shall be specified and shall be greater than zero.
// If ArrayDimensions are inconsistent with the ArrayLength then the decoder shall
// stop and raise a Bad_DecodingError.
if (hasDimension) {
self.dimensions = decodeDimension(stream);
const verification = calculate_product(self.dimensions);
}
}
function internalDecodeVariant(self, stream) {
const encodingByte = node_opcua_basic_types_1.decodeUInt8(stream);
const isArray = ((encodingByte & exports.VARIANT_ARRAY_MASK) === exports.VARIANT_ARRAY_MASK);
const hasDimension = ((encodingByte & exports.VARIANT_ARRAY_DIMENSIONS_MASK) === exports.VARIANT_ARRAY_DIMENSIONS_MASK);
self.dataType = (encodingByte & exports.VARIANT_TYPE_MASK);
if (isArray) {
self.arrayType = hasDimension ? VariantArrayType_enum_1.VariantArrayType.Matrix : VariantArrayType_enum_1.VariantArrayType.Array;
self.value = decodeVariantArray(self.dataType, stream);
}
else {
self.arrayType = VariantArrayType_enum_1.VariantArrayType.Scalar;
const decode = get_decoder(self.dataType);
self.value = decode(stream);
}
if (hasDimension) {
self.dimensions = decodeDimension(stream);
const verification = calculate_product(self.dimensions);
if (verification !== self.value.length) {
throw new Error("BadDecodingError");
}
}
}
/***
* @private
*/
function decodeVariant(stream) {
const value = new Variant({});
value.decode(stream);
return value;
}
exports.decodeVariant = decodeVariant;
function constructHook(options) {
options = options || {};
let isArrayTypeUnspecified = (options.arrayType === undefined);
if (options.constructor.name === "Variant") {
const opts = {
arrayType: options.arrayType,
dataType: options.dataType,
dimensions: options.dimensions,
value: options.value,
};
if (opts.dataType === DataType_enum_1.DataType.ExtensionObject) {
if (opts.arrayType === VariantArrayType_enum_1.VariantArrayType.Scalar) {
if (opts.value && opts.value.constructor) {
opts.value = new opts.value.constructor(opts.value);
}
}
else {
opts.value = opts.value.map((e) => {
if (e && e.constructor) {
return new e.constructor(e);
}
return null;
});
}
}
else if (opts.arrayType !== VariantArrayType_enum_1.VariantArrayType.Scalar) {
opts.value = coerceVariantArray(options.dataType, options.value);
}
return opts;
}
node_opcua_assert_1.assert(options);
options.dataType = options.dataType || DataType_enum_1.DataType.Null;
// dataType could be a string
if (typeof options.dataType === "string") {
const d = node_opcua_factory_1.findBuiltInType(options.dataType);
if (!d) {
throw new Error("Cannot find data type buildIn");
}
const t = DataType_enum_1._enumerationDataType.get(d.name);
// istanbul ignore next
if (t === null) {
throw new Error("DataType: invalid " + options.dataType);
}
options.dataType = t.value;
}
// array type could be a string
if (typeof options.arrayType === "string") {
const at = VariantArrayType_enum_1.VariantArrayType[options.arrayType];
// istanbul ignore next
if (utils.isNullOrUndefined(at)) {
throw new Error("ArrayType: invalid " + options.arrayType);
}
options.arrayType = at;
}
if (isArrayTypeUnspecified && _.isArray(options.value)) {
// when using UInt64 ou Int64 arrayType must be specified , as automatic detection cannot be made
if ((options.dataType === DataType_enum_1.DataType.UInt64 || options.dataType === DataType_enum_1.DataType.Int64)) {
// we do nothing here ....
throw new Error("Variant#constructor : when using UInt64 ou Int64" +
" arrayType must be specified , as automatic detection cannot be made");
}
else {
options.arrayType = VariantArrayType_enum_1.VariantArrayType.Array;
isArrayTypeUnspecified = false;
}
}
if (options.arrayType !== VariantArrayType_enum_1.VariantArrayType.Scalar && !isArrayTypeUnspecified) {
node_opcua_assert_1.assert(options.arrayType === VariantArrayType_enum_1.VariantArrayType.Array || options.arrayType === VariantArrayType_enum_1.VariantArrayType.Matrix);
/* istanbul ignore else */
if (options.arrayType === VariantArrayType_enum_1.VariantArrayType.Array) {
options.value = options.value || [];
const value1 = coerceVariantArray(options.dataType, options.value);
node_opcua_assert_1.assert(value1 !== options.value);
options.value = value1;
}
else {
node_opcua_assert_1.assert(options.arrayType === VariantArrayType_enum_1.VariantArrayType.Matrix);
options.value = options.value || [];
options.value = coerceVariantArray(options.dataType, options.value);
if (!options.dimensions) {
throw new Error("Matrix Variant : missing dimensions");
}
if (options.value.length !== calculate_product(options.dimensions)) {
throw new Error("Matrix Variant : invalid value size");
}
}
}
else {
node_opcua_assert_1.assert(options.arrayType === VariantArrayType_enum_1.VariantArrayType.Scalar || options.arrayType === undefined);
options.arrayType = VariantArrayType_enum_1.VariantArrayType.Scalar;
const tmp = options.value;
// scalar
options.value = coerceVariantType(options.dataType, options.value);
/* istanbul ignore next */
if (!isValidVariant(options.arrayType, options.dataType, options.value, null)) {
throw new Error("Invalid variant arrayType: " + options.arrayType.toString() +
" dataType: " + options.dataType.toString() + " value:" + options.value);
}
}
if (options.dimensions) {
node_opcua_assert_1.assert(options.arrayType === VariantArrayType_enum_1.VariantArrayType.Matrix, "dimension can only provided if variant is a matrix");
}
return options;
}
function calculate_product(array) {
if (!array) {
return 0;
}
return array.reduce((n, p) => n * p, 1);
}
function get_encoder(dataType) {
const dataTypeAsString = DataType_enum_1.DataType[dataType];
const encode = node_opcua_factory_1.findBuiltInType(dataTypeAsString).encode;
/* istanbul ignore next */
if (!encode) {
throw new Error("Cannot find encode function for dataType " + dataTypeAsString);
}
return encode;
}
function get_decoder(dataType) {
const dataTypeAsString = DataType_enum_1.DataType[dataType];
const decode = node_opcua_factory_1.findBuiltInType(dataTypeAsString).decode;
/* istanbul ignore next */
if (!decode) {
throw new Error("Variant.decode : cannot find decoder for type " + dataTypeAsString);
}
return decode;
}
const displayWarning = true;
function convertTo(dataType, arrayTypeConstructor, value) {
if (arrayTypeConstructor && value instanceof arrayTypeConstructor) {
const newArray = new value.constructor(value.length); // deep copy
if (newArray instanceof Buffer) {
// required for nodejs 4.x
value.copy(newArray);
}
else {
newArray.set(value);
}
return newArray;
}
const coerceFunc = coerceVariantType.bind(null, dataType);
const n = value.length;
const newArr = arrayTypeConstructor ? new arrayTypeConstructor(n) : new Array(n);
for (let i = 0; i < n; i++) {
newArr[i] = coerceFunc(value[i]);
}
if (arrayTypeConstructor && displayWarning && n > 10) {
// tslint:disable-next-line:no-console
console.log("Warning ! an array containing " + DataType_enum_1.DataType[dataType] +
" elements has been provided as a generic array. ");
// tslint:disable-next-line:no-console
console.log(" This is inefficient as every array value will " +
"have to be coerced and verified against the expected type");
// tslint:disable-next-line:no-console
console.log(" It is highly recommended that you use a " +
" typed array ", arrayTypeConstructor.constructor.name, " instead");
}
return newArr;
}
const typedArrayHelpers = {};
function _getHelper(dataType) {
return typedArrayHelpers[DataType_enum_1.DataType[dataType]];
}
function coerceVariantArray(dataType, value) {
const helper = _getHelper(dataType);
if (helper) {
return helper.coerce(value);
}
else {
return convertTo(dataType, null, value);
}
}
function encodeTypedArray(arrayTypeConstructor, stream, value) {
node_opcua_assert_1.assert(value instanceof arrayTypeConstructor);
node_opcua_assert_1.assert(value.buffer instanceof ArrayBuffer);
node_opcua_basic_types_1.encodeUInt32(value.length, stream);
stream.writeArrayBuffer(value.buffer, value.byteOffset, value.byteLength);
}
function encodeGeneralArray(dataType, stream, value) {
const arr = value || [];
node_opcua_assert_1.assert(arr instanceof Array);
node_opcua_assert_1.assert(_.isFinite(arr.length));
node_opcua_basic_types_1.encodeUInt32(arr.length, stream);
const encode = get_encoder(dataType);
let i;
const n = arr.length;
for (i = 0; i < n; i++) {
encode(arr[i], stream);
}
}
function encodeVariantArray(dataType, stream, value) {
if (value.buffer) {
try {
return _getHelper(dataType).encode(stream, value);
}
catch (err) {
// tslint:disable-next-line:no-console
console.log("encodeVariantArray error : DATATYPE", dataType, "\nvalue", value.length);
}
}
return encodeGeneralArray(dataType, stream, value);
}
function decodeTypedArray(arrayTypeConstructor, stream) {
const length = node_opcua_basic_types_1.decodeUInt32(stream);
if (length === 0xFFFFFFFF) {
return null;
}
const byteLength = length * arrayTypeConstructor.BYTES_PER_ELEMENT;
const arr = stream.readArrayBuffer(byteLength);
const value = new arrayTypeConstructor(arr.buffer);
node_opcua_assert_1.assert(value.length === length);
return value;
}
function decodeGeneralArray(dataType, stream) {
const length = node_opcua_basic_types_1.decodeUInt32(stream);
if (length === 0xFFFFFFFF) {
return null;
}
const decode = get_decoder(dataType);
const arr = [];
for (let i = 0; i < length; i++) {
arr.push(decode(stream));
}
return arr;
}
function decodeVariantArray(dataType, stream) {
const helper = _getHelper(dataType);
if (helper) {
return helper.decode(stream);
}
else {
return decodeGeneralArray(dataType, stream);
}
}
function _declareTypeArrayHelper(dataType, typedArrayConstructor) {
typedArrayHelpers[DataType_enum_1.DataType[dataType]] = {
coerce: convertTo.bind(null, dataType, typedArrayConstructor),
decode: decodeTypedArray.bind(null, typedArrayConstructor),
encode: encodeTypedArray.bind(null, typedArrayConstructor),
};
}
_declareTypeArrayHelper(DataType_enum_1.DataType.Float, Float32Array);
_declareTypeArrayHelper(DataType_enum_1.DataType.Double, Float64Array);
_declareTypeArrayHelper(DataType_enum_1.DataType.SByte, Int8Array);
_declareTypeArrayHelper(DataType_enum_1.DataType.Byte, Uint8Array);
_declareTypeArrayHelper(DataType_enum_1.DataType.Int16, Int16Array);
_declareTypeArrayHelper(DataType_enum_1.DataType.Int32, Int32Array);
_declareTypeArrayHelper(DataType_enum_1.DataType.UInt16, Uint16Array);
_declareTypeArrayHelper(DataType_enum_1.DataType.UInt32, Uint32Array);
function _decodeVariantArrayDebug(stream, decode, tracer, dataType) {
let cursorBefore = stream.length;
const length = node_opcua_basic_types_1.decodeUInt32(stream);
let i;
let element;
tracer.trace("start_array", "Variant", length, cursorBefore, stream.length);
const n1 = Math.min(10, length);
// display a maximum of 10 elements
for (i = 0; i < n1; i++) {
tracer.trace("start_element", "", i);
cursorBefore = stream.length;
element = decode(stream);
// arr.push(element);
tracer.trace("member", "Variant", element, cursorBefore, stream.length, DataType_enum_1.DataType[dataType]);
tracer.trace("end_element", "", i);
}
// keep reading
if (length >= n1) {
for (i = n1; i < length; i++) {
decode(stream);
}
tracer.trace("start_element", "", n1);
tracer.trace("member", "Variant", "...", cursorBefore, stream.length, DataType_enum_1.DataType[dataType]);
tracer.trace("end_element", "", n1);
}
tracer.trace("end_array", "Variant", stream.length);
}
function decodeDimension(stream) {
return decodeGeneralArray(DataType_enum_1.DataType.UInt32, stream);
}
function encodeDimension(dimensions, stream) {
return encodeGeneralArray(DataType_enum_1.DataType.UInt32, stream, dimensions);
}
function isEnumerationItem(value) {
return (value instanceof Object && (value.hasOwnProperty("value")) && value.hasOwnProperty("key"));
}
function coerceVariantType(dataType, value) {
/* eslint max-statements: ["error",1000], complexity: ["error",1000]*/
if (value === undefined) {
value = null;
}
if (isEnumerationItem(value)) {
// OPCUA Specification 1.0.3 5.8.2 encoding rules for various dataType:
// [...]Enumeration are always encoded as Int32 on the wire [...]
// istanbul ignore next
if (dataType !== DataType_enum_1.DataType.Int32) {
throw new Error("expecting DataType.Int32 for enumeration values ;" +
" got DataType." + dataType.toString() + " instead");
}
}
switch (dataType) {
case DataType_enum_1.DataType.Null:
value = null;
break;
case DataType_enum_1.DataType.LocalizedText:
if (!value || !value.schema || value.schema !== node_opcua_data_model_1.LocalizedText.schema) {
value = new node_opcua_data_model_1.LocalizedText(value);
}
break;
case DataType_enum_1.DataType.QualifiedName:
if (!value || !value.schema || value.schema !== node_opcua_data_model_1.QualifiedName.schema) {
value = new node_opcua_data_model_1.QualifiedName(value);
}
break;
case DataType_enum_1.DataType.Int16:
case DataType_enum_1.DataType.UInt16:
case DataType_enum_1.DataType.Int32:
case DataType_enum_1.DataType.UInt32:
node_opcua_assert_1.assert(value !== undefined);
const ttt = value;
if (isEnumerationItem(value)) {
// value is a enumeration of some sort
value = value.value;
}
else {
value = parseInt(value, 10);
}
if (!_.isFinite(value)) {
// xx console.log("xxx ", value, ttt);
}
node_opcua_assert_1.assert(_.isFinite(value), "expecting a number");
break;
case DataType_enum_1.DataType.UInt64:
value = node_opcua_basic_types_1.coerceUInt64(value);
break;
case DataType_enum_1.DataType.Int64:
value = node_opcua_basic_types_1.coerceInt64(value);
break;
case DataType_enum_1.DataType.ExtensionObject:
break;
case DataType_enum_1.DataType.DateTime:
node_opcua_assert_1.assert(value === null || value instanceof Date);
break;
case DataType_enum_1.DataType.String:
node_opcua_assert_1.assert(typeof value === "string" || value === null);
break;
case DataType_enum_1.DataType.ByteString:
value = (typeof value === "string") ? Buffer.from(value) : value;
if (!(value === null || value instanceof Buffer)) {
throw new Error("ByteString should be null or a Buffer");
}
node_opcua_assert_1.assert(value === null || value instanceof Buffer);
break;
default:
node_opcua_assert_1.assert(dataType !== undefined && dataType !== null, "Invalid DataType");
break;
case DataType_enum_1.DataType.NodeId:
break;
}
return value;
}
exports.coerceVariantType = coerceVariantType;
function isValidScalarVariant(dataType, value) {
node_opcua_assert_1.assert(value === null
|| DataType_enum_1.DataType.Int64 === dataType
|| DataType_enum_1.DataType.ByteString === dataType
|| DataType_enum_1.DataType.UInt64 === dataType
|| !(value instanceof Array));
node_opcua_assert_1.assert(value === null || !(value instanceof Int32Array));
node_opcua_assert_1.assert(value === null || !(value instanceof Uint32Array));
switch (dataType) {
case DataType_enum_1.DataType.NodeId:
return node_opcua_basic_types_1.isValidNodeId(value);
case DataType_enum_1.DataType.String:
return typeof value === "string" || utils.isNullOrUndefined(value);
case DataType_enum_1.DataType.Int64:
return node_opcua_basic_types_1.isValidInt64(value);
case DataType_enum_1.DataType.UInt64:
return node_opcua_basic_types_1.isValidUInt64(value);
case DataType_enum_1.DataType.UInt32:
return node_opcua_basic_types_1.isValidUInt32(value);
case DataType_enum_1.DataType.Int32:
return node_opcua_basic_types_1.isValidInt32(value);
case DataType_enum_1.DataType.UInt16:
return node_opcua_basic_types_1.isValidUInt16(value);
case DataType_enum_1.DataType.Int16:
return node_opcua_basic_types_1.isValidInt16(value);
case DataType_enum_1.DataType.Byte:
return node_opcua_basic_types_1.isValidUInt8(value);
case DataType_enum_1.DataType.SByte:
return node_opcua_basic_types_1.isValidInt8(value);
case DataType_enum_1.DataType.Boolean:
return node_opcua_basic_types_1.isValidBoolean(value);
case DataType_enum_1.DataType.ByteString:
return node_opcua_basic_types_1.isValidByteString(value);
default:
return true;
}
}
function isValidArrayVariant(dataType, value) {
if (dataType === DataType_enum_1.DataType.Float && value instanceof Float32Array) {
return true;
}
else if (dataType === DataType_enum_1.DataType.Double && value instanceof Float64Array) {
return true;
}
else if (dataType === DataType_enum_1.DataType.SByte && (value instanceof Int8Array)) {
return true;
}
else if (dataType === DataType_enum_1.DataType.Byte && (value instanceof Buffer || value instanceof Uint8Array)) {
return true;
}
else if (dataType === DataType_enum_1.DataType.Int16 && value instanceof Int16Array) {
return true;
}
else if (dataType === DataType_enum_1.DataType.Int32 && value instanceof Int32Array) {
return true;
}
else if (dataType === DataType_enum_1.DataType.UInt16 && value instanceof Uint16Array) {
return true;
}
else if (dataType === DataType_enum_1.DataType.UInt32 && value instanceof Uint32Array) {
return true;
}
// array values can be store in Buffer, Float32Array
node_opcua_assert_1.assert(_.isArray(value));
for (const valueItem of value) {
if (!isValidScalarVariant(dataType, valueItem)) {
return false;
}
}
return true;
}
/*istanbul ignore next*/
function isValidMatrixVariant(dataType, value, dimensions) {
if (!dimensions) {
return false;
}
if (!isValidArrayVariant(dataType, value)) {
return false;
}
return true;
}
function isValidVariant(arrayType, dataType, value, dimensions) {
switch (arrayType) {
case VariantArrayType_enum_1.VariantArrayType.Scalar:
return isValidScalarVariant(dataType, value);
case VariantArrayType_enum_1.VariantArrayType.Array:
return isValidArrayVariant(dataType, value);
default:
node_opcua_assert_1.assert(arrayType === VariantArrayType_enum_1.VariantArrayType.Matrix);
return isValidMatrixVariant(dataType, value, dimensions);
}
}
exports.isValidVariant = isValidVariant;
function buildVariantArray(dataType, nbElements, defaultValue) {
let value;
switch (dataType) {
case DataType_enum_1.DataType.Float:
value = new Float32Array(nbElements);
break;
case DataType_enum_1.DataType.Double:
value = new Float64Array(nbElements);
break;
case DataType_enum_1.DataType.UInt32:
value = new Uint32Array(nbElements);
break;
case DataType_enum_1.DataType.Int32:
value = new Int32Array(nbElements);
break;
case DataType_enum_1.DataType.UInt16:
value = new Uint16Array(nbElements);
break;
case DataType_enum_1.DataType.Int16:
value = new Int16Array(nbElements);
break;
case DataType_enum_1.DataType.Byte:
value = new Uint8Array(nbElements);
break;
case DataType_enum_1.DataType.SByte:
value = new Int8Array(nbElements);
break;
default:
value = new Array(nbElements);
for (let i = 0; i < nbElements; i++) {
value[i] = defaultValue;
}
}
return value;
}
exports.buildVariantArray = buildVariantArray;
// old version of nodejs do not provide a Buffer#equals test
const oldNodeVersion = (process.versions.node && process.versions.node.substring(0, 1) === "0");
function __check_same_array(arr1, arr2) {
if (!arr1 || !arr2) {
return !arr1 && !arr2;
}
if (arr1.length !== arr2.length) {
return false;
}
if (arr1.length === 0 && 0 === arr2.length) {
return true;
}
if (!oldNodeVersion && arr1.buffer) {
// v1 and v2 are TypedArray (such as Int32Array...)
// this is the most efficient way to compare 2 buffers but it doesn't work with node <= 0.12
node_opcua_assert_1.assert(arr2.buffer);
// compare byte by byte
const b1 = Buffer.from(arr1.buffer, arr1.byteOffset, arr1.byteLength);
const b2 = Buffer.from(arr2.buffer, arr2.byteOffset, arr2.byteLength);
return b1.equals(b2);
}
const n = arr1.length;
for (let i = 0; i < n; i++) {
if (!_.isEqual(arr1[i], arr2[i])) {
return false;
}
}
return true;
}
/***
* returns true if the two variant represent the same value
* @param v1 the first variant to compare
* @param v2 the variant to compare with
*/
function sameVariant(v1, v2) {
if (v1 === v2) {
return true;
}
if ((!v1 && v2) || (v1 && !v2)) {
return false;
}
if (v1.arrayType !== v2.arrayType) {
return false;
}
if (v1.dataType !== v2.dataType) {
return false;
}
if (v1.value === v2.value) {
return true;
}
if (v1.arrayType === VariantArrayType_enum_1.VariantArrayType.Scalar) {
if (Array.isArray(v1.value) && Array.isArray(v2.value)) {
return __check_same_array(v1.value, v2.value);
}
if (Buffer.isBuffer(v1.value) && Buffer.isBuffer(v2.value)) {
return v1.value.equals(v2.value);
}
}
if (v1.arrayType === VariantArrayType_enum_1.VariantArrayType.Array) {
return __check_same_array(v1.value, v2.value);
}
else if (v1.arrayType === VariantArrayType_enum_1.VariantArrayType.Matrix) {
if (!__check_same_array(v1.dimensions, v2.dimensions)) {
return false;
}
return __check_same_array(v1.value, v2.value);
}
return false;
}
exports.sameVariant = sameVariant;
// ---------------------------------------------------------------------------------------------------------
node_opcua_factory_1.registerSpecialVariantEncoder(Variant);
//# sourceMappingURL=variant.js.map