Precision Loss Due 64-bit data

Forum Home Forums Development and Integration Precision Loss Due 64-bit data

Viewing 13 posts - 1 through 13 (of 13 total)
  • Author
    Posts
  • #15353
    showmustgoon
    Participant

    I’ve encountered a precision loss issue when sending some 64-bit data.
    During my tests with data exchange between CodeSys and RapidSCADA, I noticed the following problem. In CodeSys, I wrote the following values:

    LINT1  := 9223372036854775807;
    ULINT1 := 18446744073709551615;
    LWORD1 := 18446744073709551615;
    

    However, in RapidSCADA, the values were received as:

    9223372036854775808
    18446744073709551616
    18446744073709551616
    

    After tracing the code, it appears that the precision loss occurs because all data in the Scada system is converted to double during processing. The specific code is as follows:

    /// <summary>
    /// Sets the value and status of the tag, updates the tag data type and format according to the value type.
    /// </summary>
    public void Set(DeviceTag deviceTag, object val, int stat)
    {
        if (deviceTag == null)
            throw new ArgumentNullException(nameof(deviceTag));
    
        if (val is string strVal)
        {
            deviceTag.DataType = TagDataType.Unicode;
            deviceTag.Format = TagFormat.String;
            SetUnicode(deviceTag.Index, strVal, stat);
        }
        else if (val is DateTime dtVal)
        {
            deviceTag.DataType = TagDataType.Double;
            deviceTag.Format = TagFormat.DateTime;
            SetDateTime(deviceTag.Index, dtVal, stat);
        }
        else
        {
            deviceTag.DataType = TagDataType.Double;
            deviceTag.Format = TagFormat.FloatNumber;
            Set(deviceTag.Index, Convert.ToDouble(val), stat);
        }
    }
    

    I believe the precision is lost during this conversion. One simple solution I can think of is to modify the val field in CnlData to use the decimal type instead of double. Do you think this approach would work?

    #15355
    manjey73
    Participant

    Link

    Perhaps this will help you when using LReal (long). Well, look at other methods of working with long, ulong in BitConverter

    That is, first convert your string to long using TryParse and only then add it to the channel via BitConverter. At the same time specifying the Integer data type for example

    • This reply was modified 1 month ago by manjey73.
    • This reply was modified 1 month ago by manjey73.
    #15361
    showmustgoon
    Participant

    That’s a good idea. I believe the parsing logic needs to be modified accordingly as well. this might affect some built-in functionalities of the system (such as statistics). I’ll try to resolve the issue in this direction.

    #15367
    Mikhail
    Moderator

    How do you transfer data between CodeSys and RapidSCADA?
    By default, channel values uses Double type. If you change a channel to use Int64 type, it can help. However, it may depends on a transfer method.

    #15369
    showmustgoon
    Participant

    I’m using OPC UA right now to collect data and send commands between SCADA and CodeSys.
    The main issue we’ve noticed is that large 64-bit integers lose precision when being collected and uploaded.
    I’m not sure yet if this is a big enough concern to address and are still deciding whether to make adjustments or just limit operations to avoid the issue.

    #15390
    Mikhail
    Moderator

    I’ve checked the source code of the driver. It converts numbers to Double.
    Do you have any ideas on how to get around this?

    #15394
    showmustgoon
    Participant

    Some other SCADA products I know of require configuring both the original data type and the converted data type to ensure the expected data conversion. For this purpose, the optional original types can be a very complex list, covering thousands of types across dozens of protocols.

    In RapidSCADA, the type conversion is done automatically internally, converting to double for subsequent calculations, statistics, and other logic.

    I believe data of this length is not a common requirement. If such a situation does arise, I might consider adding an additional type attribute when configuring the data point, performing extra processing during conversion, and potentially abandoning the calculation and statistical functionalities.

    Additionally, I’m not sure if replacing double with decimal is a good idea. I only know that decimal offers more precision but am not familiar with the underlying logic.

    #15395
    manjey73
    Participant

    You can check the data type of the channel – integer or double (or null by default) and perform different transformations depending on this.

    Something like this. where cnl.Values is my Cnl dictionary

    if (cnl.Value.DataTypeID == 0 || cnl.Value.DataTypeID == null) // channel data type Double
    {
    ServerContext.WriteCurrentData(cnl.Value.CnlNum, new CnlData(valDouble, 1));
    }
    else if (cnl.Value.DataTypeID == 1) // channel data type Int64
    {
    long val = Convert.ToInt64(fieldObj);
    ServerContext.WriteCurrentData(cnl.Value.CnlNum, new CnlData(BitConverter.Int64BitsToDouble(val), 1));
    }
    
    • This reply was modified 1 month ago by manjey73.
    #15399
    showmustgoon
    Participant

    You can check the data type of the channel – integer or double (or null by default) and perform different transformations depending on this.

    Your solution is great, I am planning to additionally extend the INT64 and UINT64 types in the channel table.

    #15406
    manjey73
    Participant

    You can assign the channel data type directly in the driver. Including in the View, you also generate code that allows you to use the Channel Creation Wizard based on your driver’s code. That is, the user does not need to think about what type of channel needs to be set for the signal, the channel creation wizard will do everything necessary.

    I don’t remember if it is possible to check channel types from the driver code, since it is configured even before you create channels, but it can definitely help create channels and even specify the required formulas. For example, look at the Modbus code when setting up the bitmask.

    #15410
    showmustgoon
    Participant

    My consideration is that when writing data to a channel via a command, it is still necessary to know the channel type in order to process the data entered by the user with special handling, rather than simply converting it directly to a double. Of course, this step can be automatically adapted by the driver.

    #15415
    manjey73
    Participant

    I did not try to check the channel type from the driver immediately before recording, for example, if I created an integer channel directly by the driver, and the user then changed it to double.

    If there is such an opportunity, then it is very good.

    #15429
    Mikhail
    Moderator

    It’s possible that received values are rounded by the driver. I’m sure sure. You should get channel data as a byte array and play with it.

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