Смышлёные остановки Страница 1 из 7
Сегодня у нас необычный повод поделится нашим опытом с хабросообществом. Не секрет, что в большинстве случаев проекты и продукты, которые мы создаем, живут «виртуальном» мире, и мы не всегда можем проследить от самого начала и до конца как все то, что мы делаем работает в реальной жизни.
Проект, о котором мы хотим рассказать получил у нас кодовое и немного смешное название «Смышлёные Остановки».
Итак, в чем суть – в городе «Н» есть городской транспорт на котором установлены системы слежения основанные на датчиках ГЛОНАСС, данные о перемещении стекаются в центральную диспетчерскую службу в виде постоянного потока сообщений от этих самых датчиков. Хочется разместить на остановке жидкокристаллическую панель, которая покажет, когда и какие транспортные средства, идущие по маршрутам прибудут.
Идея звучит просто и для ее реализации все есть – расписание маршрутов, сами маршруты, датчики и информация от них, координаты остановок, транспортные средства, вышедшие в рейс. Дело за малым – собрать это все в одну кучу и заставить работать.
Именно об этом мы и хотим сегодня рассказать, как от протокола общения с датчиками мы дошли до сборки железа для умной остановки и показали работающий прототип на выставке ТЕХНОПРОМ 2014.
Начнем с принципиальной архитектуры системы, а потом пробежимся по деталям.
Собираем данные в реальном времени
На данный момент нет единой системы или стандарта предоставления оперативных данных. В разных городах различные провайдеры, которые используют различные способы раздачи данных и различный формат и содержание. На данный момент мы столкнулись с двумя: передача данных по TCP и REST API на стороне провайдера.
Пример пакета, который мы получаем по TCP
<code>#D#00287;190614;034452;5628.0000;N;8457.8226;E;0;272;123;12;gosnum:3:379,num:3:3 тролл </code>
Пример пакета из REST API
<code>{ "online" : [ { "deviceHash" : 0558132541, "name" : "Автобус Х564ВЕ96", "timestamp" : 1384710565, "latitude" : 50.65, "longitude" : 60.56, "speed" : 43.5, "course" : 120.1, "sats" : 8, "route" : "021-bus" }, ... ] } </code>
Формат и содержание пакетов:
- Идентификатор устройства.
- Именование или идентификатор ТС – номер ТС и тип ТС.
- Время снятия данных.
- Широта и долгота.
- Скорость.
- Курс.
- Количество спутников.
- Идентификатор маршрута по которому движется ТС.
Все эти данные используются в работе алгоритма прогнозирования.
Рассмотрим оба варианта получения данных.
Функции и ответственность листнера:
- Получение “сырых” данных от провайдера.
- Парсинг данных и переработка в формат понимаемый системой. Из строки или из JSON в POJO.
- Пересылка полученного POJO с данными в JMS в заранее определенный топик.
- Отказоустойчивость. Для идентификации проблем в получении данных от провайдера или физической недоступности листнера, листнер должен отсылать ping пакеты в JMS.
- Сохранение POJO после парсинга в отдельную базу данных. Сохранение сырых данных длинной в неделю. Данный механизм сделан для того, чтобы иметь возможность накопить минимальную статистику работы алгоритма. В процессе жизни системы предполагалась доработка и донастройка алгоритма, и, чтобы иметь возможность проверить изменения в алгоритме, мы сохраняем данные. И умеем их проигрывать для накопления статистики и оценки работы алгоритма после изменений.
В случае с TCP мы указываем хост и порт, и провайдер данных отсылает пакеты на этот хост. Далее в дело вступает наш листенер TCP канала.
Мы использовали в качестве основы для TCP листнера Netty. Он очень прост в использовании и стабилен в работе. В случае с REST API мы использовали клиента Jsoup, который опрашивает api по таймеру.
Сам листнер, это консольное приложение с возможностью указать хост, порт и настройки для JMS и базы данных.
Процесс работы листнера:
- Получение данных по факту в случае с TCP или по таймеру в случае REST API.
- Разбор строки или JSON и создание объекта класса NavigationPackage.
- Отправка NavigationPackage в JMS.
- Сохранение NavigationPackage в базу.
- Отправка ping пакета в JMS (объект класса PingPackage).