-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51

Habrahabr/New








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/new/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-new-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

[Перевод - recovery mode ] Почему я перешел с React на Cycle.js

Понедельник, 10 Июля 2017 г. 11:46 + в цитатник

Нетрудно догадаться, что большинство разработчиков сейчас используют какие-либо фреймворки для разработки приложений. Они помогают нам структурировать сложные приложения и экономят время. Каждый день можно наблюдать большое количество обсуждений, какой фреймворк лучше, какой нужно учить, и тому подобное. Так что, в этот раз я поделюсь своим опытом и отвечу на вопрос: «Почему я перешел на Cycle.js с React?».


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


Большинство использует React без мысли о том, что есть лучший инструмент или способ разработки веб-приложений. Это дало мне толчок попробовать Cycle.js, как новый реактивный фреймворк, который становится все более и более популярным изо дня в день.


И в этой статье я хочу объяснить:

  1. Что такое реактивное программирование
  2. Как работает Cycle.js
  3. И почему он, на мой взгляд, лучше React

Что такое реактивное программирование?


Реактивное программирование (RP, РП) — это работа с асинхронными потоками данных. Если вы создавали веб-приложение, вы, возможно, уже писали много реактивного кода. В качестве примера можно привести событие клика — это асинхронный поток данных. Мы можем следить за ним и создавать побочные эффекты (side effects). Идея реактивного программирования — дать возможность создавать потоки данных где угодно и работать с ними. Тогда мы можем создать некоторые абстракции для всех побочных эффектов (side effects), которые куда проще использовать, поддерживать и тестировать.

Вы возможно зададите вопрос: «А зачем мне нужен этот новый „реактивный“ подход в программировании?». Ответ прост: Реактивное программирование помогает унифицировать код и делает его более последовательным. Больше не нужно думать о том, как что-то должно работать и как правильно это реализовать. Просто пишем код определенным образом, не беспокоясь с какими данными мы работаем (клики мышкой, http-запросы, веб-сокеты). Всё это — потоки данных, и каждый поток имеет множество методов, которые позволяют работать с этими данным, например map или filter. Эти функции возвращают новые потоки, которые могут быть так же использованы.

Реактивное программирование предоставляет абстракции, а это дает возможность концентрироваться на бизнес-логике.




Реактивное программирование в JavaScript


В Javascript есть пара замечательных библиотек для работы с потоками данных. Одна из них — это всем известный Rx-JS, расширение ReactiveX, — API для асинхронного программирования с отслеживаемыми потоками данных. Вы можете создать Observable (поток данных) и управлять им множеством функций.


Вторая библиотека — это Most.js. Она имеет лучшую производительность, что подтверждается тестами.


Также стоит отметить одну небольшую и быструю библиотеку xstream, написанную автором Cycle.js. Она содержит 26 методов и весит 30kb. Это одна из самых быстрых библиотек для реактивного программирования на JS.


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


Что такое Cycle.js?


Cycle.js — это функциональный и реактивный Javascript-фреймворк. Он предоставляет абстракции для приложений в виде чистой функции main(). В функциональном программировании функции должны принимать на вход параметры и что-то возвращать без побочных эффектов. В Cycle.js функция main() на вход принимает параметры из внешнего мира и пишет тоже во внешний мир. Побочные эффекты реализуются с помощью драйверов. Драйверы — это плагины, которые управляют DOM'ом, HTTP-запросами, вебсокетами и так далее.



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

Основное API имеет только одну функцию, run() с двумя аргументами:

run(app, drivers);

app — основная чистая функция, а drivers — это вышеуказанные драйверы.

Функционал Cycle.js разделен на несколько небольших модулей:

  • @cycle/dom – коллекция драйверов для работы с DOM; Это DOM-драйвер и HTML-драйвер, основанный на snabdom virtual DOM библиотеке
  • @cycle/history – драйвер History API
  • @cycle/http – драйвер HTTP запросов, основанный на superagent
  • @cycle/isolate – функция создания изолированных dataflow-компонентов
  • @cycle/jsonp – JSONP-драйвер
  • @cycle/most-run – run-функция для работы с most
  • @cycle/run – run-функция для работы с xstream
  • @cycle/rxjs-run – run-функция для работы с rxjs

Cycle.js код


Хотите увидеть немного Cycle.js-кода? Мы создадим простое приложение, демонстрирующее как все это работает. Мне кажется, что старое доброе приложение-счетчик, будет идеальным для этого примера. Мы увидим, как работать с DOM-событиями и перерендериванием DOM-элементов.

Давайте создадим два файла: index.html и main.js. index.html будет содержать только основной файл со скриптами, где и прописана вся логика.

npm install и продолжение настройки
Сначала надо создать новый package.json, так что запустим

npm init -y

Затем установим основные зависимости

npm install @cycle/dom @cycle/run xstream --save

Это команда поставит @cycle/dom, @cycle/xstream-run, and xstream. Также нужны babel, browserify и mkdirp, установим их:

npm install babel-cli babel-preset-es2015 babel-register babelify browserify mkdirp --save-dev

Чтобы работать с Babel, создадим .babelrc-файл в корне директории со следующим содержимым:

{
  "presets": ["es2015"]
}

Также нам нужно добавить скрипты, которые упрощают запуск команд, в package.json

"scripts": {
  "prebrowserify": "mkdirp dist",
  "browserify": "browserify main.js -t babelify --outfile dist/main.js",
  "start": "npm install && npm run browserify && echo 'OPEN index.html IN YOUR BROWSER'"
}

Для запуска нашего Cycle.js-приложения, нужно запустить команду

npm run start

Все. Установка закончена, теперь мы можем начать писать код.

Начнем с добавления небольшого HTML-кода внутри index.html

index.html
< !DOCTYPE html>


    
    


    


Мы создали div с идентификатором main. Cycle.js свяжется с этим элементом и будет в него рендерить все приложение. Мы также подключили dist/main.js-файл. Это проведенный через babel, транспайленный и объединенный js-файл, который будет создан из main.js-файла.

Время написать немного Cycle.js-кода. Откроем main.js и импортируем все необходимые зависимости:

import xs from 'xstream';
import { run } from '@cycle/run';
import { div, button, p, makeDOMDriver } from '@cycle/dom';

Мы включили xstream, run, makeDOMDriver и функции, которые помогут нам работать с Virtual DOM (div, button и p).

Напишем основную main-функцию:

function main(sources) {
  const action$ = xs.merge(
    sources.DOM.select('.decrement').events('click').map(ev => -1),
    sources.DOM.select('.increment').events('click').map(ev => +1)
  );

  const count$ = action$.fold((acc, x) => acc + x, 0);

  const vdom$ = count$.map(count =>
    div([
      button('.decrement', 'Decrement'),
      button('.increment', 'Increment'),
      p('Counter: ' + count)
    ])
  );

  return {
    DOM: vdom$,
  };
}

run(main, {
  DOM: makeDOMDriver('#main')
});

Это наша основная main-функция. Она берет на вход параметры и возвращает результат. Входные параметры — это потоки DOM (DOM streams), а результат — это Virtual DOM. Давайте начнем объяснение шаг за шагом:

const action$ = xs.merge(
  sources.DOM.select('.decrement').events('click').map(ev => -1),
  sources.DOM.select('.increment').events('click').map(ev => +1)
);

Здесь мы объединяем два потока в один поток, называемый action$ (здесь используем соглашение о суффиксе "$" тех переменных, которые представлют собой поток данных). Один из потоков является кликом по кнопке (.decrement) уменьшающей на единицу счетчик, второй — по другой, увеличивающей счетчик, кнопке (.increment). Мы связываем эти события c числами -1 и +1, соответственно. В конце слияния, поток action$ будет выглядеть следующим образом:

----(-1)-----(+1)------(-1)------(-1)------

Создадим еще один поток count$

const count$ = action$.fold((acc, x) => acc + x, 0);

Функция fold прекрасно подходит для этой цели. Она принимает два аргумента: accumulate и seed. seed is firstly emitted until the event comes. Следующее событие комбинируется с первым, основываясь на accumulate-функции. Это практически функция-reduce() для потоков.

Наш поток count$ получает 0 как начальное значение, затем при каждом новом значении из action$-потока, мы суммируем с текущим значением count$-потока.

В самом конце, завершая цикл работы функции, нужно запустить функцию run под main.

Последний шаг — создать Virtual DOM:

const vdom$ = count$.map(count =>
  div([
    button('.decrement', 'Decrement'),
    button('.increment', 'Increment'),
    p('Counter: ' + count)
  ])
);

Мы сопоставляем данные в потоке count$ и возвращаем Virtual DOM для каждого элемента в потоке. Virtual DOM содержит одну div-обертку, две кнопки и параграф. Как можно заметить, Cycle.js работает с DOM с помощью JS-функций, но JSX также можно использовать.

В конце main-функции нужно возвратить наш Virtual DOM:

return {
  DOM: vdom$,
};

Мы передаем main-функцию и DOM-драйвер, который подключен к и получаем поток событий для этого div-элемента. Мы завершаем наш цикл и создаем прекрасное Cycle.js-приложение.

Вот как это работает:


Вот и все! Таким образом и можно работать с DOM-потоками. Если вы хотите посмотреть как работать с HTTP-потоками в Cycle.js, я написал статью[eng]об этом.

Весь код можно найти на GitHub-репозитории.


Почему я перешел с React на Cycle.js?


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


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


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


Давайте рассмотрим плюсы и минусы использования Cycle.js вместо React.


Плюсы


1. Большая кодовая база


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


2. Потоки данных


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


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

3. Побочные эффекты функций (Side effects)


React имеет некоторые проблемы в работе с побочными эффектами (side effects). Нет стандартизированного подхода в работе с побочными эффектами (side effects) в React-приложениях. Есть множество инструментов, которые помогают работать, но это так же отнимает время на установку и изучения их. Наиболее популярные — это redux-saga, redux-effects, redux-side-effects, и redux-loop. Видите, что я имею ввиду? Их множество. И вам нужно выбирать, что учить и что внедрять в ваш проект.

4. Функциональное программирование


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

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

Минусы


1. Сообщество


В настоящее время React является наиболее популярным фреймворком и используется повсюду. Cycle.js — нет. Он все еще не очень популярный, и это будет проблемой, когда вы столкнетесь с проблемой, и найти решение в сети будет не таким легким. Временами решения проблемы нет, и вам придется решать ее самому.

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

Но это меняется. Много разработчиков начинают использовать Cycke.js и говорить о нем, о проблемах. Решать их вместе. Cycle.js также имеет хорошую документацию со множеством примеров. У меня не было настолько сложных ошибок, которые было трудно дебажить.

2. Изучение нового подхода


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

3. Некоторые приложения не нуждаются в реактивном подходе


Да, некоторые приложения не должны быть «реактивными». Блоки, продающие сайты, лэндинги и многие другие статичные сайты с ограниченной функциональностью остро не нуждаются в реактивном подходе. Здесь нет данных, которые проходят через все приложение в реальном времени, нет стольких форм и кнопок. Используя реактивный фреймворк, возможно, вы сделаете такие сайты медленнее. Вы должны понимать, когда приложение нуждается в Cycle.js и реактивном подходе, а когда — нет.

Заключение


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

А вы пробовали реактивное программирование или Cycle.js? Убедил ли я вас попробовать? Дайте знать, что вы думаете в комментариях ставьте лайк, подписывайтесь на...!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332674/


Метки:  

Canvas & SVG: работаем с графикой

Понедельник, 10 Июля 2017 г. 11:36 + в цитатник
В HTML5 представлено два элемента для работы с web графикой: Canvas и SVG. Две эти технологии достаточно сильно отличаются друг от друга. Важно знать об их преимуществах и недостатках, чтобы выбрать наиболее подходящую для конкретной задачи технологию. Элемент SVG позволяет создавать векторную графику, а элемент Canvas предназначен для создания растровых изображений. Элемент Сanvas также используется технологией WebGL для аппаратного ускорения 3D-графики.

SVG


Масштабируемая векторная графика (Scalable Vector Graphics — SVG) является языком разметки, расширенным из XML для описания двухмерной векторной графики.
Для создания изображения в векторной графике используются геометрические примитивы (точки, линии, кривые, многоугольники). С их помощью можно создавать масштабируемые изображения, которые не теряют в качестве при масштабировании.

SVG — технология рисования с хранением объектов в памяти (Retained mode graphics). Как и HTML, SVG имеет объектную модель документа (DOM). DOM в SVG, как и в HTML, имеется модель событий. Это значит, что при использовании этой технологии для реализации интерактивных действий (таких как управление мышью и т.п.) со стороны программиста требуется меньше усилий, поскольку события привязываются непосредственно к элементам DOM.

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

Для встраивания SVG в документ используется элемент .

Пример 1: Рисование линии

	



Пример 2: Рисование круга с заливкой
HTML:

	


CSS:
svg {
fill: blue;
}


Canvas 2d


<Сanvas> — это HTML элемент, который используется для создания растровой графики при помощи JavaScript. Элемент предоставляет удобный API для рисования 2D графики с помощью JavaScript.

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

Элемент имеет только два атрибута — ширину и высоту. Если атрибуты высоты и ширины не установлены, то согласно спецификации html5 ширина элемента canvas будет равна 300 пикселям, а высота 150. При изменении этих атрибутов canvas очищается.

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

Для рисования в первую очередь необходимо получить доступ к контексту, который предоставляет API для создания графики. Контекст можно получить при помощи метода getContext() элемента canvas. В качестве первого параметра необходимо указать тип контекста, который мы хотим использовать. На данный момент большинство современных браузеров поддерживает 2 типа контекста «2d» ( позволяет создавать 2-х мерную графику) и «webgl» (позволяет использовать технологию WebGL для создания трехмерной графики). Если указанный тип контекста не поддерживается браузером, метод getContext() возвращает null.

Пример: Рисование линии

const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
context.beginPath();
context.moveTo(5, 5);
context.lineTo(500, 60);
context.lineWidth = 3;
context.strokeStyle = '#b4241b';
context.stroke();


WebGL


WebGL — это еще одна новая технология, которая использует элемент canvas для создания графики. WebGL позволяет веб-контенту использовать API, основанный на OpenGL ES 2.0, для визуализации трехмерной графики, но возможно работать и с двухмерной графикой.

Для начала рисования так же, как и в предыдущем случае, необходимо получить доступ к контексту. Это делается при помощи метода getContext. В качестве типа контекста необходимо указать webgl либо experimental-webgl. Контекст, именуемый как «experimental-webgl» — это временное имя для контекста, используемое на время процесса разработки спецификации.

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

Сравнение технологий


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

Один из самых важных параметров, который учитывался при выборе технологии, была производительность. Этот параметр зависит от количества объектов и площади поверхности. В общем случае, по мере увеличения числа объектов на экране производительность SVG падает, поскольку объекты постепенно добавляются в модель DOM, тогда как производительность canvas почти не изменяется. WebGL имеет очень хорошую производительность, однако не все современные браузеры поддерживают данную технологию, к примеру IE начал поддерживать webGL начиная только с 11 версии. Поддержка WebGL на мобильных браузерах достаточно ограниченна, большинство мобильных браузеров начали поддерживать WebGL только с 2017 года. Некоторые мобильные браузеры на данный момент вообще не поддерживают данную технологию.

К тому же, помимо поддержки WebGL браузером, необходима также его поддержка графическим процессором клиента. Некоторые браузеры могут отключить поддержку WebGL на устройствах со старыми графическими процессорами. Подробнее об этих ограничениях можно узнать в этой статье.
По причине слабой поддержки технологии WebGL далее будут рассматриваться только две технологии: canvas 2d и svg.
image
Рис.1 Время рендеринга(y) в зависимости от количества объектов(х).

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

Однако canvas не очень хорошо подходит для экранов с высоким разрешением. Из графика ниже видно, что по мере увеличения размера экрана производительность Canvas падает, поскольку требуется обработать больше пикселей.
image
Рис.2 Время рендеринга(y) в зависимости от высоты области рисования(х).

Также необходимо учитывать, что при увеличении изображения, созданные при помощи canvas, сильно теряют качество. На рисунке ниже показаны линии, нарисованные при помощи svg и canvas.
image
Рис.3 Линии нарисованные при помощи SVG(сверху) и Canvas(снизу) при увеличении.

Еще одна проблема с которой можно столкнуться при работе с canvas — ограниченные возможности отслеживания событий и определения расположения указателя мыши на изображении. Существует встроенный API isPointInPath, который позволяет определить попадает ли указанная точка в последний нарисованный элемент path.* Существуют сторонние библиотеки, позволяющие достаточно комфортно работать с событиями.

Несмотря на перечисленные выше недостатки, было принято решение использовать именно canvas, в первую очередь из-за его высокой производительности по сравнению с svg при отрисовке большого количества объектов.

* Существует экспериментальный интерфейс Path2D, который можно использовать для создания пути, который в дальнейшем можно будет переиспользовать. К примеру этот объект можно передать в качестве первого аргумента метода isPointInPath. На данный момент эта технология поддерживается браузерами Firefox, Google Chrome и Opera.

Особенности работы с Canvas


При работе с сanvas в первую очередь необходимо учитывать то, что мы работаем с растровым изображением. В первую очередь это значит, то что при изменении размера области отрисовки (к примеру при изменении размеров окна) изображение на canvas масштабируется, что приводит к искажениям и потере качества. На рисунке ниже в первом случае css свойства width и height элемента canvas в два раза больше соответствующих атрибутов указанных изначально. Рядом изображен тот же график без масштабирования. Для избежания подобных проблем следует при изменении размеров окна выставлять необходимые значения для атрибутов width и height элемента canvas и перерисовывать текущую сцену с учетом новых размеров.

С похожей проблемой можно столкнуться при работе с экранами с увеличенной плотностью пикселей. Изображения на canvas могут выглядеть размытыми. Причиной этого является разница между разрешением дисплея в физических пикселях и разрешением в логических (CSS) пикселях. Отношение двух этих величин можно получить при помощи свойства window.devicePixelRatio. К примеру при devicePixelRatio равным двум ширина канваса в физических пикселях в 2 раза больше, чем его ширина в логических пикселях (указанная в атрибуте width). То есть мы получим тот же эффект, что и в первом случае. Один из возможных способов решения этой проблемы: указывать значения в атрибутах canvas большее чем в стилях в величину devicePixelRatio. Пример:
canvas.width = canvasWidth * window.devicePixelRatio;
canvas.height = canvasHeight * window.devicePixelRatio;
canvas.style.width = canvasWidth + 'px';
canvas.style.height = canvasHeight + 'px';

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

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

Один из способов это сделать — применять элементы canvas в несколько слоев. Это позволяет перерисовывать только те части сцены, которые необходимо, а не всю сцену целиком.
Пример: Расположение элементов canvas в 2 слоя:



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

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

Заключение


SVG хорошо подходит для создания статических изображений, высококачественных сложных векторных документов (к примеру чертежей, схем) интерактивных графиков. Но стоит учитывать, что при возрастании количества объектов, скорость отрисовки будет сильно снижаться. Использование элемента canvas позволяет достаточно быстро отрисовывать изображения на экране, именно поэтому эта технология хорошо подходит для отображения сложных сцен и анимации в режиме реального времени. Canvas может применяться для создания различных визуальных эффектов и сложных 3D сцен. Но при использовании данной технологии достаточно сложно сделать изображение или график интерактивным.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332750/


Метки:  

Первая российская материнская плата массового сегмента

Понедельник, 10 Июля 2017 г. 11:10 + в цитатник
Как мы уже писали, степень локализации отечественной серверной продукции массового сегмента выражена в большей степени в предоставлении различного рода услуг, нежели чем в производстве комплектующих. Комплектующие для серверов традиционно производятся в Китае, к какому бренду они не принадлежали бы. У одной российской компании была попытка создания полностью отечественной платформы, но продукт получился нишевой, так как платформа обладала характеристиками избыточными для классического применения серверов.
Но вот, в начале года мы получили образец серверной 2-процессорной материнской платы, разработанной и произведённой на территории России. Эта модель вызвала интерес в первую очередь из-за того, что по характеристикам и стоимости полностью подходила для массового применения. Обзору и тестированию первой российской материнской платы Rikor R-BD-E5R-V4-16.EA и посвящается эта статья.

Характеристики


  • CPU support: Intel Xeon E5-2600 V3/V4 series (135W maximum)
  • MB chipset: Intel Wellsburg C612PCH
  • Memory slots: 16 (each CPU can support 4 x DDR4 channel x 2 DDR4 DIMM)
  • Memory support: Maximum 1024GB DDR4 2133/1866/1600/1333 RDIMM/LRDIMM/NVDIMM
  • PCI-E slots: 6
  • PCH SATA ports: 10 x SATA3.0
  • HDD RAID feature: RAID 0, 1, 5, 10
  • Graphics: Aspeed AST2400
  • IPMI: Support for Intelligent Platform Management Interface v.2.0 with virtual media over
    LAN and KVM over LAN support ASPEED AST2400 BMC
  • Network: 5 x 1Gb LAN (1 x 1Gb LAN is BMC dedicated management port)
  • USB: 5 x USB 3.0 (1 x internal) + 4 x USB 2.0(internal)
  • DOM: Support SATA DOM power connector
  • Input/output interface: 1 x COM port, 1 x VGA port, 1 x dedicated IPMI port, 4 x 1000Mbps port, 4 x USB2.0 + 4 x USB3.0
  • Fan power supply socket: 8 x 4 pin smart FAN
  • Power supply interface: 1 x 24pin SSI power interface power supply interface + 2 x 8pin SSI 12Vpower supply interface


Фотографии






Немного крупных планов









Тестирование


Для тестирования собрали две аналогичные платформы на базе Rikor и Supermicro.

Стенд Rikor:
  • Rikor R-BD-E5R-V4-16.EA
  • 2 x Intel Xeon E5-2620V4
  • 8 x 8Gb ECC REG
  • 2 x 150Gb Enterprise SSD
  • Windows 2012 R2


Стенд Supermicro:
  • Supermicro X10DRi
  • 2 x Intel Xeon E5-2620V4
  • 8 x 8Gb ECC REG
  • 2 x 150Gb Enterprise SSD
  • Windows 2012 R2

Результаты тестирования PerformanceTest 9.0




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

Результаты тестирования CINEBENCH R15




Результаты тестирования 7ZIP




Результаты тестирования производительности отечественной платформы при рендеринге и архивировании имеют аналогичное отставание.

Заключение


Система на базе Rikor R-BD-E5R-V4-16.EA, не смотря на отставание в бенчмарках, показала высокую стабильность и надёжность при месячных нагрузочных тестах всех подсистем. Интерфейс IPMI материнской платы классический от Intel, такой можно увидеть и в материнских платах Asus, например. BIOS к осени планируется отечественный. Разработка и производство материнской платы (разумеется, за исключением компонентов), повторюсь, у нас — в России.
Помимо материнской платы, к нам на тесты попал и корпус 2U полностью российского производства. Всё (кроме БП), в плоть до корзин с горячей заменой, производится у нас в стране. Бэкплейны тоже. Т.к. корпус был совсем-совсем образцом, по нему появилось много нареканий, но недостатки уже были исправлены на момент написания статьи. Поэтому обзор корпуса будет чуть позже.
Спасибо за внимание, жду Ваших комментариев!
Хороший ли результат получился у Rikor?

Проголосовало 12 человек. Воздержалось 5 человек.

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

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/320798/


Анонс Гейзенбаг 2017 Moscow: удваиваем пользу

Понедельник, 10 Июля 2017 г. 10:40 + в цитатник


В 2016-м в Москве впервые прошла конференция по тестированию Гейзенбаг — тогда она длилась один день. В декабре конференция состоится в Москве уже во второй раз — и станет двухдневной. Если вспомнить выпуск xkcd про экстраполяцию, то получится, что Гейзенбаг-2380 будет длиться круглый год! А если серьёзнее, то мы готовы анонсировать новое мероприятие. Под катом подробно рассказываем, чего именно ждать 8-9 декабря.


Двухдневный формат


Переход к двухдневной конференции — это не просто «то же самое два дня подряд». Когда есть только один день и множество интересных докладчиков, хочется вместить в него столько докладов, сколько в принципе возможно. А когда всё не так жёстко ограничено во времени, появляется возможность «вдохнуть полной грудью» — например, сделать перерывы между докладами длиннее.

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

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

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



Темы


Здесь принципиальных изменений не планируется: это по-прежнему «конференция о тестировании, но не только для тестировщиков». Мы хотим приносить пользу разным специалистам, не строя стену между тестировщиками и разработчиками, а помогая им найти общий язык. Нас интересует тестирование во всех его проявлениях: хоть автоматизированное, хоть ручное, от мобильного до геймдева, от нюансов нагрузочного тестирования до общих советов, полезных всем (при этом постараемся как можно чётче указывать, кому подходит каждый доклад, чтобы вы не оказывались случайно на неподходящем для вас).

Единственное, чего мы не хотим — буллшита: бесполезных маркетинговых докладов, невнятных рассуждений ни о чём, разжёвывания всем известных азов. Мы за техническую конкретику.

Спикеры


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

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

Артём Ерошенко имеет прямое отношение к проекту Allure, и в июне на петербургском Гейзенбаге его доклад об Allure 2 оказался фаворитом зрителей. Но интересы Артёма не ограничиваются Allure — и выступление на московской конференции, вероятно, будет посвящено чему-то новому.

Илари Хенрик Эгертер выступал на обоих прошедших Гейзенбагах (московском и петербургском), оба его выступления понравились публике — неудивительно, что мы рады будем увидеть его в третий (и узнать, какой окажется длина его бороды в этот раз). Впрочем, мы получали и комментарии о том, что два его выступления слишком пересекались друг с другом. Учтём это: третье не окажется повторением двух первых.

Владимир Ситников — эксперт в вопросах производительности. Многие знают его как коммиттера инструмента для нагрузочного тестирования JMeter, но в декабре он расскажет о другом. Зачастую под словами «тестирование производительности» подразумевают только тестирование серверной части, а непосредственно работу браузера тестируют гораздо реже — Владимир намерен устранить несправедливость и раскрыть на Гейзенбаге эту тему.

Николай Алименков в июне на петербургском Гейзенбаге навёл шороху своей бурной дискуссией с Алексеем Виноградовым, а его доклад тогда получил от зрителей очень хорошие отзывы. В Москве в декабре он тоже будет очень активен: мало того, что выступит с докладом, так ещё и проведёт тренинг (об этом читайте ниже).

Алексей Лавренюк известен своей работой над проектом Яндекс.Танк, и неудивительно, что на петербургском Гейзенбаге рассказывал о нагрузочном тестировании. Но Алексей разбирается и в мобильном тестировании — так что не удивляйтесь, если тема его зимнего выступления в Москве будет сильно отличаться.

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

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


Дистанционный доклад


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





Видимо, талант рассказчика у него от отца: Джеймс — сын Ричарда Баха, известного повестью «Чайка по имени Джонатан Ливингстон». В общем, мы были бы ради видеть его на Гейзенбаге, но единственный вариант, в котором его выступление на конференции возможно — прямая онлайн-трансляция. Ранее мы не проводили выступления в таком формате и не знаем точно, насколько вам это интересно, поэтому хотим у вас это и узнать. Если у вас есть мнение по этому поводу — ткните в один из трёх вариантов по ссылке.

Тренинг Николая Алименкова


Связанное с конференцией событие: в следующие после неё два дня, 10-11 декабря, пройдёт тренинг Николая Алименкова «Секреты успешной автоматизации тестирования». Это отдельное мероприятие с отдельными билетами (их продажа начнётся позже) — но это удобная возможность, выделив четыре дня подряд, повысить свою квалификацию по полной, начав с конференции и продолжив тренингом.

Call for Papers




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

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

Главное требование: ваш доклад должен быть полезен другим разработчикам. Нам интересны следующие темы:
  • Автоматизация тестирования;
  • Нагрузочное тестирование;
  • Performance-тестирование, бенчмаркинг;
  • Интеграционное тестирование модульных/распределённых систем;
  • Concurrency testing;
  • Тестирование мобильных приложений;
  • Ручное тестирование;
  • UX, Security, A/B, статический анализ кода, метрики кода и продукта, etc.;
  • Инструментарий, окружение для тестирования;
  • Сравнение инструментов для тестирования;
  • Тестирование в gamedev.


Программа и регистрация


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

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332704/


[Перевод] Инструменты для разработчика Go: знакомимся с лейблами профайлера

Понедельник, 10 Июля 2017 г. 10:31 + в цитатник

DrawingПривет. Меня зовут Марко. Я системный программист в Badoo. Представляю вашему вниманию перевод поста замечательной rakyll о новой фиче в Go 1.9. Мне кажется, что лейблы будут очень полезны для профилирования ваших Go-программ. Мы в Badoo, например, используем аналогичную штуку для того, чтобы тегировать куски кода в наших программах на С. И если срабатывает таймер и в лог выводится стек-трейс, то в дополнение к нему мы выводим такой вот тег. В нем, например, может быть сказано, что мы обрабатывали фотографии пользователя с определенным UID. Это невероятно полезно, и я очень рад, что похожая возможность появилась и в Go.


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


Информация о полном стеке вызовов очень полезна, но этого не всегда достаточно для поиска проблемы в производительности. Большое количество Go-программистов используют Go для написания серверов, а понять, где находится проблема производительности в сервере, еще труднее. Сложно отделить одни ветки исполнения от других или сложно понять, когда только одна ветвь исполнения вызывает проблемы (какой-то пользователь или определенный хэндлер). Начиная c Go 1.9 у нас появилась возможность добавлять дополнительную информацию о контексте того, что происходит в данный момент, и использовать в дальнейшем эту информацию в профайлере, чтобы получить более изолированные данные.


Лейблы могут быть полезны во многих случаях. Вот только самые очевидные из них:


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

Добавление лейблов


В пакете runtime/pprof появятся несколько новых функций для добавления лейблов. Большинство пользователей будут использовать функцию Do, которая берет контекст, добавляет в него лейблы и передает новый контекст в функцию f:


func Do(ctx context.Context, labels LabelSet, f func(context.Context))

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


labels := pprof.Labels("worker", "purge")
pprof.Do(ctx, labels, func(ctx context.Context) {
    // Делаем какую-либо работу...

    go update(ctx) // пробрасывает лейблы дальше
})

Работа выше будет помечена лейблом worker:purge.


Смотрим на вывод профайлера


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


Я буду использовать пакет net/http/pprof в этом примере. Смотрите статью Profiling Go programs, если вам интересны подробности.


package main

import _ "net/http/pprof"

func main() {
    // Много кода...
    log.Fatal(http.ListenAndServe("localhost:5555", nil))
}

Соберем данные по использованию CPU...


$ go tool pprof http://localhost:5555/debug/pprof/profile

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


(pprof) tags
http-path: Total 80
        70 (87.50%): /messages
        10 (12.50%): /user

worker: Total 158
       158 (  100%): purge

Как видите, тут два ключа (http-path, worker) и несколько значений для каждого. Ключ http-path указывает на HTTP-хендлеры, а worker:purge указывает на код из примера выше.


Фильтруя по лейблам, мы можем сфокусироваться, например, только на коде из хендлера /user.


(pprof) tagfocus="http-path:/user"
(pprof) top10 -cum
Showing nodes accounting for 0.10s, 3.05% of 3.28s total
      flat  flat%   sum%        cum   cum%
         0     0%     0%      0.10s  3.05%  main.generateID.func1 /Users/jbd/src/hello/main.go
     0.01s   0.3%   0.3%      0.08s  2.44%  runtime.concatstring2 /Users/jbd/go/src/runtime/string.go
     0.06s  1.83%  2.13%      0.07s  2.13%  runtime.concatstrings /Users/jbd/go/src/runtime/string.go
     0.01s   0.3%  2.44%      0.02s  0.61%  runtime.mallocgc /Users/jbd/go/src/runtime/malloc.go
         0     0%  2.44%      0.02s  0.61%  runtime.slicebytetostring /Users/jbd/go/src/runtime/string.go
         0     0%  2.44%      0.02s  0.61%  strconv.FormatInt /Users/jbd/go/src/strconv/itoa.go
         0     0%  2.44%      0.02s  0.61%  strconv.Itoa /Users/jbd/go/src/strconv/itoa.go
         0     0%  2.44%      0.02s  0.61%  strconv.formatBits /Users/jbd/go/src/strconv/itoa.go
     0.01s   0.3%  2.74%      0.01s   0.3%  runtime.memmove /Users/jbd/go/src/runtime/memmove_amd64.s
     0.01s   0.3%  3.05%      0.01s   0.3%  runtime.nextFreeFast /Users/jbd/go/src/runtime/malloc.go

Этот вывод содержит только сэмплы, отмеченные лейблом http-path:/user. И вот в таком выводе мы с легкостью можем понять, где самые нагруженные места /user-хэндлера.


Также вы можете попробовать команды tagshow, taghide и tagignore для дополнительного фильтрования. Например, tagignore дает вам возможность получить данные по всем лейблам, кроме заданного. Фильтр ниже выдаст все, кроме /user хэндлера. В этом случае это worker:purge и http-path:/messages.


(pprof) tagfocus=
(pprof) tagignore="http-path:/user"
(pprof) tags
http-path: Total 70
        70 (  100%): /messages

worker: Total 158
       158 (  100%): purge

Если вы попробуете визуализировать отфильтрованные данные, вывод покажет сколько каждый лейбл привносит к полной “стоимости”.



Тут видно что worker:purge привнес 0.07s, а хэндлер messages 0.03s в функции generateID.


Попробуйте сами!


Лейблы позволяют вам добавить дополнительную информацию к профайлеру, которая не доступна из простого стека вызовов. Попробуйте, скачав Go 1.9 beta, если вам нужны дополнительные размерности в вашем профайлере. Также попробуйте пакет pprofutil, чтобы автоматически обернуть ваши HTTP-пути лейблами.


На текущий момент доступна версия Go 1.9 beta 2. Известных багов в ней нет, но команда разработчиков просит попробовать ее на своих программах, а при возникновении проблем сообщить в баг-трекер. Я же хочу сказать, что собрать Go и быть на острие разработки очень и очень просто. Сама же сборка занимает не больше минуты. Дерзайте!

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332636/


Метки:  

[Перевод] Как создать свою метроидванию

Понедельник, 10 Июля 2017 г. 10:03 + в цитатник


Метроидвания: стиль игры (2D или 3D), в котором часто присутствует исследование и где управляемый игроком персонаж получает новые способности, позволяющие ему продвигаться дальше. Такие игры часто являются экшн-адвенчурами с пересечением маршрутов движения. В них используется система «роста», открывающая важные апгрейды, необходимые для прохождения игры. Название «метроидвания» произошло от смешения слов Metroid и Castlevania, однако существуют и другие игры, в которых используется та же фундаментальная философия дизайна. К этим играм можно отнести все игры франшизы Zelda, Shadow Complex и Cave Story. На самом деле нет значительной разницы в структуре игрового процесса между играми Zelda, Metroid или Castlevania: Symphony of the Night.

Эта статья предназначена для создателей игр, желающих взяться за трудную задачу написания игры в жанре «метроидвания». Если вы никогда раньше не делали игр, и ищете советов о том, как начать создавать игры, рекомендую познакомиться с такими инструментами, как Unity, Unreal Engine, Construct 2 или любыми другими инструментами игростроительства. Вам сначала стоит научиться делать игры, потому что я не буду учить их созданию с нуля.

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



Основы метроидвании


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

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

1. Планирование геймплейных систем




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

А. Замысел/уникальность

Определитесь с замыслом игры и с тем, что делает её уникальной.

  • 2D или 3D?
  • Как работает камера? (Камера со скроллингом, над головой персонажа, управляемая игроком, с видом от первого лица, от третьего лица, перемещающаяся по кривой, управляемая скриптом, статичная, и т.д.)
  • Чем управляет игрок? (Персонажем, машиной, объектом и т.д.)
  • Каким будет стиль игры? (Sci-fi, фэнтези, реалистичный, мультяшный, гротескный, уникальный)
  • Что главное в вашей игре и что выделяет её из толпы? (Механика, задумка, внешний вид, история, персонажи) (вы можете сделать или что-то уникальное и запоминающееся, или то, что уже создано, но реализовать это качественней)

Б. Свойства игрока

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

  • Как двигается персонаж? (Летает, ходит, катается, скользит, и т.д.)
  • Что делает персонаж для взаимодействия с миром? (Прыгает, стреляет, рубит, хватает, целует, пукает и т.д.) (Взаимодействия в игре должны соответствовать её замыслу. Такие стандартные взаимодействия определяют персонаж в игре. Они могут быть новыми или производными.)

В. Способности, оружие, инструменты, оборудование

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

  • Сколькими уникальными способами могут усовершенствоваться способности к движению персонажа? (Может ли он прыгать выше, летать, ускоряться, бегать, ползать, катиться, скользить, раскачиваться, вращаться, плавать и т.д.?) (Все свойства движения требуют ограничений и уникальных механик. Нельзя, чтобы одно обесценивало другое.)
  • Сколькими уникальными способами может преображаться персонаж? (Меняет ли он форму, становится ли другим персонажем, изменяет своё состояние и т.д.) (Транформации могут полностью менять свойства движения персонажа, коллизии и его уязвимости.) (Все трансформации требуют ограничений и уникальных механик. Не стоит делать так, чтобы одна обесценивала другие.)
  • Сколько есть уникальных способов расширения функций взаимодействия персонажа? (Оружие, снаряды, магия, типы стихийного урона, контакт, особые свойства и т.д.) (Все взаимодействия требуют ограничений и уникальных механик. Нельзя, чтобы одно обесценивало другое.)
  • Если персонаж использует снаряды, то сколькими разными способами они могут двигаться? (Летят ли они по дуге или прямо, исчезают ли со временем, выстреливаются быстро, прилетают из-за края экрана, летят зигзагом, отскакивают, отражаются, останавливают объекты, взрываются и т.д.)
  • Сколькими уникальными способами видит мир персонаж и как враги видят персонажа? (Визоры, опции видимости, невидимость, освещение, измерения, секреты и т.д.) (Все опции видимости требуют ограничений и уникальных механик. Нельзя, чтобы одна обесценивала другую.)
  • Сколько уникальных опасностей есть в игре и может ли игрок получить что-то, защищающее его от этих опасностей? (Костюмы, заклинания, броня, форма, шляпы и т.д.) (Чтобы защититься или снизить урон от таких угроз, как огонь, лава, вода, враги, электричество, космос и т.д.)
  • Сколько особых способностей, изменяющих мир, может получить или использовать персонаж? (Остановка времени, изменение гравитации, телепортация, быстрое перемещение, активация и т.д.)
  • Есть ли у игрока здоровье, нужны ли ему топливо или боеприпасы, увеличивающиеся или уменьшающиеся со временем? (С каким количеством игрок начинает игру и какой максимум он может получить?)

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

2. Реализация систем




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

У вас должен быть полный список способностей, похожий на приведённый ниже.

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

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

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

Б. Стандарты способностей — вам нужны стандарты построения уровней, показывающие, какой дальности могут достигнуть способности при использовании. Это поможет в дизайне уровней и настройке. (Пример: максимальная высота прыжка по умолчанию: 2 метра, для высокого прыжка: 5 метров) (Иллюстрации и схемы помогают в планировании)

  • Сначала создайте заготовку основного персонажа и настройте базовые свойства движения персонажа. (Вам не нужна графика или анимация, достаточно прямоугольника для отображения коллизий.) Создайте небольшой тестовый уровень, достаточно близкий к настоящему игровому пространству, чтобы добиться правильных параметров. Он пока некрасив, но, по-крайней мере, в него можно поиграть и прочувствовать. Иногда даже хорошо, что он уродлив. (Постройте сетку, не нужно измерять всё «на глазок».)
  • Затем нужно реализовать все способности, изменяющие способы перемещения персонажа в мире. Это самая важная для завершения часть, потому что она будет определять все ограничения, которые нужно учитывать при создании уровней и их содержимого.
  • Реализовав и настроив все изменяющие движение способности и добившись их хорошей взаимной работы, можно начать набрасывать простую последовательность уровней или комнат.

(На этом этапе ещё нет никакого взаимодействия с врагами, но всё равно, стоит начать работу над способностями взаимодействия персонажа. Они будут использоваться и для взаимодействия с миром.)

3. Тестовая последовательность прохождения




Механика реализована, поэтому теперь нужно наметить расширенную тестовую среду для ответа на некоторые вопросы. Стоит создать ресурсы для уровней, чтобы они имели стандартизированный вид. (Используйте одинаковые размеры и стройте их по сетке.) Таким образом вы сможете перемещать ресурсы уровней и менять порядок, в котором они соединяются.

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

Каждая комната выполняет следующие цели.

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


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

Эти комнаты должны быть самодостаточным игровым процессом. Это не должны быть большие комнаты и им необязательно красиво выглядеть. Всю необходимую игрокам информацию нужно немедленно показать камерой по умолчанию. Она должна показать наиболее стандартное использование способности, которую получил игрок. Комната и ситуация должны быть простыми и понятными. Единственной интересной частью в комнате должна быть та, в которой нужно применить новую способность. (НА ЭТОМ ЭТАПЕ НЕ СТРЕМИТЕСЬ СДЕЛАТЬ СТРУКТУРУ УРОВНЯ ИНТЕРЕСНОЙ ИЛИ СЛОЖНОЙ! Это может отвлечь вас от ваших целей в фазе разработки.)

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


Начало — тестирование бега — тестирование высокого прыжка — тестирование превращения в кошку — тестирование обратной гравитации — конец

4. Тестирование последовательности




Теперь, когда вы можете протестировать последовательность, вы начнёте видеть какой будет игра в сжатом виде. (Вы поймёте ощущения от персонажа в любой момент его «роста».)

  • Может ли игрок завершить всю последовательность и получить все способности
  • Ощущается ли правильным порядок, в котором игрок получает новые способности? Может быть, что-то выбивается из ряда?
  • Где можно остановить прохождение игры, пока игрок не получит новую возможность?
  • Каковы ощущения от способности, когда её используют в созданной вами комнате?
  • Путается ли игрок в том, как или где использовать способность в последовательности?
  • Удалось ли игрокам повернуть назад или пройти последовательность неправильно?

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

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

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

5. Тестирование взаимодействий с миром, объектами и врагами




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

Скорее всего, у вас будут:

  • Двери — они разделяют области и являются отличным способом блокировки областей, пока игрок не получит нужную способность или предмет. (Особое оружие, ключ, костюм, предметы и т.д., которые нужны для разблокировки.) Создайте в вашем тестовом окружении все типы дверей! Попробуйте использовать единый стандартный размер дверей с разным внешним видом, показывающим, что нужно сделать игроку, чтобы открыть их. Помните, что двери могут:
    • Открываться и закрываться несколько раз
    • Закрываться за игроком, чтобы заставить его справиться с ситуацией
    • Разблокироваться при решении головоломки или убийстве врага.
    • Требовать для открытия способности, оружия или ключа.
    • Использоваться для маскировки загрузки уровня или помогать при переходе между областями
  • Разрушающиеся объекты — такие объекты могут ломаться, растворяться или пропадать, чтобы игроки проходили дальше или находили скрытые предметы.
  • Препятствия — эти объекты могут ранить игрока. Он должен уметь проходить через них с помощью полученных способностей. Каждое должно служить своей цели и настроено таким образом, чтобы игрок мог с лёгкостью его преодолеть после получения нужного инструмента.
  • Устройства/подвижные объекты — это предметы, требующие для активации определённых способностей. Они перемещают игрока из одной точки в другую и их нужно обязательно использовать для попадания в новую локацию. (Канаты, лифты, подвижные платформы, шестерёнки, поршни, источники питания и т.д.)

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

6. Враги




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

Враги нужны для поддержания чувства роста персонажа. Они являются важной частью уравнения.

7. Завершение последовательности тестирования способностей




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

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

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

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


Начало — тест бега — тест высокого прыжка — тест стрельбы снарядами — тест ядовитого костюма — тест превращения в кошку — тест ледяной атаки — тест ночного видения — тест обратной гравитации — конец

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

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

Теперь у вас есть список всех завершённых способностей и порядок их открытия игроком для прохождения игры. Готовый список прохождения может выглядеть так:

  1. Бег
  2. Высокий прыжок
  3. Стрельба
  4. Ядовитый костюм
  5. Превращение в кошку
  6. Ледяная атака
  7. Ночное видение
  8. Обратная гравитация


Пример дизайна уровня


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


Прохождение базового цикла взаимодействий

Это изображение — пример того, как планирования уровни для игры в стиле «метроидвания». Из легенды понятно, что означают символы на карте. Эта схема подходит для сайдскроллера, игры с камерой над головой или даже полностью трёхмерной игры.

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

1. Начало
Игроки попадают в этот мир там, где расположена пометка START.

2. Двери
Стандартные точки входа — это двери, которые игроки могут открывать с помощью способностей, имеющихся на текущем этапе игры. Они помечены зелёным.

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

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

5. Исследование комнаты A1
Исследование комнаты A1 может вознаградить игроков нахождением секрета. Он может быть важным или тривиальным, в зависимости от того, что вы можете предложить игроку.

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

7. Вход в комнату A2 (в ней требуется способность, которой у игрока пока нет)
A2 — это комната, служащая одной цели. Эта комната гарантирует, что игроки сразу же увидят область, в которой нужно использовать способность, пока отсутствующую у игрока. Это барьер, не позволяющий игроку пройти дальше. Я пометил его красным. Эта комната ведёт игроков так, что они обязательно увидят этот барьер. Он непременно должен выделяться. Нужно оформить его таким образом, чтобы он запечатлелся в памяти игроков. Это должна быть единственная важная, привлекающая внимание часть комнаты. (Для выделения этого барьера у вас есть разные инструменты. Можно использовать освещение, чтобы подчеркнуть важность области. Можно разместить рядом примечательный объект (статую или символ). Можно также придать комнате такую форму, чтобы расположение барьера было очевидным. Сделайте всё необходимое, чтобы игрок заметил точку, ведущую в недоступную ему пока область.)

8. Выход из комнаты A2
Увидев барьер в комнате A2, игроки продолжают путь к единственному доступному новому выходу. Здесь они могут увидеть ещё один барьер, но его выделять не следует. (Позже вы поймёте, почему второй барьер поставлен именно здесь.)

9. Вход в комнату A3 (головоломка или область для исследования)
Игроки наконец попадают в A3. Её можно сделать головоломкой или областью исследования. Игроки очевидным образом решают головоломку, чтобы получить доступ к A4. Если игроки умные, они могут додуматься, как добраться до дополнительной комнаты B1 (Optional B1). Маршрут через неё необязателен, но его можно сделать ценным, разместив в B1 секрет.

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

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

12. Вход в комнату A5 (дразнящая будущая способность + возможность пополнить боеприпасы/нафармить ресурсов)
A5 — это последняя область перед битвой с боссом. Схема этой комнаты служит двум целям. Первое: она дразнит игрока использованием будущей способности. Это даст ему мотивацию вернуться в этот мир, получив способность позже в игре. Второе: она создаёт небольшую сложность перед встречей с боссом. Также она позволяет пополнить боеприпасы или ресурсы, нафармив их прямо перед боссом. (В этой комнате должен быть секрет, которого игроки пока не могут достичь. Так вы можете добавить в игру то, что не запланировали раньше. Таким секретом может быть увеличение боезапаса, расширение возможностей или даже путь назад на этот уровень из другого мира.)

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

14. Вход в комнату с боссом
Если в вашей игре есть боссы, то именно в этом месте стоит устроить встречу с одним из них. Если в игре нет боссов, то просто сделайте эту комнату областью получения новой способности. Победив босса, игрок получает хорошо оформленную «особую вещь».

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

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

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

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

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

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

В заключение


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

Об авторе: Кинан Пирсон в качестве лида работал над такими играми, как Halo 4/5, Metroid Prime 2/3, Donkey Kong Country Returns.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332588/


Метки:  

[Из песочницы] STM32 + PPP (GSM) + LwIP

Понедельник, 10 Июля 2017 г. 08:55 + в цитатник
Большинство GSM модулей работают по интерфейсу UART, посредством AT-команд. Но для серьезных проектов, использование AT команд несет определенные трудности:

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

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

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

Пример PPP-пакета:



Каждый пакет PPP начинается и заканчивается символом ~ (0x7E). Протокол поддерживает аутентификацию соединения, шифрование и сжатие данных, по этому довольно сложен для написания собственного решения. Логичнее использовать готовый стек, поддерживающий PPP, например LwIP. Он поддерживает PPPOS и PPPOE (Over serial и Ethernet), протоколы аутентификации PAP и CHAP, имеет хорошую репутацию и широко распространен.

Демо проект

Блок-схема:


Примеры разрабатывались для микроконтроллера STM32 под FreeRTOS.

Старт программы, настройка периферии, создание задач
int main(void) {
     HAL_Init();
     SystemClock_Config();
     MX_GPIO_Init();
     MX_DMA_Init();
     // Настройка gsm, периферии модуля и LwIP
     InitGsmUart();
     // Создание задачи по приему-отправке пакета и установки соединения
     xTaskCreate(StartThread, "Start", configMINIMAL_STACK_SIZE*2, 0, tskIDLE_PRIORITY+1, &taskInitHandle);
     // Старт процессов
     osKernelStart(NULL, NULL);
     while (1) {}
}

void StartThread(void * argument) {
     gsmTaskInit();
     // подлючение, отправка/прием данных
     xTaskCreate(connectTask, "connectTask", configMINIMAL_STACK_SIZE*1, 0, tskIDLE_PRIORITY+1, NULL);
     // После удаляем задачу
     vTaskDelete(NULL);
}


«Верхний» уровень. Установка соединения, прием данных и зеркальная отправка
// Кол-во соединений. В этом примере 1, может быть больше, ограничено размером RAM контроллера и возможностями LwIP (последний легко настраивается)
#define GSM_MAX_CONNECTION	1
// Структура для работы с данными
typedef struct{
     uint8_t *rxBuff;
     uint16_t rxLen;
}sBuff[GSM_MAX_CONNECTION];

sBuff buff = {0};
	
void connectTask(void *pServiceNum) {
     bool connectState = false;
     eRetComm status = eError;
     uint16_t delay = 0;
     uint8_t serviceNum = *(uint8_t*)pServiceNum;
     xSemaphoreHandle xRxPppData; // семафор на прием от ppp
     xRxPppData = GsmLLR_GetRxSemphorePoint(serviceNum);
	
     for(;;) {
     /* Основной код задачи */
     if(connectState == true) {
     //Если есть подключение к серверу
     while(GsmLLR_ConnectServiceStatus(serviceNum) == eOk) {
          //Читаем данные в буфер	
          buff[serviceNum].rxLen = getRxData(serviceNum, xRxPppData,&(buff[serviceNum].rxBuff));
          if(buff[serviceNum].rxLen != 0) {
                    //Если пришли данные то отправляем обратно
                    if(GsmLLR_TcpSend(serviceNum, buff[serviceNum].rxBuff, 
buff[serviceNum].rxLen) == eOk) {
                    printf("Connect:#%i SendData OK\r\n", serviceNum);
          }else{
                    printf("Connect:#%i SendData ERROR\r\n", serviceNum);
                    connectState = false;	          
                    }
          }
     }
     //Если разрыв соединения
     printf("Connect:#%i connection lost\r\n", serviceNum);
     GsmLLR_DisconnectService(serviceNum);
     connectState = false;	
     delay = 1000;
     } else {
     // соединение закрыто, настраиваем
     printf("Connect:#%i connecting...", serviceNum);
     // Устанавливаем соединение
     if(GsmLLR_ConnectService(serviceNum) == eOk) {
          printf("Connect:#%i connected", serviceNum);
          connectState = true;		
     } else { // не получилось сконнектиться
          printf("Connect:#%i ERROR", serviceNum);				
          delay = GSM_CONNECTION_ERROR_DELAY;
          connectState = false;
          }
     }
     vTaskDelay(delay/portTICK_RATE_MS);
     }
}
// Прием данных
uint16_t getRxData(uint8_t serviceNum, xSemaphoreHandle xRxPppData, uint8_t **ppBufPacket) {
     uint16_t retLen = 0;
     uint16_t size = 0;
     if(xSemaphoreTake(xRxPppData, 1000/portTICK_PERIOD_MS) == pdTRUE) {
          size = gsmLLR_TcpGetRxCount(serviceNum);
          if(size > 1512) {
           retLen = 0;
          }else {
          retLen = GsmLLR_TcpReadData(serviceNum, ppBufPacket, size);
          }
     }
return retLen;
}


Настройка GSM, подробно
void gsmTaskInit(void) {
     xTaskCreate(vGsmTask, "GSM", configMINIMAL_STACK_SIZE*2, 0, tskIDLE_PRIORITY+1, &gsmInitTaskId);
     while((!gsmState.init) || (!pppIsOpen)) {vTaskDelay(100/portTICK_PERIOD_MS);}
}

/* Задача инициализации и управления GSM модулем */
void vGsmTask( void * pvParameters ) {
     // никзкоуровневые инициализации
     GsmLLR_Init();
     GsmLLR2_Init();
     GsmPPP_Init();
     // пока переферия не готова
     while((gsmState.initLLR != true) && (gsmState.initLLR2 != true)){};
     if(GsmLLR_PowerUp() != eOk) {
          GsmLLR_ModuleLost();
     }
     for(;;) {		
          // инициализация
          if(gsmState.init == false) {
          // если модуль перестал отвечать
          if(gsmState.notRespond == true) {
               printf("GSM: INIT Module lost\r\n");
               GsmLLR_ModuleLost();
               continue;
          }
          // готовность модуля
          if(GsmLLR_ATAT() != eOk) {
               gsmState.notRespond = true;
               continue;
          }
          // отключение предупреждений по питанию
          if(GsmLLR_WarningOff() != eOk) {
               gsmState.notRespond = true;
               continue;
          }
          // настройки ответа
          if(GsmLLR_FlowControl() != eOk) {
               gsmState.notRespond = true;
               continue;
          }
          // читаем IMEI
          if(GsmLLR_GetIMEI(aIMEI) != eOk) {
               gsmState.notRespond = true;
               continue;
          }
          DBGInfo("GSM: module IMEI=%s\r\n", aIMEI);
          // читаем IMSI
          if(GsmLLR_GetIMSI(aIMSI) != eOk) {
               gsmState.notRespond = true;
               continue;
          }			
          printf("GSM: module IMSI=%s\r\n", aIMSI);
          // Версия Software
          if(GsmLLR_GetModuleSoftWareVersion(aVerionSoftware) != eOk) {
               gsmState.notRespond = true;
               continue;
          }
          // вывод сообщения о регистрации сети (URC)
          if(GsmLLR_AtCREG() != eOk) {
               gsmState.notRespond = true;
               continue;
          }
          printf("GSM: CREG OK\r\n");
          // читаем уровень сигнала
          if(GsmLLR_UpdateCSQ(&gsmCsqValue) != eOk) {
               printf("GSM: Get CSQ ERROR, -RELOAD\r\n");
               gsmState.notRespond = true;
               continue;
          }else{
               printf("GSM: CSQ value %d\r\n", gsmCsqValue);
               // формат SMS
               if(GsmLLR_SmsModeSelect(sms_TEXT) != eOk) {
                    gsmState.notRespond = true;
                    continue;
               }
               //удаляем sms
               vTaskDelay(DELAY_REPLY_INIT/portTICK_RATE_MS);
               if(GsmLLR_SmsClearAll() != eOk) {
                    printf("GSM: clear SMS ERROR, -RELOAD\r\n");
                    gsmState.notRespond = true;
                    continue;
               }
		printf("GSM: Clear SMS Ok\r\n");
			
               printf("GSM: INIT PPPP\r\n");
		if(GsmLLR_StartPPP(&connectionSettings.gsmSettings) == eOk) {
                    printf("GSM: INIT PPPP - PPP RUN\r\n");
                    xQueueReset(uartParcerStruct.uart.rxQueue);
                    uartParcerStruct.ppp.pppModeEnable = true;
                    uartParcerStruct.uart.receiveState = true;
                    gsmState.init = true;
               }else{
                    printf("GSM: INIT PPPP - PPP ERROR!!!\r\n");
                    gsmState.notRespond = true;
                    continue;
               }
          }
     }
     vTaskDelay(1000/portTICK_RATE_MS);
     }
}


Поднятие PPP.

Для начала сессии используется 4 команды — comPPP_0-4. Как они отправляются и разбирается ответ мы не рассматриваем, это тема для отдельной статьи. Рассмотрим лишь в общем виде:

Скрытый текст
char *comPPP_0[] = {"AT+CGDCONT=1,\"IP\","};
char *comPPP_2[] = {"AT+CGQMIN=1,0,0,0,0,0"};
char *comPPP_3[] = {"AT+CGQREQ=1,2,4,3,6,31"};
char *comPPP_4[] = {"ATD*99***1#"};

eRetComm GsmLLR_StartPPP(sGsmSettings *pSettings) {
	printf("StartPPP\r\n");
	sResultCommand resultCommand;
	char **comPPP_Mass[3] = {comPPP_2, comPPP_3, comPPP_4};
	uint8_t *pData = NULL;
	if(GsmLLR_GetMutex() == true) {
		pData = pvPortMalloc(GSM_MALLOC_COMMAND_SIZE);
		if(pData != NULL) {
			memset(pData, 0, GSM_MALLOC_COMMAND_SIZE);
			sprintf((char*)pData, "%s%s", comPPP_0[0], (char*)pSettings->gprsApn);
			RunAtCommand((char*)pData, &resultCommand);
			// Счетчик команд, пока не отправили все
			uint8_t stepIndex = 0;
			while(stepIndex != (3)) {
				uint16_t len = strlen((char*)*comPPP_Mass[stepIndex]);
				sprintf((char*)pData, "%s", (char*)*comPPP_Mass[stepIndex]);
				RunAtCommand((char*)pData, &resultCommand);
				stepIndex++;
			}
			memset(pData, 0, GSM_MALLOC_COMMAND_SIZE);
			vPortFree(pData);
		}
		GsmLLR_GiveMutex();
	}
	return eOk;
}


Из кода задачи vGsmTask, следуют, что в случае успешного выполнения «GsmLLR_StartPPP» — выставляется флаг pppModeEnable и очищается очередь uartParcerStruct.uart.rxQueue. Флаг pppModeEnable отображает текущий режим модуля. Обмен между прерыванием UART и стеком/разборщиком команд — идет через очередь.

Задача сессия PPP на уровне GSM
void GsmPPP_Tsk(void *pvParamter) {
	int timeout = 0;
	uint8_t i;
	bool stateInit = false;
	uint16_t tskStackInit;
	LwipStack_Init();
	pppInit();
	pppSetAuth(PPPAUTHTYPE_CHAP, connectionSettings.gsmSettings.gprsUser, connectionSettings.gsmSettings.gprsPass);
	sioWriteSemaphore = xSemaphoreCreateBinary();
	for(i=0; i/ Если выставлен флаг о использовании PPP и все было настроено
		if(uartParcerStruct.ppp.pppModeEnable == true) {
			if(!pppIsOpen) {
				pppNumport = pppOverSerialOpen(0, linkStatusCB, &pppIsOpen);
				pppStop = 0;
				timeout = 0;
				stateInit = false;
				while(timeout < 300) {
					if(pppIsOpen) {
						printf("PPP init - OK\r\n");
						lwip_stats.link.drop = 0;
						lwip_stats.link.chkerr = 0;
						lwip_stats.link.err = 0;
						stateInit = true;
						break;
					}else{
						timeout ++;
						vTaskDelay(100/portTICK_RATE_MS);
					}
				}
				if(stateInit != true) {
					printf("PPP init - TIMEOUT-ERROR\r\n");
					pppClose(pppNumport);
					pppIsOpen = false;
					uartParcerStruct.ppp.pppModeEnable = false;
					gsmState.init = false;
					gsmState.notRespond = true;
				}
			}else{
				if((lwip_stats.link.drop !=0) || (lwip_stats.link.chkerr !=0)) {
					lwip_stats.link.drop = 0;
					lwip_stats.link.chkerr = 0;
					printf("GSMM: DROPING FAIL!!! RESTART PPP\r\n");
					for(i=0; iportTICK_PERIOD_MS);
				}
			}
		}
		vTaskDelay(500/portTICK_RATE_MS);
	}
}


Общие функции, работающие с PPP

Connect-Disconnect, чтение статуса соединения, отправка и т.п.
bool GsmPPP_Connect(uint8_t numConnect, char *pDestAddr, uint16_t port) {	
	struct ip_addr resolved = {0};
	bool useDns = false;
	uint8_t ipCut[4] = {0};
	
	if(!pppIsOpen) {
		printf("GSMPPP: CONNECT ERROR - PPP closed\r\n");
		return false;
	}
	sscanf(pDestAddr, "%i.%i.%i.%i", &ipCut[0], &ipCut[1], &ipCut[2], &ipCut[3]);	
	if((ipCut[0]!=0)&&(ipCut[1]!=0)&&(ipCut[2]!=0)&&(ipCut[3]!=0)) {
		IP4_ADDR(&connectionPppStruct.ipRemoteAddr[numConnect], ipCut[0],ipCut[1],ipCut[2],ipCut[3]); //31,10,4,158);
		useDns = false;
	}else{
		useDns = true;
	}
	
	if(connectionPppStruct.connected[numConnect] == false) {
		connectionPppStruct.tcpClient[numConnect] = tcp_new();	// create tcpPcb	
		tcp_recv(connectionPppStruct.tcpClient[numConnect], server_recv);
		
		if(useDns == true) {
			switch(dns_gethostbyname(pDestAddr, &resolved, destServerFound, &numConnect)) {
			case ERR_OK: // numeric or cached, returned in resolved
				connectionPppStruct.ipRemoteAddr[numConnect].addr = resolved.addr;
				break;
			case ERR_INPROGRESS: // need to ask, will return data via callback
				if(xSemaphoreTake(connectionPppStruct.semphr[numConnect], 10000/portTICK_PERIOD_MS) != pdTRUE) {
					while(tcp_close(connectionPppStruct.tcpClient[numConnect]) != ERR_OK) { vTaskDelay(100/portTICK_PERIOD_MS); }
					connectionPppStruct.connected[numConnect] = false;
					printf("GSMPPP: dns-ERROR\r\n");
				return false;
				}else{ }
				break;
			}
		}
		tcp_connect(connectionPppStruct.tcpClient[numConnect], &connectionPppStruct.ipRemoteAddr[numConnect], port, &TcpConnectedCallBack);
		if(xSemaphoreTake(connectionPppStruct.semphr[numConnect], 10000/portTICK_PERIOD_MS) == pdTRUE) {
			connectionPppStruct.connected[numConnect] = true;
			printf("GSMPPP: connected %s\r\n", inet_ntoa(connectionPppStruct.ipRemoteAddr));
			return true;
		}else{
tcp_abort(connectionPppStruct.tcpClient[numConnect]);//tcp_close(connectionPppStruct.tcpClient[numConnect]);
			while(tcp_close(connectionPppStruct.tcpClient[numConnect]) != ERR_OK) { vTaskDelay(100/portTICK_PERIOD_MS); }
			printf("GSMPPP: connectTimeout-ERROR\r\n");
			return false;
		}		
	}else{
		if(GsmLLR_ConnectServiceStatus(numConnect) == eOk) {
			printf("GSMPPP: CONNECT-already connected %s\r\n", inet_ntoa(connectionPppStruct.ipRemoteAddr));
			return true;
		}else{
			printf("GSMPPP: CONNECT CLOSE!!!\r\n");
			return false;
		}
	}
	return false;
}

bool GsmPPP_Disconnect(uint8_t numConnect) {
	if(!pppIsOpen) {
		printf("GSMPPP: CONNECT ERROR - PPP closed\r\n");
		return false;
	}
	if(connectionPppStruct.tcpClient[numConnect] == NULL) {
		return false;
	}
	while(tcp_close(connectionPppStruct.tcpClient[numConnect]) != ERR_OK) { vTaskDelay(100/portTICK_PERIOD_MS); }
	connectionPppStruct.connected[numConnect] = false;
	return true;
}

bool GsmPPP_ConnectStatus(uint8_t numConnect) {
	if(!pppIsOpen) {
		printf("GSMPPP: CONNECT ERROR - PPP closed\r\n");
		return false;
	}
	if(connectionPppStruct.tcpClient[numConnect]->state == ESTABLISHED) {
		return true;
	}
	return false;
}

bool GsmPPP_SendData(uint8_t numConnect, uint8_t *pData, uint16_t len) {
	if(!pppIsOpen) {
		printf("GSMPPP: CONNECT ERROR - PPP closed\r\n");
		return false;
	}
	if(tcp_write(connectionPppStruct.tcpClient[numConnect], pData, len, NULL) == ERR_OK) {
		return true;
	}else {
		while(tcp_close(connectionPppStruct.tcpClient[numConnect]) != ERR_OK) { vTaskDelay(100/portTICK_PERIOD_MS); }
		connectionPppStruct.connected[numConnect] = false;
		connectionPppStruct.rxData[numConnect].rxBufferLen = 0;
		memset(connectionPppStruct.rxData[numConnect].rxBuffer,0, sizeof(connectionPppStruct.rxData[numConnect].rxBuffer));
	}
	return false;
}

uint16_t GsmPPP_GetRxLenData(uint8_t numConnect) {
	if(!pppIsOpen) {
		printf("GSMPPP: CONNECT ERROR - PPP closed\r\n");
		return false;
	}
	return connectionPppStruct.rxData[numConnect].rxBufferLen;
}

uint16_t GsmPPP_ReadRxData(uint8_t numConnect, uint8_t **ppData) {
	if(!pppIsOpen) {
		printf("GSMPPP: CONNECT ERROR - PPP closed\r\n");
		return false;
	}
	if(connectionPppStruct.rxData[numConnect].rxBufferLen != 0) {
		*ppData = (uint8_t *) connectionPppStruct.rxData[numConnect].rxBuffer;
		uint16_t retLen = connectionPppStruct.rxData[numConnect].rxBufferLen;
		connectionPppStruct.rxData[numConnect].rxBufferLen = 0;
		return retLen;
	}
	return false;
}

static void destServerFound(const char *name, struct ip_addr *ipaddr, void *arg)
{
	uint8_t *num = (uint8_t*)arg;
	if(*num < SERVERS_COUNT) {
		printf("GSMPPP: DEST FOUND %s\r\n", inet_ntoa(ipaddr->addr));
		connectionPppStruct.ipRemoteAddr[num].addr = ipaddr->addr;
		xSemaphoreGive(connectionPppStruct.semphr[num]);	
	}else{
		printf("GSMPPP: DNS != SERVER%s\r\n", inet_ntoa(ipaddr->addr));
	}
}

static err_t TcpConnectedCallBack(void *arg, struct tcp_pcb *tpcb, err_t err) {
	for(uint8_t i=0; ilocal_ip.addr));
			xSemaphoreGive(connectionPppStruct.semphr[i]);
			break;
		}
	}
}

static err_t server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
   LWIP_UNUSED_ARG(arg);

	if(err == ERR_OK && p != NULL) {
		tcp_recved(pcb, p->tot_len);
		printf("GSMPPP:server_recv(): pbuf->len %d byte\n [%s]", p->len, inet_ntoa(pcb->remote_ip.addr));
    
		for(uint8_t i=0; iremote_ip.addr == connectionPppStruct.tcpClient[i]->remote_ip.addr) {
				printf("GSMPPP: server_recv (callback) [%s]\r\n", inet_ntoa(pcb->remote_ip.addr));
				if(p->len < sizeof(connectionPppStruct.rxData[i].rxBuffer)) {
					memcpy(connectionPppStruct.rxData[i].rxBuffer, p->payload, p->len);
					connectionPppStruct.rxData[i].rxBufferLen = p->len;
					xSemaphoreGive(connectionPppStruct.rxData[i].rxSemh);
					printf("GSMPPP: server_recv (callback) GIVE SEMPH[%s][%d]\r\n", inet_ntoa(pcb->remote_ip.addr), p->len);
				}else{
					printf("GSMPPP: server_recv p->len > sizeof(buf) -ERROR\r\n");
				}
			}
		}
    pbuf_free(p);
	}else{
		printf("\nserver_recv(): Errors-> ");
    if (err != ERR_OK)
			printf("1) Connection is not on ERR_OK state, but in %d state->\n", err);
		if (p == NULL)
			printf("2) Pbuf pointer p is a NULL pointer->\n ");
    printf("server_recv(): Closing server-side connection...");
		pbuf_free(p);
    server_close(pcb);
	}
   return ERR_OK;
}

xSemaphoreHandle * GsmPPP_GetRxSemaphorePoint(uint8_t numService) {
	return (connectionPppStruct.rxData[numService].rxSemh);
}


Функции связанные с соединением TCP в LwIP, callback-s
static err_t server_poll(void *arg, struct tcp_pcb *pcb)
{
   static int counter = 1;
   LWIP_UNUSED_ARG(arg);
   LWIP_UNUSED_ARG(pcb);
   printf("\nserver_poll(): Call number %d\n", counter++);
   return ERR_OK;
}

static err_t server_err(void *arg, err_t err)
{
   LWIP_UNUSED_ARG(arg);
   LWIP_UNUSED_ARG(err);
   printf("\nserver_err(): Fatal error, exiting...\n");
   return ERR_OK;
}

static void server_close(struct tcp_pcb *pcb)
{
	tcp_arg(pcb, NULL);
	tcp_sent(pcb, NULL);
	tcp_recv(pcb, NULL);
while(tcp_close(pcb) != ERR_OK) { vTaskDelay(100/portTICK_PERIOD_MS); }
	for(uint8_t i=0; ilocal_ip.addr));
			connectionPppStruct.connected[i] = false;
		}else{
			printf("GSMPPP: server_recv p->len > sizeof(buf) -ERROR\r\n");
		}
	}
}


Функции соединяющие уровень LwIP с UART и GSM модулем, очень важный момент
// Прослойка на чтение из очереди - отправка данных в LwIP
u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len)
{
	unsigned long i = 0;
	if(uartParcerStruct.ppp.pppModeEnable) {
		while(xQueueReceive(uartParcerStruct.uart.rxQueue,&data[i], 0) == pdTRUE) {
			if(i==0) {
				printf("Reading PPP packet from UART\r\n");
			}
			printf("%0.2x ", data[i]);
			i++;
			if (pppStop||(i==len)) {
				pppStop = false;
				return i;
			}
		}
		if (i>0) {
			printf("\n");
		}
	}
	return i;
}
// Запись из LwIP в UART (GSM)
u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len)
{
	u32_t retLen = 0;
	if(uartParcerStruct.ppp.pppModeEnable) {
		if(HAL_UART_Transmit_IT(&huart3, data, len) == HAL_OK) {
			xSemaphoreTake(sioWriteSemaphore, portMAX_DELAY);
			retLen = len;
		}else{
			printf("HAL ERRROR WRITE [sio_write]\r\n");
		}
	}else{
		printf("sio_write not in PPP mode!\r\n");
	}
	return retLen;
}
// Прерывание сессии, очистка очереди
void sio_read_abort(sio_fd_t fd)
{
	pppStop = true;
	xQueueReset(uartParcerStruct.uart.rxQueue);
}

u32_t sys_jiffies(void) {
	return xTaskGetTickCount();
}
// Калбек вызывается после успешной или не успешной попытки соединения
static void linkStatusCB(void * ctx, int errCode, void * arg) {
	printf("GSMPP: linkStatusCB\r\n"); /* just wait */
	bool *connected = (bool*)ctx;
	struct ppp_addrs * addrs = arg;
	switch (errCode) {
		case PPPERR_NONE: { /* We are connected */
			printf("ip_addr = %s\r\n", inet_ntoa(addrs->our_ipaddr));
			printf("netmask = %s\r\n", inet_ntoa(addrs->netmask));
			printf("dns1 = %s\r\n", inet_ntoa(addrs->dns1));
			printf("dns2 = %s\r\n", inet_ntoa(addrs->dns2));
			*connected = 1;
			break;
		}
		case PPPERR_CONNECT: {
			printf("lost connection\r\n"); /* just wait */
			*connected = 0;
			break;
		}
		default: { /* We have lost connection */
			printf("connection error\r\n"); /* just wait */
			*connected = 0;
			break;
		}
	}
}


Отправляем данные через TCP терминал:



В структурах видно пришедший пакет:



Подведем итоги.

Отказавшись от AT-команд, удалось значительно уменьшить и упростить код по разбору и отправки команд, сложного (потенциально не надежного) разбора ответов, приема данных и URC кодов. AT-команды остались для первоначальной настройки модема и записи параметров APN.

Записанную логическим анализатором сессию PPP, для подробного изучения, можно взять здесь.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332742/


Метки:  

The Machine и экзафлопсные вычисления для эры Big Data

Понедельник, 10 Июля 2017 г. 08:54 + в цитатник
В мае HPE продемонстрировала важный этап своей научно-исследовательской программы The Machine – прототип компьютера со 160 ТБ общей памяти. Программа направлена на разработку новой вычислительной архитектуры, ориентированной на память, а не на процессор (memory-driven computing). А уже в июне было объявлено, что на основе этой архитектуры будет создана эталонная модель экзафлопсного суперкомпьютера для Министерства энергетики США. Рассказываем подробнее об этих революционных новостях.




Экзафлопсный суперкомпьютер для Министерства энергетики США


Компания Hewlett Packard Enterprise получила исследовательский грант от Министерства энергетики США на создание эталонной модели экзафлопсного суперкомпьютера, который позволит создавать недостижимые на сегодняшний день математические модели и симуляции для использования в науке, медицине, проектировании и других технологиях.

Для достижения экзафлопсной производительности к 2022–2023 гг. необходимо повысить быстродействие, энергоэффективность и плотность высокопроизводительных вычислительных систем в 10 раз по сравнению с самыми быстрыми современными суперкомпьютерами. Чтобы реализовать экзафлопсные вычисления с низкой задержкой, эталонная модель, создаваемая HPE, должна будет устранить эти проблемы и снять ограничения по объему памяти, масштабируемости фабрики памяти и пропускной способности, присущие современной высокопроизводительной вычислительной архитектуре.

В основу референсной модели разработки HPE положена концепция Memory-Driven Computing (вычислений, ориентированных на память). Эта архитектура вычислительной платформы, центральным элементом которой является память, а не процессор, что позволяет получить недоступный ранее прирост производительности и эффективности. Архитектура Memory-Driven Computing от HPE — это масштабный набор технологий, которые разрабатываются подразделением Hewlett Packard Labs в рамках исследовательского проекта The Machine. 16 мая 2017 года компания HPE представила новую версию прототипа, созданного в ходе реализации этого проекта и ставшего крупнейшим в мире компьютером с общей памятью.

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

Более подробно о предпосылках проекта The Machine, инновационных технологиях, которые связаны с развитием проекта (мемристоры, non-volatile memory, фотоникс, система на кристалле), логической и инженерной структуре The Machine можно узнать из доклада Александра Старыгина, директора департамента подготовки технических решений HPE в России, на конференции HPE Digitize, прошедшей 5 июля в Москве.





Протокол Gen-Z


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

Протокол Gen-Z обеспечивает обмен данными между интегральными микросхемами на основе семантики памяти, что позволяет организовать надежное взаимодействие многих устройств, включая центральные и графические процессоры, программируемые логические интегральные схемы (FPGA), энергозависимую и энергонезависимую память, межсистемные элементы и множество прочих устройств; при этом все они используют единое адресное пространство. Это дает возможность создавать системы, ориентированные на память, для кардинального повышения производительности приложений и энергоэффективности. Протокол Gen-Z позволяет партнерам расширять сотрудничество при разработке суперкомпьютеров, в которых применяются самые передовые технологии, развивающиеся в открытой и конкурентной экосистеме.

Практическое применение memory-driven computing


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

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

Не так давно Немецкий центр нейродегенеративных заболеваний (DZNE) подписал договор о сотрудничестве с HPE для применения новой архитектуры вычислений в своих научных и медицинских исследованиях. DZNE генерирует огромные объемы данных, например, полученные в ходе магнитно-резонансных томографий (МРТ) мозга или информации о геноме. Глубокий анализ этих данных часто занимает одну-две недели, и только после этого врачи могут определить следующий шаг лечения и/или начать новый цикл анализов/исследований. Применение новой вычислительной архитектуры, ориентированной на память позволит не только ускорить анализ полученных данных, но и использовать изображения гораздо более высокой точности.

The Machine User Group


На конференции HPE Discover 2017 в Лас-Вегасе компания объявила о создании The Machine User Group – открытого сообщества разработчиков, заинтересованных в программировании для среды memory-driven computing, с использованием массивного пула энергонезависимой памяти и специализированных процессоров, выделяемых под конкретную задачу (SoC). Для разработчиков уже доступен набор инструментов на GitHub, а в перспективе участники User Group получат доступ к эмуляционной среде The Machine.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332732/


Метки:  

GitLab CI для непрерывной интеграции в production. Часть 1: наш пайплайн

Понедельник, 10 Июля 2017 г. 08:52 + в цитатник


Итак, GitLab CI: что можно ещё рассказать о нём? На хабре уже есть статьи про установку, про настройку раннеров, про командное использование, про GitLab Flow. Пожалуй, не хватает описаний того, как используется GitLab CI в реальном проекте, где задействовано несколько команд. А в современном мире разработки ПО это действительно так: ведь есть (как минимум) разработчики, тестировщики, DevOps- и релиз-инженеры. С подобным разделением на команды мы работаем уже несколько лет. В этой статье я расскажу о том, как мы, используя и улучшая возможности GitLab CI, реализовали и применяем в production для коллектива из нескольких команд процессы непрерывной интеграции (CI) и отчасти доставки приложений (CD).

Наш пайплайн


Если вы сталкивались с CI-системами ранее, то понятие пайплайна вам знакомо — это последовательность выполнения стадий (здесь и далее в статье для термина stage использую перевод «стадия»), каждая из которых включает несколько задач (здесь и далее в статье job — «задача»). От момента внесения изменений в код до выката в production приложение по очереди оказывается в руках разных команд — подобному тому, как это происходит на конвейере. Отсюда и возник термин pipeline («конвейер» — один из вариантов дословного перевода). В нашем случае это выглядит так:



Краткие пояснения по используемым стадиям:
  • build — сборка приложения;
  • testing — автотесты;
  • staging — развёртывание приложения для разработчиков, DevOps, тестировщиков;
  • pre-production — развёртывание в «предварительный production» для команды тестировщиков;
  • approve — «предохранитель», благодаря которому релиз-инженер заказчика может отказать в развёртывании на production определённого тега;
  • production — развёртывание на production.

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

Как это используется?


Начну с рассказа о пайплайне с точки зрения его использования — то, что можно назвать user story. Тут выяснится, что на самом деле пайплайна у нас даже два: укороченный для веток и полноценный для тегов. И вот как выглядят эти последовательности:
  1. Разработчики выкладывают код приложения в ветки с префиксом feature_, а DevOps-инженеры — код инфраструктуры в ветки с префиксом infra_. Каждый git push в эти ветки запускает процесс сборки приложения (стадия build) и автоматические тесты (стадия testing).
  2. Задачи на следующих стадиях не вызываются автоматически, а определены как задачи с ручным запуском (manual).
  3. На стадии staging можно запустить задание и выкатить собранное и протестированное приложение на упрощенное окружение. Это могут делать разработчики, DevOps-инженеры, тестировщики. При этом для выката на окружения тестировщиков должны быть пройдены все тесты.
  4. После успешного прохождения тестов и выката на одно из окружений staging можно выкатить приложение в pre-production — это делают только тестировщики, DevOps-инженеры или релиз-инженеры.
  5. По мере накопления успешно протестированных фич релиз-инженер готовит новую версию и создаёт в репозитории тег. Пайплайн для тега отличается от пайплайна для ветки двумя дополнительными стадиями.
  6. После успешной сборки, тестов и выката в pre-production проводятся дополнительные ручные тесты новой версии, показ заказчику и другие «издевательства» над приложением. Если всё прошло успешно, то релиз-инженер запускает задание approve. Если что-то пошло не так, то релиз-инженер запускает задание not approve.
  7. Выкат приложения на production возможен только после успешного выката на pre-production и выполнения задания approve. Выкат на production может запустить релиз-инженер или DevOps-инженер.


Роль релиз-инженера в пайплайне

Пайплайн и стадии в деталях


Задачи на стадии build собирают приложение. Для этого мы используем свою разработку — Open Source-утилиту dapp (о её основных возможностях читайте и смотрите в этой статье + видео), которая хорошо ускоряет инкрементальную сборку. Поэтому сборка запускается автоматически для веток с префиксами feature_ (код приложения), infra_ (код инфраструктуры) и тегов, а не только для нескольких традиционно «главных» веток (master/develop/production/release).

Следующая стадия — staging — это набор сред для разработчиков, DevOps-инженеров и тестировщиков. Здесь объявлено несколько задач, разворачивающих приложения из веток с префиксами feature_ и infra_ в урезанных средах для быстрого тестирования новой функциональности или инфраструктурных изменений (код сборки приложения хранится в репозитории приложения).

Стадии pre-production и production доступны только для тегов. Обычно тег вешается после того, как релиз-инженеры принимают несколько merge-запросов из протестированных веток. В целом можно сказать, что мы используем GitLab Flow с тем лишь отличием, что нет автоматического развёртывания на production и потому нет отдельных веток, а используются теги.

Стадия approve — это две задачи: approve и not approve. Первая включает возможность развёртывания на production, вторая — выключает. Эти задачи существуют для того, чтобы в случае проблем на production было видно, что развёртывание происходило не просто так, а с согласия релиз-инженера. Однако суть не в лишении кого-то премии, а в том, что непосредственно развёртывание на production проводит зачастую не сам релиз-инженер, а команда DevOps. Релиз-инженер, запуская задачу approve, разрешает тем самым «нажать на кнопку» deploy to production команде DevOps. Можно сказать, что эта стадия выносит на поверхность то, что могло бы остаться в почтовой переписке или в устной форме.

Такая схема взаимодействия хорошо себя показала в работе, однако пришлось потрудиться, чтобы реализовать её. Как оказалось, GitLab CI не поддерживает из коробки некоторые нужные вещи…

Разработка .gitlab-ci.yml


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

stages:
  - build
  - testing
  - staging
  - preprod
  - approve
  - production

## build stage
build:
  stage: build
  tags: [deploy]
  script:
    - echo "Build"

## testing stage
test unit:
  stage: testing
  tags: [deploy]
  script:
    - echo "test unit"

test integration:
  stage: testing
  tags: [deploy]
  script:
    - echo "test integration"

test selenium:
  stage: testing
  tags: [deploy]
  script:
    - echo "test selenium"

## staging stage
.staging-deploy: &staging-deploy
  tags: [deploy]
  stage: staging
  when: manual
  script:
    - echo $CI_BUILD_NAME


deploy to dev-1:
  <<: *staging-deploy

deploy to dev-2:
  <<: *staging-deploy

deploy to devops-1:
  <<: *staging-deploy

deploy to devops-2:
  <<: *staging-deploy

deploy to qa-1:
  <<: *staging-deploy

deploy to qa-2:
  <<: *staging-deploy


## preprod stage
deploy to preprod:
  stage: preprod
  tags: [deploy]
  when: manual
  script:
    - echo "deploy to preprod"

## approve stage
approve:
  stage: approve
  tags: [deploy]
  when: manual
  script:
    - echo "APPROVED"

NOT approve:
  stage: approve
  tags: [deploy]
  when: manual
  script:
    - echo "NOT APPROVED"

## production stage
deploy to production:
  stage: production
  tags: [deploy]
  when: manual
  script:
    - echo "deploy to production!"

Всё довольно просто и скорее всего понятно. Для каждой задачи используются следующие директивы:
  • stage — определяет стадию, к которой относится задача;
  • script — действия, которые будут произведены, когда запустится задача;
  • when — вид задачи (manual означает, что задача будет запускаться из пайплайна вручную);
  • tags — теги, которые в свою очередь определяют, на каком раннере будет запущена задача.

Примечание: Раннер — часть GitLab CI, аналогичная другим системам CI, т.е. агент, который получает задачи от GitLab и выполняет их script.

Слайд про раннеры из презентации «Coding the Next Build» ((c) 2016 Niek Palm)

Кстати, заметили этот блок?
.staging-deploy: &staging-deploy
  tags: [deploy]
  stage: staging
  when: manual
  script:
    - echo $CI_BUILD_NAME

Он демонстрирует возможность формата YAML определять общие блоки и подключать их в нужное место на нужном уровне. Подробнее см. в документации.

В описании пайплайна было сказано, что стадии approve и production доступны только для тегов. В .gitlab-ci.yml это можно сделать с помощью директивы only. Она определяет ветки, для которых будет создаваться пайплайн, а с помощью ключевого слова tags можно разрешить создавать пайплайн для тегов. К сожалению, директива only есть только для задач — её нельзя указать при описании стадии.

Таким образом, задачи на стадиях build, testing, staging, pre-production, которые должны быть доступны для веток infra_, feature_ и тегов, принимают следующий вид:
test unit:
  stage: testing
  tags: [deploy]
  script:
    - echo "test unit"
  only:
    - tags
    - /^infra_.*$/
    - /^feature_.*$/

А задачи на стадиях approve и production, которые доступны только для тегов, имеют такой вид:
deploy to production:
  stage: production
  tags: [deploy]
  script:
    - echo "deploy to production!"
  only:
    - tags

В полном варианте директива only вынесена в общий блок (пример такого .gitlab-ci.yml доступен здесь).

Что дальше?


На этом создание .gitlab-ci.yml для описанного пайплайна затормаживается, т.к. GitLab CI не предоставляет директив, во-первых, для разделения задач по пользователям, а во-вторых, для описания зависимостей выполнения задач от статуса выполнения других задач. Также хотелось бы разрешить изменять .gitlab-ci.yml только отдельным пользователям.

Полная реализация задуманного стала возможной только благодаря нескольким патчам к GitLab и использованию артефактов задач. Подробнее об этом я расскажу в следующей части статьи (мы опубликуем её в среду).
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332712/


[Перевод] Создание chatbot-a с помощью sockeye (MXNet) на базе AWS EC2 и AWS DeepLearning AMI

Понедельник, 10 Июля 2017 г. 07:45 + в цитатник
AWS AI
Недавно, команда AWSDeepLearning выпустила новый фреймворк — “sockeye”, цель которого является упрощение обучения seq2seq сетей. Забегая вперед — я даже не ожидал такой простоты. Так что решил написать простое, быстрое и самодостаточное руководство, которое не требует от читателя глубоких знаний в области нейронных сетей. Единственное, что все же требуется для успешного выполнения всех шагов, это иметь некоторый опыт работы с:

  • AWS EC2;
  • SSH;
  • python;

Если все эти три вещи не вызывают проблем — прошу под кат.

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

Как уже упомянул, намедни команда AWS DeepLearning выпустила новый фреймворк — “sockeye”. Позвольте мне привести цитату с официального сайта:

… the Sockeye project, a sequence-to-sequence framework for Neural Machine Translation based on MXNet. It implements the well-known encoder-decoder architecture with attention.

Вольнвый перевод:

Sockeye это фреймворк для обучения нейронных сетей машинному переводу, который базируется на известной архитектуре encoder-decoder.

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

Описание процесса


В целом, процесс состоит из следующих шагов:

  1. Поднять EC2 машину с GPU на базе DeepLearning AMI
  2. Подготовить EC2 машину для тренировки
  3. Начать обучение
  4. Подождать
  5. Profit

Поднимаем EC2 машину с GPU, на базе AWS DeepLearning AMI


В данной статье мы будем использовать AWS DeepLearning AMI, далее по тексту: DLAMI (кстати, если вы не знаете что такое AMI, то рекомендую ознакомится с официальной документацией вот тут). Основные причины использования именно этого AMI:

  • в него включены драйвера Nvidia CUDA (на момент написания статьи версия: 7.5);
  • собранный с поддержкой GPU — MXNet;
  • включает все(почти) утилиты что нам необходимы, например: git;
  • может быть использован с, сюрприз-сюрприз, машинами в которых есть GPU.

Для того что бы быстро создать нужную нам машину из AMI, идем на страницу DLAMI в AWS Marketplace. Тут стоит обратить внимание на следующие вещи:

1. Версия AMI




На момент написания — «Jun 2017» была самая свежая версия, так что если хотите, что бы ваш процесс был консистентет с остальной частью этой стать — рекомендую выбрать именно его.

2. Регион для создания



Учитывайте что не все типы машин с GPU доступны во всех регионах. Собственно, даже если они формально доступны, то не всегда есть возможность их создать. Так, например, в 2016 во время NIPS конференции с ними было очень проблематично. Нам потребуется машина типа p2, плюс, и на момент написания статьи, DLAMI был доступен только в тех регионах, где этот самый тип был доступен:



3. Выбор типа инстанса



p2.xlarge — является самой дешёвой машинкой которая удовлетворяет нашим требованиям к памяти GPU (можете конечно попробовать и g2.2xlarge, но не говорите потом что вас не предупредили). На момент написания цена за него составляла ~0.9$ в час. Но лучше проверьте цену на официальном сайте.

4. VPC



Если не в курсе что с этим делать — не трогайте.

5. Security group



Так же как и с VPC, не знаете — не трогайте. Однако, если хотите использовать существующую группу, то убедитесь что там открыт SSL.

6. Key pair



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

7. Жмем создать!




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

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


Подключимся к ново созданной машине. Сразу после, самое время запустить screen. И не забудьте тот факт, что при подключении нужно использовать логин ec2-user:



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

  • машина с этим IP уже не в сети так что не пытайтесь туда стучаться;)
  • я сказал “screen” потому какDLAMI не содержит tmux из коробки!!! ага, грустно.
  • если не знаете что такое screen или tmux — не проблема, можете просто продолжать чтение, все будет работать без проблем. Однако, лучше все же пойти и почитать о том, что это за звери такие: tmux(мой выбор) и screen.

1. устанавливаем sockeye


Первое что нам нужно, это установить sockeye. С DLAMI процесс установки очень простой, всего одна команда:

sudo pip3 install sockeye --no-deps

Важная чать тут то, что нужно использовать именно pip3, а не просто pip, так как по умолчанию pip из DLAMI использует Python 2, который в свою очередь не поддерживается в sockeye. Так же нет необходимости устанавливать какие либо зависимости ибо они все уже установлены.

2. Подготовка данных(диалогов) для обучения


Для тренировки, мы будем использовать “Cornell Movie Dialogs Corpus”(https://www.cs.cornell.edu/~cristian/Cornell_Movie-Dialogs_Corpus.html). Это, по факту огромный корпус диалогов из фильмов. Для обучения его нужно «приготовить», собственно я уже реализовал скрипт который готовит корпус и рассказывал детальнее о не ранее.

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

# cd ~/src
src# git clone https://github.com/b0noI/dialog_converter.git
Cloning into ‘dialog_converter’…
remote: Counting objects: 59, done.
remote: Compressing objects: 100% (49/49), done.
remote: Total 59 (delta 33), reused 20 (delta 9), pack-reused 0
Unpacking objects: 100% (59/59), done.
Checking connectivity… done.

src# cd dialog_converter

dialog_converter git:(master)# git checkout sockeye_chatbot
Branch sockeye_chatbot set up to track remote branch sockeye_chatbot from origin.
Switched to a new branch 'sockeye_chatbot'

dialog_converter git:(sockeye_chatbot)# python converter.py

dialog_converter git:(sockeye_chatbot)# ls
LICENSE README.md converter.py movie_lines.txt train.a train.b test.a test.b

Пару вещей на которые стоит обратить внимание:

  • папка src уже существует, нет необходимости ее создавать;
  • обратите внимание на бранч: “sockeye_chatbot”, в нем я храню код который консистентен с этой статьей. Используйте master на ваш страх и риск.

Теперь давайте создадим папку где будем проводить обучение и скопируем туда все данные:

# cd ~
# mkdir training
# cd training
training# cp ~/src/dialog_converter/train.* .
training# cp ~/src/dialog_converter/test.* .

Ну все, мы готовы начать обучение…

Обучение


С sockeye процесс обучения очень прост — нужно лишь запустить одну единственную команду:

python3 -m sockeye.train --source train.a --target train.b --validation-source train.a --validation-target train.b --output model

Знаю-знаю, НИКОГДА не используйте один и те же данные для обучения и валидации. Однако мой скрипт на данный момент не совсем корректно разбивает данные на две группы и посему более лучшие результаты (я о субъективном оценивании) получаются, как не странно, без разбивки.



Если читали прошлую статью то могли заметить что sockeye пытается за вас найти подходящую конфигурацию для тренировки, а именно:

  • размер словаря;
  • параметры сети;
  • и т.д.…

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

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

Ждем результат


Пока ждете можно посмотреть как MXNet пожёвывает ресурсы GPU во время обучения. Для этого нужно запустить вот эту команду в новом окне:

watch -n 0.5 nvidia-smi

Увидите что-то вроде:



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



Теперь можно начать общение:

Чатимся...


Этот процесс не требует остановки обучения, нужно лишь открыть новое окно (ну или новое SSH соединение) перейти в ту же папку где происходит обучение и выполнить команду:

python3 -m sockeye.translate --models model --use-cpu --checkpoints 0005

Несколько элементов на которых хочу с акцентировать внимание:

  • python3 — естессно;
  • model — имя папки в которую процесс обучения сохраняет модель, должно соответствовать имени указанном при обучение;
  • --use-cpu — без этого MXNet попробует задействовать GPU что скорее всего закончится неудачей так как процесс обучения его все еще использует.
  • --checkpoints 0005 - номер контрольной точки, взят из консольного вывода в момент сохранения контрольной точки.

После запуска команды sockeye будет считывать ввод из STDIN и выводить ответ в STDOUT. вот несколько примеров:

после часа обучения:




после 2х часов обучения




после 3х часов обучения оно мне начало угрожать =)




Заключение


Как можно увидеть с sockeye процесс обучения очень прост. Собственно, самое сложное, пожалуй, это поднять нужную машину и подключится к ней =)
Я все до сих пор жду что бы кто-то из читателей:

  • создал бота по мастере Yoda (ну или Вейдера);
  • создал бота из вселенной Lord of The Rings;
  • создал бота из вселенной StarWars.

PS: не забудьте скачать вашу модель и прибить машину после окончания обучения.

Натренированная модель


Вот тут можно скачать натренированную модель (тренировал всего 4 часа). Ее можно использовать на локальной машине, что бы поиграться с ботом, если не хочется тренировать своего.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332654/


Метки:  

Вебсокеты на php. Выбираем вебсокет-сервер

Понедельник, 10 Июля 2017 г. 02:48 + в цитатник
Давным-давно я публиковал статью на хабре, как написать свой вебсокет-сервер с нуля. Статья переросла в библиотеку. Несколько месяцев я занимался её развитием, ещё несколько лет — поддержкой и багфиксом. Написал модуль интеграции с yii2. Какой-то энтузиаст написал интеграцию с laravel. Моя библиотека совместима с php7. Недавно я решил отказаться от её дальнейшей поддержки (причины ниже), поэтому хочу помочь её пользователям перейти на другую библиотеку.


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

phpdaemon

1400 звёзд на гитхабе
  • зависит от установки библиотеки libevent
  • протоколы: HTTP, FastCGI, FlashPolicy, Ident, Socks4/5.


Ratchet

3600 звёзд на гитхабе
  • тянет за собой около десятка зависимостей
  • протоколы: websocket, http, wamp
  • поддержка windows
  • нет ssl


Эти библиотеки были очень монструозны и при этом не соответствовали моим внутренним требованиям:
  • отсутствие зависимостей
  • наличие таймеров

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

В итоге я написал библиотеку для себя и поделился ею с сообществом на гитхабе. Сделал несколько демок (в том числе игру «танчики»). Переписал стороннюю игру (с разрешения авторов) с node.js на свою библиотеку. Делал нагрузочное тестирование. Демки работали годами без перезагрузки. Старался отвечать на тикеты в течения дня. Всё это показывало, что моя библиотека может быть использована на продакшене и многие её использовали.

Была единственная проблема. Мне хватало моей библиотеки для использования в своих проектах, а вот другим нет. Они хотели, чтобы я её развивал, а мне это было не нужно. Кому-то требовалась поддержка windows, а кому-то ssl, pg_notify, safari, pthreads и многое другое. Открытые тикеты с запросами на реализацию различного функционала висят годами.

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

Workerman

4500 звёзд на гитхабе
  • отсутствие зависимостей
  • протоколы: websocket, http/https, tcp, сustom
  • поддержка таймеров
  • интеграция с react-компонентами
  • поддержка windows


Первый его релиз был ещё два года назад, но почему-то всё новые и новые люди начинали пользоваться моей библиотекой для новых проектов. Я ещё могу понять, что ею пользуются на старых проектах (работает — не трогай), но на новых… — для меня это была загадка.

Если загуглить «php websocket», то первая страница — это моя статья на хабре, а вторая — «Ratchet», который кому-то может показаться сложным и он выберет из-за этого мою библиотеку или вообще откажется от идеи делать вебсокеты.
Что ж, пришло время исправить эту досадную ошибку и донести до как можно большего количества людей о существовании такой библиотеки как Workerman и привести несколько примеров по её использованию.

На главной странице проекта в гитхабе уже есть несколько примеров.
Рассмотрим один из них:
websocket server

https://habrahabr.ru/post/331462/


Метки:  

[Перевод] Отзыв сертификатов не работает

Понедельник, 10 Июля 2017 г. 00:01 + в цитатник
Прямо сейчас у нас есть небольшая проблема, но на мой взгляд, со временем ситуация может только ухудшиться. Всё больше и больше сайтов получают сертификаты — необходимые документы для внедрения HTTPS — но у нас нет механизма для защиты от злоупотреблений.

Сертификаты


Мы сейчас видим настоящую золотую лихорадку вокруг сертификатов, поскольку всё больше сайтов внедряют HTTPS. Кроме очевидных преимуществ безопасности и приватности, есть и другие выгоды от внедрения защищённых соединений, которые я перечислил в статье «Вы всё ещё думаете, что вам не нужен HTTPS?». Обычно именуемые «SSL-сертификаты» или «HTTPS-сертификаты» разлетаются со скоростью, которой мы никогда не видели в истории интернета. Каждый день я исследую сайты из первого миллиона по посещаемости и анализирую различные аспекты их безопасности, а каждые 6 месяцев публикую отчёт. Вы можете изучить эти отчёты здесь, но сейчас посмотрим на темпы внедрения HTTPS.


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

Мы не просто продолжаем внедрять HTTPS, но скорость внедрения тоже увеличивается. Так выглядит настоящий прогресс. Процесс получения сертификата со временем становится более простым, благодаря великолепному Let's Encrypt, к тому же ещё и сертификаты стали бесплатными. Если вкратце, мы просто отправляем запрос на получение сертификата (Certificate Signing Request, CSR) в центр сертификации (CA), а тот предлагает доказать факт владения доменом. Обычно это делается с помощью изменения записи DNS TXT или размещения специального кода где-нибудь по случайному URL на нашем домене. Как только задание выполнено, CA выдаёт сертификат, и мы можем предъявлять его браузерам и наслаждаться зелёным замком и указанием HTTPS в адресной строке.



Я написал несколько инструкций по этому процессу, в том числе с чего начать, как грамотно обновить и как использовать двойные сертификаты. Это всё здорово, не правда ли? Но в чём проблема? А проблема возникнет тогда, когда всё пойдёт не по плану и у вас случится плохой день.

Нас хакнули


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



Если злоумышленник завладел нашим секретным ключом, то он может выдать себя за нас. Повторим это: кто-то в интернете может доказать, что он — это вы, хотя в реальности он таковым не является. Это реальная проблема, и прежде чем вы подумаете «Со мной такого никогда не произойдёт», вспомните Heartbleed. Этот крошечный баг в библиотеке OpenSSL позволял злоумышленнику украсть ваш секретный ключ, даже если вы соблюдали все меры безопасности. Кроме этого, было бесчисленное множество случаев, когда секретные ключи утекали из-за случайности или небрежности. Реальность такова, что мы можем потерять наш секретный ключ, а когда это случается, нам требуется способ помешать злоумышленнику использовать наш сертификат. Нужно его отозвать.

Отзыв


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



Как только мы узнаём о факте взлома, мы связываемся с CA и просим отозвать наш сертификат. Нужно доказать факт владения сертификатом, и как только мы сделали это, то CA помечает сертификат как отозванный. Теперь нужен способ сообщить об этом факте каждому клиенту, которому может потребоваться данная информация. Прямо в этот момент браузер, конечно, ничего не знает, и это проблема. Есть два механизма, которые используются для распространения информации: это список отозванных сертификатов (Certificate Revocation List, CRL) и протокол проверки статуса сертификата (Online Certificate Status Protocol, OCSP).

Списки отозванных сертификатов


CRL — действительно очень простая концепция, это просто список всех сертификатов, которые CA пометил как отозванные. Клиент может отправить запрос к серверу CRL и скачать копию списка. Имея копию этого списка, браузер сверяет с ним предъявленный сертификат. Если он там присутствует, то браузер знает, что сертификат недействителен и ему нельзя доверять, он тогда выдаст ошибку и разорвёт соединение. Если сертификата нет в списке, то всё нормально — и браузер продолжит работу.



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

Протокол проверки статуса сертификата


OCSP предлагает намного более красивое решение проблемы и имеет значительное преимущество перед CRL. Здесь мы спрашиваем у CA статус единственного, конкретного сертификата. Это значит, что CA должен вернуть только простой ответ: сертификат либо хороший, либо отозван, и такой ответ гораздо меньше по размеру, чем список CRL. Отлично!



Действительно, OCSP превосходит CRL по скорости получения ответа, но за это преимущество нужно платить (вы тоже ненавидите, когда такое случается?). Цена довольно высока — это ваша приватность… Если подумать о сути запроса OCSP, это очень конкретный запрос на единственный сертификат HTTPS. По сути, происходит утечка информации. Когда вы отправляете запрос OCSP, вы буквально спрашиваете у центра сертификации:

Сертификат для pornhub.com валидный?

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

Полный сбой


Выше я говорил о CRL и OCSP, двух механизмах проверки сертификатов браузером, и они выглядят таким образом.



После получения сертификата браузер обратится к одному из этих сервисов и отправит запрос для окончательного выяснения статуса сертификата. А что, если у вашего CA плохой день и его инфраструктура в офлайне? Что, если ситуация выглядит так?



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

Частичный сбой


На самом деле сегодня браузеры выполняют так называемую проверку отзыва сертификата с частичным сбоем. То есть браузер попытается проверить статус сертификата, но если ответ не пришёл совсем или не пришёл за короткий промежуток времени, то браузер просто забывает об этом. Ещё хуже, что Chrome даже не пытается проверить сертификат. Да, вы прочитали правильно, Chrome даже не пытается проверить статус сертификата, который ему поступает. Вы можете найти это странным, но я полностью согласен с их подходом и я рад сообщить, что Firefox тоже, вероятно, скоро начнёт работать так же. Позвольте объяснить. Проблема с полным сбоем очевидна: если у CA плохой день, то у нас тоже он будет, вот так мы пришли к логике частичного сбоя. Браузер теперь пытается осуществить проверку сертификата на предмет отзыва, но полностью отказывается от неё, если она занимает слишком много времени или если кажется, что CA ушёл в офлайн. Погодите, какие были последние слова? Проверка сертификата на предмет отзыва отменяется, «если кажется, что CA ушёл в офлайн». Интересно, может ли злоумышленник имитировать такие условия?



Если вы осуществляете атаку MiTM, то вам нужно всего лишь блокировать запрос на проверку сертификата и создать впечатление, что CA не работает. Браузер тогда столкнётся с частичным сбоем проверки и продолжит радостно использовать отозванный сертификат. Если вас никто не атакует, то каждый раз при проверке этого конкретного сертификата вы тратите время и ресурсы на проверку, что сертификат не отозван. И один раз, когда вас атакуют — тот единственный раз, когда вам по-настоящему нужна такая проверка — злоумышленник просто блокирует соединение, и браузер проходит через частичный сбой. Адам Лэнгли из Google лучше всех описал, что такое отзыв сертификата: это ремень безопасности, который защёлкивается в момент аварии, и он прав. Вы каждый день садитесь в машину и пристёгиваете ремень безопасности — и он даёт вам приятное и комфортное ощущение безопасности. А потом в один день что-то идёт не по плану — вы попадаете в аварию, и вот тут вы вылетаете в ветровое стекло. В тот единственный раз, когда он действительно нужен, ремень безопасности вас подводит.

Исправление проблемы


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

Проприетарные механизмы


Если сайт скомпрометирован и злоумышленник получил секретный ключ, то он может подделать этот сайт и причинить некоторый вред. Здесь ничего хорошего, но могло быть и хуже. Что если CA скомпрометирован и злоумышленник получил секретный ключ для промежуточного сертификата? Это было бы катастрофой, потому что тогда злоумышленник может подделать буквально любой сайт, который захочет, подписав собственный сертификат. Поэтому вместо онлайновой проверки промежуточных сертификатов на предмет отзыва у Chrome и Firefox есть собственные механизмы для той же задачи.



В Chrome он называется CRLsets, а в Firefox — OneCRL. Эти механизмы проверяют списки отозванных сертификатов, объединяя доступные CRL и выбирая оттуда сертификаты. Так проверяются особо ценные сертификаты вроде промежуточных, но что насчёт обычных, наших с вами?

OCSP Must-Staple


Чтобы объяснить, что такое OCSP Must-Staple, нужно сначала вкратце разобраться, что такое OCSP Stapling. Я не хочу здесь вдаваться в лишние подробности, вы можете получить всестороннюю информацию из моего блога по OCSP Stapling, но вот суть. OCSP Stapling избавляет браузер от необходимости отправлять запрос OCSP, выдавая ответ OCSP вместе с самим сертификатом. Это называется OCSP Stapling, потому что сервер должен «скрепить» (staple) ответ OCSP с сертификатом и выдать их вместе.



На первый взгляд это кажется немного странным, потому что сервер как будто «сам удостоверяет» собственный сертификат как неотозванный, но всё работает правильно. Ответ OCSP действует только короткий промежуток времени и подписан CA точно так же, как сертификат. Так что если браузер может удостовериться, что сертификат подписан CA, то точно так он может удостовериться, что ответ OCSP тоже подписан этим CA. Это устраняет большую проблему приватности и избавляет клиента от бремени выполнять внешний запрос. Лучший вариант! Но на самом деле не лучший, извините. OCSP Stapling — отличная вещь, и все мы должны поддерживать эту технологию на своих сайтах, но действительно ли мы думаем, что её будет поддерживать злоумышленник? Нет, я так не думаю, конечно же он не будет этого делать. Что нам на самом деле нужно — так это заставить сервер поддерживать OCSP Stapling, и вот для чего нужен OCSP Must-Staple. При запросе нашего сертификата у CA мы просим его установить на нём флаг OCSP Must-Staple. Этот флаг указывает браузеру, что сертификат должен поставляться с откликом OCSP или он будет отвергнут. Установить флаг легко.



После установки этого флага мы должны гарантировать, что используется OCSP Staple, иначе браузер отвергнет сертификат. В случае компрометации, если злоумышленник получит наш ключ, ему также придётся использовать OCSP Staple вместе с нашим сертификатом, а если он не включит OCSP Staple, то ответ OCSP скажет, что сертификат отозван, и браузер его не примет. Тада!



OCSP Expect-Staple


Хотя Must-Staple кажется отличным решением проверки отзыва сертификатом, это не совсем так. На мой взгляд, одной из самых больших проблем является то, что я как оператор сайта не могу точно знать, насколько надёжны метки OCSP Staple и как их принимает клиент. Без включенного OCSP Must-Staple это не является проблемой, но если включить OCSP Must-Staple и мы не уверены в надёжности или правильности OCSP Staple, это проблема для сайта. Чтобы попытаться и получить некую обратную связь о качестве меток OCSP Staple, мы можем активировать функцию под названием OCSP Expect-Staple. Я писал о ней раньше, и вы можете узнать все подробности в блоге OCSP Expect-Staple, но здесь тоже объясню вкратце. Вдобавок к списку предзагрузки HSTS вы запрашиваете браузер прислать отчёт, удовлетворён ли он меткой OCSP Staple. Вы можете собирать отчёты сами или использовать мой сервис report-uri.io, в обоих случаях вы точно узнаете, когда ваш сайт столкнулся с проблемами при работе OCSP Must-Staple. Поскольку использование списка предзагрузки HSTS не настолько очевидно, как мне бы хотелось, я также написал спецификацию для определения нового заголовка безопасности под названием Expect-Staple, чтобы обеспечивать ту же функциональность ценой меньших усилий. Идея в том, что теперь вы можете установить этот заголовок и включить функцию отправки отчётов, которые так нам нужны, ещё до активации Must-Staple. Установка заголовка будет простой, как и всех остальных заголовков безопасности:

Expect-Staple: max-age=31536000; report-uri="https://scotthelme.report-uri.io/r/d/staple"; includeSubDomains; preload

Поддельные сертификаты


Если мы говорим об отзыве сертификатов, то должны рассмотреть тему их подделки. Если некто пытается скомпрометировать CA или как-то ещё получить сертификат, который ему не положен, то как он будет действовать? Если я прямо сейчас взломаю CA и получу сертификат на ваш сайт, то вы не узнаете об этом до тех пор, пока об этом не сообщат в новостях. У вас в компании даже может быть инсайдер, который получит сертификат в обход внутренних процедур, и он будет делать с ним всё что захочет. Нам нужна стопроцентная прозрачность, и скоро мы её получим. Это Certificate Transparency.

Certificate Transparency


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



Эти журналы полностью открыты, и любой может посмотреть их, так что если кто-то получит сертификат на ваш сайт, то вы об этом узнаете. Например, здесь вы можете увидеть все сертификаты, выданные на мой домен, и поискать свой собственный. Есть также сервис CertSpotter от sslmate для той же цели, а я использую инструмент Facebook Certificate Transparency Monitoring, который присылает вам письмо каждый раз, когда выдан сертификат на заданный домен. Стандарт CT — это фантастическая идея, и я не могу дождаться, когда он станет обязательным, но есть одна оговорка. Дело в том, что CT — это только первый шаг. Знать об этих сертификатах хорошо, но у нас по-прежнему остаются все упомянутые проблемы с их отзывом. Тем не менее, мы можем решать только по одной проблеме за раз, и даже самые лучшие в мире механизмы отзыва неэффективны, если мы не знаем, какие сертификаты нужно отзывать. CT по крайней мере даёт нам эту информацию.

Авторизация центров сертификации


Предотвратить выдачу сертификата намного проще, чем пытаться отозвать его, и именно для этого нужна авторизация центров сертификации (CAA). Опять же, подробности есть в статье по ссылке, но вкратце суть в том, что мы можем авторизовать только конкретные центры сертификации выдавать нам сертификаты, в отличие от нынешней ситуации, когда мы не можем указать вообще никаких предпочтений. Авторизация делается так же просто, как создание записи DNS:

scotthelme.co.uk. IN CAA 0 issue "letsencrypt.org"

Хотя авторизация CA — не особенно сильный механизм, и он не способен помочь во всех ситуациях некорректной выдачи сертификатов, но в некоторых случаях он полезен, так что следует заявить о своих предпочтениях, создав запись CAA.

Заключение


В настоящий момент существует реальная проблема, что мы не можем отозвать сертификат, если кто-то получил наш секретный ключ. Только представьте, во что это выльется при раскрытии следующей глобальной уязвимости масштаба Heartbleed! Одна вещь, которую вы можете попытаться сделать — это ограничить размер ущерба от утечки, сократив срок действия своего сертификата. Вместо трёх лет указывайте один год или даже меньше. Let's Encrypt выдаёт только сертификаты, которые действительны всего лишь 90 дней! С сокращением времени жизни вашего сертификата у злоумышленника будет меньше времени для злоупотреблений. Кроме этого мы мало что можем сделать.

Для демонстрации проблемы и того, насколько она реальна, попробуйте зайти на новый поддомен, который я открыл на своём сайте, revoked.scotthelme.co.uk. Как вы вероятно догадываетесь, к этому поддомену прикреплён отозванный сертификат, и вполне вероятно, что он нормально загрузится в вашем браузере. Если нет, если ваш браузер выдаст предупреждение об истёкшем сроке действия, то это значит, что ваш браузер по-прежнему отправляет запросы OCSP и вы только что сообщили в CA о том, что посетили мой сайт. Для доказательства, что такая проверка с мягким сбоем бесполезна, можете добавить в hosts домен ocsp.int-x3.letsencrypt.org с IP-адресом 127.0.0.1 или блокировать его каким-нибудь другим способом — и повторить попытку подключения. На этот раз страница нормально загрузится, потому что проверка отозванного сертификата не сработает, а браузер продолжит загружать страницу. Толку от такой проверки…

Я бы хотел закончить статью вопросом: следует ли нам исправлять процедуру отзыва сертификатов? Впрочем, это тема для другой статьи.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332730/


Игровое управление моделям в условиях неполной информации

Воскресенье, 09 Июля 2017 г. 23:59 + в цитатник

(В соавторстве с Юлией Филимоновой jul305a@gmail.com)


Введение


Представьте себе, что летите такой весь победитель на базу, бомб уже нет, и ничего не предвещает беды…
image
А тут, скажем, горочка из тумана/облаков выступает неожиданно, или, что несколько хуже, — вот это… И вам рады, но исключительно в качестве цели:
image
#поравалить — а вот как это делать с математическим уклоном сейчас и будем разбираться.
Да и вообще есть множество случаев, когда необходимо уклониться от неожиданно возникшей помехи/преграды, нашЛось, как говорят в Яндексе, например.


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


Часть первая — модель в «простых движениях»


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


$$display$$\dot x = u, \ x(t_0) = x_0,$$display$$


где $inline$x(t)\in R^n \ u\in P\subset R^n$inline$, -управление ограничено $inline$P:|u|math> (что логично, т.к. скорость не бесконечна), а $inline$R^n$inline$$inline$n$inline$-мерное Евклидово пространство (для нашего примера, как видно, вполне достаточно двумерного). Множество $inline$M$inline$ — наша база, т.е. сюда мы хотим вернуться в целости и сохранности, — располагается в точке $inline$x_1$inline$.


Что это нам даёт? Если проинтегрировать уравнение, то мы получим траекторию движения — прямую линию $inline$x(t)=u t +C$inline$, то есть, куда направлено управление, туда и летим. Значит, при отсутствии помех вектор управления $inline$u$inline$ сонаправлен с вектором движения $inline$x_1 - x_0$inline$.


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


$$display$$\ddot x = u \Rightarrow \left\{ \begin{array}{rcl} \dot x_1 &=& x_2\\ \dot x_2 &=& u. \end{array} \right.$$display$$


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


Описанная модель отражает наши динамические возможности. Добавим в нее противника $inline$v$inline$, который всячески старается нам помешать:


$$display$$ x = u - v,\, x(t_0) = x_0,\, \exists T: x(T)\in M,\, \forall tmath>


Здесь аналогично ограниченное управление $inline$Q:|v|math>.


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


Поскольку наша цель — всё--таки попасть на $inline$M$inline$, гарантированно уклонившись от $inline$N$inline$, и мы должны знать от чего, собственно, мы уклоняемся, то рано или поздно помеху мы обнаружим. В случае безынерционного движения мы можем не знать о ней ничего до предпоследнего момента времени, так как в этом случае мы всегда можем направить вектор скорости (и, соответственно, траекторию) в сторону от помехи.


При этом, как можно заметить, помеха типа <<гора>> не самая страшная ситуация, так как она неподвижна, хотя и является большим препятствием, — хуже если противник подвижен, поэтому надо обсудить ещё пару моментов:


  • чтобы иметь возможность уклоняться, мы должны обладать большими динамическими возможностями, чем противник, иначе рано или поздно нас поймают, то есть $inline$Q$inline$ в некотором смысле должно быть меньше $inline$P$inline$;
  • пусть множество помехи, которым управляет противник — выпуклое, иначе, если применять стратегию уклонения <<угол падения равен углу отражения>> — можно и не уклониться.

Воспользовавшись приведенными выше эвристическими рассуждениями, посмотрим, как можно решить приведенную выше задачу при следующих условиях в пространстве $inline$R^2$inline$:


$$display$$ \begin{array}{rcl} \dot{x}_1 &=& u_1 - v_1,\\ \dot{x}_2 &=& u_2 - v_2. \end{array}$$display$$


Здесь $inline$x_1,\ x_2$inline$ — координаты объекта $inline$x$inline$ на плоскости, $inline$x(t) = [x_1(t),\ x_2(t)]^{T} \in \mathbb{R}^2.$inline$


Пусть объект начинает движение из точки $inline$x(t_0) = [3,\ 4]^{T} \in \mathbb{R}^2.$inline$
Ограничения на управления $inline$u(t),\ v(t)$inline$ имеют вид


  • $inline$u(t) \in P \subset \mathbb{R}^2,\ P = S_{1} ([0,\ 0]^{T})$inline$ — круг радиуса 1;
  • $inline$v(t) \in Q \subset \mathbb{R}^2,\ Q = S_{0.9} ([0,\ 0]^{T})$inline$ — круг радиуса 0.9.

Первый игрок стремится перевести траекторию системы за конечное время на терминальное множество $inline$M = S_{1}([12,\ 6]^{T})$inline$ — круг радиуса 1 с центром в точке $inline$[12,\ 6]^{T}$inline$, избежав при этом попадания на множество помехи $inline$N = S_{2}([8,\ 5]^{T})$inline$ — круг радиуса 2 с центром в точке $inline$[8,\ 5]^{T}$inline$.


Время успешного завершения игры первым игроком $inline$T = 36.0$inline$.


Траектория движения системы и компоненты управления первого игрока показаны на следующих рисунках.


Траектория движения системы.image
Зависимость первой компоненты управления от времени.image
Зависимость второй компоненты управления от времени.image


Данный подход, очевидно имеет несколько недостатков, а именно:


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

Часть вторая — модель всё ещё в «простых движениях», но сокращается количество переключений


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


Давайте думать на один шаг вперёд: если наша система сначала наводилась на цель, а на следующем шаге начала уклонятся, то можно найти точку, в которую система придёт через два шага, и наводиться сразу на неё как на промежуточную цель. Можем? По нашему предположению — ДА.


На том же примере посмотрим, что получается. Время успешного завершения игры первым игроком сократилось почти в три раза с $inline$T = 36.0$inline$ до $inline$T = 11,5$inline$. Количество переключений тоже резко уменьшилось. Берём на заметку — думать хотя бы на шаг вперёд эффективно и вообще полезно для здоровья.


Траектория движения системы и компоненты управления первого игрока показаны на на следующих рисунках.


Траектория движения системы.image
Зависимость первой компоненты управления от времени.image
Зависимость второй компоненты управления от времени.image


Часть третья — модель всё ещё в «простых движениях», но теперь решаем вопрос с приближением <<по нормали>>


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


Построение дополнительного множества избегания для траектории.
image
То есть просто построим дополнительное множество, от которого будем уклоняться, содержащее в себе «проблемную» точку не как центр, а с некоторым смещением, как на рисунке.


Часть четвертая — математический базис


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


В качестве основы будем использовать теорию дифференциальных игр, которые в нашей стране развивались Львом Семёновичем Понтрягиным [1] (если не знаете кто такой — обязательно почитайте, Личность с большой буквы, как говорится, таких уже не делают) и Красовским Николаем Николаевичем [2].


Для решения нам потребуются два множества:


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

С первым множеством, при отсутствии помехи всё более или менее понятно, если можем построить его таким образом, чтобы включить конечное множество $inline$M$inline$ — значит игру завершим успешно, не сможем — значит игра точно не сможет быть завершена. Строится оно в теории следующим образом — для каждой точки начиная с начальной $inline$x_0$inline$ путём перебора всех доступных управлений строим множество в которое можем попасть через $inline$\Delta t$inline$, после чего операцию повторяем. Выглядит довольно сложно, но для выпуклых множеств и линейных систем можно всё радикально упростить, используя аппарат выпуклого анализа, — опорные функции и введя соответствующие сетки [4]. Построение такого множества решает задачу наведения первого игрока, назовём её — задача А.
Наведение на множество $M$ в условиях отсутствия помехи.image


Что касается второго множества — множества откуда второй игрок может поймать нас и от которого нам и нужно уклоняться, то здесь несколько сложнее — следите за пальцами:


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

То есть что получается множество от которого мы уклоняемся должно быть с одной стороны большим (при приближении), а с дугой стороны (при удалении) может быть едва превышающим то, от которого необходимо уклониться?
Уклонение от множества $inline$N$inline$.image


Совпадение? Противоречие? Не думаю. Подобно Доку в "Назад в будущее" с его "пространственно-временным континуумом" вспомним, что у нас есть ещё одна переменная — время и будем трактовать термины "приближаемся" и "удаляемся" приведенные в кавычках как поведение системы вдоль траектории, что не всегда совпадает с прямолинейным движением и уж точно не характеризуется расстоянием в евклидовом пространстве. Зато временем движения до конкретной точки понятия "далеко-близко" и "приближаемся-удаляемся" характеризуются очень неплохо.
В этом случае давайте будем строить управление уклонения не от множества от которого нам надо уклоняться $inline$N$inline$, а от множества в которое оно преобразуется (а оно уменьшится ввиду превосходства первого игрока) к моменту его приближение на соответствующее временной интервал в множеству $inline$N$inline$ — т.е. построим этакую воронку направленную к первому игроку своей узкой стороной и будем отталкиваться уже от неё. Построение такого множества в каждый момент игры и уклонение от него будет решает задачу уклонения первого игрока, назовём её — задача Б.


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


Формализуем теперь движение нашего объекта $inline$x$inline$, в $inline$x$inline$--мерном евклидовом пространстве $inline$\mathbb{R}^n$inline$ следующей системой дифференциальных уравнений:


$$display$$\dot{x} = A x + B u - C v,$$display$$


где $inline$x \in \mathbb{R}^n,\ u \in P \subset \mathbb{R}^p,\ v \in Q \subset \mathbb{R}^q$inline$; $inline$P,\ Q$inline$ — выпуклые компакты из евклидовых пространств $inline$\mathbb{R}^p,\ \mathbb{R}^q$inline$; $inline$A,\ B,\ C$inline$ — постоянные матрицы, $inline$A \in \mathbb{R}^{n \times n},\ B \in \mathbb{R}^{n \times p},\ C \in \mathbb{R}^{n \times q}$inline$, что обеспечивает существование, единственность и продолжимость при всех $inline$t \ge t_0$inline$ решения задачи Коши.


Вектор $inline$u$inline$ находится в распоряжении первого игрока, вектор $inline$v$inline$ находится в распоряжении второго игрока.


Движение начинается при $inline$t = t_0$inline$ из начального состояния $inline$(x_0,\ t_0)$inline$ и протекает под воздействием измеримых по Лебегу функций $inline$u(t) \in P,\ v(t) \in Q$inline$.


В $inline$\mathbb{R}^n$inline$ выделены некоторые непустые выпуклые замкнутые множества $inline$M$inline$ и $inline$N$inline$. Множество $inline$M$inline$ является терминальным множеством первого игрока. Цель первого игрока — добиться выполнения включения $inline$x(t_1) \in M$inline$ при некотором $inline$t_1 \ge t_0$inline$. Множество $inline$N$inline$ является терминальным множеством второго игрока и множеством помехи первого игрока. Цель второго игрока — добиться выполнения включения $inline$x(t_1') \in N$inline$ при некотором $inline$t_1' \ge t_0$inline$. В момент первого попадания точки $inline$x(t)$inline$ на $inline$N$inline$ игра считается успешно завершенной вторым игроком. Дополнительная задача первого игрока — избежать попадания точки $inline$x(t)$inline$ на $inline$N$inline$.


Игра считается успешно завершенной первым игроком в момент первого попадания точки $inline$x(t)$inline$ на $inline$M$inline$ при условии, что для всех предыдущих моментов времени точка $inline$x(t)$inline$ ни разу не попадала на $inline$N$inline$. Таким образом, цели игроков не совпадают, и точка $inline$x(t)$inline$ находится под воздействием противоборствующих управлений $inline$u(t),\ v(t)$inline$.


Будем отдельно рассматривать дифференциальную игру с точки зрения первого и второго игроков.
A: Предполагается, что первый игрок знает:


  1. динамические возможности конфликтно управляемого объекта $inline$x$inline$, то есть матрицы $inline$A,\ B,\ C$inline$, множества $inline$P,\ Q$inline$;
  2. начальное состояние игры $inline$(x_0, t_0)$inline$;

Предполагается также, что первый игрок способен обнаружить множество $inline$N$inline$ не позднее чем за время $inline$\Theta>0$inline$, значению которого определим ниже.


Определим стратегию первого игрока $inline$u(t) = U(x_0, t_0, v_t(\cdot))$inline$ как отображение, определенное на множестве произвольных измеримых функций $inline$v(t) \in Q,\ t \ge t_0$inline$, и обладающее следующим свойством: для произвольной измеримой $inline$v(t) \in Q,\ t \ge t_0$inline$, функция $inline$u(t) = U(x_0, t_0, v_t(\cdot))$inline$ измерима по $inline$t$inline$ и $inline$u(t) \in P$inline$.


Задача A: Найти начальные состояния $inline$(x_0, t_0)$inline$, для которых первый игрок обладает такой стратегией, что она обеспечивает окончание игры для произвольной измеримой $inline$v \in Q$inline$ не позже некоторого конечного момента. Такие состояния $inline$(x_0, t_0)$inline$ будем называть решениями задачи A.


Б:Второй игрок обладает полной информацией о ходе игры.
Определим стратегию второго игрока $inline$v(t) = V(x_0, t_0, u_t(\cdot))$inline$ как отображение, определенное на множестве произвольных измеримых функций $inline$u(t) \in P,\ t \ge t_0$inline$, и обладающее следующим свойством: для произвольной измеримой $inline$u(t) \in P,\ t \ge t_0$inline$, функция $inline$v(t) = V(x_0, t_0, u_t(\cdot))$inline$ измерима по $inline$t$inline$ и $inline$v(t) \in Q$inline$.


Задача Б: Найти начальные состояния $inline$(x_0, t_0)$inline$, для которых второй игрок обладает такой стратегией, что она обеспечивает окончание игры для произвольной измеримой $inline$u \in P$inline$ не позже некоторого конечного момента. Такие состояния $inline$(x_0, t_0)$inline$ будем называть решениями задачи Б.


Будем считать, что $inline$M = M^1 + M^2$inline$, где $inline$M^1$inline$ — линейное подпространство пространства $inline$\mathbb{R}^n$inline$, $inline$M^2$inline$ — выпуклый компакт, $inline$M^2 \subset L^1,\ L^1 \oplus M^1 = \mathbb{R}^n$inline$. Аналогично $inline$N = N^1 + N^2$inline$, где $inline$N^1$inline$ — линейное подпространство пространства $inline$\mathbb{R}^n$inline$, $inline$N^2$inline$ — выпуклый компакт, $inline$N^2 \subset L^1,\ L^1 \oplus N^1 = \mathbb{R}^n$inline$. При этом $inline$\pi$inline$ — оператор ортогонального проектирования из $inline$\mathbb{R}^n$inline$ в $inline$L^1$inline$, $inline$\pi \in \mathbb{R}^{\nu \times n}$inline$. Данные построения нужны для того, чтобы учесть, что игра у нас в общем случае (а при наличии инерционных объектов это так и есть) ведётся в пространстве меньшей размерности чем размерность системы дифференциальных уравнений.


Подраздел 4.1. Достаточное условие разрешимости задачи уклонения первого игрока от множества $inline$N$inline$


Рассмотрим задачу Б — задачу преследования вторым игроком первого и построим для неё множество точек, из которого данная задача будет иметь решение. Так вот, для подобного типа задач Понтрягин придумал способ построения нужного множества — альтернированная сумма — $inline$W(t)$inline$ [3], [5]. Так вот альтернированная сумма мало того является выпуклым компактом, так ещё и $inline$v$inline$-стабильно (т.е. позволяет строить нужные нам стратегии второго игрока), а также обеспечивает выполнение условия существования седловой точки маленькой игры [6, стр. 56] (что означает, что игра в принципе разрешима) — т.е. всё и сразу.


Из [6, стр. 69 — теорема 17.1] следует, что в этих условиях можно воспользоваться теоремой об альтернативе:


Для всякой начальной позиции $inline$(t_0, x_0)$inline$ и выбранного $inline$\bar T \ge t_0$inline$ верно одно из двух утверждений:
1) Либо найдется стратегия $inline$\bar{v}$inline$, которая для всех движений $inline$x(t)=x(t,t_0,x_0,\bar{v})$inline$ обеспечит встречу $inline$\{\tau,x(\tau)\}\in N$inline$ за конечное время $inline$\tau<\bar{T}$inline$. То есть, в классе позиционных стратегий второго игрока разрешима задача преследования (задача Б).
2) Либо, в противном случае, найдется стратегия $inline$\bar{u}$inline$, которая для всех движений $inline$x(t)=x(t,t_0,x_0,\bar{u})$inline$, обеспечит уклонение от множества $inline$\epsilon$inline$-окрестности множества $N$ вплоть до момента времени $inline$\bar{T}$inline$. То есть, в классе позиционных стратегий первого игрока разрешима задача уклонения (задача А).

При этом, ввиду $inline$v$inline$-стабильности множества $inline$W(T)$inline$ на сновании [6, стр. 62 — теорема 15.1] получим, что условие:


$$display$$ \forall t \in [t_0, \bar T]: x(t_0) \notin W(t)$$display$$


является достаточным условием решения задачи уклонения первого игрока из начальной позиции $inline$(t_0, x_0)$inline$ от $inline$\epsilon$inline$-окрестности множества $inline$N$inline$ в течение времени $inline$\bar T$inline$.


Если ресурсы первого игрока, определяемые множеством $inline$P$inline$, превосходят ресурсы второго игрока, определяемые множествами $inline$Q$inline$ и $inline$N$inline$, то найдется такое время $inline$\Theta$inline$, что $inline$W(\Theta) = \emptyset$inline$.
Существование момента $inline$t$inline$, для которого выполнено включение $inline$x(t) \in W(t)$inline$, обеспечивает разрешимость задачи Б за время из диапазона $inline$[t, t+\Theta]$inline$.


То есть можно построить стратегию $inline$u(x_0, t_0, x(t))$inline$, удовлетворяющую условию:


$$display$$ \forall \tau \in [t, t + \Theta]: x(\tau, u(x_0, t_0, x(\tau))) \notin W(\tau).$$display$$


Следовательно, для того, чтобы обеспечить уклонение от множества $inline$N$inline$, необходимо проверять приведенное выше условие как для текущего момента времени $inline$t$inline$, так и для всех последующих моментов времени на глубину $inline$t + \Theta$inline$.


Множество $inline$\Phi(\Theta, t)$inline$, построенное в виде:


$$display$$ \Phi(\Theta,t) = \bigcup_{\tau\in[t,t+\Theta]}W(\tau),$$display$$


характеризует минимальное расстояние между преследователем и убегающим в момент времени $inline$t$inline$, на котором преследование может быть завершено успешно. Откуда в общем-то и следует, что обнаруживать помеху мы должны за такое время (не за расстояние), за которое сможем от неё уклониться, что логично.


И, если настоящие математики на некоторое время закроют глаза, так как следующее рассуждение является ооочень частным случаем, то на пальцах это выглядит следующим образом: пусть множество помехи у нас шарик радиуса 5, множество управления шарик радиуса 2, а помехи -- соответственно 1, тогда время за которое мы должны обнаруживать помеху никак не должно быть меньше 4. Математики могут открывать глаза.


Аналогичным образом можно решить задачу наведения на множество $inline$M$inline$ в условиях отсутствия помехи. При этом достаточным условием наведения траектории системы на множество $inline$M$inline$ является условие:


$$display$$ \exists T: z(t_0) \in \bar{W}(T).$$display$$


Подраздел 4.2. Построение управления первого игрока для решения задачи А


Пусть первый игрок может заметить множество помехи $inline$N$inline$ не раньше, чем за время $inline$\Theta$inline$. Это означает, если ввести равномерное разбиения $inline$\omega_K$inline$ временного отрезка $inline$[t_0,\ T]$inline$, что первый игрок может заметить помеху не раньше, чем за $inline$s$inline$ шагов по времени:


$$display$$ s = \frac{\Theta}{\Delta_{\omega_K}}.$$display$$


Тогда возможность перехвата вторым игроком первого (успешного завершения задачи Б) в момент $inline$\tau + \Delta_{\omega_K}$inline$ определяется условием:


$$display$$x(\tau) \in \Delta_{\omega_K} \pi e^{\tau A}CQ + W(\tau),\, \tau\in[t,t+\Theta].$$display$$


Здесь первый мы в момент времени $inline$\tau$inline$ проверяем, возможна ли поимка первого игрока на следующем шаге $inline$\tau + \Delta_{\omega_K}$inline$.
Будем строить управление первого игрока, считая, что выполнены следующие предположения.


На второго игрока наложены следующие ограничения:


  1. второй игрок никогда не сможет достичь терминального множества $inline$M$inline$:


    $$display$$\forall T: \Phi( T, \tau) \cap M \neq \emptyset;$$display$$


    в противном случае стратегия "закрыть собой" обеспечит победу второму игроку;


  2. начальная позиция игры не принадлежит множеству $inline$\Phi( T, \tau)$inline$, откуда возможно успешное завершение задачи Б — преследования вторым игроком первого:

    $$display$$x(t_0)\notin \Phi( T, \tau),$$display$$

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



    Рассмотрим задачу уклонения первого игрока от множества $inline$N$inline$ отдельно от задачи наведения на целевое множество $inline$M$inline$.


    При этом, если для нашей игры выполнено приведенное выше предположение, то при любом начальном значении $inline$x_0: x_0 \notin \Phi(\Theta,\tau)$inline$, можно показать, что существует такая стратегия уклонения, что расстояние между траекторией $inline$x(t)$inline$ и множеством избегания $inline$\Phi(\Theta,\tau)$inline$ может будет больше $inline$\epsilon$inline$ и зависеть только от хода игры, а не от её начального значения.


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


    При этом, в качестве стратегии уклонения можно выбрать управление экстремального сдвига, обеспечивающее, согласно теореме об альтернативе [6, стр. 69], уклонение от множества $inline$N$inline$ в виде:


    $$display$$ U(\theta - t_i, x(\theta - t_i)) \in \underset{u \in P} {\mathrm{argmax}} \left\{ \pi u,\tilde{\psi_i}(x(\theta - t_i)) \right\},$$display$$


    где


    $$display$$\tilde{\psi_i}(x(\theta-t_i))\in \underset{\psi \in S_1(0)} {\mathrm{argmin}} \left\{ c(\Phi(\Theta,\tau),\psi)-(\pi x(\theta-t_i),\psi) \right\}.$$display$$


    Так как выполняются условия леммы 15.2 [6, стр. 65], то, повторяя её доказательство, получаем нужную оценку для минимального евклидового расстояния между траекторией $inline$x(t)$inline$ и множеством $inline$\Phi(\Theta,\tau)$inline$, которая стремится к $inline$0$inline$ при измельчении шага разбиения по времени.


    Рассмотрим теперь задачу наведения траектории системы на терминальное множество $inline$M$inline$. При отсутствии каких-либо помех в виде множества $inline$N$inline$ и выполнении условия теоремы об альтернативе задача наведения разрешима, если начальная точка $inline$x(t_0)$inline$ принадлежит множеству управляемости, а само управление наведения будет иметь вид:


    $$display$$ U(\theta-t_i,x(\theta-t_i))\in \underset{u \in P}{\mathrm{argmin}}(\pi u,\tilde{\psi_i}(x(\theta-t_i))).$$display$$


    Будем строить управление наведения--уклонения в следующем виде:


    • определяем $inline$T$inline$ в ходе построения альтернированного интеграла, решающего для нашей системы задачу наведения;
    • рассматриваем движение системы в момент $inline$t_i$inline$, $inline$i=\overline{0,T}$inline$.

    Проверяем для следующего шага пустоту пересечения:


    $$display$$ \pi (x(t_i) + P) \cap \Phi(\Theta,\tau)$$display$$


    • если множество достижимости системы из точки $inline$x(t_i)$inline$ не пересекается с множеством $inline$\Phi(\Theta,t_i)$inline$, то первый игрок выбирает управление наведения;

    • в противном случае первый игрок выбирает управление уклонения.

    Отметим, что в общем случае условие окончания игры наведения может быть нарушено, т.е. $inline$w(t_{i+1})\notin W(T)$inline$, где $inline$T$inline$, $inline$W(T)$inline$ — это найденные ранее значения времени окончания игры и соответствующий ему альтернированный интеграл. В данном случае необходим пересчёт альтернированного интеграла Л.С. Понтрягина $inline$W(T^j)$inline$ и поиск соответствующего ему нового времени окончания игры $inline$T^j$inline$, $inline$j$inline$-натуральное число.


    Применяя описанную стратегию, первый игрок в каждый момент времени будет либо наводиться на терминальное множество $inline$M$inline$, сокращая расстояние до него, либо уклоняться от множества помехи $inline$N$inline$, не попадая внутрь него.


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


    $$display$$ \bar{T}_{min} = T$$display$$


    — время завершения игры не меньше времени наведения, а $inline$\bar{T}_{max}$inline$ в общем случае оценить невозможно, хотя оно и существует.


    Часть пятая — собираем всё вместе


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


    Соотвествующая система дифференциальных уравнений будет иметь вид:


    $$display$$\begin{array}{rcl} \dot{x}_1 &=& x_3,\\ \dot{x}_2 &=& x_4,\\ \dot{x}_3 &=& u_1 - v_1,\\ \dot{x}_4 &=& u_2 - v_2. \end{array}$$display$$


    Здесь $inline$x_1,\ x_2$inline$ — координаты объекта $inline$x$inline$ на плоскости, а $inline$x_3,\ x_4$inline$ — компоненты его скорости, $inline$x(t) = [x_1(t),\ x_2(t),\ x_3(t),\ x_4(t)]^{T} \in \mathbb{R}^4;$inline$
    объект начинает движение из точки $inline$x(t_0) = [3,\ 4,\ 0,\ 0]^{T} \in \mathbb{R}^4.$inline$


    Ограничения на управления $inline$u(t),\ v(t)$inline$ имеют вид


    $$display$$u(t) = [u_1(t),\ u_2(t)]^{T} \in P \subset \mathbb{R}^2,\ P = S_{1} ([0,\ 0]^{T}),$$display$$


    $$display$$v(t) = [v_1(t),\ v_2(t)]^{T} \in Q \subset \mathbb{R}^2,\ Q = S_{0.9} ([0,\ 0]^{T}).$$display$$


    Первый игрок стремится перевести траекторию системы за конечное время на терминальное множество


    $$display$$M = S_{1}([12,\ 6]^{T}),$$display$$


    избежав при этом попадания на множество помехи


    $$display$$N = S_{2}([8,\ 5]^{T}).$$display$$


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


    $$display$$T = 22,30.$$display$$


    Траектория движения системы и компоненты управления первого игрока показаны на следующих рисунках.


    Траектория движения системы.
    image


    Зависимость первой компоненты управления от времени.
    image


    Зависимость второй компоненты управления от времени.
    image


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


    $$display$$T = 8,80$$display$$


    а соответствующие траектории показаны на следующих рисунках.


    Траектория движения системы.
    image


    Зависимость первой компоненты управления от времени.
    image


    Зависимость второй компоненты управления от времени.
    image


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


    Выводы


    Что в итоге получаем (если дочитали, конечно, до этого места), — "наивный" и "продвинутый" способы управления динамической моделью, позволяющие конструктивно строить управление зависящие исключительно от позиции (см. книги Н.Н. Красовcкого, А.И. Субботина и Л.С. Понтрягина), причем не обладая полными знаниями о помехе.


    Исходники посмотреть можно здесь: репозитарий на GitHub


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


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


    Отдельное спасибо Екатерине Кудешовой за критику.


    Бибилиография


    [1] Понтрягин Л.С. Жизнеописание Л. С. Понтрягина, математика, составленное им самим», М, 1983


    [2] Красовский Н.Н. Теория управления движением: Линейные системы, М, Наука, 1968


    [3] Григоренко Н.Л. Математические методы управления несколькими динамическими процессами, М, Издательство Московского Университета, 1990


    [4] Ю.Н. Киселёв, С.Н. Аввакумов, М.В. Орлов ОПТИМАЛЬНОЕ УПРАВЛЕНИЕ. ЛИНЕЙНАЯ ТЕОРИЯ И ПРИЛОЖЕНИЯ, М, 2007


    [5] Григоренко Н.Л., Камзолкин Д.В., Пивоварчук Д.Г. Линейные дифференциальные игры, М, 2007


    [6] Красовский Н.Н., Субботин А.И. Позиционные дифференциальные игры, М, Наука, 1974


    [7] Ли Э.Б., Маркус Л. Основы теории оптимального управления, М, Наука, 1972


    [8] Понтрягин Л.С., Болтянский В.Г., Гамкрелидзе Р.В., Мищенко Е.Ф. Математическая теория оптимальных процессов, М, Наука, 1969

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332142/


Метки:  

Дайджест свежих материалов из мира фронтенда за последнюю неделю №270 (3 — 9 июля 2017)

Воскресенье, 09 Июля 2017 г. 23:33 + в цитатник

Отменяется ли бунт роботов?

Воскресенье, 09 Июля 2017 г. 23:19 + в цитатник
(Неформальная рецензия на книгу Дэвида Минделла «Восстание машин отменяется! Мифы о роботизации», “Альпина нон-фикшн”, 2017)

image
(User Chmouel on en.wikipedia: file )

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

С основным выводом полностью согласен: абсолютная автономность – вредный миф. По крайней мере, на ближайшие десятилетия. На собственном и на чужом опыте автор подробно показывает, что в наши дни наиболее успешными являются системы, где достаточно полно реализуется взаимодействие человека с автоматом, а не отчуждение человека от процесса принятия решений. В правильности этой идеи лично я убедился на своем скромном примере игровых ботов для игры КР2HD: бот для планетарных битв по нашей с соавтором идее должен быть полностью автономным, и сейчас этот проект забуксовал. В новом проекте бота для прохождения битвы у Роджерии, благодаря этой многоходовой битве, можно получить львиную долю очков всей игры, я выбрал полуавтоматический режим: относительно рутинные операции (некоторые не тривиальные, так как приходится использовать распознавание образов) выполняет бот, но при наступлении определенных условий он не пытается «блеснуть интеллектом», а запрашивает вмешательство игрока. Делает он это не часто: сейчас успел написать вышеизложенное на одном компьютере, а бот в то время накручивает мне очки на другом. И так как этот подход оправдал себя, опишу его подробнее в отдельной статье. За раздражающими примерами попыток разных программ «блеснуть интеллектом» далеко ходить не приходится: в старом Microsoft Word 2000 при установках по умолчанию затруднительно набирать фразу «Цикл со счетчиком i …» — Ворд тут же заменяет «i» на «I».

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

Как это ни забавно, именно такая высокотехнологичная компания, как Google, в своей риторике шагает назад в XX век, архаически выставляя водителя пассивным наблюдателем. Их «новый» подход становится жертвой всех трех порожденных XX веком мифов о роботах и автоматизации: 1) автомобильная техника должна логически развиваться до полной, утопической автономности (миф о линейном прогрессе); 2) автономные системы управления освободят водителя от обязанности водить (миф замещения); 3) автономные машины могут действовать полностью самостоятельно (миф о полной автономности).


Прочитав в книге массу историй, в частности, о том, что при всех посадках на Луну, начиная с Нила Армстронга, – все астронавты выключали автоматическую посадку и садились вручную, пользуясь при этом информацией бортового компьютера, аналогично было при посадке Шатлов на Землю, я согласен с автором. Однако чуть ниже автор рассказывает о новом проекте, в котором он участвует. Это проект ALIAS – система автоматического управления самолетом. Все выглядит хорошо, но поставлена амбициозная задача: минимумом усилий оснастить ею любой самолет так, чтобы не сертифицировать снова воздушное судно полностью, не вмешиваться в его конструкцию. В частности, использовать компьютерное зрение, чтобы считывать информацию с дисплеев, установленных в кабине самолета. Прочитав это, схватился за голову – перестал что-либо понимать и могу только догадываться. Может, мне показалось, но автор хочет разместить в кресле второго пилота web-камеру, направленную на дисплей и распознавать информацию с этого дисплея! Это же дико усложнит систему и сильно понизит надежность. Разве не проще подсоединиться к бортовому компьютеру с помощью USB-кабеля и качать цифровой поток напрямую без всякого распознавания? Возможно, что любое подсоединение, даже только на чтение, требует сертификации, но идти на распознавание, чтобы только избежать сертификации — это абсурд. Так же как в этом плане абсурдны мои боты с распознаванием – если бы у игры был COM-интерфейс, все задачи моих ботов решались бы тривиально.

Интересно, что во всей книге автор нечасто произносит «ИИ», при этом заявляя, что не будет обсуждать вопрос «может ли машина мыслить». Возможно, вопреки общему мнению, автор не считает задачи распознавания образов задачами ИИ? Дело не в названии, а в том, что это принципиально иные задачи. Упрощенно говоря, в исправной вычислительной среде дважды два всегда будет четыре, но та же среда не всегда правильно распознает цифру «2» с бумаги или с монитора. Пока человек распознает образы гораздо лучше компьютера, но и он ошибается. Так, не все и не всегда сразу могут понять каждое слово, что поет какой-нибудь вокально-инструментальный ансамбль на казалось бы знакомом языке. И в зрительной области у человека бывают иллюзии и миражи:

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

А пугаться-то было нечего. Это была не галлюцинация, а иллюзия, то есть неправильное, искаженное отражение реального предмета. Халат и шляпа показались человеком.
(Константин Платонов, Занимательная психология, «РИМИС», 2011.)


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

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

Конечно же, Лем предвидел не всё. Так, он не предвидел взлома, не предвидел вирусов и «троянских коней» (хотя и моделирует случаи неадекватного проведения роботов, но не в результате предумышленного взлома ОС). Однако странно, что в наше время постоянных катастроф, связанных со взломами, о них ничего не говорит Минделл. На мой взгляд, в этом плане он чем-то напоминает Азимова, у которого три закона робототехники обеспечивают гармоничное сосуществование людей и машин. При этом не автономность, то есть подконтрольность оператору-человеку, может не спасти — Минделл неоднократно отмечает, что грань между автономными и неавтономными устройствами постепенно стирается и одно и тоже устройство может работать как в автономном, так и в не автономном режиме, подобно бортовому компьютеру упомянутого выше спускаемого на Луну отсека «Аполлон». При этом выглядит очевидным, что робот, в которого внедрен «троян», превратится в шпиона, а робот, зараженный вирусом, может совершать крайне неадекватные и опасные действия. Почему в книге об этом не сказано? Может потому, что такая слишком реальная угроза опровергает слишком оптимистичный заголовок книги про отмену восстания машин?
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332734/


[recovery mode] Техподдержка 3CX отвечает: не открывается файл конфигурации клиента для Android и плохое качество звука на Android

Воскресенье, 09 Июля 2017 г. 22:19 + в цитатник
Некоторые пользователи Android жалуются на то, что не могут открыть вложение — файл конфигурации клиента 3CX для Android, который приходит к ним в Приветственном письме 3CX. Поскольку создать учетную запись SIP в клиенте вручную невозможно (она не будет корректно работать с 3CX), пользователь остается без работающей SIP телефонии на своем устройстве.

Для решения этой проблемы вкратце объясним принцип работы файла конфигурации.

Когда вы устанавливаете клиент 3CX для Android, он создает файловую ассоциацию на системном уровне для файлов с расширением 3cxconfig (файл автонастройки клиента имеет примерно такой вид 3cxProv_170621074918_152.3cxconfig). Такие файлы распознаются и открываются в Android приложением 3CX Client, но только в том случае, если они открываются непосредственно из почтового клиента.

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

Чтобы избежать эти неприятности, рекомендуем использовать один из протестированных почтовых клиентов для Android:

  • Email (почтовое приложение Android по умолчанию)
  • GMail
  • Yahoo
  • Outlook
  • Microsoft OWA / Office365
  • Yandex.Почта
  • Mail.com
  • BlueMail

Если вы хотите сохранить файл и открыть его, приводим список протестированных файловых менеджеров:
  • File Manager (Maple Media) – нажмите на файле, выберите 3CX
  • ES File Explorer – нажмите на файле, выберите Other > 3CX
  • File Manager (Mobile Clean System Lab) – нажмите на файле, выберите Other > 3CX
  • File Manager + – нажмите на файле, выберите Other > 3CX
  • Total Commander – нажмите на файле, выберите Open with > Open with * > 3CX
  • File Commander (MobiSystems) – длительное нажатие на файле, нажмите в углу окна, выберите Open as > Other > 3CX
  • FX File Explorer (NextApp) – нажмите на файле, выберите Open as > Application/* > 3CX

Решаем проблемы с качеством звука на Android


На рынке существует великое множество Android-смартфонов, и конечно мы не можем протестировать их всех. Однако мы предусмотрели несколько параметров клиента 3CX для Android, чтобы улучшить качество звука на самых разных устройствах. Все настройки делаются в разделе Параметры > Параметры аудио.
  • Эхоподавление – по умолчанию должно быть выключено. Современные телефоны имеют аппаратное эхоподавление. Однако старые и недорогие модели могут не иметь этого модуля, поэтому, если на вашем телефоне возникает эхо во время разговора, либо включите эту опцию, либо рассмотрите возможность использования более нового устройства.
  • Обнаружение тишины – по умолчанию должно быть выключено. Этот режим подавляет передачу трафика при молчании абонента, однако может вызывать артефакты при передаче голоса.
  • Усиление микрофона – если вас слышно слишком тихо, увеличение этого параметра повысит громкость звука для вашего собеседника. Например, для телефонов Nexus 5, 5x и HTC One V рекомендуется установить его в 2.
  • Микрофон – большинство устройств обеспечивают лучшее качество в Расширенном режиме. Однако, если вы слышите треск, попробуйте установить Обычный режим, особенно на старых устройствах. Например, HTC ONE SV лучше “звучит” в Обычном режиме.
  • Аудиоподсистема – для большинства устройств рекомендуется ускорение обработки звука Open SL. Однако некоторые телефоны обеспечивают лучшее качество, используя подсистему Java.

image

Отдельная рекомендация для телефонов Samsung – если вы использовали с телефоном Bluetooth гарнитуру, а затем отключили ее, не отключив Bluetooth модуль, 3CX клиент для Android будет искажать звук. Для устранения этой проблемы отключите Bluetooth и перезапустите клиент.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332728/


ПЛК от производителей Овен, Segnetics и Schneider Electric для HVAC

Воскресенье, 09 Июля 2017 г. 20:50 + в цитатник
В этой статье краткий обзор программируемых контроллеров для HVAC (отопление, вентиляция и кондиционирование) от 3х производителей:

Исходя из личного опыта их использования.




ОВЕН ПЛК63/73




Итак, два брата акробата: ПЛК63 –обитатель DIN рейки и ПЛК73 – упрямый форточник (для монтажа вам придется вмонтировать его в дверцу щита)
При любой модификации вы получаете – 8 дискретных входов, 8 аналоговых входов. Для задач автоматизации вентиляционных установок такого количества датчиков вполне хватает.
ПЛК73 может похвастаться 2мя COM портами, если докупить дополнительную плату ПИ73. Вариаций 5шт. Лично я считаю самой интересной ПИ73-5 — RS-485 * 2шт. У ПЛК63 всегда есть RS485 на борту.
Еще у 73го 4 строчки на дисплее против 2х строчек на ПЛК63.
Батарейку в часах и память обе модели имеют сразу (но помять для переменных меньше, чем у segnetics). Механизм сохранения в ПЗУ у этих ПЛК веселее, чем Segnetics. Как я понял, в ПЗУ данные записываются только на момент выключения питания. В остальное время данные пишутся в ОЗУ.
И у той и у другой модели куча вариаций выходов, которые называют ВУ (выходное устройство). Это могут быть реле, ЦАП тока или напряжения, оптотранзисторы или оптосиммисторы. Для производителя вариации создавать не сложно, т.к. основная плата одна и в нее могут быть впаяны разные модули ВУ.
Нужно больше релейных выходов? Не проблема — докупаем модуль МР1 (8 реле). Модуль только такой — ОВЕН больше вариаций нам не дает.
Для ПЛК63 – Реле плюс 5 шт ВУ, которые вы выбираете при покупке. Для ПЛК73 – 4 транзистора и 4 ВУ.
Вообще ПЛК73 спорная модель. Больше всего от чего бесятся люди – суровые жесткие кнопки. О да… Кнопки заставят неопытного человека испытывать весь спектр негативных эмоций. Ну и аналоговых выходов у ПЛК73 меньше.
Поработав с обеими моделями, я оставил фаворитом ПЛК63. Как-то серьёзнее он выглядит, и кнопки легче нажимаются. Но очень жаль, что тут только один RS485.

Хочется второй RS485, Карл!


Что же, если очень хочется, помните, что у вас есть RS232-debug – тот порт, через который мы льем прошивку. Правда придется докупить преобразователь RS232 to RS485.

Среда программирования


Codesys 2.3. Про нее уже много чего написано. И не один ОВЕН ее использует (это плюс). Есть много бибилиотек (обратите внимание на Oscat). Среда не наша, а немецкая (как удивительно, да?). Функционала много, языки и текстовые и графические (расширенный МЭК 61131-3). Детальнее читаем в книжках

А что там с Modbus?


Slave. Есть из коробки, сразу дает читать входа, выхода, нажатие кнопок, часы. Адреса новым переменным придется придумывать отдельно. Читается все без групповых запросов.
Но если очень хочется, можно и групповыми, есть описания в руководстве. Однако адреса в руководстве даны с ошибками.
Не огорчайтесь — один добрый человек уже нашел правильные адреса. Можно найти на форуме ОВНА или тут (уже залил, чтоб не потерялось).
Режим Мастер. Тут уже сложнее. Вам придется применить такую страшную вещь как «навыки программирования» – берете библиотеку Modbus.lib (ну или сами сделаете) и пишите взаимодействие с внешними устройствами. Звучит страшновато, но в некоторой мере это даст больше гибкости. Например, можно написать работу с кучей типовых устройств (те же частотные преобразователи), а не создавать кучу однотипных переменных

Плюсы
  • среда программирования;
  • легко создать меню с уставками и настройками (Segnetics отдыхает)
  • часы и память всегда «с собой»;
  • возможность работать с нестандартными протоколами по COM порту;
  • больше входов и выходов у ПЛК (в сравнении с Pixel);
  • «из коробки» в меню ПЛК можно изменить типы датчиков, посмотреть их показания
  • питание от 220В (наверно плюс, хотя придется предусматривать защиту по питанию в виде предохранителей и автоматов — уже был опыт с срабатыванием внутренней защиты по питанию)

Минусы:
  • все модификации с питанием только от 220В;
  • кнопки ПЛК73;
  • модуль расширения только один;
  • текстовый дисплей у ПЛК63 большой, но бестолковый — 2 x 16 символов;
  • вход в меню с уставками одной кнопкой «Ввод». Мне лично не нравится, т.к. усложняет создание дополнительных меню;
  • неудобно переносить меню из одной модели ПЛК в другую (тиражировать однотипные настройки). Приходится создавать заново. Раздражает.


Из личной коллекции:





Segnetics SMH2g и Pixel



Итак, Pixel и его большой брат-форточник SMH2g. Есть еще SMH2gi с доступом к linux. Вроде как дает больше возможностей для любителей той самой страшной вещи как «навыки программирования».
Относительно I/O SMH2g не особо интересен без модулей расширения (на борту 4 DI / 4 DO и 5 DI / 2 DO для SMH2gi). У Pixel дела получше (6DI/3DO/5AI/2AO), но без модулей тоже не всегда обойдешься.
Важно заметить — Pixel гальванической изоляции по аналоговым выходам и входам НЕТ.
Конечно, Segnetics в плане расширяемости заморочился. Можно даже Ethernet для Pixel сделать.
Однако чтобы часы тикали без сбоев понадобится купить у Segnetics батарейку. И позиция эта отдельная. Так же с памятью. Докупаете отдельно (если вам, например, нужна работа по расписанию).
Еще маленький камешек в огород Segnetics — злые цены на кабель для соединения ПЛК с модулями расширения. На деле — обычный шлейф с разъемами IDC. Изготовить можно самим, купив все в магазине или радиорынке. Получится раз в 5 дешевле. Главное не напутайте с подключением.
В плане внешнего вида, удобства нажатия кнопок, дисплея – тут все отлично. Для большего пафоса Segnetics даже ни слова по-русски на корпусе контроллера не написал. Ну и нам приятно.

Среда программирования


SMLogix. Разработана Segnetics. Язык только графический со всеми минусами графических языков. Чтобы как-то удобнее можно было делать правки в программе — желательно делать блоки (они же макросы) по типу матрешки:
Так вы не потеряете внешние связи в программе.
Segnetics значительно повысил притягательность своего оборудования благодаря конфигуратору SMConstructor, который генерирует рабочую программу ПЛК для систем вентиляции (теперь и для ИТП). Останется подпилить ее под свои нужды.
Однако тот же Pixel не даст вам полного доступа к COM порту. Нужный вам протокол вы не напишите. Только Modbus.

А что там с Modbus?


Все неплохо. Каждую переменная, которую вы создаете, автоматически становится переменной Modbus. Так реализуется режим slave.
Для Master все тоже неплохо. Можно подгружать карты Modbus от других контроллеров Segnetics или создавать (и сохранять на компьютер) карту переменных отдельно. Для большинства задач этого должно хватать

Плюсы:
  • мало времени для создания типовых программ для вентиляции;
  • приятный дизайн оборудования;
  • до 8 модулей расширения
  • можно всегда получить Ethernet

Минусы:
  • слабый «язык» программирования
  • отсутствие гальванической изоляции аналоговых входов и выходов (у Pixel)
  • «из коробки» нет возможности менять тип аналоговых датчиков
  • нет возможности работать с нестандартными протоколами по COM порту (что-то может получится через linux у SMH2gi, но сама среда программирования такой возможности не даст)
  • «плюющиеся» клеммы у Pixel. Наконечник типа НШВИ 1,5-8 частенько будет выталкиваться из клемм при закручивании. Рекомендую длиннее — НШВИ 1,5-12. Иначе рискуете много материться при монтаже.


Из личной коллекции:





Schneider Electric M171/172



Познакомился с этой линейкой для HVAC около года назад. По тому, что прочитали на форумах — разработка итальянская, видимо была перекуплена Schneider.
М171 версия проще и дешевле, потому решили не мелочиться и взять M172
По входам и выходам – сказка. Например, модель TM172PDG42R – 42 I/O. Интерфейсы — Ethernet (M172), RS485 (у М172 их даже 2 шт.), CAN. Экран неплохой, графический.
Прибавим к этому кучу всяких модулей расширения и получаем весьма интересное решение в плане аппаратной части.

Среда программирования


SoMachine HVAC. И тут начинается первая ложка дегтя. Среда не особо дружелюбная для начинающего. С документацией все плохо – готовьтесь шерстить поисковик.
Среда разбита как бы на несколько отдельных программ. У них даже ярлыки свои. В одной вы инициализируете интерфейсы и аппаратную часть, в другой рисуете экран, в третьей пишете программу. Youtube все лучше расскажет:



Есть и графические и текстовые языки. Есть ST (переменных типа Time нет – будете крутиться с DWORD). Так же тут дурацкая система инициализации переменных. Вы не сможете просто написать:
Var
Var1 : bool;
Var2 : bool;
End_var

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

А что там с Modbus?


Каждая переменная имеет свой адрес. Т.е. slave будет сразу. С Modbus в этом плане все неплохо. Все, да не все. Вот вам еще 1,5 ложки дегтя:
  • 1 ложка – Master режим ВООБЩЕ не работает.
    Сколько наш программист не бился, так и не смог запустить. Вместо этого на выходе RS485 мы получали только первый байт посылки. Т.е. адрес опрашиваемого устройства. Так на одном объекте нам пришлось дополнять щит – вставлять Овна ПЛК100, чтоб 100тый выполнял роль Master'a (картинка ниже);
  • 0.5 ложки – Slave режим на объекте у нас падал в течении 12ти часов работы. Это касалось Modbus TCP. Лечится только сбросом питания. А вот тут уже не знаем чего делать – попробуем перенести опрос на COM порт.


Плюсы:
  • большая линейка оборудования, модулей расширения;
  • много интерфейсов;
  • есть модификации ПЛК с большим количеством I/O;
  • графический дисплей (старшие модели);
  • есть выносная и настенная панель;
  • неплохой внешний вид;
  • несколько языков программирования.

Минусы:
  • сырой продукт (во всяком случае, М172). Касается как ПО, так и самих ПЛК;
  • недружелюбная среда разработки;
  • нет нормальной документации по работе в среде программирования;
  • никакой тех поддержки. Дистрибьютор еще чего-то пытался помочь – но и он не особо выручил.
  • долгая поставка оборудования. В случае, когда надо все «вчера» – эти ПЛК не ваш выбор.


Из личной коллекции. ПЛК100 (справа) как раз работает как Мастер вместо М172:




P.S. Еще немного о средах


Codesys 2.3. Порог вхождения выше, чем у среды программирования Segnetics, но и вещи можно делать более интересные (тут даже некоторые основы ООП есть). Потому вот так без опыта программу будет сложно написать/нарисовать.
Если вы решили изучать Codesys, то крайне рекомендую начинать с ST (Structured Text), паскалеподобного языка. У текстовых языков есть важнейшее преимущество – возможность переноса на другие среды, поддерживающие язык.
Автор уже имел опыт переноса программы из ПЛК63 на тот самый Schneider M172 с минимальными изменениями.
Графические языки изначально поманят вас наглядностью. Но не поддавайтесь – в больших программах вы запутайтесь, да и еще гибкость потеряете (элементарно не сможете закомментировать участок кода).
А с какими ПЛК работаете вы?

Проголосовало 15 человек. Воздержалось 7 человек.

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

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332722/


Метки:  

Android Architecture Components. Часть 2. Lifecycle

Воскресенье, 09 Июля 2017 г. 20:06 + в цитатник
image
Как я уже упоминал в предыдущем материале, компонент Lifecycle призван упростить работу с жизненным циклом, а имено избежать калбеков с Activity/Fragment в наш компонент, который должен реагировать на события жизненого цикла. В этой статье, мы подробно разберем как он устроен и как с ним работать.

Сам компонент состоит из классов: Lifecycle, LifecycleActivity, LifecycleFragment, LifecycleService, ProcessLifecycleOwner, LifecycleRegistry. Интерфейсов: LifecycleOwner, LifecycleObserver, LifecycleRegistryOwner.

Lifecycle — класс, который хранит информацию про состояние жизненного цикла и разрешает другим объектам отслеживать его c помощью реализации LifecycleObserver. Состоит из методов: addObserver(LifecycleObserver), removeObserver(LifecycleObserver) и getCurrentState(). Как понятно из названий для добавления подписчика, удаления и соответственно получения текущего состояния.

Для описания состояния есть два enum. Первый Events — который обозначает изменение цикла и второй State — который описывает текущее состояние.

Events — повторяет стадии жизненного цикла и состоит из ON_CREATE, ON_RESUME, ON_START, ON_PAUSE, ON_STOP, ON_DESTROY, а также ON_ANY который информирует про изменения состояния без привязки к конкретному этапу. Отслеживание изменений цикла происходит с помощью пометки метода в обсервере аннотацией OnLifecycleEvent, которому как параметр передается интересуещее нас событие.
@OnLifecycleEvent(Lifecycle.Event.ON_ANY)
   void stateUpdated() {
      //будет вызваться при каждом изменении состояния жизненого цикла у овнера.
   }


State — состоит из следующих констант: INITIALIZED, CREATED, STARTED, RESUMED, DESTROYED. Для получения состояния используеться метод getCurrentState() из Lifecycle. Также в Enum State реадизован метод itAtLeast(State), который отвечает на вопрос являтся State выше или равным от переданого как параметр.

Для работы с компонентом Lifecycle, нам нужно определить owner, то есть владельца жизненного цикла и observer, того кто на него будет подписан. У owner может быть любое количество подписчиков, также стоит отметить что observer будет проинформирован про изменение состояния, еще до того как у owner будет вызван метод super() на соответствующий метод жизненного цикла.

Owner должен реализовывать интерфейс LifecycleOwner, который содержит один метод getLifecycle(), который возвращает екземпляр класса холдера Lifecycle.

Observer должен реализовать интерфейс маркер LifecycleObserver.

Для самостоятельного обьявления кастомной Activity/Fragment, как owner-а, которые еще не поддерживают новый компонент и соответственно не имеют реализации Lifecycle, созданы: класс LifecycleRegistry и интерфейс LifecycleRegistryOwner.

Интерфейс LifecycleRegistryOwner, который в свою очередь расширяет интерфейс LifecycleOwner с единственым отличием в том что метод getLifecycle() возвращает LifecycleRegistry вместо Lifecycle.

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

Вот как выглядит реализация:
public class MyFragment extends Fragment implements LifecycleRegistryOwner {
    LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
    @Override
    public LifecycleRegistry getLifecycle() {
       return lifecycleRegistry;
    }
}


В пакете android.arch.lifecycle приведено 4 реализации owner: LifecycleActivity, LifecycleFragment, LifecycleService, ProcessLifecycleOwner.

LifecycleActivity — это FragmentActivity c реализацией LifecycleRegistryOwner. Является временным решением для упрощения работы и как сказано в документации будет пока Lifecycle не будет интегрирован с support library.

LifecycleFragment — это Fragment c пакета support.v4, который также как и в случае с LivecycleActivity реализовывает LifecycleRegistryOwner и является временным решением.

LifecycleService — это Service, который является также LifecycleOwner.

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

Для того чтоб, связать owner и observer нужно у owner вызвать getLifecycle().addObserver(LifecycleObserver) Ниже я продемонстрирую работу всех этих классов. Для демонстрации, я создал класс SomeObserver, который будет логировать вызовы ON_CREATE и ON_STOP, я его буду использовать для всех видов owner-ов, поэтому для упрощения я добавил enum, константа с какого будет передаваться в конструктор и потом использоваться чтоб отличить владельца по логам:

public class SomeObserver implements LifecycleObserver {
   private Owner owner;
   public SomeObserver(Lifecycle lifecycle, Owner owner) {
      this.owner = owner;
      lifecycle.addObserver(this);
   }
   @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
   void onCreate() {
      Log.d(«Observer», owner + «: onCreate»);
   }
   @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
   void onStop() {
      Log.d(«Observer», owner + «: onStop»);
   }
   enum Owner {
      ACTIVITY, FRAGMENT, PROCESS, SERVICE
   }
}


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

Листинг LifecycleActivity:
public class MainActivity extends LifecycleActivity {
   SomeObserver someObserver = new SomeObserver(getLifecycle(), SomeObserver.Owner.ACTIVITY);
   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      Log.d(«Owner», «onCreate»);
      setContentView(R.layout.activity_main);
   }
   @Override
   protected void onStop() {
      super.onStop();
      Log.d(«Owner», «onStop»);
}
}


Листинг LifecycleFragment:
public class MyFragment extends LifecycleFragment {
   SomeObserver someObserver = new SomeObserver(getLifecycle(), SomeObserver.Owner.FRAGMENT);
  
   public MyFragment() {
   }
   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container,
 Bundle savedInstanceState) {
      return inflater.inflate(R.layout.fragment_my, container, false);
   }
}


Листинг LifecycleService, он отрабатывает 5 секунд и завершается, его я запускаю из Application:
public class MyService extends LifecycleService {
   SomeObserver someObserver;
   @Override
   public int onStartCommand(Intent intent, int flags, int startId) {
      someObserver = new SomeObserver(getLifecycle(), SomeObserver.Owner.SERVICE);
      final Handler handler = new Handler();
      handler.postDelayed(new Runnable() {
         @Override
         public void run() {
            stopSelf();
         }
      }, 5000);
      return super.onStartCommand(intent, flags, startId);
}
}


И для ProcessLifecycleOwner я решил расширить Application, как можно заметить ProcessLifecycleOwner сделан как singleton и являеться абсолютно самостоятельным компонентом:
public class CustomApplication extends Application {
   private SomeObserver processObserver;
   @Override
   public void onCreate() {
      super.onCreate();
      processObserver = new SomeObserver(ProcessLifecycleOwner.get().getLifecycle(), SomeObserver.Owner.PROCESS);
      Intent intent = new Intent(this, MyService.class);
      startService(intent);
}
}


Полный листинг вы можете посмотреть по линке: here

Также полезные ссылки:

developer.android.com/reference/android/arch/lifecycle/package-summary.html

developer.android.com/topic/libraries/architecture/lifecycle.html

В следующей статье мы более подробно разберем LiveData компонент.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332718/


[Из песочницы] Шифр Хила. Подробнй разбор

Воскресенье, 09 Июля 2017 г. 18:48 + в цитатник
В этой публикации я попытаюсь максимально подробно описать шифрования и дешифрование по алгоритму Хилла. Итак, без лишних слов сразу к делу.

Шифрование


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

  1. Создаем кодированный алфавит. Допустим мы хотим шифровать русский текст. Тогда длина алфавита будет 33 буквы. Целесообразно добавить к алфавиту еще 4 символа на выбор, я добавлю такие: "?", ".", ","," ". Это делается для того, чтобы длина алфавита была простым числом, т.е. числом, которое делится нацело только на себя и на 1. Это, конечно, не обязательно, но очень удобно, потому что для расшифровки необходимо, чтобы детерминант ключа и длина алфавита были взаимно простыми, т.е. не имели общих делителей кроме 1. Если длина алфавита – простое число, то таких ключей, для которых выполняется это условие значительно больше. Каждому символу нашего алфавита ставим в соответствие целочисленный код. Удобнее всего использовать просто номера букв. Таким образом получаем кодированный алфавит:



  2. Теперь берем текст, который хотим зашифровать и кодируем его с помощью нашего алфавита. Возьмем для примера слово «ШИФР», его код будет таким: 25 9 21 17.

  3. Теперь выбираем ключевое слово, или просто набор букв, который будем использовать в качестве ключа. Тут важно, чтобы длина этого ключевого слова была равна квадрату целого числа, т.е. 4, 9, 16, 25 и т.д. Только тогда мы сможем сделать из него квадратную матрицу, необходимую для шифрования. Я выбрал слово «АЛЬПИНИЗМ». Кодируем его с помощью нашего алфавита. Получаем: 0 12 29 16 9 14 9 8 13. Запишем ключ в виде матрицы 3х3:



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

  4. Теперь надо разбить текст на блоки по n символов в каждом, где n-размерность матрицы, в моем случае – 3. Начнем разбивать:

    Первый блок: (25 9 21)

    На второй блок у нас осталось всего одно число – 17. Самое простое решение в таком случае: добавить столько символов, чтобы образовать целый блок. Я решил добавить пробелы.

    Тогда второй блок: (17 35 35)

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

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

    Итак, умножаем первый блок на ключ:



    Умножаем второй блок на ключ:



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

    Делим первую матрицу:



    Делим вторую матрицу:



    Почему делим на 37? Потому что это длина нашего алфавита, будь у вас алфавит другой длины, вы бы делили на другое число. Например, для английского алфавита делим на 26, или 29, если вы добавили какие-то символы.

  6. Теперь декодируем полученные матрицы с помощью нашего алфавита.

    Первая матрица: АЮН
    Вторая матрица: ЧХЯ

    Склеиваем две матрицы и получаем зашифрованный текст: АЮНЧХЯ

Дешифрование


Теперь переходим к дешифрованию. Дешифрование производим по следующему алгоритму:

  1. Обратно кодируем шифротекст в цифры и разбиваем на блоки.

  2. Находим определитель матрицы ключа:



    Нахождение определителя тоже очень простая операция, так что я ее не расписывал.

  3. Теперь по расширенному алгоритму Евклида находим d, x, y.

    Описание и сам алгоритм я расписывать не буду. Информацию об этом алгоритме легко можно найти в Интернете. На вход алгоритма подаем det K и длину нашего алфавита. На выходе мы получим d=1, x=-4, y=41. Нас интересует только x.

  4. Теперь сложная и важная вещь. Нам надо найти обратный детерминанту элемент в кольце по модулю 37. Для этого делаем следующее:

    • Если детерминант отрицательный, а x – положительный, то обратный элемент детерминанта будет равен x.
    • Если детерминант положительный, а x – отрицательный, то обратный элемент детерминанта будет равен 37+x.
    • Если детерминант положительный, и x – положительный, то обратный детерминанту элемент будет равен x.
    • Если детерминант и x – отрицательные, то обратный элемент будет равен -x.

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

    Итак, наш детерминант равен 379, он положительный, а x равен -4 – отрицательный. Тогда обратный детерминанту элемент находим по формуле 37+x=37+(-4)=37-4=33.

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



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



    Умножаем матрицу алгебраических дополнений на обратный детерминанту элемент. Получаем такую матрицу:



    Делим данну матрицу по модулю на 37:



    Транспонируем ее (меняем строки и столбцы местами):



    Теперь если элемент матрицы отрицательный, меняем его на другой, вычисленный по формуле 37+<элемент>:



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



  6. Для дешифровки шифротекста умножаем строки шифротекста на матрицу обратную ключу.
    Умножаем первую строку:



    Умножаем вторую строку:



    Делим полученные строки на 37 по модулю:



    Склеиваем матрицы (25 9 21 13 35 35) и декодируем с помощью нашего алфавита: ШИФР.

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

Спасибо за внимание!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332714/


Метки:  

[Перевод] Быстрое удаление пробелов из строк на процессорах ARM

Воскресенье, 09 Июля 2017 г. 18:33 + в цитатник
Предположим, что я дал вам относительно длинную строку, а вы хотите удалить из неё все пробелы. В ASCII мы можем определить пробелы как знак пробела (‘ ’) и знаки окончания строки (‘\r’ и ‘\n’). Меня больше всего интересуют вопросы алгоритма и производительности, так что мы можем упростить задачу и удалить все байты со значениями меньшими либо равными 32.

В предыдущией статье, где я задавал вопрос об удалении пробелов на скорость, лучшим ответом было использование векторизации с помощью 128-битных регистров (SSE4). Оно оказалось в 5-10 раз быстрее подхода в лоб.

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

Давайте сначала рассмотрим быструю скалярную реализацию:

size_t i = 0, pos = 0;
while (i < howmany) {
    char c = bytes[i++];
    bytes[pos] = c;
    pos += (c > 32 ? 1 : 0);
}

Она удаляет все символы со значениями меньшими либо равными 32 и записывает данные обратно. Работает очень быстро.

Можно ли добиться ещё большей скорости с векторными инструкциями?

На процессорах x64 лучшей стратегией будет захватить 16 байт данных, быстро сравнить на предмет пустых символов, затем извлечь значение маски (или bitset), созданное из 16 бит, один бит на символ, где каждый бит соответствует значению, найден пустой символ или нет. Такой битсет быстро вычисляется на процессоре x64, поскольку там есть специальная инструкция (movemask). На процессорах ARM такой инструкции нет. Можно эмулировать movemask с помощью нескольких инструкций.

Итак, мы не можем обработать данные на ARM так, как на процессорах x86. Что можно предпринять?

Как это делает SS4, мы можем быстро проверить, что значения байтов меньше или равны 32, и так определить пустые символы:

static inline uint8x16_t is_white(uint8x16_t data) {
  const uint8x16_t wchar = vdupq_n_u8(' ');
  uint8x16_t isw = vcleq_u8(data, wchar);
  return isw;
}

Теперь мы можем быстро проверить любой из 16 символов, является он пустым, используя всего две инструкции:

static inline uint64_t is_not_zero(uint8x16_t v) {
  uint64x2_t v64 = vreinterpretq_u64_u8(v);
  uint32x2_t v32 = vqmovn_u64(v64);
  uint64x1_t result = vreinterpret_u64_u32(v32);
  return result[0];
}

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

uint8x16_t vecbytes = vld1q_u8((uint8_t *)bytes + i);
uint8x16_t w = is_white(vecbytes);
uint64_t haswhite = is_not_zero(w);
w0 = vaddq_u8(justone, w);
if(!haswhite) {
      vst1q_u8((uint8_t *)bytes + pos,vecbytes);
      pos += 16;
      i += 16;
 } else {
      for (int k = 0; k < 16; k++) {
        bytes[pos] = bytes[i++];
        pos += w[k];
     }
}

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

Я написал бенчмарк, в котором попытался оценить, сколько займёт удаление пробелов, по одной штуке за раз, на основе входных данных с небольшим количеством пустых символов, разбросанных случайным образом. Исходный код доступен, но для запуска вам нужен процессор ARM. Я запускал его на 64-битном ARM-процессоре (сделанном из ядер A57). У Джона Регера есть ещё несколько бенчмарков на такой же машине. Мне кажется, такие же ядра работают в Nintendo Switch.

скаляр 1,40 нс
NEON 1,04 нс

Технические спецификации небогатые. Однако процессор работает на частоте 1,7 ГГц, в чём каждый может убедиться, если запустит perf stat. Вот сколько циклов нам символ нам нужно:

скаляр ARM Недавний x64
скаляр 2,4 цикла 1,2 цикла
векторизованные (NEON и SS4) 1,8 цикла 0,25 цикла

Если сравнить, то на процессоре x64 скалярная версия использует что-то вроде 1,2 цикла на символ, а ARM уступает примерно вдвое по циклам на символ. Это вполне ожидалось, потому что вряд ли ядра A57 могут конкурировать с x64 по производительности циклов. Однако при использовании SS4 на машине x64 я сумел добиться производительности всего 0,25 цикла на символ, что более чем в пять раз быстрее, чем на ARM NEON.

Такое большое отличие объясняется разницей в алгоритмах. На процессорах x64 мы используем сочетание movemask/pshufb и алгоритм без ветвлением с очень малым количеством инструкций. Версия для ARM NEON гораздо слабее.

На процессорах ARM есть много приятных вещей. Код ассемблера гораздо более элегантный, чем аналогичный код для процессоров x86/x64. Даже инструкции ARM NEON кажутся чище, чем инструкции SSE/AVX. Однако для многих задач полное отсутствие инструкции movemask может ограничить вас в работе.

Но, возможно, я недооцениваю ARM NEON… можете выполнить задачу эффективнее, чем удалось мне?

Примечание. Статья была отредактирована: как заметил один из комментаторов, на 64-битных процессорах ARM есть возможность перестановки 16 бит одной инструкцией.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332710/


Метки:  

Поиск сообщений в rss_rss_hh_new
Страницы: 1437 ... 1043 1042 [1041] 1040 1039 ..
.. 1 Календарь