Невозможно полноценно тестировать и отлаживать мобильные приложения без тестовых устройств. Таких устройств должно быть много, потому что один и тот же код на разных моделях ведёт себя по-разному. Но как организовать учёт устройств? Как сделать так, чтобы разработчики и тестировщики быстро и без волокиты получали конкретный смартфон в необходимой конфигурации? Меня зовут Алексей Лавренюк. Вы можете знать меня как одного из авторов Яндекс.Танка и докладчика на тему нагрузочного тестирования. Потом я измерял энергопотребление мобильных телефонов. Теперь я делаю Яндекс.Ровер в команде беспилотных автомобилей (и иногда подрабатываю Дедом Морозом). А между телефонами и Ровером был Гиперкуб. Пару лет назад в наш отдел нагрузочного тестирования зашёл руководитель мобильной разработки и пожаловался на ситуацию с тестовыми устройствами. Телефоны кочевали из рук в руки, выбрать и быстро найти телефон было проблемой. У нас уже был опыт работы с мобильными устройствами: мы строили цифровой амперметр, чтобы измерять их энергопотребление. Поэтому мы решили помочь коллегам и быстро сделать классную штуку: казалось, работы всего на три месяца. Смеюсь над собой, наивным, из 2020 года и рассказываю, что нас ждало на самом деле. Мы были одними из немногих в Яндексе, кто собирал железные устройства. Кроме того, мы разбирались в мобильных. Для меня идея работы сервиса лежала на поверхности: надо подключить телефоны к хабу и отслеживать по USB их наличие и перемещение по офису. Конструкция поместится в серверный шкаф. Вот как я представлял себе процесс: Таких шкафов может быть много, но информация обо всех мобильных устройствах в них лежит в одном месте. Все кубы (шкафы) объединены и логически связаны, а вместе они образуют большой Гиперкуб (отсюда и пошло внутреннее название сервиса). Идея всем понравилась, мы начали работу. Мы взяли в хелпдеске RFID-считыватель и мини-компьютер Intel NUC. Купили ардуинку, электрозамок, шкафчик для сетевого оборудования (он был меньше серверного шкафа, но с такими же «рельсами» для крепления оборудования). Конечно, нам не дали ТЗ и списка покупок. Мы не знали точно, какая модель шкафа подойдёт, сколько нужно болтов и метров провода. Требования к шкафу менялись на ходу в соответствии с пожеланиями команд. Мы договорились, например, что в одном шкафу будем хранить 40 мобильных телефонов, а шкаф обязательно поставим на тумбочку, потому что он маленький и наклоняться к нему неудобно. Мы долго думали, как сделать полки удобными. В Яндексе были стойки для демонстрации телефонов или тестирования интерфейсов. Думали переиспользовать стойки, но они не очень подходили нам: телефоны там ставят экраном к пользователю, как на витрине — нам это незачем. Мы хотели оптимизировать место в шкафу, поэтому решили сами сделать полки. Появилась идея сложить телефоны внутри плотно, как книги. Для этого мы искали разделители в магазинах канцтоваров, пробовали использовать держатели CD-дисков, магазинные полки — всё мимо. В порядке эксперимента приклеили термоклеем к полке пластиковые уголки из порезанного на куски разделителя от кабельного короба. «Книжная раскладка» получилась очень удачной и понравилась нам. Внизу шкафа было мало места для четырёх 10-портовых хабов и Intel NUC — нам далеко не с первой попытки удалось впихнуть их туда. Дверь шкафа не предназначалась для крепления замка — мы использовали много термоклея, деревянные бруски и ножовку. Напомню: проект делала группа нагрузочного тестирования, ни у кого из нас ещё не было опыта работы с болгаркой на кофе-пойнте. С этим разобрались быстро: сделали страничку, которая показывала местонахождение телефонов, и разместили её прямо в нашем сервисе нагрузочного тестирования. Просто потому, что это позволяло быстро и практически без усилий попробовать, что получилось. Мы собрали первый Гиперкуб за три недели. Выглядел он так: Сначала решили протестировать прототип у себя, в московском офисе Яндекса (заказчик находился в Екатеринбурге). Он уехал на обкатку к команде мобильного Яндекс.Браузера — с ней мы уже были знакомы по проекту Yandex Volta (более свежая версия доклада тут). Эти ребята хорошо разбираются в энергопотреблении. Оказалось, термоклей плохо держит пластиковые уголки на металлических полках. Мы вернулись к идее подставок для телефонов. Я долго думал о том, как должна выглядеть идеальная полка в шкафу. Сейчас кажется, что нарисовать её можно было за 15 минут. В итоге получилось не совсем так, как на первом эскизе, но вот что я нарисовал тогда: Мы взяли покупные полки для серверных шкафов, а сверху поставили фрезерованные разделители из листового пластика, которые изготовили под заказ. Эта конструкция сохранилась до сих пор. Хабы для первых шкафов были с выключателем питания, кнопками без фиксации, выключенные по умолчанию. Поэтому после каждой перезагрузки приходилось руками включать все четыре хаба. Мы подключили SpeechKit Яндекса, чтобы куб произносил, если хабы вылетают: «Мне нехорошо, позови, пожалуйста, ответственного». Когда приходит ответственный — голос подсказывает проверить хабы. А ещё голосовой помощник стал называть сотрудников по имени и озвучивать, кто сколько телефонов взял. С замком было несколько итераций. Когда я искал замки для прототипа в самом начале, я нашёл единственный устроивший меня вариант — «Шериф»: маленький, сильный и очень остроумно сделанный (аналогов я до сих пор не видел). Закрывался отлично, но, чтобы открыть шкаф, требовалось слегка нажать на дверь. Не все пользователи это понимали, поэтому мы решили сменить замок.  Взяли «Шерифа» с пружинкой, которая отталкивала дверь. Шкаф открывался отлично — но стал плохо закрываться и оставался открытым (а сам себя считал закрытым). Это происходило потому, что датчик закрытия был установлен отдельно от замка, дверь имела люфт и датчик иногда срабатывал до закрытия двери. Настроить его было нереально.  Мы ещё раз сменили замок на очередного «Шерифа» уже не такой остроумной конструкции, но зато прямо в него был встроен датчик закрытия. На прототипе замок работал, но после запуска в эксплуатацию в первом же шкафу он стал заедать. Пришлось танцевать с бубном: разбирать, смазывать, допиливать напильником. С горем пополам он завёлся. После первых тестов стало понятно: шкаф нравится разработчикам и тестировщикам, они активно им пользуются. Это удобно: можно следить за устройствами и искать нужное в веб-сервисе, а не спрашивать у коллег: «У кого сейчас этот телефон?». Мы выявили самые важные проблемы: неудобные USB-хабы, которые нужно включать руками, длинные USB-провода плохого качества (разъёмы ломались, а провода путались), плохой замок и ненадёжные разделители полок, которые всё время отваливались. Работа продолжалась. Менеджер из отдела управления проектами — он же помог нам организовать аутсорс для полок — активно занимался внутренним пиаром в Яндексе: ходил в разные команды и рассказывал о нашем замечательном сервисе. Мы встречались с представителями других отделов и неожиданно для себя стали получать внутренние заказы на несколько десятков шкафов. Вырисовывалась мрачная картина: нагрузочные тестировщики неделями всей командой вручную собирают шкафы из того, что под рукой.  Мы поняли, что нам нужна помощь со сборкой. Перед этим следовало оптимизировать стоимость одного экземпляра шкафа и его конструкцию для мелкосерийного производства. На полтора летних месяца нам удалось взять стажёра, который умел паять, сверлить и немного кодить. С его помощью мы переделали старый шкаф на Raspberry Pi (чтобы избавиться от дорогостоящего и ненужного Intel NUC) и собрали ещё по одному шкафу для установки в хелпдесках в Москве и в Питере (там сидят умные ребята, которые помогут нашим пользователям справиться с прототипом и дадут полезный фидбек). Чтобы оптимизировать стоимость, мы заменили самые неоправданно дорогие компоненты в прототипе: мини-компьютер Intel NUC (25 тыс. рублей) и RFID-читалку (5 тыс.). Альтернативную читалку нашли на АлиЭкспрессе за 150 рублей, а с мини-компьютера переехали на Raspberry Pi за 3,5 тыс. рублей. На новой платформе мы столкнулись с несколькими неприятностями. Сначала выяснилось, что реле для замков не переключаются, потому что RPi выдаёт 3,3 В, а механические реле рассчитаны на 5 В. Пришлось перейти на твердотельные. Потом обнаружили, что на плате нет часов, а забирать время по NTP не получится: без правильного времени нельзя аутентифицироваться в офисной сети (нашли специальную плату-расширение с часами для RPi). Самая фееричная проблема была с айфонами. Как только в RPi втыкали шестой по счёту айфон, она становилась недоступна по сети. С этим нам помог разобраться проходивший мимо руководитель проектов (а в прошлом суровый админ). Оказалось, что сетевая карта на RPi разведена через USB, а айфоны провоцировали один глючный сервис досить USB-шину. Эта проблема впервые появилась в минском офисе, ребята смогли оперативно её воспроизвести и дать обратную связь, которая нам очень помогла. Решили проблему, когда снесли графический интерфейс (если я правильно помню), потому что сервис был в него проинтегрирован. О размере шкафа шли споры. Кто-то считал, что нужно помещать в шкаф в два раза больше телефонов, кто-то — что большие шкафы неудобно использовать в офисе. А потом мы обнаружили, что уже скупили все маленькие шкафы из московских магазинов, и следующая поставка будет только через три месяца. Споры завершились сами собой. Все следующие шкафы решили делать большими. Забегая вперёд, скажу: это оказалось верным решением. На базе большого шкафа стало проще размещать электронику, в него помещалось уже не 40, а 80 телефонов. В нём между рельсами были стандартные серверные 19 дюймов. В маленьком шкафу расстояния между рельсами не хватало. Мы своими силами потыкали палочкой в один из внутренних фреймворков для веб-разработки, поняли, что он хорош, и перетащили табличку телефонов из Django в React. Но было ясно, что просто табличка никого не устроит. Отдел геосервисов подумал и запилил нам задачу на несколько экранов. Пришлось подключать команду веб-разработки. Нам выделили дизайнера продукта, который полностью продумал взаимодействие пользователей с сервисом. Он сделал удобные наклейки на шкафы, разместил шкафы на карте офиса, изучил чат поддержки, чтобы лучше понять, чего хотят пользователи. Короче говоря, взял проект под своё крыло с точки зрения пользовательского опыта. Веб-разработчики взялись за фронтенд и быстро реализовали дизайн-макеты. У нас появилось что-то похожее на Яндекс.Маркет в уменьшенном масштабе: приходишь, выбираешь телефон, идёшь его забирать в шкафу. Мечта просто. Где-то здесь мы поняли, что проект придётся раскатывать на весь Яндекс. Мы согласовали траты, чтобы поправить ошибки и серьёзно улучшить качество сервиса: появился общий бюджет на покупку шкафов, запчастей и инструментов. С помощью Паши Мельникова, руководителя группы железных RnD-проектов (на тот момент, а теперь он занимается Яндекс.Станцией), удалось договориться с внешней компанией о производстве серийных шкафов с учётом наших требований. Купили другую версию хабов, без кнопок включения. Теперь не нужно было лезть внутрь шкафа и включать хабы после перезагрузки. Мы собрали один прототип вручную — он работал отлично. Но когда мы отправили его в Екатеринбург, нам сразу же стали поступать жалобы, что шкаф не видит некоторые телефоны. Разобрались в этом только на месте. Оказалось, что у новых хабов другая топология (даже у одной и той же модели хаба в разных сериях топология может различаться). И старый, и новый 10-портовый хаб внутри состоит из трёх USB-чипов по четыре порта. Но в старом хабе с кнопкой питания два чипа воткнуты в один, а в новом они соединены «гирляндой». Количество портов одинаковое, но глубина вложенности разная. Старые телефоны не определялись в портах с большой глубиной вложенности. Хорошо, что они были старыми и не очень нужными. Мы решили проблему больной головы путём отрубания головы: просто взяли для шкафа более новые модели телефонов, ну и ждали реализации собственных хабов. Правда, в софте пришлось заложить возможность задавать разные конфигурации топологии USB для разных шкафов. Вместе с Пашей мы начали разработку нашего собственного USB-хаба на стороне. Он требовался, чтобы решить проблему покупных хабов, а ещё — чтобы подсвечивать устройства, за которыми пришёл пользователь, и ускорить зарядку современных телефонов: для этого мы взяли мощные блоки питания и поддержали последние стандарты. В хабах была заложена возможность подключать внешние датчики температуры — так мы сможем контролировать её на каждой полке. Размеры выбрали такие, чтобы две платы хаба, установленные в один корпус, в точности встали в 19-дюймовую серверную стойку. Хабы получили низкий профиль, чтобы сэкономить место между полок — в общем, они получились клёвые и современные. Паша ещё расскажет подробнее об этом проекте на Хабре. Сборка из комплектующих на базе покупных шкафов тоже отправилась на аутсорс. По нашим расчётам, команда сборки должна была справиться за пару вечеров, но из-за обилия ручного труда это растянулось на пару недель. Провода, которые подключаются нестандартным образом по схемам, нарисованным ручкой на листочке, ручная пайка, отсутствие готовых посадочных мест для компонентов и самих готовых компонентов… Шкафы допиливали в буквальном смысле слова: сверлили отверстия для замков и фигурно выпиливали болгаркой под другие компоненты. Мы уже мечтали отдать шкафы в продакшен. Решили, что для ускорения нужно упростить производство и увеличить долю аутсорса. Спроектировали периферийную плату и обновили компоненты. Теперь она подключалась к Raspberry Pi одним шлейфом, а все остальные компоненты — стандартными разъёмами. Позже оказалось: при разводке я не учёл, что подключать микросхему RS-485 нужно ко вполне определённым ножкам RPi. Пришлось обновлять версию платы. Когда рисовал компонент для развязки питания RS-485, я не обратил внимания на то, что в даташите на микросхему дан вид снизу, а не сверху, и при первом включении она сделала «Бах!» с характерным запахом. Пришлось для всей первой серии плат перепаять микросхему на обратную сторону платы. Покупные шкафы перестали нас устраивать. Они странно выглядели, потому что предназначались для серверной, а не офиса, а ещё их требовалось дорабатывать руками. На это уходило много времени, и результат получался кустарный. Решили делать собственные шкафы на заводе в Арзамасе. Мы попросили завод собрать модифицированный шкаф на базе их стандартного серверного шкафа: поменять размер, установить замок, вывести наружу нужные разъёмы, сделать отверстия для крепления элементов. Нарисовали эскизы полок, сделали корпус для блока электроники (RPi и её друзья), корпус для хаба. Когда дело дошло до замка, инженер-проектировщик с завода слегка модифицировал покупной вариант, и тот стал работать лучше. Баг-репорт с пулл-реквестом на доработку железа отправили авторам «Шерифа». Очень удобно, что вся электроника управления шкафом теперь сосредоточилась в одном корпусе, который крепится на четыре болта. Его можно собирать отдельно от шкафа, его легко извлечь и заменить целиком при поломке. В тестовых шкафах провода путались, разъёмы вылетали из хаба и ломались, а порой сотрудники забирали с собой провода. Мы решили жёстко прикрепить провода к полкам, при этом оставив возможность менять провода. Я нарисовал эскиз — и завод реализовал нам его в железе. Мы, конечно, несколько раз ездили в Арзамас: договариваться, налаживать производство, принимать результат. В результате мы получили шкафы практически полностью готовыми, оставалось ввести их в эксплуатацию. Но это уже отдельная история. Кстати, если случится побывать в Арзамасе, зайдите в бургерную в центре: она клёвая. Сейчас наши кубы активно используются в компании. Мы даже придумали для них названия: Стэнли Кубик, Кубик Магги, Кубик Арни (Арнольда Шварценеггера, шесть штук) и т. п. В ближайшее время коллеги поделятся с вами историями о дальнейшей судьбе сервиса. Например, о том, как разрабатывался Яндекс.Хаб. Или о том, как некоторые порты хабов стали «неметь». На «онемевшем» порту не видно отключений USB-устройств, даже в сообщениях ядра ничего нет. Но всё нормализуется, если вытащить из порта телефон и воткнуть его обратно. А пока ребята пишут для вас статьи — попробуйте угадать сами, почему такое может быть. Технический менеджер продукта