Тема оформления
Группировка и слияние телеметрии
1. Концепция технологических групп оборудования
В условиях реального промышленного производства тепловой агрегат (печь или сушильный барабан) представляет собой сложный распределенный комплекс. Технологические датчики подключаются к различным шкафам автоматики и модулям ввода-вывода:
- Термопары рабочих зон опрашиваются многоканальными модулями аналогового ввода ПЛК (AI 8xTC);
- Расходомер природного газа передает объемный расход через искробезопасный барьер по интерфейсу RS-485 Modbus RTU;
- Весовой терминал вагонеток передает массу садки по протоколу Modbus TCP через цеховой Ethernet;
- Электросчетчики ТЭНов включены в автоматизированную систему коммерческого учета электроэнергии (АСКУЭ);
- Оптический газоанализатор дымовых газов выдает концентрации через собственный цифровой интерфейс.
Для объединения разрозненных потоков телеметрии в микросервисе HeatBalance.IoTService реализована концепция Технологических групп (Device Groups). Группа устройств выступает цифровым двойником физической печи.
2. Модель данных технологической группы
Сущность DeviceGroup описывается следующей схемой:
csharp
public sealed class DeviceGroup
{
public Guid Id { get; set; }
public string OwnerId { get; set; } = string.Empty; // Изоляция данных пользователя
public string Name { get; set; } = string.Empty; // Наименование печи/участка
public CalculationType CalculationType { get; set; } // 1: Conveyor, 2: Chamber, 3: Electric, 4: Drum
public Guid DatasetId { get; set; } // Эталонный шаблон геометрии и констант
public List<Guid> DeviceIds { get; set; } = new(); // Идентификаторы связанных датчиков
public DateTimeOffset CreatedAt { get; set; } // Дата регистрации группы
}Связанные сущности и ограничения:
- Изоляция арендаторов (
OwnerId): пользователи имеют доступ исключительно к своим группам и связанным датчикам; - Привязка к датасету (
DatasetId): группа жестко связана с конфигурационным датасетом, содержащим статические константы агрегата (теплоемкости материалов, коэффициенты излучения, размеры рабочей камеры); - Список датчиков (
DeviceIds): динамический массив UUID, определяющий, какие зарегистрированные измерительные каналы входят в состав агрегата.
3. Алгоритм динамического слияния (JSON Deep Merge)
Математическая постановка задачи
Пусть — базовый JSON-документ технологического датасета, представленный в виде множества пар «ключ-значение»:
Пусть агрегат объединяет множество датчиков . Для каждого датчика в базе данных хранится история показаний. Обозначим через самый последний по временной метке валидный телеметрический пакет датчика :
Результирующий расчетный вектор формируется оператором правостороннего слияния:
Правило разрешения коллизий для любого ключа :
Таким образом, динамические оперативные показания КИПиА приоритетно перекрывают статические проектные уставки базового датасета.
Программная реализация слияния в DeviceGroupsController
Слияние выполняется на базе узлов DOM-дерева System.Text.Json.Nodes.JsonObject:
csharp
// 1. Извлечение базового шаблона датасета
var dataset = await _datasetApiClient.GetDatasetAsync(group.DatasetId);
var datasetNode = JsonNode.Parse(dataset.InputJson) as JsonObject ?? new JsonObject();
// 2. Итеративный обход всех датчиков группы
if (group.DeviceIds != null && group.DeviceIds.Count > 0)
{
foreach (var deviceId in group.DeviceIds)
{
// Извлечение самого свежего пакета телеметрии для каждого датчика
var latestDeviceLog = await _dbContext.TelemetryLogs
.Where(l => l.DeviceId == deviceId && !string.IsNullOrWhiteSpace(l.PayloadJson))
.OrderByDescending(l => l.Timestamp)
.FirstOrDefaultAsync(ct);
if (latestDeviceLog != null && !string.IsNullOrWhiteSpace(latestDeviceLog.PayloadJson))
{
var telemetryNode = JsonNode.Parse(latestDeviceLog.PayloadJson);
if (telemetryNode is JsonObject telemetryObj)
{
// Наложение динамических полей поверх шаблона датасета
foreach (var prop in telemetryObj)
{
datasetNode[prop.Key] = prop.Value?.DeepClone();
}
}
}
}
}
// 3. Формирование монолитного расчётного вектора
var mergedInputJson = datasetNode.ToJsonString();
log.MergedPayloadJson = mergedInputJson;4. Пример пошаговой трансформации данных
Исходный шаблон датасета (статические характеристики печи):
json
{
"dryMaterialMass": 10000.0,
"initialMoisture": 10.0,
"finalMoisture": 2.0,
"airTemperature": 20.0,
"recirculateTemp": 200.0,
"fuelCalorificValue": 35800.0,
"wallsArea": 120.0,
"vaultArea": 45.0
}Пакет телеметрии от датчика влажности (ID 2):
json
{
"initialMoisture": 14.5,
"finalMoisture": 1.1
}Пакет телеметрии от термопары рециркулята (ID 0):
json
{
"recirculateTemp": 242.8,
"airTemperature": 24.0
}Результирующий расчетный вектор MergedPayloadJson:
json
{
"dryMaterialMass": 10000.0,
"initialMoisture": 14.5,
"finalMoisture": 1.1,
"airTemperature": 24.0,
"recirculateTemp": 242.8,
"fuelCalorificValue": 35800.0,
"wallsArea": 120.0,
"vaultArea": 45.0
}Обратите внимание: конструктивные параметры (fuelCalorificValue, wallsArea, vaultArea, dryMaterialMass) сохранены из проектного шаблона, а динамические теплотехнические параметры обновлены до фактически измеренных физических значений.
5. Конвейер выполнения группового расчета
Инициация расчета теплового баланса группы выполняется через эндпоинт:
http
POST /api/v1/iot/groups/{id}/calculate
Authorization: Bearer <JWT-токен оператора>6. Аудит и структура таблицы GroupTelemetryLogs
Все выполненные расчеты агрегатов сохраняются в таблице GroupTelemetryLogs для построения трендов и анализа энергоэффективности:
| Поле | Тип данных | Назначение |
|---|---|---|
Id | UUID | Уникальный суррогатный ключ записи |
GroupId | UUID | Внешний ключ привязки к технологической группе |
Timestamp | DateTimeOffset | Точное астрономическое время инициации расчета (UTC) |
Status | TelemetryStatus | Статус выполнения: 0: Pending, 1: Success, 3: CalculationError |
MergedPayloadJson | TEXT | Полный объединенный JSON входных параметров, переданный в расчет |
ResultJson | TEXT | Полный структурированный результат расчета статей теплового баланса |
Efficiency | DOUBLE PRECISION | Итоговый эксергетический или тепловой КПД установки (, %) |
ErrorMessage | TEXT | Диагностическое сообщение об ошибке (при сбое расчета) |
Получение ретроспективной истории расчетов группы осуществляется методом:
http
GET /api/v1/iot/groups/{id}/historyОтвет возвращает хронологический список выполненных балансов, упорядоченный от самых свежих к архивным, что позволяет строить графики деградации футеровки и изменения удельных энергозатрат во времени.