Всем доброго времени суток.
Если Вы читали статью о цели практики, то знаете, что должно получиться в итоге. Спойлер: мы хотим собрать робота-исследователя, который будет снимать то, что происходит перед ним и управляться удаленно. Программная часть данного проекта уже почти готова, а значит настало время показать интерфейс управления роботом и рассказать как он будет работать.
Начнем!
В сердце веб приложения находится js фреймворк Vue. Для передачи данных между роботом и браузером оператора будет использоваться socket.io.
Контейнер app содержит 2 компонента:
<video-stream /> отвечает за, собственно, стрим видео с робота
<control-keyboard /> отвечает за панель управления роботом.
Эти компоненты располагаются друг за другом в одну колонку.
%lang(css)% .app { position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; flex-direction: column; }
Тут описаны css стили для класса .app, который и является родителем для вышеописанных компонентов.
Теперь по порядку о компонентах:
<video-stream />
Никакой сложной логики нет, компонент просто пытается грузить картинку с url стрима. Как это работает можно прочитать тут. В случае, если загрузить по таймауту не удалось, показывается ошибка:
Если нам удалось подключиться, показывается видеопоток:
Это все, что делает этот компонент.
<control-keyboard />
Этот компонент рендерит нам клавиатурку (компонент <keys-figure />), на которой находятся 4 кнопки. Также он привязывает слушатели для событий нажатия/отжатия клавиш клавиатуры.
%lang(js)% { registerListeners() { document.addEventListener('keydown', this.keyDownHandler); document.addEventListener('keyup', this.keyUpHandler); }, unRegisterListeners() { document.removeEventListener('keydown', this.keyDownHandler); document.addEventListener('keyup', this.keyUpHandler); }, keyUpHandler({ key }) { if (this.nowKey) { let found = keyCalls.find(x => x.key === key); found.onPull && found.onPull(this.socket); this.nowKey = null; } }, keyDownHandler({ key }) { let found = keyCalls.find(x => x.key === key); if (!found || this.nowKey === found.id) return; found.onPush && found.onPush(this.socket); this.nowKey = found.id; } }
Еще этот компонент инициирует открытие сокет-соединения с роботом:
%lang(js)% { mounted() { this.registerListeners(); this.socket = socketConnect(); }, destroyed() { this.unRegisterListeners(); this.socket.close(); } }
Если соединение не будет установлено, мы увидим ошибку:
Метод socketConnect содержит всего одну строку:
%lang(js)% import io from 'socket.io-client'; function socketConnect() { return io('http://192.168.0.108:8765'); } export { socketConnect };
Вам наверное интересно что за keyCalls я использовал. Это просто массив вот таких объектов:
%lang(js)% { key: 'ArrowUp', onPush: (socket) => { go(socket, 'forward'); }, onPull: (socket) => { stop(socket); }, id: 'up' }
key - это название клавиши, на которую нажали или которую отпустили. Два метода говорят нам что делать в соответствующем случае и поле id для того, чтобы можно было гарантировать уникальность объекта (на самом деле еще для кое чего).
Мы хотим, чтобы при нажатии кнопки и удержании onPush сработал только один раз, также мы хотим знать на какую кнопку мы сейчас нажали. Для этого существует переменная nowKey, куда и записывается id объекта. Потом мы передает проп color-key в компонент <keys-figure />, чтобы подсветить нажатую клавишу.
Примерно так это работает:
При нажатии и отпускании кнопок, вызываются методы onPush, onPull, которые в свою очередь вызывают один из этих методов:
%lang(js)% function go(socket, direction) { socket.emit('go', direction); } function stop(socket) { socket.emit('stop', ''); } function turn(socket, direction) { socket.emit('turn', direction); } export { go, stop, turn };
Данные отправляются роботу и он начинает или останавливает свое движение.
Работает как нажатие клавиш на клавиатуре, так и нажатие на нарисованные кнопки мышкой.
Это все, что я хотел рассказать про веб-приложение для управления нашим роботом.
Спасибо за внимание.
ps. Приложение пока в разработке и, скорее всего, будет обрастать новыми фичами, из-за чего могут быть несоответствия между написанным здесь и кодом на гитхабе. Хотя, надеюсь, логика в целом не изменится.