> For the complete documentation index, see [llms.txt](https://navixy.com/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://navixy.com/docs/user/ru/guide/account/iot-logic/nodes/missing-values-in-expressions.md).

# Отсутствующие значения в выражениях

Поймите, почему формула или условие в IoT Logic не возвращает значение, когда отсутствует нужное ей значение, и какие шаблоны сравнений предотвращают это.

Формулы и условия в IoT Logic работают со значениями, которые отправляет GPS-устройство. Когда значение, необходимое формуле, отсутствует, большинство операторов не могут вычислить результат, и формула вообще ничего не выдаёт: не `ложь`, и не ноль. Эта страница объясняет, когда значение пропадает, как выглядит сбой и как писать формулы и условия, которые продолжают работать. Она предназначена для всех, кто настраивает поток IoT Logic, и не предполагает знаний программирования.

Эта страница относится к обоим местам, где вы пишете выражение: к **Formula** полю [Вычисление атрибута](/docs/user/ru/guide/account/iot-logic/nodes/initiate-attribute-node.md) узла и к **поле «Выражение условия» (JEXL)** полю [IF/THEN Логика](/docs/user/ru/guide/account/iot-logic/nodes/logic-node.md) Узле.

## Когда выражение не может вернуть значение

Пустое значение останавливает всё выражение. Это относится к любой стороне вычисления, сравнения размеров, проверки префикса текста или логического `!`, `&&`, или `||`. В формуле и в Анализ данных пустое значение отображается как `null`.

Чтение атрибута, который GPS-устройство никогда не отправляло, само по себе не является ошибкой. Останавливается оператор, которому затем приходится работать с пустым значением. Одно пустое значение останавливает всё выражение целиком, включая части, в которых были значения. Условие, проверяющее четыре геозоны, останавливается на первой, которая не может ответить, и остальные три вообще не проверяются. Исключение — терм в начале выражения, который уже определил результат.

## Что вы видите, когда выражение ничего не выдаёт

Ничто не указывает на сбой. Результат зависит от того, в каком узле находится выражение:

| Где выполняется выражение                                        | Что происходит с этим пакетом данных                                                                      |
| ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| **Formula** полю **Вычисление атрибута** узел                    | У атрибута нет значения для этого пакета, и Анализ данных показывает его как `null`.                      |
| **поле «Выражение условия» (JEXL)** полю **IF/THEN Логика** узел | Данные выходят через соединение ELSE, а узел сохраняет свой атрибут как пустое значение, а не как `ложь`. |

{% hint style="warning" %}
Ничто не сообщает о сбое. В потоке не появляется ошибка, счётчик не меняется, и все остальные атрибуты по-прежнему вычисляются нормально. Единственный признак — само отсутствующее значение, поэтому поток может выглядеть исправным, хотя один атрибут не выдаёт значение неделями.

Сохранение потока тоже не доказывает, что формула работает. Проверка, которая выполняется при сохранении, подтверждает лишь, что выражение записано правильно. Она не запускает выражение на данных вашего GPS-устройства, поэтому формула, которая ничего не выдаёт для каждого пакета, всё равно сохраняется без предупреждения.
{% endhint %}

## Как каждый оператор обрабатывает пустое значение

Четыре оператора всегда продолжают работать с пустым значением: `==`, `!=`, `=~`, и `!~`. Операторы `&&` и `||` продолжают работать только тогда, когда сторона, прочитанная первой, уже определила ответ. Все остальные операторы останавливаются.

Эта таблица охватывает атрибут, на который вы ссылаетесь по имени, то есть форму, которую [автозаполнение](/docs/user/ru/guide/account/iot-logic/nodes/initiate-attribute-node/managing-attributes.md#autofill-attribute-names) вставляет. Четыре оператора ведут себя по-разному, когда значение поступает из `value()` вместо этого. См. [Чтение по имени или с помощью value()](#reading-by-name-or-with-value).

| Оператор                                                        | С пустым значением с любой стороны                                                                                      |
| --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `==`, `!=` (равно, не равно)                                    | Даёт настоящий ответ «да» или «нет». `null == null` — это `true`, и `null != null` — это `ложь`.                        |
| `=~`, `!~` (есть в списке или соответствует текстовому шаблону) | Даёт настоящий ответ «да» или «нет». Пустое значение никогда не совпадает, поэтому `=~` даёт `ложь` и `!~` даёт `true`. |
| `??` (использовать запасное значение)                           | Даёт значение справа. Подставляется только вместо пустого значения.                                                     |
| `?:` (использовать запасное значение)                           | Даёт значение справа. Оно также подставляется вместо `ложь`, для `0`, и для пустого текста.                             |
| `a ? b : c` (выбор между двумя значениями)                      | Берёт `c` ветвь c, так же, как и для `ложь`.                                                                            |
| `=^`, `!^`, `=$`, `!$` (начинается с, заканчивается на)         | Выражение ничего не выдаёт.                                                                                             |
| `<`, `<=`, `>`, `>=` (сравнения размеров)                       | Выражение ничего не выдаёт.                                                                                             |
| `+`, `-`, `*`, `/`, `%` (арифметические операции)               | Выражение ничего не выдаёт.                                                                                             |
| `&`, `\|`, `^` (битовые операции)                               | Выражение ничего не выдаёт.                                                                                             |
| `!` (не)                                                        | Выражение ничего не выдаёт.                                                                                             |
| `&&`, `\|\|` (и, или)                                           | Выражение ничего не выдаёт, если только сторона, прочитанная первой, не определяет ответ.                               |

Шесть текстовых операторов делятся надвое. `=~` и `!~` выдерживают пустое значение, но `=^`, `!^`, `=$`, и `!$` не выдерживают, когда к атрибуту обращаются по имени. Условие вроде `hardware_key !^ 'AB'` кажется безопасным способом исключить префикс, но оно ничего не выводит для каждого пакета без `hardware_key`.

Узел `math:` функции ничего не возвращают, когда получают пустое значение, поэтому `math:round(engine_temp)` ничего не возвращает, когда `engine_temp` отсутствует. Эти `утилиты:` функции более терпимы и вместо этого возвращают пустое значение, поэтому `util:join` и `util:joinNonNull` по-прежнему дают результат, когда один из их аргументов отсутствует. Объединение текста с `+` не терпит пустых значений: `'Зона: ' + zone_label` ничего не возвращает.

## Логические операторы с пустым значением

Порядок важен для обоих `&&` и `||`. Каждый оператор сначала читает левую часть и останавливается на ней, когда левая часть уже определяет результат. А `ложь` левая часть определяет результат `&&`, а a `true` левая часть определяет результат `||`.

| Выражение                 | Результат                                                                                      |
| ------------------------- | ---------------------------------------------------------------------------------------------- |
| `false && (пусто)`        | `ложь`. Ложная левая часть определяет результат AND, поэтому правая часть никогда не читается. |
| `true && (пусто)`         | Ничего не возвращает.                                                                          |
| `(пусто) && что угодно`   | Ничего не возвращает. Левая часть читается первой.                                             |
| `true \|\| (пусто)`       | `true`. Истинная левая часть завершает ИЛИ, поэтому правая часть никогда не вычисляется.       |
| `false \|\| (пусто)`      | Ничего не возвращает.                                                                          |
| `(пусто) \|\| что угодно` | Ничего не возвращает. Левая часть читается первой.                                             |

Размещение термина, который может быть пустым, в конце помогает только тогда, когда левая сторона определяет результат для этого пакета. Вы редко можете гарантировать такой исход, поэтому лучше сравнивать значение явно.

## Чтение по имени или с помощью value()

Четыре оператора дают разные результаты для одного и того же пропущенного значения в зависимости от того, как формула его интерпретирует. Операторы следующие `=^`, `!^`, `=$` и `!$`, те, которые проверяют начало или конец текстового значения.

| Formula                                | Результат при `ID водителя` никогда не было отправлено |
| -------------------------------------- | ------------------------------------------------------ |
| `Водитель_id !^ 'AB'`                  | Не производит ничего                                   |
| `value('driver_id', 0, 'all') !^ 'AB'` | `true`                                                 |

Обращение к атрибуту по имени — это Форма, которая [автозаполнение](/docs/user/ru/guide/account/iot-logic/nodes/initiate-attribute-node/managing-attributes.md#autofill-attribute-names) вставки, поэтому условие, созданное с помощью выбора атрибута, принимает первый путь. Запишите сравнение с `value()` когда вам нужно `!^` или `!$` сообщить `true` в пакете, который не содержит атрибут.

Все остальные операторы ведут себя одинаково в обеих формах. Сравнение размеров, арифметика и `!` в любом случае ничего не дают, и `==`, `!=`, `=~` и `!~` в любом случае дайте однозначный ответ «да» или «нет».

То же разделение применяется к двум резервным формам. `??` и `? :` может содержать ошибочный расчёт, когда значение поступает из `value()`, и не может, если речь идет об имени:

| Formula                                 | Результат при `одометр` никогда не было отправлено |
| --------------------------------------- | -------------------------------------------------- |
| `(одометр + 1) ?? 0`                    | Не производит ничего                               |
| `(value('Одометр', 0, 'all') + 1) ?? 0` | `0`                                                |

Простой `??` по имени по-прежнему работает, потому что до него вычисление не выполняется. Только вычисление, заключённое внутри `??` или `? :` ведёт себя иначе.

## Сравнение значения в явном виде

Чтобы безопасно использовать в условии значение, которое может быть пустым, сравните его с `== true` или `== false` вместо того, чтобы использовать его отдельно. Оба `==` и `!=` Задайте настоящее значение «да» или «нет» для пустого значения, чтобы один термин без данных больше не блокировал всё условие.

Это условие прекращает работу при любом пакете, в котором отсутствует один из двух атрибутов:

```jexl
door_open || hood_open
```

Это условие продолжает выполняться и возвращает «да», когда открыта любая из дверей:

```jexl
door_open == true || hood_open == true
```

Функции зоны работают так же, потому что они возвращают пустое значение, когда пакет не содержит координат. Это условие прекращает работу, как только одна из функций не может ответить:

```jexl
leaveGeofence(51577 /* Austin Warehouse */) || leaveGeofence(85269 /* Construction site 1 */)
```

Это условие проверяет обе зоны в каждом пакете:

```jexl
leaveGeofence(51577 /* Склад в Остине */) == true || leaveGeofence(85269 /* Строительная площадка 1 */) == true
```

Выберите сравнение, которое соответствует тому, как следует считать пакет без данных:

* `== true` означает «известно, что это да». Пустое значение дает `ложь`, поэтому пакет без данных не соответствует условию.
* `== false` означает «известно, что нет». Пустое значение дает `ложь`, поэтому пакет без данных не соответствует условию.
* `!= true` означает «неизвестно, что да». Пустое значение дает `true`, так что пакет без данных все же совпадает.

{% hint style="warning" %}
Не используйте `!` для значения, которое может быть пустым. `!inЗона(35229)` ничего не возвращает, когда местоположение неизвестно, поэтому не может определить устройство вне Зоны. Запишите `inЗона(35229) == false` для «известно, что вне», или `inЗона(35229) != true` для «неизвестно, что находится внутри».
{% endhint %}

## Откуда берутся пустые значения

Атрибут в формуле принимает пустое значение в следующих случаях:

* Этот атрибут ни разу не отправлялся для этого GPS-устройства.
* В текущем пакете атрибут отсутствует, хотя в предыдущем пакете он был.
* Позиция в истории, которую вы запросили с помощью `value()` ещё не существует, потому что устройство отправило меньше пакетов, чем требуется для этой позиции.
* Устройство не передавало данные более 30 дней, поэтому Navixy удалила сохранённую историю. См. [Полный синтаксис](/docs/user/ru/guide/account/iot-logic/nodes/initiate-attribute-node/managing-attributes.md#full-syntax).
* `inGeofence()`, `enterGeofence()`, или `leaveGeofence()` не может ответить, потому что пакет не содержит координат или Зона больше не существует в аккаунте. См. [Когда функция зоны не дает ответа](/docs/user/ru/guide/account/iot-logic/nodes/geofence-functions.md#when-a-geofence-function-gives-no-answer).
* `geofenceName()` не имеет имени, которое можно вернуть. См. [Что возвращается](/docs/user/ru/guide/account/iot-logic/nodes/geofence-functions.md#what-you-get-back).

Все эти причины выглядят одинаково в формуле. Формула, которая должна продолжать работать, должна обрабатывать пустое значение, а не причину.

## Формулы, которые всегда должны возвращать значение

Защитите расчёт с помощью `!= null`, и сначала установите защитный кожух. Защитный кожух использует `!=`, что дает для пустого значения настоящий ответ «да» или «нет», так что сама проверка никогда не останавливается:

```jexl
value('odometer', 0, 'all') != null && value('odometer', 0, 'all') > 100
```

Именно порядок обеспечивает работу сторожевого условия. Если поменять термины местами, сравнение выполнится до сторожевого условия, и всё условие не даст результата.

Чтобы вместо этого подставить резервное значение, используйте `??`. Он возвращает правую часть только тогда, когда левая часть пуста:

```jexl
can_speed ?? 0
```

`??` подставляет значение только при отсутствии значения, а не при неудачном вычислении. `(can_speed * 1,1) ?? 0` ничего не возвращает, когда `can_speed` отсутствует, потому что умножение останавливается раньше `??` достигается. Поместите `??` вокруг атрибута, а не вокруг вычисления:

```jexl
(can_speed ?? 0) * 1,1
```

{% hint style="warning" %}
`?:` и `??` выглядят похоже и ведут себя по-разному. `??` подставляет правую часть только для пустого значения. `?:` подставляет его вместо пустого значения, а также вместо `ложь`, для `0`, а также вместо пустого текста. Формула `зажигание ?: true` поэтому сообщает `true` когда зажигание действительно выключено. Используйте `??` когда имеете в виду «если значение отсутствует».
{% endhint %}

Также можно ограничить вычисление извне формулы. Поместите **IF/THEN Логика** узел перед **Вычисление атрибута** узлом и проверьте наличие атрибутов в его условии. Подключите ветку THEN к вычисляющему узлу, чтобы он получал только пакеты, содержащие необходимые ему значения.

Подключите ветку ELSE к **узлу передачи данных** узел тоже. Ветка, которая заканчивается без него, отбрасывает каждый пакет, который до нее доходит, поэтому пакеты, не прошедшие проверку наличия, никогда не покидают поток.

Когда отсутствие значения лучше неверного, атрибут без значения — правильный результат. Добавляйте запасное значение только тогда, когда системе дальше по потоку нужно значение в каждом пакете.

## Проверьте, не возвращает ли выражение ничего

Исходите из того, что вы видите в [Анализ данных](/docs/user/ru/guide/account/iot-logic/data-stream-analyzer.md):

1. Выберите поток, затем GPS-устройство и вычисляемый атрибут.
2. Отключите **переключатель «Исключать null»** . Неудачный расчет отображается как `null` значение. Когда переключатель включен, он скрывает эти значения.
3. Увеличьте глубину, если пакет, который вы ищете, старше значений на экране.
4. Сравните атрибут с атрибутами, которые использует его формула. Пакет, в котором входные данные формулы имеют значения, а вычисляемый атрибут — `null` указывает на формулу.
5. Проверьте каждый оператор в формуле на соответствие [Как каждый оператор обрабатывает пустое значение](#how-each-operator-handles-an-empty-value).
6. Перепишите каждую часть, которая может быть пустой. Используйте `== true`, `== false`, или `??` резервный вариант.

{% hint style="warning" %}
Когда **переключатель «Исключать null»** он включен, последнее значение, которое сформула сгенерировала, остается на экране со своей более ранней меткой времени. Показание выглядит актуальным, но относится к более раннему пакету. Выключите переключатель, прежде чем делать вывод, продолжает ли IoT Logic вычислять атрибут.
{% endhint %}

## Часто задаваемые вопросы

#### Является ли пустое значение тем же, что и `ложь`?

Нет. Условие, которое действительно `ложь` сохраняет `ложь`, а условие, которое не удалось вычислить, сохраняет пустое значение. Оба отправляют данные через соединение ELSE узла **IF/THEN Логика** узла, поэтому одна ветвь сама по себе не говорит вам, что именно произошло. Последующее выражение, которое проверяет атрибут с `my_flag == false` соответствует только первому случаю.

#### Почему одно GPS-устройство перестало соответствовать условию, которое по-прежнему работает для других?

Устройства разных моделей не всегда отправляют одни и те же атрибуты, и одно устройство может перестать передавать атрибут, который остальные по-прежнему отправляют. Условие, которое читает этот атрибут без сравнения, тогда ничего не возвращает только для этого устройства. Сравните каждый термин явно с `== true` или `== false`.

## См. также

* [выражения и синтаксис IF/THEN Логика](/docs/user/ru/guide/account/iot-logic/nodes/logic-node/logic-node-expressions-and-syntax.md#missing-values-and-null-routing) о том, какую ветвь выбирает узел Logic и что он сохраняет.
* [Функции зон](/docs/user/ru/guide/account/iot-logic/nodes/geofence-functions.md) для случаев, когда функция геозоны не может ответить.
* [Управление атрибутами](/docs/user/ru/guide/account/iot-logic/nodes/initiate-attribute-node/managing-attributes.md#expression-language) для `value()` функция и два варианта синтаксиса.
* [Примеры расчетов](/docs/user/ru/guide/account/iot-logic/nodes/initiate-attribute-node/calculation-examples.md) для примеров формул.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://navixy.com/docs/user/ru/guide/account/iot-logic/nodes/missing-values-in-expressions.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
