Сертификация ISO27001

Однажды мой хороший друг сказал мне: «вся индустрия аудита основана на том, что люди друг другу врут». Это как нельзя лучше отражало истинную причину почему банкам и другим финансовым институтам нужно получать заключение внешних аудиторов каждый год – для того, чтобы другие институты верили их финансовой отчетности. 

Похожая ситуация в ИТ. Любая компания может сказать: «мы защищаем свои системы и следим за безопасностью передачи данных. Но так ли это? И насколько хорошо защищают?  Cертификация ISO 27001 отвечает на эти вопросы за вас, и позволяет сэкономить время на доказательствах. Под катом пример подготовки к сертификации ISO 27001 одной маленькой, европейской ИТ компании. 

Предыстория

ISO 27001 — это международный стандарт, в котором собраны требования для создания и развития системы менеджмента информационной безопасности. Сертификация ISO 27001 особенно актуальна для крупных компаний, но в последнее время его стали требовать от маленьких компаний на этапе обсуждения контракта.

Раньше было так: Заказчик предоставляет альтернативу: либо у подрядчика есть сертификат ISO 27001, либо если компания не прошла сертификацию, можно продемонстрировать в соответствии с официальными договорными обязательствами, что у компании есть «план безопасности».

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

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

  • возможность сохранить за компанией будущие контракты и упростить подачу на новые тендеры; 

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

  • Европа погрузилась в самоизоляцию — основное место работы сотрудника стал его дом, а не офис. В этой связи необходимо было заново оценить риски в ИТ процессах. Например стало гораздо менее важно обеспечивать надежное шифрование wi-fi в офисе – им попросту никто не пользовался целый год. 
  • Главные инструменты работы и места хранения данных – сместились в облачное пространство. По сути главный офис «испарился» в облако, локация компании потеряла смысл, фокус лег на суть сервисов которые компания использует и продает. 

Коротко о стандарте

ISO 27001 состоит из двух частей – Body (основная часть стандарта, своего рода стратегия) и Annex A (114 потенциально применимых контролей). 

Body of the Standard: 

  • p.4 Context of the organization
  • p.5 Leadership
  • p.6 Planning
  • p.7 Support
  • p.8 Operation
  • p.9 Performance evaluation
  • p.10 Improvement

Annex A

  • Information security policies (2 controls)
  • Organization of Information security (7 controls)
  • Human resource security (6 controls)
  • Asset management (10 controls)
  • Access control (14 controls)
  • Cryptography (2 controls)
  • Physical and environmental security (15 controls)
  • Operations security (14 controls)
  • System acquisition, development, and maintenance (13 controls)
  • Supplier relationships (5 controls)
  • Information security incident management (7 controls)
  • Information security aspects of business continuity management (4 controls)
  • Compliance (8 controls)

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

О компании, которая получает сертификацию:

ИТ консалтинг, всего 25 человек сотрудников, из которых двое это топ менеджмент и двое это администрация. Основные данные хранятся на внешних облачных серверах. В процессе 2020 года администрация (бухгалтерия, учет времени сотрудников) также переехали с внутренних серверов на внешние сервисы. 

Из поставщиков – внешние услуги техподдержки, а также все «физические» поставщики, охрана офиса, уничтожение старого оборудования, документов. 

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

С чего начать и чем закончить

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

Зато именно в маленьких компаниях нагляднее всего видны результаты подготовки. 

Несколько практических советов на этапе старта проекта: 

  1. Нарисуйте схему информационных систем компании. 

    Стандарт диктует: «определите границы и сферы информационных систем». 

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

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

  2. Создайте общую папку для документации по ISO

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

  3. Пишите политики исходя с запасом гибкости

    Все политики нужно писать, предварительно прочитав стандарт ISO27002, он дает направление по тому, что именно требуется и будет проверяться аудитором. Любой документ можно написать по-разному. На примере политики по паролям – можно либо прописать письменно что пароль должен быть минимум 8 символов, либо написать что пароль должен быть «сложным» и отражать требованиям информационных систем. 

  4. Написали? Переписывайте.

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

План vs реальность

План подготовки к ISO 27001 был следующий: 

  1. Купить официальный текст (да, до сих пор не понимаю почему официальные стандарты надо покупать) и изучить стандарт.

    → Планировали неделю, заняло 2 недели

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

    → Планировали месяц, заняло 2 месяца 

  3. Внутренний сотрудник составляет схему информационных систем компании.

    → Первая схема была сделана за неделю, но далее она менялась целый год

  4. Совместно изучается Annex A – список потенциально применимых контролей, из которых отбираются те, которые актуальны для нашей фирмы. 

    → Планировали за неделю, заняло 2 месяца

  5. Под выбранные контроли из Annex A находится или пишется необходимая документация, и дополнительные политики.

    → Планировали месяц, ушло почти полгода

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

    → Планировали за месяц, но валидация как раз прошла относительно быстро – 3 недели

  7. После одобрения документации, для всех сотрудников компании проводится информационная. 

    → Нужен всего один день, но две недели ушло на подготовку

  8. После этого приглашается сертифицирующий орган для проведения аудита

▍Из положительного: 

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

▍Из отрицательного: 

  • Иногда вопросов от консультанта было СЛИШКОМ много и мы ходили по кругу, от одного к другому, возвращаясь и углубляясь в детали. Например консультант предложил создать схему под каждый вариант работы сотрудника – из дома, из офиса, от клиента, и.т.д., тогда как подобная схема уже была отражена в главной схеме инф.системы. Кончилось это просто лишней неделей работы, детальные схемы потом не потребовались.
  • В маленькой компании каждый сам себе админстратор. В компании нет центральной AD для компьютеров сотрудников, все максимально гибко, это же наталкивается на то, что очень сложно найти создать единые для всех правила, но еще сложнее заставить им следовать. 

▍Из практических результатов: 

  • После уточняющих вопросов о патчинге наших серверов, поставщик изменил политику патчинга;
  • Компания решила полностью перенести все системы и хранилища данных в облако;
  • Были формализованные следующие процессы: 
    • Политика проверки кандидата перед приемом на работу
    • Политика работы из дома (teleworking)
    • Политика безопасности мобильных устройств
    • Политика действий в случае инцидентов с безопасностью
    • Политика предоставления и удаления доступа
    • Матрица ответственности
    • Политика инвентаризации
    • Роли и обязанности в отношениях с поставщиками (конкретно по каждому поставщику) 
    • Политика в отношении персональных данных
    • Проектный менеджмент
    • Политика бэкапов и архивирования

И конечно венцом всего стала обновленная политика информационной безопасности  компании. 

Оповещение сотрудников

Оповещение сотрудников о подготовке к сертификации это одновременно и возможность закрыть пункт A_7.2.2 Awareness, а также получить обратную связь на все написанные политики и процедуры. 

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

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

Чеклист это просто список вопросов, разбитых по темам, например:

Тема: Безопасность офиса

— Я знаю, как активировать систему сигнализации
— Я знаю, как действовать в случае утери входного баджа

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

Что было дальше

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

ссылка на оригинал статьи https://habr.com/ru/company/ruvds/blog/545742/

Техники повторного использования кода

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

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

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

Для некоторых подходов я добавил схемы, чтобы показать, как организованы составляющие сложных объектов. Будет часто упоминаться агрегация (агрегирование/делегирование/включение) и композиция.

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

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

  • Разделение функционала на классы/объекты и смешивание их полей, методов в одном объекте.

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

  • Вынесение части функционала в отдельные объекты/функции и помещение их в основной объект.

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

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

1) Объединение (смешивание) функционала нескольких объектов в одном
Смешивание и примеси (миксины)
Классическое наследование
Множественное наследование и интерфейсы

2) Композиция/агрегация с использованием списка
Прототипное наследование
Паттерн декоратор и аналоги

3) Композиция/агрегация с использованием одноуровневых структур данных (ссылка, массив ссылок, словарь)
Паттерн стратегия
Entity Component (EC)

4) Композиция/агрегация с вынесением логики вне объекта и его составляющих
Entity Component System (ECS)

5) Композиция/агрегация с использованием графов
Паттерн State machine

6) Композиция/агрегация с использованием деревьев
Паттерн composite и другие древовидные структуры
Behaviour tree

7) Смешанные подходы
React hooks

Объединение (смешивание) функционала нескольких объектов в одном.

Смешивание и примеси (миксины)

Самый простой, но ненадежный способ повторного использования кода – объединить один объект с другим(и). Подходит лишь для простых случаев, т.к. высока вероятность ошибки из-за замещения одних полей другими с такими же именами. К тому же, так объект разрастается и может превратиться в антипаттерн God Object.

Существует паттерн примесь (mixin/миксина), в основе которого лежит смешивание.
Примесь – это объект, поля и методы которого смешиваются с полями и методами других объектов, расширяя функциональность объекта, но который не используется сам по себе.
Можно добавить несколько миксин к одному объекту/классу. Тогда это схоже с множественным наследованием.

Классическое наследование

Здесь описывается классическое наследование, а не то, как наследование классов устроено в JS.

Подразумеваю, что читатель уже знаком с понятиями «наследование» и «множественное наследование». В отличие от простого смешивания, в классическом наследовании есть ряд строгих правил и механизмов, которые позволяет избежать множество проблем. В основе классического наследования лежит все то же смешивание — члены нескольких объектов объединяются в один объект.

При наследовании происходит копирование членов родительского класса в класс-наследник. При создании экземпляра класса тоже происходит копирования членов класса. Я не исследовал детали этих механизмов, к тому же они явно отличаются в различных языках. Подробнее с этой темой можно ознакомиться в 4-ой главе книги «Вы не знаете JS: this и Прототипы Объектов«.

Когда можно использовать наследование, а когда не стоит?
Наследования не стоит использовать в качестве основной техники для повторного использования кода для сложных объектов. Его можно использовать совместно с композицией для наследования отдельных частей сложного объекта, но не для самого сложного объекта. Например, для React компонентов наследование плохо, а для частей (вроде объектных аналогов custom hooks) из которых мог быть состоять компонент-класс, наследования вполне можно использовать. Но даже так, в первую очередь стоит рассматривать разбиение на большее число составляющих или применения других техник, вместо наследования.

При возможности появления сложной иерархии наследование (более 2-х уровней, где первый уровень иерархии – родитель, а второй уровень — наследники) тоже не следует использовать наследование.

Множественное наследование и интерфейсы

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

Интерфейсы есть, например, в Typescript. Реализация нескольких интерфейсов в одном классе отчасти похоже на наследование, но с их использованием «наследуется» только описание свойств и сигнатура методов интерфейса. Наследование реализации не происходит.

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

Композиция/агрегация с использованием списка

Прототипное наследование

При прототипном наследовании уже не происходит смешивания родительского объекта и его наследника. Вместо этого наследник ссылается на родительский объект (прототип).

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

Стоит отметить, что в JavaScript операции записи/удаления работают непосредственно с объектом. Они не используют прототип (если это обычное свойство, а не сеттер). Если в объекте нет свойства для записи, то создается новое. Подробнее об этом.

Цепочка прототипов организована как стек (Last-In-First-Out или LIFO). Какой объект добавлен в цепочку последним (если считать от итогового объекта-контейнера), к тому обращение будет первым.

Также существует вариант, когда при создании нового объекта с прототипом, создается копия прототипа. В таком случае используется больше памяти (хотя это проблема разрешаема), но зато это позволяет избежать ошибок в других объектах из-за изменения прототипа конкретного объекта.

Паттерн Декоратор и аналоги

Декоратор (wrapper/обертка) позволяет динамически добавлять объекту новую функциональность, помещая его в объект-обертку. Обычно объект оборачивается одним декоратором, но иногда используется несколько декораторов и получается своего рода цепочка декораторов.

Цепочка декораторов устроена как стек (LIFO). Какой объект добавлен в цепочку последним (если считать от итогового объекта-контейнера), к тому обращение будет первым.

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

Как в случае с прототипами, зачастую можно подменять декораторы во время выполнения. Декоратор оборачивает только один объект. Если оборачивается несколько объектов, то это уже что-то другое.

HOF (higher order function) и HOC (Higher-Order Component) — паттерны с похожей идей. Они оборачивают функцию/компонент другой функцией/компонентом для расширения функционала.

HOF — функция, принимающая в качестве аргументов другие функции или возвращающая другую функцию в качестве результата. Примером HOF в JS является функция bind, которая, не меняя переданную функцию, возвращает новую функцию с привязанным к ней с помощью замыкания значением. Другим примером HOF является карринг.

HOC — чистая функция, которая возвращает другой компонент (а он уже содержит в себе произвольную логику), который внутри себя «рендерит» переданный компонент. При этом сам переданный компонент не меняется, но в него могут быть переданы props.

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

const funcA = сompose(funcB, funcC, funcD);

или же менее читабельный вариант:

const funcA = ()=> {    funcB( funcC( funcD() ) ) ; };

То же самое можно получить такой записью:

function funcA() {   function funcB() {       function funcC() {          function funcD()       }     } }  

Недостатком последнего варианта является жесткая структура функций. Нельзя поменять их очередность или заменить одну из функций без создания новой аналогичной цепочки или ее части. funcC нельзя использовать без funcD, а funcB без funcC и без funcD. В первых же двух примерах – можно. Там функции независимы друг от друга.

Итого

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

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

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

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

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

Композиция/агрегация с использованием одноуровневых структур данных (ссылка, массив ссылок, словарь)

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

Паттерн стратегия

Паттерны декоратор и стратегия служат для одной цели – с помощью делегирования расширить функциональность объекта. Но делают они это по разному. Хорошо описана эта разница по ссылке: «Стратегия меняет поведение объекта «изнутри», а Декоратор изменяет его «снаружи».»

Паттерн Cтратегия описывает разные способы произвести одно и то же действие, позволяя динамически заменять эти способы в основном объекте (контексте).

На схеме ниже пара примеров связи стратегий с основным объектом.

К похожим способам (использование ссылки) расширения функционала объекта и повторного использования кода можно отнести события в HTML элементах и директивы в Angular и Vue.

<button onclick="customAction()" /> // html <input v-focus v-my-directive="someValue" /> // vue

Entity Component (EC)

Я не знаю, как называется данный паттерн. В книге Game Programming Patterns он называется просто «Компонент», а по ссылке его называют системой компонентов/сущностей. В статье же я буду называть его Entity Component (EС), чтобы не путать с подходом, который будет описан в следующей главе.

Сначала пройдемся по определением:

  • Entity (сущность) – объект-контейнер, состоящий из компонентов c данными и логикой. В React и Vue аналогом Entity является компонент. В Entity не пишут пользовательскую логику. Для пользовательской логики используются компоненты. Компоненты могут храниться в динамическом массиве или словаре.

  • Component  – объект со своими данными и логикой, который можно добавлять в любую Entity. В React компонентах похожим аналогом являются custom hooks. И описываемые здесь компоненты и пользовательские хуки в React служат для одной цели – расширять функционал объекта, частью которого они являются.

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

Данный паттерн похож на паттерн стратегия. Если в объекте использовать динамический массив со стратегиями, организовать их добавление, удаление и получение определенной стратегии, то это будет похоже на Entity Component. Есть еще одно серьезное отличие — контейнер не реализует интерфейс компонентов или методы для обращения к методам компонентов. Контейнер только предоставляет доступ к компонентам и хранит их. Получается составной объект, который довольно своеобразно делегирует весь свой функционал вложенным объектом, на которые он ссылается. Тем самым EC избавляет от необходимости использования сложных иерархий объектов.

Плюсы  EC

  • Низкий порог вхождения, т.к. в основе используется простая одноуровневая структура данных.

  • легко добавлять новую функциональность и использовать код повторно.

  • можно изменять составной объект (Entity) в процессе выполнения, добавляя или удаляя его составляющие (компоненты)

Минусы

  • для простых проектов является ненужным усложнением из-за разбиение объекта на контейнер и компоненты

В одной из своих следующих статей я опишу применение этого подхода для React компонентов. Тем самым я покажу, как избавиться от первых двух недостатков компонентов на классах, описанных в документации React-а:
https://ru.reactjs.org/docs/hooks-intro.html#its-hard-to-reuse-stateful-logic-between-components
https://ru.reactjs.org/docs/hooks-intro.html#complex-components-become-hard-to-understand

Этот подход используется с самого начала выхода движка Unity3D для расширения функционала элементов (объектов) дерева сцены, включая UI элементы, где вы можете получше ознакомится с данным подходом. Но в таком случае придётся потратить не мало времени на изучение движка.

Итого

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

В случае использования EC может появиться новая проблема – при большом количестве компонентов, связанных между собой в одном объекте, становиться сложно разобраться в его работе. Выходом может стать некий компонент, который контролирует взаимодействия между компонентами в одной Entity или в группе вложенных Entities. Такой подход известен как паттерн Посредник (Mediator).

Но даже “посредника“ будет недостаточно для более сложных случаев. К тому же он не является универсальным. Для каждой Entity с множеством связанных компонентов придёться реализовывать новый тип “посредника”. Есть и другой выход. EC можно комбинировать с другими подходами на основе графов и деревьев, которые будут описаны позже.

Композиция/агрегация с вынесением логики вне объекта и его составляющих

Entity Component System (ECS)

Я не работал с этим подходом, но опишу то, как я его понял.

В ECS объект разбивается на 3 типа составляющих: сущность, компонент (один или несколько), система (общая для произвольного числа объектов). Этот подход похож на EC, но объект разбивается уже на 3 типа составляющих, а компонент содержит только данные.

Определения:

  • Entity – его основное назначение, это идентифицировать объект в системе. Зачастую Entity является просто числовым идентификатором, с которым сопоставляется список связанных с ним компонентов. В других вариациях Entity также может брать на себя роль контейнера для компонентов. Как и в EC подходе, в Entity нельзя писать пользовательский код, только добавлять компоненты.

  • Component — объект с определенными данными для Entity. Не содержит логики.

  • System — в каждой системе описывается логика. Каждая система перебирает список компонентов определенных типов или компоненты определенных entities и выполняет логику с использованием данных в компонентах. Может извлекать компоненты из entities. Результатом выполнения системы будет обновление данных в компонентах. В некоторых случаях системы могут быть обычными функциями, получающими на вход нужные данные.

Также может быть некий объект-менеджер (или несколько менеджеров), который хранит все системы и объекты, а также периодически запускает все системы. Здесь уже реализация произвольная.

Пример простой ECS: Допустим есть несколько объектов, у которых есть идентификаторы. Несколько из этих объектов ссылаются на компоненты Position, в которых хранятся текущие координаты x, y, и на компонент Speed, который содержит текущую скорость. Есть система Movement, которая перебирает объекты, извлекает из них компоненты Position и Speed, вычисляет новую позицию и сохраняет новые значения x, y в компонент Position.

Как я уже говорил, реализации ECS могут отличаться. Например:

a) entity является контейнером для своих компонентов

b) компоненты содержится в массивах/словарях. Entity является просто идентификатором, по которому определяется компонент, связанный с сущностью.
http://jmonkeyengine.ru/wiki/jme3/contributions/entitysystem/introduction-2
http://entity-systems.wikidot.com/fast-entity-component-system#java
https://www.chris-granger.com/2012/12/11/anatomy-of-a-knockout/

На схеме изображен первый вариант, когда entity ссылается на свои компоненты.

Плюсы ECS

  • Слабое сцепление составляющих объекта, поэтому легко добавлять новую функциональность комбинирую по-разному составляющие.

  • Проще тестировать, т.к. нужно тестировать только системы. Компоненты и сущности тестировать не нужно.

  • Легко выполнять многопоточно.

  • Более эффективное использование памяти, кэша и, следовательно, большая производительность.

  • Легко реализовать сохранение всего приложения, т.к. данные отделены от функционала.

Минусы ECS

  • Высокая сложность, не стандартный подход.

  • для простых проектов является ненужным усложнением.

Так как я занимаюсь фронтенд разработкой, а она по большей части относится к разработки UI, то упомяну, что ECS используется в игре World of Tanks Blitz для разработки UI:
https://www.youtube.com/watch?v=nu8JJEJtsVE

Итого

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

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

Композиция/агрегация с использованием графов

К данному способу повторного использования кода я отнес паттерн «машина состояний» (State machine/Finite state machine/конечный автомат).

Аналогом машины состояний простой является switch:

switсh (condition) {   case stateA: actionA();   case stateB: actionB();   case stateC: actionC(); }

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

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

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

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

Я уже описывал паттерн “Машина состояний” и его составляющие, и вкратце писал о иерархической машине состояний в статье «Приемы при проектировании архитектуры игр« в главе «машина состояний«.

Преимущества использования машины состояний:

Хорошо описано по ссылке.

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

Где при разработке UI можно использовать машину состояний?

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

Другие примеры использования в UI:
https://24ways.org/2018/state-machines-in-user-interfaces/
https://xstate.js.org/docs/ (библиотека для JS, которую можно использовать c React, Vue, Svelte)
https://github.com/MicheleBertoli/react-automata (библиотека для React)
https://habr.com/ru/company/ruvds/blog/346908/

Подходит ли State machine в качестве основного механизма повторного использования кода и разбиения сложных объектов на составные части?

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

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

Композиция/агрегация с использованием деревьев

Паттерн composite и другие древовидные структуры

Деревья часто встречается в разработке. Например, объекты в JavaScript могут содержать вложенные объекты, а те также могут содержать другие вложенные объекты, тем самым образую дерево. XML, JSON, HTML, DOM-дерево, паттерн Комповщик (Composite)  – все это примеры древовидной композиции.

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

Behaviour tree

Интересным вариантом композиции является Behaviour tree (дерево поведения). Это организация логики программы (обычно AI) или ее частей в виде дерева.

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

Я уже описывал деревья поведений в прошлом в этой статье.

Более наглядный пример схемы готового дерева из плагина banana-tree

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

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

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

Для простых случаев, как обычно, этот подход будет ненужным усложнением.

Смешанные подходы

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

Довольно многое можно отнести к смешанных подходам. Entity Component в Unity3D реализован так, что позволяет хранить не только компоненты, но и вложенные сущности. А для пользовательских компонентов можно использовать наследование в простых случаях, либо объединить компоненты с более продвинутыми техниками (паттерн mediator, машина состояний, дерево поведения и другие).

Примером смешивания подходов является анимационная система Mecanim в Unity3D, которая использует иерархическую машину состояний с деревьями смешивания (blend tree) для анимаций. Это относится не совсем к коду, но является хорошим примером комбинации подходов.

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

React hooks

Эта часть статьи для разработчиков, знакомых c React. Остальным многое в ней будет не понятно.

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

Как я понял, хуки при вызове добавляют к текущему обрабатываемому компоненту (точнее к fiber-ноде) свое состояние – объект, в котором могут быть указаны переданные сallback-и (в случае useEffect, useCallback), массив зависимостей, значения (в случае useState) и прочие данные (в случае useMemo, useRef, …).

А вызываются хуки при обходе дерева компонентов, т.е. когда вызывается функция-компонент. React-у известно, какой компонент он обходит в данный момент, поэтому при вызове функции-хука в компоненте, состояние хука добавляется (или обновляется при повторных вызовах) в очередь состояний хуков fiber-ноды. Fiber-нода – это внутреннее представление компонента.

Стоит отметить, что дерево fiber элементов не совсем соответствует структуре дерева компонентов. У Fiber-ноды только одна дочерняя нода, на которую указывает ссылка child. Вместо ссылки на вторую ноду, первая нода ссылается на вторую (соседнюю) с помощью ссылки sibling. К тому же, все дочерние ноды ссылаются на родительскую ноду с помощью ссылки return.

Также для оптимизации вызова эффектов (обновление DOM, другие сайд-эффекты) в fiber-нодах используются 2 ссылки (firstEffect, nextEffect), указывающие на первую fiber-ноду с эффектом и следующую ноду, у которой есть эффект. Таким образом, получается список нод с эффектами. Ноды без эффектов в нем отсутствуют. Подробнее об этом можно почитать по ссылкам в конце главы.

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

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

Чтобы просмотреть содержимое fiber-ноды, достаточно воспользоваться console.log и вставить туда JSX код, который возвращает компонент:

function MyComponent() {   const jsxContent = (<div/>);   console.log(jsxContent);   return jsxContent; }

Корневую fiber-ноду можно просмотреть следующим образом:

const rootElement = document.getElementById('root'); ReactDOM.render(<App />, rootElement);  console.log(rootElement._reactRootContainer._internalRoot);

Также есть интересная наработка: react-fiber-traverse

Под спойлером приведен код компонента с хуками и отображение его fiber-ноды
import { useState, useContext, useEffect,useMemo, useCallback,          useRef, createContext } from 'react'; import ReactDOM from 'react-dom';  const ContextExample = createContext('');  function ChildComponent() {   useState('childComponentValue');   return <div />; }  function useMyHook() {   return useState('valueB'); }  function ParentComponent() {   const [valueA, setValueA] = useState('valueA');   useEffect(function myEffect() {}, [valueA]);   useMemo(() => 'memoized ' + valueA, [valueA]);   useCallback(function myCallback() {}, [valueA]);   useRef('refValue');   useContext(ContextExample);   useMyHook();    const jsxContent = (     <div>       <ChildComponent />       <button onClick={() => setValueA('valueA new')}>Update valueA</button>     </div>   );    console.log('component under the hood: ', jsxContent);   return jsxContent; }  const rootElement = document.getElementById('root');  ReactDOM.render(   <ContextExample.Provider value={'contextValue'}>     <ParentComponent />   </ContextExample.Provider>,   rootElement, );

С более подробным описанием работы внутренних механизмов React на русском языке можно ознакомиться по ссылкам:

Как Fiber в React использует связанный список для обхода дерева компонентов
Fiber изнутри: подробный обзор нового алгоритма согласования в React
Как происходит обновление свойств и состояния в React — подробное объяснение
За кулисами системы React hooks
Видео: Под капотом React hooks

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

Линейность кода и составляющих сложного объекта

Известно, что множество вложенные условий, callback-ов затрудняют читаемость кода: https://refactoring.guru/ru/replace-nested-conditional-with-guard-clauses
https://habr.com/ru/company/oleg-bunin/blog/433326/ (в статье упоминается линейный код)
https://www.azoft.ru/blog/clean-code/ (в статье упоминается линейность кода)

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

ссылка на оригинал статьи https://habr.com/ru/post/545368/

Интернет вещей по-русски. Безопасность в OpenUNB

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

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

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

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

а также изучить первоисточник на сайте Сколтеха.

Сегодня мы рассмотрим механизм криптографической защиты передаваемых данных (шифрование, контроль целостности и подлинности).

Чтобы обеспечить базу безопасности, у каждого устройства и сервера должно быть что-то общее, секретное, которое никто не знает. В OpenUNB это секретный ключ шифрования K0, имеющий длину k, равную 128 или 256 бит. Весь остальной протокол должен быть сделан так, чтобы этот базовый, основной ключ не стал бы известен никому никаким образом, а информация при этом была защищена. Поэтому базовый ключ претерпевает такое множество преобразований в своем стремлении встретиться с информацией.

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

В недрах устройства и сервера OpenUNB есть счетчик активаций Na и счетчик эпох Ne, которые остаются примерно одинаковыми в процессе работы системы. Сначала вырабатывается ключ активации:

$ Ka = CTR (K0, Na || 0^{n/2-16}, 0^k).$

Потом уже по ключу активации вырабатываются рабочие ключ шифрования Km и ключ имитозащиты Ke:

$ Km = CTR (Ka, 0x02 || Ne || 0^{n/2-32}, 0^k),$

$ Ke = CTR (Ka, 0x03 || Ne || 0^{n/2-32}, 0^k).$

Для криптографических преобразований разработчики OpenUNB рекомендуется использовать блочный шифр «Магма», который имеет длину блока n = 64 бита и длину ключа k = 256 бит, определенный в стандарте ГОСТ Р 34.12-2015.

Шифрование выглядит так:

$ EncryptedMACPayload = CTR (Ke, Nn || 0^{n/2-16}, MACPayload),$

где Nn — номер пакета, а выработка имитовставки так:

$MIC = CMAC24 (Km, P),$

где значение вектора P зависит от длины блока n и длины поля MACPayload. Пусть len –
однобайтная целочисленная беззнаковая переменная, равная длине полезных данных
MACPayload в битах (переменная len может принимать значения 16 или 48), тогда:

если len = 48 и n = 64, то

$P = DevAddr || MACPayload || Nn || 0^{2n-len-48} || len;$

в остальных случаях

$P = DevAddr || MACPayload || Nn || 0^{n-len-48} || len.$

Процедура CMAC24 здесь это криптопреобразование "режим выработки имитовставки" согласно ГОСТ.

Далее эти поля вставляются на свои места. Напомним схему формирования пакета:
image

Более подробно, со всеми деталями, вы можете прочитать о безопасности в OpenUNB в первоисточнике.

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

Как всегда, исходники крипто-преобразований от deef137 доступны на Github.

ссылка на оригинал статьи https://habr.com/ru/post/545970/

Mr. Stylin

Вам не кажется, что мы слишком далеко зашли? Да вы, ну те что пишете на реакте. Вначале нам пришла гениальная мысль миксить представление с основным кодом, затем стали писать CSS стили прямо в JS, как в том фильме “а тут всё в бак можно заливать с бензином”. Как сейчас помню вопли и негодование одних и счастье других, признаться честно, позже я и сам не брезгал styled-components-ами. Но на этом вся затея не остановились, нашлись и такие что решили: “нафиг писать стили отдельно” и стали сразу передавать их в пропсы компонента. Такой подход хорошо отражается в styled-system и жутко напоминает те дикие времена когда балом правили  эти господа: 

<font color="red" face="Verdana" size="+1">   Вы думали <b>я</b> <i>не вернусь</i>? </font>

Это безумие можно оправдать, тем что всё развивается спиралью, всё новое это хорошо забытое старое. И возможно в следующей ветви развития мы вновь вернемся к CSS с какой нибудь новизной. Однако. Я не стал ждать того момента и создал своего гомункула, свой велосипед.

И вот что вышло:

import {render} from 'react-dom' import {Title} from './styles.scss'  render(   <Title color='tomato' size='small'>     Hello world!   </Title>,   document.getElementById('app') )

styles.scss

/**   @tag: h1   @component: Title   size: small | medium | large   color: #38383d --color */ .title {   --color: #38383d;   color: var(--color);   font-size: 18px;    &.small {     font-size: 14px;     margin: 2px 0;   }   &.medium {     font-size: 18px;     margin: 4px 0;   }   &.large {     font-size: 20px;     margin: 6px 0;   } }

Если вы не заметили, тут есть два абсурда:

import {Title} from './styles.scss'

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

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

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

Если именовать CSS классы также как и значение свойств компонента то их можно сократить до type: primary | secondary | link, как было сделано в первом примере, полный список сокращалок можно найти по ссылке.

Конечно, чтобы эта магия заработало, нужен специальный webpack loader и библиотека:

npm install @stylin/style npm install --save-dev @stylin/msa-loader

И внести некоторые изменения в конфигу вашего webpack-а.

Однако, это еще не всё! Если вы из тех извращенцев, что используют TypeScript, то вы сможете получить типы бонусом ко своим стилизованным компонентам. И знаете это так избавляет от скучной работы:

Для этого вам надо установить еще один webpack loader и добавить его в конфигу.

npm install --save-dev @stylin/ts-loader

Возможно у вас возникли вопросы типа: “Че там брат, че там под капотом?”

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

Но я, всё равно сравнил.

Stylin быстрее порядком на 30% в сравнении с styled-components, а в определенных кейсах он делает styled-components как стоячего в несколько раз!

Передачи значений из JS в CSS происходит через нативные CSS переменные отсюда и скорость и никаких инъекций стилей в рантайм, все происходит так как должно быть — традиционным способом для всех браузеров. Справедливости ради, нужно сказать далеко не все браузеры это поддерживают, к примеру E11 обречен отсюда и минус данной либы.

Пример использования переменных:

componentPropertyName: default-value --css-variable

/**   @tag: button   @component: SexyButton   width: 150px --btn-width */ .sexy-button {   --btn-width: 150px;   width: var(--btn-width); }  /* JSX */ <SexyButton width='180px'>   Love me </SexyButton>

Благодаря CSS переменам поддержка тем становится гораздо проще, вы полностью избавитесь от какой либо логики в стилях, все будет в старой и доброй декларативной манере. Для этих целей я подготовил онлайн демку, правда нужно отметить магия авто-генерации типов там не работают (в sandbox нет доступа к FS), чтобы их пощупать вам придется стянуть исходники к себе и уже на локальной машине  экспериментировать.

Ах да, чуть ли не забыл, переключая темную тему на светлую и наоборот, вы заметите, что всё приложение не перерисовывается (react render) и это какая то магия!

И естественно есть возможность пере-стилизовать существующие компоненты.

import {Button} from 'antd' import {appleStyle} from './style.scss'  // sexy-button is css-class const StyledButton = appleStyle(`sexy-button`, Button)  <StyledButton type='dashed'>   Love me </StyledButton>

Я бы с удовольствием вам рассказал о всех возможностей данной библиотеки, однако я не хочу показаться, будто я пришел сюда пиариться :D. Я тут только, чтобы избавить вас от боли, которыми причиняют современные решения и другая причина послушать вашу критику и  предложения, может вы изволите такую балалайку и для вашего любимого фреймворка типа next.js или preact?

Всем желаю легких трудовых  будней и долгих выходных!

ссылка на оригинал статьи https://habr.com/ru/post/545994/

Как я делаю цифровую минигитару. Часть 2

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

Напомню какие требования к девайсу я поставил, когда начинал разработку:

  • Устройство должно имитировать гитару с 6-ю струнами и 12-ю ладами на грифе

  • Должно быть компактным, в идеале складным, чтобы можно было брать его с собой куда угодно

  • Должно подключаться ко всем популярным осям — Android, IOS, Windows, Linux, MacOS и определяться там как MIDI устройство без каких-либо драйверов

  • Работа от аккумулятора

  • Подключение должно производиться без проводов по Bluetooth Low Energy (но раз уж там будет USB разъем для зарядки, то и по проводу пусть тоже подключается)

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

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

  • Возможность использования основных техник игры на гитаре: hummer on, pull off, slide, vibrato

  • Задержка передачи midi команд не более 10мс

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

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

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

Было принято решение пытаться делать стартап и выходить на кикстартер.

Итак, что было дальше?

Следующим шагом стал стандартный этап поиска pre-seed раунда инвестирования. Деньги нужны были на доработку и изготовление нового прототипа, проведения пиар кампании и оплату юридических манипуляций (для участия на кикстартере необходимо юр. лицо в США со всеми вытекающими организационными тратами). Эту задачу нам с моим партнером, отвечающим за бизнес процессы, удалось решить за 2 месяца.

Позиционирование

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

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

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

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

Конкуренты

Есть на этом рынке и конкуренты. Мы купили по экземпляру каждого для оценки.

1. Artiphon — панель, чувствительная к нажатию, по форме напоминает гитару, но позиционируется скорее как настольная клавиатура.

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

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

3. Jamtik — игрушка с 7-ю ладами на батарейках. Сыграть на ней не удалось даже «В траве сидел кузнечик».

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

Теперь самое интересное – новый прототип

На основе испытаний старого прототипа, конкурентов и личных предпочтений, я решил расширить функционал девайса и добавить несколько пунктов к требованиям:

  • Конечно, RGB подсветка

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

  • Детектирование силы нажатия на сенсоры грифа для реализации стандартных гитарных техник игры

  • Встроенный синтезатор со встроенной библиотекой инструментов и разъем Jack 3.5мм для подключения наушников или внешних колонок. Встроенные динамики делать не стал – добиться хорошего звучания было бы слишком сложно и дорого

  • Мобильное приложение со встроенным качественным синтезатором и функционалом обучения

  • Пады с подсветкой для записи лупов

  • Упоры на деке и удобного удержания сидя и стоя, крепления для ремешка

Корпус

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

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

Электроника

Электронику пришлось разделить на 4 платы:

  • Гриф

Адресные RGB светодиоды подключены последовательно к одной ноге STM-ки. Пришлось повозиться с двойной буферизацией и выводом данных через DMA 100 раз в секунду. Зато, теперь оно работает очень быстро и можно запускать цветные визуальные анимации на поверхности грифа.

  • Плата с падами и подпружиненными контактами для соединения с грифом в разложенном состоянии гитары

  • Основная плата со струнами, мозгами, силовой частью, радио частью, синтезатором и датчиками

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

  • Плата с разъемами USB type-C, Jack 3.5мм и тремя индикаторными светодиодами

Мобильное приложение

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

Мы сформировали протокол общения гитары с приложением и разбили его на несколько интерфейсов:

  • Midi команды, разумеется, по умолчанию передаются по стандартному BLE-Midi интерфейсу. Таким образом, к приложению при желании можно будет подключить и другие midi устройства, например, клавиатуру

  • Опционально поддерживается прием midi команд и по проводному USB-Midi интерфейсу. Это будет полезно для старых телефонов без поддержки BLE, а также в случае необходимости сокращения задержки до минимума (порядка 5мс)

  • Отдельный кастомный BLE сервис для передачи уникальных команд, связанных с управлением светодиодами, режимами работы девайса, синхронизацией состояния и т.д.

  • Стандартный BLE battery service для передачи уровня заряда аккумулятора. Он поддерживается на уровне операционной системы и, в случае в виндой, даже отображается соответствующая иконка в панели устройств

Приложение разбито на несколько экранов, соответствующих разным режимам работы:

Свободная игра

В этом режиме пользователь выбирает один из инструментов (акустика, электрогитара, пианино, укулеле, барабаны, и т.д.) и просто играет как ему хочется. Есть возможность загружать свои инструменты в виде саундфонтов в формате «.sf2».

Игра по табулатурам

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

Обучение

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

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

Теперь играть на ней можно тремя разными способами:

  1. Подключение через BLE MIDI протокол к телефону или компу, где девайс распознается как миди устройство, и игра через внешние виртуальные синтезаторы (Ableton, FL studio, Garage Band и т.д. или наше приложение)

  2. То же самое, но с подключением через USB MIDI (работает со всеми хостами, которые я проверял – Android, IOS, Windows, MacOS, Debian)

  3. Игра внутренним синтезатором, с подключением наушников или внешней колонки напрямую в гитару. В этом случае звук будет не самым Hi-Fi, но вполне приемлемым для игры для себя

Интересно, что можно играть всеми тремя способами одновременно, может кому-то пригодится.

Завершение

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

Кому интересно следить за новостями проекта или оформить предзаказ – оставляйте почту в форме на сайте и подписывайтесь на соцсети.

Спасибо за внимание! Буду рад обратной связи в комментариях.

ссылка на оригинал статьи https://habr.com/ru/post/546000/