Реализация мониторинга промышленного оборудования через мобильное приложение
Мониторинг промышленного оборудования — это задача не столько разработки, сколько правильного выбора архитектуры сбора данных. Вибродатчик на подшипнике генерирует 25 600 сэмплов в секунду. Температурный датчик — раз в 5 секунд. Ток двигателя — 1000 измерений в секунду. Мобильное приложение не может и не должно работать с этим потоком напрямую — только агрегированные показатели, тренды и алерты.
Архитектура сбора данных
На уровне оборудования данные собирает Edge-компонент: промышленный шлюз (Moxa, Advantech, Siemens IPC) или кастомный Linux-сервер. Он нормализует данные с разных протоколов и публикует агрегаты в MQTT или отдаёт через REST/WebSocket.
Для мобильного приложения важны два потока:
- Реальное время — текущие значения ключевых параметров, обновление раз в 1-5 секунд. WebSocket.
- История — тренды за смену, сутки, неделю. REST API с пагинацией и агрегацией.
Датчики → PLC / Edge Gateway → Time-Series DB (InfluxDB / TimescaleDB)
↓
Backend API (REST + WebSocket)
↓
Мобильное приложение
Реальное время через WebSocket: Flutter
class EquipmentMonitorRepository {
WebSocketChannel? _channel;
final StreamController<EquipmentState> _stateController =
StreamController.broadcast();
Stream<EquipmentState> get stateStream => _stateController.stream;
void connect(String equipmentId, String token) {
_channel = WebSocketChannel.connect(
Uri.parse('wss://iiot.factory.com/ws/equipment/$equipmentId'),
);
_channel!.stream
.map((event) => json.decode(event as String))
.map(EquipmentState.fromJson)
.listen(
_stateController.add,
onError: _handleError,
onDone: _scheduleReconnect,
);
_channel!.sink.add(json.encode({'auth': token}));
}
void _scheduleReconnect() {
Future.delayed(const Duration(seconds: 5), () => connect(_lastId, _lastToken));
}
}
BLoC для управления состоянием экрана мониторинга:
class EquipmentMonitorBloc extends Bloc<EquipmentEvent, EquipmentMonitorState> {
StreamSubscription<EquipmentState>? _subscription;
EquipmentMonitorBloc(this._repository) : super(EquipmentMonitorInitial()) {
on<StartMonitoring>((event, emit) async {
_subscription = _repository.stateStream.listen(
(state) => add(StateUpdated(state)),
);
_repository.connect(event.equipmentId, event.token);
});
on<StateUpdated>((event, emit) {
final current = event.state;
final isAlert = current.temperature > 85.0 || current.vibrationRms > 12.5;
emit(EquipmentMonitorRunning(state: current, hasAlert: isAlert));
});
}
}
Визуализация трендов
Для исторических данных используем fl_chart (Flutter) или MPAndroidChart (Android native). Ключевая оптимизация — не передавать в чарт все 86 400 точек за сутки, а агрегировать на стороне API. Запрос к InfluxDB-based API:
GET /api/v1/equipment/{id}/trend?
parameter=temperature&
from=2024-01-15T06:00:00Z&
to=2024-01-15T18:00:00Z&
resolution=300 # агрегация по 5 минут
Ответ — массив из 144 точек вместо 43 200. Чарт рисует без фризов.
Baseline и отклонения
Полезная функция — отображение baseline (нормального диапазона) на графике. Если ток двигателя в норме 12-15А, выделяем зону, и оператор сразу видит отклонение:
LineChartData buildTrendChart(List<TrendPoint> data, Range baseline) {
return LineChartData(
extraLinesData: ExtraLinesData(
horizontalLines: [
HorizontalLine(y: baseline.min, color: Colors.green.withOpacity(0.3)),
HorizontalLine(y: baseline.max, color: Colors.green.withOpacity(0.3)),
],
),
betweenBarsData: [
BetweenBarsData(
fromIndex: 0,
toIndex: 0,
color: Colors.green.withOpacity(0.1),
),
],
lineBarsData: [
LineChartBarData(
spots: data.map((p) => FlSpot(p.timestamp.toDouble(), p.value)).toList(),
color: data.any((p) => p.value > baseline.max || p.value < baseline.min)
? Colors.red
: Colors.blue,
),
],
);
}
Алерты и эскалация
Простой алерт — пуш-уведомление через FCM/APNs. Но для критических ситуаций нужна эскалация: если оператор не подтвердил алерт за 5 минут — уведомление идёт мастеру смены, потом начальнику цеха.
Логику эскалации держим на бэкенде, мобильное приложение только отображает и подтверждает. Подтверждение алерта:
Future<void> acknowledgeAlert(String alertId) async {
await _dio.post('/api/v1/alerts/$alertId/acknowledge', data: {
'acknowledgedBy': currentUser.id,
'acknowledgedAt': DateTime.now().toIso8601String(),
'note': _noteController.text,
});
}
Экран алерта показывает историю эскалации — кто получил, кто видел, кто подтвердил. Это важно для постсменного анализа.
Офлайн и работа в цехе
Производственные цеха — зона нестабильного Wi-Fi. Критичные данные (текущие показатели, активные алерты) кешируем в SQLite через Room или Drift. При потере соединения показываем последнее известное состояние с меткой «данные от HH:MM, нет связи».
Разработка приложения мониторинга для одного типа оборудования с WebSocket телеметрией и историческими трендами — 4-6 недель. Добавление поддержки нескольких типов оборудования, сложных алертов с эскалацией, офлайн-режима — 2-3 месяца. Стоимость рассчитывается индивидуально после анализа источников данных и требований к надёжности.







