Файл: Пояснительная записка к курсовой работе Разработка по для кодового замка по курсу Программирование микропроцессоров.docx

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 22.11.2023

Просмотров: 127

Скачиваний: 3

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.


Передача данных с аппаратного уровня на сетевой происходит при помощи Serial-communicationчерез пины rx/tx. Взаимодействие же сетевого и серверного уровней осуществлено при помощи HTTP-запросов на унифицированный указатель ресурса сервера.



Рисунок. 1.10 - Структура программного обеспечения

7 ОПИСАНИЕ ПРОЦЕССА ОТЛАДКИ РАЗРАБОТАННОГО ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ



При разработке программного обеспечения для отладки работоспособности кода для Arduino Uno использовалась библиотека avr-debugger в связке с IDE Visual Studio Code 1.67 [7].

Для отладки кода для серверной части на Java 8 написана документация для OpenAPI [https://www.openapis.org/] [8]. Также для упрощения процесса отладки приложение имеет конфигурацию запуска в тестовом окружении, где помимо наличия уже готовых скриптов по внедрению данных в тестовую базу данных h2, отображаются все исполняемые SQL-запросы от пользователя и сетевого уровня кодового замка.

Для отладки HTTPзапросов использовалась кроссплатформенная служебная программа командной строки, позволяющая взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL – cURL [9]. А также Postman [10] с заготовленным набором тестовых данных.

При проектировании серверной части использовалась методология разработки REST-приложений [11]
  1. ОПИСАНИЕ РАЗРАБОТАННОГО И ОТЛАЖЕННОГО ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ




8.1 Аппаратный уровень


Описание последующего кода представлено в приложении 3 (Исходное ПО аппаратного уровня).

В методе setup() производится инициализация Serialпорта и установка его значения ожидания на 100 миллисекунд. Нужно это для взаимодействия Arduino Uno с ESP8266 NodeMCU через rx/tx пины. Далее инициализируется LCD2004 – включается подсветка и регистрируются пользовательские символы, а именно: перевёрнутый знак степени, для использования его в меню, и два квадрата с заливкой и без, они будут использоваться для отображения параметров конфигурации кодового замка. Затем проверяется открыта ли дверь, после чего за пином, к которому подключен датчик Холла, закрепляется doorInteruptAction - прерывание по падению.

Метод doorInteruptAction() нужен для прослушивания датчика Холла, если пользователь получил доступ с пароля, то прерывание на этом методе будет откреплено, если доступ не с пароля, то на сетевой уровень отправляется запрос на регистрацию непредвиденного доступа.


В методе loop() происходит переход в openMenu(), который сначала обновляет конфигурацию, и затем, если из переменных конфигурации выходит так, что замок заблокирован, то происходит переход в режим блокировки, в котором пользователю мы отрисовываем сообщение о блокировке на LCDпо средством метода drawBlock() и каждые 5 секунд запрашиваем конфигурацию с сетевого уровня, ожидая, что главный пользователь разблокирует доступ с взаимодействием кодового замка. Если переменная конфигурации для блокировки имеет значение false, то замок переходит в нормальный режим работы. В нормальном режиме работы производится отрисовка меню при помощи метода drawMenu(), выводим пользователю предложение выбрать нужную опцию, а на следующей строке – линию разделения. Затем методом keyAndSettingsScene() отображаем пользователю опции ввода с клавиатуры и настройки. После чего пользователь при помощи listenKeyboardAndChanegeLcd клавиатурой может выбрать необходимую ему опцию, выбранная опция подсвечивается мигающим квадратом.

При выборе опции настроек происходит переход в метод openSettings(), в котором методом drawMenuSettings() производится отрисовка заглавного меню настроек, и список со всеми настройками, такие как: показывать пароль при вводе, издавать звук пьезоэлементом при действии пользователя. В зависимости от значения настройки квадрат напротив настройки будет либо с заливкой, либо без, соответственно означая включен он или выключен для этого параметра. При нажатии клавиши подтверждения параметр меняется на противоположное значение и сохраняется. Также при установке настройки на аппаратном уровне она становится приоритизированной, то есть при получении конфигурации с сетевого уровня приоритет выдаётся пользовательским настройкам с аппаратного уровня, таким образом настройки конфигурации с сервера не будут применены, за исключением настройки блокировки доступа.

Если пользователь в главном меню выбирает способ ввода пароля при помощи клавиатуры, то производится переход к методу openUsingKeypad(), где отрисовывается заголовок в первой строке с просьбой пользователя ввести пароль. На второй строке мы выводим пользователю его введённые символы, в зависимости от того какие настройки для отображения пароля были выбраны. При включении этого параметра пароль будет выводится в таком виде в каком он есть, при отключении этого параметра пароль будет отображаться только в количественной мере символом «*». При написании пароля пользователь может ошибиться, для удаления неправильных символов он может нажать на клавишу удаления «С», для подтверждения ввода пароля пользователь нажимает на клавишу «А». После чего на экране отрисовывается надпись с проверкой пароля, далее введённый пользователем пароль отправляется на сетевой уровень при помощи метода sendPasswordOnCheck(enteringPassword, "type")), где получается либо подтверждение совпадения в базе паролей, либо отказ. При неудачной попытке ввести пароль больше трёх раз, кодовый замок блокируется, а на сетевой уровень отправляется запрос на переопределение переменной блокировки в модели конфигурации кодового замка в базе данных при помощи метода sendBlockState(). Если введённый пользователем пароль оказался правильным с сетевого уровня в метод applyPassword(boolean) приходит значение true, в данном методе замок открывается. При любом окончании ввода пароля буферная переменная enteringPassword обнуляется. Если в метод applyPassword передаётся true, помимо открытия замка на LCD пишется надпись об успешном вводе пароля и будет произведено обнуление счётчика попыток ввода, если false - пользователю на экран будет выведено сообщение о неправильном пароле и к счётчику попыток неправильного ввода будет добавлена единица, также в зависимости от параметра конфигурации звука от кодового замка при правильном пароле и выключенном параметре звука,

звуковое оповещение не будет воспроизведено, однако при неправильном пароле вне зависимости от этой конфигурации пьезоэлемент будет издавать звук.

Датчик Холла в данном проекте играет важную роль, помимо регистрации непредвиденного доступа к кодовому замку, одна из его функций — это после некоторого времени ожидания, смотреть на то, закрыл ли пользователь дверь или нет. Так как в случае если пользователь воспользовался объектом, но при этом не закрыл до конца дверь, регистрация непредвиденного доступа не будет осуществлена, так как прерывание с датчика не будет работать. При корректном пароле пользователю даётся n-времени на взаимодействие с объектом, по истечении данного времени проверяется дверь, если она закрыта, то на сетевой уровень отправляется запрос на регистрацию закрытия двери, иначе регистрация неполного закрытия двери, за данным действием будет закреплён последний пользователь, который авторизовано взаимодействовал с замком.

Так как в кодовом замке имеется прерывание, использование delay() может помещать корректной работе прерывания на датчике Холла, поэтому для функции ожидания используется метод improvedDelay(unsignedint), который на вход принимает время ожидания в миллисекундах.


8.2 Сетевой уровень


Описание последующего кода представлено в приложении 4 (Исходное ПО сетевого уровня).

Инициализация платы производится в методе setup(), в котором мы подключаемся к сети по локальной сети Wi-Fi, переводим пины для светодиодов индикации HTTPзапросов и индикации взаимодействия с аппаратным уровнем. Также инициализируем Serial-communicationна той же скорости, что и аппаратный уровень, то есть в 9600 бод/сек и таймаутом в 5 миллисекунд.

В методе loop() расположен один метод hardwareListener(), который слушает все запросы с аппаратного уровня и в зависимости от запроса выполняет необходимые действия.

В методе hardwareListener() проверяется доступно ли к записи какие-либо байты, если они доступны, то все они записываются в expression, который обрабатывает первую инструкцию, в случае проверки пароля, первая команда запроса от аппаратного уровня содержит инструкцию «check», далее прописывается тип проверяемого пароля в переменную type, а его значение записывается в переменную enteringPassword. Полученные данные для проверки передаются в метод checkPassword для сравнения этих данных с полученными с серверного уровня. При отсутствии подходящих паролей в базе данных, checkPassword вернёт false, после чего на аппаратный уровень будет отправлен ответ «wrong*», а на серверный уровень отправится POST запрос с записью действия с описанием «WrongKeyAccess». Если пароль в базе имеется, то аппаратный уровень получит ответ «open*», а на серверный уровень будет отправлен POST запрос с записью действия с описанием «WrongKeyAccess».

Алгоритм работы checkPassword заключается в получении JSONфайла с сервера с уникальными идентификаторами пользователей, типом паролей и их значением. Дальше введённый пользователем пароль поочерёдно сравнивается с каждым элементом из JSONфайла, при нахождении совпадения метод возвращает true, иначе false.

Помимо сравнения паролей в методе hardwareListener() имеется метод для проверки датчика Холла, если expressionсодержит значение «hall», то значение для параметра описания в POSTзапросе будет взято из последующего за ним, то есть «hall:Open»в POSTзапрос поместит «Open».

Для получения конфигурации в expressionв hardwareListener() должен содержать «getConfig»после чего будет вызван метод getServerConfig(), в ответ с сервера будет получен JSONфайл вида:


{

“sound”: true,

“showPassword”: true,

“lock”: false

}

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

Для изменения в модели конфигурации кодового замка на сервере используется POSTзапрос по пути “/esp/setLock” в методе sendLockConfigAction(). Данный метод будет вызван только в случае, если expressionсодержит значение lock.

Для записи всех действий на серверном уровне используется метод sendAction(), который проводит сериализацию JSONфайла вида:

{

“openerId”: “id”,

“descritption”: “descritption”,

}

В первый параметр отправляется значение взаимодействующего пользователя. Если же происходит обработка непредвиденного доступа, то openerId имеет значение 1.

8.3 Серверный уровень


Исходный код серверного уровня расположен на сервисе GitHubпо ссылке https://github.com/Niatomi/keylock дальнейшее описание данного уровня будет относится к проекту spring-keylock в данном репозитории.

На серверной части реализована model-view-controllerархитектура на фреймворке SpringMVC. [12]

Для обработки запросов написан интерфейс ESPController, а также имеется его реализация в ESPControllerImpl. Для получения списка всех паролей используется метод getPasswords(). Для добавления действий в историю взаимодействий используется метод addActions(@RequestBody(ActionsHistoryDtoactionsHistory)), который принимает DTO [13] объект, обладающий только уникальным идентификатором пользователя, а также описанием действия. Для получения конфигурации используется метод getConfig(), который возвращает ResponseEntity[14]. Для переопределения модели конфигурации модели используется метод setLock().

Взаимодействие с базой данных происходит черезspringмодуль repository, а именно его реализация CrudRepository [15].

Обработка данных полученных или запрашиваемых с контроллера для ESPосуществляется на слое сервиса, который имеет все те же методы, что и контроллер, только на этом слое происходит вся логика для взаимодействия cбазой данных.

Пользовательская часть web-приложения представлена в ClientController, именно этот интерфейс должен использоваться для внедрения его на стороне пользовательского интерфейса, будь то web-приложение, либо приложение, ориентированное под определённую операционную систему.

При помощи этого интерфейса главный пользователь может:

  • Проводить регистрацию новый взаимодействующих с объектом пользователей – signUpOpener();

  • Обновлять информацию о пользователях – updateOpener();

  • Добавлять пароли для пользователей по уникальному идентификатору – addPassword();

  • Получать всех зарегистрированных пользователей вне зависимости от того имеют ли они пароль или нет – getAllOpeners();

  • Получать данные для одного пользователя по его уникальному идентификатору – getOpener();

  • Удалять пользователя по уникальному идентификатору – deleteOpener();

  • Удалять только пароли закреплённые за пользователем – deleteOpenerPasswords();

  • Открывать или блокировать доступ к взаимодействию с кодовым замком – openKeylock() и blockKeylock() соответственно.

Для получения данных с постраничным представлением данных используются методы:

  • getOpenersWithPasswordInPages – получение всех пользователей имеющих паролей;

  • getAllOpenersInPages – получение всех пользователей;

  • getAllHistoryInPages – получение всей истории взаимодействий.

Реализация данного интерфейса представлена в классе ClientControllerImpl. Каждый из методов данного контроллера обращается к одноимённым методам на слое сервиса.

Для обработки ошибок на слое сервиса реализован GlobalExceptionHandler, который выполняет обработку всех ошибок, выброшенных на уровнях сервиса, контроллера и JPA, возвращая пользователю ответ со временем ошибки, HTTP статусом и сообщения ошибки.

Из возможных выброшенных ошибок может быть:

  • PasswordIsNotUniqueException – заданный пароль для взаимодействующего не является уникальным;

  • OpenerNotFoundException – пользователь с указанным уникальным идентификатором не найден;

  • InvalidFirstUserException – попытка изменить первого пользователя. Этого пользователя нельзя менять так как за ним закреплены параметры непредвиденного взаимодействия с кодовым замком.

Документация для OpenAPIреализована в контроллерах и доступна по ссылке http://localhost:8080/swagger-ui/index.html при запуске серверного приложения

Для определения запуска разных конфигураций написаны тестовая и prodконфигурации –application-prod.yml и application-prod.yml соответственно.