...

суббота, 31 января 2015 г.

Мод USB-COM переходника, который сэкономит Вам нервы при прошивке Arduino Pro Mini и не только

Вы прошиваете Arduino Pro Mini?

Вы устали нажимать на кнопку reset чтобы залить Ваш скетч?

image


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


Так давайте Мы это исправим.


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

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


image


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


image


Зачастую покупают самый дешёвый переходник:


image


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


Те, кто умеет паять и понимают суть, они никогда не покупают то — что можно сделать самому, так как создавать самому это более чем приятно. Я думаю, многие согласятся со мной. Да есть те случаи, когда время дороже и проще купить готовое, чем тратить время на доработку – но это редкость.


Поэтому предлагаю Вам усовершенствовать свой Usb-to-Com переходник. А те, кто собрался только заказывать, посчитайте … по цене 1 со сбросом можно купить 3 шт без сброса и переделать.


Сложного ничего нет.

На чипе PL2303 есть 2-я ножка с сигналом DTR, на который IDE программа и так посылает сигнал сброса при прошивке, так почему нам его не использовать.


image


На Arduino Pro Mini уже есть вывод DTR сигнала, но не на всех. Сигнал DTR подключен через конденсатор на вывод сброса(reset),


image


поэтому если Вы хотите использовать, к примеру ATmega 8 или 328, то для авто-сброса при прошивке Вам необходимо установить конденсатор 0,1мкФ.


image


Вот видео обзор Модификации переходника:


А также, смотрите тест записи скетча:


Надеюсь, этот маленький обзор поможет Вам в той или иной степени.


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Как мы делали Multitouch Table

Привет Хабр.

Занимаясь Computer Vision, я интересовался Natural интерфейсами, общался с людьми кто проектирует столы для баров с touch интерфейсами. И у меня появилась идея сделать свой. Дешево, сердито, но главное, чтобы все работало. То есть важно испытать и протестировать. А тут, мой друг Александр Жеделев, музыкальный продюссер Русского Драматического театра Эстонии, предложил сделать какой нибудь новый музыкальный инструмент для выступления на музыкальном фестивале Tallinn Music Week. Времени было немного, и мы приступили.



Вообще есть несколько подходов к созданию такого типа столов. Я попытаюсь описать три из них.

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

image

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



Обычно в камере стоит фильтр, который отсекает инфракрасный и пропускает только видимый спектр. Для наших целей нам необходимо переделать все наоборот. То есть снять обьектив, убрать оттуда фильтр, который пропускает только видимый свет, и поставить фильтр, который пропускает только инфракрасное излучение, поскольку жесты мы будет опознавать пользуясь этой частью спектра. Вообще подойдет любая веб-камера. Я взял старую добрую PS3 Eye, поскольку она дает оптимальный результат по цене/качеству. Само снятие фильтра также не представляет сложности. Например так.

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

Теперь о самих подходах.

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

image


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


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


Сама рама была собрана из дерева. На нее посажено оргстекло. Камера находилась непосредственно под стеклом.

Это план лаборатории AudioKinetica. Внизу можно видеть схему стола, начерченную маркером.

image


А вот сам стол.


Для распознавания BLOB (это отражения-пятна, которые видит камера) я использовал систему Community Core Vision, CCV. Это готовое решение, которое ретранслирует распознавание и передает результаты по TUIO протоколу. TUIO это открытый фреймворк, который определяет протокол и API для построения multitouch интерфейсов. В принципе, если бы было больше времени, я бы написал свой детектор BLOB на OpenCV, поскольку ничего особо сложного для этой задачи нет. Пятна хорошо видятся, и на OpenCV это выглядело бы так — получаем картинку, убираем шум, строим битмап карту, пропускаем через Canny алгоритм, находим контура и далее транслируем их координаты в TUIO обьекты по спецификации. Понадобился бы модуль калибровки для задания координат. CCV использует вычитание картинки фона из полученного изображения, там есть также адаптивный режим, который учитывает медленные изменения фона. На OpenCV это можно реализовать методом codebook и connected components.

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


Настройки CCV.

image


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


А вот уже подредактированная версия. Смотрите в наушниках.


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


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

Есть одно неудобство связанное с TUIO протоколом, его обьекты надо ретранслировать. То есть если я хочу паралелльно запустить красивые визуалы и синтез звука, то мне нужен ретранслятор, поскольку если модуль сопряжения принимает TUIO обьект, то допустим Flash визуалы этот обьект уже не видят.


Хочу сказать спасибо всем людям, кто участвовал, особенно Александру Жеделеву, Сергею Драгунову, Кристе Кёстер.

AudioKinetica


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Анонс React Native


Не так давно в Калифронии прошла конференция по React.js (доклады с этой конференции уже размещены на канале facebook разработчиков в youtube). Доклады, как не сложно догадаться, были о различных возможностях React.js и применении их в реальной жизни, но два доклада презентовали исключительно новую технологию, бета-версия которой в данный момент доступна только для разработчиков, посетивших мероприятие. Если вы уже посмотрели доклады, то понимаете, что речь идет о React Native. В данной статье я хочу сделать краткий обзор того, что нас ждёт в будущем с этой технологией и как изменится наше представление о создании мобильных приложений с использованием JavaScript.



Что такое React Native?




Итак, давайте разберемся что такое React Native и для каких целей инженеры facebook изобрели эту технологию. Но перед этим, я хочу вас спросить, что вы думаете насчёт Apache Cordova? Медленно? Плавный UI — миф? Лучше использовать WebView с обвязкой на JS? Да-да, так оно и было до анонса React Native. Представьте себе, что в течении пары ближайших месяцев произойдет релиз гибридной системы от facebook на основе React.js и взаимодействия с родными элементами iOS / Android систем (т.е. не создавать WebView, как это делает PhoneGap, но использовать встроенные компоненты, которые предоставляют вышеуказанные платформы). Такой подход позволит разработчикам, уже знакомым с React.js, в схожей манере разрабатывать нативные приложения. React Native не использует ни браузер, ни WebView — только JavaScript API поверх нативных компонентов. Позвольте пояснить как это работает: вы пишете JavaScript код (имхо, скорее всего это будет JSX код), и он работает с нативными компонентами операционной системы, под которую вы разрабатываете, тем самым перенося достоинства и удобства использования React.js из браузера в мобильные приложения. В отличие от того же PhoneGap, который при возникновении нативного события блокирует поток и передает управление на JS-код, ожидая его инструкций (собственно, лаги вы можете наблюдать именно из-за этого), React Native выполняет JS в отдельном фоновом потоке, взаимодействуя с главным потоком асинхронно, т.е. в потоке JS собирается ряд команд к главному потоку и в определенный момент времени, отправляется сгруппированный запрос (batch-запрос), тем самым никак не блокируя главный поток выполнения программы (как это работает в жизни можно посмотреть тут или скачать приложение facebook groups, которое скоро появится в AppStore).

Как насчёт стилей?




Тут надо сразу сказать, что facebook не остановился на использовании HTML-like синтаксиса в JS(X) файлах, и следующим решением было использовать CSS-объектную нотацию (сродни той, что можно использовать в браузерах) в рамках мобильных приложений. Это дает ряд неоспоримых преимуществ, например вы можете вычислять количественные свойства элементов (таких как цвет, толщина, размер и т.п.) прямо на лету в ходе выполнения программы и инкапсулировать стили на уровне с вашими компонентами. Выглядит это так:

var styles = {
textStyle: {
color: 'yellow',
fontSize: 14
}
};

React.render(Test, document.body);


Соответственно, в момент компиляции приложения, span будет транслирован в Text для iOS и аналогичный компонент на Android (так же для всех поддерживаемых тегов, список которых будет здесь опубликован на момент публичного релиза технологии).


В завершении обзора хочу сказать, что идеей React Native не является поддержка идеологии «Write once, run anywhere» (Однажды написав код, вы можете использовать его на всех платформах), но «Learn once, write anywhere» (Научившись однажды, вы можете писать для любой платформы).


В статье использовались материалы и информация из видео:


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Опубликован официальный Node.JS-драйвер для базы данных Oracle




На конференции OpenWorld 2014 корпорация Oracle объявила о том, что она работает над собственным Node.JS-драйвером для своей базы данных. На прошлой неделе был открыт его исходный код под лицензией Apache 2.0.

Драйвер включает в себя не только стандартные средства для исполнения SQL-запросов и PL/SQL-кода, но и инструменты для прозрачной работы с объектами JavaScript и массивами, продвинутые средства для работы с транзакциями и встроенными в Oracle инструментами масштабирования. Например, можно быстро прервать неудачную транзакцию на событие от сервера о невозможности корректно закончить работу, позволяя Node.js-приложению быстро переключиться на другую базу данных без потери пользовательских данных.



На данный момент также реализована поддержка Runitme Load Balancing и Fast Application Notification. Preview-release версия работает в Linux и Mac OS X, поддержка Windows-систем запланирована в следующих версиях.


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[Перевод] Открытое письмо Йона фон Тэчнера

Руководитель компании Vivaldi Technologies, выпустившей несколько дней назад новый браузер Vivaldi, опубликовал в сети открытое письмо к пользователям своего нового программного продукта. Предлагаю вам перевод этого письма на русский язык.



Дорогие друзья!

Сейчас я сижу в самолёте и лечу домой после очень напряжённой недели в Норвегии. Очень продуктивной недели.


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


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


Функции. Дизайн. Детали.


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


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


С этого момента мы начнём выпускать еженедельные сборки.


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


Это браузер для всех нас.


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


С уважением,


Йон.


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Стартовали Стэнфордские курсы cs193p 2015: iOS 8 + Swift и только Swift

image

В прошлой публикации я писала о том, что ожидаются Стэнфордские курсы cs193p 2015.

И вот они стартовали. Вернее в Стэнфорде они стартовали давно, 5 января 2015 года. Но только сейчас их выкладывают в iTunes Developing iOS 8 Apps with Swift

В первой же лекции Пол Хегэрти (Paul Hegarty) четко сказал, что в курсе будет использоваться только Swift. Стэнфорд окончательно переключился на Swift не только потому, что многие компании в Силиконовой долине проводят интервью с выпусниками на позиции разработчиков iOS приложений на Swift, но и потому, что с благословления Apple свободное распространение такого качественного контента c 1.2 миллионами скачиваний откроет для многих разработчиков по всему миру язык разработки iOS приложений Swift.

Для того, чтобы воспользоваться этим контентом, вам нужно иметь только Mac — Macbook Pro, MacBook Air, iMac. Все программное обеспечение — бесплатное.

Уже выложены 4 лекции и одно домашнее задание.

Если кто-то чувствует себя не совсем уверенно с английским или просто хотите почитать материал на русском языке, предлагаю свой неавторизованный конспект лекций на русском языке. Выложена Лекция 1 CS193P Winter 2015 — Логистика курса, обзор iOS 8. В ближайшее время будет выложены остальные.

Блестящие лекции профессора Пола Хегэрти (Paul Hegarty) будут полезны как начинающим, так и опытным программистам.



Пол Хегэрти не только высвечивает множество нюансов операционной системы iOS и языка программирования, в данном случае Swift, которые не так-то легко найти в документации, но и снабжает вас приемами программирования на iOS, которые вы не найдете ни в одной книге (может быть, на WWDC 2011, 2012, 2013, 2014). Он многократно сократит ваш путь изучения. Реально, он — гений в преподавании программирования на iOS.

Хочу поделиться первыми впечатлениями о первых 4-х лекциях.

Как всегда, ежегодная Лекция №1 — это общее введение в курс и обзор iOS, MVC и на этот раз Swift. И опять Paul Hegarty подчеркивает важность знакомства с объектно-ориентированным программированием как обязательное требование к курсу, и что этот курс — не для начинающих.

После обзора структуры программного обеспечения iOS, профессор Paul Hegarty погружается в демонстрационный пример, это старый пример, который был представлен 3 года назад на Objective-C. Я прошла почти все курсы профессора Пола Хэгарти — от iOS 5 до iOS 7 — до самого конца (решения домашних заданий представлены на Github ). Мне знаком этот демонстрационный пример, но для iOS 5 и Objective-C. Это RPN калькулятор. На примере этого калькулятора профессор на протяжении 3-х лекций рассказывает все тонкости Swift как объектно-ориентированного языка. На основе RPN калькулятора построены первые домашние задания, в результате выполнения которых вы разработаете очень качественные и красивые iOS приложения. Надо сказать, что графический RPN калькулятор на iPad ( по-видимому третье или четвертое домашнее задание) — это самое удачное, трудоемкое и красивое приложение из всех, представленных за последние 3 года.

Вначале профессор очень подробно рассказывает о синтаксисе Swift и о возможностях Xcode 6. Буквально объясняет каждый символ. И может возникнуть обманчивое впечатление, что это курс для начинающих. Но он умышленно это делает, чтобы дальше, при объяснении протоколов, замыканий, анимации, многопоточности, Autolayout (разметки), объектно-ориентированной базы данных Core Data, Dynamic Animation и т.д., не вернуться к этому никогда, и начать говорить только о сложных вещах. Поэтому первые 3-4 лекции — просто находка для тех, кто только начинает разрабатывать iOS приложения. Сейчас мы все немного в таком положении, потому что Swift — новый язык.

Но что меня поразило на этот раз, это то, что буквально с первой лекции начинается работа с адаптивным интерфейсом, с применения такой сложной и запутанной системы, как Autolayout. Кстати Пол Хэгарти на этой, а также на 2-ой лекции, представил ускоренный мини курс обучения системе Autolayout (Разметки). Дальше еще круче. Он рассматривает тип implicitly unwrapped optional (неявно развернутого Optional), семантика которого менялась по мере продвижения бэта — версий ( то мы ставим! на UI объектах, то нет).

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

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

Я собираюсь выкладывать решения домашних заданий на своем сайте «Разработка iOS приложений».

Хотелось бы организовать в Рунете форум на подобие Piazza, который сопровождает обучение студентов в Стэнфорде. На нашем форуме мы могли бы обмениваться решениями (язык Swift — новый, и могут быть очень интересные решения в зависимости от опыта), а также помогать продвинуться по курсу тем, кто по каким-то причинам застрял.

Откликнитесь те, у кого есть какие-то идеи на этот счет.


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


STM32 и FreeRTOS. 3. Встаем в очередь

Раньше: про потоки и про семафоры

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


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


Пойдем со стороны пользователя. Com-порт, или USART (universal synchronous/asynchronous receiver/transmitter) — штука очень нежная и капризная. Основной каприз заключается в том, что ее нельзя оставлять без внимания. По одной простой причине — для экономии выводов в 99% случаев выводят только сигналы приема и передачи, оставляя сигналы разрешения приема и передачи (rtr/rts,dtr,cts и прочие) за бортом. И стоит микропроцессору хоть чуть-чуть замешкаться, как символ будет потерян. Судите сами: магические цифры 9600/8N1 говорят нам, что в секунду по линии летит 9600 бод, а один символ кодируется 10 (1 стартовый, 8 данных и 1 стоповый) импульсами. В итоге максимальная скорость передачи составляет 9600/10 = 960 байт в секунду. Или чуть больше одной миллисекунды на байт. А если по ТЗ скорость передачи 115200? А ведь микроконтроллеру надо еще обработать эти данные. Кажется, что все плохо, но в реальности куча устройств работает и не доставляет проблем. Какие же решения применяются?


Cамым распространенным (и правильным) решением является перекладка всей работы по приему или передаче символов на плечи микроконтроллера. Делать аппаратный usart порт научились сейчас все, поэтому типовое решение в большинстве случаев выглядит примерно вот так:



void URARTInterrupt()
{
a=GetCharFromUSART();
if(a!=0x13)
buffer[count++]=a;
else
startProcessing();
}


Где проблема? Во-первых, проблема в вызове startProcessing. Стоит этой функции хоть чуть-чуть задержаться с работой, как очередной символ будет потерян. Берем STM32L1 (который на минимальной частоте успевает за 1мс обработать всего 84 команды) и при более-менее развесистой логике полученная конструкция будет терять символы.


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


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


П: ATZ (Контроллер, ты живой?)

К: ОК (Ага, я тут)

П: М340,1 (Сделай чего-то)

(тут может быть пауза, иногда очень большая)

К: ОК (Сделал типа)


Где проблемы? Во-первых, нельзя послать несколько команд подряд. Значит либо надо будет менять логику или делать сложные команды с несколькими параметрами. Во-вторых, между запросом и ответом не может быть никаких других запросов и ответов. А вдруг пользователь в процессе движения нажмет кнопку? Что делать? Выход только один — использовать родную природу порта, а именно его асинхронность. В результате диалог между пользователем и контроллером выглядит примерно так


П: ATZ (жив?)

К: ОК (ага)

П: M340,1

К: К1=1 (кнопку нажали)

П: Е333,25,2

(тишина)

К: Е=1 (задачу Е выполнил)

К: М=1 (задачу М выполнил)


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


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

Во-вторых, такое поведение ближе к поведению небольшого начальника в реальной жизни. И скопировать такое поведение очень легко — все видели, все участвовали.

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


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


Что делает обычный программист? Так как он прочитал предыдущие статьи и кучку книжек, то он рисует логику расставляя семафоры по необходимости, а для блокирования одновременного доступа к моторчику использует мутексы. Все хорошо, но код получается громоздкий и тяжело читаемый. Что делать? У нас в studiovsemoe.com мы используем рецепт Шарикова: «В очередь, сукины дети! В очередь!». Опять же, концепция очереди впитывается чуть ли не с молоком матери, поэтому проблем с пониманием никаких.


В данном примере можно просто создать три очереди. Первая это команды, полученные от пользователя. В нее засовывается все (ну или после минимальной проверки), что принято со всех входных портов. Вторая это те данные, которые необходимо выдать пользователю. Состояние кнопок, результаты расчетов и так далее. И наконец, третья очередь служит для заданий моторчику/считалке/кому-то еще. А между этими очередями потоки для преобразования данных от пользователя в задания для считалки. Так как в FreeRTOS есть официальные функции для «заглядывания» внутрь очереди, то легко сделать оптимизацию для случаев, когда следующее действие продолжает/повторяет текущее.


Итак, всего три очереди, а дикая куча проблем решена. Во-первых, нет даже потенциальной проблемы потери или переписи буфера принятых символов. Правда результатом станет чуть больший расход памяти, но это допустимая цена. Во-вторых, нет проблем с выводом. Все процессы просто пишут в одну очередь, а как выводить, в каком формате и прочее — это уже не их забота. Надо оформлять вывод в рамочку — переписываем одну функцию, а не все, которые что-то могут выводить. Опять же, нет проблем с одновременным/перекрывающимся выводом (когда один поток выводит 12345, а другой qwerty, но пользователь получает что-то типа 1qw234er5ty). И наконец благодаря такому подходу задачи очень легко разделить на более мелкие и раскидать их по потокам/ядрам микропроцессора. А все это означает ускорение разработки и снижение стоимости поддержки. Все рады, все довольны.


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


Где-то в начале кода определим очередь



xQueueHandle q;


Код зажигания светодиодов поменяем по принципу «в очереди больше Н элементов? зажигаем, если нет, то нет»



if(uxQueueMessagesWaiting(q)>1)
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_9,GPIO_PIN_SET);
else
НAL_GPIO_WritePin(GPIOE,GPIO_PIN_9,GPIO_PIN_RESET);
osDelay(100);


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



q = xQueueCreate( 8, sizeof( unsigned char ) );


Ну и код кнопки для помещения символов в очередь



if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_SET)
{
unsigned char toSend;
xQueueSend( q, ( void * ) &toSend, portMAX_DELAY );
}
osDelay(500);


Чего не хватает? Воркера, который забирает из очереди задания. Пишем.



static void WorkThread(void const * argument)
{
for(;;)
{
unsigned char rec;
xQueueReceive( q, &( rec ), portMAX_DELAY );
osDelay(1000);
}
}


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



Код со всеми потрохами доступен по адресу http://ift.tt/15SxVBD


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Краткий курс компьютерной графики: пишем упрощённый OpenGL своими руками, статья 3.14 из 6

[Перевод] Делаем игру 2048 на AngularJS

Приглашение на FOSDEM 2015


Друзья, FOSDEM уже в эти выходные! На стенде ReactOS ожидаются: Colin Finck, Pierre «HeisSpiter» Schweitzer, Giannis «smiley» Adamopoulos — впервые увидимся лично (не только я, но и остальные члены команды с ним никогда не встречались до этого момента), и я, Alekesy «Fireball» Bragin — мне очень приятно снова оказаться на этом замечательном событии.








Вообще, FOSDEM — это выставка/конференция №1 по свободному ПО на европейском континенте. Первый раз она состоялась в 2001 году, её организовал Raphael Bauduin (у меня, кстати, есть приглашение на FOSDEM подписанное лично им) со-товарищи. Сейчас мероприятие собирает более 5 тысяч разработчиков свободного ПО со всего мира.


Интересный факт: в оригинале название было OSDEM (можно подумать, что это конференция об операционных системах, но это всего-лишь Open Source Developers European Meeting), но вездесущий Ричард Столлман и здесь приписал слово Free к названию. Ну хорошо, хоть не GNU\OSDEM


FOSDEM традиционно проходит в зданиях Свободного Университета Брюсселя — ULB, основанного, к слову сказать, масоном Pierre-Theodore Verhaegen. Название обычно не переводят, т.к. со свободой в нынешних её пониманиях там не особо много общего.


Памятник основателю стоит у главного корпуса университета (чем-то мне это напоминает ГЗ МГТУ и памятник Бауману). В общем, это старое и очень авторитетное негосударственное образовательное учреждение.


Для FOSDEM'а уже всё собрано и мы начинаем подтягиваться к месту нашей встречи. Я добрался с помощью самолёта, немцы едут поездами, а Giannis из Греции по идее уже там. Ещё хороший человек по имени Nuno рассказал мне про ещё одно небольшое опенсорсное событие, которое состоится уже прямо сегодня. Это FLOSS Community Metrics Meeting. Там будут обсуждать различные метрики опенсорса и тулзы для этих метрик. Я тоже подал заявку на участие, посмотрим, может расскажу что-нибудь про ReactOS. Если есть интересные идеи — высказывайте в комментариях.



Алексей Брагин


Бонусом видео о том, как это было 6 лет назад.



Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


пятница, 30 января 2015 г.

Горячие клавиши YouTube

Все из нас (ну почти) пользуются сервисом YouTube, но не все знают, что процесс просмотра видеороликов на вышеупомянутом сервисе может быть сравним по удобству с просмотром видео на локальном медиа-плеере (например Media Player Classic), если пользоваться горячими клавишами.

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



Управление воспроизведением




Пробел или k: пауза / воспроизведение

>: увеличить скорость воспроизведения

<</strong>: уменьшить скорость воспроизведения

f (fullscreen): перейти в полноэкранный режим и обратно (выйти из полноэкранного режима можно также с помощью клавиши Esc)


Перемотка видео




— перемотать назад на 5 секунд

— перемотать вперёд на 5 секунд

Ctrl + ← или j — перемотать назад на 10 секунд

Ctrl + → или l — перемотать вперёд на 10 секунд

0 или Home — перемотать видео на начало

1 — перемотать на позицию 10% общего времени видео



9 — перемотать на позицию 90% общего времени видео

End — перемотать видео в конец


Управление громкостью




— увеличить громкость на 5%

— уменьшить громкость на 5%

m (mute) — выключить/включить звук

Навигация по плейлисту




N (Next) — перейти к следующему видео в плейлисте

P (Previous) — перейти к предыдущему видео в плейлисте

Другие функции




/ — переместить курсор в поле поиска (работает даже в полноэкранном режиме)

Esc — покинуть поле поиска / выйти из полноэкранного режима

? — отобразить/скрыть помощь по горячим клавишам (скрыть можно так же по Esc)

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



Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Краткий курс компьютерной графики: пишем упрощённый OpenGL своими руками, статья 6 из 6

Подключаем Ethernet ENC28J60 через SD card-reader для WEB-Servera на Arduino

Всем привет.

Столкнувшись с технической трудностью в построении WEB сервера, придумал сделать переходник которого наверное еще никто никогда не делал(хотя могу ошибаться).

Все оказалось просто и довольно таки практично.

image

Для чего это нужно и как это создать я расскажу и покажу, а также покажу видео обзор работоспособности web servera на переходнике.




Началось все с создание Сауны с сенсорным экраном и WEB интерфейсом.

Отдельно программировал меню для экрана на Arduino MEGA2560 и web server на Arduino UNO (по окончанию проекта «САУНА» — будет статья со схемами и программой).


Все вроде идет отлично, начал монтаж:


imageimage


И когда подключил и залил сервер,

image


то я понял что веселье только начинается. Сайт то открывался то нет, пинг иногда переваливал за 1000, пробивал менять выбор кристалла, грешил на оперативную память(Хотя у АTmega2560 больше чем у ATmega328(UNO)).

И только когда руки опустились я решил убрать переходную плату для LCD с MEGA 2560, и тут все заработало, сайт открывается за доли секунды, пинг < или = 1, все заработало идеально.


Причиной глюков и проблем стали буфера на 3,3В в переходной плате, я изначально подумал что они активируются когда идет сигнал выбор кристалла СS SD карты… А оказывается что не все там так как бы хотелось.


И тут у меня промелькнула мысль, а что если использовать этот буфер для подачи сигналов на Ethernet модуль, ведь SD карта на 3,3В и Ethernet модуль на 3,3В(Хотя есть и 5В). В итоге отпаял я шлейф и моя переходная плата стала выглядеть так:


image


Шлейф я делал относительно недолго кто хочет посмотреть как его сделать то есть видео обзор создания этого шлейфа.

Запасайтесь поп-кормом:


Схема шлейфа:

image


В данном случае мой WEB сервер создан без использования SD карты, по этому можно спокойно занимать слот под SD-карту.

image

image


Надеюсь Вам понравилась идея (возможно Китайцы уже делают что то подобное).


Ну и напоследок хочу предоставить Видео обзор теста переходника:


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Запускается проект для iOS-разработчиков Digest MBLTdev


сегодня в 16:48


e-Legion является организатором различных отраслевых мероприятий. Большую нишу занимают образовательные программы для мобильных разработчиков: международная конференция MBLTdev, бесплатная школа iOS, Android и WP разработчиков, встречи Apple Developers Community.

Сегодня запускается наш новый проект для iOS-разработчиков Digest MBLTdev.

В течение недели наши топовые разработчики Саша Чёрный и Руслан Гуменный будут собирать наиболее интересные и полезные ссылки на свежие статьи и записи в блогах с просторов мирового интернета. В пятницу всем подписавшимся на сайте участникам отправляется имейл с собранной информацией. Вся информация также публикуется на сайте проекта. Первый выпуск уже там!





Мы вдохновились успехом похожего проекта за рубежом и решили, что в России обязательно должен быть такой же. Постараемся максимально охватывать все полезные материалы и будем рады, если вы примете активное участие в формировании рассылки! Вы можете присылать ссылки на интересные страницы по адресу digest@mbltdev.ru. После прохождения модерации они будут включены в еженедельную рассылку.




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


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Первый опыт с RealSense

Однажды я писал на Хабр про различные технологии получения 3D изображения с одной камеры. Заканчивал я ту статью словами: “Сам я, правда, до сих пор не сталкивался ни с одной из этих камер, что жалко и досадно."

И вот, внезапно, не прошло и года, Intel проводит в Москве семинар + хакатон по новому поколению своих 3D камер (Intel RealSense). Любопытство взыграло: мы с коллегой записались на мероприятие. Как выяснилось, не зря. Хакатон мы выиграли + получили Developer-версию камеры, которую теперь мучаем.



Статья посвящена двум вещам:

1) Рассказу про камеру, её плюсы и недостатки. Обзор того, что можно сделать, а для каких задач она не годится.

2) Рассказ про концепцию, которую мы предложили на хакатоне и за которую получили первое место.


Камера




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

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

image

Если честно, я не понимаю, как они нивелируют эту картинку в ИК потоке. Вероятно какой-то межкадровой апроксимацией. Но явно это не заметно.

Во-вторых, очень классная математика для выделения пальцев на руках и контуров лица. Контуры лица выделяются через активные модели внешнего вида, но, алгоритмы расширены на 3D область. Это решение позволило увеличить стабильность и точность работы. Интересный момент — выделение лица работает на обычных камерах. Точность, конечно, поменьше, но при ярком освещении неплохо. Идея мне понравилась. Насколько мне известно, стабильного решения по активным моделям, которое можно было бы взять и использовать бесплатно просто не было (хотя, конечно, 4хгиговый SDK останавливает).



Выделение лица двухэтапно. Сначала каким-то быстрым алгоритмом (вероятно хааром) ищется регион с лицом, потом через активную модель формы натягивается лицо. Устойчиво к мимике и к очкам. Не устойчиво к поворотам более 15-20 градусов.

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



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

В третьих, хотелось бы отметить, что Kinect и RealSense имеют разные ниши. Kinect нацелен на большие пространства, для работы с человеком издалека. А RealSense для прямого взаимодействия с системой, куда он установлен. Во многом это определяет и параметры 3D сенсора.

Минусы




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

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

При инициализации видеопотока периодически происходит фэйл, всё приложение зависает секунд на 20-30 и не стартует. Словлено на двух компах.

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

1) Глаза — зеркало души. Почему не выделяется явное направление взгляда? Есть направление лица, есть выделение положения зрачков (откуда, теоретически можно получить направление). Но при углах поворота головы более 5 градусов положение зрачков начинает апроксимироваться центром глаза. При этом это явно никак не указывается. Конечно, хотелось бы, чтобы в API было явно вынесена возможность использовать направление.

2) Выделение лица работает только в двух режимах, в "FACE_MODE_COLOR_PLUS_DEPTH" и в «FACE_MODE_COLOR». Почему нет «FACE_MODE_IR_PLUS_DEPTH» или хотя бы «FACE_MODE_IR»? В условиях слабой освещенности лица выделение перестаёт работать. Почему нельзя использовать для выделения режим, где лицо всегда видно хорошо и стабильно. Многие любят сидеть перед компьютером в полутёмной комнате.

Третий момент — архитектура. Может мы не поняли до конца что-то, но у нас не удалось одновременно запустить распознавание лица и распознавание рук. Любую из этих систем нужно инициализировать отдельно.


Четвёртый минус — не все заявленные частоты работают. Конечно, можно попробовать подобрать с камеры видео 640*480*300fps. Но оно не подбирается в таком качестве и не сохраняется. Хотелось бы, чтобы перечислялись рабочие режимы.


Пятый минус немного персонализированный для той тематике, где мы часто работаем — «биометрия». Будь бы длинна волны лазера 800-900нм, а не 700-600, как в камере, было бы видно много биометрических признаков человека, системы распознавания можно было бы делать прямо на этой камере.


Как мы выиграли Хакатон




Хакатон начинался после полутора часов лекций и показа примеров. Всего было 40 человек в 13 командах. Формат: «даём камеру, через 6 часов покажите проект».Учитывая, что любая видеоаналитика весьма сложна, это немного. С другой стороны, странно проводить такие мероприятия в другом формате (суммарно всё длилось часов 8, под конец все были сильно измотаны).

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

1) Попробовать взять какой-нибудь наш проект и приспособить к нему 3D камеру. Проблема была, что все проекты были именно для 2D-распознавания/аналитики. И прикручивать камеру к ним было явно больше 4х часов. Плюс к тому, непонятно как это можно было красиво обставить.

2) Попробовать показать демонстрационный эффект, какое-то простое и классическое приложение. Управление мышкой/самолётиком глазами/рукой. Рисование на лице усов/бакенбардов. Минус такого варианта — он достаточно пустой и неинтересный. До красивого состояния доводить долго, а простой вариант не будет интересен публике.

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

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

Ответ оказался на удивление тривиальным и очевидным, стоило только Васю спросить меня: «а чем может помочь 3D камера в машине?». И тут нас просто понесло. Ведь 3D камера в машине может:

· Следить за тем, засыпает водитель или нет.

· Следить за вниманием водителя, например, что при перестроении водитель не смотрит в зеркала заднего вида.

· Распознавать жесты водителя: водитель может не отрываться от дороги скролить карту/управлять музыкой.

· Автоматическая настройка зеркал перед поездкой.

А самое удивительное: этого ещё никто не сделал. Есть системы определения усталости, в последние пару лет они даже начали использовать видеокамеры. Но с 3d камерой можно точнее определять положение головы водителя, что позволяет контролировать его реакции. К тому же, одно дело, засыпание, а другое — анализ действий и помощь водителю.

За четыре часа собрали простенькую демонстрацию:



За эту идею и демонстрацию нам внезапно выдали первое место.

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


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[Из песочницы] Кроссплатформенное приложение в Android Studio на jMonkeyEngine 3

[Из песочницы] Нетрадиционный, но качественный ЛУТ

Импортозамещению посвящается.

Если вам интересно, как в домашних условиях быстро и качественно делать печатные платы с дорожками 0.2мм и размером 90х150мм без воды и химикатов, прошу заглянуть.






Я не так давно занимаюсь всякими электронными поделками и с самого начала пробовал, как думаю и все, различные макетные платы, как паячные, так и безпаячные, вязал путанку из проводов МГТФ и т.д. Но в один прекрасный момент это дело мне надоело и я познал прелести Eagle. Потом выяснилось, что развести и сделать плату в нем быстрее, чем собирать прибор на макетке, уже не говоря о том, что это намного долговечнее. В общем, я сделал для себя вывод, что нормальная и качественно сделанная печатная плата — залог здоровья будущего прибора. А дальше начался тяжелый путь по выяснению оптимальных и недорогих способов производства печатных плат в домашних условиях.


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


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


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


Итак, конструкция представляет собой бутерброд следующего состава (перечисление снизу вверх):

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


Весь бутерброд стягивается болтами.



Нагреватель использовал размером 150х150мм, напряжение — 12В, мощнсоть — 120Вт. Электрическая часть представляет собой простейший термостат, собранный на основе ардуины и ЭМ реле, которое управляет электронным трансформатором ташибра, через который питается нагреватель. Информация о температуре считывается с термистора 100К, встроенного в нагреватель. Поскольку при 250гр нагреватель разрушается (плавится силикон), то я ограничил температуру нагрева на уровне 190гр — этого более чем достаточно.


Схема прожига следующая: собираем бутерброд, нагреваем до 190гр, оставляем остывать минут 10, чтобы не обжечь пальцы, извлекаем плату для травления. Время нагрева у меня составило около 10-15 минут.



Поскольку метод не требует сноровки и мастерства, то результат получается стабильным на 100%. Уже сделал на этом станке около 100 плат и не было ни одного косяка. Причем размер платы у меня доходит до 150х90мм, а ширина дорожки не превышала 0.2мм. Еще раз повторюсь, ни одного разрыва дорожки.


Теперь подводные камни.


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


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


Третий подводный камень в том, что плиту надо где-то найти и поработать инструментами (электролобзик, дрель и напильник).


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



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



Небольшое видео:



Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


How-to: Что нужно учитывать при разработке стратегии для торгового робота

image

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


Что нужно учитывать прежде всего




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

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



  • Наличие времени — работает ли будущий инвестор полный день или удаленно, живет рядом с офисом или вынужден тратить на дорогу несколько часов? Ответы на эти вопросы помогут определить «частотность» торговой стратегии, которую следует избрать. Очевидно, что люди, проводящие большую часть дня в дороге и в офисе, не должны создавать высокочастотные стратегии, потому что не смогут контролировать работу робота. Если же инвестор обладает значительным количеством времени, то ему може быть более интересно окунуться в мир высокочастотной торговли (HFT)

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

  • Объём доступных средств — транзакционные издержки для алгоритмических торговцев могут быть довольно значительными (прямое подключение к бирже, аренда серверов для запуска робота, комиссии биржи и брокера — тарифы ITinvest по ссылке). Считается, что для алгоритмической стратегии средней стартовым капиталом должно являться примерно $50 тысяч — такого мнения, в частности, придерживается «квант», разработчик и инвестор Майк Халлс-Мур.

  • Умение программировать — знание таких языков программирования, как C++, C# Java, Python, R (или TradeScriptздесь представлены примеры роботов на этом скриптовом языке) позволит создавать все элементы робота — от торгового движка до системы бэктестинга.

  • Финансовая цель— вопреки расхожему мифу, алгоритмические торговцы не купаются в деньгах (по крайней мере очень быстро разбогатеть, создав торгового робота, скорее всего не выйдет). Это долгий и кропотливый труд, что необходимо учитывать. Если торговец надеется получать стабильный доход и ежемесячного снимать с брокерского счета определенную сумму денег, то должен соответственным образом выбирать «частотность» стратегии — необходимость постоянно выводить средства потребует более высокочастотной стратегии с небольшой волательностью, долгосрочные инвесторы могут позволить себе более «спокойную» стратегию поведения на рынке.




Как искать идеи для торговых стратегий




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

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

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

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


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


Оценка торговой стратегии




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

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


image


Вот список некоторых таких критериев, предлагаемых Майклом Халс-Муром:


Методология



Насколько сложна стратегия? Как много параметров необходимо учесть для ее реализации (чем их больше, тем больше вероятность «ошибок оптимизации»)? Сумеет ли стратегия пережить существенные изменения на рынке (например, изменение законов или ужесточение регулирования финансовых рынков)?
Коэффициент Шарпа



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



Предполагает ли стратегия использование «кредитного плеча» (то есть заемных средств) для успешного осуществления торговли? Участвуют ли в стратегии производные инструменты, торгующиеся с плечом (подобные инструменты обычно обладают высокой волатильностью, что может приводит к маржин коллам)? Достаточно ли у трейдера капитала, который можно будет донести, чтобы избежать закрытия позиций?
Частотность



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



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



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



Величина самого большого допустимого сокращения капитала в ходе исполнения стратегии. «Импульсные» стратегии (Momentum strategies) характеризуются значительными и длительными периодами просадки депозита – даже если бэктестинг доказал, что подобное поведение нормально для стратегии и не сказывается на ее прибыльности, трейдеру будет тяжело не закрыть позиции в момент серьезных убытков.
Число параметров



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



Производительность большинства стратегий измеряется в сравнении с каким-то бенчмарком, в роли которого чаще всего выступает какой-либо индекс, характеризующий большой сегмент финансовых активов, аналогичных тем, что использованы в стратегии. Такими бенчмарками могут быть индекс S&P 500, в России — индексы ММВБ и РТС (вот материал о том, зачем нужны фондовые индексы).

image


Динамика индекса РТС


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


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


Работа с историческими данными




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



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



Несомненно, что новости влияют на фондовый рынок — и такой информации гораздо больше. Сюда входят новостные заметки крупных и небольших СМИ, посты в блогах и микроблогах. Для хранения таких неструктурированных данных хорошо подходят СУБД NoSQL.
Данные по цене активов и частотность



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

image


Чем выше частотность данных, тем выше их стоимость и требования к хранилищу. Для некоторых стратегий нужны тиковые данные и даже копии так называемых «очереди заявок» (order book). Работа с такими данными очень сложна технологически. Помимо прочего, описанные выше стратегии наверняка будут сравниваться с бенчмарками (индекс S&P500 и FTSE100), а значит, понадобятся и данные по этим бенчмаркам.


Заключение




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

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


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Блокируем блокировку от Роскомнадзора

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

image

В моём частном случае речь пойдёт о блокировке от Билайна, чтобы избавится от её назойливости нам понадобится веб сервер, подойдёт установленный на локальной машине. Билайн перекидывает нас на страницу http://ift.tt/1tDV45R...., где в переменной url в закодированном urlencode виде содержится наш адрес. Копировать, и декодировать его каждый раз руками достаточно скушное дело, поэтому мы попробуем использовать нашу эволюционную особенность — мозг.



Для начала найдо найти файл hosts

В Windows он распологается в %SystemRoot%\system32\drivers\etc\hosts, в unix-like ОС в /etc/hosts.

В этом файле прописываются соответствия между доменами и IP адресами.


Добавляем туда строку:

127.0.0.1 blackhole.beeline.ru

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


В index.php на этом сервере добавим такой код:







Этот код срабатывает, если передаётся параметр url методом get, и не мешает работе существующего сайта.

В переменной $webProxy указан адрес рабочего веб прокси.

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


UPD:

кстати, может быть ктонибудь поставит этот скрипт к себе на нелокальный сервер, и даст IP широкой общественности?)


Recommended article: Chomsky: We Are All – Fill in the Blank.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[Перевод] Flux для глупых людей

Пытаясь разобраться с библиотекой от Facebook ReactJS и продвигаемой той же компанией архитектурой «Flux», наткнулся на просторах интернета на две занимательные статьи: «ReactJS For Stupid People» и «Flux For Stupid People». Чуть раньше я поделился с хабравчанами переводом первой статьи, настала очередь второй. Итак, поехали.

TL;DR Мне, как глупому человеку, как раз не хватало этой статьи, когда я пытался разобраться с Flux. Это было не просто: хорошей документации нет и многие ее части перемещаются.

Это продолжение статьи «ReactJS For Stupid People».



Должен ли я использовать Flux?




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

Если ваше приложение просто набор статичных представлений и вы не сохраняете и не обновляете данные, тогда нет. Flux не даст вам какой-либо выгоды.


Почему Flux?




Юмор в том, что Flux — это не самая простая идея. Так зачем же все усложнять?

90% iOS приложений — это данные в табличном виде. Инструменты для iOS имеют четко определенные представления и модель данных, которые упрощают разработку приложений.


Для frontend’a (HTML, JavaScript, CSS) у нас такого нет. Вместо этого у нас есть большая проблема: никто не знает, как структурировать frontend приложение. Я работал в этой сфере в течении многих лет и «лучшие практики” никогда нас этому не учили. Вместо этого нас „учили“ библиотеки. jQuery? Angular? Backbone? Настоящая проблема — поток данных — до сих пор ускользает от нас.


Что такое Flux?




Flux — это термин, придуманный для обозначения однонаправленного потока данных с очень специфичными событиями и слушателями. Нет Flux библиотек (прим. перев.: на данный момент их полно), но вам будет нужен Flux Dispatcher и любая JavaScript event-библиотека.

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


Не пытайтесь сравнивать Flux с MVC-архитектурой. Проведение параллелей только еще больше запутает вас.


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


1. Ваши представления отправляют события




Dispatcher по своей сути является event-системой. Он траслирует события и регистрирует колбэки. Есть только один глобальный dispather. Вы можете использоватьdispather от Facebook. Он очень легко инициализируется:

var AppDispatcher = new Dispatcher();




Скажем, в вашем приложении есть кнопка “New Item”, которая добавляет новый элемент в список.





Что происходит при клике? Ваше представление отправляет специальное событие, которое содержит в себе название события и данные нового элемента:

createNewItem: function( evt ) {

AppDispatcher.dispatch({
eventName: 'new-item',
newItem: { name: 'Marco' } // example data
});

}


2. Ваше хранилище(store) реагирует на отправленные события




Как и „Flux“, „Store“ — это просто термин, придуманный Facebook. Для нашего приложения нам необходимы некоторый набор логики и данные для списка. Это и есть наше хранилище. Назовем его ListStore.

Хранилище — это синглтон, а это значит, что вам можно не объявлять его через оператор new.



// Global object representing list data and logic
var ListStore = {

// Actual collection of model data
items: [],

// Accessor method we'll use later
getAll: function() {
return this.items;
}

}




Ваше хранилище будет реагировать на посланное событие:

var ListStore = …

AppDispatcher.register( function( payload ) {

switch( payload.eventName ) {

case 'new-item':

// We get to mutate data!
ListStore.items.push( payload.newItem );
break;

}

return true; // Needed for Flux promise resolution

});





Это традиционный подход к тому, как Flux вызвает колбэки. Объект payload содержит в себе название события и данные. А оператор switch решает какое действие выполнить.

Ключевая концепция: Хранилище — это не модель. Хранилище содержит модели.


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


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


Только хранилища регистрируют колбеки в dispatcher. Ваши представления никогда не должны вызвать AppDispatcher.register. Dispatcher только для отправки сообщений из представлений в хранилища. Ваши представления будут реагировать на другой вид событий.


3. Ваше хранилище посылает событие „Change“




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

Ваше хранилище посылает событие, но не использует dispather. Это может сбить с толку, но это „Flux way“. Давайте дадим нашему хранилищу возможность инициировать событие. Если вы используете MicroEvents.js, то это просто:



MicroEvent.mixin( ListStore );




Теперь инициализируем наше событие „change“:

case 'new-item':

ListStore.items.push( payload.newItem );

// Tell the world we changed!
ListStore.trigger( 'change' );

break;




Ключевая концепция: Мы не передаем данные вместе с событием. Наше представление беспокоиться только о том, что что-то изменилось.

4. Ваше представление реагирует на событие „change“




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

Во-первых, давайте подпишемся на событие „change“ из нашего ListStore сразу после создания компонента:



componentDidMount: function() {
ListStore.bind( 'change', this.listChanged );
}




Для простоты мы просто вызовем forceUpdate, который вызовет перерисовку:

listChanged: function() {
// Since the list changed, trigger a new render.
this.forceUpdate();
},




Не забываем удалять слушателя, когда компонент удаляется:

componentWillUnmount: function() {
ListStore.unbind( 'change', this.listChanged );
},




Что теперь? Давайте посмотрим на нашу функцию render, которую я намерено оставил напоследок:

render: function() {

// Remember, ListStore is global!
// There's no need to pass it around
var items = ListStore.getAll();

// Build list items markup by looping
// over the entire list
var itemHtml = items.map( function( listItem ) {

// "key" is important, should be a unique
// identifier for each list item
return


  • { listItem.name }


  • ;

    });

    return


    { itemHtml }



    ;
    }





    Мы пришли к полному циклу. Когда вы добавляете новый элемент, представление отправляет событие, хранилище подписано на это событие, хранилище изменяется, хранилище создает событие „change“ и представление, подписанное на событие „change“, перерисовывается.

    Но тут есть одна проблема. Мы полностью перерисовываем представление каждый раз, когда список изменяется! Разве это не ужасно неэффективно?


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


    Ключевая концепция: Когда хранилище изменяется, ваши представления не должны заботиться том, какое событие произошло: добавление, удаление или изменение. Они должны просто полностью перерисоваться. Алгоритм сравнения „вирутального DOM“ справится с тяжелыми расчетами и изменит реальный DOM. Это сделает вашу жизнь проще и уменьшит головную боль.


    И еще: что вообще такое „Action Creator“?

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



    AppDispatcher.dispatch({
    eventName: 'new-item',
    newItem: { name: 'Samantha' }
    });




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

    ListActions = {

    add: function( item ) {
    AppDispatcher.dispatch({
    eventName: 'new-item',
    newItem: item
    });
    }

    };




    Теперь, ваше представление просто вызывает ListAction.add({name: “...”}) и не переживает о синтаксисе отправки сообщений.

    Оставшиеся вопросы




    Все, о чем говорит нам Flux, это как управлять потоком данных. Но он не отвечает на вопросы:


    • Как вам загружать данные на сервер и как их сохранять на сервере?

    • Как управлять связью между компонентами с общим родителем?

    • Какую event-библиотеку использовать? Имеет ли это значение?

    • Почему Facebook не включил все это в свою библиотеку?

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


    Ответ на все эти вопросы: развлекайтесь!


    PS: Не используйте forceUpdate

    Я использовал forseUpdate ради простоты. Правильное решение будет считать данные из хранилища и скопировать их в state компонента, а в функции render прочитать данные из state. Вы можете посмотреть, как это работает в этом примере.


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


    Вот и все!


    В дополнение можете посмотреть Example Flux Applcation от Facebook.


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


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


    Если этот пост помог вам понять Flux, то подписывайтесь на меня в твиттере.


    Recommended article: Chomsky: We Are All – Fill in the Blank.

    This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


    Простые решения. Прокачиваем картинки


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





    Суть была в следующем: наш проект Сars Mail.Ru имеет множество объявлений, к каждому из которых привязаны несколько фоток. Фотки могут загружаться пользователями вручную, а могут автоматически скачиваться краулером с партнёрских сайтов и прицепляться к объявлениям. При этом сами фотки довольно большие (до 10 Мб), и их почти всегда по несколько штук на объявление. Сами фотки хранятся на нескольких синхронных DAV-aх, нарезаются в несколько размеров, могут снабжаться watermark-ами. Т.е. процесс обработки одной фотографии (crop-resize-split-upload) весьма затратен и требует времени и ресурсов (CPU, диски, сетка).

    Почти идеальная для нас архитектура должна минимизировать использование этих ресурсов и уметь решать следующие задачи:



    • уметь хранить несколько фотографий в разных размерах для одного объявления

    • хранить фотку в единственном экземпляре, даже если она привязана к нескольким объявлениям

    • не выполнять лишних действий при заливке фотографии, которая уже есть в базе, например, если пользователь залил фотку, потом ушел с формы, а потом снова попал на форму и снова залил ту же фотку, не выполнять crop-resize-split-upload, а использовать то, что сделано 5 минут назад

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

    • не оставлять мусора на диске, если пользователь загрузил фото и ушел с сайта, так и не разместив объявление

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

    • сделать чистку хранилища от сгнивших фоток максимально быстрой




    Если вы писали вертикальный поиск или импортируете от партнеров много сущностей с привязанными картинками, то ситуация вам несомненно знакома.

    Прямое решение достаточно традиционно. При подаче объявления вручную делаем POST форму с , пользователь отправляет POST-ом все фотки, они заливаются на проект, а их id прицепляются к объявлению, если оно успешно добавлено в базу. Можем использовать предзагрузчик, а временные фотки класть во временные файлы, память, таблицу и т.п. При автоматическом импорте фоток скачиваем фотографии, заливаем их на проект, привязываем их к объявлениям, возможно, используем кэширование скачивания (если фотку с данного URL уже качали, берем ее с диска, а не льём с партнёра). Удаляя объявление, сначала удаляем с проекта все фотки данного объявления и только потом сносим само объявление.

    Перечислим некоторые недостатки этого решения.



    • Долгое добавление объявления (если не используется предзагрузчик).

    • Необходимо реализовывать отдельный механизм для предзаливки фоток в форме подачи объявления.

    • Предзагрузка пользователем фотки (с выводом preview) и реальное добавление фотки на проект (crop-resize-split-upload) — это разные алгоритмы, и успех первого не означает успех второго.

    • Долгое удаление объявления — при удалении надо удалить все связанные фотки с диска-DAV-а.

    • Суммарные последствия этих минусов, в полной мере проявляющие себя на больших объёмах и при распараллеливании импорта.




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

    CREATE TABLE Images (
    image_id: char(32) PRIMARY KEY, -- id фотки, из которого формируется урл, шарды, префикс для нарезки нескольких размеров и т.д.
    offer_id: int unsigned NOT NULL FOREIGN KEY REFERENCES offers_table(id) ON DELETE RESTRICT, -- обязательная ссылка на объявление для данной фотки
    url_hash: char(32) NULL, -- md5 от урла, с которого картинку скачали
    body_hash: char(32) NULL, -- md5 от тела фотки
    num: tinyint unsigned NOT NULL, -- порядковый номер фотки в объявлении
    last_update: timestamp NOT NULL, -- время последнего изменения записи
    );




    Как я и обещал, решение очень простое — мы просто позволили существовать фоткам, не привязанным к объявлениям, а записям о них — дублироваться. Т.е. просто сделали необязательным внешний ключ offer_id, и убрали UNIQUE с image_id. Вот так:

    image_id: char(32), -- теперь image_id неуникален, и может дублироваться
    offer_id: int unsigned NULL FOREIGN KEY REFERENCES Offers(id) ON DELETE SET NULL




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

    Для этого мы реализовали нижеописанные сценарии:

    Сама форма добавления объявления не содержит и не обязана использовать POST для отправки данных. Фотки в этой форме являются просто скрытыми полями, в которые после предзаливки пользователем фотографий будут записаны соответствующие id фоток. Для заливки фоток используется отдельный ajax url, в который пользователь просто передает файл фотки, а в ответ получает image_id:

    Загрузить фото 1


    Загрузить фото 2





    Внутри URL /pre-upload-photo/ (в который мы отправляем файл фотки) происходит следующее:



    Получаем тело файла фотографии и считаем this_body_hash(md5 от тела фотки);

    Ищем в таблице записи с body_hash == this_body_hash;

    IF (такие записи существуют) {
    Обновляем last_update у этих записей;
    Выбираем одну из них либо создаём новую запись с пустым offer_id и тем же image_id;
    } ELSE {
    Делаем crop-resize-spilt-upload;
    Добавляем запись с данным body_hash, свежим last_update, пустым offer_id и новым image_id;
    }

    Возвращаем image_id выбранной записи;




    Теперь, заливая каждую фотку, юзер получает в ответ image_id, который кладется в соответствующий данной фотке input:







    При добавлении собственно объявления пользователь отправляет на бэкенд пары:

    photo1: 0cc175b9c0f1b6a831c399e269772661
    photo2: 92eb5ffee6ae2fec3ad71c777531578f
    photo3: 4a8a08f09d37b73795649038408b5f33


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



    offer_id: NULL, num: 0, image_id: 4a8a08f09d37b73795649038408b5f33
    offer_id: 1234, num: 3, image_id: 92eb5ffee6ae2fec3ad71c777531578f
    offer_id: 1234, num: 1, image_id: 0cc175b9c0f1b6a831c399e269772661


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


    Итак, если я пользователь и у меня есть всего десять фоток, то, сколько бы я ни размещал объявлений, ни переставлял местами фотографии, и т.п., кроме одноразового crop-resize-split-upload никаких манипуляций над моими фотками выполнено не будет. Только скачивание, подсчет хэша и манипуляции над строками в таблице. Также не забудем rate-limit на URL предзагрузки фоток — чтобы нас не затопили злые DoS-еры.




    Краулер партнерских фоток действует в другой последовательности, но смысл похож. Т.к. у него самое большое время занимает выкачка фотки с сайта партнёра, вместо body_hash используется url_hash (md5 от URL фотки). Таким образом, при помощи той же самой таблицы и той же схемы реализуется кэш скачивания фоток. Т.е. если мы качали фотку в течение N последних дней, независимо от того, использовали мы её или нет, мы не будем второй раз ходить за ней и делать crop-resize-split-upload.

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



    Посчитать this_url_hash от входного URL;

    Получить список фоток из таблицы, у которых url_hash == this_url_hash;

    IF (таких нет) {
    # новая фотка
    Сделать crop-resize-spilt-upload;
    Добавить в таблицу новую запись c нужным offer_id, url_hash, num, last_update;
    } ELSIF (существует такая запись, но с другим, непустым offer_id) {
    # фотка уже есть, но привязана к другому объявлению
    Добавить в таблицу новую запись c тем же image_id, но нашим offer_id, url_hash, num и last_update;
    } ELSE {
    # есть запись о непривязанной, но уже залитой фотке, используем её
    в найденной записи обновить offer_id или num, а также last_update;
    }




    Таким образом, следующее скачивание по этому URL не понадобится — мы используем текущую запись, продублировав её либо обновив.

    DELETE FROM Offers WHERE id=?




    Всё. ON DELETE SET NULL сбросит offer_id у всех фотографий данного объявления, а через N дней за ними явится скрипт чистки фоток и отправит туда, куда отправляются все ненужные фотографии. Собственно, процедура удаления объявления вообще ничего не знает ни про какие фотки, и это прекрасно.

    SELECT image_id FROM ImagesTable i WHERE offer_id IS NULL AND (last_update + INTERVAL ? DAY) < NOW();




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

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


    Таким образом, если юзер предзалил фотку, она будет валяться в базе и на дисках еще две недели, пока не придет чистильщик и не снесёт её. В течение этого времени, если пользователь вернётся на проект, заливка этих фоток для нас будет существенно легче (несколько простых манипуляций с записями в базе вместо crop-resize-split-upload). Фактически мы держим на проекте N-дневное «окно» фоток, которые, возможно, понадобятся.




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

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


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


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


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


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




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

    • предзагрузка юзерских фоток теперь имеет «кэш» (в течение N дней мы никогда дважды не crop-resize-split-upload одну и ту же фотку)

    • отсутствуют временные файлы (или memcached какой-нибудь) при предзагрузке юзерских фоток (за исключением тех, которые требуются для crop-resize-split-upload)

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

    • чистка сгнивших фоток производится очень быстро

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

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




    Простых вам решений!

    Recommended article: Chomsky: We Are All – Fill in the Blank.

    This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.