module import { assert } from "node-opcua-assert" ; import { BinaryStream , OutputBinaryStream } from "node-opcua-binary-stream" ; import { coerceDateTime , DateWithPicoseconds , getCurrentClock , minOPCUADate , PreciseClock } from "node-opcua-date-time" ; import { BaseUAObject , buildStructuredType , check_options_correctness_against_schema , DecodeDebugOptions , initialize_field , parameters , registerSpecialVariantEncoder , StructuredTypeSchema } from "node-opcua-factory" ; import { coerceStatusCode , StatusCode , StatusCodes } from "node-opcua-status-code" ; import { DataType , sameVariant , Variant , VariantArrayType , VariantOptions , VariantOptionsT , VariantT } from "node-opcua-variant" ; import { DateTime , decodeHighAccuracyDateTime , decodeStatusCode , decodeUInt16 , decodeUInt8 , encodeHighAccuracyDateTime , encodeStatusCode , encodeUInt16 , encodeUInt8 , UInt16 } from "node-opcua-basic-types" ; import { AttributeIds } from "node-opcua-data-model" ; import { DataValueEncodingByte } from "./DataValueEncodingByte_enum" ; import { TimestampsToReturn } from "./TimestampsToReturn_enum" ; type NumericalRange = any ; function getDataValue_EncodingByte ( dataValue ) { let encodingMask = 0 ; if ( dataValue . value && dataValue . value . dataType !== DataType . Null ) { encodingMask |= DataValueEncodingByte . Value ; } if ( dataValue . statusCode !== null && typeof dataValue . statusCode === "object" && dataValue . statusCode . value !== 0 ) { encodingMask |= DataValueEncodingByte . StatusCode ; } if ( dataValue . sourceTimestamp && ( dataValue . sourceTimestamp as any ) !== "null" ) { encodingMask |= DataValueEncodingByte . SourceTimestamp ; } if ( dataValue . sourcePicoseconds ? dataValue . sourcePicoseconds % 100000 : false ) { encodingMask |= DataValueEncodingByte . SourcePicoseconds ; } if ( dataValue . serverTimestamp && ( dataValue . serverTimestamp as any ) !== "null" ) { encodingMask |= DataValueEncodingByte . ServerTimestamp ; } if ( dataValue . serverPicoseconds ? dataValue . serverPicoseconds % 100000 : false ) { encodingMask |= DataValueEncodingByte . ServerPicoseconds ; } return encodingMask ; } export function encodeDataValue ( dataValue , stream ) { const encodingMask = getDataValue_EncodingByte ( dataValue ) ; assert ( isFinite ( encodingMask ) && encodingMask >= 0 && encodingMask <= 0x3f ) ; encodeUInt8 ( encodingMask , stream ) ; if ( encodingMask & DataValueEncodingByte . Value ) { if ( ! dataValue . value ) { dataValue . value = new Variant ( ) ; } if ( ! dataValue . value . encode ) { console . log ( " CANNOT FIND ENCODE METHOD ON VARIANT !!! HELP" , JSON . stringify ( dataValue , null , " " ) ) ; } dataValue . value . encode ( stream ) ; } if ( encodingMask & DataValueEncodingByte . StatusCode ) { encodeStatusCode ( dataValue . statusCode , stream ) ; } if ( encodingMask & DataValueEncodingByte . SourceTimestamp && dataValue . sourceTimestamp !== null ) { encodeHighAccuracyDateTime ( dataValue . sourceTimestamp , dataValue . sourcePicoseconds , stream ) ; } if ( encodingMask & DataValueEncodingByte . SourcePicoseconds ) { assert ( dataValue . sourcePicoseconds !== null ) ; const sourcePicoseconds = Math . floor ( ( dataValue . sourcePicoseconds % 100000 ) / 10 ) ; encodeUInt16 ( sourcePicoseconds , stream ) ; } if ( encodingMask & DataValueEncodingByte . ServerTimestamp && dataValue . serverTimestamp !== null ) { encodeHighAccuracyDateTime ( dataValue . serverTimestamp , dataValue . serverPicoseconds , stream ) ; } if ( encodingMask & DataValueEncodingByte . ServerPicoseconds ) { assert ( dataValue . serverPicoseconds !== null ) ; const serverPicoseconds = Math . floor ( ( dataValue . serverPicoseconds % 100000 ) / 10 ) ; encodeUInt16 ( serverPicoseconds , stream ) ; } } function decodeDebugDataValue ( dataValue , stream , options ) { const tracer = options . tracer ; let cur = stream . length ; const encodingMask = decodeUInt8 ( stream ) ; assert ( encodingMask <= 0x3f ) ; tracer . trace ( "member" , "encodingByte" , "0x" + encodingMask . toString ( 16 ) , cur , stream . length , "Mask" ) ; tracer . encoding_byte ( encodingMask , DataValueEncodingByte , cur , stream . length ) ; if ( encodingMask & DataValueEncodingByte . Value ) { dataValue . value = new Variant ( ) ; dataValue . value . decodeDebug ( stream , options ) ; } cur = stream . length ; if ( encodingMask & DataValueEncodingByte . StatusCode ) { dataValue . statusCode = decodeStatusCode ( stream ) ; tracer . trace ( "member" , "statusCode" , dataValue . statusCode , cur , stream . length , "StatusCode" ) ; } cur = stream . length ; if ( encodingMask & DataValueEncodingByte . SourceTimestamp ) { dataValue . sourceTimestamp = decodeHighAccuracyDateTime ( stream ) ; dataValue . sourcePicoseconds = ( dataValue . sourceTimestamp as DateWithPicoseconds ) . picoseconds ; tracer . trace ( "member" , "sourceTimestamp" , dataValue . sourceTimestamp , cur , stream . length , "DateTime" ) ; } cur = stream . length ; dataValue . sourcePicoseconds = 0 ; if ( encodingMask & DataValueEncodingByte . SourcePicoseconds ) { const tenPico = decodeUInt16 ( stream ) ; dataValue . sourcePicoseconds += tenPico * 10 ; tracer . trace ( "member" , "sourcePicoseconds" , dataValue . sourcePicoseconds , cur , stream . length , "UInt16" ) ; } cur = stream . length ; dataValue . serverPicoseconds = 0 ; if ( encodingMask & DataValueEncodingByte . ServerTimestamp ) { dataValue . serverTimestamp = decodeHighAccuracyDateTime ( stream ) ; dataValue . serverPicoseconds = ( dataValue . serverTimestamp as DateWithPicoseconds ) . picoseconds | 0 ; tracer . trace ( "member" , "serverTimestamp" , dataValue . serverTimestamp , cur , stream . length , "DateTime" ) ; } cur = stream . length ; if ( encodingMask & DataValueEncodingByte . ServerPicoseconds ) { const tenPico = decodeUInt16 ( stream ) ; dataValue . serverPicoseconds += tenPico * 10 ; tracer . trace ( "member" , "serverPicoseconds" , dataValue . serverPicoseconds , cur , stream . length , "UInt16" ) ; } } function decodeDataValueInternal ( dataValue , stream ) { const encodingMask = decodeUInt8 ( stream ) ; if ( encodingMask & DataValueEncodingByte . Value ) { dataValue . value = new Variant ( ) ; dataValue . value . decode ( stream ) ; } if ( encodingMask & DataValueEncodingByte . StatusCode ) { dataValue . statusCode = decodeStatusCode ( stream ) ; } else { dataValue . statusCode = StatusCodes . Good ; } dataValue . sourcePicoseconds = 0 ; if ( encodingMask & DataValueEncodingByte . SourceTimestamp ) { dataValue . sourceTimestamp = decodeHighAccuracyDateTime ( stream ) ; dataValue . sourcePicoseconds += ( dataValue . sourceTimestamp as DateWithPicoseconds ) . picoseconds | 0 ; } if ( encodingMask & DataValueEncodingByte . SourcePicoseconds ) { dataValue . sourcePicoseconds += decodeUInt16 ( stream ) * 10 ; } dataValue . serverPicoseconds = 0 ; if ( encodingMask & DataValueEncodingByte . ServerTimestamp ) { dataValue . serverTimestamp = decodeHighAccuracyDateTime ( stream ) ; dataValue . serverPicoseconds += ( dataValue . serverTimestamp as DateWithPicoseconds ) . picoseconds | 0 ; } if ( encodingMask & DataValueEncodingByte . ServerPicoseconds ) { dataValue . serverPicoseconds += decodeUInt16 ( stream ) * 10 ; } } export function decodeDataValue ( stream , dataValue ? ) { dataValue = dataValue || new DataValue ( ) ; decodeDataValueInternal ( dataValue , stream ) ; return dataValue ; } function isValidDataValue ( self ) { if ( self . value !== null && typeof self . value === "object" ) { assert ( self . value ) ; return self . value . isValid ( ) ; } else { assert ( ! self . value ) ; assert ( self . statusCode !== StatusCodes . Good ) ; } return true ; } const schemaDataValue = buildStructuredType ( { baseType : "BaseUAObject" , name : "DataValue" , fields : [ { name : "value" , fieldType : "Variant" , defaultValue : null } , { name : "statusCode" , fieldType : "StatusCode" , defaultValue : StatusCodes . Good } , { name : "sourceTimestamp" , fieldType : "DateTime" , defaultValue : null } , { name : "sourcePicoseconds" , fieldType : "UInt16" , defaultValue : 0 } , { name : "serverTimestamp" , fieldType : "DateTime" , defaultValue : null } , { name : "serverPicoseconds" , fieldType : "UInt16" , defaultValue : 0 } ] } ) ; export interface DataValueOptions { value ? : VariantOptions ; statusCode ? : StatusCode ; sourceTimestamp ? : DateTime ; sourcePicoseconds ? : UInt16 ; serverTimestamp ? : DateTime ; serverPicoseconds ? : UInt16 ; } function toMicroNanoPico ( picoseconds ) { return "" + w ( ( picoseconds / 1000000 ) >> 0 ) + "." + w ( ( ( picoseconds % 1000000 ) / 1000 ) >> 0 ) + "." + w ( picoseconds % 1000 >> 0 ) ; } function d ( timestamp , picoseconds ) { return timestamp ? timestamp . toISOString ( ) + " $ " + toMicroNanoPico ( picoseconds ) : "null" ; } const emptyObject = { } ; const defaultDate = minOPCUADate ; export class DataValue extends BaseUAObject { public static possibleFields : string [ ] = [ "value" , "statusCode" , "sourceTimestamp" , "sourcePicoseconds" , "serverTimestamp" , "serverPicoseconds" ] ; public static schema = schemaDataValue ; public value : Variant ; public statusCode : StatusCode ; public sourceTimestamp : DateTime ; public sourcePicoseconds : UInt16 ; public serverTimestamp : DateTime ; public serverPicoseconds : UInt16 ; class constructor extends BaseUAObject param options { Object } constructor ( options ? ) { super ( ) ; if ( options === null ) { this . statusCode = StatusCodes . Bad ; this . sourceTimestamp = null ; this . sourcePicoseconds = 0 ; this . serverTimestamp = null ; this . serverPicoseconds = 0 ; this . value = new Variant ( null ) ; return ; } options = options || emptyObject ; if ( parameters . debugSchemaHelper ) { const schema = schemaDataValue ; check_options_correctness_against_schema ( this , schema , options ) ; } if ( options . value === undefined || options . value === null ) { this . value = new Variant ( { dataType : DataType . Null } ) ; } else { this . value = options . value ? new Variant ( options . value ) : new Variant ( { dataType : DataType . Null } ) ; } this . statusCode = coerceStatusCode ( options . statusCode || StatusCodes . Good ) ; this . sourceTimestamp = options . sourceTimestamp ? coerceDateTime ( options . sourceTimestamp ) : null ; this . sourcePicoseconds = options . sourcePicoseconds || 0 ; this . serverTimestamp = options . serverTimestamp ? coerceDateTime ( options . serverTimestamp ) : null ; this . serverPicoseconds = options . serverPicoseconds || 0 ; } public encode ( stream ) { encodeDataValue ( this , stream ) ; } public decode ( stream ) { decodeDataValueInternal ( this , stream ) ; } public decodeDebug ( stream , options ) { decodeDebugDataValue ( this , stream , options ) ; } public isValid ( ) { return isValidDataValue ( this ) ; } public toString ( ) { let str = "{ /* DataValue */" ; if ( this . value ) { str += "\n" + "   value: " + Variant . prototype . toString . apply ( this . value ) ; } else { str += "\n" + "   value:            <null>" ; } str += "\n" + "   statusCode:      " + ( this . statusCode ? this . statusCode . toString ( ) : "null" ) ; str += "\n" + "   serverTimestamp: " + d ( this . serverTimestamp , this . serverPicoseconds ) ; str += "\n" + "   sourceTimestamp: " + d ( this . sourceTimestamp , this . sourcePicoseconds ) ; str += "\n" + "}" ; return str ; } public clone ( ) { return new DataValue ( { serverPicoseconds : this . serverPicoseconds , serverTimestamp : this . serverTimestamp , sourcePicoseconds : this . sourcePicoseconds , sourceTimestamp : this . sourceTimestamp , statusCode : this . statusCode , value : this . value ? this . value . clone ( ) : undefined } ) ; } } DataValue . prototype . schema = DataValue . schema ; registerSpecialVariantEncoder ( DataValue ) ; export type DataValueLike = DataValueOptions | DataValue ; function w ( n ) { return ( "0000" + n ) . substr ( - 3 ) ; } function _partial_clone ( dataValue ) { const cloneDataValue = new DataValue ( { value : undefined } ) ; cloneDataValue . value = dataValue . value ; cloneDataValue . statusCode = dataValue . statusCode ; return cloneDataValue ; } export function apply_timestamps ( dataValue , timestampsToReturn , attributeId ) { let cloneDataValue = null ; let now = null ; switch ( timestampsToReturn ) { case TimestampsToReturn . Neither : cloneDataValue = cloneDataValue || _partial_clone ( dataValue ) ; break ; case TimestampsToReturn . Server : cloneDataValue = cloneDataValue || _partial_clone ( dataValue ) ; cloneDataValue . serverTimestamp = dataValue . serverTimestamp ; cloneDataValue . serverPicoseconds = dataValue . serverPicoseconds ; now = now || getCurrentClock ( ) ; cloneDataValue . serverTimestamp = now . timestamp as DateTime ; cloneDataValue . serverPicoseconds = now . picoseconds ; break ; case TimestampsToReturn . Source : cloneDataValue = cloneDataValue || _partial_clone ( dataValue ) ; cloneDataValue . sourceTimestamp = dataValue . sourceTimestamp ; cloneDataValue . sourcePicoseconds = dataValue . sourcePicoseconds ; break ; case TimestampsToReturn . Both : default : assert ( timestampsToReturn === TimestampsToReturn . Both ) ; cloneDataValue = cloneDataValue || _partial_clone ( dataValue ) ; cloneDataValue . serverTimestamp = dataValue . serverTimestamp ; cloneDataValue . serverPicoseconds = dataValue . serverPicoseconds ; now = now || getCurrentClock ( ) ; cloneDataValue . serverTimestamp = now . timestamp as DateTime ; cloneDataValue . serverPicoseconds = now . picoseconds ; cloneDataValue . sourceTimestamp = dataValue . sourceTimestamp ; cloneDataValue . sourcePicoseconds = dataValue . sourcePicoseconds ; break ; } if ( attributeId !== AttributeIds . Value ) { cloneDataValue . sourceTimestamp = null ; } return cloneDataValue ; } export function apply_timestamps_no_copy ( dataValue , timestampsToReturn , attributeId , now ? ) { switch ( timestampsToReturn ) { case TimestampsToReturn . Neither : dataValue . sourceTimestamp = null ; dataValue . sourcePicoseconds = 0 ; dataValue . serverTimestamp = null ; dataValue . serverPicoseconds = 0 ; break ; case TimestampsToReturn . Server : now = now || getCurrentClock ( ) ; dataValue . serverTimestamp = now . timestamp as DateTime ; dataValue . serverPicoseconds = now . picoseconds ; dataValue . sourceTimestamp = null ; dataValue . sourcePicoseconds = 0 ; break ; case TimestampsToReturn . Source : break ; case TimestampsToReturn . Both : default : assert ( timestampsToReturn === TimestampsToReturn . Both ) ; now = now || getCurrentClock ( ) ; dataValue . serverTimestamp = now . timestamp as DateTime ; dataValue . serverPicoseconds = now . picoseconds ; break ; } if ( attributeId !== AttributeIds . Value ) { dataValue . sourceTimestamp = null ; } return dataValue ; } function apply_timestamps2 ( dataValue , timestampsToReturn , attributeId ) { assert ( attributeId > 0 ) ; assert ( Object . prototype . hasOwnProperty . call ( dataValue , "serverTimestamp" ) ) ; assert ( Object . prototype . hasOwnProperty . call ( dataValue , "sourceTimestamp" ) ) ; const cloneDataValue = new DataValue ( { } ) ; cloneDataValue . value = dataValue . value ; cloneDataValue . statusCode = dataValue . statusCode ; const now = getCurrentClock ( ) ; switch ( timestampsToReturn ) { case TimestampsToReturn . Server : cloneDataValue . serverTimestamp = dataValue . serverTimestamp ; cloneDataValue . serverPicoseconds = dataValue . serverPicoseconds ; cloneDataValue . serverTimestamp = now . timestamp as DateTime ; cloneDataValue . serverPicoseconds = now . picoseconds ; break ; case TimestampsToReturn . Source : cloneDataValue . sourceTimestamp = dataValue . sourceTimestamp ; cloneDataValue . sourcePicoseconds = dataValue . sourcePicoseconds ; break ; case TimestampsToReturn . Both : cloneDataValue . serverTimestamp = dataValue . serverTimestamp ; cloneDataValue . serverPicoseconds = dataValue . serverPicoseconds ; cloneDataValue . serverTimestamp = now . timestamp as DateTime ; cloneDataValue . serverPicoseconds = now . picoseconds ; cloneDataValue . sourceTimestamp = dataValue . sourceTimestamp ; cloneDataValue . sourcePicoseconds = dataValue . sourcePicoseconds ; break ; } if ( attributeId !== AttributeIds . Value ) { cloneDataValue . sourceTimestamp = null ; } return cloneDataValue ; } function _clone_with_array_replacement ( dataValue , result ) { const statusCode = result . statusCode === StatusCodes . Good ? dataValue . statusCode : result . statusCode ; const clonedDataValue = new DataValue ( { statusCode , serverTimestamp : dataValue . serverTimestamp , serverPicoseconds : dataValue . serverPicoseconds , sourceTimestamp : dataValue . sourceTimestamp , sourcePicoseconds : dataValue . sourcePicoseconds , value : { dataType : DataType . Null } } ) ; clonedDataValue . value . dataType = dataValue . value . dataType ; clonedDataValue . value . arrayType = dataValue . value . arrayType ; clonedDataValue . value . dimensions = result . dimensions ; clonedDataValue . value . value = result . array ; return clonedDataValue ; } function canRange ( dataValue ) { return ( dataValue . value && ( dataValue . value . arrayType !== VariantArrayType . Scalar || ( dataValue . value . arrayType === VariantArrayType . Scalar && dataValue . value . dataType === DataType . ByteString ) || ( dataValue . value . arrayType === VariantArrayType . Scalar && dataValue . value . dataType === DataType . String ) ) ) ; } param dataValue { DataValue } param indexRange { NumericalRange } return { DataValue } export function extractRange ( dataValue , indexRange ) { const variant = dataValue . value ; if ( indexRange && canRange ( dataValue ) ) { if ( ! indexRange . isValid ( ) ) { return new DataValue ( { statusCode : StatusCodes . BadIndexRangeInvalid } ) ; } const result = indexRange . extract_values ( variant . value , variant . dimensions ) ; dataValue = _clone_with_array_replacement ( dataValue , result ) ; } else { dataValue = dataValue . clone ( ) ; } return dataValue ; } function sameDate ( date1 , date2 ) { if ( date1 === date2 ) { return true ; } if ( date1 && date2 === null ) { return false ; } if ( date1 === null && date2 ) { return false ; } if ( date1 === null || date2 === null ) { return false ; } return date1 . getTime ( ) === date2 . getTime ( ) ; } export function sourceTimestampHasChanged ( dataValue1 , dataValue2 ) { return ( ! sameDate ( dataValue1 . sourceTimestamp , dataValue2 . sourceTimestamp ) || dataValue1 . sourcePicoseconds !== dataValue2 . sourcePicoseconds ) ; } export function serverTimestampHasChanged ( dataValue1 , dataValue2 ) { return ( ! sameDate ( dataValue1 . serverTimestamp , dataValue2 . serverTimestamp ) || dataValue1 . serverPicoseconds !== dataValue2 . serverPicoseconds ) ; } export function timestampHasChanged ( dataValue1 , dataValue2 , timestampsToReturn ? ) { if ( timestampsToReturn === undefined ) { return sourceTimestampHasChanged ( dataValue1 , dataValue2 ) ; } switch ( timestampsToReturn ) { case TimestampsToReturn . Neither : return false ; case TimestampsToReturn . Both : return sourceTimestampHasChanged ( dataValue1 , dataValue2 ) || serverTimestampHasChanged ( dataValue1 , dataValue2 ) ; case TimestampsToReturn . Source : return sourceTimestampHasChanged ( dataValue1 , dataValue2 ) ; default : assert ( timestampsToReturn === TimestampsToReturn . Server ) ; return serverTimestampHasChanged ( dataValue1 , dataValue2 ) ; } } export function sameStatusCode ( statusCode1 , statusCode2 ) { return statusCode1 . value === statusCode2 . value ; } method param v1 { DataValue } param v2 { DataValue } param timestampsToReturn { TimestampsToReturn } return { boolean } export function sameDataValue ( v1 , v2 , timestampsToReturn ? ) { if ( v1 === v2 ) { return true ; } if ( v1 && ! v2 ) { return false ; } if ( v2 && ! v1 ) { return false ; } if ( ! sameStatusCode ( v1 . statusCode , v2 . statusCode ) ) { return false ; } if ( timestampHasChanged ( v1 , v2 , timestampsToReturn ) ) { return false ; } return sameVariant ( v1 . value , v2 . value ) ; } export interface DataValueOptionsT < T , DT extends DataType > extends DataValueOptions { value : VariantOptionsT < T , DT > ; } export declare interface DataValueT < T , DT extends DataType > extends DataValue { value : VariantT < T , DT > ; } export class DataValueT < T , DT extends DataType > extends DataValue { }