Главная » Правописание слов » Как написать программу квадратного уравнения на питоне

Слово Как написать программу квадратного уравнения на питоне - однокоренные слова и морфемный разбор слова (приставка, корень, суффикс, окончание):


Морфемный разбор слова:

Однокоренные слова к слову:

Введение в Python

Поиск

Новое на сайте

Графический калькулятор квадратных уравнений на Python и Tkinter

Часть первая: функция решения квадратного уравнения.

Напомним, что квадратным является уравнение вида:

Есть несколько способов решить квадратное уравнение, мы выберем решение через дискриминант.

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

Если же дискриминант меньше нуля, то уравнение не имеет решений.

Превратим данные формулы в код:

Чтобы все работало не забудьте импортировать функцию sqrt из модуля math.

Теперь пора переходить к созданию графической оболочки для нашего приложения.

Часть вторая: создаем GUI для программы

Для простоты будем создавать GUI встроенными средствами Python, поэтому импортируем все из библиотеки Tkinter:

Далее создаем само окно и размещаем на нем необходимые виджеты:

Если вы в точности повторили указанный код, то после запуска скрипта у вас получится примерно следующее окно:

Отлично, программа работает. Осталось объяснить Python как связать эти две части.

Часть третья: объединяем все воедино

Функция вставки информации:

Функция inserter предельно проста: очищает поле для ввода и вставляет туда переданный ей аргумент value.

Напишем функцию обработки введенной информации. Назовем ее handler:

В зависимости от данных введенных в поля для ввода передает функции inserter либо результат решения уравнения, либо сообщение о неверно введенных данных.

Чтобы все работало, следует изменить строку создания виджета Button следующим образом:

Теперь можно спокойно пользоваться нашей программой:

Дискриминант больше нуля Дискриминант равен нулю
Дискриминант меньше нуля. Решений нет Введены не все аргументы

Часть четвертая: необязательная

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

Готово. Программа работает, Вы великолепны!

Исходный код калькулятора квадратных уравнений с GUI на GitHub

Источник

Квадратное уравнение

Программа, позволяющая находить корни квадратного уравнения, – это один из примеров простых программ, которые можно написать на Python 3. Она хорошо подойдет для начинающих изучать этот язык программирования.

Постановка задачи

Уравнение, которое будем решать, выглядит следующим образом: a·x²+b·x+c=0. Пользователю предлагается ввести значения a, b и с в терминале. После этого программа посчитает дискриминант. На его основе найдем решения уравнения – значения x, для которых будет выполняться равенство.

Вот пример работы программы, которая будет написана.

Программа

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

Запустим программу и введём нужные коэффициенты.

Все посчитано, найдены два корня, которые будут являться решением квадратного уравнения.

Дополнительно

Хотелось бы уделить внимание ещё одному моменту. Если дискриминант отрицательный, то действительных корней нет. Но будут комплексные корни. Если мы хотим их обрабатывать, то следует изменить конструкцию условных операторов следующим образом:

Тогда пример решения уравнения будет выглядеть следующим образом:

Как видим, получили два комплексных корня.

Этот простой код написанный на Python 3 можно для обучения программированию немного усложнить:

Источник

Введение в Python

Поиск

Новое на сайте

Графический калькулятор квадратных уравнений на Python и Tkinter

Часть первая: функция решения квадратного уравнения.

Напомним, что квадратным является уравнение вида:

Есть несколько способов решить квадратное уравнение, мы выберем решение через дискриминант.

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

Если же дискриминант меньше нуля, то уравнение не имеет решений.

Превратим данные формулы в код:

Чтобы все работало не забудьте импортировать функцию sqrt из модуля math.

Теперь пора переходить к созданию графической оболочки для нашего приложения.

Часть вторая: создаем GUI для программы

Для простоты будем создавать GUI встроенными средствами Python, поэтому импортируем все из библиотеки Tkinter:

Далее создаем само окно и размещаем на нем необходимые виджеты:

Если вы в точности повторили указанный код, то после запуска скрипта у вас получится примерно следующее окно:

Отлично, программа работает. Осталось объяснить Python как связать эти две части.

Часть третья: объединяем все воедино

Функция вставки информации:

Функция inserter предельно проста: очищает поле для ввода и вставляет туда переданный ей аргумент value.

Напишем функцию обработки введенной информации. Назовем ее handler:

В зависимости от данных введенных в поля для ввода передает функции inserter либо результат решения уравнения, либо сообщение о неверно введенных данных.

Чтобы все работало, следует изменить строку создания виджета Button следующим образом:

Теперь можно спокойно пользоваться нашей программой:

Дискриминант больше нуля Дискриминант равен нулю
Дискриминант меньше нуля. Решений нет Введены не все аргументы

Часть четвертая: необязательная

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

Готово. Программа работает, Вы великолепны!

Исходный код калькулятора квадратных уравнений с GUI на GitHub

Источник

Решать квадратичные уравнения с Python

Исследуйте, как использовать Python, чтобы решить квадратичные уравнения и отображать графики квадратичных функций.

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

Много лет назад я использовал калькуляторы Ti-84, чтобы научить математику. Одна отличная особенность этих калькуляторов в том, что вы можете на самом деле написать программы на них, используя язык, называемый Ti-Basic Отказ Некоторые из вас, которые знакомы с псевдокодом из различных учебных программ экзамена компьютерных наук в Великобритании могут заметить сходство между псевдокодом и Ti-Basic (и другими формами основных – одним из наиболее широко используемых языков, когда домашние компьютеры впервые стали «вещь “). Это история в другое время, хотя.

Использование Ti-Basic для решения квадратичных уравнений выглядела что-то подобное:

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

Python Program для решения квадратичных уравнений

Построение квадратичной функции с Python

Есть несколько потрясающих инструментов с открытым исходным кодом, доступными для работы с математикой в Python. Например, мы можем использовать Матплотлиб Пакет (может потребоваться установка), чтобы визуализировать квадратичную функцию и посмотреть, где он пересекает ось X (ее реальные решения). Код ниже дает способ сделать это. Как только ваш график будет произведен, вы можете увеличить масштаб, чтобы увидеть решения, если это необходимо.

Эта статья показала вам, как использовать Python, чтобы найти решения для квадратичных уравнений и структуре квадратичных функций. Я надеюсь, что вы нашли это интересно и полезно. Счастливые вычисления!

Источник

Решение квадратных уравнений на python

Хаха, моя первая программа тоже квадратные уравнения решала. Только она была на бейсике, а писал я ее на БК-0010Ш.

С каких пор если «дискриминант меньше нуля, корней нет»?

Если дискриминант меньше нуля, но ВЕЩЕСТВЕННЫХ корней нет. А так корни есть.

С такими вопросами лучше идти на stackoverflow.

Но зачем?
На этапе когда ты изучаешь программирование и учишься считать квадратные корне при помощи питона, таким париться не стоит

несколько значений из потока проще получить так:
x = input()

a,b,c = [int(c) for c in x.split()]

но это если заморочиться. Ветвление можно переписать как

(нет нужды проверять в третий раз)

В целом оптимизация таких мелочей не критична.

Разгадка

А надо ли учиться?

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

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

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

Ответ на пост «Как войти в IT после 30, мой путь от офисного планктона до Middle Java разработчика в Сбербанке»

Как войти в IT после 30, мой путь от офисного планктона до Middle Java разработчика в Сбербанке

поэтому три года назад, перегорев от творческой работы по раскладыванию пасьянса «косынка», я решил что-то в жизни поменять. Начал с изучения английского. Не стану превращать пост в подробное описание этого процесса, тем более есть уже замечательный пост от @L4rever, на эту тему. Добавлю лишь, что для пополнения словарного запаса мне очень зашёл сайт\приложение memrise который помог мне дотянуть свой словарик до 5000 слов примерно за год в бесплатном режиме. Хоть изучение английского и не имело решающего значения в моей дальнейшей судьбе, но весьма помогло.

Глава 1. Избавление от обязательств и накопление финансовой подушки

Как и у большинства людей старше 25 у меня была ипотека, благо не такая большая, всего на 1,5 миллиона (хоть какие-то преимущества жизни в пригороде провинциального города).

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

Интернет рассказал мне о фрилансеров, различных специальностей, всяких копирайтерах, smm менеджерах и так далее. С копирайтингом у меня не особо получилось, за 50 заказов и около 30 часов работы я получил 1500 рублей. Писать тексты было скучно, бесконечные поправки и конкуренция в виде армии школьников за заказ стоимостью 35 рублей, 84 копейки.

Поэтому было решено попробовать второй путь. И тут мне повезло уже на второй день поисков на зарубежной бирже я смог найти заказчика, которому нужно было вести группы в ВК и Facebook для нескольких брендов. Работы было не сильно много, всего на пару часов в день. Платили тоже не супер, 15 тысяч, однако это на 13 500 больше чем зарплата копирайтера, поэтому я приступил к работе.

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

1) Устроится туда самому.
2) Устроить туда жену, со знанием языка на уровне летспик фромахарт используя хитроумное устройство, в виде наушника и меня с микрофоном.

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

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

Глава 2. Куда пойти

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

UPD. Сейчас многие крупные компании также открыли свои школы, видел такие у Яндекса и ВТБ, поэтому выбор стал шире.

Здание школы в Казани

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

Так как школа бесплатная и фактически никаких ограничений по возрасту и образованию к поступающим не предъявляет (единственное это возраст 18+), желающих поступить приходит больше, чем существующее количество мест. Поэтому в качестве вступительных экзаменов там есть «Бассейны». Но перед тем как попасть на них нужно пройти две игры на сайте, на память и логику\алгоритмы, после чего записать видеоинтервью с коротким рассказом о себе и ждать приглашения на испытание бассейном.

Нет, плавательные шапочки можно отложить, это не совсем то

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

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

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

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

Для закрытия 9 уровня мне потребовался ровно год, я это сделал в мае 2021 и начал искать место стажировки, которая кстати в 99% случаев оплачиваемая.

Глава 4. Стажировка и работа

Офис какой-то ИТ-компании

Логично было бы подумать, что раз школа финансируется Сбером, то и работать ты обязан только там. Но это не так. Никаких финансовых или трудовых обязательств у учеников нет, в интернете ходит информация о штрафе в 50к за отчисление, но она устарела, этот штраф был лишь стимулирующей мерой не забрасывать школу, как оказалось она не работает и вызывает массу негатива, поэтому была заменена на дедлайны по набору уровней, при нарушении которых тебя просто отчисляют.

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

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

Мне опять же повезло, я прошел 4 собеседования в различные команды, по итогу получил 1 предложение на стажировку и 1 сразу в штат. Так не мудрствуя лукаво, в Июне я стал Junior Java разработчиком в Сбере в городе Иннополис (недалеко от Казани). Про город кстати отдельная тема, достойная целого поста, но можно просто почитать посты @veronichka.pb, где она подробно рассказывает о городе и процессе переезда в него.

Ну а спустя пол года, неделю назад, получил повышение (сейчас тру-погроммисты будут меня ругать, так как настоящий мидл это человек с опытом 2-3 года. И честно сказать будут правы, я считаю что получил повышение авансом, но в конце концов кто мы такие чтобы спорить с начальством)))

Касательно работы именно в Сбере, я очень доволен тем, что имею. Особенно сравнивая свой текущий опыт с отношением работодателей в не-айтишном прошлом, даже не беря в расчёт зарплату, а откинуть такой весомый аргумент довольно сложно =D. Коллектив поддерживает меня, несмотря на пробелы в моих знаниях и более низкую эффективность, отпроситься с работы для каких-то дел очень просто, а при необходимости легко можно работать удаленно. Ну и весьма неплохо иметь возможность переключится на другой проект, если текущий для тебя станет скучным, а проектов здесь огромное количество.

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

В конце хотел бы сказать, что ничего нереального во вхождение в ИТ-сферу нет, да вам будет легче, если вам 20 лет, нет детей и ипотеки, но рядом со мной прошёл путь человек с двумя детьми, ипотекой и 40 годами за плечами, который тоже сейчас работает разработчиком. И это не какая-то единичная ситуация, таких людей буквально сотни, даже на пикабу @vigerf, почитав мои комментарии решился начать свой путь, надеюсь он также поможет ответить на вопросы о школе в комментариях))

Не бойтесь начинать это в 30, 40, любое другое количество лет, намного страшнее как мне кажется, так и не попробовать.

Помогите!

Когда клиент попросил сделать креативную форму

Типичный ИИ-стартап

PS сам в таком работал

Естественный отбор

Бесплатно помогаю пикабушникам учить программирование, часть 27: «Мы составили план обучения по Swift»

Пикабушника @lycrois, которая любезно согласилась помогать мне в обучении программированию, составила план обучения по Swift.

Также мы завели группу в Telegram, где оперативно отвечаем на часто возникающие вопросы: https://t.me/+uKgZmAzvhpRjZjNi

Все это было и будет бесплатно. Добро пожаловать всем желающим =)

Английский для программирования не важен!©

Гарри Поттер и магия программирования

Как я делал систему оптического трекинга

Дело было в далеком 2015 году. В продаже только появились очки виртуальной реальности Oculus DK2, рынок VR игр быстро набирал популярность.

Возможности игрока в таких играх были невелики. Отслеживалось всего 6 степеней свободы движений головы — вращение (инерциалкой в очках) и перемещение в маленьком объеме в зоне видимости инфракрасной камеры, закрепленной на мониторе. Процесс игры представлял собой сидение на стуле с геймпадом в руках, вращение головой в разные стороны и борьбу с тошнотой.

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

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

1) Берем несколько игроков, надеваем на них VR очки, ноутбук и датчики на руки, ноги и туловище.

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

3) Создаем игру. Игра представляет собой многопользовательский квест, в котором несколько игроков надевают на себя оборудование и оказываются в виртуальном мире. В нем они видят себя, видят друг друга, могут ходить по локации, открывать двери и совместно решать игровые задачи.

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

Для реализации заявленного функционала, нужно было создать две основные технологии:

1) Костюм, состоящий из датчиков на руках, ногах и торсе, отслеживающий положения частей тела игрока

2) Система трекинга, отслеживающая игроков и интерактивные объекты в 3D пространстве.

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

Бюджета на все это, конечно, у нас не было, поэтому нужно было сделать все из подручных материалов. Для задачи отслеживания игроков в пространстве я решил использовать оптические камеры и светодиодные маркеры, закрепленные на VR очках. Опыта подобных разработок у меня не было, но я уже что-то слышал про OpenCV, Python, и подумал, что справлюсь.

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

Соответственно, камеры нужно было закрепить на потолке так, чтобы каждая точка пространства просматривалась минимум двумя камерами (лучше больше, чтобы избежать перекрытия обзора телами игроков). Для покрытия трекингом предполагаемого помещения площадью около 100 кв.м., требовалось около 60 камер. Я выбрал первые попавшиеся дешевые на тот момент usb вебки.

Эти вебки нужно к чему-то подключать. Эксперименты показали, что при использовании usb удлинителей (по крайней мере, дешевых), камеры начинали глючить. Поэтому решил разделить вебки на группы по 8 штук и втыкать их в системники, закрепленные на потолке. На моем домашнем компе как раз было 10 usb портов, так что пришло время начинать разработку тестового стенда.

Архитектуру я придумал следующую:

На каждые очки вешается акриловый матовый шарик от гирлянды с вклеенным внутрь RGB светодиодом. Одновременно в игре предполагалось несколько игроков, так что для идентификации решил разделять их по цвету – R, G, B, RG, RB, GB, RB. Вот так это выглядело:

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

Поиск шарика на кадре

Мне нужно было в каждом кадре, пришедшем с камеры, искать координаты центра шарика и его цвет для идентификации. Звучит несложно. Качаю OpenCV под Python, втыкаю камеру в usb, пишу скрипт. Для минимизации влияния лишних объектов на кадре, выставляю экспозицию и выдержку на камере в самый минимум, а яркость светодиода делаю высокой, чтобы получить яркие пятна на темном фоне. В первой версии алгоритм был следующий:

1) Переводим изображение в градации серого

2) Бинаризуем по порогу (если яркость пикселя больше порога, он становится белым, иначе – черным). При этом размытое пятно от шарика превращается в кластер белых пикселей на черном фоне

3) Находим контуры кластеров и их центры. Это и есть координаты шарика на кадре

4) Определяем усредненный цвет пикселей кластера (на исходном цветном изображении) в окрестности его центра для идентификации

Вроде, работает, но есть нюансы.

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

Во-вторых, разрешение камеры всего лишь 640*480, поэтому на некотором расстоянии (не очень большом) шарик виден как пара пикселей на кадре и алгоритм поиска контуров перестает нормально работать.

Пришлось придумывать новый алгоритм. В голову пришла следующая идея:

1) Переводим изображение в градации серого

2) Размываем картинку мощным Gaussian blur –ом так, чтобы изображения светодиодов превратились в размытые пятна с градиентом яркости от центра к периферии

3) Находим самые яркие пиксели на изображении, они должны соответствовать центрам пятен

4) Так же определяем средний цвет кластера в окрестности центра

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

Чтобы убедиться, что все это будет работать с 8-ю камерами на одном компе, нужно провести нагрузочный тест.

Подключаю 8 камер к своему десктопу, располагаю их так, чтобы каждая видела светящиеся точки и запускаю скрипт, где описанный алгоритм работает в 8-ми независимых процессах (спасибо питонской либе «multiprocessing») и обрабатывает все потоки сразу.

И… сразу натыкаюсь на фейл. Изображения с камер то появляются, то исчезают, framerate скачет от 0 до 100, кошмар. Расследование показало, что часть usb портов на моем компе подключены к одной шине через внутренний хаб, из-за чего скорость шины делится между несколькими портами и ее уже не хватает на битрейт камер. Втыкание камер в разные порты компа в разных комбинациях показало, что у меня всего 4 независимых usb шины. Пришлось найти материнку с 8-ю шинами, что было довольно непростым квестом.

Продолжаю нагрузочный тест. На этот раз все камеры подключились и выдают нормальные потоки, но сразу сталкиваюсь со следующей проблемой – низкий fps. Процессор загружен на 100% и успевает обрабатывать лишь 8-10 кадров в секунду с каждой из восьми вебок.

Похоже, нужно оптимизировать код. Узким местом оказалось Гауссово размытие (оно и не удивительно, ведь нужно на каждый пиксель кадра производить свертку с матрицей 9*9). Уменьшение ядра не спасало ситуацию. Пришлось искать другой метод нахождения центров пятен на кадрах.

Решение удалось найти внезапно во встроенной в OpenCV функции SimpleBlobDetector. Она делает прямо то, что мне нужно и очень быстро. Преимущество достигается благодаря последовательной бинаризации изображения с разными порогами и поиску контуров. Результат – максимальные 30 fps при загрузке процессора меньше 40%. Нагрузочный тест пройден!

Классификация по цвету

Следующая задача – классификация маркера по его цвету. Усредненное значение цвета по пикселям пятна дает RGB компоненты, которые очень нестабильны и сильно меняются в зависимости от расстояния до камеры и яркости светодиода. Но есть отличное решение: перевод из RGB пространства с HSV (hue, saturation, value). В таком представлении пиксель вместо «красный», «синий», «зеленый», раскладывается на компоненты «тон», «насыщенность», «яркость». В этом случае насыщенность и яркость можно просто исключить и классифицировать только по тону.

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

Я использовал pinhole модель камеры, в которой все лучи падают на матрицу через точку, находящуюся на фокусном расстоянии от матрицы.

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

Для отслеживания 3D координат маркера нужно получить минимум две скрещивающиеся прямые в пространстве от разных камер и найти точку их пересечения. Увидеть маркер двумя камерами не сложно, но для построения этих прямых нужно, чтобы система знала все о подключенных камерах: где они висят, под какими углами, фокусное расстояние каждого объектива. Проблема в том, что все это неизвестно. Для вычисления параметров требуется некая процедура калибровки.

В первом варианте решил сделать калибровку трекинга максимально примитивной.

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

2) С помощью лазерного нивелира и дальномера измеряю XYZ координаты всех камер в единой системе координат

3) Для вычисления ориентаций и фокусных расстояний камер, измеряю координаты специальных стикеров. Стикеры вешаю следующим образом:

В интерфейсе отображения картинки с камеры рисую две точки. Одну в центре кадра, другую в 200 пикселях справа от центра:

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

Измеряю XYZ координаты этих точек с помощью тех же нивелира и дальномера. Итого для блока из восьми камер нужно измерить координаты самих камер и еще по две точки на каждую. Т.е. 24 тройки координат. А таких блоков должно быть около десяти. Получается долгая муторная работа. Но ничего, позже сделаю калибровку автоматизированной.

Запускаю процесс расчета на основе измеренных данных.

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

1) Берем исходную матрицу с нулевыми поворотами и смещением.

2) Берем единичный вектор в локальной системе камеры, который смотрит из объектива вперед и преобразуем его в глобальные координаты по исходной матрице.

3) Берем другой вектор в глобальной системе, который из камеры смотрит на центральную точку на стене.

4) С помощью градиентного спуска поворачиваем исходную матрицу так, чтобы после преобразования эти векторы были сонаправлены. Таким образом, мы зафиксировали направление камеры. Осталось зафиксировать вращение вокруг этого направления. Для этого и измерялась вторая точка в 200 пикселях от центра кадра. Поворачиваем матрицу вокруг главной оси, пока два вектора не станут достаточно параллельны.

5) По расстоянию между этими двумя точками вычисляю фокусное расстояния в пикселях (учитывая, что расстояние между проекциями этих точек на кадре составляет 200 пикселей).

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

Для визуализации результатов калибровки я сделал 2D интерфейс с картой, на которой скрипт рисует метки камер и направления, в которых они видят маркеры. Треугольником обозначаются ориентации камер и углы обзора.

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

Похоже на правду, но точность явно могла быть выше. Первая причина несовершенства, которая пришла в голову – искажения в объективах камер. Значит, нужно эти искажения как-то компенсировать.

У идеальной камеры важный для меня параметр только один – фокусное расстояние. У реальной кривой камеры нужно учитывать еще дисторсии объектива и смещение центра матрицы.

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

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

А также вектор коэффициентов дисторсии, который позволяет компенсировать искажения объектива с помощью преобразований координат пикселей.

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

Провожу новый тест трекинга:

Уже гораздо лучше! Выглядит настолько хорошо, что даже вроде будет работать.

Вычисление координат маркера

И так, я получил кучу прямых, разбросанных по пространству, на пересечениях которых должны находиться маркеры. Только вот прямые в пространстве на самом деле не пересекаются, а скрещиваются, т.е. проходят на некотором расстоянии друг от друга. Моя задача – найти точку, максимально близкую к обеим прямым. Формально говоря, нужно найти середину отрезка, являющегося перпендикуляром к обеим прямым.

Длина отрезка AB тоже пригодится, т.к. она отражает «качество» полученного результата. Чем он короче, тем ближе друг к другу прямые, тем лучше результат.

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

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

Магнитное поле в нашем помещении было направлено ВЕРТИКАЛЬНО ВНИЗ! Видимо, это связано с железом в конструкции здания.

Но ведь в VR очках тоже используется магнитометр. Почему у них такого эффекта нет? Иду проверять. Оказалось, что в очках он тоже есть… Если сидеть неподвижно, можно заметить, как виртуальный мир медленно, но верно вращается вокруг тебя в рандомную сторону. За минут 10 он уезжает почти на 180 градусов. В нашей игре это неминуемо приведет к рассинхрону виртуальной и реальной реальностей и сломанным об стены очкам.

Похоже, что помимо координат очков, придется определять и их направление в горизонтальной плоскости. Решение напрашивается само – ставить на очки не один, а два одинаковых маркера. Оно позволит определять направление с точностью до разворота на 180 градусов, но с учетом наличия встроенных инерциальных датчиков, этого вполне достаточно.

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

Игроки надевали очки, костюмы и рюкзаки-компьютеры и заходили в игровую зону. Координаты трекинга отсылались им по wi-fi и применялись для позиционирования виртуального персонажа. Все работало достаточно неплохо, посетители довольны. Приятнее всего было наблюдать ужас и крики особо впечатлительных посетителей в моменты, когда на них из темноты нападали виртуальные призраки =)

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

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

1) вешаю камеры и на глаз направляю их в игровую область

2) запускаю режим записи на сервере, в котором все приходящие с камер 2D точки сохраняются в файл

3) хожу по темной игровой локации с маркером в руках

4) останавливаю запись и запускаю расчет калибровочных данных, при котором вычисляются расположения, ориентации и фокусные расстояния всех камер.

5) в результате предыдущего пункта получается единое пространство, наполненное камерами. Т.к. это пространство не привязано к реальным координатам, оно имеет случайное смещение и поворот, которое я вычитаю вручную.

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

Вот так выглядит напечатанная на принтере специальная палка-калибровалка.

Тестирование большого проекта

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

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

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

Алгоритмы трекинга пришлось заметно усложнить, но в итоге все заработало! Телепортации игроков друг в друга исчезли, нагрузка на процессор упала, засветки перестали мешать.

Проект был успешно запущен, первым делом я сделал новые платы костюмов с поддержкой активного трекинга, и мы произвели обновление оборудования.

Чем все закончилось?

За 3 года мы открыли множество развлекательных точек по всему миру, но коронавирус внес свои коррективы, что дало нам возможность сменить направление работы в более общественно-полезную сторону. Теперь мы довольно успешно занимаемся разработкой медицинских симуляторов в VR. Команда у нас все еще маленькая и мы активно стремимся расширять штат. Если среди читателей есть опытные разработчики под UE4, ищущие работу, пожалуйста, напишите мне.

Традиционный забавный момент в конце статьи:

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

Система в прямом смысле работала через задницу =)

Вот уже на протяжении нескольких лет Тимофей, преподаватель кафедры информатики МФТИ, выкладывает свои лекции по программированию на своём Youtube канале с открытым доступом.

Разработка системы заметок с нуля. Часть 2: REST API для RESTful API Service + JWT + Swagger

Продолжаем серию материалов про создание системы заметок. В этой части мы спроектируем и разработаем RESTful API Service на Go cо Swagger и авторизацией. Будет много кода, ещё больше рефакторинга и даже немного интеграционных тестов.

В первой части мы спроектировали систему и посмотрели, какие сервисы требуются для построения микросервисной архитектуры.

Подробности в видео и текстовой расшифровке под ним.

Начнём с макетов интерфейса. Нам нужно понять, какие ручки будут у нашего API и какой состав данных он должен отдавать. Макеты мы будем делать, чтобы понять, какие сущности, поля и эндпоинты нам нужны. Используем для этого онлайн-сервис NinjaMock. Он подходит, если макет надо сделать быстро и без лишних действий.

Страницу регистрации сделаем простую, с четырьмя полями: Name, Email, Password и Repeat Password. Лейблы делать не будем, обойдемся плейсходерами. Авторизацию сделаем по юзернейму и паролю.

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

Интерфейс, который будет у нашего веб-приложения:

— Слева — список категорий любой вложенности.

— Справа — список заметок в виде карточек, который делится на два списка: прикреплённые и обычные карточки.

— Каждая карточка состоит из заголовка, который урезается, если он очень длинный.

— Справа указано, сколько секунд/минут/часов/дней назад была создана заметка.

— Тело заголовка — отрендеренный Markdown.

— Панель инструментов. Через неё можно изменить цвет, прикрепить или удалить заметку.

Тут важно отметить, что файлы заметки мы не отображаем и не будем запрашивать у API для списка заметок.

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

Так будет выглядеть открытая заметка

В ходе прототипирования стало понятно, что в первой части мы забыли добавить еще один микросервис — TagsService. Он будет управлять тегами.

Для страниц авторизации и регистрации нам нужны эндпоинты аутентификации и регистрации соответственно. В качестве аутентификации и сессий пользователя мы будем использовать JWT. Что это такое и как работает, разберём чуть позднее. Пока просто запомните эти 3 буквы.

Для страницы списка заметок нам нужны эндпоинты /api/categories для получения древовидного списка категорий и /api/notes?category_id=? для получения списка заметок текущей категории. Перемещаясь по другим категориям, мы будем отдельно запрашивать заметки для выбранной категории, а на фронтенде сделаем кэш на клиенте. В ходе работы с заметками нам нужно уметь создавать новую категорию. Это будет метод POST на URL /api/categories. Также мы будем создавать новый тег при помощи метода POST на URL /api/tags.

Чтобы обновить заметку, используем метод PATCH на URL /api/notes/:uuid с измененными полями. Делаем PATCH, а не PUT, потому что PUT требует отправки всех полей сущности по спецификации HTTP, а PATCH как раз нужен для частичного обновления. Для отображения заметки нам ещё нужен эндпоинт /api/notes/:uuid/files с методами POST и GET. Также нам нужно скачивать файл, поэтому сделаем метод GET на URL /api/files/:uuid.

Структура репозитория системы

Ещё немного общей информации. Структура репозитория всей системы будет выглядеть следующим образом:

В директории app будет исходный код сервиса (если он будет). На уровне с app будут другие директории других продуктов, которые используются с этим сервисом, например, MongoDB или ELK. Продукты, которые будут использоваться на уровне всей системы, например, Consul, будут в отдельных директориях на уровне с сервисами.

Писать будем на Go

— Идём на официальный сайт.

— Копируем ссылку до архива, скачиваем, проверяем хеш-сумму.

— Распаковываем и добавляем в переменную PATH путь до бинарников Go

— Пишем небольшой тест проверки работоспособности, собираем бинарник и запускаем.

Установка завершена, всё работает

Теперь создаём проект. Структура стандартная:

— cmd — точка входа в приложение,

— internal — внутренняя бизнес-логика приложения,

— pkg — для кода, который можно переиспользовать из проекта в проект.

Я очень люблю логировать ход работы приложения, поэтому перенесу свою обёртку над логером logrus из другого проекта. Основная функция здесь Init, которая создает логер, папку logs и в ней файл all.log со всеми логами. Кроме файла логи будут выводиться в STDOUT. Также в пакете реализована поддержка логирования в разные файлы с разным уровнем логирования, но в текущем проекте мы это использовать не будем.

APIService будет работать на сокете. Создаём роутер, затем файл с сокетом и начинаем его слушать. Также мы хотим перехватывать от системы сигналы завершения работы. Например, если кто-то пошлёт приложению сигнал SIGHUP, приложение должно корректно завершиться, закрыв все текущие соединения и сессии. Хотел перехватывать все сигналы, но линтер предупреждает, что os.Kill и SIGSTOP перехватить не получится, поэтому их удаляем из этого списка.

Теперь давайте добавим сразу стандартный handler для метрик. Я его копирую в директорию pkg, далее добавляю в роутер. Все последующие роутеры будем добавлять так же.

Далее создаём точку входа в приложение. В директории cmd создаём директорию main, а в ней — файл app.go. В нём мы создаём функцию main, в которой инициализируем и создаём логер. Роутер создаём через ключевое слово defer, чтобы метод Init у роутера вызвался только тогда, когда завершится функция main. Таким образом можно выполнять очистку ресурсов, закрытие контекстов и отложенный запуск методов. Запускаем, проверяем логи и сокет, всё работает.

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

Создадим для приложения контекст. Сделаем его синглтоном при помощи механизма sync.Once. Пока что в нём будет только конфиг. Контекст в виде синглтона создаю исключительно в учебных целях, впоследствии он будет выпилен. В большинстве случаев синглтоны — необходимое зло, в нашем проекте они не нужны. Далее создаём конфиг. Это будет YAML-файл, который мы будем парсить в структуру.

В роутере мы вытаскиваем из контекста конфиг и на основании listen.type либо создаем сокет, либо вешаем приложение на порт. Код graceful shutdown выделяем в отдельный пакет и передаём на вход список сигналов и список интерфейсов io.Close, которые надо закрывать. Запускаем приложение и проверяем наш эндпоинт heartbeat. Всё работает. Давайте и конфиг сделаем синглтоном через механизм sync.Once, чтобы потом безболезненно удалить контекст, который создавался в учебных целях.

Теперь переходим к API. Создаём эндпоинты, полученные при анализе прототипов интерфейса. Тут важно отметить, что у нас все данные привязаны к пользователю. На первый взгляд, все ручки должны начинаться с пользователя и его идентификатора /api/users/:uuid. Но у нас будет авторизация, иначе любой пользователь сможет программно запросить заметки любого другого пользователя. Авторизацию можно сделать следующим образом: Basic Auth, Digest Auth, JSON Web Token, сессии и OAuth2. У всех способов есть свои плюсы и минусы. Для этого проекта мы возьмём JSON Web Token.

Работа с JSON Web Token

JSON Web Token (JWT) — это JSON-объект, который определён в открытом стандарте RFC 7519. Он считается одним из безопасных способов передачи информации между двумя участниками. Для его создания необходимо определить заголовок (header) с общей информацией по токену, полезные данные (payload), такие как id пользователя, его роль и т.д., а также подписи (signature).

JWT использует преимущества подхода цифровой подписи JWS (Signature) и кодирования JWE (Encrypting). Подпись не даёт кому-то подделать токен без информации о секретном ключе, а кодирование защищает от прочтения данных третьими лицами. Давайте разберёмся, как они могут нам помочь для аутентификации и авторизации пользователя.

Аутентификация — процедура проверки подлинности. Мы проверяем, есть ли пользователь с полученной связкой логин-пароль в нашей системе.

Авторизация — предоставление пользователю прав на выполнение определённых действий, а также процесс проверки (подтверждения) данных прав при попытке выполнения этих действий.

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

Важно понимать, что использование JWT не скрывает и не маскирует данные автоматически. Причина использования JWT — проверка, что отправленные данные были действительно отправлены авторизованным источником. Данные внутри JWT закодированы и подписаны, но не зашифрованы. Цель кодирования данных — преобразование структуры. Подписанные данные позволяют получателю данных проверить аутентификацию источника данных.

Реализация JWT в нашем APIService:

— Создаём директории middleware и jwt, а также файл jwt.go.

— Описываем кастомные UserClaims и сам middlware.

— Получаем заголовок Authorization, оттуда берём токен.

— Берём секрет из конфига.

— Создаём верификатор HMAC.

— Парсим и проверяем токен.

— Анмаршалим полученные данные в модель UserClaims.

— Проверяем, что токен валидный на текущий момент.

При любой ошибке отдаём ответ с кодом 401 Unauthorized. Если ошибок не было, в контекст сохраняем ID пользователя в параметр user_id, чтобы во всех хендлерах его можно было получить. Теперь надо этот токен сгенерировать. Это будет делать хендлер авторизации с методом POST и эндпоинтом /api/auth. Он получает входные данные в виде полей username и password, которые мы описываем отдельной структурой user. Здесь также будет взаимодействие с UserService, нам надо там искать пользователя по полученным данным. Если такой пользователь есть, то создаём для него UserClaims, в которых указываем все нужные для нас данные. Определяем время жизни токена при помощи переменной ExpiresAt — берём текущее время и добавляем 15 секунд. Билдим токен и отдаём в виде JSON в параметре token. Клиента к UserService у нас пока нет, поэтому делаем заглушку.

Добавим в хендлер с heartbeat еще один тестовый хендлер, чтобы проверить работу аутентификации. Пишем небольшой тест. Для этого используем инструмент sketch, встроенный в IDE. Делаем POST-запрос на /api/auth, получаем токен и подставляем его в следующий запрос. Получаем ответ от эндпоинта /api/heartbeat, по истечении 5 секунд мы начнём получать ошибку с кодом 401 Unauthorized.

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

Чтобы этого избежать, прикрутим refresh-токен. Он позволит пересоздать основной токен доступа без запроса данных авторизации пользователя. Такие токены живут очень долго или вообще бессрочно. После того как только старый JWT истекает мы больше не можем обратиться к API. Тогда отправляем refresh-токен. Нам приходит новая пара токена доступа и refresh-токена.

Хранить refresh-токены на сервере мы будем в кэше. В качестве реализации возьмём FreeCache. Я использую свою обёртку над кэшем из другого проекта, которая позволяет заменить реализацию FreeCache на любую другую, так как отдает интерфейс Repository с методами, которые никак не связаны с библиотекой.

Пока рассуждал про кэш, решил зарефакторить существующий код, чтобы было удобней прокидывать объекты без dependency injection и синглтонов. Обернул хендлеры и роутер в структуры. В хендлерах сделал интерфейс с методом Register, которые регистрируют его в роутере. Все объекты теперь инициализируются в main, весь роутер переехал в мейн. Старт приложения выделили в отдельную функцию также в main-файле. Теперь, если хендлеру нужен какой-то объект, я его просто буду добавлять в конструктор структуры хендлера, а инициализировать в main. Плюс появилась возможность прокидывать всем хендлерам свой логер. Это будет удобно когда надо будет добавлять поле trace_id от Zipkin в строчку лога.

Вернемся к refresh_token. Теперь при создании токена доступа создадим refresh_token и отдадим его вместе с основным. Сделаем обработку метода PUT для эндпоинта /api/auth, а в теле запроса будем ожидать параметр refresh_token, чтобы сгенерировать новую пару токена доступа и refresh-токена. Refresh-токен мы кладём в кэш в качестве ключа. Значением будет user_id, чтобы по нему можно было запросить данные пользователя у UserService и сгенерировать новый токен доступа. Refresh-токен одноразовый, поэтому сразу после получения токена из кэша удаляем его.

Для описания нашего API будем использовать спецификацию OpenAPI 3.0 и Swagger — YAML-файл, который описывает все схемы данных и все эндпоинты. По нему очень легко ориентироваться, у него приятный интерфейс. Но описывать вручную всё очень муторно, поэтому лучше генерировать его кодом.

— Создаём эндпоинты /api/auth с методами POST и PUT для получения токена по юзернейму и паролю и по Refresh-токену соответственно.

— Добавляем схемы объектов Token и User.

— Создаём эндпоинты /api/users с методом POST для регистрации нового пользователя. Для него создаём схему CreateUser.

Понимаем, что забыли сделать хендлер для регистрации пользователя. Создаём метод Signup у хенлера Auth и структуру newUser со всеми полями для регистрации. Генерацию JWT выделяем в отдельный метод, чтобы можно было его вызывать как в Auth, так и в Signup-хендлерах. У нас всё еще нет UserService, поэтому проставляем TODO. Нам надо будет провалидировать полученные данные от пользователя и потом отправить их в UserService, чтобы он уже создал пользователя и ответил нам об успехе. Далее вызываем функцию создания пары токена доступа и refresh-токена и отдаём с кодом 201.

У нас есть подсказка в виде Swagger-файла. На его основе создаём все нужные хендлеры. Там, где вызов микросервисов, будем проставлять комментарий с TODO.

Создаём хендлер для категорий, определяем URL в константах. Далее создаём структуры. Опираемся на Swagger-файл, который создали ранее. Далее создаём сам хендлер и реализуем метод Register, который регистрирует его в роутере. Затем создаём методы с логикой работы и сразу пишем тест API на этот метод. Проверяем, находим ошибки в сваггере. Таким образом мы создаём все методы по работе с категориями: получение и создание.

Далее создаём таким же образом хендлер для заметок. Понимаем, что забыли методы частичного обновления и удаления как для заметок, так и для категорий. Дописываем их в Swagger и реализуем методы в коде. Также обязательно тестируем Swagger в онлайн-редакторе.

Здесь надо обратить внимание на то, что методы создания сущности возвращают код ответа 201 и заголовок Location, в котором находится URL для получения сущности. Оттуда можно вытащить идентификатор созданной сущности.

В третьей части мы познакомимся с графовой базой данных Neo4j, а также будем работать над микросервисами CategoryService и APIService.

Источник

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

Какие вы еще знаете однокоренные слова к слову Как написать программу квадратного уравнения на питоне:



Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *