dm-script

Unexpected Behavior in RealField Widget Threshold Validation


I'm trying to enforce a value threshold on a realfield input widget in DM-script such that valid values are between 0 and 0.05. When I input 0.06 and click outside the widget, it warns that the value is invalid and resets to the previous value. However, when I input 0.05, no warning is triggered. Then, if I try entering any other value afterward, I get an error stating "Wrong config file."

Below is a simplified version of my code:

Number get_tag_as_number(TagGroup tg, String tag_path, String tag_type, Number low_limit, Number high_limit)
{
    Number tag_value, tag_is_valid
    if (tag_type == "Boolean") tag_is_valid = tg.TagGroupGetTagAsBoolean(tag_path, tag_value)
    if (tag_type == "Number") tag_is_valid = tg.TagGroupGetTagAsNumber(tag_path, tag_value)
    if (!tag_is_valid)
    {
        Result("ERROR: Can not get tag value of " + tag_path + ".\n")
        ShowAlert("Can not get tag value of " + tag_path + ".", 0)
        Exit(0)
    }

    if (tag_value < low_limit || tag_value > high_limit)
    {
        Result("ERROR: Wrong config file, " + tag_path + ".\n")
        ShowAlert("Wrong config file, " + tag_path + ".", 0)
        Exit(0)
    }
    return tag_value
}

Class testUI : UIFrame
{
    TagGroup ui_config_tg
    Void tg_init(Object self)
    {
        ui_config_tg = NewTagGroup()
        ui_config_tg.TagGroupSetTagAsFloat("display_font:init_value", 0.015)
    }
    TagGroup create_realfield_widget(Object self)
    {
        Number low_limit = 0
        Number high_limit = 0.05
        Number init_value = get_tag_as_number(ui_config_tg, "display_font:init_value", "Number", low_limit, high_limit)
        TagGroup realfield_widget
        realfield_widget = DLGCreateRealField(init_value, 16, 3, "action")
        realfield_widget.DLGIdentifier("#realfield_widget")
        return realfield_widget
    }
    Void action(Object self, TagGroup item_tag)
    {
        Number low_limit = 0
        Number high_limit = 0.05
        Number curr_value = item_tag.DLGGetValue()
        Number init_value = get_tag_as_number(ui_config_tg, "display_font:init_value", "Number", low_limit, high_limit)
        
        if (curr_value < low_limit || curr_value > high_limit)
        {
            OKDialog("Font size must between " + low_limit + " to " + high_limit)
            self.LookUpElement("#realfield_widget").DLGValue(init_value)
            item_tag.DLGValue(init_value)
            Exit(0)
        }
        ui_config_tg.TagGroupSetTagAsFloat("display_font:init_value", curr_value)
        Result(curr_value)
    }
    
    TagGroup create_dialog(Object self)
    {
        self.tg_init()
        TagGroup realfield_widget = self.create_realfield_widget()        
        TagGroup tabs = DLGCreateTabList()
        tabs.DLGAddElement(realfield_widget)
        TagGroup dialog = DLGCreateDialog("test")
        dialog.DLGAddElement(tabs)
        return dialog
    }
    
    testUI(Object self)
    {
        self.init(self.create_dialog())
        self.Display("test")
    }
}

Alloc(testUI)

Questions:

  1. Why does the widget behave correctly (warning on 0.06) but not on 0.05, even though 0.05 is the upper boundary?
  2. Is there a potential logic error in my threshold validation that could be causing the "Wrong config file" error on subsequent inputs?
  3. Has anyone encountered similar issues with DM-script's realfield widget or threshold checks?

Any help debugging this issue would be greatly appreciated. Thanks!


Solution

  • The logic is fine, but you are running into rounding issues which are particularly hard to spot on scripting languages with automatic number & string conversions.

    It is saver to stick with ...asNumber to avoid these issues. You need to be type specific only in cases where it matters, like streaming to a binary stream.

    See the following example

    number tag_value = 0.05
    number low_limit = 0
    number high_limit = 0.05
    
    clearresults()
    Result("\n Limits are (double):")
    result("\n: low limit:  "+low_limit)
    result("\n: high limit: "+high_limit)
    
    Result("\n Tag value is (double):")
    result("\n tag value:  "+Format(tag_value,"%20.12f"))
    Result("\n (tag_value - low_limit) : "+(tag_value - low_limit))
    Result("\n (tag_value - high_limit): "+(tag_value - high_limit))
    
    taggroup tg = newTagGroup()
    
    tg.TagGroupSetTagAsNumber("Test",tag_value)
    tg.TagGroupGetTagAsNumber("Test",tag_value)
    
    Result("\n ~~~~~ Now with Tag read & write as NUMBER (internally this is DOUBLE)")
    result("\n tag value:  "+Format(tag_value,"%20.12f"))
    Result("\n (tag_value - low_limit) : "+(tag_value - low_limit))
    Result("\n (tag_value - high_limit): "+(tag_value - high_limit))
    
    tg.TagGroupSetTagAsDouble("Test",tag_value)
    tg.TagGroupGetTagAsDouble("Test",tag_value)
    
    Result("\n ~~~~~ Now with Tag read & write as DOUBLE")
    result("\n tag value:  "+Format(tag_value,"%20.12f"))
    Result("\n (tag_value - low_limit) : "+(tag_value - low_limit))
    Result("\n (tag_value - high_limit): "+(tag_value - high_limit))
    
    tg.TagGroupSetTagAsFloat("Test",tag_value)
    tg.TagGroupGetTagAsFloat("Test",tag_value)
    
    Result("\n ~~~~~ Now with Tag read & write as FLOAT")
    result("\n tag value:  "+Format(tag_value,"%20.12f"))
    Result("\n (tag_value - low_limit) : "+(tag_value - low_limit))
    Result("\n (tag_value - high_limit): "+(tag_value - high_limit))
    

    Alternatively, you can fix your script by not checking against "0" exactly, but a against a small epsilon value that accounts for the precision problem:

    [...]
        number eps = 1e-9
        if ((tag_value - low_limit)< eps || (tag_value - high_limit) > eps)
        {
            Result("ERROR: Wrong config file, " + tag_path + "." + "\n")
            ShowAlert("Wrong config file, " + tag_path + ".", 0)
            Exit(0)
        }