UNPKG

node-opcua-variant

Version:

pure nodejs OPCUA SDK - module variant

922 lines 38 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.VARIANT_TYPE_MASK = exports.VARIANT_ARRAY_DIMENSIONS_MASK = exports.VARIANT_ARRAY_MASK = exports.Variant = void 0; exports.encodeVariant = encodeVariant; exports.decodeVariant = decodeVariant; exports.coerceVariantType = coerceVariantType; exports.isValidVariant = isValidVariant; exports.buildVariantArray = buildVariantArray; exports.sameVariant = sameVariant; /** * @module node-opcua-variant */ const node_opcua_assert_1 = require("node-opcua-assert"); const node_opcua_nodeid_1 = require("node-opcua-nodeid"); 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 node_opcua_utils_1 = require("node-opcua-utils"); const node_opcua_debug_1 = require("node-opcua-debug"); const DataType_enum_1 = require("./DataType_enum"); const VariantArrayType_enum_1 = require("./VariantArrayType_enum"); // tslint:disable:no-bitwise const warningLog = (0, node_opcua_debug_1.make_warningLog)(__filename); const schemaVariant = (0, node_opcua_factory_1.buildStructuredType)({ baseType: "BaseUAObject", category: node_opcua_factory_1.FieldCategory.basic, 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); return value; } class Variant extends node_opcua_factory_1.BaseUAObject { constructor(options) { super(); if (options === null) { this.dataType = DataType_enum_1.DataType.Null; this.arrayType = VariantArrayType_enum_1.VariantArrayType.Scalar; this.value = null; this.dimensions = null; return; } const options2 = constructHook(options || {}); this.dataType = DataType_enum_1.DataType.Null; this.arrayType = VariantArrayType_enum_1.VariantArrayType.Scalar; const schema = schemaVariant; this.dataType = options2.dataType; this.arrayType = options2.arrayType; this.value = (0, node_opcua_factory_1.initialize_field)(schema.fields[2], options2.value); this.dimensions = options2.dimensions || null; if (this.dataType === DataType_enum_1.DataType.ExtensionObject) { if (this.arrayType === VariantArrayType_enum_1.VariantArrayType.Scalar) { /* istanbul ignore next */ if (this.value && !(this.value instanceof node_opcua_factory_1.BaseUAObject)) { throw new Error(`A variant with DataType.ExtensionObject must have a ExtensionObject value.\nMake sure that you specify a valid ExtensionObject to the value options in the Variant Constructor`); } } else { if (this.value) { for (const e of this.value) { /* istanbul ignore next */ if (e && !(e instanceof node_opcua_factory_1.BaseUAObject)) { throw new Error("A variant with DataType.ExtensionObject must have a ExtensionObject value\nMake sure that you specify a valid ExtensionObject for all element of the value array passed to the Variant Constructor`"); } } } } } } 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); } } exports.Variant = Variant; Variant.maxTypedArrayLength = 16 * 1024 * 1024; Variant.maxArrayLength = 1 * 1024 * 1024; Variant.schema = schemaVariant; Variant.coerce = _coerceVariant; Variant.computer_default_value = () => new Variant({ dataType: DataType_enum_1.DataType.Null }); Variant.prototype.schema = schemaVariant; function variantToString(self, options) { function toString(value) { switch (self.dataType) { case DataType_enum_1.DataType.ByteString: return value ? "0x" + value.toString("hex") : "<null>"; case DataType_enum_1.DataType.NodeId: return value instanceof node_opcua_nodeid_1.NodeId ? value.displayText() : value ? value.toString(options) : ""; 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 = []; (0, node_opcua_assert_1.assert)(Array.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; const nullVariant = new Variant({ dataType: DataType_enum_1.DataType.Null }); /*** * @private */ function encodeVariant(variant, stream) { if (!variant) { variant = nullVariant; } 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 && variant.arrayType === VariantArrayType_enum_1.VariantArrayType.Matrix) { (0, node_opcua_assert_1.assert)(variant.dimensions.length >= 0); encodingByte |= exports.VARIANT_ARRAY_DIMENSIONS_MASK; } (0, 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 || DataType_enum_1.DataType.Null); encode(variant.value, stream); } if ((encodingByte & exports.VARIANT_ARRAY_DIMENSIONS_MASK) === exports.VARIANT_ARRAY_DIMENSIONS_MASK && variant.dimensions) { encodeDimension(variant.dimensions, stream); } } /*** * @private * * istanbul ignore function */ function decodeDebugVariant(self, stream, options) { const tracer = options.tracer; const encodingByte = (0, 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 = (0, 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 BadDecodingError. if (hasDimension) { self.dimensions = decodeDimension(stream); const verification = calculate_product(self.dimensions); } } function internalDecodeVariant(self, stream) { const encodingByte = (0, 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); /* istanbul ignore next */ if (verification !== self.value.length) { throw new Error("internalDecodeVariant: BadDecodingError: inconsistent matrix "); } } } /*** * @private */ function decodeVariant(stream, value) { value = value || new Variant(null); value.decode(stream); return value; } function constructHook(options) { let isArrayTypeUnspecified = options.arrayType === undefined; if (options instanceof 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 { if (opts.value) { 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(opts.dataType, options.value); } return opts; } options.dataType = options.dataType || DataType_enum_1.DataType.Null; // dataType could be a string if (typeof options.dataType === "string") { const d = (0, node_opcua_factory_1.findBuiltInType)(options.dataType); /* istanbul ignore next */ if (!d) { throw new Error("Cannot find Built-In data type or any DataType resolving to " + options.dataType); } options.dataType = DataType_enum_1.DataType[d.name]; } // array type could be a string if (typeof options.arrayType === "string") { const at = VariantArrayType_enum_1.VariantArrayType[options.arrayType]; /* istanbul ignore next */ if (at === undefined) { throw new Error("ArrayType: invalid " + options.arrayType); } options.arrayType = at; } if (isArrayTypeUnspecified && Array.isArray(options.value)) { // when using UInt64 ou Int64 arrayType must be specified , as automatic detection cannot be made /* istanbul ignore next */ 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) { (0, 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) { const value1 = coerceVariantArray(options.dataType, options.value); (0, node_opcua_assert_1.assert)(value1 === null || value1 !== options.value); options.value = value1; } else { (0, node_opcua_assert_1.assert)(options.arrayType === VariantArrayType_enum_1.VariantArrayType.Matrix); options.value = options.value || []; options.value = coerceVariantArray(options.dataType, options.value); /* istanbul ignore next */ if (!options.dimensions) { throw new Error("Matrix Variant : missing dimensions"); } /* istanbul ignore next */ if (options.value.length != 0 && options.value.length !== calculate_product(options.dimensions)) { throw new Error("Matrix Variant : invalid value size = options.value.length " + options.value.length + "!=" + calculate_product(options.dimensions) + " => " + JSON.stringify(options.dimensions)); } } } else { (0, 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: " + VariantArrayType_enum_1.VariantArrayType[options.arrayType] + " dataType: " + DataType_enum_1.DataType[options.dataType] + " value:" + options.value + " (javascript type = " + typeof options.value + " )"); } } if (options.dimensions) { (0, 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) { /* istanbul ignore next */ if (!array || array.length === 0) { return 0; } return array.reduce((n, p) => n * p, 1); } function get_encoder(dataType) { const dataTypeAsString = typeof dataType === "string" ? dataType : DataType_enum_1.DataType[dataType]; /* istanbul ignore next */ if (!dataTypeAsString) { throw new Error("invalid dataType " + dataType); } const encode = (0, 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 = (0, 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) { // istanbul ignore next if (value === undefined || value === null) { return null; } if (arrayTypeConstructor && value instanceof arrayTypeConstructor) { const newArray = new value.constructor(value.length); // deep copy /* istanbul ignore if */ 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]); } // istanbul ignore next if (arrayTypeConstructor && displayWarning && n > 10) { warningLog("Warning ! an array containing " + DataType_enum_1.DataType[dataType] + " elements has been provided as a generic array. "); warningLog(" This is inefficient as every array value will " + "have to be coerced and verified against the expected type"); warningLog(" 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) { (0, node_opcua_assert_1.assert)(value instanceof arrayTypeConstructor); (0, node_opcua_assert_1.assert)(value.buffer instanceof ArrayBuffer); (0, node_opcua_basic_types_1.encodeUInt32)(value.length, stream); stream.writeArrayBuffer(value.buffer, value.byteOffset, value.byteLength); } function encodeGeneralArray(dataType, stream, value) { if (!value) { (0, node_opcua_assert_1.assert)(value === null); (0, node_opcua_basic_types_1.encodeUInt32)(0xffffffff, stream); return; } (0, node_opcua_basic_types_1.encodeUInt32)(value.length, stream); const encode = get_encoder(dataType); for (const e of value) { encode(e, stream); } } function encodeVariantArray(dataType, stream, value) { if (value && value.buffer) { return _getHelper(dataType).encode(stream, value); } return encodeGeneralArray(dataType, stream, value); } function decodeTypedArray(arrayTypeConstructor, stream) { const length = (0, node_opcua_basic_types_1.decodeUInt32)(stream); /* istanbul ignore next */ if (length === 0xffffffff) { return null; } if (length > Variant.maxTypedArrayLength) { throw new Error(`maxTypedArrayLength(${Variant.maxTypedArrayLength}) has been exceeded in Variant.decodeArray (typed Array) len=${length}`); } const byteLength = length * arrayTypeConstructor.BYTES_PER_ELEMENT; const arr = stream.readArrayBuffer(byteLength); const value = new arrayTypeConstructor(arr.buffer); (0, node_opcua_assert_1.assert)(value.length === length); return value; } function decodeGeneralArray(dataType, stream) { const length = (0, node_opcua_basic_types_1.decodeUInt32)(stream); /* istanbul ignore next */ if (length === 0xffffffff) { return null; } if (length > Variant.maxArrayLength) { throw new Error(`maxArrayLength(${Variant.maxArrayLength}) has been exceeded in Variant.decodeArray len=${length}`); } 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 = (0, node_opcua_basic_types_1.decodeUInt32)(stream); let i; let element; tracer.trace("start_array", "Variant", -1, cursorBefore, stream.length); if (length === 0xffffffff) { // empty array tracer.trace("end_array", "Variant", stream.length); return; } 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 && Object.prototype.hasOwnProperty.call(value, "value") && Object.prototype.hasOwnProperty.call(value, "key") && value.constructor.name === "EnumValueType"); } 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 && dataType !== DataType_enum_1.DataType.ExtensionObject) { 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.Int32: case DataType_enum_1.DataType.Int16: case DataType_enum_1.DataType.UInt32: case DataType_enum_1.DataType.UInt16: (0, node_opcua_assert_1.assert)(value !== undefined); value = parseInt(value, 10); /* istanbul ignore next */ if (!isFinite(value)) { throw new Error("expecting a number " + value); } break; case DataType_enum_1.DataType.UInt64: value = (0, node_opcua_basic_types_1.coerceUInt64)(value); break; case DataType_enum_1.DataType.Int64: value = (0, node_opcua_basic_types_1.coerceInt64)(value); break; case DataType_enum_1.DataType.ExtensionObject: break; case DataType_enum_1.DataType.DateTime: (0, node_opcua_assert_1.assert)(value === null || !!value.getTime); break; case DataType_enum_1.DataType.String: (0, 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; // istanbul ignore next if (!(value === null || value instanceof Buffer)) { throw new Error("ByteString should be null or a Buffer"); } (0, node_opcua_assert_1.assert)(value === null || value instanceof Buffer); break; default: (0, node_opcua_assert_1.assert)(dataType !== undefined && dataType !== null, "Invalid DataType"); break; case DataType_enum_1.DataType.NodeId: break; } return value; } function isValidScalarVariant(dataType, value) { (0, 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)); (0, node_opcua_assert_1.assert)(value === null || !(value instanceof Int32Array)); (0, node_opcua_assert_1.assert)(value === null || !(value instanceof Uint32Array)); switch (dataType) { case DataType_enum_1.DataType.NodeId: return (0, node_opcua_basic_types_1.isValidNodeId)(value); case DataType_enum_1.DataType.String: return typeof value === "string" || (0, node_opcua_utils_1.isNullOrUndefined)(value); case DataType_enum_1.DataType.Int64: return (0, node_opcua_basic_types_1.isValidInt64)(value); case DataType_enum_1.DataType.UInt64: return (0, node_opcua_basic_types_1.isValidUInt64)(value); case DataType_enum_1.DataType.UInt32: return (0, node_opcua_basic_types_1.isValidUInt32)(value); case DataType_enum_1.DataType.Int32: return (0, node_opcua_basic_types_1.isValidInt32)(value); case DataType_enum_1.DataType.UInt16: return (0, node_opcua_basic_types_1.isValidUInt16)(value); case DataType_enum_1.DataType.Int16: return (0, node_opcua_basic_types_1.isValidInt16)(value); case DataType_enum_1.DataType.Byte: return (0, node_opcua_basic_types_1.isValidUInt8)(value); case DataType_enum_1.DataType.SByte: return (0, node_opcua_basic_types_1.isValidInt8)(value); case DataType_enum_1.DataType.Boolean: return (0, node_opcua_basic_types_1.isValidBoolean)(value); case DataType_enum_1.DataType.ByteString: return (0, node_opcua_basic_types_1.isValidByteString)(value); default: return true; } } function isValidArrayVariant(dataType, value) { if (value === null) { return true; } 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 (0, node_opcua_assert_1.assert)(Array.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: (0, node_opcua_assert_1.assert)(arrayType === VariantArrayType_enum_1.VariantArrayType.Matrix); return isValidMatrixVariant(dataType, value, dimensions); } } 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); } if (defaultValue !== undefined) { for (let i = 0; i < nbElements; i++) { value[i] = defaultValue; } } return value; } // old version of nodejs do not provide a Buffer#equals test const oldNodeVersion = typeof process === "object" && process.versions && process.versions.node && process.versions.node.substring(0, 1) === "0"; function __type(a) { return Object.prototype.toString.call(a); } function __check_same_object(o1, o2) { if (o1 === o2) return true; if ((!o1 && o2) || (!o2 && o1)) return false; const t1 = __type(o1); const t2 = __type(o2); if (t1 !== t2) return false; switch (t1) { case "[object Array]": return __check_same_array(o1, o2); case "[object Object]": { if (o1.constructor?.name !== o2.constructor?.name) { return false; } const keys1 = Object.keys(o1); const keys2 = Object.keys(o2); // istanbul ignore next if (keys1.length !== keys2.length) { return false; } for (const k of Object.keys(o1)) { if (!__check_same_object(o1[k], o2[k])) { return false; } } return true; } case "[object Float32Array]": case "[object Float64Array]": case "[object Int32Array]": case "[object Int16Array]": case "[object Int8Array]": case "[object Uint32Array]": case "[object Uint16Array]": case "[object Uint8Array]": { const b1 = Buffer.from(o1.buffer, o1.byteOffset, o1.byteLength); const b2 = Buffer.from(o2.buffer, o2.byteOffset, o2.byteLength); return b1.equals(b2); } default: return o1 === o2; } } 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 (0, node_opcua_assert_1.assert)(arr2.buffer && __type(arr2.buffer) === "[object ArrayBuffer]"); // 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 (!__check_same_object(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 (v1.dataType === DataType_enum_1.DataType.ExtensionObject) { // compare two extension objects return __check_same_object(v1.value, v2.value); } 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; } // --------------------------------------------------------------------------------------------------------- (0, node_opcua_factory_1.registerSpecialVariantEncoder)(Variant); (0, node_opcua_factory_1.registerType)({ name: "Variant", subType: "", coerce: _coerceVariant, encode: encodeVariant, decode: decodeVariant }); //# sourceMappingURL=variant.js.map