Симуляция событий для тестирования (виртуальные датчики)
Введение в симуляцию: Зачем нужны виртуальные устройства?
В процессе пусконаладки системы автоматизации инженер неизбежно сталкивается с ситуацией, когда необходимо протестировать логические сценарии до того, как всё физическое оборудование установлено и подключено, или когда требуется проверить реакцию системы на редкие, но критически важные события. Симуляция событий — это процесс создания и отправки в систему искусственных данных, имитирующих сигналы от реальных датчиков и устройств. По сути, мы создаем виртуальные устройства, которые ведут себя как настоящие, позволяя нам тестировать автоматику в контролируемой среде.
Роль симуляции в жизненном цикле проекта невозможно переоценить. Это не просто инструмент для отладки; это фундаментальная методология, обеспечивающая качество и надёжность конечного решения.
🔗 Связанный материал: Этот урок тесно связан с концепциями, изложенными в уроке COURSE-07-M09-L02 'Разработка Тест-плана сдачи объекта'. Симуляция — это ваш главный инструмент для выполнения пунктов разработанного тест-плана, превращающий теоретические проверки в практические, воспроизводимые тесты.
Ключевые преимущества симуляции:
- Тестирование до монтажа оборудования: Вы можете полностью отладить логику управления климатом, освещением или безопасностью в офисе или лаборатории, не имея на руках ни одного физического датчика протечки или привода штор. Это радикально сокращает время, проводимое на объекте, и позволяет приехать на монтаж с уже работающим программным ядром системы.
- Проверка сложных и редких сценариев: Как поведет себя система при пожаре? Что произойдет, если датчик протечки сработает одновременно с активацией режима "Отсутствие"? Воспроизвести такие события на реальном объекте сложно, дорого, а иногда и опасно. Симуляция позволяет прогнать эти сценарии десятки раз, убедившись, что вся цепочка действий — от перекрытия клапанов до отправки push-уведомлений — работает без сбоев.
- Автоматизация регрессионного тестирования: После внесения изменений в один из сценариев (например, вы изменили логику работы теплого пола), вы должны быть уверены, что не "сломали" что-то в другом месте. Вместо того чтобы вручную бегать по объекту и нажимать все кнопки, вы можете запустить набор симуляционных тестов, которые автоматически проверят все ключевые функции системы за несколько минут.
Симуляция напрямую связана с типами тестирования, которые мы рассмотрели ранее:
- Модульное тестирование: Вы можете создать виртуальный датчик температуры и подать на вход вашего субпотока-термостата значения `-50`, `0`, `25`, `100`, чтобы проверить, как он обрабатывает корректные, граничные и невалидные данные.
- Интеграционное тестирование: Симулируя сигнал от датчика движения (`hi/virtual/living_room/motion`), вы проверяете, что сообщение корректно доходит по MQTT, обрабатывается сценарием управления светом, который, в свою очередь, отправляет команду на физическое реле.
- Сценарное тестирование: Создав набор виртуальных устройств (датчики освещенности, присутствия, температуры), вы можете полностью сымитировать сценарий "Утро в доме": плавное открытие штор, включение теплого пола, запуск кофейной машины.
На платформе контроллеров HI основным инструментом для создания симуляций является среда Node-RED. Её гибкость, визуальный интерфейс и мощные узлы `Inject` и `Function` позволяют создавать виртуальные устройства любой сложности — от простой кнопки до полноценной погодной станции.
---
Простая симуляция: Узел Inject для ручного триггера событий
Самый быстрый и простой способ сымитировать событие в Node-RED — использовать узел `Inject`. Этот узел, по сути, является "виртуальной кнопкой", которая при нажатии генерирует сообщение (`msg`) с заранее определенным содержимым. Это идеальный инструмент для ручного модульного тестирования отдельных участков логики.
Узел `Inject` позволяет настроить `msg.payload` для отправки данных любого типа, что дает возможность эмулировать практически любое простое устройство.
| Тип данных | Пример полезной нагрузки (`msg.payload`) | Эмулируемое устройство |
| --------------- | ------------------------------------------ | ---------------------------------------------------- |
| Boolean | `true` / `false` | Датчик движения, геркон (датчик открытия), кнопка |
| Number | `23.5` (число) | Датчик температуры, влажности; уставка термостата |
| String | `"NIGHT"` (строка) | Переключатель режимов (День/Ночь/Вечер) |
| JSON | `{"value": true, "source": "virtual-01"}` | Комплексное устройство, отправляющее данные в стандартном контракте |
Практический пример: Создание виртуального геркона
Задача: Протестировать сценарий "При открытии входной двери включить свет в холле на 3 минуты". У нас еще нет физического геркона, поэтому мы создадим его виртуальный аналог. Реализация:* Name: `[V] Door Opened`
* Payload: выберите тип `JSON`.
* В редактор JSON вставьте следующий код, соответствующий контракту сообщений:
{
"value": true,
"source": "virtual-door-main",
"ts": 0
}
* В поле `Topic` укажите `hi/virtual/hall/door_sensor/state`. Это MQTT-топик, который будет "слушать" ваш сценарий управления светом.
* > 💡 Подсказка: Установка `ts` (timestamp) в `0` здесь допустима для простого теста, но для более реалистичных симуляций лучше использовать `timestamp` (текущее время). Узел `Inject` умеет это делать автоматически.
* Name: `[V] Door Closed`
* Payload: выберите тип `JSON`.
* В редактор JSON вставьте:
{
"value": false,
"source": "virtual-door-main",
"ts": 0
}
* В поле `Topic` укажите тот же самый топик: `hi/virtual/hall/door_sensor/state`.
Теперь у вас есть панель ручного управления виртуальным датчиком.
- Шаг 1: Нажмите на кнопку слева от узла `[V] Door Opened`. В топик `hi/virtual/hall/door_sensor/state` будет отправлено сообщение `{ "value": true, ... }`.
- Шаг 2: Пронаблюдайте за реакцией системы. Сценарий, подписанный на этот топик, должен сработать. В идеале, вы увидите, как узел `Status` под реле света в холле сменит цвет на зеленый, и в журнале появится запись о включении света.
- Шаг 3: Нажмите на кнопку узла `[V] Door Closed`. Система получит сообщение о закрытии двери. Это не должно выключить свет немедленно, так как в сценарии заложена логика таймера на 3 минуты.
- Шаг 4: Вы можете использовать эти "кнопки" для проверки всех аспектов сценария: что будет, если дверь открыть и закрыть несколько раз подряд? Сбросится ли таймер?
Таким образом, узел `Inject` позволяет быстро и эффективно проводить ручное тестирование, имитируя простые дискретные события и проверяя реакцию системы в реальном времени.
---
Динамическая симуляция: Узел Function для сложных сценариев
Хотя узел `Inject` отлично подходит для ручных проверок, у него есть фундаментальное ограничение: он статичен. Вы не можете с его помощью легко сымитировать плавное изменение температуры или случайные колебания потребляемой мощности. Для создания динамических симуляций нам потребуется вся мощь JavaScript, доступная в узле `Function`.
Узел `Function` позволяет нам писать код, который будет генерировать данные "на лету", делая симуляцию гораздо более реалистичной и открывая возможности для автоматизированного тестирования.
> 💡 Подсказка: Используйте переменные контекста (`flow` или `global`) для хранения состояния ваших виртуальных устройств. Например, чтобы симулятор температуры 'помнил' текущее значение между вызовами, сохраняйте его в `flow.set('current_temp', new_temp)`. Это ключевой механизм для создания "живых" симуляций.
Пример 1: Эмуляция датчика температуры с плавным ростом
Задача: Создать виртуальный датчик уличной температуры, который имитирует утренний прогрев воздуха. Каждые 10 секунд температура должна немного повышаться. Реализация:
// Получаем текущее значение температуры из контекста потока (flow context).
// Если значения нет (первый запуск), начинаем с 10.0 градусов.
let currentTemp = flow.get("virtualOutdoorTemp") || 10.0;
// Симулируем небольшой, слегка случайный прирост температуры.
// Math.random() * 0.3 добавляет случайности от 0.0 до 0.3.
let newTemp = currentTemp + (Math.random() * 0.3);
// Округляем до одного знака после запятой для реалистичности.
newTemp = Math.round(newTemp * 10) / 10;
// Если температура превысила 25 градусов, сбрасываем ее обратно, чтобы симуляция не ушла в бесконечность.
if (newTemp > 25.0) {
newTemp = 10.0;
}
// Сохраняем новое значение обратно в контекст для следующего запуска.
flow.set("virtualOutdoorTemp", newTemp);
// Формируем сообщение в соответствии с контрактом.
msg.payload = {
"value": newTemp,
"unit": "°C",
"source": "virtual-weather-station",
"ts": Date.now()
};
msg.topic = "hi/virtual/outdoor/temperature";
// Обновляем статус узла для наглядности.
node.status({ fill: "blue", shape: "dot", text: "Temp: " + newTemp + " °C" });
// Отправляем сообщение дальше по потоку.
return msg;
Теперь у вас есть автоматический симулятор, который будет непрерывно генерировать реалистичные данные о росте температуры, позволяя тестировать сценарии, зависящие от плавного изменения этого параметра (например, управление системой отопления или кондиционирования).
Пример 2: Эмуляция счетчика электроэнергии
Задача: Создать симулятор, который генерирует показания потребляемой мощности, имитируя работу бытовых приборов в доме — с базовым потреблением и случайными пиками. Реализация (в узле `Function`):// Базовое потребление в доме (холодильник, дежурный режим техники).
const basePower = 150; // Ватт
// Вероятность включения мощного прибора (чайник, микроволновка).
const highLoadProbability = 0.1; // 10%
let currentPower = basePower;
// Добавляем небольшие случайные колебания к базовой мощности.
currentPower += (Math.random() * 50) - 25; // от -25 до +25 Вт
// Проверяем, не включился ли мощный прибор.
if (Math.random() < highLoadProbability) {
// Симулируем пик потребления от 1500 до 2200 Вт.
currentPower += 1500 + (Math.random() * 700);
}
// Формируем payload.
msg.payload = {
"value": Math.round(currentPower),
"unit": "W",
"source": "virtual-power-meter",
"ts": Date.now()
};
msg.topic = "hi/virtual/home/main_power/value";
node.status({ fill: "green", shape: "dot", text: "Power: " + msg.payload.value + " W" });
return msg;
Этот симулятор, запускаемый, например, раз в 5 секунд, позволит вам тестировать сценарии энергоменеджмента: отключение неприоритетных нагрузок (бойлер, теплый пол) при превышении заданного порога мощности.
---
Практический пример: Создание виртуальной погодной станции
Теперь объединим полученные знания для создания более сложного, но чрезвычайно полезного виртуального устройства — погодной станции, которая будет поставлять в систему данные о температуре, влажности и освещенности. Это позволит нам тестировать множество взаимосвязанных сценариев автоматизации.
> ⚠️ Внимание: Неконтролируемая генерация сообщений (например, каждые 100 мс) может создать высокую нагрузку на MQTT-брокер и сеть контроллера. Всегда выбирайте разумный интервал для симуляции, соответствующий реальному физическому устройству. Для погодных данных интервал в 1-5 минут является более чем достаточным.
Задача: Создать "виртуальную метеостанцию", которая с интервалом в 1 минуту генерирует и публикует в разные MQTT-топики три параметра:Для этого нам понадобится всего три узла: `Inject` -> `Function` -> `mqtt out`.
// === Симуляция температуры ===
// Используем синусоиду для имитации суточных колебаний температуры.
const hours = (new Date()).getHours();
const baseTemp = 15; // Средняя температура
const amplitude = 8; // Амплитуда колебаний
let temp = baseTemp + amplitude Math.sin((hours - 9) (Math.PI / 12));
temp += (Math.random() - 0.5); // Добавляем немного шума
temp = Math.round(temp * 10) / 10;
// === Симуляция влажности ===
// Влажность часто обратно коррелирует с температурой
let humidity = 70 - (temp - baseTemp) * 2.5;
humidity += (Math.random() * 10 - 5);
humidity = Math.max(30, Math.min(99, Math.round(humidity))); // Ограничиваем диапазон 30-99%
// === Симуляция освещенности (lux) ===
let lux;
if (hours > 7 && hours < 20) { // День
// Максимальная освещенность в полдень
lux = 50000 Math.sin((hours - 7) (Math.PI / 13));
} else { // Ночь
lux = 5 + (Math.random() * 10); // Очень низкая освещенность
}
lux = Math.round(lux);
// --- Создание трех независимых сообщений ---
// Сообщение для температуры
const msgTemp = {
topic: "hi/virtual/outdoor/temperature",
payload: {
"value": temp,
"unit": "°C",
"source": "virtual-weather-station",
"ts": Date.now()
}
};
// Сообщение для влажности
const msgHumidity = {
topic: "hi/virtual/outdoor/humidity",
payload: {
"value": humidity,
"unit": "%",
"source": "virtual-weather-station",
"ts": Date.now()
}
};
// Сообщение для освещенности
const msgLux = {
topic: "hi/virtual/outdoor/lux",
payload: {
"value": lux,
"unit": "lux",
"source": "virtual-weather-station",
"ts": Date.now()
}
};
// Обновляем статус узла для удобства отладки
node.status({ text: `T: ${temp}°C, H: ${humidity}%, L: ${lux} lx` });
// Возвращаем массив сообщений. Node-RED отправит каждое из них как отдельное.
return [msgTemp, msgHumidity, msgLux];
Имея такую виртуальную станцию, вы можете легко протестировать сложный сценарий, например, "Управление фасадной подсветкой":
Условие: «Если уровень освещенности на улице упал ниже 50 lux, И сейчас не активен режим "Безопасность", И время после заката, то включить фасадную подсветку».*- Тестирование: Вам больше не нужно ждать вечера или заклеивать датчик освещенности. Ваша симуляция автоматически пройдет через суточный цикл. Вы сможете наблюдать в логах, как около 20:00 (в зависимости от времени года в симуляции) значение `lux` упадет ниже 50, и ваш сценарий должен будет активироваться. Вы также можете вручную изменить код симулятора, чтобы мгновенно создать "темноту" и проверить реакцию системы.
---
Итоги и интеграция симуляций в Тест-план
В этом уроке мы рассмотрели два мощных подхода к симуляции событий, которые являются неотъемлемой частью профессионального тестирования систем автоматизации.
- Узел `Inject`: Ваш инструмент для быстрых, ручных, повторяемых тестов. Идеален для проверки дискретных сигналов и отладки отдельных модулей.
- Узел `Function`: Инструмент для создания динамических, автоматизированных и реалистичных симуляций. Позволяет имитировать сложные процессы (изменение температуры, энергопотребление, погодные условия) и проводить комплексное сценарное тестирование.
Теперь давайте вернемся к Тест-плану, который вы разработали в уроке COURSE-07-M09-L02. Симуляция позволяет сделать его пункты гораздо более конкретными и выполнимыми.
Пример модификации пункта в Тест-плане:- Было (общая формулировка):
- Стало (конкретный, исполняемый план с симуляцией):
> - Шаг 1 (Симуляция): С помощью узла `Inject` отправить сообщение `{"value": true}` в MQTT-топик `hi/virtual/living_room/motion_sensor/state`.
> - Шаг 2 (Проверка логики): Убедиться, что в журнале событий появилась запись об активации сценария "Свет в гостиной". Проверить, что статус сценария изменился на "АКТИВЕН".
> - Шаг 3 (Проверка исполнения): Проверить, что физическое реле `RL-08 (Гостиная, основной свет)` включилось (визуально или по статусу в Node-RED).
> - Шаг 4 (Симуляция): Через `N` минут (согласно настройке сценария) убедиться, что свет погас. Либо симулировать повторное движение для проверки сброса таймера.
Этот подход превращает абстрактную проверку в четкий алгоритм, который может выполнить любой инженер.
Лучшие практики работы с симуляциями:
Что дальше?
Освоив симуляцию, вы получили в свои руки мощнейший инструмент для обеспечения качества. В следующем уроке мы перейдем к финальному этапу — подготовке исполнительной документации и актов сдачи-приемки работ, где результаты вашего тестирования станут официальным подтверждением работоспособности системы.