Всем доброго времени суток.
Если Вы читали статью о цели практики, то знаете, что должно получиться в итоге. Спойлер: мы хотим собрать робота-исследователя, который будет снимать то, что происходит перед ним и управляться удаленно. Программная часть данного проекта уже почти готова, а значит настало время показать интерфейс управления роботом и рассказать как он будет работать.
Начнем!
В сердце веб приложения находится js фреймворк Vue. Для передачи данных между роботом и браузером оператора будет использоваться socket.io.
Контейнер app содержит 2 компонента:
<video-stream /> отвечает за, собственно, стрим видео с робота
<control-keyboard /> отвечает за панель управления роботом.
Эти компоненты располагаются друг за другом в одну колонку.
.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 кнопки. Также он привязывает слушатели для событий нажатия/отжатия клавиш клавиатуры.
{
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;
}
}
Еще этот компонент инициирует открытие сокет-соединения с роботом:
{
mounted() {
this.registerListeners();
this.socket = socketConnect();
},
destroyed() {
this.unRegisterListeners();
this.socket.close();
}
}
Если соединение не будет установлено, мы увидим ошибку:
Метод socketConnect содержит всего одну строку:
import io from 'socket.io-client';
function socketConnect() {
return io('http://192.168.0.108:8765');
}
export { socketConnect };
Вам наверное интересно что за keyCalls я использовал. Это просто массив вот таких объектов:
{
key: 'ArrowUp',
onPush: (socket) => {
go(socket, 'forward');
},
onPull: (socket) => {
stop(socket);
},
id: 'up'
}
key - это название клавиши, на которую нажали или которую отпустили. Два метода говорят нам что делать в соответствующем случае и поле id для того, чтобы можно было гарантировать уникальность объекта (на самом деле еще для кое чего).
Мы хотим, чтобы при нажатии кнопки и удержании onPush сработал только один раз, также мы хотим знать на какую кнопку мы сейчас нажали. Для этого существует переменная nowKey, куда и записывается id объекта. Потом мы передает проп color-key в компонент <keys-figure />, чтобы подсветить нажатую клавишу.
Примерно так это работает:
При нажатии и отпускании кнопок, вызываются методы onPush, onPull, которые в свою очередь вызывают один из этих методов:
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. Приложение пока в разработке и, скорее всего, будет обрастать новыми фичами, из-за чего могут быть несоответствия между написанным здесь и кодом на гитхабе. Хотя, надеюсь, логика в целом не изменится.