reading 64-bit double 4 register tag

I am trying to read The active energy from Energy meter called Pac3200 but the tag is defined in double format and is stored in 4 registers(64-bit).

as far as I know there is no data type in citect can interpret double data type and I tried to use other data types such as real, long, Ulong but it did not work and I ended up reading every register alone as int or every two register as real or long.

is there any solutions or cicode function that could convert the two real tags into double format?

  • You can find cicode for this from citect Toolbox.
  • That would be great if the current website had not hidden Citect Toolbox to make it inaccessible.
  • It's not hidden...it's just hard to find. Log in to Aveva Support, go to Community, Citect Community, Toolbox. Unfortunately, the categories are gone so you have to use the search bar to find anything.

    Maybe Bhadresh can tell what he was referring to for floating point values. All I found was my code to convert 2x 32-bit LONGs into a 64-bit INT: https://softwareforums.aveva.com/citect_community/cit-toolbox/f/187/t/23033
  • OK, I found some VB6 code on the Internet for creating a 64 bit double precision floating point value from a binary value stored as a string. It isn't especially efficient since it uses string conversions but it should work. I just added a wrapper function to convert 4 integer registers into binary so it can convert the whole binary string into a double.


    For example, 1234567890.123456 in binary is: 0100000111010010011001011000000010110100100001111110011010110111
    If you read that from a PLC as 4x 16-bit unsigned integers, they will be:
    16850 (0100000111010010)
    25984 (0110010110000000)
    46215 (1011010010000111)
    59063 (1110011010110111)

    Call IntsToDouble(16850, 25984, 46215, 59063) and it will return 1234567890.123456 as a 64-bit REAL.

    Here's the Cicode to add to your project:

    //Converts a 64-bit double-precision floating point value read from a PLC using 4 16-bit integer registers into a 64-bit Cicode REAL
    REAL FUNCTION IntsToDouble(INT nInt1, INT nInt2, INT nInt3, INT nInt4)
    	RETURN Bin2Double(IntToBinary(nInt1) + INTToBinary(nInt2) + INTToBinary(nInt3) + INTToBinary(nInt4));
    END
    
    
    //Convert an INT to a binary string, optionally with spaces every 4 bits
    STRING FUNCTION IntToBinary(INT nValue, INT nBits = 16, INT bAddSpaces = FALSE)
    	STRING 	sBinary;
    	INT 	nBit;
    
    	FOR nBit = 0 TO nBits - 1 DO
    		IF (nBit > 0) AND ((nBit MOD 4) = 0) AND (bAddSpaces = TRUE) THEN
    			sBinary = " " + sBinary;
    		END
    
    		IF (nValue BITAND Pow(2, nBit)) <> 0 THEN
    			sBinary = "1" + sBinary;
    		ELSE
    			sBinary = "0" + sBinary;
    		END
    	END
    	
    	RETURN sBinary;
    END
    
    
    //Convert a 64-bit binary value stored as a string into a double-precision floating point value (64-bit REAL)
    //Original 32-bit VB6 code by WonderWareTran
    REAL FUNCTION Bin2Double(STRING stringdata)
    
    	INT i; 	
    	STRING signed_part; 
    	STRING exponent_part; 
    	STRING floating_part; 
    	
    	INT signed; 
    	INT exponent; 
    	INT float_digit; 
    	
    	REAL floating; 
    	REAL float_number; 
    	REAL float_factor; 
    	REAL rReturn;
    		
    	signed_part = StrLeft(stringdata, 1);
    	exponent_part = StrMid(stringdata, 1, 11);
    	floating_part = StrRight(stringdata, 52);
    
    	ErrLog("Signed:   " + signed_part);
    	ErrLog("Exponent: " + exponent_part);
    	ErrLog("Floating: " + floating_part);
    		
    	signed = Bin2Dec(signed_part);
    	exponent = Bin2Dec(exponent_part);
    	
    	FOR i = 1 TO 52 DO
    		float_digit = Bin2Dec(StrMid(floating_part, i - 1, 1));
    		
    		IF float_digit = 1 THEN
    			floating = float_digit / Pow(2, i);
    			float_number = float_number + floating;
    		END
    	END
    	
    	IF exponent > 0 THEN
    		float_factor = 1 + float_number;
    	ELSE 
    		IF exponent = 0 THEN
    			float_factor = float_number;
    		END
    	END
    	
    	IF exponent > 0 AND exponent < 2047 THEN
    		rReturn = Pow(-1, signed) * Pow(2, (exponent - 1023)) * float_factor;
    	ELSE
    		IF exponent = 0 THEN
    			rReturn = Pow(-1, signed) * Pow(2, -1022) * float_factor;
    		ELSE
    			IF exponent = 2047 THEN
    				rReturn = 0;
    			END
    		END
    	END
    
    	ErrLog(rReturn);
    	RETURN rReturn;
    END
    
    
    
    //Convert a 32-bit binary value stored as a string into a 32-bit floating point value (stored in a 64-bit REAL)
    //Original VB6 code by WonderWareTran
    REAL FUNCTION Bin2Float(STRING stringdata)
    
    	INT i; 	
    	STRING signed_part; 
    	STRING exponent_part; 
    	STRING floating_part; 
    	
    	INT signed; 
    	INT exponent; 
    	INT float_digit; 
    	
    	REAL floating; 
    	REAL float_number; 
    	REAL float_factor; 
    	REAL rReturn;
    		
    	signed_part = StrLeft(stringdata, 1);
    	exponent_part = StrMid(stringdata, 1, 8);
    	floating_part = StrRight(stringdata, 23);
    
    	ErrLog("Signed:   " + signed_part);
    	ErrLog("Exponent: " + exponent_part);
    	ErrLog("Floating: " + floating_part);
    		
    	signed = Bin2Dec(signed_part);
    	exponent = Bin2Dec(exponent_part);
    	
    	FOR i = 1 TO 23 DO
    		float_digit = Bin2Dec(StrMid(floating_part, i - 1, 1));
    		
    		IF float_digit = 1 THEN
    			floating = float_digit / Pow(2, i);
    			float_number = float_number + floating;
    		END
    	END
    	
    	IF exponent > 0 THEN
    		float_factor = 1 + float_number;
    	ELSE 
    		IF exponent = 0 THEN
    			float_factor = float_number;
    		END
    	END
    	
    	IF exponent > 0 AND exponent < 255 THEN
    		rReturn = Pow(-1, signed) * Pow(2, (exponent - 127)) * float_factor;
    	ELSE
    		IF exponent = 0 THEN
    			rReturn = Pow(-1, signed) * Pow(2, -126) * float_factor;
    		ELSE
    			IF exponent = 255 THEN
    				rReturn = 0;
    			END
    		END
    	END
    	
    	RETURN rReturn;
    END
    
    
    //Convert a binary value stored in a string into an integer
    INT FUNCTION Bin2Dec(STRING stringdata)
    	INT n; 
    	INT a;
    	STRING x;
    	INT nReturn;
    
    	n = StrLength(stringdata) - 1;
    	a = n;
    
    	WHILE n > -1 DO
    		x = StrMid(stringdata, ((a + 1) - n) - 1, 1);
    		nReturn = IIf((x = "1"), nReturn + Pow(2, n), nReturn);
    		n = n - 1
    	END
    
    	RETURN nReturn;
    END 
    
    
    //Inline IF function
    STRING FUNCTION IIF(INT bExpression, STRING sTrue, STRING sFalse = "")
    	IF bExpression THEN RETURN sTrue; ELSE RETURN sFalse; END
    END
    
    
  • Yes, Eric Black,

    I was suggesting this only.
  • Hi.
    Cicode REAL variables works until 15 decimal digits without loss. If there are more digits, its starts rounding, which means the least significant digits are lost. For an energy counter, these are often the most important once. In this specific case it is no problem, because according to the Pac3200 datasheet the energy counter has his overflow at 1.0e+12.
    But e.g. the very common energymeters from Carlo Gavazzy store the counters in UINT64: 0 - (2^64)-1. This mean 20 significant decimal digits.
  • Bradley, is there a plan to implement 64 bit to the Modus driver?