ГлавнаяАкадемияCOURSE-16: Основы Интернета Вещей и практическое применение → Интеграция с внешними сервисами, визуализация и тестирование

Интеграция с внешними сервисами, визуализация и тестирование

Урок 1 · COURSE-16: Основы Интернета Вещей и практическое применение · theory

COURSE-16-M07-LAB03 — Интеграция с внешними сервисами, визуализация и тестирование

Введение

На предыдущих этапах вы освоили базовые операции с контроллером HI, научились считывать данные с датчиков и управлять реле. Данная лабораторная работа является завершающей в модуле и объединяет полученные навыки для создания комплексного решения. Вы реализуете сценарий автоматического климат-контроля, который будет учитывать не только внутренние показания, но и данные из внешнего мира, полученные через API.

Вы научитесь интегрировать сторонние веб-сервисы, строить сложную логику на базе конечных автоматов (FSM), создавать простой интерфейс для мониторинга и разрабатывать план тестирования для проверки надежности вашей системы.

Цели лабораторной работы

Сценарий: Интеллектуальный климат-контроль в офисном помещении

Задача: Автоматизировать работу кондиционера в офисном помещении на базе контроллера HI. Логика работы системы:
  • Контроллер постоянно считывает температуру в помещении с датчика DS18B20, подключенного к универсальному входу (UI).
  • Раз в 15 минут контроллер запрашивает прогноз погоды через публичный API, чтобы определить ожидаемую температуру на улице.
  • Система переходит в режим "Активное охлаждение" (`COOLING`), если:
  • * Температура в помещении превышает 25°C.

    * И при этом прогнозируемая температура на улице выше 22°C (чтобы не включать кондиционер без надобности, если на улице прохладно).

  • Система возвращается в режим "Ожидание" (`IDLE`), когда температура в помещении опускается ниже 23°C.
  • Состояние системы (режим, текущая температура внутри и снаружи) отображается на простой веб-панели Node-RED Dashboard.
  • Все изменения состояния и ошибки логируются в базу данных MySQL, доступную на контроллере HI.
  • Шаг 1: Реализация логики климат-контроля (Конечный автомат)

    Для управления сложным поведением, зависящим от множества условий и предыдущего состояния, мы применим паттерн "Конечный автомат" (Finite State Machine, FSM).

    Состояния автомата: Переходы: Реализация в Node-RED:
  • Создайте новую вкладку в Node-RED и назовите ее "Климат-контроль".
  • Состояние автомата (`fsm_state`) будем хранить в переменной контекста вкладки (`flow context`). Это позволит всем узлам на вкладке иметь доступ к текущему состоянию.
  • Для сохранения состояния между перезагрузками контроллера убедитесь, что в `settings.js` вашего Node-RED включено персистентное хранилище контекста. Для этого в файле `settings.js` (обычно `/home/nodered/.node-red/settings.js`) найдите секцию `contextStorage` и раскомментируйте или добавьте:
  •     contextStorage: {

    default: {

    module: "localfilesystem"

    },

    // Для персистентного хранения в MySQL (если настроен)

    // mysql: {

    // module: "node-red-contrib-mysqldb-context",

    // host: "localhost",

    // port: 3306,

    // user: "nodered",

    // password: "your_password",

    // database: "nodered_context"

    // }

    },

    💡 Совет: Для критически важных систем рекомендуется использовать MySQL для хранения контекста, так как это обеспечивает более высокую надежность и возможность резервного копирования.

    ASCII-схема потока:
    // Поток данных от внутреннего датчика
    

    [Inject: 10s] -> [1-Wire In: DS18B20] -> [Function: "Prepare Internal Temp"] -> [Link Out: "To FSM Input"]

    // Поток данных от внешнего API

    [Inject: 15m] -> [HTTP Request: Weather API] -> [Function: "Prepare External Temp"] -> [Link Out: "To FSM Input"]

    // Основной поток логики FSM

    [Link In: "To FSM Input"] -> [Function: "FSM Logic"] --+-- (сообщение для реле) --> [Relay Out: AC Control]

    |

    +-- (сообщение для dashboard) --> [Link Out: "To Dashboard"]

    |

    +-- (сообщение для лога) --> [Link Out: "To Audit Log"]

    // Поток обработки ошибок на вкладке "Климат-контроль"

    [Catch: All Nodes] -> [Function: "Format Error"] -> [Link Out: "To Audit Log"]

    // Отдельный поток для аудита (может быть на другой вкладке)

    [Link In: "To Audit Log"] -> [Function: "Format Audit Message"] -> [MySQL Out: Audit Table]

    // Отдельный поток для Dashboard (может быть на другой вкладке)

    [Link In: "To Dashboard"] -> [Dashboard Nodes]

    Код для узла `Function: "Prepare Internal Temp"`:
    // Контракт входящего сообщения от 1-Wire In:
    

    // msg.payload = <число> (температура в °C)

    let temp_in = parseFloat(msg.payload);

    // Валидация: проверяем, что значение в разумных пределах

    if (isNaN(temp_in) || temp_in < -20 || temp_in > 60) {

    node.status({ fill: "red", shape: "dot", text: "Некорректная температура: " + msg.payload });

    node.error("Некорректное значение температуры с датчика DS18B20: " + msg.payload, msg);

    // Отправляем ошибку в аудит

    let auditError = {

    event: "SENSOR_ERROR",

    timestamp: Date.now(),

    source: "ds18b20_internal",

    error_code: "INVALID_VALUE",

    error_message: "Received out-of-range temperature",

    details: `Raw value: ${msg.payload}`

    };

    // Используем третий выход для аудита ошибок

    return [null, null, { payload: auditError, topic: "audit_log" }];

    }

    // Сохраняем значение в контекст, чтобы FSM мог его использовать

    flow.set("temperature_internal", temp_in);

    // Формируем исходящее сообщение по контракту

    // msg.payload = { value: 23.5, source: "ds18b20_internal", ts: 1678886400000, unit: "°C" }

    msg.payload = {

    value: temp_in,

    unit: "°C",

    source: "ds18b20_internal",

    ts: Date.now()

    };

    msg.topic = "telemetry/climate/internal_temperature";

    node.status({ fill: "green", shape: "dot", text: "OK: " + temp_in + "°C" });

    return msg;

    Код для узла `Function: "FSM Logic"`:
    // Получаем текущее состояние FSM из контекста. При первом запуске - IDLE.
    

    let state = flow.get("fsm_state") || "IDLE";

    // Получаем текущие температуры из контекста (их туда запишут другие потоки)

    let temp_in = flow.get("temperature_internal");

    let temp_out = flow.get("temperature_external");

    // Контракт сообщения для аудита

    let auditMessage = {

    event: "FSM_STATE_CHECK",

    timestamp: Date.now(),

    current_state: state,

    internal_temp: temp_in,

    external_temp: temp_out,

    transition: null,

    action: null,

    details: "Checking FSM conditions"

    };

    // Если какие-то данные еще не пришли, выходим

    if (temp_in === undefined || temp_out === undefined) {

    node.status({ fill: "yellow", shape: "ring", text: "Ожидание данных..." });

    auditMessage.details = "Waiting for all sensor data";

    // Отправляем в аудит только если это не первый запуск и данные действительно отсутствуют

    if (flow.get("fsm_initialized")) {

    node.send([null, null, { payload: auditMessage, topic: "audit_log" }]);

    }

    return null;

    }

    // Устанавливаем флаг инициализации FSM

    flow.set("fsm_initialized", true);

    let original_state = state;

    let command = null; // Команда для реле

    // --- Логика переходов FSM ---

    switch (state) {

    case "IDLE":

    if (temp_in > 25 && temp_out > 22) {

    state = "COOLING";

    command = true; // Включить реле

    auditMessage.transition = `${original_state} -> ${state}`;

    auditMessage.action = "Turn ON AC";

    auditMessage.details = `Internal temp (${temp_in}°C) > 25°C AND External temp (${temp_out}°C) > 22°C`;

    }

    break;

    case "COOLING":

    if (temp_in < 23) {

    state = "IDLE";

    command = false; // Выключить реле

    auditMessage.transition = `${original_state} -> ${state}`;

    auditMessage.action = "Turn OFF AC";

    auditMessage.details = `Internal temp (${temp_in}°C) < 23°C`;

    }

    break;

    }

    // Сохраняем новое состояние в контекст

    flow.set("fsm_state", state);

    // --- Формирование исходящих сообщений ---

    let msgRelay = null;

    let msgDashboard = null;

    let msgAudit = null;

    // 1. Сообщение для управления реле (только если состояние изменилось)

    if (state !== original_state) {

    msgRelay = {

    payload: {

    value: command,

    source: "climate_fsm",

    ts: Date.now()

    },

    topic: "command/ac/set"

    };

    // Добавляем информацию о команде в аудит, если она была

    if (auditMessage.action) {

    auditMessage.action_status = "sent";

    }

    msgAudit = { payload: auditMessage, topic: "audit_log" };

    } else {

    // Если состояние не изменилось, но мы хотим логировать периодические проверки

    // Можно добавить условие, чтобы не логировать слишком часто

    // Например, логировать только если прошло X минут с последнего лога

    // Или логировать только изменения состояния

    // Для данной ЛР логируем только изменения состояния

    }

    // 2. Сообщение для обновления Dashboard (отправляем всегда)

    msgDashboard = {

    payload: {

    state: state,

    temp_in: temp_in,

    temp_out: temp_out,

    timestamp: Date.now()

    },

    topic: "telemetry/climate/status"

    };

    // 3. Обновляем статус узла для визуальной диагностики

    node.status({ fill: "green", shape: "dot", text: `State: ${state} | In: ${temp_in}°C, Out: ${temp_out}°C` });

    // Отправляем сообщения на соответствующие выходы

    return [msgRelay, msgDashboard, msgAudit];

    💡 Совет: Настройте узел `Function` на 3 выхода. Первый — для команд реле, второй — для данных на Dashboard, третий — для аудита. Это делает поток более читаемым.

    Конфигурация узла `Relay Out: AC Control`:

    Шаг 2: Интеграция с внешним API погоды

    Используем бесплатный API от OpenWeatherMap для получения прогноза погоды.

  • Получите API ключ:
  • * Зарегистрируйтесь на сайте OpenWeatherMap.

    * Перейдите в раздел "API keys" и скопируйте ваш ключ.

  • Настройте поток в Node-RED:
  • * `Inject`: Настройте на запуск каждые 15 минут.

    * `http request`:

    * Method: `GET`

    * URL: `https://api.openweathermap.org/data/2.5/weather?q=Moscow&appid=ВАШ_API_КЛЮЧ&units=metric`

    (Замените `Moscow` на ваш город и `ВАШ_API_КЛЮЧ` на ваш ключ).

    * Return: `a parsed JSON object`.

    * `Function: "Prepare External Temp"`: Этот узел будет извлекать температуру из ответа API, валидировать ее и сохранять в контекст.

    Код для узла `Function: "Prepare External Temp"`:
    // Контракт сообщения от API (пример):
    

    // msg.payload = { "main": { "temp": 19.5, ... }, "cod": 200, ... }

    // 1. Валидация ответа

    if (msg.statusCode !== 200 || !msg.payload || msg.payload.cod !== 200) {

    node.error("Ошибка API погоды: " + msg.statusCode + " - " + (msg.payload ? JSON.stringify(msg.payload) : "No payload"), msg);

    node.status({ fill: "red", shape: "dot", text: "API Error: " + msg.statusCode });

    // Отправляем ошибку в аудит

    let auditError = {

    event: "API_ERROR",

    timestamp: Date.now(),

    source: "openweathermap_api",

    error_code: msg.statusCode || "UNKNOWN",

    error_message: msg.payload ? (msg.payload.message || JSON.stringify(msg.payload)) : "No payload",

    details: "Failed to fetch external temperature"

    };

    // Используем третий выход для аудита ошибок

    return [null, null, { payload: auditError, topic: "audit_log" }];

    }

    if (msg.payload && msg.payload.main && typeof msg.payload.main.temp === 'number') {

    let temp_out = msg.payload.main.temp;

    // 2. Сохраняем значение в контекст, чтобы FSM мог его использовать

    flow.set("temperature_external", temp_out);

    // 3. Формируем сообщение по нашему внутреннему контракту

    // msg.payload = { value: 19.5, source: "openweathermap_api", ts: 1678886400000, unit: "°C" }

    let newMsg = {

    payload: {

    value: temp_out,

    unit: "°C",

    source: "openweathermap_api",

    ts: Date.now()

    },

    topic: "telemetry/weather/temperature"

    };

    node.status({ fill: "green", shape: "dot", text: "OK: " + temp_out + "°C" });

    // Отправляем сообщение дальше, например, на вход FSM

    return newMsg;

    } else {

    node.error("Некорректный формат ответа от API погоды", msg);

    node.status({ fill: "red", shape: "dot", text: "Invalid API response" });

    // Отправляем ошибку в аудит

    let auditError = {

    event: "API_ERROR",

    timestamp: Date.now(),

    source: "openweathermap_api",

    error_code: "PARSE_ERROR",

    error_message: "Invalid JSON structure or missing temperature data",

    details: "Failed to parse external temperature from API response"

    };

    // Используем третий выход для аудита ошибок

    return [null, null, { payload: auditError, topic: "audit_log" }];

    }

    ⚠️ Предупреждение: Никогда не храните API ключи прямо в коде узла `Function`. Используйте переменные окружения или узел `Change` для установки URL, чтобы ваш ключ не был виден в экспортированном потоке. Для этого можно создать узел `Change` перед `http request` и установить `msg.url` из переменной окружения `process.env.OPENWEATHER_API_KEY`.

    Шаг 3: Визуализация данных на Dashboard

    Для быстрой визуализации используем встроенный `node-red-dashboard`.

  • Установка: В меню Node-RED (три полоски справа вверху) -> `Manage palette` -> `Install`, найдите и установите `node-red-dashboard`.
  • После установки справа появится новая вкладка "dashboard". Создайте в ней новую вкладку (Tab) и группу (Group).
  • Создание интерфейса:
  • * `ui_gauge` (Датчик температуры в помещении):

    * Подключите к выходу `Function: "Prepare Internal Temp"`.

    * `Group`: выберите созданную группу.

    * `Label`: "Температура в помещении".

    * `Value format`: `{{value | number:1}} °C`.

    * `Range`: 0-40.

    * `ui_text` (Температура на улице):

    * Подключите к выходу `Function: "Prepare External Temp"`.

    * `Label`: "Температура на улице".

    * `Value format`: `{{msg.payload.value | number:1}} °C`.

    * `ui_text` (Состояние системы):

    * Подключите ко второму выходу узла `Function: "FSM Logic"` (который отправляет `msgDashboard`).

    * `Label`: "Режим работы".

    * `Value format`: `{{msg.payload.state}}`.

    * `ui_switch` (Ручное управление AC - опционально):

    * Позволяет вручную включать/выключать кондиционер, переопределяя FSM.

    * Подключите к `Relay Out: AC Control`.

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

    Теперь, открыв `http://:1880/ui`, вы увидите простую панель мониторинга.

    Шаг 4: Тестирование и оценка производительности

    Профессиональный подход требует не только создания, но и тщательной проверки системы.

    📋 Чек-лист для тестирования и сдачи системы:

    | ID теста | Описание теста (Test Case) | Ожидаемый результат (Expected Result) | Статус |

    | :------- | :--------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- | :----- |

    | Функциональные тесты |

    | FT-01 | Имитировать температуру в помещении `26°C` (через `Inject` с нужным `msg.payload`). Температура на улице `23°C`. | Система переходит в состояние `COOLING`. Реле кондиционера включается. На Dashboard отображается статус "COOLING". Запись в логе. | |

    | FT-02 | Имитировать температуру в помещении `22°C`. | Система переходит в состояние `IDLE`. Реле кондиционера выключается. На Dashboard отображается статус "IDLE". Запись в логе. | |

    | FT-03 | Имитировать температуру в помещении `26°C`, но на улице `15°C`. | Система остается в состоянии `IDLE`. Реле не включается. | |

    | FT-04 | Имитировать температуру в помещении `24°C`. | Система остается в текущем состоянии (если `IDLE`, то `IDLE`; если `COOLING`, то `COOLING`). Реле не меняет состояние. | |

    | Тесты на обработку ошибок |

    | ET-01 | Отключить контроллер от интернета. Дождаться срабатывания потока запроса погоды. | В логе Node-RED (и в вашей БД `audit_log`) появляется ошибка API. Система продолжает работать на основе последних валидных данных. | |

    | ET-02 | Физически отключить датчик DS18B20 от контроллера. | В логе появляется ошибка чтения 1-Wire. Система переходит в безопасное состояние (например, `IDLE`) и сообщает об ошибке. | |

    | ET-03 | Отправить в `Function: "Prepare Internal Temp"` некорректное значение (например, строку "abc" или число 1000). | Узел `Function` должен отфильтровать некорректное значение, вывести ошибку в лог и не передать его дальше. | |

    | Тесты пользовательского интерфейса |

    | UI-01 | Открыть панель Dashboard в браузере. | Все виджеты отображаются корректно. Данные обновляются в реальном времени (или с заданной периодичностью). | |

    | UI-02 | Изменить значения температур через `Inject` узлы. | Виджеты Dashboard должны оперативно обновить отображаемые значения. | |

    Мини-runbook «Если что-то не работает»

    * Решение: Проверьте правильность вашего API ключа. Убедитесь, что он активен в личном кабинете OpenWeatherMap. * Решение: Подключите узел `Debug` ко всем входам и выходам узла `Function: "FSM Logic"`. Проверьте, что данные приходят в правильном формате и что переменные контекста (`flow.get`) считываются корректно. Убедитесь, что вы используете `flow.set`, а не `context.set`.

    * Проверьте, что все `Link In` и `Link Out` узлы имеют правильные имена и соединены.

    * Решение: `85°C` — значение по умолчанию при включении. ` -127°C` — ошибка чтения. Проверьте физическое подключение датчика (контакты Data, VCC, GND). Убедитесь, что на шине 1-Wire есть подтягивающий резистор 4.7 кОм между VCC и Data. * Решение: Убедитесь, что палитра `node-red-dashboard` установлена и не отключена. Проверьте лог запуска Node-RED на наличие ошибок, связанных с dashboard. Убедитесь, что узлы Dashboard подключены к соответствующим `Link In` узлам. * Решение: Проверьте конфигурацию узла `MySQL Out`. Убедитесь, что таблица `audit_log` существует в базе данных и имеет соответствующие столбцы (`id`, `timestamp`, `event`, `source`, `details`). Проверьте логи MySQL на предмет ошибок подключения или записи. * Решение: Увеличьте гистерезис в логике FSM (например, `IDLE` -> `COOLING` при `temp_in > 26`, `COOLING` -> `IDLE` при `temp_in < 22`). Добавьте задержку (`Delay` узел) перед отправкой команды на реле, чтобы избежать мгновенных переключений.

    Лабораторная работа 1: Реализация базового климат-контроля

    Цель: Создать и протестировать основную логику климат-контроля с использованием FSM и имитацией внешних данных. Задание:
  • Создайте новую вкладку в Node-RED под названием "Климат-контроль (ЛАБ1)".
  • Разместите узлы `Inject` для имитации внутренней и внешней температуры.
  • * `Inject: "Внутренняя температура"`: отправляет число (например, 26) каждые 5 секунд.

    * `Inject: "Внешняя температура"`: отправляет число (например, 23) каждые 10 секунд.

  • Создайте два узла `Function`:
  • * `Function: "Set Internal Temp"`: принимает `msg.payload` от "Внутренней температуры" и сохраняет его в `flow.temperature_internal`.

    * `Function: "Set External Temp"`: принимает `msg.payload` от "Внешней температуры" и сохраняет его в `flow.temperature_external`.

  • Реализуйте узел `Function: "FSM Logic"` согласно предоставленному коду. Подключите к нему `Inject` для периодического запуска (например, каждые 5 секунд).
  • Подключите первый выход `FSM Logic` к узлу `Debug` для проверки команд реле.
  • Подключите второй выход `FSM Logic` к узлу `Debug` для проверки данных Dashboard.
  • Подключите третий выход `FSM Logic` к узлу `Debug` для проверки аудита.
  • Протестируйте систему, изменяя значения в `Inject` узлах и наблюдая за выводами `Debug`. Заполните часть "Функциональные тесты" из чек-листа.
  • Flow Diagram (ASCII):
    // FLOW-LAB1-CLIMATE-FSM-SIM
    

    // Вкладка "Климат-контроль (ЛАБ1)"

    [Inject: "Внутренняя температура"] --> [Function: "Set Internal Temp"] --> [Link Out: "To FSM Input Lab1"]

    [Inject: "Внешняя температура"] --> [Function: "Set External Temp"] --> [Link Out: "To FSM Input Lab1"]

    [Inject: "Запуск FSM"] --> [Link In: "To FSM Input Lab1"] --> [Function: "FSM Logic"] --+--> [Debug: "Relay Commands Lab1"]

    |

    +--> [Debug: "Dashboard Data Lab1"]

    |

    +--> [Debug: "Audit Log Lab1"]

    Рубрика оценивания:

    Лабораторная работа 2: Полная интеграция и визуализация

    Цель: Интегрировать реальные датчики, внешний API и создать Dashboard для мониторинга. Задание:
  • Используйте потоки из Шага 1 и Шага 2 урока.
  • Подключите реальный датчик DS18B20 к универсальному входу контроллера HI. Настройте узел `1-Wire In` для чтения с него.
  • * WIRING-SENS-DS18B20: Подключение датчика DS18B20 к универсальному входу HI.

            //========= WIRING-SENS-DS18B20: DS18B20 to UI =========

    // Используется универсальный вход UI-01

    // Подтягивающий резистор 4.7 кОм между VCC и Data обязателен.

    [CTRL:HI-Core] (SENS:Temp:DS18B20-01)

    Клемма Цвет

    +3.3V/5V --------- (Красный) ---- VCC

    UI-01 --------- (Желтый) ----- DATA

    GND --------- (Черный) ----- GND

  • Настройте узел `HTTP Request` для получения данных от OpenWeatherMap с вашим API ключом.
  • Настройте узел `Relay Out` для управления одним из реле контроллера HI (например, `RL-01`).
  • * WIRING-ACT-AC-RELAY: Подключение кондиционера через реле HI.

            //========= WIRING-ACT-AC-RELAY: AC Unit Control via Relay =========

    // Управление кондиционером через реле RL-01 (сухой контакт или силовое)

    // Предполагается, что кондиционер имеет вход для внешнего управления (например, "сухой контакт" или низковольтное управление).

    // Если кондиционер управляется по 230V, используйте соответствующее силовое реле и соблюдайте правила электробезопасности.

    [CTRL:HI-Core]

    Клемма

    RL-01 (C) --------- COM (AC Unit Control Input)

    RL-01 (NO) -------- NO (AC Unit Control Input)

    // Если кондиционер управляется по 230V:

    // Щит АВР [CTRL:HI-Core]

    // ~L~ --------- L

    // ~N~ --------- N

    // ~PE~ -------- PE

    //

    // ~L~ ---+-- C (RL-01)

    // \-- NO (RL-01) --- ~L~ --- L (AC Unit)

    // ~N~ ----------------------------- N (AC Unit)

  • Создайте панель `node-red-dashboard` с виджетами для отображения внутренней, внешней температуры и состояния FSM.
  • Реализуйте поток для логирования аудита в базу данных MySQL. Создайте таблицу `audit_log` со столбцами `id (INT AUTO_INCREMENT PRIMARY KEY)`, `timestamp (BIGINT)`, `event (VARCHAR(255))`, `source (VARCHAR(255))`, `details (TEXT)`.
  • * SQL-схема для `audit_log`:

            CREATE TABLE IF NOT EXISTS audit_log (

    id INT AUTO_INCREMENT PRIMARY KEY,

    timestamp BIGINT NOT NULL,

    event VARCHAR(255) NOT NULL,

    source VARCHAR(255),

    error_code VARCHAR(50),

    error_message TEXT,

    details TEXT

    );

    * Код для узла `Function: "Format Audit Message"`:

            // Контракт входящего сообщения:

    // msg.payload = { event: "FSM_STATE_CHECK", timestamp: ..., ... }

    // msg.payload = { event: "API_ERROR", timestamp: ..., ... }

    if (msg.payload && msg.payload.event) {

    let auditEntry = {

    timestamp: msg.payload.timestamp || Date.now(),

    event: msg.payload.event,

    source: msg.payload.source || null,

    error_code: msg.payload.error_code || null,

    error_message: msg.payload.error_message || null,

    details: msg.payload.details || null

    };

    // Формируем сообщение для MySQL

    msg.topic = "INSERT INTO audit_log (timestamp, event, source, error_code, error_message, details) VALUES (?, ?, ?, ?, ?, ?)";

    msg.payload = [

    auditEntry.timestamp,

    auditEntry.event,

    auditEntry.source,

    auditEntry.error_code,

    auditEntry.error_message,

    auditEntry.details

    ];

    return msg;

    }

    return null;

  • Проведите полное тестирование системы согласно чек-листу, включая тесты на обработку ошибок.
  • Flow Diagram (ASCII):
    // FLOW-CLIMATE-CONTROL-FULL
    

    // Вкладка "Климат-контроль"

    [Inject: 10s] -> [1-Wire In: DS18B20] -> [Function: "Prepare Internal Temp"] -> [Link Out: "To FSM Input"]

    [Inject: 15m] -> [HTTP Request: Weather API] -> [Function: "Prepare External Temp"] -> [Link Out: "To FSM Input"]

    [Link In: "To FSM Input"] -> [Function: "FSM Logic"] --+--> [Relay Out: AC Control (RL-01)]

    |

    +--> [Link Out: "To Dashboard"]

    |

    +--> [Link Out: "To Audit Log"]

    [Catch: All Nodes] -> [Function: "Format Error"] -> [Link Out: "To Audit Log"]

    // Вкладка "Dashboard"

    [Link In: "To Dashboard"] --> [ui_gauge: "Внутренняя температура"]

    [Link In: "To Dashboard"] --> [ui_text: "Внешняя температура"]

    [Link In: "To Dashboard"] --> [ui_text: "Режим работы"]

    // Вкладка "Audit Log"

    [Link In: "To Audit Log"] --> [Function: "Format Audit Message"] --> [MySQL Out: Audit Table]

    Рубрика оценивания:

    COURSE-16-M07-QUIZ — Тест по модулю "Интеграция с внешними сервисами, визуализация и тестирование"

  • Какой паттерн Node-RED наиболее подходит для управления сложными системами с несколькими состояниями и переходами?
  • a) Контракт сообщения

    b) Обработка ошибок

    c) Конечный автомат (FSM)

    d) Переиспользуемый компонент

  • Где рекомендуется хранить текущее состояние конечного автомата (FSM) в Node-RED для обеспечения его доступности всем узлам на вкладке?
  • a) `msg.payload`

    b) `global context`

    c) `flow context`

    d) `node context`

  • Для чего используется персистентное хранилище контекста (`contextStorage`) в Node-RED?
  • a) Для ускорения выполнения потоков

    b) Для сохранения состояния переменных контекста после перезагрузки контроллера

    c) Для обмена данными между разными экземплярами Node-RED

    d) Для шифрования конфиденциальных данных

  • Какую информацию обязательно нужно получить для интеграции с внешним веб-сервисом (API)?
  • a) IP-адрес сервера

    b) API ключ и документацию по запросам/ответам

    c) Имя пользователя и пароль

    d) MAC-адрес сервера

  • Какой узел Node-RED используется для выполнения HTTP-запросов к внешним API?
  • a) `mqtt in`

    b) `http in`

    c) `http request`

    d) `websocket in`

  • Что произойдет, если узел `Function` в Node-RED вызовет `node.error("Сообщение об ошибке", msg);`?
  • a) Node-RED завершит работу.

    b) Сообщение об ошибке будет выведено в консоль Node-RED, и узел `Catch` сможет его перехватить.

    c) Сообщение будет отправлено на следующий узел в потоке.

    d) Ничего не произойдет.

  • Какой узел Node-RED используется для создания графического интерфейса пользователя (Dashboard)?
  • a) `ui_button`

    b) `node-red-dashboard` (палитра)

    c) `http response`

    d) `template`

  • Почему важно использовать `node.status()` в узлах `Function`?
  • a) Для отправки сообщений в `Debug` панель.

    b) Для визуального отображения текущего состояния узла в редакторе Node-RED.

    c) Для сохранения данных в контексте.

    d) Для выполнения HTTP-запросов.

  • Что такое "контракт сообщения" в контексте Node-RED?
  • a) Соглашение о лицензировании Node-RED.

    b) Строгая структура `msg.payload` и других свойств `msg` для стандартизации данных.

    c) Договор между узлами о порядке их выполнения.

    d) Метод шифрования сообщений.

  • Если в Node-RED поток должен логировать все ошибки в базу данных MySQL, какой узел следует использовать для перехвата ошибок?
  • a) `Debug`

    b) `Switch`

    c) `Catch`

    d) `Delay`

    Мини-runbook «Если что-то не работает»

    * Решение: Проверьте правильность вашего API ключа. Убедитесь, что он активен в личном кабинете OpenWeatherMap. * Решение: Подключите узел `Debug` ко всем входам и выходам узла `Function: "FSM Logic"`. Проверьте, что данные приходят в правильном формате и что переменные контекста (`flow.get`) считываются корректно. Убедитесь, что вы используете `flow.set`, а не `context.set`.

    * Проверьте, что все `Link In` и `Link Out` узлы имеют правильные имена и соединены.

    * Решение: `85°C` — значение по умолчанию при включении. ` -127°C` — ошибка чтения. Проверьте физическое подключение датчика (контакты Data, VCC, GND). Убедитесь, что на шине 1-Wire есть подтягивающий резистор 4.7 кОм между VCC и Data. * Решение: Убедитесь, что палитра `node-red-dashboard` установлена и не отключена. Проверьте лог запуска Node-RED на наличие ошибок, связанных с dashboard. Убедитесь, что узлы Dashboard подключены к соответствующим `Link In` узлам. * Решение: Проверьте конфигурацию узла `MySQL Out`. Убедитесь, что таблица `audit_log` существует в базе данных и имеет соответствующие столбцы (`id`, `timestamp`, `event`, `source`, `details`). Проверьте логи MySQL на предмет ошибок подключения или записи. * Решение: Увеличьте гистерезис в логике FSM (например, `IDLE` -> `COOLING` при `temp_in > 26`, `COOLING` -> `IDLE` при `temp_in < 22`). Добавьте задержку (`Delay` узел) перед отправкой команды на реле, чтобы избежать мгновенных переключений.