Не работает precision c типом value в Homeui для Virtual Device

Добрый день.
Требуется ограничить ввод в поле WEB UI одним знаком после запятой
в конвенции https://github.com/wirenboard/conventions/blob/main/README.md нашел следующее

Generic value type control

A control for a arbitrary value.

* Meta type value: value
* Possible values: float Different values can be set by publishing an arbitrary float that is in range from `min` to `max`. Precision could be specified in `precision` property. The value is rounded to defined precision by a driver and it is also used by `wb-mqtt-homeui` during user input validation

что гугл переводит как

 Элемент управления произвольным значением. 

Мета-тип значения: value Возможные значения: float 

Различные значения могут быть установлены путем публикации произвольного значения
float, находящегося в диапазоне от min до max.
 *Точность может быть указана в свойстве precision*. 
Значение округляется до заданной точности драйвером,
 а также используется **wb-mqtt-homeui** при проверке пользовательского ввода.

Распространяется ли это утверждение на реализацию типа “value” в виртуальном устройстве?. Потому что у меня в поле объявленное как

// Уставка температуры
                HeatSP: {
                    title: { 
                        en: "SetPoint Temp.", 
                        ru: "Уставка температуры" 
                    },
                    type: "value",
                    value: 150,
                    units: "°C",
                    readonly: true,
                    precision: 0.1,
                    order: 3,
                },

Можно ввести и 12.345657 в виджете и оно никак не обрезается (не округляется) до заданной точности.
P.S. флаг readonly перед редактированием естественно снимаю (false) . Update да и min max не отрабатывают, если я правильно понял следующее

Possible values: float Different values can be set by publishing an arbitrary float that is in range from min to max
то есть если здесь говорится не за тип range, а за диапазон от min до max

Здравствуйте!

Рекомендую рассмотреть следующий вариант округления температуры CPU:

defineRule("update_cpu_temp", {
    whenChanged: "hwmon/CPU Temperature",
    then: function (newValue, devName, cellName) {
        // Округление до одного знака после запятой
        var roundedValue = Math.round(newValue * 10) / 10;
        // Обновление значения в виртуальном устройстве
        dev["cpu_temp_rounded"]["Temperature"] = roundedValue;
    }
});

Этот код выполняет округление значения температуры до одного знака после запятой и обновляет соответствующую ячейку в виртуальном устройстве

Спасибо Александр за Ваш ответ. В правилах округлить всегда можно было (я и сам так делаю), вопрос был именно про precision в Виртуальном устройстве. Задача ограничивать ввод , сейчас сделал через подписку на этот топике и в нем проверяю, если ввели больше или меньше, заодно и округляю до нужного. Но это же не совсем то (если полей ввода много приходится делать много движений - или множество подписок или много проверок в одном правиле множественной подписки). Ведь у каждого контрола свои настройки (точность, пределы мин и мах). Просто происходит путаница что к чему относится - что то только для виртуальных устройств, что то только для физических, а некоторые параметры для обоих.

Вот пример подписки на контрол с ограничением вводимого значения в интерфейсе

return defineRule(RuleName + "_" + this.NameVD, {               // Функция подписки на изменение значения топика "/Out_SetFlow", физическое значение уставки расхода в WEB UI 
            whenChanged: self.NameVD + self.nSetOutFlow,                // "/Out_SetFlow"
            then: function (newValue, devName, cellName) {
                                                                        // Проверка введенного кода и его ограничение до расчета (иначе незатухающие изменения - "качели")
                newValue = newValue * self.outRatioGFC;                 // приводим расход к мл/мин  (справка 0,01В = 1 мл/мин) с учетом коэффициента
                if (newValue > self.LimitOutMAX * self.outRatioGFC) {   // Проверяем выход за пределы и если больше то ограничиваем ввод
                    newValue = self.LimitOutMAX;
                    dev[devName + "/" + cellName] = newValue;           // Верхним значением лимита
                } else if (newValue < 0) {                              // Если значение меньше 0 то ограничиваем 
                    newValue = 0;
                    dev[devName + "/" + cellName] = newValue;           // Нижним значением лимита или 0
                };
            },
        });

Что позволяет не дать пользователю ввести не правильное значение в поле в WEB

ну и округление также только через переменные
вначале скрипта указываем необходимую точность


Precision   : 3,                                // Значение до какого знака округлять рассчитанный по таблице калибровки выходной сигнал канала.

this.Precision      = Math.pow (10,DeviceParam.Precision);  // Требуемая точность отображения (знак после запятой) в виджете WEB UI 

далее в коде

return Math.round(GetValue * this.Precision) / this.Precision;    // Быстрое преобразование в число c округлением до заданной точности 

Александр, правильно ли я понимаю что, то что я спрашивал в первом посте не работает с Виртуальными устройствами, а используется в основном для шаблонов описывающих физические устройства (Modbus RTU, TCP и т.д.)?

Я пока пробую воспроизвести ваш сценарий, но пока не выходит.

А что не выходит? Если не секрет.
Если мной ожидаемое поведение отсюда

HeatSP: {
                    title: { 
                        en: "SetPoint Temp.", 
                        ru: "Уставка температуры" 
                    },
                    type: "value",
                    value: 150,
                    min: 100,
                    max: 200,
                    units: "°C",
                    readonly: true,
                    precision: 0.1,
                    order: 3,
                },

то скорее всего не сработает как и у меня, но min max сработает для типа “range”. А код ниже он не полный, но общий подход виден.

Выполняю округление до десятых, по тому же принципу, что и у вас.

Добрый день, на данный момент данный функционал не доступен, оформлена заявка на реализацию данного функционала.

Вот о чем я говорил . после создания устройства обновите страницу, а то иногда теряется переключатель доступа к редактированию . Для того что бы увидеть результат округления и ограничения по лимиту нужно уйти из поля или закрыть доступ к редактированию т.е. чтобы значение опубликовалось в MQTT и сработало правило подписки

// Объект для хранения созданных виртуальных устройства "Test"
var oTest = {
    Test_1: null, // Экземпляр виртуального устройства "Test_1"
    Test_2: null, // Экземпляр виртуального устройства "Test_2"
};

// Объект с переменными необходимыми при создании экземпляра виртуального устройства "Мой тест 1"
var TestDevice_1 = {
    NameVD: "MuTest_1",
    NameEN: "MuTest",
    NameRU: "Мой тест",
    nTopic: "/Mu_Test",
    nSwitch: "/swAccessEdit",
    Precision: 2,
    LimitMax: 1000,
    LimitMin: 10,
};
// Объект с переменными необходимыми при создании экземпляра виртуального устройства "Мой тест 2"
var TestDevice_2 = {
    NameVD: "MuTest_2",
    NameEN: "MuTest 2",
    NameRU: "Мой тест 2",
    nTopic: "/Mu_Test",
    nSwitch: "/swAccessEdit",
    Precision: 3,
    LimitMax: 100,
    LimitMin: 10,
};

//-------------------------------------СОЗДАНИЕ ВИРТУАЛЬНОГО УСТРОЙСТВА-----------------------------------------------------------//
// Функция создание виртуального устройства "Мой тест"
function vdMuTest(DeviceParam) {
    try {
        this.NameVD = DeviceParam.NameVD; // Имя виртуального устройства (Мой тест)
        this.NameEN = DeviceParam.NameEN; // Имя выводимое в Web UI при анг.языке интерфейса
        this.NameRU = DeviceParam.NameRU; // Имя выводимое в Web UI при рус.языке интерфейса
        this.Precision = Math.pow(10, DeviceParam.Precision); // Требуемая точность отображения (знак после запятой) в виджете WEB UI
        this.LimitMax = DeviceParam.LimitMax; // Верхний предел вводимого значения
        this.LimitMin = DeviceParam.LimitMin; // Нижний предел  вводимого значения
        this.Topic = DeviceParam.nTopic; // Отслеживаемый топик ввода значения
        this.Switch = DeviceParam.nSwitch; // Переключатель для включения доступа к переменным
        this.vDeviceTest = null; // Свойство для хранения экземпляра виртуального устройства
        this.RuleChangeValue = null; // Свойство для хранения экземпляра правила подписки на изменение значения в WEB UI
        this.RuleChangeEdit = null; // Свойство для хранения экземпляра правила подписки на открытие доступа к редактированию
    } catch (error) {
        log.error("GT try vdMuTes {}, ERR = {}", DeviceParam.NameVD, error);
    }
}

//-------------------------------------СОЗДАНИЕ ВИРТУАЛЬНОГО УСТРОЙСТВА В MQTT и WEB UI-------------------------------------------//
// Функция создания экземпляра виртуального устройства (мой тест) в MQTT и WEB UI
vdMuTest.prototype.CreateVirtualDevice = function () {
    try {
        var self = this; // Сохраняем ссылку на контекст
        return defineVirtualDevice(this.NameVD, {
            // имя виджета в GUI
            title: {
                en: "Device " + this.NameEN,
                ru: "Устройство " + this.NameRU,
            },
            cells: {
                //Разделитель  для WEB_GUI
                Separator: {
                    title: { en: "", ru: "" },
                    type: "text",
                    value: "--------------------------- ТЕСТ 1 Wirenboard -----------------------",
                    forceDefault: true,
                    readonly: true,
                    order: 1,
                },
                // Пробуем ограничить встроенными средствами
                WB_Test: {
                    title: {
                        en: "WB test limit and round",
                        ru: "WB тест ограничения и округления",
                    },
                    type: "value",
                    value: 150,
                    min: 0,
                    max: 65534,
                    precision: 0.1,
                    forceDefault: true,
                    readonly: true,
                    order: 2,
                },
                //Разделитель  для WEB_GUI
                Separator2: {
                    title: { en: "", ru: "" },
                    type: "text",
                    value: "-----------ТЕСТ 2 реализация через правило------------",
                    forceDefault: true,
                    readonly: true,
                    order: 3,
                },
                // Тест 2 ограничиваем через правило
                Mu_Test: {
                    title: {
                        en: "test limit and round ",
                        ru: "Тест ограничения и округления 10 <x> 1000",
                    },
                    type: "value",
                    value: 0,
                    readonly: true,
                    order: 4,
                },

                // Переключатель для доступа к редактированию калибровочной таблицы из WEB UI
                swAccessEdit: {
                    title: { en: "Access EDIT", ru: "Доступ к редактированию" },
                    type: "switch",
                    value: "false",
                    forceDefault: true,
                    order: 5,
                },
            },
        });
    } catch (error) {
        log.error(
            "GT try CreateVirtualDevice: = {}, error = {}",
            self.NameVD,
            error
        );
    }
};

//--------------------------СОЗДАНИЕ ЭКЗЕМПЛЯРОВ ПРАВИЛ ПОДПИСОК НА ИЗМЕНЕНИЕ ЗНАЧЕНИЙ--------------------------------------------//
//Функция  подписки на изменения значения в топике и не дает ввести неправильное значение в WEB UI, а также  округляет его но требуемой величины
vdMuTest.prototype.GetValue = function (RuleName) {
    try {
        var self = this; // Сохраняем ссылку на текущий контекст

        // Единичное копирование и расчет переменных при создании устройства
        //..................

        return defineRule(RuleName + "_" + this.NameVD, {
            // Функция подписки на изменение значения топика "/Mu_Test", физическое значение уставки ... в WEB UI
            whenChanged: self.NameVD + self.Topic, // "/Mu_Test"
            then: function (newValue, devName, cellName) {
                log("newValue = " + newValue);
                newValue =
                    Math.round(newValue * self.Precision) / self.Precision; // Быстрое преобразование в число c округлением до заданной точности
                log("Round newValue = " + newValue);
                // Проверка введенного кода и его ограничение до расчета (иначе незатухающие изменения - "качели")
                if (newValue > self.LimitMax) {
                    // Проверяем выход за пределы и если больше то ограничиваем ввод
                    newValue = self.LimitMax;
                    //dev[devName + "/" + cellName] = newValue;      // Верхним значением лимита
                } else if (newValue < self.LimitMin) {
                    // Если значение меньше 0 то ограничиваем
                    newValue = self.LimitMin;
                    //dev[devName + "/" + cellName] = newValue;      // Нижним значением лимита или 0
                }
                dev[devName + "/" + cellName] = newValue;
            },
        });
    } catch (error) {
        log.error("GT try GetValue: Device = {}, ERR = {}", self.NameVD, error);
    }
};

// Функция подписки на изменения переключателя в WEB UI и включения доступа к редактированию значений
vdMuTest.prototype.EnableSettingsEdit = function (RuleName) {
    try {
        var self = this; // Сохраняем ссылку на текущий контекст
        return defineRule(RuleName + "_" + this.NameVD, {
            whenChanged: self.NameVD + self.Switch, // топик, при изменении которого сработает правило
            then: function (newValue, devName, cellName) {
                var isEdit = +!newValue; // Быстрая конвертация в число
                // Открываем доступ к полям ввода в WEB UI
                dev[devName + "/WB_Test#readonly"] = isEdit;
                dev[devName + "/Mu_Test#readonly"] = isEdit;

                // при закрытии доступа ...... Что то делаем
                if (!newValue) {
                    //...... Что то делаем
                }
            },
        });
    } catch (error) {
        log.error(
            "GT try EnableSettingsEdit: Device = {}, ERR = {}",
            self.NameVD,
            error
        );
    }
};

//---------------------СОЗДАНИЕ ЭКЗЕМПЛЯРОВ ВИРТУАЛЬНЫХ УСТРОЙСТВ-------------------------------------------------//
// Функция создает  экземпляр виртуального устройства с необходимыми для его работы подписками на топики MQTT
function CreateDevice_Test(PropertyNameTest, CreateDataTest) {
    try {
        // Создание экземпляра виртуального устройства Тест
        if (PropertyNameTest === null) {
            PropertyNameTest = new vdMuTest(CreateDataTest);
        }

        // Создание виртуального устройства "ТЕСТ" в MQTT и WEB UI
        if (PropertyNameTest.vDeviceTest === null) {
            PropertyNameTest.vDeviceTest =
                PropertyNameTest.CreateVirtualDevice();
        }

        // Создание подписки на изменение значения поля в WEB UI  ссылку на объект записываем в поле экземпляра
        if (PropertyNameTest.RuleChangeValue === null) {
            PropertyNameTest.RuleChangeValue =
                PropertyNameTest.GetValue("RuleGetValue");
        }

        // Создание подписки на изменение переключателя  для доступа к редактированию ссылку на объект записываем в поле экземпляра
        if (PropertyNameTest.RuleChangeEdit === null) {
            PropertyNameTest.RuleChangeEdit =
                PropertyNameTest.EnableSettingsEdit("RuleChangedEnSetEdit");
        }
    } catch (error) {
        log.error(
            "GT try CreateDevice_Test: Device =  {}, error = {}",
            CreateDataTest.NameVD,
            error
        );
    }
}

CreateDevice_Test(oTest.Test_1, TestDevice_1); // Вызов функции создания виртуального устройства "Тест_1"
//CreateDevice_Test (oTest.Test_2, TestDevice_2);   // Вызов функции создания виртуального устройства "Тест_2"

LimitAndPrcisionTest.zip (2,9 КБ)

1 лайк

Добрый день, на данный момент данный функционал не доступен, оформлена заявка на реализацию данного функционала.

Спасибо Александр. может заодно подвезут и изменения цветом отсюда

чтобы можно было установить флаг как отображать в WEB UI красным или красным с оранжевым значком.