Наша команда разработки начала работать с телевизорами два года назад. В тот момент казалось, что эта тема не может быть кому-то интересна, так как больше похожа на пляски с бубном, чем на нормальную работу разработчика. Но недавно в телеграм-чате спросили, как работают наши экраны статуса заказа, и сказали, что об этом будет интересно почитать на Хабре. Так появилась эта статья и развёрнутый ответ.

Про дизайн экранов со статусом заказа и почему пришлось его менять

Задолго до того, как мы начали этим заниматься, в пиццериях уже висели телевизоры, сообщающие о готовности заказов:

Это были обычные домашние телевизоры LG модели 32LF580U со SmartTV, в которых открывался встроенный браузер, внутри которого уже открывался экран статуса заказа.

Интерфейс сделали давно, ещё в 2013 году. В то время мало кто из других компаний использовал подобные решения, и общепринятого пользовательского опыта не было. К 2019 году такие экраны появились в крупных сетях, таких как KFC и McDonald’s, и они, по сути, продиктовали рынку визуальный паттерн:

Разделение экрана на готовящиеся заказы и готовые стало в какой-то момент рыночным стандартом. А табло, которое просто оповещало, что заказ готов, осталось более типичным для бургерных и нам подходить перестало. Финальной точкой послужил запуск нашего старого табло в Нигерии, где люди трактовали появление своего номера заказа на экране как сигнал о том, что заказ только что взяли в работу.

Стало ясно, что старый дизайн никуда не годится. Наш дизайнер Мурад Гаммадов разработал новый, который мы и принялись воплощать в жизнь.

Пишем новый сервис

В 2019 году в Dodo было больше сервисов, чем команд разработки, поэтому мы постоянно ротировались между собой. В какой-то момент в рамках борьбы с техдолгом у нас сложилось правило, что если ты заходишь в новый компонент с фичами, то его нужно отрефакторить. Мы посмотрели на старый дизайн и поняли, что отрефакторить не получится. Вот почему:

  • фронт был написан на JavaScript и Angular, а у нас в техрадаре в качестве принятой технологии стоял TypeScript и React;

  • сам код фронта был размазан по HTML и JS-скриптам и ангуляровскими модулями;

  • фронтенд опрашивал бэк простым поллингом, и получалось, что каждый новый телевизор в пиццерии увеличивал нагрузку на нашу систему;

  • код бэка находился в монолите и просто проксировал запросы в другую часть системы (трекер), создавая тем самым высокую нагрузку на него. По сути до 80% всей нагрузки.

Поэтому решили переписать сервис в сторонке, вытащить его в Kubernetes и сделать так, чтобы он меньше нагружал трекер.

MVP: полёт нормальный

В минимально жизнеспособной версии мы решили не сильно увлекаться оптимизацией бэка, а для начала просто сделать так, чтобы новый дизайн заработал как можно быстрее в первой пиццерии. 10 января 2020 года мы вернулись после каникул и начали работу над новым сервисом, а 22 января экран с новым дизайном заработал в московской пиццерии на Таганке:

В тот момент это была одна из самых новых пиццерий в Москве, там висели профессиональные телевизионные панели Samsung Tizen и мы не поймали каких-либо критичных замечаний. Всё работало так, как было запланировано. Мы начали раскатывать новое решение на остальные пиццерии. 

Старые телевизоры против

Когда более старые пиццерии, работающие с LG 2012 года выпуска, стали включать наше решение, полетели первые проблемы Одна из них была в том, что на этих телевизорах наш экран вообще не запускался, не показывая при этом никакой ошибки. Просто белый экран. Вкручивание логов во фронтенд не помогло разобраться в причинах, так как веб-приложение просто не запускалось на этих телевизорах.

Где-то в кладовой мы смогли откопать такую же модель и принесли ее в офис:

На фото видно одну из многочисленных ошибок, но в тот момент экран просто был белым. 

Подключили клавиатуру и стали перебирать комбинации клавиш, которые теоретически могли бы открыть консоль разработчика. Начиная с Ctrl+Shift+I и заканчивая Shift + Ctrl + J, но это не помогло. В конечном итоге, случайно нажав на сочетание клавиш Alt + Shift + F10, мы открыли меню, через которое можно открыть консоль разработчика:

Отсюда же узнали, что не хватает полифилов на Promise. В дальнейшем таких проблем встречалось много, и каждый раз они исправлялись последовательным подбором полифилов. На словах это звучит просто, но на деле требовало какого-то невероятного количества времени на поиск информации о том, что же значит тот или иной стектрейс. В результате вышел вот такой список, выстраданный десятками экспериментов:

"@babel/polyfill": "^7.7.0",
"abortcontroller-polyfill": "^1.4.0",
"isomorphic-fetch": "^2.2.1",
"promise-polyfill": "^8.1.3",

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

Уже не помню точно всех проблем с Samsung, помню, что на нём не открывался полноэкранный режим по клику мышки. Здесь уже помог сервис Browser Stack, в котором вы по сути арендуете виртуальную машину, на которой стоит нужная вам старинная версия браузера и операционки. Сервис не бесплатный — бесплатные в нём только первые 30 минут в сутки. Остальное за деньги. В итоге проблему с полноэкранным режимом удалось исправить, достав из прошлой версии телевизоров скрипт для развёртывания на полный экран.

declare global {
  interface Document {
     exitFullscreen: () => Promise<void>
     mozCancelFullScreen: () => void
     webkitExitFullscreen: () => void
     fullscreenElement: () => void
     mozFullScreenElement: any
     msFullscreenElement: any
     webkitFullscreenElement: any
     msExitFullscreen: () => void
  }

  interface HTMLElement {
     mozRequestFullScreen: () => any
     msRequestFullscreen: () => any
     webkitRequestFullscreen: (ALLOW_KEYBOARD_INPUT: number) => any
  }
}

const fullScreen = async (): Promise<void> => {
  if (document.documentElement.requestFullscreen) {
     await document.documentElement.requestFullscreen()
  } else if (document.documentElement.msRequestFullscreen) {
     document.documentElement.msRequestFullscreen()
  } else if (document.documentElement.mozRequestFullScreen) {
     document.documentElement.mozRequestFullScreen()
  } else if (document.documentElement.webkitRequestFullscreen) {
     document.documentElement.webkitRequestFullscreen((Element as any).ALLOW_KEYBOARD_INPUT)
  }
}

const fullScreenExit = async (): Promise<void> => {
  if (document.exitFullscreen) {
     await document.exitFullscreen()
  } else if (document.msExitFullscreen) {
     document.msExitFullscreen()
  } else if (document.mozCancelFullScreen) {
     document.mozCancelFullScreen()
  } else if (document.webkitExitFullscreen) {
     document.webkitExitFullscreen()
  }
}

export const toggleFullsreenMode = async (): Promise<void> => {
  if (
     !document.fullscreenElement &&
     !document.mozFullScreenElement &&
     !document.webkitFullscreenElement &&
     !document.msFullscreenElement
  ) {
     await fullScreen()
  } else {
     await fullScreenExit()
  }
}

Где-то через месяц работы мы победили примерно 90% такого рода проблем и начали оптимизацию бэка, поскольку к нашему решению успело подключиться более 400 пиццерий. И они начали довольно ощутимо нагружать трекер.

Оптимизируем работу бэка

Как выглядела схема работы старых экранов статуса заказа:

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

Эта схема была бы идеальной, однако:

  • cтарые телевизоры не умеют в веб-сокеты;

  • если использовать SignalR, он вроде как может перейти в режим лонг-поллинг, которым автоматически заменяются сокеты, если телевизор старый. Но он тоже с ходу не заведётся: надо долго и аккуратно впиливать.

В конце концов всё равно надо будет прийти к лонг-поллингу, т.к. мы уже сейчас сильно нагружаемся из-за постоянных запросов, но взяться за это пока не успели. Поэтому текущая схема работы выглядит так:

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

Ещё одним плюсом стал перенос сервиса в Kubernetes. Это дало возможность не только добавлять и удалять мощности в случае необходимости, но и снизить количество работающих инстансов сервиса, так как до этого у нас на каждую страну приходилось по одному инстансу, а стран было 14. После переезда сервис поместился на 3 пода.

Сложности с Redis

Есть одна вещь, которая нас ужасно расстроила — это Redis. В компании ходила дурная слава про этот инструмент. Но мы подумали, что это только потому, что все остальные просто не умеют его готовить.

В начале всё шло неплохо, пока дело не дошло до прода. Почему-то начались рандомные таймауты клиента от сервиса. Мы пошли разбираться внутрь исходников библиотеки StackExchange.Redis, чтобы найти проблему, но не обнаружили ничего, что могло бы вызывать такое странное поведение. Пробовали отладку вот по этой статье. Увидев эти «разборки», наш архитектор Глеб Лесников порекомендовал скопировать решение у соседнего сервиса — HostedService, который переподключает клиента в случае отключения. Но это не помогло.

Проблема оказалась в том, что мы хранили слишком большой объём данных по ключу, больше 4 КБ. Снизили объём данных — это исправило проблему переподключений.

Но при нагрузочном тестировании выявилась другая проблема — почему-то начало «подскакивать» количество тредов. Эту проблему мы так и не побороли, просто увеличили количество подов в Kubernetes с 3 до 5 и оставили так.

Где-то пару месяцев назад мы обновили библиотеку StackExchange.Redis и снизили количество подов. Проблема подскакивающих тредов ушла. Что это было, так и осталось загадкой.

Какой вывод можно сделать из этой истории? Думаю, такой, что всё же Redis нужно изучать отдельно и не использовать на авось, ожидая, что он будет работать из коробки. Нужно знать его настройки и попробовать под нагрузкой, прежде чем выкатывать на продакшен. 

Делаем фронт вертикальным

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

Сначала показалось здесь, что просто получится применить rotate. На новых браузерах это решение сработало нормально. Однако на «эталонном» телевизоре вёрстка съезжала в правую часть экрана.

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

В Dodo принята практика «трэвелерства» — это когда более опытный разработчик с навыками, например, во фронтенде, ходит по командам и передаёт знания о новой области. И к нам пришла Наташа Гулько, наша фронтенд-разработчица (правда, сейчас она работает в EPAM).

Для начала она предложила всё же отказаться от опасной авантюры с отдельным приложением и попробовать поиграться с размерами экрана. К примеру, при повороте экрана мы изменяем смещение по left и top, а так же меняем местами ширину и высоту в глобальных стилях:

const getSize = (orientation: Orientation) => {
  const screenSize = getScreenSize()
  const magicCoefficient = screenSize.width - screenSize.height

  return orientation === Orientation.portrait
     ? css`
           height: ${screenSize.width}px;
           width: ${screenSize.height}px;
           transform: rotate(-90deg);
           left: ${magicCoefficient / 2}px;
           top: ${-magicCoefficient / 2}px;
       `
     : css`
           height: 100%;
           width: 100%;
           left: 0;
           top: 0;
       `
}

export const ContentStyled = styled.div<{ orientation: Orientation }>`
  ${props => getSize(props.orientation)}
  position: absolute;
  overflow-x: hidden;
  overflow-y: hidden;
`

Это привело к тому, что блоки спозиционировались верно, однако теперь у нас возникла проблема с масштабированием шрифтов. Поэтому мы для каждой ориентации отдельно задавали размеры:

{
  portrait: {
     header: {
        fontSize: calcSizeToRem(88),
        lineHeight: calcSizeToRem(100),
        marginBottom: calcSizeToRem(20),
     },
     number: {
        fontSize: calcSizeToRem(72),
        lineHeight: calcSizeToRem(84),
        borderRadius: calcSizeToRem(12),
        height: calcSizeToRem(108),
        width: calcSizeToRem(192),
        marginRight: calcSizeToRem(36),
        paddingTop: calcSizeToRem(4),
     },
     name: {
        fontSize: calcSizeToRem(72),
        lineHeight: calcSizeToRem(102),
        marginTop: calcSizeToRem(3),
        maxWidth: calcSizeToRem(541),
        paddingTop: calcSizeToRem(4),
     },
     line: {
        marginRight: calcSizeToRem(36),
        marginBottom: calcSizeToRem(20),
     },
     pageIndicator: {
        width: calcSizeToRem(90),
     },
  } as LineSizes,

  landscape: {
     header: {
        fontSize: calcSizeToRem(100),
        lineHeight: calcSizeToRem(100),
        marginBottom: calcSizeToRem(40),
     },
     number: {
        fontSize: calcSizeToRem(84),
        lineHeight: calcSizeToRem(84),
        borderRadius: calcSizeToRem(12),
        height: calcSizeToRem(126),
        width: calcSizeToRem(229),
        marginRight: calcSizeToRem(42),
        paddingTop: calcSizeToRem(4),
     },
     name: {
        fontSize: calcSizeToRem(84),
        lineHeight: calcSizeToRem(102),
        marginTop: calcSizeToRem(8),
        maxWidth: calcSizeToRem(470),
        paddingTop: calcSizeToRem(4),
     },
     line: {
        marginRight: calcSizeToRem(42),
        marginBottom: calcSizeToRem(30),
     },
     pageIndicator: {
        width: calcSizeToRem(108),
     },
  } as LineSizes,

  pageIndicator: {
     height: calcSizeToRem(8),
     marginRight: calcSizeToRem(13),
     borderRadius: calcSizeToRem(12),
  },
  container: {
     paddingTop: calcSizeToRem(68),
     paddingBottom: calcSizeToRem(43),
     paddingHorizontal: calcSizeToRem(92),
  },
  column: {
     paddingRight: calcSizeToRem(80),
  },
  pizzeriaLabel: {
     fontSize: calcSizeToRem(105),
  },
  countryLabel: {
     fontSize: calcSizeToRem(78),
  },
  counterNumber: {
     fontSize: calcSizeToRem(250),
     height: calcSizeToRem(273),
     width: calcSizeToRem(250),
     marginRight: calcSizeToRem(30),
     borderRadius: calcSizeToRem(20),
  },
  numbersRow: {
     marginBottom: calcSizeToRem(40),
  },
  screenRotation: {
     container: {
        width: calcSizeToRem(180),
        height: calcSizeToRem(180),
     },
     image: {
        margin: calcSizeToRem(30),
        width: calcSizeToRem(120),
        height: calcSizeToRem(120),
     },
  },
}

При этом при повороте мы ещё и пересчитываем размеры с учётом того ,что ширина изменилась, потому что при повороте размер шрифта и блоков всё ещё остаётся чуть больше, чем нужно.

function calcSizeToRem(px: number): string {
  return `${px / pxToNumber(defaultFontSize)}rem`
}

export const defaultFontSize = '16px'
const defaultHeight = '1080px'

export function pxToNumber(size: string): number {
  const px = -2
  return Number(size.slice(0, px))
}

export function calcFontSize(height: string | number): string {
  const heightNumber = typeof height === 'number' ? height : pxToNumber(height)
  const defaultFontSizeNumber = pxToNumber(defaultFontSize)
  const defaultHeightNumber = pxToNumber(defaultHeight)

  return (heightNumber / defaultHeightNumber) * 
defaultFontSizeNumber + 'px'
}

Выглядит это довольно сложно, но позволяет не делать две отдельных вёрстки с начала до конца, а задать отдельно размеры под каждую.

К тому времени, как мы справились с вертикальной вёрсткой, прошло уже два месяца, как пиццерии начали пользоваться новым решением и накопили ряд замечаний. К примеру, оказалось, что если количество готовых и готовящихся заказов больше пяти, тогда не все заказы помещаются на экране. Ничего страшного – делаем слайдеры. Показываем первые пять заказов, потом через десять секунд следующие пять заказов и так по кругу. Но нужно учесть, что при появлении новых заказов нужно проиграть звук «До-до». Что если в этот момент на экране показана первая страница? Надо дождаться последней и только тогда проиграть звук. А что если накопилось несколько заказов? Сколько раз проиграть звук?

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

В итоге конечный дизайн стал выглядеть вот так:

Цвета стали умеренней, а все заказы, наконец, поместились на экраны. 

Факапы

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

В общем, нужно было перевести всех с HTTP на HTTPS. Для этого подняли два ингресcа, обновили фронт так, чтобы он ходил на HTTPS и вырубили ингресс, который слушал клиентов на HTTP. И вдруг получили вот такой график запросов:

Он означал, что практически все пиццерии получили примерно вот такую картинку на своих экранах (только с другой ошибкой):

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

Дело оказалось в работе механизма обновления. Он был довольно простой: мы ходили на эндпоинт с версией и получали GUID. Если он менялся по сравнению с предыдущим, значит, нужно сделать обновление страницы на текущей локации. Поскольку локации с HTTP не стало, то и телевизоры после обновления текущей страницы просто получили 404.

Мы сделали более умное обновление, которое проверяет доступность сайта перед тем, как обновить страницу.

export const useReloadOnVersionChange = () => {
  useEffect(() => {
     const fetchData = async (): Promise<void> => {
        const fetchedVersion = await fetchAppVersion()

        if (fetchedVersion) {
           const isUpdateRequired = Boolean(pageProps.bundleVersion && fetchedVersion !== pageProps.bundleVersion)
           if (isUpdateRequired) {
              const isHealthy = await fetchHealthCheck()
              if (isHealthy) {
                 refreshPage()
              }
           }
        } else {
           console.warn('fetchedVersion is not defined')
        }
     }

     const intervalIndex = setIntervalAsync(fetchData, versionUpdatePeriod)

     return () => {
        clearIntervalAsync(intervalIndex)
     }
  }, [])
}

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

Протухшие сертификаты

Последняя вещь, которая была болезненной в работе с телевизорами — это инвалидированные корневые сертификаты. Вы могли буквально недавно видеть вот такие апокалиптичные новости, о том что скоро часть устройств окирпичится. Конечно, это чушь, для владельцев устройств скорее всего не так страшно, а вот для владельцев сайтов — настоящая головная боль. Летом 2020-го произошло переименование компании, которая предоставляла такие корневые сертификаты, из Comodo в Sectigo, и часть пиццерий потеряла возможность показывать своё меню и статусы заказов. 

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

В результате мы сделали вывод, что нам нужно как-то ограничить количество моделей техники и вообще желательно прописывать такие моменты в договорах с вендорами. Сейчас в новые пиццерии и кофейни ставят телевизоры Samsung Tizen, где все эти детали подробно прописаны.

Сложности, достойные упоминания

  • Набор адреса в строке браузера. Вам нужно с помощью стрелочек выбрать нужную букву, а потом нажать ОК. Повторить это действие столько раз, сколько букв в строке. Иногда страница не загружается, строка очищается и всё приходится набирать заново

  • Телевизоры выцветают. Оказывается, что обычный домашний телевизор не рассчитан на круглосуточную работу в жаркой пиццерии, поэтому со временем — сюрприз — он выцветает! Угадайте, кому эта проблема прилетает в виде бага? Правильно, нам! 

  • Не всем странам нравится счётчик пиццерий. Когда на экране нет заказов, мы показываем счётчик открытых пиццерий. В Латвии он почему-то не понравился гостям. Пришлось убирать.

  • Не воспроизводились звуки. На LG 32LF580U в какой-то момент перестал воспроизводиться звук «До-до», и никаких ошибок в консоли не было. При этом на остальных телевизорах звук работал прекрасно. Мы неделю сидели, перебирая варианты от неправильного формата файла до поломки механизма воспроизведения. Оказалось, что blob, на котором лежал звуковой файл, был подписан корневым сертификатом, которого нет в прошивке этой модели, и браузер просто молча игнорировал этот файл.

Выводы

Как вывод будет такой совет: если работаете с телевизорами, старайтесь ограничивать набор моделей на самом первом этапе. Если такой возможности нет, то лучше попробовать что-то вроде Raspberry Pi в качестве устройства. Но знайте, что это тоже может давать нагрузку на саппорт, так как у этой платформы может быть своя специфика настройки, работы и возможных проблем. К примеру перегрев, о котором вы вообще вначале можете не подумать, но в пиццерии бывает жарко, так как весь теплый воздух поднимается вверх. В общем, тоже могут быть проблемы и это не серебряная пуля.

Спасибо, что дочитали статью! Наверняка многие вещи не удалось раскрыть во всей красе, так что я готов ответить в комментариях на вопросы.

Комментарии (13)


  1. kolabaister
    10.11.2021 17:06

    А вы не рассматривали вариант изначально не умных телевизоров в hdmi стиков с каким нибудь линуксом или даже андроидом? С точки зрения масштабирования намного проще, да и по цене наверное так же или меньше.


    1. Indermove Автор
      10.11.2021 17:22

      Я там в выводах коротко написал про вариант к примеру с Raspberry Pi???? Думаю, что с остальными стиками там тоже могут быть проблемы. Но я так скажу, что на малых масштабах, думаю, в целом Raspberry Pi тоже неплохое решение, но вот в нашем случае все же нет. Нужно чтобы телек не только поддерживаемым достаточно долгое время но и по экрану не выходил из строя.


      1. venanen
        11.11.2021 02:30

        Сомневаюсь, что удастся перегреть малину простым показом веб-странички, которая, скажем прямо, совершенно не выглядит как ресурсоемкая.
        Кроме того - есть возможность удаленно к ним подключаться и смотреть логи и ошибки. Вообще, как по мне, единственный минус устройств а-ля малинка в том, что их кто-то должен настроить (подключить к сети и запустить браузер) и повесить (кабельными стяжками). А плюсы - единая платформа, куча инструкций, вариативность, и главное - если работает у вас - работает и в пиццерии.
        В любом случае, нужно было бы сначала проверить все варианты, а потом выбрать лучший.
        А так получается, что у вас даже не было понимания на чем это будет запускаться (как я понял) и не было тестовых семплов тоже, а телевизоры 2013 года, да еще и все разные. Сейчас выяснится, что где-то кинескопные Рубины стоят - и все, габелла


        1. Indermove Автор
          11.11.2021 11:43
          +1

          Перегреть браузером не получится, но вот горячим воздухом от печи, вполне. Но это не самый важный аргумент, да. Смотрите, тут еще момент, что нужно убедить франчази купить дополнительное оборудование и научить это оборудование настраивать. Это не так просто, так как человек уже вложил в пиццерию 12-14 миллионов. А когда таких партнеров много, это становится еще более тяжелым делом.

          Самый сложный фактор – это легаси. В 2012, когда эти экраны только только появлялись, Raspberry Pi только вышла в релиз и не особо могла похвастаться тем уровнем поддержки, производительности, количества доступных стабильных систем, которые есть сейчас. И вот на дворе 2019 год и нужно убедить франчази, что то, на что 7 предыдущих лет хватало телевизора, теперь нужно еще докупать какое-то устройство. Причем для каждого телевизора отдельно, а их в пиццерии 2-4, а иногда и больше и висят они, порой, на разных этажах. Конечно, можно так сделать, но тогда получится время раскатки решения на все 500+ пиццерий увеличится до нескольких лет, и все это время придется поддерживать оба решения.

          Но честно скажу, мы тоже на самом деле в какой-то момент очень хотели взять малинку ???? Возможно когд-анибудь к ней придем все же. Но на тот момент у нас можно сказать и выбора не было.


  1. w_the_h
    10.11.2021 17:16
    +2

    Ох, я почти совсем этим столкнулся. Но пока не делал с Redis. Спасибо за статью. Интересно узнать, как звук у вас работает? Сотруднику в момент открытия экрана, надо совершать какое-то действие на включение звука? Или через blob он сразу воспроизводится?


    1. Indermove Автор
      10.11.2021 17:24

      Да, нам нужно по экрану кликнуть, это ограничение браузера, иначе он ошибку выдает. Но у нас для открытия на полный экран, как раз нужен клик по экрану, так что убиваем двух зайцев одним кликом ????


      1. w_the_h
        10.11.2021 17:32
        +1

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


  1. SergeyMax
    10.11.2021 21:12

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


    1. Indermove Автор
      10.11.2021 23:54

      Если широкими мазками, то да, все так, только постгресс не потребовался.???? Ну и чтобы отобразить в браузере десять чисел, потребовалось пол часа. А чтобы отобразить уже десятки чисел на сотнях экранов и в нескольких версиях браузеров, да потребовалось месяц отлаживться и придумывать такие решения. ???? Если у Вас есть идеи, как оптимизировать этот процесс, я бы почитал!


      1. venanen
        11.11.2021 02:33

        Ну смотря о каких сотнях мы говорим. Если эти сотни - 200-300 клиентов, то их выдержит даже простейшая вдска долларов за 50 в месяц. Но сдается мне, что вы количество пиццерий на порядок-два уменьшили)


        1. Indermove Автор
          11.11.2021 10:02

          Сейчас у нас 753 пиццерий в каждой по 2-4 таких экрана, в некоторых пиццериях больше. Где-то 2200 клиентов получается. У нас три пода не очень больших держат эту нагрузку. На них ограничение в 300 MHz стоит и по памяти они каждая ограничены в 512 Mb по памяти. Но они каждая используют сейчас около половины этих ресурсов. В общем в простейшую вдску может и влезла бы ????


  1. yarnstart
    11.11.2021 11:23
    +2

    А десяток чисел нельзя было вывести на экран без react + typescript + babel + styled components ? vanilla.js(callback) было не достаточно для такого проекта ?


    1. Indermove Автор
      11.11.2021 11:26

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