» » » Реализация прототипа мобильного/веб клиентов и сервера для системы контроля оборудования предприятий

 

Реализация прототипа мобильного/веб клиентов и сервера для системы контроля оборудования предприятий

Автор: admin от 27-02-2018, 09:05, посмотрело: 115

Реализация прототипа мобильного/веб клиентов и сервера для системы контроля оборудования предприятий



Заказчик был заинтересован в системе, которая упрощала бы контроль за состоянием оборудования на предприятиях отрасли. Существующая процедура контроля представляет собой регулярные обходы оборудования установленного в цехах (по маршрутам) и заполнение бумажных бланков, с указанием в них показаний приборов и внешних признаков неисправностей.



Поскольку полная автоматизация (с контролем всех параметров датчиками и передачей данных с них по сети) технически и организационно невозможна, предлагалось частично автоматизировать процедуру. А именно — в местах проверок (контрольных точках) повесить NFC метки, а сотрудникам, осуществляющим обход, выдать смартфоны с приложением, которое фиксирует прикладывание смартфона к метке и предлагает ввести требуемые для каждой точки параметры. Данные затем передаются на сервер, откуда могут быть просмотрены руководителем в браузере (с отметками выхода за пределы допустимых значений, статистикой и пр).



Для реализации был выбран javascript, а конкретно — связка в виде NodeJS, React+Redux, React-Native (с обменом между ними через GraphQL). Это позволило использовать один язык для сервера, клиента и мобильного приложения. Прежде с перечисленными технологиями (кроме javascript) я вплотную не сталкивался, поэтому статья во многом отражает опыт знакомства с ними.



Функционал системы и организация БД



Сущности и их взаимосвязь



'checkpoint' — Контрольная точка, эквивалентна единице оборудования

'param' — Параметры контрольной точки

'unit' — Единица измерения параметра

'range' — Диапазон допустимых для данного параметра значений



'checkup' — Факт обхода контрольных точек по одному из маршрутов

'check' — Факт проверки контрольной точки в ходе обхода (содержит замечания по точке)

'checkvalue' — Фактическое значения одного параметра при проверке контрольной точки



'firm' — Компания

'plant' — Предприятие компании

'shop' — Цех предприятия

'route' — Маршрут по цеху

'rclink' — Привязка контрольной точки к маршруту (определяет порядок посещения точек на данном маршруте )



'position' — Должность пользователя

'user' — Пользователь



Реализация прототипа мобильного/веб клиентов и сервера для системы контроля оборудования предприятий



Привязка контрольных точек к маршрутам выполняется не напрямую, а через промежуточную rclinks. Это связано с тем, что в рамках одного цеха может быть несколько маршрутов, причём по одним и тем же контрольным точкам — на разных маршрутах, в разном порядке, и необязательно по всем. Поэтому, здесь используется двунаправленный список.



Все описанные сущности представлены в базе данных одноименными таблицами (во множественном числе — т.е. routes, checkpoints и т.д). Идентификаторы записей являются глобальными и генерируются через node-uuid
на сервере (в React Native — через react-native-uuid) с добавлением типа (для удобства отладки)



Например: 0ec74560-9da3-11e7-b19a-b5183ad4a4a8_checkpoint



Для каждой записи во всех таблицах фиксируется дата создания записи, дата её изменения (dt_crt, dt_mod), дата последней синхронизации (dt_sync), идентификатор сессии синхронизации (sync_id), а также id пользователя, создавшего или изменившегося запись (user_crt_id, user_mod_id).



Структуры баз на сервере и в мобильном приложении — идентичны (по этой причине для серверной части был выбран SQLite).



Перед началом работы в веб-интерфейсе создаются организации и их структура. Высшим уровнем иерархии является организация (firm), которая содержит производственные объекты (plants). Каждый объект имеет в своём составе цеха (shops). Уровень цехов технически является основным — к ним разрешается доступ должностям (positions), добавляются контрольные точки, в рамках цехов имеются маршруты (routes) и т.д.



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



Контрольные точки



Ключевой сущностью системы является понятие контрольной точки (checkpoint). Смыслом обхода (checkup) является посещение контрольных точек и снятие значений параметров размещённого на них оборудования (checkvalue) для последующего хранения и оценки.



Первоначальное добавление контрольной точки осуществляется в контексте заданного цеха (shop), через веб интерфейс. При этом NFC тэг в форму вносится путем касания смартфоном NFC метки.



Также, в процессе добавления точки к ней привязываются один или несколько параметров (params) и допустимые диапазоны значений (ranges). К параметрам — единицы их измерения (units), которые также привязаны к организации (firms). Т.е. каждая организация может иметь собственный набор единиц измерения.



Параметры могут быть двух типов — float (число) и boolean (значение вида да/нет). Третий тип (text) зарезервирован.



Для каждого параметра должны быть указаны минимальное и максимальное допустимые значения. При показе результатов обхода значения вне диапазона будут выделены красным.



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



Порядок задаётся промежуточной таблицей rclinks, где для каждой пары точка-маршрут указан id следующей и предыдущей точки (null для первой и последней).



Процесс обхода



При выполнении обхода мобильное приложение функционирует следующим образом:



Пользователь в предложенном ему, либо выбранном им маршруте нажимает кнопку «Начать обход». При этом создаётся checkup. При выборе контрольной точки пользователь заполняет значениями полученные приложением из params, units поля параметров. При их сохранении, для данной точки в контексте данного обхода создаётся check, в котором сохраняются также замечания по точке и к check привязываются созданные checkvalues, в которых сохраняются значения каждого из введённых параметров.



После сохранения значений параметров точка считается проверенной (т.к. для неё создан check) и ожидается следующая (в соответствии с rclinks.next_id) точка на данном маршруте.

При этом, уже проверенные точки допускается редактировать повторно (до окончания обхода). Пропуск же точек не допускается.



Серверная часть (backend)



Серверная часть реализована на NodeJS, SQLite, GraphQL и позволяет:



1. Хранить данные

2. Обеспечивать доступ к данным из мобильного и веб приложений с соответствующим контролем доступа



Для сервера используется express (с ним стыкуются graphql-server-express и express-jwt). SQLite — через node-sqlite (поддерживает промисы).



Сервер состоит из schema.js, где описываются типы и методы для GraphQL запросов (используется Apollo Server), resolvers.js (с методами, описанными в schema), connectors.js (со всей логикой и SQL запросами к БД) и, собственно, основного server.js



server.js





scheme.js





resolvers.js





connectors.js





Реализация прототипа мобильного/веб клиентов и сервера для системы контроля оборудования предприятий


Реализована двусторонняя неинкрементальная синхронизация данных между сервером и мобильным приложением. От сервера в мобильное приложение передаются



positions, shops, routes, rclinks, checkpoints, ranges, params, units, users



(для синхронизации формируется выборка только записей относящихся к цехам, разрешенным для должности (position) пользователя мобильного приложения). Т.е. состав выборки определяется должностью.



От мобильного приложения на сервер передаются результаты обходов — checkups, checks, checkvalues.



Веб клиент (frontend)



Веб клиент реализован на React + Redux и позволяет:



1. Сформировать в системе организационную структуру организации (промышленные объекты, цеха, пользователи, их должности), схемы и режим обходов (маршруты по цехам, контрольные точки на маршрутах, их параметры)



2. Контролировать режим и результаты проводимых обходов по маршрутам на объектах и в цехах, просматривать внесённые обходчиком значения параметров на контрольных точках, их отклонения от заданных допустимых значений, различную статистику



3. Регистрировать и авторизовывать зарегистрированных пользователей, обеспечивать им доступ к ресурсам



Все страницы представлены React компонентами и делятся на две группы — пользовательские (основной интерфейс) и административные (для контроля, выборочного просмотра и редактирования таблиц БД).



Реализация прототипа мобильного/веб клиентов и сервера для системы контроля оборудования предприятий



Навигация реализована на
react-router — в store хранится текущий контекст, в котором находится пользователь, поэтапно заполняемый вызовами updateContext(). В ссылках передаётся только путь к компоненту (кроме административных страниц, где можно указать id объекта в query string). Из данных контекста вверху каждой страницы строится навигационная строка показывающая, где находится пользователь.

Контекст автоматически сохраняется в local storage через redux-persist.



Вёрстка адаптивная (bootstrap), большинство компонентов страниц в основе имеет таблицу на react-bootstrap-table, ячейки которой заполняются результатами GraphQL запроса, параметрами которого являются id объектов, взятые из контекста и query string.

Там, где необходимо, для ячеек определены custom formatters/renderers (отображение дат, редактирование с выбором из списка и т.п.)

Для GraphQL используется Apollo (и в веб клиенте и в мобильном приложении).



Типовой компонент простой страницы с редактируемой таблицей выглядит примерно так:



components/PlantsPage.js:





components/PlantsContainer.js:





Для всплывающих уведомлений и сообщений об ошибках использован react-redux-toastr



Там, где требовались формы (создание контрольной точки, регистрация, логин, редактирование профиля и т.п.) использовался redux-form



Принцип работы с формами следующий:



components/ProfilePage.js:





components/CheckpointPage.js





components/CheckpointAttachPage.js





containers/CheckpointAttachContainer.js





Сервер:



server.js





schema.js





resolvers.js





Веб клиент:



index.js





components/AddCheckpointPage.js





containers/AddCheckpointContainer.js





В завершении разбора — небольшое видео, демонстрирующее работу мобильного приложения и веб клиента:





Средства разработки и отладка



Писалось и отлаживалось всё под Windows 7, но для демонстрации заказчику переносилось на linux сервер. Что касается среды разработки — я любитель Sublime. Правда, должен заметить, что с подсветкой синтаксиса gql запросов внутри js кода регулярно случаются глюки. Уж больно там это всё нетрадиционно сочетается. Пару раз возникала мысль попробовать VSCode (но пока держусь).



Реализация прототипа мобильного/веб клиентов и сервера для системы контроля оборудования предприятий



Установка nodejs под Win7 никаких особых проблем не вызвала, а вот с установкой и настройкой Android SDK так, чтобы всё потом вместе зашевелилось — пришлось хорошо повозиться.



Что касается средств отладки, то для веб клиента это обычный Chrome Dev tools с расширениями Apollo Client Developer Tools и Redux DevTools. Для мобильного приложения —
React Native Debugger (практически тоже самое, только реализованное отдельно на Electron).

Отмечу, что для локальной отладки с эмулятором, вместо localhost нужно использовать адрес 10.0.2.2



Кроме того, для удобства отладки в самих клиентах и сервере я реализовал возможность посылать SQL запросы к SQLite базе в смартфоне — как непосредственно со смартфона (из меню «Отладка»), так и удалённо — из веб приложения.



Во втором случае это осуществляется через механизм GraphQL подписки, через веб-сокеты. Веб приложение вызывает sendDebugRequest() на сервере, который формирует сообщение newDebugRequestCreated, на которое подписано мобильное приложение. В мобильном приложении при получении этого сообщения выполняется execSql(). Результат запроса отправляется на сервер вызовом sendDebugResponse(), который в свою очередь формирует сообщение newDebugResponseCreated, на которое подписано веб приложение. Фактически, это подобие чата, просто из мобильного приложения «отвечает» не человек, а БД.



Для просмотра и редактирования файлов SQLite под Win7 очень хорошо мне зашёл (после перебора разных вариантов) — SQLiteStudio.



Полезная мелочь — чтобы на сервере console.log() красиво и полностью выводил объекты, можно пользоваться util.inspect()



const util = require('util')
util.inspect.defaultOptions.showHidden = false
util.inspect.defaultOptions.depth = null
util.inspect.defaultOptions.maxArrayLength = 1000
util.inspect.defaultOptions.colors = true
[...]
console.log("data: ", util.inspect(data))


Также в связи с сервером — всё время забываю, что если в переменной промис то, чтобы увидеть результат в логе сервера не в виде "[Promise]", обязательно нужен then.



variable.then(function(result) {    console.log('Эврика!',result)  })


Авторизация/аутентификация



При выполнении регистрации, веб-клиент вызывает серверный метод signUp, где из введённого пароля при помощи bcrypt.hash генерируется хэш, который вместе с email (логином) помещается в БД.



При логине через веб-клиент вызывает серверный метод signIn, в котором проверяется наличие пользователя с данным email и корректность пароля (через bcrypt.compare). После этого в signIn вызовом jwt.sign генерирует токен, который возвращается веб-клиенту.



Веб-клиент сохраняет токен в localStorage и далее достаёт его оттуда и предъявляет серверу в http заголовке при каждом GraphQL запросе (для чего в network.use applyMiddleware указывается req.options.headers.authorization = `Bearer ${token}`)



Сервер, получая GraphQL запросы, в server.js каждый раз берёт полученный с ними токен из http заголовка Authorization, проверяет и дешифрует его при помощи express-jwt, получая user.id, по которому из БД извлекаются необходимые данные о правах пользователя, затем передаваемые в context, доступный внутри всех вызываемых клиентом серверных методов (в connectors.js). При этом важно, что jwt() вызывается с параметром «credentialsRequired: false», что позволяет игнорировать ошибки аутентификации и обрабатывать их самим (это необходимо для работоспособности компонентов SignIn и SignUp в незалогиненном состоянии).



Мобильное приложение, в отличии от веб клиента, должно работать без доступа к сети. Поэтому, при синхронизации БД с сервера, среди прочего передаются данные (email, хэш пароля) пользователя. При логине в мобильное приложение запроса к серверу не осуществляется — проверяется существование пользователя и корректность пароля либо nfc_id (взависимости от того, вводится логин вручную или прикладывается NFC карта) по копии в локальной БД, вызовом bcrypt.compare для React Native.



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



Проблемы



Как уже было упомянуто в начале статьи, это моё первое знакомство с перечисленными технологиями. И вот каковы мои впечатления:




  • Зоопарк пакетов, связанных сложными зависимостями, причём каждый тянет за собой десяток других. Обновление одних нередко всё ломает — причём, иногда это происходит без каких-либо сообщений об ошибках и обнаружить, в чём проблема — весьма сложно. Особенно неприятны ситуации, когда какой-то пакет необходимо обновить, т.к. в нём исправлен критичный баг, но обновлять его нельзя, потому что тогда перестанет работать третий. Другая ситуация — когда обновить необходимо, но в пакете изменилась какая-то логика и требуется переписать, скажем, 10-20% моего приложения. Получается дилемма — или ты сидишь на старых версиях тщательно подобранных друг к другу пакетов и боишься чихнуть, или постоянно всё обновляешь и занят переделыванием своего кода под всё новые и новые изменения в них.



    Чисто эмоциональный аспект: ситуация, когда node_modules/ для проекта занимает порядка 200mb и содержит десятки тысяч (!) файлов — с непривычки шокирует. Да, понятно, что в окончательный код попадает не так уж много (десятки файлов и единицы мегабайт), но тем не менее.

  • Настройка webpack/babel — очень нетривиальное занятие. Я так понимаю, люди не так часто руками пишут все конфиги с нуля — наверное обычно берут какой-нибудь готовый «суповой набор», типа create-react-app и create-react-native-app. По этому пути пошёл и я, но в итоге всё равно возникли ситуации, когда пришлось лезть в конфиги и разбираться. Кроме того, есть вещи, которые такие наборы принудительно навязывают (к примеру, неотключаемый eslint или внезапный service-worker.js, используемый для кэширования)


  • При сборке (в моём случае — под Win7) в разных ситуациях регулярно возникают сообщения об ошибках, которые на самом деле не указывают на проблемы с приложением и зачастую эти сообщения лечатся простым перезапуском. Причём, вчера ошибки не возникали, сегодня появились, завтра могут исчезнуть опять и потом вновь появиться. К примеру, в данный момент при запуске react-native run-android --variant=release у меня последовательно появляются несколько разных ошибок (связанных, по всей видимости, с невозможностью получить доступ к файлам в assets/) и проблема решается трёх-пятикратным повторением этой операции (если не помогает, ещё приходится дополнительно почистить android.gradle ).



    Когда начинаешь гуглить обо всём этом, выясняется, что через такое проходят сотни человек, причём все решают это разными (часто — магическими) способами, которые у каждого свои и редко помогают другим страдальцам. Постепенно к этому привыкаешь и уже интуитивно понимаешь, что конкретно надо попробовать предпринять в первую очередь.



    Стоит также упомянуть о плясках с бубном для получения подписанного для релиза apk — с ползанием по конфигам gradle и выяснением что, оказывается, где-то там надо было буквы строчные вместо прописных, лишний перевод строки, еще один конфиг не в том директории где он был (внезапно!) и т.п. ньюансы. Это тоже одна из популярных проблем, судя по огромному количеству вопросов, советов и магических решений.



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




  • Что бы я сделал иначе, если бы начинал эту работу сейчас?




  • Взял бы более свежую комбинацию пакетов:

  • React Router 4 (он сильно изменился по сравнению с 3)

  • Новую четвертую версию react-native-router-flux (аналогично)

  • Вместо своего NFC велосипеда посмотрел бы, что появилось в этой области. Например это.

  • Обновил версию Apollo GraphQL и попробовал бы весь обмен в GraphQL сделать через сокеты (сейчас через сокеты только сообщения).

  • Вместо react-bootstrap-table использовал бы react-bootstrap-table2 или что-то другое (может быть react-table).

  • Возможно, отказался бы от Bootstrap. С другой стороны, Material UI выглядит на десктопе не очень адекватно в плане использования пространства экрана. По существу, итоговый выбор еще зависит и от остальных используемых пакетов.

  • Для работы с БД возможно стоит использовать прокладку в виде Sequilize. Кроме того, по-прежнему не уверен, следовало ли в мобильном приложении возиться с SQLite и не стоило ли всё, что нужно (данные с сервера, результаты обходов) держать вместо этого в redux store.

    Вообще, понимание, что нужно хранить в store, для чего достаточно локального state, а что помещать в БД — это всё, конечно, приобретается с опытом.

  • Использовать в React Native другой drawer (тот, что был доступен на момент начала работ был не целиком нативным и из-за этого заметно тормозил, а при включенной отладке — вообще еле двигался). По идее, сейчас этот вопрос уже решён в react-navigation, которая используется в новом react-native-router-flux.

  • Формы, на мой взгляд — настоящая проблема. Я использовал redux-form, но даже в веб клиенте простые вещи решаются достаточно сложно и запутанно. А с React Native она вообще не очень работает. Несколько раз я думал избавиться от redux form и сделать всё самому и в react и в react native. Но лень каждый раз побеждала.



  • В заключение, порекомендую две статьи, которые однозначно стоит прочесть по теме (таких статей, увы, не так уж много, особенно в связи с быстрым устареванием материала): Создание «клона WhatsApp» (на React-Native, NodeJS, GraphQL) и лучшая, на мой взгляд, статья про GraphQL: Dive intro GraphQL.

    Источник: Хабрахабр

    Категория: Админитстрирование » Сетевые технологии

    Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
    Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

    Добавление комментария

    Имя:*
    E-Mail:
    Комментарий:
    Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
    Введите два слова, показанных на изображении: *