...

суббота, 26 сентября 2015 г.

Артем Гавриченков, Qrator — DDoS-атаки [Видеолекция]

сегодня в 13:58

Пару недель назад нам удалось вытащить на CodeFreeze с докладом Артема Гавриченкова aka ximaera, CTO в Qrator Labs — компании, которая является одним из мировых лидеров по вопросам борьбы с DDoS-атаками.

Собственно, я позвал Артема сделать нам обзорную лекцию о том, что такое DDoS-атаки, какие они бывают и как с ними бороться.

Если конкретнее:

  • История термина. Чем отличаются DoS- и DDoS-атаки?
  • DDoS-атаки с точки зрения специалистов по информационной безопасности, оценка рисков;
  • Виды атак и их классификация;
  • Противодействие для каждого вида атак;
  • DDoS-атаки в Интернете будущего: как IPv6, IoT и рост числа участников Сети повлияет на её, Сети, безопасность?

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

Команда CodeFreeze желает вам приятного просмотра!

Автор: @23derevo

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

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.

Модификация системного вызова. Часть 2

В предыдущей части мы договорились до того, что не экспортируемые имена ядра Linux могут использоваться в коде собственных модулей ядра с тем же успехом, что и экспортируемые. Одним из таких имён в ядре является селекторная таблица всех системных вызовов Linux. Собственно, это и есть основной интерфейс любых приложений к сервисам ядра. Теперь мы рассмотрим как можно модифицировать оригинальный обработчик любого системного вызова, подменить его, или внести разнообразие в его выполнение в соответствии с собственным видением.
Техника модификации системных вызовов операционной системы известна давно и использовалась в самых разных операционных системах. Это любимая техника писателей вирусов начиная с системы MS-DOS — системы, которая просто провоцировала на такие эксперименты. Но мы будем использовать эту технику в мирных целях… (В разных публикациях такие действия называют по-разному: модификация, встраивание, имплементация, подмена, перехват — они имеют свои нюансы, но в нашем обсуждении могут использоваться как синонимы.)

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

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

Реально схема будет чуть сложнее — необходимо прежде сохранить старое (оригинальное) значение системного обработчика:
— Для вызова оригинального обработчика из собственной функции обработки перед или/и после выполнения модифицированного кода;
— Для восстановления оригинального обработчика при выгрузке модуля.


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

Следующая, гораздо более существенная, трудность здесь состоит в том, что селекторная таблица sys_call_table, в процессорных архитектурах которые это позволяют (а I386 и X86_64 в их числе), размещается в страницах памяти, разрешённых исключительно для чтения (readonly). Это контролируется аппаратно (средствами MMU — Memory Management Unit) и при нарушении прав доступа возбуждается исключение. Поэтому нам нужно снять флаг запрета записи на время модификации элемента sys_call_table и восстановить его после.

В архитектурах I386 и X86_64 флаг разрешения записи определяется битовым флагом в скрытом регистре состояния процессора CR0. Для выполнения нужных нам действий мы используем соответствующе функции, для 32-бит архитектуры, например, они будут выглядеть так (файл CR0.c, этот код написан на инлайновых ассемблерных вставках — расширение компилятора GCC):

// page write protect - on
#define rw_enable()              \
asm( "cli \n"                    \
     "pushl %eax \n"             \
     "movl %cr0, %eax \n"        \
     "andl $0xfffeffff, %eax \n" \
     "movl %eax, %cr0 \n"        \
     "popl %eax" );

// page write protect - off
#define rw_disable()             \
asm( "pushl %eax \n"             \
     "movl %cr0, %eax \n"        \
     "orl $0x00010000, %eax \n"  \
     "movl %eax, %cr0 \n"        \
     "popl %eax \n"              \
     "sti " );


P.S. Различные варианты техники записи в защищённые от записи страницы обсуждались, например, в WP: Safe or Not? и Кошерный способ модификации защищённых от записи областей ядра Linux.

Теперь мы готовы заменить любой системный вызов Linux (man(2)) на свою собственную функцию-обработчик — а это и есть то, к чему мы и стремились. Для иллюстрации работоспособности метода мы заменим (расширим) системный вызов write( 1, … ) — вывод на терминал, задублируем поток вывода в системный журнал (подобно тому что делает команда tee):

#define PREFIX "! " 
#define DEB2(...) if( debug > 1 ) printk( KERN_INFO PREFIX " ---- " __VA_ARGS__ ) 
#define LOG(...) printk( KERN_INFO PREFIX __VA_ARGS__ ) 
#define ERR(...) printk( KERN_ERR PREFIX __VA_ARGS__ ) 

static int debug = 0;                    // debug output level: 0, 1, 2 
module_param( debug, uint, 0 ); 

asmlinkage long (*old_sys_write) ( unsigned int fd, const char __user *buf, size_t count ); 

#define LEN 250 
asmlinkage long new_sys_write ( unsigned int fd, const char __user *buf, size_t count ) { 
   if( 1 == fd ) { 
      char msg[ LEN + 1 ]; 
      int n = count < LEN ? count : LEN, r; 
      if( ( r = copy_from_user( msg, (void*)buf, n ) ) != 0 ) return -EINVAL; 
      if( '\n' == msg[ n - 1 ] ) msg[ n - 1 ] = '\0'; 
      else msg[ n ] = '\0'; 
      if( strchr( msg, '!' ) != NULL ) goto rec; // to prevent recursion 
      LOG( "{%04d} %s\n", count, msg ); 
   } 
rec: 
   return old_sys_write( fd, buf, count );       // original write() 
};

static void **taddr;                             // address of sys_call_table 

static int __init wrchg_init( void ) { 
   void *waddr; 
   if( NULL == ( taddr = find_sym( "sys_call_table" ) ) ) { 
      ERR( "sys_call_table not found\n" ); return -EINVAL; 
   } 
   old_sys_write = (void*)taddr[ __NR_write ]; 
   if( NULL == ( waddr = find_sym( "sys_write" ) ) ) { 
      ERR( "sys_write not found\n" ); return -EINVAL; 
   } 
   if( old_sys_write != waddr ) { 
      ERR( "Oooops! : addresses not equal\n" ); return -EINVAL; 
   } 
   LOG( "set new sys_write syscall [%p]\n", &new_sys_write ); 
   show_cr0(); 
   rw_enable(); 
   taddr[ __NR_write ] = new_sys_write; 
   show_cr0(); 
   rw_disable(); 
   show_cr0(); 
   return 0; 
} 

static void __exit wrchg_exit( void ) { 
   rw_enable(); 
   taddr[ __NR_write ] = old_sys_write; 
   rw_disable(); 
   LOG( "restore old sys_write syscall [%p]\n", (void*)taddr[ __NR_write ] ); 
   return; 
} 

module_init( wrchg_init ); 
module_exit( wrchg_exit ); 


Функцию поиска символа ядра find_sym(), использующую вызов API ядра kallsyms_on_each_symbol(), мы видели в предыдущей части обсуждения. Кроме того, мы делаем контроль (больше для иллюстрации) совпадение адреса имени оригинального sys_write() с этим же адресом, находящимся в позиции __NR_write таблицы sys_call_table.

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

$ sudo insmod wrlog.ko debug=2 
$ ls 
CR0.c  find.c  Makefile  Modi.hist  wrlog.0.c  wrlog.1.c  wrlog.2.c  wrlog.3.c  wrlog.c  wrlog.hist  wrlog.ko 
$ sudo rmmod wrlog 
$ dmesg | tail -n31 
[ 1594.231242] ! set new sys_write syscall [f8854000] 
[ 1594.231248] !  ---- CR0 = 80050033 
[ 1594.231250] !  ---- CR0 = 80040033 
[ 1594.231252] !  ---- CR0 = 80050033 
[ 1594.232737] ! {0052} /home/olej/2015_WORK/http://ift.tt/1LcZRnp 
[ 1594.233368] ! {0078} \x1b[01;32molej@nvidia\x1b[01;34m ~/2015_WORK/http://ift.tt/1LcZRnp $\x1b[00m 
[ 1596.866659] ! {0001} l 
[ 1597.154675] ! {0001} s 
[ 1597.644985] ! {0110} CR0.c  find.c  Makefile  Modi.hist  wrlog.0.c  wrlog.1.c  wrlog.2.c  wrlog.3.c  wrlog.c  wrlog.hist  wrlog.ko 
[ 1597.645196] ! {0113} 
[ 1597.645196] CR0.c  find.c  Makefile  Modi.hist  wrlog.0.c  wrlog.1.c  wrlog.2.c  wrlog.3.c  wrlog.c  wrlog.hist  wrlog.ko 
[ 1597.645321] ! {0052} /home/olej/2015_WORK/http://ift.tt/1LcZRnp 
[ 1597.645951] ! {0078} \x1b[01;32molej@nvidia\x1b[01;34m ~/2015_WORK/http://ift.tt/1LcZRnp $\x1b[00m 
[ 1600.226651] ! {0001} s 
[ 1600.346587] ! {0001} u 
[ 1600.522683] ! {0001} d 
[ 1601.026667] ! {0001} o 
[ 1602.170701] ! {0001} 
[ 1602.426522] ! {0001} r 
[ 1603.218682] ! {0001} m 
[ 1603.682677] ! {0001} m 
[ 1603.906615] ! {0001} o 
[ 1604.338566] ! {0001} d 
[ 1606.442570] ! {0001} 
[ 1606.946670] ! {0001} w 
[ 1607.226667] ! {0001} r 
[ 1607.834662] ! {0001} l 
[ 1608.106672] ! {0001} o 
[ 1608.842694] ! {0001} g 
[ 1612.003059] ! {0002} 
[ 1612.014102] ! restore old sys_write syscall [c1179f70] 


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

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

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

Архив кода для экспериментов можно взять здесь или здесь (из-за несущественности примеров я не размещаю их на GitHub).

P.S. Всё показанное работает в неизменном виде в 32-бит. В 64-бит архитектуре картина становится несколько сложнее за счёт необходимости эмуляции 32-битных приложений. Чтобы не усложнять картину, этот вариант сознательно не затрагивался (возможно пока, и к нему стоит вернуться позже).

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.

Поддержка C++ модулей в Visual Studio 2015 Update 1

сегодня в 18:11

На конференции CppCon, которая проходит прямо сейчас, команда разработчиков компилятора Visual C++ заявила, что в следующем обновлении (Visual Studio 2015 Update 1) в компилятор С++ от Microsoft будет добавлена экспериментальная возможность из нового (ещё не утверждённого) стандарта С++ — поддержка модулей!

Для тех, кто не в курсе в чём эпохальность данного события: так уж сложилось, что механизм использовани компонентов в программах на С++ придумывался где-то лет 35 назад. Его нельзя назвать удобным: если вы хотите создать библиотеку — вам нужно сделать заголовочный файл и распространять с ним либо код, либо скомпилированную версию библиотеки. При этом возникает куча проблем:

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

В итоге в инфраструктуре С++ отсутствуют понятия «сборок» или «пакетов» и, в отличии от С# или Python, где установка компонентов тривиальна, в С++ подключение каждой новой библиотеки может нести свои неожиданности. Предлагаемый механизм модулей в С++ призван убрать данную проблему, отказаться от директивы препроцессора #include и ссылаться на компоненты, как на некоторую сущность, состоящую из кода и метаданных, целостную и легко подключаемую. В итоге мы вскоре можем получить существенное ускорение внедрения новых компонентов в проект, появления полноценных менеджеров пакетов, установка новой библиотеки сведется к выполнению одной строки или нескольким кликам мышью. Это ли не счастье!

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

Видео презентации пока что не доступно на канале конференции, так что всё, что у нас есть — несколько фотографий из твиттера, да ссылки на предлагаемый драфт стандарта модулей и его реализации в Visual C++ и Clang.

Автор: @tangro

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

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.

«Книжная полка фронтендера» — как гугл, только лучше

Список книг — «Книжная полка фронтендера

Привет, читатель!

В этой публикации я расскажу тебе о «Книжной полке фронтендера».

Предыстория


К сожалению, в рунете очень мало по-настоящему качественных ресурсов для изучения фронтенда. Эти ресурсы можно буквально пересчитать по пальцам одной руки — это Frontender Magazine, HTML Academy, «Современный учебник JavaScript». Я благодарю авторов этих ресурсов за их неоценимый вклад в развитие и популяризацию фронтенда в рунете.

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

Проблема


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

Решение


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

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

Планы на будущее


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

Кстати, для реализации всего вышеперечисленного придётся писать бэкэнд — было бы достаточно простого RESTful API. Если вы хотите принять участие в разработке проекта, то свяжитесь со мной (контакты есть в профиле) или напишите в комментарии и я сам свяжусь с вами.

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

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

И да, приветствуется любая конструктивная критика и предложения по улучшению проекта. Надеюсь что «книжная полка» будет вам полезна!

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.

[Перевод] Присоединяйте файлы к комментариям

Довольно давно вы можете присоединять картинки к описаниям проблем и запросов на слияние. Теперь мы расширили эту возможность, и она получает поддержку файлов:
  • Microsoft Word (.docx)
     
  • Microsoft Powerpoint (.pptx)
     
  • Microsoft Excel (.xlsx)
     
  • Текстовых (.txt)
     
  • Документов PDF (.pdf)

[скриншот присоединения файла]
Просто перетащите и бросьте файлы в рамку комментария — и они появятся у вас в комментарии.

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.

DropBox вновь раздает место студентам/преподавателям

сегодня в 09:54

Университетский кубок Dropbox — очередная акция с раздачей до 25 ГБ на 2 года (до 1 января 2018 года).
Акция заканчивается 14 декабря 2015 года.
Как зарегистрироваться для участия в Университетском кубке?
1. Откройте домашнюю страницу Университетского кубка.
2. Войдите в свой аккаунт Dropbox (если Вы еще этого не сделали).
3. Подтвердите электронный адрес, который у Вас зарегистрирован в колледже, университете или другом учебном заведении (это должен быть именно тот Ваш адрес, который располагается на домене Вашего вуза).
4. Проверьте свою электронную почту, подтвердите адрес и завершите регистрацию.

  • Если у Вас показывается пустая страничка, отключите блокировщик рекламы.
  • Бизнес аккаунты Dropbox не могут принимать участие в Университетском кубке

Бонусы
За регистрацию/верификацию имейла Вы получаете 3ГБ места.
При регистрации большого числа участников от Вашего учебного заведения эти участники получают дополнительное место (до 22ГБ). Для каждого допущенного учебного заведения устанавливаются 3 значения очков, при которых добавляется пространство в хранилище. Первый порог — 5ГБ, второй — 7ГБ, третий — 10 ГБ.

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

  • Установить Dropbox на свой компьютер и войти в аккаунт — 20
  • Установить мобильное приложение Dropbox и войти в аккаунт — 5
  • Создать новую общую папку — 10
  • Создать ссылку доступа — 5
  • Создать запрос файла — 5
  • Пригласить друга (этот друг должен принять приглашение) в течение первой недели Вашего участия в кубке — 3 (за приглашение)
  • Пригласить друга (этот друг должен принять приглашение) по истечении первой недели Вашего участия в кубке — 1 (за приглашение)

Полученное бонусное место можно проверить в http://ift.tt/1KAXwPC. Дополнительное место появится в Вашем аккаунте через час после того, как Ваш университет достигнет порогового количества очков.

Ссылки по теме.
Страничка акции
F.A.Q

@benipaz

карма 0,0

рейтинг 4,0

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.

Перенаправление данных из COM-порта в web. Доработка

Недавно я опубликовал статью «Перенаправление данных из COM-порта в web», в которой описал прототип системы, транслирующей строки из последовательного порта компьютера в веб-браузер. В той статье я указал направления, в которых надо доработать прототип, чтобы приблизить его к продакшен-стадии:
— никакой дизайн веб-страницы
— в каждый момент времени данные получит только один веб-клиент
— очень ограниченный набор браузеров, с помощью которых можно получить доступ. Например, не работает ни в Internet Explorer 8, ни в браузере из Android 2.3.5
— требуется установка python

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

Сразу покажу итоговый результат:

На этом видео видно, что строки из COM-порта отображаются сразу в трёх браузерах одновременно: в Firefox и IE 8 на том же компьютере, к которому подключена Arduino, и на смартфоне.
Сама Arduino передаёт строку «Температура: XXXXXX», а строку с датой-временем и номером строки — одна из частей бэкенда.

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

Плохой дизайн веб-страницы

В предыдущей статье я писал, что в создании веб-фронтенда я разбираюсь от слова «никак», поэтому создание нормальной веб-страницы было для меня самым сложным. К счастью я почти сразу же обнаружил сайт w3schools.com, на котором открыл для себя Bootstrap и нашёл хорошие учебники по Ajax и jQuery. Для матёрых фронтенд-разработчиков представленные на этом сайте учебники, наверное, вызовут только улыбку, но для таких новичков, как я, это самое то, что надо.
Самое приятное в этих учебниках то, что они очень небольшие и очень по существу. Без размазывания каши по тарелке. В принципе, одного вечера на изучение достаточно, чтобы начать что-то делать.

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

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

  <!-- Vertical aligment of text from "Vertical Centering in CSS" at 
  http://ift.tt/1YFVTaw -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
    <style type="text/css">
    html, body {
        height: 100%;
        margin: 0px;
    }
  </style>
</head>

<body>

  <div style="display: table; height: 90%; width: 100%; overflow: hidden;">
    <div style=" display: table-cell; vertical-align: middle;">
      <div style="text-align: center">
        data place<br>
        пока нет данных
      </div>
    </div>
  </div>

  <div style="display: table; height: 10%; width: 100%; overflow: hidden;">
    <div style=" display: table-cell; vertical-align: middle;">
        <div style="text-align: right;">
            buttons
        </div>
    </div>
  </div>
</body>
</html>

Практически весь остальной код страницы получился после чтения учебников по Bootstrap и JavaScript (раздел JS HTML DOM). Исключение — это код jQuery, который обновляет данные на странице. Но об этом чуть позже.

Ограниченный набор поддерживаемых браузеров

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

Можно было бы, наверное, воспользоваться технологией WebSockets, но, насколько я понял, в IE она поддерживается только с 10-й версии, а у меня IE 8, поэтому я даже не стал прорабатывать это направление.
Ещё буквально на днях в какой-то из статей на хабре или гиктаймс я наткнулся на упоминание библиотеки SockJS, которая, похоже, умеет обходить отсутствие WebSockets, но, во-первых, для неё требуется спец. поддержка на стороне сервера, а, во-вторых, у меня к этому моменту нормально работал Ajax, поэтому и она осталась без моего внимания.

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

        function get_data()
        {
            var xmlhttp;
            xmlhttp=new XMLHttpRequest();
            xmlhttp.open("GET","/get_serial?r=" + Math.random(),true);
            xmlhttp.onreadystatechange=function()
              {
              if (xmlhttp.readyState==4 && xmlhttp.status==200)
                {
                document.getElementById("data").innerHTML=xmlhttp.responseText;
                get_data();
                }
              }
            xmlhttp.send();
        }


который вызывался при окончании загрузки страницы:
<body onload="get_data()">

В этом коде надо, наверное, обратить внимание на два момента. Во-первых, на строчку

            xmlhttp.open("GET","/get_serial?r=" + Math.random(),true);


Именно в ней происходит обращение к веб-серверу за очередной строкой из COM-порта. Добавка
r=" + Math.random()


нужна для того, чтобы Internet Explorer не кэшировал ответы. В противном случае он пошлёт только один Ajax-запрос, получит ответ и больше уже не будет обращаться к серверу. В интернете я видел решения проблемы кэширования путём посылки со стороны сервера специальных HTTP-заголовков
        response.headers['Last-Modified'] = datetime.now()
        response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0'
        response.headers['Pragma'] = 'no-cache'
        response.headers['Expires'] = '-1'


но у меня это почему-то не заработало.
Альтернативный вариант — это использование POST вместо GET. Говорят, что такие запросы IE не кэширует, но я не проверял.
Кстати, jQuery, использует точно такой же способ борьбы с кэшированием — он к УРЛ-у тоже добавляет случайную строку. Только выглядит она несколько иначе.

Второй момент — это последовательность строк

            xmlhttp.open("GET","/get_serial?r=" + Math.random(),true);


и
            xmlhttp.onreadystatechange=function()
            ...

Изначально они стояли в другом порядке. И в Internet Explorer-е это приводило к странным проблемам — он выжирал всю доступную память. И только после этого обновлял данные. На чём и заканчивал свою работу.
Только на сайте XmlHttpRequest.ru я нашёл, что при повторном использовании объекта XMLHttpRequest рекомендуется сначала вызвать метод open() и только после этого менять свойство onreadystatechange.

Получив столько проблем с IE на ровном месте, я решил, что надо бы воспользоваться библиотекой, в которой подобные нюансы скорее всего уже учтены. Поскольку для Bootstrap-а на странице уже загружался jQuery

  <script src="http://ift.tt/1LdO4RA"></script>


я решил, что негоже не воспользоваться поддержкой Ajax, встроенной в эту библиотеку. Поэтому код обновления данных преобразовался в такой:
        function on_load(responseTxt, statusTxt, xhr){
            if( statusTxt == "success" ){
                $("#data").load("/get_serial?r=" + Math.random(), on_load )
            } else {
                $("#data").text( "Error: " + xhr.status + ": " + xhr.statusText );
            }
        }
        $(document).ready(function(){
                on_load("", "success", 3);
            });

Один клиент в каждый момент времени

В предыдущей статье я уже писал про возможный путь решение этой проблемы: разбиение бэкенда на две части, использование ZMQ для их связи + многопользовательский http-сервер в качестве второй части. В качестве такого сервера я выбрал Flask. Я не проводил никаких сравнений альтернатив, он просто попался мне первым. Выяснилось, что его можно запустить в режиме параллельной обработки http-запросов и этого оказалось достаточным. Для запуска такого режима достаточно передать ему параметр
threaded = True (см. http://ift.tt/1YFVRiZ):

    app.run(host='0.0.0.0', debug=False, threaded=True)

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

Но больше всего мне хотелось бы рассказать про библиотеку ZMQ — продемонстрировать её потрясающие возможности.
Вот код демонстрационного ZMQ-сервера:

import zmq
import time

context = zmq.Context.instance()

pub_sock = context.socket(zmq.PUB)
pub_sock.bind( 'tcp://*:12345' )

count = 0
def get_full_line_from_serial():
    global count
    
    count += 1
    return time.strftime( '%Y.%m.%d %H:%M:%S' ) + ': string N %d' % count


while True:
    line = get_full_line_from_serial()
    print line
    pub_sock.send( line )
    time.sleep(1)


который имитирует чтение из COM-порта. На самом деле он каждую секунду генерирует новую строку, «публикует» её (т. е. отсылает всем подписавшимся клиентам) и для отладки выводит её на печать.

А вод код клиента:

import zmq

context = zmq.Context.instance()
zmq_sub_sock = context.socket(zmq.SUB)
zmq_sub_sock.setsockopt(zmq.SUBSCRIBE, '')
zmq_sub_sock.connect( 'tcp://localhost:12345' )

poller = zmq.Poller()
poller.register( zmq_sub_sock, zmq.POLLIN )

while True:

    socks = dict(poller.poll(timeout=5000))
    if zmq_sub_sock in socks:
        print zmq_sub_sock.recv()
    else:
        print 'No data within 5 sec'


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

А вот демонстрация их совместной работы:

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

Standalone приложение

Я уже, вроде бы, упоминал, что python слабо приспособлен к созданию standalone приложений. Это интерпретируемый язык, для обычной работы которого требуется программа-интерпретатор. К сожалению для него не существует компилятора, который бы умел генерировать нативный бинарный код. Мне известны две программы, позволяющие создать из скрипта некое подобие самостоятельного приложения: py2exe и pyInstaller. Сам я чаще всего пользуюсь вторым, поэтому и в данном проекте решил использовать его.

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

Поскольку ни создатели питона, ни создатели библиотек под него не задумывались о таком возможном использовании, всё это работает несколько «через пень-колоду». Основные проблемы заключаются в подготовке списка всех необходимых библиотек и модулей. Потому что многие авторы библиотек используют неявное импортирование, при котором нет явного указания на то, какой модуль будет импортирован в скрипт или другой модуль. Соответственно частенько после автоматического анализа скрипта pyInstaller-ом надо руками дописывать в настроечный файл (с расширением .spec) имена дополнительных модулей и/или путей, где их искать. Вместе с pyInstaller-ом идёт большой набор готовых функций для таких библиотек, но жизнь не стоит на месте. В частности так оказалось с текущей версией ZMQ — разработчики pyZMQ (биндинга ZMQ к python-у) изменили механизм импортирования вспомогательных библиотек, в результате чего собранное pyInstaller-ом приложение не запускается. Народ с этим делом уже разобрался и соответствующий патч для pyInstaller-а подготовили. Патч этот рабочий, но в официальный релиз пока не попал, поэтому пришлось руками патчить pyInstaller. Код патча смотри на http://ift.tt/1YFVTaB

Но даже этого патчка оказалось мало в случае, если standalone приложение должно представлять из себя только один исполнимый файл, а не целую папку. Пришлось руками ещё корректировать .spec-файл, чтобы библиотека libsodium.pyd из дистрибутива ZMQ попала в нужное место при распаковке .exe-файла.

Вторая проблема заключалась в модуле multiprocessing. Бэкенд я разбил на две части, которые должны работать параллельно. Мне показалось неправильным, если для запуска бэкенда придётся запускать две отдельные программы. А использовать многопоточность (multithreading) мне показалось неправильным из-за наличия GIL (Global Interpretation Lock). Казалось бы, чего проще написать простенький скрипт, который при старте будет просто порождать два новых процесса: один для ZMQ-сервера, читающего данные из COM-порта, второй — для HTTP-сервера, отвечающего на запросы веб-клиентов. И действительно, при работе в обычном режиме (т. е. при «ручном» запуске интерпретатора) прекрасно работает следующий скрипт:

# -*- coding: utf-8 -*-

from multiprocessing import Process
import serial_to_zmq
import zmq2web_using_flask
import time


def main():
    args = get_command_line_params()
    
    p1 = Process( target=serial_to_zmq.work, args=(args.serial_port_name, args.serial_port_speed, args.zmq_pub_addr) )
    p1.start()
    
    p2 = Process(target=zmq2web_using_flask.work, args=(args.zmq_sub_addr,))
    p2.start()    
    
    print 'Press Ctrl+C to stop...',
    while True:
        time.sleep(10)
    
    
def get_command_line_params():
    ...
    
if __name__ == '__main__':
    main()

Но после обработки pyInstaller-ом полученный .exe нормально не работал. Происходил запуск нескольких ZMQ-серверов и Flask-приложений с соответствующими сообщениями о том, что «сокет уже занят» и ещё какие-то странности. Выяснилось, что при запуске Flask-приложения ему надо передавать параметр debug=False:

    app.run(host='0.0.0.0', debug=False, threaded=True)

а для модуля multiprocessing нужно вызывать специальную функцию freeze_support(), которая нужна в том режиме (froozen), который создаётся при интерпретации скрипта в случае standalone приложения, созданного pyInstaller-ом.

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

Все исходные тексты можно взять на гитхабе: http://ift.tt/1YFVTaD

P.S. Ещё учебник по Bootstrap: http://ift.tt/1YFVRj7

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.

Facebook вводит шифрование для Internet.org

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

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

Как это работает?


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

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

Таким образом, новая система дает пользователям internet.org надежный способ подключения к своим серверам, с шифрованием всех передаваемых и принимаемых данных. Сейчас платформа обеспечивает потребности около миллиарда человек в 19 различных странах. Из новых стран стоит указать Южную Африку, Сенегал и Боливию, где Internet.org заработал летом.

По словам Марка Цукерберга, при желании к Internet.org может подключиться любой сервис. Сейчас в каталоге сервисов и приложений Internet.org уже относительно большое количество участников, среди которых, конечно же, и Facebook.

Что такое Internet.org?


Сам проект был представлен главой Facebook Марком Цукербергом еще два года назад. В проекте приняли участие и другие компании, включая Ericsson, Qualcomm, Samsung и Opera. Основная миссия проекта — обеспечение доступа в Сеть для 4 миллиардов еще не имеющих подключения жителей Земли. Понятно, что это касается, в основном, беднейших регионов.

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

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 Data Binding in RecyclerView

На Google IO 2015 анонсировали новую библиотеку Data Binding Library. Основная ее задача — вынесения взаимодействия модели и View в xml-файлы. Она значительно упрощает написание кода и избавляет от необходимости использования методов findByViewId(), добавления ссылок на view-элементы внутри Activity/Fragment’ов. Также она позволяет использовать кастомные атрибуты, привязывая их к статическим методам. Поскольку статьей просто по Data Binding уже достаточно, но по его использованию в RecycleView всего ничего, восполним этот пробел.

Настройка

Для начала заходим в файл build.gradle, который лежит в корневом каталоге проекта. В блоке dependencies выставляем:
buildscript {
   repositories {
       jcenter()
   }
   dependencies {
       classpath "com.android.tools.build:gradle:1.3.0"
       classpath "com.android.databinding:dataBinder:1.0-rc1"
   }
}

allprojects {
   repositories {
       jcenter()
   }
}

Далее подключим Data Binding плагин к проекту. Для этого в build.gradle добавляем строчку с плагином. Также проверяем, чтобы compileSdkVersion была 23.

apply plugin: 'com.android.application'
apply plugin: 'com.android.databinding'

Биндинг

Перейдем к созданию xml-файла. Он, как обычно, создается в паке res/layoyt. В качестве корневого тега используем layout. Android Studio может подсвечивать его красным или предлагать выставить ширину и высоту, но мы ее игнорируем.
<layout xmlns:android="http://ift.tt/nIICcg" 
   xmlns:app="http://ift.tt/GEGVYd"> 

   <data> 
   </data> 

   <!-- Сюда добавляем свой layout --> 

</layout>

Чтобы создался биндер-класс, который и будет привязывать модель к view, нужно привязать xml к модели. Для этого внутри тега указываем имя и путь к нашей модели. В качестве примера будет отображатьcя список фильмов.

public class Movie {
   public boolean isWatched;
   public String image;
   public String description;
   public String title;

   public Movie(boolean isWatched, String image, String description, String title) {
       this.isWatched = isWatched;
       this.image = image;
       this.description = description;
       this.title = title;
   }
}

<layout xmlns:android="http://ift.tt/nIICcg" 
   xmlns:app="http://ift.tt/GEGVYd"> 

  <data>
   <variable
       name="movie"
       type="com.example.databinding.Movie" />
  </data>

 <!-- Сюда добавляем свой layout --> 

</layout>

Осталось добавить свой layout и привязать к нему модель. Пусть у каждого фильма будет картинка, заголовок и краткое описание. Чтобы указать, что поле будет считываться из модели используем “@{*какое поле из модели использовать*}”.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://ift.tt/nIICcg"
   xmlns:app="http://ift.tt/GEGVYd">

   <data>
       <variable
           name="movie"
           type="com.example.databinding.Movie" />
   </data>

   <android.support.v7.widget.CardView
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:orientation="vertical"
       android:layout_margin="8dp">

       <RelativeLayout
           android:id="@+id/relativeLayout"
           android:layout_width="match_parent"
           android:layout_height="wrap_content">

           <ImageView
               android:id="@+id/imageView"
               ...              
               app:imageUrl="@{movie.image}"/>
           <TextView
               android:id="@+id/textView"
               ...
               android:text="@{movie.title}"
               android:textAppearance="?android:attr/textAppearanceLarge" />

           <TextView
               android:id="@+id/textView2"
               ...
               android:text="@{movie.description}"
               android:textAppearance="?android:attr/textAppearanceSmall" />

       </RelativeLayout>

   </android.support.v7.widget.CardView>

</layout>

С android:text="@{movie.title}" и android:text="@{movie.description}" все понятно — просто в качестве текста будет показано соответствующее поле, но что на счет app:imageUrl="@{movie.image}"? Тут начинается реальная магия Data Binding. Вы можете добавлять сколько угодно кастомных атрибутов и даже не прописывать их в atts.xml, а аннотация @BindingAdapter() поможет вам их обработать. Ниже будет показано, как обрабатывать такие аннотации.

Перейдем к адаптеру. Напишем простой RecyclerView.Adapter. Начнем с ViewHolder. Как он выглядел раньше:

public static class MovieItemViewHolder extends RecyclerView.ViewHolder {
   private TextView title, description;
   private ImageView image;

   public ViewHolder(View v) {
       super(v);
       title = (TextView) v.findViewById(R.id.textView);
       description = (TextView) v.findViewById(R.id.textView2);
       image = (ImageView) v.findViewById(R.id.imageView);
   }
}

Как он выглядел после Butter Knife:

public static class MovieItemViewHolder extends RecyclerView.ViewHolder {
   @Bind(R.id.textView) TextView title;
   @Bind(R.id.textView2) TextView description;
   @Bind(R.id.imageView) ImageView image;

   public ViewHolder(View v) {
       super(v);
       ButterKnife.bind(v);
   }
}

Как он выглядит после DataBinding:

public class MovieItemViewHolder extends RecyclerView.ViewHolder {

   MovieItemBinding binding;

   public MovieItemViewHolder(View v) {
       super(v);
       binding = DataBindingUtil.bind(v);
   }
}

Далее нас интересуют два основных метода адаптера: onCreateViewHolder и onBindViewHolder. Созданием и биндигом будет заниматься MovieItemBinding. Он генерируется по названию xml, который мы написали выше. В данном случае файл xml назывался movie_item.xml.

@Override
public MovieItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
   LayoutInflater inflater = LayoutInflater.from(parent.getContext());
   MovieItemBinding binding = MovieItemBinding.inflate(inflater, parent, false);
   return new MovieItemViewHolder(binding.getRoot());
}

Теперь перейдем к onBindViewHolder, как он выглядел раньше:

@Override
public void onBindViewHolder(MovieItemViewHolder holder, int position) {
   Movie movie = Movie.ITEMS[position];
   holder.title.setText(movie.title);
   holder.description.setText(movie.description);
   Picasso.with(holder.image.getContext()).load(movie.image).into(holder.image);
}

Как он выглядит теперь:

@Override
public void onBindViewHolder(MovieItemViewHolder holder, int position) {
   Movie movie = Movie.ITEMS[position];
   holder.binding.setMovie(movie);
}

Но это еще не всё, как на счет кастомного app:imageUrl="@{movie.image}"?.. Опять же все просто: внутри адаптера делаем статический метод с аннотацией @BindingAdapter. Внутрь аннотации передаем наш аттрибут. В итоге получаем

@BindingAdapter("bind:imageUrl")
public static void loadImage(ImageView imageView, String v) {
   Picasso.with(imageView.getContext()).load(v).into(imageView);
}

На вход поступит imageView и то, что передаст модель в качестве image. Теперь все заработает.

Остальные полезности

В модели Movie была переменная isWatched. Допустим, мы хотим, чтобы у просмотренных и новых фильмов были разные обработчики на клик. С DataBinding’ом теперь это проще простого. Напишем обработчик нажатия для фильма.
public interface MovieClickHandler{
   void onNewClick(View view);
   void onWatchedClick(View view);
}

Добавим его в xml-файл в тег data.

...

   <data>
       ...
       <variable name="click" type="com.example.databinding.MovieClickHandler" />

   </data>
...
           <ImageView
            ...
           android:onClick="@{movie.isWatched ? click.onWatchedClick : click.onNewClick}"/>
         
...

Теперь в методе адаптера onBindViewHolder можно засетить наш лисенер. Как и в случае с биндером, название метода генерируется соотвественному названию переменной в xml-файле.

public void onBindViewHolder(MovieItemViewHolder holder, int position) {
   Movie movie = Movie.ITEMS[position];
   holder.binding.setMovie(movie);
   holder.binding.setClick(new MovieClickHandler() {
       @Override
       public void onWatchedClick(View view) {

       }

       @Override
       public void onOldClick(View view) {

       }
   });
}

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

<ImageView
   ...
   app:filter='@{movie.isWatched ? "grey" : null}'
   .../>

В адаптере через @BindingAdapter реализуем обработку

@BindingAdapter("bind:filter")
public static void applyFilter(ImageView imageView, String v) {
   imageView.setColorFilter(null);
   if("grey".equals(v)){
       ColorMatrix matrix = new ColorMatrix();
       matrix.setSaturation(0);
       ColorMatrixColorFilter cf = new ColorMatrixColorFilter(matrix);
       imageView.setColorFilter(cf);
   }
}

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

<TextView
   ...
   android:text='@{movie.title ?? "unknown"}'
   ... />

Стоит также отметить, что внутри MovieItemBinding содержатся ссылки на все view, у которых есть ID в xml-файле.

Итог

Библиотека очень упрощает работу с RecycleView, количество кода при написании теперь уменьшается в разы, при этом никаких if/else для колбеков и данных. С JavaRX можно еще больше упростить обновление данных, пока правда оно работает только в одну сторону: при изменении данных обновляется UI, но не наоборот.

Полезные ссылки:

Тестовый проект.
Официальная документация.
Быстрый старт Data Binding в Android.

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.

пятница, 25 сентября 2015 г.

Дизайн карты: как и почему

В сети можно найти достаточно материалов по созданию стилей карт (и на хабре в том числе), но в основном они показывают техническую сторону создания стиля в tilemill/mapbox studio. А вот с теорией дела обстоят гораздо хуже, в основном пишут о каких-нибудь отдельных аспектах дизайна, и то не всегда объясняют почему нужно делать так, а не иначе. В этой статье я постараюсь исправить эту ситуацию и поделиться опытом с начинающими картостроителями.


Всех интересующихся прошу под кат.

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


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

Изначально у нас пустой холст, иногда его принимают за мировой океан, поскольку добавлять данные об океанах не всегда целесообразно. Затем идут континенты, потом данные по землепользованию, потом водоёмы и так дальше. Хорошим ориентиром будет описание слоёв данных в Mapbox Streets. Особенно обратите внимание на слои с подписями, их очерёдность зачастую не совпадает с очерёдностью самих объектов. Так же заранее продумайте как могут пересекаться ваши слои: тоннели, мосты, эстакады и т.д. Для дорожной сети хорошим ориентиром является организация уровней в osm-bright. Ещё обратите своё внимание на железнодорожную сеть, трамваи и прочие виды транспорта, стоит ли их выделять в отдельные слои и как в этом случае поступать с пересечениями.

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

Пример

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


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

Цвет


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

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

Как видите на Яндекс-картах дела обстоят лучше чем в дефолтном стиле OSM.

А вот при наложении информации о пробках у Яндекса ломается иерархия, народ с нарушениями цветовосприятия давно уже жалуется, но увы…

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

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

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

Цветовое многообразие


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

Пример

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


Старайтесь использовать естественные (логичные) цвета, не стоит делать водоёмы розового цвета. Используйте устоявшиеся ассоциации.

Принципы гештальта


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

Советую прочитать все статьи Steven Bradley из цикла Design principles, и особенно внимательно эти: Design Principles: Connecting And Separating Elements Through Contrast And Similarity, Design Principles: Visual Weight And Direction.
Вооружившись новыми знаниями, приступайте к экспериментам, пробуйте разные варианты, и смотрите что работает лучше.

Подписи


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

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

Иконки


Иконки добавляются одним из последних слоёв, наряду с подписями. К этому моменту плотность информации на карте уже довольно большая. Но у иконок есть преимущество — мы можем задавать их форму. Есть несколько вариантов иконок: только изображение иконки и изображение иконки на шильдике (в рамке какой-либо формы). Так вот, разным группам объектов на карте можно сопоставлять иконки разного типа. Вспомните, как некоторые категории дорожных знаков задаются формой и цветом. Здесь можно поступить таким же образом. Если не стоит задача создавать уникальный набор иконок с нуля, то можно воспользоваться одним из готовых, например набором maki, кстати, для его модификации существует отдельный инструмент: makizushi.
После того как вы закончили с визуальной стороной вопроса, можно переходить к оптимизации стиля. Суть оптимизации в уменьшении количества запрашиваемых данных, необходимых для отрисовки метатайлов. Поэтому первым делом убираем все данные, которые нам по какой-то причине так и не пригодились. Убираем тестовые данные, ненужные атрибуты и т. д. Затем нужно добавить дополнительные ограничения по охвату, зуму и плотности подписей на ячейку условной пиксельной сетки. Это существенно уменьшит время отрисовки метатайлов. Почитать про это можно в мануале по PostGIS от Mapbox: postgis manual.
Если вы работаете с данными OSM, а скорее всего это так, то не забывайте о двух вещах. Во-первых, в данных есть ошибки, так что учитывайте это, делайте соответствующие проверки. Во-вторых, вы можете легко модифицировать данные: добавлять свои атрибуты, менять и добавлять геометрии, объединять собственные данные с данными OSM.
На этом всё, надеюсь статья будет полезной начинающим картографам-дизайнерам. Создавайте свои стили и делитесь результатами с друзьями миром и вместе наслаждайтесь созданной красотой =)

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

На этом всё. Изучайте правила, чтобы знать, как правильно их нарушать. Как на этой картинке про контекст =).

Всем удачи.

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.

[Перевод] Алгоритмы в реальном мире: Подбор жилья для гостей конференции

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

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

Сложности


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

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

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

Процесс подбора


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

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

Граф совместимости может выглядеть как-то так:

Алгоритм


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

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

Чтобы добиться более хороших результатов, понадобится другой алгоритм. Можно взглянуть на проблему выбора, как на проблему максимального потока и использовать алгоритм Эдмондса-Карпа или Хопкрофта-Карпа.

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

Результаты


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

Первый не самый оптимальный алгоритм нашел максимальное совпадение для 95% хакеров. Оптимальный алгоритм позволил разместить всех гостей.

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.

[Перевод] Утилиты командной строки могут быть в 235-раз быстрее вашего Hadoop кластера

Примечания tsafin:

Перед публикацией своего цикла статей по MapReduce в Caché, мне показалось важным озвучить данную прошлогоднюю точку зрения из статьи Адама Дрейка «Command-line tools can be 235x faster than your Hadoop cluster». К сожалению оригинальная статья Тома Хайдена, на которую он ссылается стала уже недоступна на сайте Тома, но её, по-прежнему, можно найти в архивах. Для полноты картины предлагаю ознакомиться и с ней тоже.


Введение


Посещая в очередной раз свои любимые сайты, я нашел крутую статью Тома Хайдена об использовании Amazon Elastic Map Reduce (EMR) и mrjob для вычисления статистики отношения выигрыш/проигрыш в наборе данных со статистикой по шахматным матчам, которую он скачал с сайта millionbase archive, и c которой он начал играться используя EMR. Так как объем данных был всего 1.75GB, описывающий 2 миллиона шахматных партий, то я скептически отнесся к использованию Hadoop для данной задачи, хотя были и понятны его намерения просто поиграться и изучить плотнее, на реальном примере, утилиту mrjob и инфраструктуру EMR.

Изначально формулировка задачи (найти строку результата в файле и подсчитать расклад) больше подходила для поточной обработки посредством команд shell. Я попытался применить такой подход с похожим объемом данных на моем лаптопе, и получил результат примерно через 12 секунд (т.е. эффективная скорость обработки данных около 270МБ/сек), тогда как обработка в Hadoop заняла 26 минут (т.е. эффективная скорость обработки данных 1.14МБ/сек).

При рассказе об обработке такого объема данных на кластере из 7 машин c1.medium Том сказал, что это заняло 26 минут в кластере и «возможно это время лучше чем если бы я делал это последовательно на моей машине, но все-таки медленнее чем если бы обрабатывал это посредством хитрого, многопоточного приложения локально.»

Это абсолютно верно, хотя заметим, что даже локальная последовательная обработка может легко побить эти 26 заявленных минут. Да, Том делал этот проект просто чтобы развлечься и получить удовольствие, но часто многие другие используют инструменты Big Data (tm) для обработки и анализа такого, «не очень большого», объема данных, с которым получить результаты можно применяя инструменты и технике и попроще и побыстрее.

Один, наиболее недооцененный подход для обработки данных — это использование старых добрых инструментов и конструкций shell. Преимущества такого подхода огромны, т.к. создавая конвейер данных из shell команд, вы все шаги обработки запускаете в параллель с минимальными накладными расходами. Это, как если бы у вас был свой локальный кластер Storm. Можно даже попытаться перевести концепции Spouts, Bolts и Sinks в простые команды и конвейеры в shell. Вы можете легко конструировать конвейер для обработки данных из простых команд, и работать они будут быстрее большинства инструментов из арсенала Big Data (tm).

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

Результирующий конвейер, который мы создали, был более чем 236 раза быстрее аналогичной Hadoop реализации и почти не съедал память.

Исследование данных


Первым шагом упражнения будет выкачивание этих файлов PGN. Т.к. я не представлял, как выглядит этот формат, то я заглянул в Wikipedia.
[Event "F/S Return Match"]
[Site "Belgrade, Serbia Yugoslavia|JUG"]
[Date "1992.11.04"]
[Round "29"]
[White "Fischer, Robert J."]
[Black "Spassky, Boris V."]
[Result "1/2-1/2"]
(moves from the game follow...)


Нас интересуют только результаты игр, с 3 возможными вариантами. 1-0 будет означать что выиграли белые, 0-1 – выиграли черные и 1/2-1/2 означает ничью в партии. Там так же есть результат - , который означает, что игра продолжается или была прервана, но мы игнорируем этот случай в нашем упражнении.

Получение набора данных

Первым делом постараемся скачать данные об играх. Это оказалось не так легко, но после короткого поиска в сети я нашел Git репозиторий rozim в котором было множество различных источников данных о шахматных играх за различные промежутки времени. Я использовал код из этого репозитория для компиляции набора данных объемом в 3.46ГБ, что почти вдвое больше объема данных, используемых Томом в его тесте. Следующим шагом поместим все эти данные в наш конвейер.

Построение конвейера по обработке данных


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

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

sleep 3 | echo "Привет, мир"


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

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

В этом случае операция занимает 13 секунд, и для массива 3.46ГБ это означает 272МБ/сек. Это будет нашей верхней границей скорости чтения с диска.

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

cat *.pgn


Так как нас интересуют только строки с результатами, то мы можем сканировать файлы и выбирать только строки содержащие 'Result' посредством утилиты grep:
cat *.pgn | grep "Result"


[Непонятно, почему он сразу не запускал это как `grep Result *.pgn`?]

Это даст нам только строки содержащие Result. Теперь если хотите, то можете использовать команды sort и uniq для того, чтобы получить список уникальных элементов и для подсчета их количества.

cat *.pgn | grep "Result" | sort | uniq -c


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

Чтобы ускориться, мы можем убрать шаги sort | uniq из конвейера и заместить их вызовом AWK, который является замечательным инструментом для обработки данных событий.

cat *.pgn | grep "Result" | awk '{ split($0, a, "-"); res = substr(a[1], length(a[1]), 1); if (res == 1) white++; if (res == 0) black++; if (res == 2) draw++;} END { print white+black+draw, white, black, draw }'


Здесь мы возьмем всю запись, разделим по знаку дефис, и возьмем символ непосредственно ему предшествующий слева, если там будет 0, то выиграли черные, 1 – белые, и 2 – ничья. Заметим, что здесь $0 это встроенная переменная, представляющая всю запись.

Это уменьшает время исполнения до примерно 65 секунд, и т.к. мы обрабатываем примерно в 2 раза больше данных, то это ускорение примерно в 47 раз.

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

Параллелизация узкого места


Проблема недоиспользования дополнительных ядер может быть решена при помощи замечательной команды xargs, которая может запустить grep параллельно. Т.к. xargs ожидает вход в определенном формате, то будем использовать find с аргументом -print0 , для того чтобы имя передаваемого в xargs  файла заканчивалось нулем. Соответственно передадим -0,  чтобы xargs на своей стороне ожидал завершенные нулем строки. Опция -n управляет количеством строк передаваемым в один вызов, и -P  означает количество параллельных запускаемых команд. Важно заметить, что такой параллельный конвейер не гарантирует порядка доставки, но это не проблема, если вы рассчитывали на распределенную обработку изначально. Опция -F в grep означает, что мы ищем по простому совпадению строки, и не спрашиваем никаких замороченных регулярных выражений, что в теории может дать дополнительный выигрыш по скорости, что не было замечено в экспериментах
[это утверждение не совсем верно для нашего примера, gnu grep строит детерминированный конечный автомат, что в данном простом выражении будет эквивалентно простому совпадению строки. Единственное что выигрываем, это время компиляции регулярного выражения, чем в данном случае можно пренебречь].
find . -type f -name '*.pgn' -print0 | xargs -0 -n1 -P4 grep -F "Result" | gawk '{ split($0, a, "-"); res = substr(a[1], length(a[1]), 1); if (res == 1) white++; if (res == 0) black++; if (res == 2) draw++;} END { print NR, white, black, draw }'


В результате получаем время 38 секунд, что дало «привар» в 40% только за счет параллелизации запуска grep в нашем конвейере. Теперь мы примерно в 77 раз быстрее реализации на Hadoop.

Хотя мы и улучшили производительность «драматически» за счет параллелизации шага с grep, но мы можем вообще избавиться от него, заставляя сам awk искать нужные записи и работать только с теми, что содержат строку «Result».

find . -type f -name '*.pgn' -print0 | xargs -0 -n1 -P4 awk '/Result/ { split($0, a, "-"); res = substr(a[1], length(a[1]), 1); if (res == 1) white++; if (res == 0) black++; if (res == 2) draw++;} END { print white+black+draw, white, black, draw }'


Это может показаться правильным решением, но это выдает результаты о каждом файле в отдельности. Тогда как нам нужен общий, агрегированный результат. Корректная реализация агрегации концептуально очень похожа на MapReduce:
find . -type f -name '*.pgn' -print0 | xargs -0 -n4 -P4 awk '/Result/ { split($0, a, "-"); res = substr(a[1], length(a[1]), 1); if (res == 1) white++; if (res == 0) black++; if (res == 2) draw++ } END { print white+black+draw, white, black, draw }' | awk '{games += $1; white += $2; black += $3; draw += $4; } END { print games, white, black, draw }'


Добавляя второй вызов awk, в итоге мы получаем желаемую агрегатную информацию об играх.

Это еще более увеличивает финальную скорость, сокращая время запуска до 18 секунд, что 174 раз быстрее Hadoop реализации.

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

find . -type f -name '*.pgn' -print0 | xargs -0 -n4 -P4 mawk '/Result/ { split($0, a, "-"); res = substr(a[1], length(a[1]), 1); if (res == 1) white++; if (res == 0) black++; if (res == 2) draw++ } END { print white+black+draw, white, black, draw }' | mawk '{games += $1; white += $2; black += $3; draw += $4; } END { print games, white, black, draw }'


Таким образом, конвейер с применением перенаправления вывода между процессами find | xargs mawk | mawk позволил выполнить обработку за 12 секунд, что составляет 270 МБ/сек, и что более чем в 235 раз быстрее реализации на Hadoop.

Заключение


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

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.

О пользе железа, или знакомство с GPS/ГЛОНАСС трекерами

Основное направление деятельности нашей компании – разработка ПО для спутникового мониторинга. В связи с чем мы вплотную сотрудничаем с производителями GSP/ГЛОНАСС оборудования. Поэтому мы решили поделиться опытом и выпустить цикл тематических статей, посвященных GPS/ГЛОНАСС трекерам, в которых расскажем, как классифицируем навигационное оборудование, и подробно рассмотрим технические особенности устройств, наиболее популярных среди пользователей нашего продукта.

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

image

Что такое GPS трекер?


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

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

Софт трекер – программное обеспечение, позволяющее использовать смартфоны, планшеты и другие приборы в качестве аппаратной платформы GPS трекера. Приложение получает доступ к штатному GPS приемнику, а обмен сообщениями с навигационным сервером осуществляется посредством GSM и Wi-Fi модуля портативного устройства. Примером софт трекера может служить мобильное приложение GPS Tag.

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

  • Приемник сигнала навигационных GPS/ГЛОНАСС спутников, с помощью которых устройство определяет свое местоположение. При недоступности сигнала от спутников местоположение может определяться путем анализа сигнала стационарных базовых станций GSM, однако точность подобного метода значительно снижается.

  • GSM модуль для обмена данными с навигационным сервером. После того, как устройство определило свою локацию, оно передает собранную информацию на базовую станцию сотовой связи посредством GPRS пакета или SMS сообщения. Затем сигнал передается на сервер обработки информации. Подключившись со своего ПК или мобильного устройства к серверу обработки данных, оператор может узнать точное местоположение объекта, данные о его техническом состоянии и информацию о маршруте движения. В зависимости от конфигурации установленного оборудования, оператор системы может отправить объекту текстовое сообщение или команды управления.

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

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

Типы трекеров по назначению


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

Трекеры для мониторинга транспорта


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

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

Для улучшения приема сигнала GPS и GSM могут использоваться внешние антенны.

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

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

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

Недостатки: трудоемкость монтажа и ремонта.

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

Примером популярного GPS/ГЛОНАСС трекера может служить GALILEOSKY 3G v 5.0.

image

  • OBD II трекеры.

Разъем OBD II (On-Board diagnostic) разработан для диагностики систем автомобиля и является универсальным для большинства типов современных авто. С данного разъема можно считать множество параметров с бортового компьютера и штатных датчиков транспортного средства.

Производители GPS трекеров не упустили возможность использовать эти преимущества и разработали устройства, подключаемые напрямую к разъему OBD II.

Достоинства: такие трекеры имеют множество функций и легко монтируются в автомобиль.

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

Приблизительная стоимость OBDII трекеров составляет 1500-9000 руб.

Примером OBD II трекера может служить ATrack AX7.

image

  • Трекеры, работающие от прикуривателя.

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

Достоинства: данный тип трекеров имеет компактные размеры и легко устанавливается в авто.

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

Примером GPS/ГЛОНАСС трекера с питанием от прикуривателя может служить Gosafe G717.

image

Приблизительная стоимость трекеров с питанием от прикуривателя составляет 6000-30000 руб.

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

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

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

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

Охранные службы. GPS/ГЛОНАСС трекер подключается к бортовой системе сигнализации автомобиля. Владелец или оператор охранной службы может отследить местоположение угнанного автомобиля и при необходимости отправить сигнал на остановку двигателя.

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

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

Персональные GPS трекеры


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

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

В каких сферах используются персональные трекеры?

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

Наблюдение за маленькими детьми, престарелыми родственниками. Примером компактных персональных GPS трекеров для мониторинга малышей и пожилых людей является SkyPatrol TT8850.

image

Наблюдение за домашними животными. Отличаются наличием влагозащищенного ударопрочного корпуса, источником питания повышенной емкости для длительной автономной работы. Такие трекеры часто используются для контроля за выпасом племенного скота на обширных территориях. Также устройства данного типа часто интегрируются в ошейники для охотничьих собак или домашних питомцев для удобства их поиска. Примером трекера-ошейника может служить Rilla G19. Стоимость персональных трекеров стартует от 1000 рублей.

image

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

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

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


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

Для контроля за важными почтовыми отправлениями могут использоваться компактные GPS маяки.

Мониторинг стационарных объектов


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

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

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.