V6 Formula to extract 2 int8_t values from a Modbus ushort register

Forum Home Forums Understanding the Software Using Formulas V6 Formula to extract 2 int8_t values from a Modbus ushort register

Tagged: 

Viewing 9 posts - 1 through 9 (of 9 total)
  • Author
    Posts
  • #12582
    Merdock
    Participant

    Hello, I have this formula that extracts 2 int8 values from a holding register.
    in the channel I apply it as follows:
    the value is read in channel 118
    “GetI8ByteDirect(Val(118),0)” here I get the first byte.
    “GetI8ByteDirect(Val(118),1)” here I get the value of the second byte.
    2 queries.
    1 – note that when using bitmask the formula that is loaded is: “GetBit(DataRel(-1), 0)” for bit 0 and so on for the other bits.
    What function does DataRel() fulfill?
    Also note that in the webstation in the table if the device is not connected it shows “–” and my formula shows “0”
    How do I make my formula also show “–” when the device is disconnected?
    2- How do I write or modify the 2 Bytes of my formula with a command?
    Something like putting in the output channel SetI8ByteDirec(Value, 0 or 1)

    //This function takes the same arguments as the original function:
    //a value of type double called val and a value of type int called n.
    //The function returns a value of type byte.
    public sbyte GetI8ByteDirect(double val, int n) {
         int lVal = (int)val;
         return (sbyte)((lVal >> (n * 8)) & 0xFF);
    }
    //Inside the function, the val value is converted to a value of type ulong and stored in the variable ulVal.
    //Then shift the bits of ulVal (n * 8) positions to the right and apply an AND operation with the value 0xFF.
    //The result of this operation is returned as a value of type byte.
    
    //This function returns the value of the byte at position n of the value val.
    //For example, if you want to get the first byte of the value val,
    //you can call the function with the argument n equal to 0.
    //If you want to get the second byte of the val value,
    //you can call the function with the argument n equal to 1, and so on.
    
    #12583
    manjey73
    Participant

    Get I 8 Byte Direct(Vol(118),0); Stat(118)

    #12584
    manjey73
    Participant

    2 One of the ways. You need to turn the CmdVal into a byte, Then, depending on whether it is older or younger, add it with the calculated byte of the second half and only then send the command. For example, if you change the highest byte. (Convert.Toint16(Cmd) >> 8) || Convert Uint16(Val(119)) where 119 is the value that you do not change and it is the lowest byte. Something like that

    I haven’t checked. Perhaps you can solve it differently or turn it into bytes beforehand, or apply a mask, and so on

    #12585
    manjey73
    Participant

    Well, bind the control command to the required channel that you are changing. To change the major and minor bytes, the write formulas will be different, just as you have made the formulas for obtaining the minor and major bytes different.

    #12586
    manjey73
    Participant

    Or for example like this

    For Low Byte
    Mask Input channel 118 – X = (ushort)Val(118) && 0xFF00
    Further – X || (ushort)Cmd

    For Older Byte
    Mask Input channel 118 – X = ((ushort)Val(118)>>8) && 0xFF
    Further – X || ((ushort)Cmd>>8)

    • This reply was modified 11 months, 2 weeks ago by manjey73.
    #12588
    Merdock
    Participant

    @manjey73 Works perfect, thanks GetI8ByteDirect(Vol(118),0); Stat(118)

    • This reply was modified 11 months, 2 weeks ago by Merdock.
    #12590
    Merdock
    Participant

    Fixed, full explanation:
    Formula to read 2 int8_t data stored in 1 holding register.

    
    public byte GetUi8Byte(double val, int n)
    {
         int lVal = (int)val;
         return (byte)((lVal >> ((1 - n) * 8)) & 0xFF);
    }
    

    Script to write uint8_t values to the output

    
    public double InsertByte(double valueToInsert, double destinationValue, bool insertInMostSignificantByte)
    {
         // Convert the input values to the corresponding types
         ushort val = Convert.ToUInt16(valueToInsert);
         ushort destVal = Convert.ToUInt16(destinationValue);
        
         ushort result;
         if (insertInMostSignificantByte)
         {
             result = (ushort)((destVal & 0x00FF) | (val << 8));
         }
         else
         {
             result = (ushort)((destVal & 0xFF00) | val);
         }
        
         // Convert the result to double before returning it
         return Convert.ToDouble(result);
    }
    

    We create 3 input channels in the table:
    Channel 101 = data of 16 uint16_t
    Channel 102 = uint8_t data 1
    Input formula for channel 102 GetUi8Byte(Val(101),0); Stat(101)
    Channel 103 = data 2 uint8_t
    Input formula for channel 103 GetUi8Byte(Val(101),1); Stat(101)

    Formula to write to the output channel:
    Channel 102 InsertByte(Cmd, Val(101), true)
    channel 103 InsertByte(Cmd, Val(101), false)

    • This reply was modified 11 months, 2 weeks ago by Merdock.
    #12592
    Merdock
    Participant

    Add an overflow protection for the uint8_t value, so if it exceeds the value of 255 it will set the command to 255.
    You could also make it not modify the output value if the limit of 255 is exceeded.

    
    public double InsertByte(double valueToInsert, double destinationValue, bool insertInMostSignificantByte)
    {
        // Protect against overflow of valueToInsert
        if (valueToInsert > 255)
        {
            valueToInsert = 255;
        }
        
        // Convert the input values to the corresponding types
        ushort val = Convert.ToUInt16(valueToInsert);
        ushort destVal = Convert.ToUInt16(destinationValue);
        
        ushort result;
        if (insertInMostSignificantByte)
        {
            result = (ushort)((destVal & 0x00FF) | (val << 8));
        }
        else
        {
            result = (ushort)((destVal & 0xFF00) | val);
        }
        
        // Convert the result to double before returning it
        return Convert.ToDouble(result);
    }
    

    Here the protection of the input value is that nothing is modified if the value exceeds 255

    public double InsertByte(double valueToInsert, double destinationValue, bool insertInMostSignificantByte)
    {
        // We check if the value to insert is greater than 255 and we return the destination variable unchanged
        if (valueToInsert > 255)
        {
            return destinationValue;
        }
        
        // Convert the input values to the corresponding types
        ushort val = Convert.ToUInt16(valueToInsert);
        ushort destVal = Convert.ToUInt16(destinationValue);
        
        ushort result;
        if (insertInMostSignificantByte)
        {
            result = (ushort)((destVal & 0x00FF) | (val << 8));
        }
        else
        {
            result = (ushort)((destVal & 0xFF00) | val);
        }
        
        // Convert the result to double before returning it
        return Convert.ToDouble(result);
    }
    
    • This reply was modified 11 months, 2 weeks ago by Merdock.
    #12610
    Mikhail
    Moderator

    Hello,

    1 – note that when using bitmask the formula that is loaded is: “GetBit(DataRel(-1), 0)” for bit 0 and so on for the other bits.
    What function does DataRel() fulfill?

    The DataRel function provides data (value and status) of the channel having the specified offset from the current channel. You can find this function in the Scripts table.

    How do I make my formula also show “–” when the device is disconnected?

    In Rapid SCADA 6 a formula can return value of the CnlData type that contains value and status. In some cases, it allows to make formulas shorter. Using ; as before is also supported. See GetBit implementation.

    How do I write or modify the 2 Bytes of my formula with a command?

    Command value is provided by Cmd variable of the double type.

Viewing 9 posts - 1 through 9 (of 9 total)
  • You must be logged in to reply to this topic.