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

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

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

 

 -Статистика

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

Habrahabr/New








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

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

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

Vivaldi 1.10 — маленькие радости широких возможностей

Четверг, 15 Июня 2017 г. 22:09 + в цитатник
image

Всем привет!

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

  • Добавление собственных эскизов в ячейки Экспресс-панели
  • Управление количеством столбцов ячеек Экспресс-панели
  • Закрепление средств разработки в окне браузера
  • Возможность управления стартовой страницей с помощью расширений
  • Сортировка списка загруженных файлов

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

Расширяем возможности Экспресс-панели


Экспресс-панель — это первое, что видит пользователь, запустив только что установленный браузер. Также в большинстве случаев именно эта панель является стартовой при ежедневной работе в Vivaldi. Поэтому логично, что мы уделяем улучшению этого элемента большое значение. Сегодня Экспресс-панель становится ещё чуть более удобной в работе благодаря новым возможностям по управлению ячейками закладок. В частности, мы добавили возможность устанавливать собственные эскизы для ячеек:

image

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

image

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

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

image

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

Закреплённые средства разработчика


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

image

При этом у вас есть возможность как выбрать место расположения инструментов (слева-внизу-справа), так и открепить их от окна браузера:

image

Другие мелочи


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

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

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

Загрузить новую версию браузера можно с официальной страницы загрузки.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331022/


Метки:  

Microsoft Azure Media Services — обзор основных возможностей платформы

Четверг, 15 Июня 2017 г. 21:13 + в цитатник
Прежде, чем попасть на сайт или проигрыватель смартфона, потоковое видео и онлайн-трансляции проходят несколько важных этапов обработки. Они нужны для того, чтобы картинка отображалась корректно и работала на максимальном количестве устройств и платформ. Эта обработка происходит при помощи специальных сервисов.





Azure Media Services — это сервисы технологии платформы Microsoft Media Platform, которые доступны из облака в формате SaaS (Software as Service) и PaaS (Platform as Service). Среди преимуществ работы с ними — возможность масштабирования, а также использования их совместно с собственными средствами кодирования, вещания и распространения контента. Также платформа позволяет загружать контент, проводить его перекодирование и конвертацию, организовывать прямые трансляции, вещание по расписанию и просто встраивать видео на сайты. Также пользователям доступана защита контента, работа с рекламой и аналитикой.

Эти и многие другие полезные функции сделали Azure Media Services очень популярной платформой. Например, с ее помощью велась трансляция соревнований Олимпийских игр 2016 года в Рио-де-Жанейро, сервисами Azure Media пользуется один из самых титулованных футбольных клубов мира Реал (Мадрид).

Интерфейс

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



Работа с мультимедиа

Перед тем, как видео появляется на том или ином сайте, оно проходит несколько этапов подготовки. Начинается все с загрузки исходного видео на сервер, его обработки, конвертации, и т. д. Для этих и многих других действий в Azure Media Services есть специальные инструменты REST API, .NET и Java SDK. Также присутствует ряд клиентских инструментов, разработанные специалистами Microsoft. С помощью этого обширного инструментария можно настроить и контролировать каждый аспект подготовки и воспроизведения видео, например, перемотку или добавление рекламы. Расскажем вкратце о всех этих этапах по порядку.

Загрузка

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

Ресурсы также могут быть отправлены в облако при помощи инструментов .NET и REST. В этом случае данные могут быть сразу зашифрованы, также будут доступны некоторые другие возможности. Подробности и примеры вы можете найти на сайте Microsoft.

При необходимости отправки множества больших файлов также можно воспользоваться функцией массовой загрузки из библиотеки .NET. Альтернатива —сторонние инструменты, например, Aspera, — программное обеспечение для высокоскоростной передачи файлов. Это средство должно быть куплено отдельно в Azure Marketplace. После регистрации продукта, установки модуля для браузера и клиента, а также настройки инструмента вы сможете без проблем отправлять большие объемы файлов в облако. Подробнее об Aspera читайте здесь.

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

Кодировка

У Windows Azure Media Services есть инструменты для перекодировки файлов в разнообразные мультимедийные форматы. Система поддерживает FLV, MXF, GXF, MPEG2, 3GP, Windows Media Video, Microsoft Digital Video Recording, AVI, MP4, Matroska/WebM, WAVE/WAV и QuickTime (все поддерживаемые видео- и аудиокодеки смотрите по ссылке).

Одним из главных преимуществ платформы Azure Media Services перед конкурентами является то, что она использует динамичную упаковку контента. Каждый файл в облаке хранится в одном варианте и перекодируется уже непосредственно перед доставкой на конечное устройство, вместо того, чтобы хранить по экземпляру файла для каждой платформы (например, для Android, iOS, и XboX). Это позволяет существенно экономить место в облаке и, как следствие, средства.

Скорость обработки мультимедиа можно регулировать вручную. Таким задачам присваиваются значения — S1, S2 и S3. Например, если использовать тип зарезервированной единицы S2, задание по кодированию выполняется быстрее по сравнению заданием, для которого выбран тип S1. В разных типах учетных записей количество параллельных задач обработки варьируется. Например, если в учетной записи имеется пять зарезервированных единиц, то можно запустить пять одновременно выполняемых задач. Остальные задания, если они были поставлены, будут дожидаться своей очереди и как только один из потоков будет обработан, задание из очереди станет выполняться автоматически. Более подробную информацию о кодировании вы найдете на сайте Microsoft.

Проигрыватель

Еще одна отличительная особенность Azure Media Services — это универсальность фирменного проигрывателя. Сервис поддерживает практически все современные устройства и платформы — Mac и ПК, iOS и Android, HTML5, Flash, Silverlight, XboX и т. д. Для работы с разнообразными встраиваемыми устройствами, которые имеют собственный фреймворк разработки, можно лицензировать пакеты для портирования Smooth Streaming Client Porting Kit и Microsoft PlayReady Device Porting Kit. Примеры таких устройств — медиаплееры, Smart TV и т. д.



Проигрыватель в своей работе также использует отраслевые стандарты Media Source Extensions (MSE) и Encrypted Media Extensions (EME), которые лучше всего подходят для адаптивной потоковой передачи. Если же они недоступны на устройстве или в браузере, проигрыватель переходит на Flash и Silverlight. Эти опции задаются в расширенных настройках проигрывателя путем выбора соответствующих пунктов из выпадающего списка. Причем для трансляции можно задать как описанную выше возможность автовыбора, так и использование конкретной технологии.



Службы мультимедиа Microsoft Azure поддерживают форматы потоковой передачи DASH, Smooth Streaming и HLS для воспроизведения содержимого. Azure Media Player учитывает эти различные форматы и автоматически воспроизводит наиболее подходящую ссылку в зависимости от возможностей платформы или браузера. Но можно также задать работу с конкретным форматом. Какую-бы технологию и платформу не выбрал бы разработчик, он получает единый интерфейс JavaScript для доступа к интерфейсам API. Все это позволяет проигрывателю Azure обслуживать содержимое для воспроизведения на самом широком спектре устройств и в различных браузерах. Говоря проще, не стоит волноваться, что видео не будет работать или отображаться некорректно на каком-то типе устройств.



Если транслятор хочет зашифровать свои ресурсы от несанкционированного доступа, сделать это можно при помощи технологий PlayReady, Widevine или 128-битного алгоритма шифрования AES. Все что для этого нужно сделать — это поставить галочку напротив соответствующего пункта в настройках плеера и указать необходимый маркер. Больше вы можете узнать на сайте Microsoft.


Схема кодирования 128-битным алгоритмом шифрования AES и другими средствами Azure Media Services

Для применения всех выбранных опций, значений и параметров выставлены нажать кнопку Update Player. Затем можно получить код ролика со всеми заданными параметрами для вставки на ресурсы.



Фирменные коннекторы

В службе Azure Media Services доступны удобный и простой в использовании коннектор для интеграции со сторонними сервисами. Он доступен для таких сервисов и платформ, как Dropbox, Facebook, Twitter и другие. Коннектор позволяет клиентам подготавливать и размещать видео и мультимедийные материалы в своих приложениях и сервисах не беспокоясь о сложностях, связанных с подготовкой и размещением видео на разнообразных устройствах. Вот несколько примеров того, где он может использоваться:

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



Выводы

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

https://habrahabr.ru/post/331020/


Метки:  

Межпланетная файловая система — больше нет необходимости копировать в сеть

Четверг, 15 Июня 2017 г. 20:15 + в цитатник

InterPlanetary File System — это новая децентрализованная сеть обмена файлами (HTTP-сервер, Content Delivery Network). О ней я рассказывал в статье "Межпланетная файловая система IPFS".


Всем хороша идея IPFS но вот только был один недостаток у неё. Данные загружаемые в сеть копировались в хранилище блоков удваивая занимаемое ими место. Более того файл резался на блоки которые мало пригодны для повторного использования.


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


Также появился новый тип идентификаторов. Его мы тоже разберём.


image


--nocopy


Это опция заставляет IPFS использовать как источник блоков исходные файлы. Тем самым файлы не копируются в хранилище блоков и не занимают в 2 раза больше места.


Для использования этой опции выполним следующие действия:


  1. необходимо включить Filestore


    ipfs config --json Experimental.FilestoreEnabled true

  2. в каталог ".ipfs"(он в каталоге пользователя) нужно сделать ссылку на каталог или файл который надо загрузить в сеть


    Файл можно связать hardlink'ом:


    fsutil hardlink ".ipfs\[имя файла]" "[путь к файлу]\[имя файла]"

    или


    mklink /h ".ipfs\[имя файла]" "[путь к файлу]\[имя файла]"

    Каталог можно связать символьной ссылкой:


    linkd ".ipfs\[имя каталога]" "[путь к каталогу]\[имя каталога]"

    или


    mklink /j ".ipfs\[имя каталога]" "[путь к каталогу]\[имя каталога]"

  3. И теперь добавляем


    ipfs add -w --nocopy ".ipfs\[имя каталога или файла]"

    Ключ -w оборачивает цель в каталог тем самым сохраняя её имя.


    Результатом будут идентификаторы CIDv1 и CIDv0 (мультихеш)



WebSeed


Теперь в магнит или торрент можно будет добавить WebSeed.


Для файла:


http://127.0.0.1:8080/ipfs/[идентификатор]/[имя файла]
http://ipfs.io/ipfs/[идентификатор]/[имя файла]

Для каталога:


http://127.0.0.1:8080/ipfs/[идентификатор]/
http://ipfs.io/ipfs/[идентификатор]/

Пример магнита c WebSeed


magnet:?xt=urn:btih:2F825A27112B0E5C89D20B656045920F1C10830C28&ws=https://ipfs.io/ipfs/QmPbs8syAxac39bcNuMLpHXnqjKUguqakCM8LN8sZVPD9R/Magnet-ссылка.txt

Идентификатор контента (CID)


В связи с этими изменениями в IPFS появились RAW блоки. Ключ --nocopy автоматически включает использование RAW блоков. Но можно включить этот режим и ключом --raw-leaves. В связи с этим появился новый CID (Content IDentifier) или по русски "идентификатор контента".


Старый идентификатор


Называется CIDv0 и обычно имеет постоянный префикс "Qm".


CIDv0 это просто мультихеш в Base58


[varint ID хеша][varint длинна хеша][хеш]

Пример CIDv0: QmPbs8syAxac39bcNuMLpHXnqjKUguqakCM8LN8sZVPD9R


Новый идентификатор


У RAW блоков свой CID. Его можно отличить по постоянному префиксу "zb2rh".


CIDv1 содержит в себе больше информации.


[префикс основания][varint версия CID][varint тип контента][varint ID хеша][varint длинна хеша][хеш]

Пример CIDv1: zb2rhe143L6sgu2Nba4TZgFMdPidGMA6hmWhK9wLUoVGWYsR7


Разберём его на части


  1. zbase58 Bitcoin [префикс основания]


    base58: b2rhe143L6sgu2Nba4TZgFMdPidGMA6hmWhK9wLUoVGWYsR7
    переводим в
    HEX: 01 55 12 20 6D542257CBD1BE7FD0AE8914F42066BCBF1E79487EF67B959A86DBEE4670B386


  2. 01 — v1 [varint версия CID]
  3. 55 — raw binary [varint тип контента]
  4. 12sha2-256 [varint ID хеша]
  5. 20 — 32 байта [varint длинна хеша]
  6. 6D542257CBD1BE7FD0AE8914F42066BCBF1E79487EF67B959A86DBEE4670B386 — sha2-256 digest [хеш]

Заключение


Опция --nocopy очень помогает когда вы хотите поделиться с миром например дампом Википедии. Или другими полезными но очень большими по объёму массивами информации.


Ссылки


Ссылки в Windows, символьные и не только
ipfs command reference
List of experimental IPFS features


Другие части


  1. Межпланетная файловая система IPFS
  2. Публикуем сайт в межпланетной файловой системе IPFS
  3. Хостим сайт в межпланетной файловой системе IPFS под Windows
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331010/


Метки:  

[Из песочницы] Система управления проектами Redmine + Mercurial на Ubuntu 16.04

Четверг, 15 Июня 2017 г. 19:18 + в цитатник
По мере увеличения числа вовлечённых в проект людей возникает необходимость как-то более эффективно организовывать и управлять их деятельностью. На начальном этапе для этой цели использовались Google-таблицы, но их возможности ограничены, и появилось желание перейти на новый уровень. Изучение доступных систем управления проектами показало, что из систем с открытым кодом Redmine наиболее продвинутая и по некоторым показателям обгоняет даже проприетарные системы.

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

Компоненты


Система управления проектами Redmine


-> Официальный сайт

Основные возможности:

  • ведение нескольких проектов
  • система отслеживания событий (bug, feature)
  • разные роли пользователей (менеджер, разработчик, репортер) по каждому проекту
  • поддержка новостей, документов, файлов, wiki, форумов для каждого проекта
  • интеграция с системами управления версиями (SVN, Git, Mercurial)
  • уведомления о событиях по электронной почте
  • возможность учёта времени работы

Система контроля версий Mercurial


-> Официальный сайт

Кросс-платформенная распределённая система управления версиями.

Также понадобится


Web-сервер и система управления базами данных. Использованы Mysql и Apache.

Установка


Инструкция составлена на основе полезной, но сильно устаревшей инструкции
HowTo Install Redmine 1.2.x with Mercurial and Subversion on Ubuntu Server 10.04.

Также использовалась официальная инструкция по установке
Redmine Installation Guide.

Предполагаем что у нас уже есть сервер с предустановленным на нём Ubuntu Server 16.04. Дальнейшие инструкции описывают установку системы управления и вспомогательного ПО.

Итак, начнём. Сначала устанавливаем LAMP server:

$ sudo tasksel install lamp-server

Во время установки понадобится ввести пароль root-пользователя базы данных MySQL (не путать с паролем root операционной системы).

Создаём базу данных MySQL и пользователя redmine для работы с ней. Вместо [password] вставляем желаемый пароль пользователя.

$ mysql -u root -p
(вводим пароль root базы данных MySQL)
> create database redmine character set utf8;
> create user 'redmine'@'localhost' identified by '[password]';
> grant all privileges on redmine.* to 'redmine'@'localhost';
> exit

Скачиваем Redmine со страницы www.redmine.org/projects/redmine/wiki/Download или командой

$ wget http://www.redmine.org/releases/redmine-3.3.3.tar.gz

Распаковываем Redmine в каталог /usr/share/redmine. Находим подкаталог config и копируем config/database.yml.example в config/database.yml. После этого редактируем файл, для того чтобы установить «production» режим базы данных:

$ sudo cp /usr/share/redmine/config/database.yml.example /usr/share/redmine/config/database.yml
$ sudo nano /usr/share/redmine/config/database.yml

Вводим текст и сохраняем файл (ctrl+x):

production:
  adapter: mysql2
  database: redmine
  host: localhost
  username: redmine
  password: "[password]"
  encoding: utf8

Устанавливаем необходимые пакеты:

$ sudo apt install ruby ruby-dev build-essential libmysqlclient-dev

Устанавливаем Bundler:

$ gem install bundler

Теперь можно установить gems, необходимые для Redmine:

$ cd /usr/share/redmine
$ bundle install --without development test rmagick

Создаём случайный ключа, который Rails будет использовать для шифрования данных в cookie:

$ cd /usr/share/redmine
$ bundle exec rake generate_secret_token

Дальше создаём структуру базы данных (выполняем в /usr/share/redmine):

$ RAILS_ENV=production bundle exec rake db:migrate
$ RAILS_ENV=production bundle exec rake redmine:load_default_data

Устанавливаем необходимые права доступа:

$ cd /usr/share/redmine
$ sudo chown -R www-data:www-data files log tmp public/plugin_assets
$ sudo chmod -R 755 files log tmp public/plugin_assets

При желании можно протестировать установку Redmine с помощью веб-сервера WEBrick:

$ sudo -u www-data bundle exec rails server webrick -e production

После запуска WEBrick стартовая страница Redmine должна быть доступна в браузере по адресу http://localhost:3000/

Интеграция с Apache


Установить Passenger:

$ sudo apt-get install libapache2-mod-passenger

Добавить символьную ссылку на public каталог Redmine:

$ sudo ln -s /usr/share/redmine/public /var/www/redmine

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

$ sudo nano /etc/apache2/mods-available/passenger.conf

Нужно добавить следующую строчку и сохранить (ctrl+x):

PassengerDefaultUser www-data

В итоге файл должен выглядеть так:


  PassengerRoot /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini
  PassengerDefaultRuby /usr/bin/ruby
  PassengerDefaultUser www-data

Далее создать конфигурационный файл redmine.conf для apache:

$ sudo nano /etc/apache2/sites-available/redmine.conf 

Добавить следующий текст и сохранить (ctrl+x):


        ServerAdmin webmaster@localhost
        DocumentRoot /var/www
        ServerName myservername

        RewriteEngine on
        RewriteRule   ^/$  /redmine  [R]

        var/www/redmine>
                RailsBaseURI /redmine
                PassengerResolveSymlinksInDocumentRoot on
        

        ErrorLog /var/log/apache2/error.log

        # Possible values include: debug, info, notice, warn, error, crit,
        # alert, emerg.
        LogLevel warn

        CustomLog /var/log/apache2/access.log combined

Подключить модули Passenger и Rewite:

$ sudo a2enmod passenger
$ sudo a2enmod rewrite

Отключить default вебсайт и подключить redmine:

$ sudo a2dissite 000-default
$ sudo a2ensite redmine

Установить права доступа на /tmp/cache Redmine:

sudo chmod 777 /usr/share/redmine/tmp/cache

Перезапустить Apache:

$ sudo service apache2 reload

Теперь можно открыть любимый браузер и зайти на http://[my site or ip]/redmine или просто http://[my site or ip]. Должна появиться стартовая страничка системы Redmine.

Установка Mercurial


Необходимо установить пакеты:

$ sudo apt-get install mercurial libapache2-mod-perl2 libapache-dbi-perl libdbd-mysql-perl

Создать директорию, в которой будут храниться репозитории проектов:

$ sudo mkdir -p /var/hg/

Теперь мы хотим сделать репозитории доступными по http протоколу. Для этого необходимо создать cgi-скрипт:

$ sudo nano /var/hg/hgwebdir.cgi

Добавить следующий текст и сохранить:

#!/usr/bin/python
from mercurial import demandimport; demandimport.enable()
from mercurial.hgweb.hgwebdir_mod import hgwebdir
import mercurial.hgweb.wsgicgi as wsgicgi
application = hgwebdir('hgweb.config')
wsgicgi.launch(application)

Теперь нужно создать файл hgweb.config:

$ sudo nano /var/hg/hgweb.config

Добавить следующее содержимое и сохранить:

[paths]
/=/var/hg/**
[web]
allow_push = *
push_ssl = false
allowbz2 = yes
allowgz = yes
allowzip = yes

Установить разрешения для файлов:

$ sudo chown -R www-data:www-data /var/hg/*
$ sudo chmod gu+x /var/hg/hgwebdir.cgi

Теперь надо создать conf файл для Apache:

$ sudo nano /etc/apache2/conf-available/hg.conf

Добавить следующее содержимое и сохранить:

PerlLoadModule Apache2::Redmine
 ScriptAliasMatch ^/hg/(.*)  /var/hg/hgwebdir.cgi/$1

    var/hg>
      Options +ExecCGI
    

    hg>
        AuthType Basic
        AuthName "Mercurial" 
        Require valid-user
        AuthUserFile /dev/null

        #Redmine auth
        PerlAccessHandler Apache::Authn::Redmine::access_handler
        PerlAuthenHandler Apache::Authn::Redmine::authen_handler
        RedmineDSN "DBI:mysql:database=redmine;host=127.0.0.1" 
        RedmineDbUser "redmine" 
        RedmineDbPass "[password]" 
    

Ещё необходимо создать ссылки:

$ sudo ln -s /etc/apache2/conf-available/hg.conf /etc/apache2/conf-enabled/
$ sudo ln -s /usr/share/redmine/extra/svn/Redmine.pm /usr/lib/x86_64-linux-gnu/perl5/5.22/Apache2/

Включить CGI модуль и перезапустить Apache:

$ sudo a2enmod cgi
$ sudo service apache2 reload

Репозитории будут доступны по адресам http://[my site or ip]/hg/*. Например, для проекта project адрес будет таким http://[my site or ip]/hg/project. Если у проекта project будет подпроект subproject1, то его репозиторий будет доступен по адресу http://[my site or ip]/hg/project/subproject1.

Чтобы клонировать репозиторий надо будет выполнить:

$ hg clone http://[my site or ip]/hg/project

Если клонируемый проект не публичный (устанавливается в настройках проекта через веб-интерфейс системы Redmine), то потребуется ввести имя пользователя и пароль.

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

Для того чтобы всё корректно работало, каталоги репозиториев в /var/hg/ должны совпадать с Identifier репозиториев, которые задаются в настройках репозитория Redmine при его создании (через веб-интерфейс). Также при создании реопзитория в Redmine необходимо указать путь к нему, например /var/hg/project. Репозитории в /var/hg необходимо создать вручную для каждого проекта и инициализировать командой (hg init).

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

$ sudo chown -R www-data:www-data /var/hg/[repository name]

В принципе, есть возможность автоматизировать создание репозиториев. Информация об этом есть в руководстве по ссылке HowTo Install Redmine 1.2.x with Mercurial and Subversion on Ubuntu Server 10.04

Уведомления о фиксации изменений по email


Redmine поддерживает уведомления о разных событиях (изменениях в жизни баг/фич и т.п.). Для того чтобы пользоваться этим функционалом достаточно настроить способ отправки email-сообщений. Делается это в файле /usr/share/redmine/config/configuration.yml В файле имеются шаблоны для разных конфигураций. Нужно разкомментировать и отредактировать нужный.

Например, так:

  email_delivery:
    delivery_method: :smtp
    smtp_settings:
      address: "10.11.12.13"
      port: 25
      authentification: :none
      enable_starttls_auto: false
      openssl_verify_mode: 'none'

Обратите внимание, что каждая секция в файле configuration.yml сдвинута на два пробела. Это важно.

Базовые уведомления должны быть доступны после указания способа рассылки электронных писем. Однако для уведомлений об изменениях в репозитории необходимо использовать внешний плагин. Скачать его можно с сайта github.com/lpirl/redmine_diff_email.

Установим этот плагин. Для этого скопируем содержимое плагина в каталог /usr/share/redmine/plugins/redmine_diff_email. В соответствии с инструкцией по установке плагина изменяем файл /usr/share/redmine/app/views/repositories/_form.html.erb:

--- OLD
 +++ NEW
 @@ -23,6 +23,7 @@
    
    
  
 +
  
  


Оригинальный плагин работает с устаревшей версией redmine. Для redmine-3.3 нужно внести изменения в файл
/usr/share/redmine/plugins/redmine_diff_email/db/migrate/002_add_repositories_is_diff_email_attached.rb. Содержимое файла должно быть таким:

class AddRepositoriesIsDiffEmailAttached < ActiveRecord::Migration
  def self.up
    add_column :repositories, :is_diff_email_attached, :boolean, :default => false, :null => false
    Repository.update_all(["is_diff_email_attached = ?", true])
    Repository.update_all(["is_diff_email = ?", true])
  end

  def self.down
    remove_column :repositories, :is_diff_email_attached
  end
end

После этого в каталоге /usr/share/redmine выполнить команду для обновления базы данных:

bundle exec rake redmine:plugins:migrate RAILS_ENV=production

Перезапускаем Redmine:

$ sudo service apache2 reload

Если плагин установлен правильно, то в списке плагинов Administration -> Plugins появится Redmine Diff Email Plugin, а также в веб-интерфейсе Redmine SomeProject -> «Settings» Tab -> «Repositories» Tab -> «Edit» появятся настройки уведомлений.

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

* В web-интерфейсе Redmine в меню Administration выбрать Settings
* Перейти на вкладку Repositories
* Включить 'Enable WS for repository management'
* Кликнуть на ссылку 'Generate a key'
* Сохранить изменения кнопкой 'Save'

Далее создаём скрипт:

$ sudo nano /var/hg/fetch_changes

Добавляем следующий текст и сохраняем: (необходимо заменить [your API key] сгенерированным в API-ключом)

#!/bin/sh
curl "http://localhost/redmine/sys/fetch_changesets?key=[your API key]"  > /dev/null 2>&1

Устанавливаем права доступа для созданного файла:

$ sudo chown www-data:www-data /var/hg/fetch_changes
$ sudo chmod ug+x /var/hg/fetch_changes

Остаётся добавить в /var/hg/hgweb.config секцию [hooks], чтобы скрипт fetch_changes выполнялся после каждого коммита:

[hooks]
changegroup = /var/hg/fetch_changes

Теперь при изменениях в репозитории Redmine будет автоматически отсылать уведомления участникам проекта.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331006/


Метки:  

Replication Framework • глубинное копирование и обобщённое сравнение связных графов объектов

Четверг, 15 Июня 2017 г. 19:08 + в цитатник
Приветствую, читатель!

Хочу познакомить тебя с молодой, но многообещающей библиотекой Replication Framework для платформы .NET (возможно, при наличии достаточного интереса к теме в дальнейшем будет также реализована Java-версия). Библиотека является портабельной (portable) и может быть использована в любом проекте под Microsoft .NET или Mono.

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

image


Прежде всего определимся с терминологией и основными сущностями

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

Снимки можно делать с различных ракурсов, то есть по-разному интерпретировать состояние объектов, к примеру, собирать значения абсолютно всех свойств и полей экземпляров или же только публичных, но зачастую лишь тех членов, что отмечены специальным атрибутом DataMember. То, каким образом делать снимок, зависит от ReplicationProfile [профиля репликации] и в особенности от его внутреннего списка MemberProviders [провайдеров членов].

* По умолчанию, если класс имеет атрибуты DataContract или CollectionDataContract, то на снимок транслируются лишь члены с атрибутом DataMember, в ином же случае на снимок попадают все поля и свойства класса как публичные, так и нет.

Небольшой пример использования профилей репликации
var snapshot0 = instance0.CreateSnapshot(); /* use default ReplicationProfile */
var customReplicationProfile = new ReplicationProfile
{
    MemberProviders = new List
    {
        //new MyCustomMemberProvider(), /* you may override and customize MemberProvider class! */
        new CoreMemberProviderForKeyValuePair(),
        //new CoreMemberProvider(BindingFlags.Public | BindingFlags.Instance, Member.CanReadWrite),
        new ContractMemberProvider(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, Member.CanReadWrite)
    }
};

var snapshot1 = instance1.CreateSnapshot(customReplicationProfile );
Snapshot.DefaultReplicationProfile = customReplicationProfile;


В общих чертах снимок представляет собой json-подобную структуру данных, в которой сложные составные объекты разобраны на примитивы и преобразованы в словари, где ключом является имя члена (свойства или поля), а значением — соответствующий примитив (string, int, DateTime, etc.). Все коллекции, включая массивы, — это особый род объектов, у которых помимо обычных свойств есть ещё одно неявное для операции перечисления (foreach), а его значение является эквивалентом json-массива.

Реконструкция (Reconstruction) — операция перевода графа объектов в исходное состояние на основе снимка и уже имеющихся закэшированных экземпляров объектов. Обычно в процессе исполнения программы объекты и состоящие из них графы видоизменяются, то есть мутируют, но иногда полезно иметь возможность вернуть [откатить] граф и входящие в него объекты к какому-то определённому состоянию зафиксированному ранее.

Реконструкция выполняется следующим образом
var cache = new Dictionary();
var snapshot0 = graph0.CreateSnapshot(cache);
/* modify 'graph0' by any way */

var graphX = snapshot0.ReconstructGraph(cache);
/* graphX is the same reference that graph0, all items of the graph reverted to the previous state */


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

Репликация (Replication) — операция глубинного копирования графа объектов на основе снимка в результате которой создаётся новая копия графа изолированная от исходной.

Репликация выполняется следующим образом
var snapshot0 = graph0.CreateSnapshot(cache);
/* modify 'graph0' by any way */

var graph1 = snapshot0.ReplicateGraph(cache);
/* graph1 is a deep copy of the source graph0 */


* Разница между поверхностным и глубинным копированием
Копирование бывает двух видов — поверхностное и глубинное. Пускай даны объекты А и Б, причём А содержит ссылку на Б (граф А=>Б). При поверхностном копировании объекта А будет создан объект А', который также будет ссылаться на Б, то есть в итоге получится два графа А=>Б и А'=>Б. У них будет общая часть Б, поэтому при изменении объекта Б в первом графе, автоматически его состояние будет мутировать и во втором. Объекты же А и А' останутся независимы. Но наибольший интерес представляют графы с замкнутыми (циклическими) ссылками. Пускай А ссылается на Б и Б ссылается на А (А<=>Б), при поверхностном копировании объекта А в А' получим весьма необычный граф А'=>Б<=>А, то есть в итоговый граф попал изначальный объект, который подвергался клонированию. Глубинное же копирование предполагает клонирования всех объектов, входящих в граф. Для нашего случая А<=>Б преобразуется в А'<=>Б', в итоге оба графа совершенно изолированы друг от друга. В некоторых случаях достаточно поверхностного копирования, но далеко не всегда.


Сопоставление (Juxtaposition) — рекурсивная операция сравнения эталонного снимка объекта со снимком текущего образца.

Пример сопоставления двух снимков
var snapshot0 = instance0.CreateSnapshot(); /* etalon */
var snapshot1 = instance1.CreateSnapshot(); /* sample */
var juxtapositions = snapshot0.Juxtapose(snapshot1).ToList();
var differences = juxtapositions.Where(j=>j.State == Etalon.State.Different);


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

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

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

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


Перейдём к практике и обратим внимание на ключевые моменты

Код для генерации диагностического графа объектов
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace Art.Replication.Diagnostics
{
    [DataContract]
    public class Role
    {
        [DataMember] public string Name;           
        public string CodePhrase;
        [DataMember] public DateTime LastOnline = DateTime.Now;
            
        [DataMember] public Person Person;
    }
        
    public class Person
    {
        public string FirstName;
        public string LastName;
        public DateTime Birthday;
            
        public List Roles = new List();
    }

    public static class DiagnosticsGraph
    {
        public static Person Create()
        {
            var person0 = new Person
            {
                FirstName = "Keanu",
                LastName = "Reeves",
                Birthday = new DateTime(1964, 9 ,2)
            };
                   
            var roleA0 = new Role
            {
                Name = "Neo",
                CodePhrase = "The Matrix has you...",
                LastOnline = DateTime.Now,
                Person = person0
            };
            
            var roleB0 = new Role
            {
                Name = "Thomas Anderson",
                CodePhrase = "Follow the White Rabbit.",
                LastOnline = DateTime.Now,
                Person = person0
            };
            
            person0.Roles.Add(roleA0);
            person0.Roles.Add(roleB0);
            return person0;
        }
    }
}


Пространства имён, которые могут пригодиться
using Art;
using Art.Replication;
using Art.Replication.Replicators;
using Art.Replication.MemberProviders;
using Art.Serialization;
using Art.Serialization.Converters;


Создание снимка и его сериализация в строку без искажений с настройками по умолчанию
        public static void CreateAndSerializeSnapshot()
        {
            var person0 = DiagnosticsGraph.Create();
            var snapshot0 = person0.CreateSnapshot();
            string rawSnapsot0 = snapshot0.ToString();
            Console.WriteLine(rawSnapsot0);
            Console.ReadKey();
        }


Результат работы (хорошо видна полная структура снимка)
{
  #Id: 0,
  #Type: "Art.Replication.Diagnostics.Person, Art.Replication.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
  FirstName: "Keanu",
  LastName: "Reeves",
  Birthday: "1964-09-02T00:00:00.0000000+03:00",
  Roles: {
    #Id: 1,
    #Type: "System.Collections.Generic.List`1[[Art.Replication.Diagnostics.Role, Art.Replication.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
    #Set: [
      {
        #Id: 2,
        #Type: "Art.Replication.Diagnostics.Role, Art.Replication.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
        Name: "Neo",
        LastOnline: "2017-06-14T14:42:44.0000575+03:00",
        Person: {
          #Id: 0
        }
      },
      {
        #Id: 3,
        #Type: "Art.Replication.Diagnostics.Role, Art.Replication.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
        Name: "Thomas Anderson",
        LastOnline: "2017-06-14T14:42:44.0000575+03:00",
        Person: {
          #Id: 0
        }
      }
    ]
  }
}


• Класс Person имеет атрибут DataContract, поэтому все его поля с атрибутом DataMember, кроме CodePhrase, попали на снимок.
• Каждому объекту ставится в соответствие свой идентификатор #Id: 0, если ссылка на объект встречается в графе объектов более одного раза, то вместо повторной репликации подставляется следующая конструкция
        Person: {
          #Id: 0
        }

Это защищает от множественной репликации одного и того же экземпляра объекта, а в случаях циклических ссылок от захода в бесконечную рекурсию и Stack Overflow Exception (примечание: далеко не все сериализаторы справляются с подобными ситуациями).
• К каждому объекту добавляется полная информация о типе по ключу #Type.
• Некоторые примитивы также содержат информацию о типе
Birthday: "1964-09-02T00:00:00.0000000+03:00"
. Она необходима для восстановления (десериализации) снимка без искажений.
• Коллекция List сериализована как объект, но у неё есть свойство #Set, которое используется для перечисления вложенных объектов.

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

Сериализация объекта в классический json и его успешная десериализация
        public static void UseClassicalJsonSettings()
        {
            Snapshot.DefaultReplicationProfile.AttachId = false;
            Snapshot.DefaultReplicationProfile.AttachType = false;
            Snapshot.DefaultReplicationProfile.SimplifySets = true;
            Snapshot.DefaultReplicationProfile.SimplifyMaps = true;
            
            Snapshot.DefaultKeepProfile.SimplexConverter.AppendTypeInfo = false;
            Snapshot.DefaultKeepProfile.SimplexConverter.Converters
                .OfType().First().AppendSyffixes = false;   
        }

        public static void CreateAndSerializeSnapshotToClassicJsonStyle()
        {
            UseClassicalJsonSettings();
            
            var person0 = DiagnosticsGraph.Create();
            var snapshot0 = person0.CreateSnapshot();
            string rawSnapsot0 = snapshot0.ToString();
            Console.WriteLine(rawSnapsot0);
            var person0A = rawSnapsot0.ParseSnapshot().ReplicateGraph();
            Console.WriteLine(person0A.FirstName);
            Console.ReadKey();
        }


Классический json
{
  FirstName: "Keanu",
  LastName: "Reeves",
  Birthday: "1964-09-02T00:00:00.0000000+03:00",
  Roles: [
    {
      Name: "Neo",
      LastOnline: "2017-06-14T18:31:20.0000205+03:00",
      Person: {
        #Id: 0
      }
    },
    {
      Name: "Thomas Anderson",
      LastOnline: "2017-06-14T18:31:20.0000205+03:00",
      Person: {
        #Id: 0
      }
    }
  ]
}



О сохранении и восстановление состояния без искажений

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

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

    public class Distorsion
    {
        public object[] AnyObjects =
        {
            Guid.NewGuid(), Guid.NewGuid().ToString(),
            DateTime.Now, DateTime.Now.ToString("O"),
            123, 123L,
        };
    }


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

Основные сценарии использования

Репликация:
        public static void Replicate()
        {   
            var person0 = DiagnosticsGraph.Create();
            var snapshot0 = person0.CreateSnapshot();
            var person1 = snapshot0.ReplicateGraph();
            person1.Roles[1].Name = "Agent Smith";
            
            Console.WriteLine(person0.Roles[1].Name); // old graph value: Thomas Anderson
            Console.WriteLine(person1.Roles[1].Name); // new graph value: Agent Smith
            Console.ReadKey();
        }

Реконструкция:
        public static void Reconstract()
        {   
            var person0 = DiagnosticsGraph.Create();
            
            var cache = new Dictionary();
            var s = person0.CreateSnapshot(cache);
            
            Console.WriteLine(person0.Roles[1].Name); // old graph value: Thomas Anderson
            Console.WriteLine(person0.FirstName); // old graph value: Keanu
            person0.Roles[1].Name = "Agent Smith";
            person0.FirstName = "Zion";
            person0.Roles.RemoveAt(0);

            var person1 = (Person)s.ReconstructGraph(cache);
         
            Console.WriteLine(person0.Roles[1].Name); // old graph value: Thomas Anderson
            Console.WriteLine(person1.Roles[1].Name); // old graph value: Thomas Anderson
            Console.WriteLine(person0.FirstName); // old graph value: Keanu
            Console.WriteLine(person1.FirstName); // old graph value: Keanu
            Console.ReadKey(); // result: person0 & person1 is the same one reconstructed graph
        }

Сопоставление:
        public static void Justapose()
        {
            // set this settings for less details into output
            Snapshot.DefaultReplicationProfile.AttachId = false;
            Snapshot.DefaultReplicationProfile.AttachType = false;
            Snapshot.DefaultReplicationProfile.SimplifySets = true;
            Snapshot.DefaultReplicationProfile.SimplifyMaps = true;

            var person0 = DiagnosticsGraph.Create();
            var person1 = DiagnosticsGraph.Create();
            
            person0.Roles[1].Name = "Agent Smith";
            person0.FirstName = "Zion";
            
            var snapshot0 = person0.CreateSnapshot();
            var snapshot1 = person1.CreateSnapshot();
            
            var results = snapshot0.Juxtapose(snapshot1);

            foreach (var result in results)
            {
                Console.WriteLine(result);
            }

            Console.ReadKey();
        }

  [this.FirstName] {Zion} {Keanu}
  [this.LastName] {Reeves} {Reeves}
  [this.Birthday] {9/2/1964 12:00:00 AM} {9/2/1964 12:00:00 AM}
  [this.Roles[0].Name] {Neo} {Neo}
  [this.Roles[0].LastOnline] {6/14/2017 9:34:33 PM} {6/14/2017 9:34:33 PM}
  [this.Roles[0].Person.#Id] {0} {0}
  [this.Roles[1].Name] {Agent Smith} {Thomas Anderson}
  [this.Roles[1].LastOnline] {6/14/2017 9:34:33 PM} {6/14/2017 9:34:33 PM}
  [this.Roles[1].Person.#Id] {0} {0}


О производительности

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

Проигрыш:
— большее потребление памяти при сериализации объектов
— приблизительно в 2-2.5 раза меньшая скорость сериализации и последующей десериализации (зависит от настроек сериализации и рода тестов)

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

* Сравнение производительности производилось с BinaryFormatter, Newtonsoft.Json, а также с DataContractJsonSerializer.


Несколько слов в заключение о Replication Framework

Разработано решение в маленькой студии творческого программирования «Мэйклофт» [Makeloft]. Сейчас проект находится на стадии предварительной версии, но и её возможности впечатляют, хотя реализован только лишь базовый функционал. На разработку было потрачено очень много сил и времени, поэтому фреймворк является бесплатным лишь для учебных и некоммерческих проектов.

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

Скачать триал-версию можно с Nuget, она функциональна до сентября 2017 года. Проект с примерами кода из статьи можно скачать отсюда. Если библиотека оставит хорошее впечатление и ты решишь использовать её в каком-либо своём решении, то отправь, пожалуйста, запрос на получение бесплатной или платной лицензии по адресу makeman@tut.by. В запросе укажи название и род проекта, в котором планируется использование библиотеки.

Большое спасибо за внимание! Смело задавай вопросы и пиши пожелания!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330294/


[Из песочницы] Шифрование БД под управлением Firebird 3.0

Четверг, 15 Июня 2017 г. 18:53 + в цитатник
В современном информационном мире, информация играет значительную роль в жизни человека, общества и государства. Рост размера накапливаемых и обрабатываемых данных подымает вопросы об их хранении и обеспечении конфиденциальности. Уже существует немало технических решений и предложений для решения подобных задач. Среди них конечно же есть и системы управления базами данных (СУБД) которые поддерживают шифрование хранимых данных. Вот об одном из таких решений и пойдёт речь.

В апреле 2016 года вышла новая версия СУБД Firebird под номером 3. Из нововведений, среди прочего, появилось и немало механизмов защиты хранимых и передаваемых данных. Там есть и защита канала передачи данных, есть управление пользователями, а также есть шифрование самой БД, которое реализовано как прозрачное шифрование на уровне страниц данных. Реализуется это всё с помощью написания специальных расширений для Firebird. Можно конечно и самому разобраться и написать эти расширения, но почему бы не взять существующие. Тем более, что для написания, как минимум, нужно понимать в криптографии, иметь на вооружении знания какого-нибудь криптографического пакета и разобраться с новым С++ Firebird API.

Можно воспользоваться существующим готовым решением, скачав по этой ссылке для платформы Windows x64. В бесплатном ознакомительном варианте оно ограничено используемым алгоритмом шифрования — поддерживается Triple DES (3DES). Но для защиты своей БД этого вполне достаточно. В состав пакета входит 4 библиотеки и файлы с описанием интерфейсов.
Наименование файла Описание
CiKeyHolder.dll расширение Firebird – хранитель секретного ключа
CiDbCrypt.dll расширение Firebird – шифрования данных
CiFbEnc_x86.dll модуль активации ключа шифрования для платформы 32 bit.
CiFbEnc_x86-64.dll модуль активации ключа шифрования для платформы 64 bit.
ICiFbEncActivator.h описание интерфейса модуля для С++
CI.ICiFbEncActivator.pas описание интерфейса модуля для Delphi
Как пример, можно написать приложение, которое использует некий справочник (условно с конфиденциальными данными), который как обновляемый доступен по ссылке в глобальной сети. Его может скачать кто угодно, но при этом расшифровать и работать с ним сможет только наше приложение.

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

В качестве платформы возьмём ОС Windows 10 x64, а в качестве инструментов реализации задачи будем использовать IDE Embarcadero RAD Studio (на данный момент была доступна версия 10.2 Tokyo — 30-дневная пробная), поскольку она содержит удобные компоненты для работы с БД Firebird и применяется часто для разработки прикладных решений (пробную версию можно скачать по ссылке) и СУБД Firebird 3.0.2 x64, которую можно скачать по ссылке https://www.firebirdsql.org/en/firebird-3-0-2 для своей платформы. Так как нам в режиме разработки не нужен постоянно выполняемый сервер, да и перезапуск его может понадобиться неоднократно, то во время инсталляции выбираем способ запуска сервера как приложения.

Установка Firebird

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

После того, как был проинсталлирован пакет Firebird, необходимо произвести его настройку. Если же планируется использование Firebird в Embedded режиме, то такие же настройки нужно будет проделать в директории, где будет находиться используемая библиотека клиент fbclient.dll. Базовые файлы настроек находятся в корневой директории установленного Firebird — %ProgramW6432%\Firebird\Firebird_3_0.

Пропишем псевдоним (алиас) БД в файле databases.conf, добавив строку с указанием полного пути к будущей БД:

TESTDB = C:\TESTAPP\DB\TESTDB.FDB

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

KeyHolderPlugin = CiKeyHolder

Где искать плагины Firebird узнаёт также из конфигурационных файлов. Изменим файл plugins.conf, добавив следующие строки:

Plugin = CiKeyHolder {
	# Прописываем путь к плагину хранителю
 	Module = $(dir_plugins)/CiKeyHolder
}

Plugin = CiDbCrypt {
	# Прописываем путь к плагину шифрования
 	Module = $(dir_plugins)/CiDbCrypt
}

Директория под псевдонимом $(dir_plugins) подразумевает директорию plugins в корневом каталоге Firebird. Копируем туда ранее скачанные плагины CiKeyHolder.dll и CiDbCrypt.dll.

Поскольку мы предполагаем, что БД справочник у нас уже есть, создадим её средствами пакета Firebird, а именно, используя приложение isql. Жмём Win + R и выполняем следующую команду:

%ProgramW6432%\Firebird\Firebird_3_0\isql -q -user SYSDBA -password masterkey

Или же как-то по-другому запускаем isql, например, используя программу Far.

Следующие команды создадут БД TESTDB, псевдоним которой ранее был прописан в настройках и выведут информацию о созданной БД:

SQL> create database 'TESTDB';
SQL> create table t_1 (i1 int, s1 varchar(15), s2 varchar(15));
SQL> insert into t_1 values (1,'value 1-1','value 1-2');
SQL> insert into t_1 values (2,'value 2-1','value 2-2');
SQL> commit;
SQL> select * from t_1;

          I1 S1              S2
============ =============== ===============
           1 value 1-1       value 1-2
           2 value 2-1       value 2-2

SQL> show db;
Database: TESTDB
        Owner: SYSDBA
PAGE_SIZE 8192
Number of DB pages allocated = 196
Number of DB pages used = 184
Number of DB pages free = 12
Sweep interval = 20000
Forced Writes are ON
Transaction - oldest = 4
Transaction - oldest active = 5
Transaction - oldest snapshot = 5
Transaction - Next = 9
ODS = 12.0
Database not encrypted
Default Character set: NONE
SQL> quit;

Как видно из полученной информации по команде show db;, созданная БД в данный момент не зашифрована — Database not encrypted.

Теперь всё настроено и можно приступить к написанию приложения.

Создаём новый проект VCL Forms Application в IDE Embarcadero RAD Studio. Сохраняем проект, например, как testapp в директории C:\TESTAPP.

Для начала мы можем просто связать наше приложение с БД, обеспечив подключение и отключение к ней. Наша тестовая БД имеет одну таблицу, данные которой можно вывести при подключении. Из визуального оформления достаточно пары кнопок, таблица и навигатор с управлением данными. Для доступа к БД Firebird воспользуемся компонентами FireDAC. Далее представлена таблица с компонентами и их настраиваемыми параметрами.
Компоненты, помещаемые на форму тестового приложения
Класс компонента Наименование компонента Параметр Значение параметра
TFDPhysFBDriverLink FDPhysFBDriverLink1 - -
TFDConnection FDConnection1 DriverName FB
LoginPrompt False
Params\Database TESTDB
Params\UserName SYSDBA
Params\Password masterkey
Params\Protocol ipTCPIP
Params\Server localhost
TFDGUIxWaitCursor FDGUIxWaitCursor1 - -
TFDTransaction FDTransaction1 - -
TFDTable FDTable1 TableName T_1
TDataSource DataSource1 DataSet FDTable1
TDBGrid DBGrid1 DataSource DataSource1
TDBNavigator DBNavigator1 DataSource DataSource1
TButton Button1 Caption Connect
TButton Button2 Caption Disconnect
В итоге форма может выглядеть приблизительно так.

Форма в дизайнере среды разработки

Пропишем обработку нажатия на кнопку “Connect”:

FDConnection1->Connected = true;
FDTable1->Active = true;

И для “Disconnect”:

FDTable1->Active = false;
FDConnection1->Connected = false;

В случае использования Delphi всё аналогично:

FDConnection1.Connected := True;
FDTable1.Active := True;

. . . 

FDConnection1.Connected := False;
FDTable1.Active := False;

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

Win + X -> Командная строка (администратор)

И выполнить команду:

“%ProgramW6432%\Firebird\Firebird_3_0\firebird” –a

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

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

Успешное подключение к БД

Настало время подключить модуль активации ключа и управления шифрованием к нашему приложению. Для этого копируем файлы CiFbEnc_x86.dll и ICiFbEncActivator.h, CI.ICiFbEncActivator.pas в директорию C:\TESTAPP. Стоит обратить внимание, что поскольку по умолчанию в среде разработки создаётся проект под 32-bit Windows и триал версия среды разработки не позволяет изменить целевую платформу, приложению необходим именно файл-модуль CiFbEnc_x86.dll.

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

#include "ICiFbEncActivator.h"

Или файл Ci.ICiFbEncActivator.pas в случае Delphi.

uses
  . . . , CI.ICiFbEncActivator;

Добавим на форму ещё три кнопки: “Encrypt”, “Decrypt” и “Get State”.

Дополнительные кнопки для запуска операций модуля

Для начала рассмотрим обработчик кнопки “Get State”. Что следует из названия, это даст нам возможность получить текущее состояние БД.

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

// C++ Builder

// Загружаем модуль
std::unique_ptr mHandle(
	::LoadLibraryEx(L"C:\\TESTAPP\\CiFbEnc_x86.dll",0,LOAD_WITH_ALTERED_SEARCH_PATH),
	&::FreeLibrary);

if (!mHandle)
{
	MessageBox(
		NULL,
		L"Модуль CiFbEnc_x86.dll не найден или рядом с ним нет fbclient.dll",
		L"Загрузка модуля",
		MB_OK|MB_ICONERROR);

	return;
}

// Получаем активатор
typedef CALL_CONV int(__stdcall *CREATEFUNCPTR)(CI::ICiFbEncActivator**);
CREATEFUNCPTR GetActivator = 
(CREATEFUNCPTR)::GetProcAddress(mHandle.get(), "createCiFBEncActivator");

if (!GetActivator)
{
	MessageBox(
		NULL,
		L"Не удалось получить из модуля CiFbEnc_x86.dll процедуру" 
              "createCiFBEncActivator"
		 " - попробуйте посмотреть, что пишет tdump.",
		L"Загрузка модуля",
		MB_OK|MB_ICONERROR);

	return;
}

CI::ICiFbEncActivator* pActivator = NULL;
GetActivator(&pActivator);
if (!pActivator) { ShowMessage("ERROR GetActivator!"); return; }

// . . .
//
// обращения к объекту модуля активации
//
// . . .

// Уничтожаем экземпляр активатора
pActivator->Destroy();
pActivator = NULL;

// Delphi

var
pActivator : ICiFbEncActivator;
res : Integer;
CreateActivator: TActivatorFunction;
mHandle : HINST;
. . .
begin
// Загружаем модуль
mHandle := LoadLibraryEx(
PChar('C:\TESTAPP\CiFbEnc_x86.dll'), 0, LOAD_WITH_ALTERED_SEARCH_PATH);

if mHandle = 0 then
begin
     MessageBox(
        Application.Handle,
        'Модуль CiFbEnc_x86.dll не найден или рядом с ним нет fbclient.dll',
        'Загрузка модуля',
        MB_OK OR MB_ICONERROR);
      Exit;
end;

// Получаем активатор
CreateActivator := GetProcAddress(mHandle, 'createCiFBEncActivator');
if not Assigned(CreateActivator) then
begin
     MessageBox(
        Application.Handle,
        'Не удалось получить из модуля CiFbEnc_x86.dll процедуру createCiFBEncActivator'
        +
        ' - попробуйте посмотреть, что пишет tdump.',
        'Загрузка модуля',
        MB_OK OR MB_ICONERROR);

      Exit;
end;

pActivator := nil;
res := CreateActivator(pActivator);
if not Assigned(pActivator) then begin ShowMessage('ERROR CreateActivator!'); Exit; end;

// . . .
//
// обращения к объекту модуля активации
//
// . . .
 

// Уничтожаем экземпляр активатора
pActivator.Destroy;
pActivator := nil;

FreeLibrary(mHandle);
end;

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

Теперь можно перейти и к получению состояния БД.

// C++ Builder

. . .

// Устанавливаем параметры подключения к БД
pActivator->SetDBAccess("localhost:TESTDB", "SYSDBA", "masterkey");

// Посмотрим, что нам расскажет о БД сервис Firebird
char stat_buf[1024] = { 0 };
size_t bufsize = sizeof(stat_buf);

int res = pActivator->GetStateSVC(stat_buf, bufsize);
String sStatMsg = (String)stat_buf;
if (Err_OK == res)
{
	MessageBox(NULL, sStatMsg.c_str(), L"Статус БД", MB_OK|MB_ICONINFORMATION);
}
else
{
	String sErrMsg = L"ERROR GetStateSVC: " + sStatMsg;
	MessageBox(
		NULL,
		sErrMsg.c_str(),
		L"Статус БД",
		MB_OK|MB_ICONERROR);

}

. . .

// Delphi

var
. . .
stat_buf : array[0..1023] of AnsiChar;
bufsize : NativeUInt;

. . .

// Устанавливаем параметры подключения к БД
res := pActivator.SetDBAccess('localhost:TESTDB', 'SYSDBA', 'masterkey');

// Посмотрим, что нам расскажет о БД сервис Firebird
bufsize := SizeOf(stat_buf);
ZeroMemory(@stat_buf, bufsize);
res := pActivator.GetStateSVC(stat_buf, bufsize);

if Err_OK = res then
begin
       MessageBox(Application.Handle, PChar(String(stat_buf)),
       'Статус БД', MB_OK OR MB_ICONINFORMATION);
end
else
begin
       MessageBox(Application.Handle, PChar(String(stat_buf)),
       'Ошибка', MB_OK OR MB_ICONERROR);
end;

. . .

В случае, если удалось подключиться к БД и получить информацию, мы увидим сообщение с детальной информацией о текущем состоянии.

Успешное получение текущего статуса БД

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

// C++ Builder

// От куда-то у нас есть ключ 192 бита
const uint8_t key[24] =
{
	0x06,0xDE,0x81,0xA1,0x30,0x55,0x1A,0xC9,
	0x9C,0xA3,0x42,0xA9,0xB6,0x0F,0x54,0xF0,
	0xB6,0xF9,0x70,0x18,0x85,0x04,0x83,0xBF
};

// Delphi

const
key : array [0..23] of Byte =
(
  	$06,$DE,$81,$A1,$30,$55,$1A,$C9,
	$9C,$A3,$42,$A9,$B6,$0F,$54,$F0,
	$B6,$F9,$70,$18,$85,$04,$83,$BF
);

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

// C++ Builder

. . .

// Устанавливаем параметры подключения к БД
pActivator->SetDBAccess("localhost:TESTDB", "SYSDBA", "masterkey");

// Устанавливаем ключ активатору
int res = pActivator->SetKey(&key, sizeof(key));
if (Err_OK != res) { ShowMessage("ERROR SetKey!"); pActivator->Destroy(); return; }

// Активируем доступ к ключу
res = pActivator->Activate();
if (Err_OK != res)
{
// Обрабатываем ошибку
	char errmsg[512] = {0};
	size_t esize = sizeof(errmsg);
	pActivator->GetFBStat(errmsg, esize);
	String sErrMsg = "ERROR Activate: " + String(errmsg);
	MessageBox(
		NULL,
		sErrMsg.w_str(),
		L"Активация доступа к ключу",
		MB_OK|MB_ICONERROR);

	pActivator->Destroy();
	return;
}

. . .

// Delphi

. . .

// Устанавливаем параметры подключения к БД
res := pActivator.SetDBAccess('localhost:TESTDB', 'SYSDBA', 'masterkey');

// Устанавливаем ключ активатору
res := pActivator.SetKey(@key, Length(key));
if Err_OK <> res then begin ShowMessage('ERROR SetKey!'); pActivator.Destroy; Exit; end;

// Активируем доступ к ключу
res := pActivator.Activate;
if Err_OK <> res then
begin
bufsize := SizeOf(errmsg);
    	ZeroMemory(@errmsg, bufsize);
	pActivator.GetFBStat(errmsg, bufsize);
	MessageBox(
		Application.Handle,
		PChar('ERROR Activate: ' + String(errmsg)),
		'Активация доступа к ключу',
		MB_OK OR MB_ICONERROR);

	pActivator.Destroy;
	Exit;
end;

. . .

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

Теперь можем написать обработчики кнопок “Encrypt” и “Decrypt”. Если опустить проверку статуса выполненной задачи и предварительную активацию ключа, то достаточно только добавить вызов у объекта активации одноимённой функции:

// C++ Builder

// зашифровать
res = pActivator->Encrypt();

. . .

// расшифровать
res = pActivator->Decrypt();

// Delphi

// зашифровать
res := pActivator.Encrypt;

. . .

// расшифровать
res := pActivator.Decrypt;

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

Теперь мы можем зашифровать БД. А вызов её статуса расскажет нам о том, что она уже зашифрована.

Статус зашифрованной БД

После этого, попытка выполнить подключение к БД не меняя обработчик кнопки “Connect” приведёт к ошибке.

Попытка подключения к зашифрованной БД без активации ключа

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

Для C++ Builder
// C++ Builder

void __fastcall TForm1::Button1Click(TObject *Sender)
{
// Загружаем модуль
 	std::unique_ptr mHandle(
		::LoadLibraryEx(L"C:\\TESTAPP\\CiFbEnc_x86.dll", 0,
LOAD_WITH_ALTERED_SEARCH_PATH),
		&::FreeLibrary);

	if (!mHandle)
	{
		MessageBox(
			NULL,
			L"Модуль CiFbEnc_x86.dll не найден или рядом с ним нет fbclient.dll",
			L"Загрузка модуля",
			MB_OK|MB_ICONERROR);
		return;
	}

	// Получаем активатор
	typedef CALL_CONV int(__stdcall *CREATEFUNCPTR)(CI::ICiFbEncActivator**);
	CREATEFUNCPTR GetActivator = 
(CREATEFUNCPTR)::GetProcAddress(mHandle.get(), "createCiFBEncActivator");

	if (!GetActivator)
	{
		MessageBox(
			NULL,
			L"Не удалось получить из модуля CiFbEnc_x86.dll"
" процедуру createCiFBEncActivator"
			 " - попробуйте посмотреть, что пишет tdump.",
			L"Загрузка модуля",
			MB_OK|MB_ICONERROR);
		return;
	}

	CI::ICiFbEncActivator* pActivator = NULL;
	GetActivator(&pActivator);
	if (!pActivator) { ShowMessage("ERROR GetActivator!"); return; }

	// Устанавливаем параметры подключения к БД
	pActivator->SetDBAccess("localhost:TESTDB", "SYSDBA", "masterkey");

	// Устанавливаем ключ активатору
	int res = pActivator->SetKey(&key, sizeof(key));
	if (Err_OK != res) { ShowMessage("ERROR SetKey!"); pActivator->Destroy();return; }

	// Активируем доступ к ключу
	res = pActivator->Activate();
	if (Err_OK != res)
	{
		// Обрабатываем ошибку
		char errmsg[512] = {0};
		size_t esize = sizeof(errmsg);
		pActivator->GetFBStat(errmsg, esize);
		String sErrMsg = "ERROR Activate: " + String(errmsg);
		MessageBox(
			NULL,
			sErrMsg.w_str(),
			L"Активация доступа к ключу",
			MB_OK|MB_ICONERROR);
		pActivator->Destroy();
		return;
	}

	// Подключаемся к БД
	try
	{
		FDConnection1->Connected = true;
		FDTable1->Active = true;
	}
	catch(EFDDBEngineException &e)
	{
		String sErrMsg = L"Не удалось подключиться к БД. " + e.Message;
        	MessageBox(
			NULL,
			sErrMsg.w_str(),
			L"Подключение к БД",
			MB_OK|MB_ICONERROR);
	}

	// Уничтожаем экземпляр активатора
	pActivator->Destroy();
	pActivator = NULL;
}


Для Delphi
// Delphi

procedure TForm2.Button1Click(Sender: TObject);
var
pActivator : ICiFbEncActivator;
res : Integer;
CreateActivator: TActivatorFunction;
mHandle : HINST;
errmsg : array[0..511] of AnsiChar;
bufsize : NativeUInt;
begin
  // Загружаем модуль
	mHandle := LoadLibraryEx(PChar('C:\TESTAPP\CiFbEnc_x86.dll'), 0,
 LOAD_WITH_ALTERED_SEARCH_PATH);

  if mHandle = 0 then
  begin
     MessageBox(
        Application.Handle,
        'Модуль CiFbEnc_x86.dll не найден или рядом с ним нет fbclient.dll',
        'Загрузка модуля',
        MB_OK OR MB_ICONERROR);
      Exit;
  end;

  // Получаем активатор
  CreateActivator := GetProcAddress(mHandle, 'createCiFBEncActivator');
  if not Assigned(CreateActivator) then
  begin
     MessageBox(
        Application.Handle,
        'Не удалось получить из модуля CiFbEnc_x86.dll процедуру createCiFBEncActivator'
 +
        ' - попробуйте посмотреть, что пишет tdump.',
        'Загрузка модуля',
        MB_OK OR MB_ICONERROR);
		  Exit;
  end;
  pActivator := nil;
  res := CreateActivator(pActivator);
  if not Assigned(pActivator) then begin ShowMessage('ERROR CreateActivator!');Exit; end;

  // Устанавливаем параметры подключения к БД
  res := pActivator.SetDBAccess('localhost:TESTDB', 'SYSDBA', 'masterkey');

  // Устанавливаем ключ активатору
  res := pActivator.SetKey(@key, Length(key));
  if Err_OK <> res then begin ShowMessage('ERROR SetKey!'); pActivator.Destroy;Exit; end;

  // Активируем доступ к ключу
  res := pActivator.Activate;
  if Err_OK <> res then
  begin
	bufsize := SizeOf(errmsg);
    	ZeroMemory(@errmsg, bufsize);
	pActivator.GetFBStat(errmsg, bufsize);
	MessageBox(
		Application.Handle,
		PChar('ERROR Activate: ' + String(errmsg)),
		'Активация доступа к ключу',
		MB_OK OR MB_ICONERROR);

	pActivator.Destroy;
	Exit;
  end;

  // Подключаемся к БД
  try
    	FDConnection1.Connected := True;
FDTable1.Active := True;
  except
    	on E: EFDDBEngineException do begin
       MessageBox(
        Application.Handle,
        PChar('Не удалось подключиться к БД. ' + String(E.Message)),
        'Подключение к БД',
        MB_OK OR MB_ICONERROR);
    end;
  end;

  // Уничтожаем экземпляр активатора
  pActivator.Destroy;
  pActivator := nil;

  FreeLibrary(mHandle);
end;


Вот так вот достаточно просто можно работать с зашифрованной БД.

Полностью проект можно взять тут.

Демо-пакет — тут.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331002/


Метки:  

MyTaskHelper.ru на практике: создание бесплатной БД для проверки результатов ОГЭ

Четверг, 15 Июня 2017 г. 18:47 + в цитатник

С конца мая до конца июня в российских школах проходит ОГЭ (основной государственный экзамен) по всем возможным предметам и ученики с нетерпением ждут своих результатов.


Раздумывая над темами будущих публикаций о нашем проекте MyTaskHelper, мы никак не могли остановиться на чем-то конкретном. Но идея пришла сама собой в тот момент, когда наш сервис начало использовать Министерство образования и науки Астраханской области. Да-да, вам не показалось: MTH активно используется государственными структурами. А в описанном выше конкретном случае администратор сайта astrcmo.ru создал и добавил на ресурс форму входа, позволяющую получить доступ к записи после заполнения требуемых данных (логина и пароля).



Рис. 1 Встроенный на сайт astrcmo.ru виджет


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


Выглядит это следующим образом (мы создали тестовую веб-форму с нуля (глянуть, как это делается, можно в теме, но можно и просто экспортировать свою готовую базу данных в формате CSV или Excel):



Рис.2 Поля сконструированной в MTH веб-формы, посредством которой можно добавлять информацию в БД.


В настройках поля под названием «Серия документа» выбираем тип данных «Логин», а «Номер документа» — «Пароль».


А так выглядит заполненная база данных.



Рис. 3 Заполненная база данных с персональными данными и оценками


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



Рис. 4 Форма входа на виджет записи


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


­
  • разрешить редактировать запись после логина;
  • ­разрешить экспорт записей в Excel;
  • ­разрешить экспорт записей в PDF.

ВСЕ! Далее ученик, желающий узнать свои баллы, переходит на страницу сайта и введя логин (в нашем случае это – серия документа) и пароль (номер документа) (Рис. 4), получает доступ к записи из базы данных, в которой хранится искомый результат (Рис.5).



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


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


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



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

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

https://habrahabr.ru/post/331000/


[recovery mode] Verizon завершили поглощение Yahoo

Четверг, 15 Июня 2017 г. 18:35 + в цитатник
На этой неделе завершился долгий путь Yahoo Inc., пионера коммерческого интернета, в портфель телекоммуникационной компании Verizon Communications Inc. Поглощение холдинга, владевшего самым посещаемым сайтом в США, многие называют концом эпохи.

JD Hancock / Flickr / CC

Поглощение Yahoo


13 июня стало известно об официальном закрытии сделки. Активы Yahoo, в том числе новостные, спортивные, финансовые сайты и сервисы прочих тематик, присоединятся к портфелю Verizon в качестве дочернего предприятия под новым именем Oath. Руководить им будет бывший генеральный директор медиахолдинга AOL Тим Армстронг. Oath объединяет свыше 50 медиа- и технологических брендов, в том числе и популярное интернет-издание Huffington Post.

Стоимость сделки по поглощению Yahoo составляет $4,48 млрд. Это на $250 млн меньше первоначальной суммы. Причиной изменения условий стала череда скандалов, которая сопровождала Yahoo в последнее время.

История поглощения Yahoo


Предпосылкой для покупки Yahoo послужило приобретение Verizon компании AOL в 2014 году за $4,4 млрд. Так компания обозначила свою готовность к расширению медиаактивов. Yahoo на тот момент также зарекомендовала себя как медийная организация. По следам сделки с AOL Фран Шаммо (Fran Shammo), главный финансовой директор Verizon, в 2015 году во время медиаконференции намекнул на заинтересованность компании в Yahoo.

«Все, что я могу сказать сейчас, мы не знаем, что решит совет директоров Yahoo. Пока еще рано об этом говорить», — заявил он тогда. И годом позднее (в июне 2016) Verizon объявила о планах приобрести основной интернет-бизнес Yahoo за $4,8 млрд.

На тот момент и до самого окончания сделки президентом и главным исполнительным директором Yahoo была Марисса Майер. В июле 2016 года она выразила готовность остаться на посту после завершения сделки, но теперь покидает компанию с «золотым парашютом» в размере $186 млн. Именно ее решения и действия привели к падению оценки Yahoo за последний год.

На фото: Марисса Майер TechCrunch CC

Причиной стало публичное раскрытие ряда инцидентов, произошедших несколькими годами ранее. Сначала в сентябре 2016 года представители Yahoo объявили, что в ходе хакерской атаки в 2014 году были похищены личные сведения по меньшей мере 500 млн пользователей сервисов компании. Представитель Verizon Боб Вареттони (Bob Varettoni) дал понять, что эта информация стала неприятным сюрпризом для холдинга. По версии Yahoo, обнаружить следы взлома удалось только летом 2016 года. Масштаб утечки был отмечен даже на уровне американского Сената.

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

Verizon не заставила себя долго ждать, подтвердив, что эти события оказали «существенное» влияние на оценку бизнеса Yahoo, и условия сделки будут пересмотрены. Изначально Verizon просили скидку в $1 млрд, но затем размер скидки был снижен до $250 млн.

Причины заката Yahoo


Yahoo была запущена в 1994 году Дэвидом Фило (David Filo) и Джерри Янгом (Jerry Yang). На своем пике компания оценивалась в $125 млрд. Это был «золотой век» Yahoo. У менеджмента в то время даже были амбиции по приобретению Google, но уже в 2008 году Microsoft предлагала за Yahoo $44,6 млрд. С позиции 2017 года, это предложение можно рассматривать как выгодное, однако, очевидно, что компания пережила катастрофическое обесценивание. Как правило, его связывают с управленческим кризисом.

В момент переговоров с Microsoft у руля Yahoo стоял сам Джерри Янг. Он пришел на смену Терри Семелю (Terry Semel) после серьезных спадов в финансовых отчетах. В своем предложении Microsoft прибавила дополнительные $5 млрд к рыночной оценке компании, но Янг запросил еще $5 млрд. Тогда Стив Балмер прекратил переговоры. В этом же году на фоне финансового кризиса акции Yahoo серьезно упали. К концу 2008-го компания стоила менее половины того, что Microsoft готова была заплатить ранее.

На фото: Джерри Янг Yahoo CC

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

На смену Бартц пришел бывший президент PayPal Скотт Томпсон (Scott Thompson). Его служба продлилась всего 4 месяца, в течение которых он успел поучаствовать в скандалах с фальсификацией своего резюме и еще сильнее ударить по финансовому состоянию Yahoo. Именно он принял роковое решение о продаже 20-процентной доли компании в Alibaba. Эта сделка обошлась Yahoo в $54 млрд. Пакет акций Alibaba был куплен в 2005 году за $1 млрд.

После этого главой Yahoo становится Марисса Майер. С ее подачи фокус компании сместился в сторону медиа. Для этого было совершено приобретение около 50 активов, в том числе платформы социальных блогов Tumblr за $1,1 млрд. Тем не менее за время ее руководства с 2012 года позитивных изменений в части прибыли Yahoo не произошло. Частично это связано с отсутствием у Мариссы единой стратегии развития. Скандалы с хакерскими атаками, пришедшиеся на период ее работы, окончательно снизили стоимость компании.

Зачем Verizon нужна Yahoo?


«Закрытие этой сделки представляет собой важный шаг в развитии глобальной перспективы, необходимой для нашей цифровой медиакомпании, — заявила Марни Уолден (Marni Walden), президент Verizon по инновационным продуктам и новым предприятиям. — Объединенный портфель активов Verizon и Oath: от VR до AI, от 5G до IoT [...] создаст захватывающие новые способы привлечь аудиторию со всего мира».

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

Генеральный директор AOL Тим Армстронг в прошлом году сказал, что потенциальная прибыль не является основным аспектом слияния. Главная цель — расширить присутствие AOL и Yahoo на рынке цифровой рекламы. В то время как сейчас Facebook и Google доминируют на нем, Yahoo и AOL серьезно отстают от лидеров. Объединение активов поможет холдингу забрать порядка 5% рынка с дальнейшим расширением доли.

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

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

P.S. О чем еще мы пишем в нашем блоге:

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

https://habrahabr.ru/post/330994/


Метки:  

Моделирование переходных процессов при коммутации электрической цепи средствами Python

Четверг, 15 Июня 2017 г. 18:12 + в цитатник


Зачем нужно учитывать переходные процессы


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

С другой стороны, переходные процессы находят практическое применение, например, в различные рода электронных генераторах, в схемах электроники и автоматики.
В сети много публикаций по данной теме [1,2,3], однако большая их часть содержит описания переходных процессов, основанное на методах аналитического решения соответствующих уравнений. Численные методы используются значительно реже, причём большая часть таких публикаций посвящена описанию метода численного решения дифференциального уравнения.
Учитывая хорошо развитые в библиотеке SciPy численные методы, привожу пример математического моделирования переходных процессов при коммутации в электрических цепях средствами данной библиотеки.


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


Обобщённая схема электрической цепи.



Рассмотрим цепь, содержащую источник тока — E, катушку индуктивности – L, два сопротивления — R1, R2, конденсатор — C и рубильник.
Приведём параметры электрической цепи для состояния после коммутации.

В разомкнутом состоянии приведённая на рисунке цепь соответствует условиям:



Листинг программы построения графиков переходных процессов в цепи при размыкании.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
R1=10;R2=20;L=0.02;C=0.00005;E=100;tm=0.02; #параметры электрической цепи
def f(y, t):#  дифференциальное уравнение переходного процесса.
                y1,y2 = y 
                return [y2,-(R1/L)*y2-(1/(L*C))*y1+E/(L*C)]
t = np.linspace(0,tm,1000)
y0 = [E*R2/(R1+R2),0]#начальные условия
z = odeint(f, y0, t)#решение  дифференциального уравнения
y1=z[:,0] # вектор значений решения
y2=100*C*z[:,1] # вектор значений производной
plt.title('Напряжение на конденсаторе и ток при размыкании цепи', size=12)
plt.plot(t*1000,y1,linewidth=2, label=' Uс(t)')
plt.plot(t*1000,y2,linewidth=2, label=' i3(t)=100*C*dUc(t)/dt')
plt.ylabel("Uc(t), i3(t)")
plt.xlabel("t*1000")
plt.legend(loc='best')
plt.grid(True)
plt.show()

Результат работы программы




В разомкнутой цепи i2=0, а i3=i1, поэтому на графике приведены только ток и напряжение. Характер переходных процессов – затухающие колебания.

В замкнутом состоянии приведённая на рисунке цепь соответствует условиям:



Листинг программы построения графиков переходных процессов в цепи при замыкании.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
R1=10;R2=20;L=0.02;C=0.00005;E=100;tm=0.02; # параметры электрической цепи
def f(y, t):#  дифференциальное уравнение переходного процесса.
                y1,y2 = y 
                return [y2,-((L+R1*R2*C)/(R2*L*C))*y2-((R1+R2)/(R2*L*C))*y1+E/(L*C)]
t = np.linspace(0,tm,1000)
y0 = [E,0]#начальные условия
z = odeint(f, y0, t)#решение  дифференциального уравнения
y1=z[:,0] # вектор значений решения
y2=100*(C*z[:,1]+y1/R2)
y3=100*C*z[:,1]
y4=100*y1/R2
plt.title('Напряжение на конденсаторе и токи  при замыкании цепи', size=12)
plt.plot(t*1000,y1,linewidth=2, label=' Uc(t)')
plt.plot(t*1000,y2,linewidth=2, label=' i1(t)*100')
plt.plot(t*1000,y3,linewidth=2, label='i3(t)*100')
plt.plot(t*1000,y4,linewidth=2, label=' i2*100')
plt.ylabel("Uc(t),i1(t),i2(t),i3(t)")
plt.xlabel("t*1000")
plt.legend(loc='best')
plt.grid(True)
plt.show()

Результат работы программы




В замкнутой цепи для токов выполняется соотношение i1=i2+i3. Переходные процессы апериодические. В установившимся режиме i3=0, i1=i2, что и следует из графика.

Вывод


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

Ссылки


  1. torus.pp.ua/manuals/lessons/matusko/perechodn.html
  2. electricalschool.info/spravochnik/electroteh/747-perekhodnye-processy-v-jelektricheskojj.html
  3. ru.wikipedia.org/wiki/Переходные_процессы_в_электрических_цепях
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330990/


Метки:  

Аналитика в рекрутменте: она вам не бигдата

Четверг, 15 Июня 2017 г. 17:22 + в цитатник
Как-то мы проводили опрос HR'ов и спросили: как часто они используют аналитику. 27% ответили что не используют ни один показатель, 30% используют один или два (вроде текучки и времени на закрытие). Сегодня попробуем доказать, что в рекрутменте есть место сбору данных. Потому что данные = польза и даже данные = крутой HR-бренд.



Да, наш опыт показывает, что HR’ы и аналитика в большинстве своём пока далеки друг от друга. Речь идет не о Big Data, а даже о Small Data, а о простейшем умении собирать данные и обрабатывать их.


Какие показатели в процессе рекрутмента важно иметь ввиду?


1. Источники кандидатов


Понимать, откуда приходят кандидаты

Если компания пользуется только ХХ для поиска кандидатов — одно дело. Им хватает внутренней аналитики. Но, скажем, если в работу подключается уже Суперджоб или социальные сети, собирать данные становится сложнее. Это занимает время, которое можно было бы потратить эффективнее. Раз в сто.

Понимание эффективности каждого отдельного источника важно для всего процесса рекрутмента. Всё очень просто: будете и дальше раскидывать вакансию направо-налево, а она не принесет отклик. Потому что #тут_ей_не_место. Аналитика = экономия времени и ресурсов. Понимая эффективность каналов привлечения, можно распределять вакансии только по ресурсам, благодаря которым приходят нужные кандидаты. Метод опыления тут не должен работать и не будет.




2. Пассивный или активный рекрутмент


Понимать, эффективнее “ручной” поиск пассивных кандидатов или отклик активных соискателей

Если вы занимаетесь привлечением пассивных соискателей, то работные сайты для вас не так актуальны, как социальные сети. Анализировать, какой подход к отбору эффективнее для конкретной позиции, важно, чтобы в следующий раз сокращать время на ненужный постинг, а также сразу знать, где искать кандидатов. За it-специалистами — в чатики Telegram, сюда, на Хабрахабр, на Github, на Мой Круг, может быть в ВК или в Slack. Особенно же интересны те, кто сейчас уже занят на каком-то месте работы, востребован и просто очень крут. Может быть вам вообще нет смысла привлекать активных кандидатов и можно ограничиться “ручным поиском”, добавляя их в базу резюме.




3. Воронка кандидатов


Знать, где отваливаются кандидаты

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

Обязательно для того, чтобы эффективно выстраивать подбор по каждой конкретной вакансии. Не сваливать на заказчика 35 резюме “на посмотреть”. Тоже прям критичный показатель для каждой позиции (скажем, для it-вакансии нужно больше этапов, чем для бухгалтера — это логично и просто). Снова вопрос о логично выстроенных процессах и грамотно потраченных ресурсах и времени на рекрутмент.




4. Время закрытия вакансии (а также стоимость)


Понимать, сколько времени нужно на принятие решений

Superjob в 2015 году проводили исследование и выяснили, что среднее время закрытия вакансии в России составляет 3 недели. Опоссум успевает за это время выносить и родить детеныша. На самом деле срок не очень большой, но нужно обязательно понимать точно, сколько времени вы и ваши коллеги тратите на поиск нового сотрудника, чтобы оптимизировать процессы, на которых вы застреваете. Скажем, прохождение Службы Безопасности. Или собеседование, или просмотр резюме заказчиком. Сокращая время на закрытие вакансии, вы уменьшаете затраты и помогаете своей компании развиваться быстрее.




5. Продуктивность (такой довольно уникальный отчет есть в potok’е)


Знать, сколько усилий нужно приложить для закрытия вакансии

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




6. Причины отказа


Знать, по какой причине кандидаты отклоняют оффер и по какой причине вы им отказываете

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




Плюсы аналитики


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

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

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

// Наладить бизнес-процессы и перестать быть кадровиком
В этом и отличие кадровика от HR-департамента. Теперь все вопросы найма и формирования команды очень прочно связаны с решением бизнес-задач. От грамотного использования данных зависит в том числе успех компании и её эффективность. Если вы будете закрывать вакансии в течение двух месяцев, то позиция будет простаивать, деньги капать, а компания не будет справляться с задачами. Связь прям проста очень.


Как обрабатывать данные?


Всегда есть вариант — сбор данных вручную (не делайте так!)
Окей, если ХХ предоставляет бесплатно аналитику тем компаниям, которые имеют профиль работодателя, то как быть с другими источниками? Для этого рекрутеры и HR’ы заводят Excel-файлы невероятных размеров. Куда тщательно заносится информация о каждом кандидате. В комментариях отмечается, на каком он этапе, откуда попал в табличку. Это безумная трата времени и сил. А если вы хотя бы несколько раз забыли поменять статус кандидата, то всё. Ваша статистика летит к чертям.

Второй вариант — ничего не делать и просто в реальном времени получать отчёты в ATS. То есть в CRM для рекрутмента. Это не тонкий намек на potok. Это намек на то, что обработка данных и создание отчетов — это не вопрос квартала или года, это прям таск на каждый день. Только анализируя активность, эффективность каждый день можно наладить бизнес-процессы в том числе в отношении кадров и их поиска.

Ну и вывод тут может быть только один: аналитика нужна, чтобы принимать умные решения. Умные компании это уже понимают.


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

https://habrahabr.ru/post/330982/


[Из песочницы] Энергоучет в составе SCADA системы торгового центра

Четверг, 15 Июня 2017 г. 17:20 + в цитатник
Расскажу, как писал консольную программу для снятия показаний со счетчиков Меркурий 230 в торговом центре. Ссылка на исходники в конце статьи.

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

Кратко:

— контроль температур в помещениях;
— централизованное управление вентиляцией по расписанию;
— управление подпиткой ГВС;
— учет воды и электричества;
— sms уведомления.

В этой статье об электричестве.

С выбором модели электрики слушали, потому тут особых проблем не испытал. Решил остановиться на Меркурии. Для 3х фаз – 234тые (электрики купили 230) для однофазной сети 206тая модель. Далее получилось, что электрики насовали по всему ТЦ только трехфазные счетчики. Ну мне только меньше проблем. Хотя не пойму зачем.

Программировал я до этого в основном ПЛК и небольшие скрипты на C#. Но тут решил, что смогу сам сделать энергоучет (хотелось опыта в программировании набраться). Погорячился, конечно.

Идея была такова:

— опросчик-сервер ведет энергоучет в базу данных;
— SCADA система отвечает за визуализацию

Способ опроса


Опрос реализовал просто – в бесконечном цикле по одному порту RS485. Вообще для работы системы диспетчеризации подобрал MOXA UPORT 1650-16. Под опрос отдал только один порт, но чтобы не создавать звезду (для RS-485 это не желательно) воспользовался повторителем. Странно, что буржуйских повторителей RS-485 с большим количеством входов не нашлось. Однако нашлась отечественная штуковина Тахион ПРТ-1/8(12) на 12 портов. Если получиться – выложу видео работы всей связки. Работает хорошо. Только к MOXA UPORT 1650-16 есть претензии, но это уже другая история.

Разработка самого опросчика


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

Цепочка наследования получилась такая:

MeterDevice -> Mercury230 -> Mercury230_DatabaseSignals

MeterDevice – общий класс для всех счетчиков. Реализует обмен по COM порту c Modbus подобным протоколом;
Mercury230 – класс с набором функций опроса для конкретного счетчика;
Mercury230_DatabaseSignals – класс с конкретными параметрами счетчика (токи, напряжения и т.д.) и функцией их обновления. Наследование в нем было не удобным решением. Т.к. потом хлебнул проблем с сериализацей и десериализацией объектов. Но этот класс так прочно влез в код, что отступать было нельзя.

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

public enum error_type : int { none = 0, 
                                       AnswError = -5,  // вернул один или несколько ошибочных ответов
                                       CRCErr = -4, 
                                       NoAnsw = -2,    // ничего не ответил на запрос после коннекта связи
                                       WrongId = -3,   // серийный номер не соответствует 
                                       NoConnect = -1   // отсутствие ответа
                                      };
        public struct RXmes
        {
            public error_type err;
            public byte[] buff;
            public byte[] trueCRC;

            public void testCRC()
            {
                err = error_type.CRCErr;
                if (buff.Length < 4)
                {
                    err = error_type.CRCErr;
                    return;
                }
                byte[] newarr = buff;
                Array.Resize(ref newarr, newarr.Length - 2);
                byte[] trueCRC = Modbus.Utility.ModbusUtility.CalculateCrc(newarr);
                if ((trueCRC[1] == buff.Last()) && (trueCRC[0] == buff[(buff.Length - 2)]))
                {
                   err = error_type.none;
                }
            }
            public void ReadArr(byte[] b)
            {
                buff = b;
                testCRC();
            }
        }

public RXmes SendCmd(byte[] data)
        {
            RXmes RXmes_ = new RXmes();
            byte[] crc = Modbus.Utility.ModbusUtility.CalculateCrc(data);
            Array.Resize(ref data, data.Length + 2);
            data[data.Length - 2] = crc[0];
            data[data.Length - 1] = crc[1];

            rs_port.Write(data, 0, data.Length);

            System.Threading.Thread.Sleep(timeout);

            if (rs_port.BytesToRead > 0)
            {
                byte[] answer = new byte[(int)rs_port.BytesToRead];
                rs_port.Read(answer, 0, rs_port.BytesToRead);
                RXmes_.ReadArr(answer);
                if (RXmes_.err == error_type.none)
                {
                DataTime_last_contact = DateTime.Now;
                }
                return RXmes_;
            }
            RXmes_.err = error_type.NoConnect;
            return RXmes_;            
        }

Таким образом, функция получения серийного номера счетчика Меркурий 230 получилась такая:

public byte[] GiveSerialNumber()
        {
            byte[] mes = {address, 0x08 , 0};
            RXmes RXmes = SendCmd(mes); 
            if (RXmes.err == error_type.none) {
                byte[] bytebuf = new byte[7];
                Array.Copy(RXmes.buff, 1, bytebuf, 0, 7);
                return bytebuf;
            }
            return null;
        }

Кому интересно посмотреть другие функции – можно посмотреть исходники.

Протокол связи со SCADA


Изначально протокол простого TCP севера был простенький. Ответ SCADе по TCP выглядел для Меркурия 230того так.

«type=mеrc230*add=23*volt=1:221-2:221-3:221*cur=1:1.2-2:1.2-3:1.2»

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

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

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

public MetersParameter() {
            minalarm = false;
            maxalarm = false;
        }
        public MetersParameter(float min, float max, float hist, float scalefactor = 1)
        {
            MinValue = min;
            MaxValue = max;
            Hist = hist;
            minalarm = false;
            maxalarm = false;
            ScalingFactor = scalefactor;
        }
        public string alias{set; get;} 
        public float MaxValue { set; get; }
        public float MinValue { set; get; }
        public float ScalingFactor { set; get; } // коэффициент масштабирования. К примеру Коэффициент трансформации по току
        public float Hist { set; get; }
        private bool minalarm;
        private bool maxalarm;
        public bool ComAlarm { get { return MinValueAlarm || MaxValueAlarm ; } }
        public virtual bool MinValueAlarm { get{
             return minalarm;
        } }
        public virtual bool MaxValueAlarm { get{
             return maxalarm;
        } }
        public virtual void RefreshData()
        {
            if (null != ParametrUpdated)
            {
                ParametrUpdated();
            }
            if ((MinValue == 0) && (MaxValue == 0))
            {
                return;
            }
            float calc_par = parametr * ScalingFactor;
            if (calc_par < (MinValue - Hist))
             {
                 minalarm = true;
             }
            if (calc_par > (MinValue + Hist))
             {
                 minalarm = false;
             }
            if (calc_par < (MaxValue - Hist))
             {
                 maxalarm = false;
             }
            if (calc_par > (MaxValue + Hist))
             {
                 maxalarm = true;
             }
             
        }
        float parametr;
        public bool UseScaleForInput = false;
        public virtual float Value { 
            set{
                parametr = UseScaleForInput ? value / (ScalingFactor <= 0 ? 1 : ScalingFactor) : value;
            RefreshData();
            }
            get
            {
            return parametr * ScalingFactor;
            }
        }

        public void CopyLimits(MetersParameter ext_par)
        {
            this.MinValue = ext_par.MinValue;
            this.MaxValue = ext_par.MaxValue;
            this.Hist = ext_par.Hist;
        }
    }

Тут выручила сериализация объектов. Попробовав Байтовую, XML и JSON сериализацию, было решено остановиться на JSON (DataContractJsonSerializer). Она удобно читалась глазом, объем данных получался меньше XML. И вообще DataContractJsonSerializer прощал отсутствие конструктора без аргументов. Это значительно упрощало жизнь.

База данных


Конечно, важнейшим моментом было — запись показаний счётчиков. Т.к. Scada система работала с MySql, то и опросчик было решено завязывать с ней. Тут особых проблем не было.

Вопрос был только один – «какие данные записывать?», т.к. вариантов счетчик дает не мало. Собственно коды для запроса:

public enum peroidQuery : byte
        {
            afterReset = 0x0, 
            thisYear = 1,
            lastYear = 2,
            thisMonth = 3, thisDay = 4, lastDay = 5,
            thisYear_beginning = 9,
            lastYear_beginning = 0x0A,
            thisMonth_beginning = 0x0B,
            thisDay_beginning = 0x0C,
            lastDay_beginning = 0x0D
        }

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

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

Итог


Получилась немного костыльная, но все же рабочая программа учета электроэнергии. На данный момент программка опрашивает около 70 счетчиков. Консольное приложение крутится на сервере, а клиентская часть работает на АРМе пользователя.

Исходник опросчика выкладываю на GitHub. Ссылку на клиентскую часть постараюсь выложить позже.

P.S. Про сходство протокола Меркурия 230 и СЭТ-4тм


Если кто не сталкивался. То есть такой Завод им. Фрунзе (в Нижнем Новгороде). И счетчики у них работают с очень похожим протоколом. Пробегался по мануалам обоих – один в один. Но, слышал, что в протоколах есть какие-то различия (пока не вдавался). Жаль, что на руках нет СЭТа.
Ноги сходства растут из того, что Меркурии разработаны бывшими работниками Фрунзе. Такие дела. Странно, почему на слуху больше Меркурий.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330980/


Метки:  

Работа с периферией из JavaScript: от теории к практике

Четверг, 15 Июня 2017 г. 17:14 + в цитатник
Работа сотрудника банка опасна и трудна. В Единой фронтальной системе мы стараемся помочь сотруднику банка и автоматизировать его работу. Одна из многочисленных задач, которую нам нужно решить, — доработать тонкий клиент для возможности работы с периферийным банковским оборудованием. Но сделать это не так-то просто. В данной статье мы расскажем, как мы пытались решить задачу, и к чему это привело.

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



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

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

  1. Сетевой/локальный принтер.
  2. Чековый принтер (Epson-M950 или Olivetti).
  3. POS-терминал (Verifone VX820).
  4. Устройство для чтения «таблеток» (touch memory с ЭЦП).
  5. Сканеры разных типов (для штрих-кодов, паспортов или просто документов).
  6. Веб-камера (фотографировать потенциальных заемщиков).
  7. Специфическое банковское оборудование – cash dispenser/receiver и т.п.

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

Работа с устройствами на низком уровне осуществляется через драйвера производителей оборудования или native библиотеки внутренней разработки. Установка и обновление драйверов и другого ПО на рабочих местах осуществляются централизованно. На рабочих местах стоят Windows и Internet Explorer 11 или 8. Обсуждается возможность перехода на Linux и Chrome/Firefox. Отсюда возникает требование кросс-платформенности и кросс-браузерности.

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

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

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

Как мы пытались все это приручить, почему не сразу получилось


Из условий эксплуатации следует схема работы с периферией:

Мы попытались найти решение проблемы и проработали несколько вариантов.

HTML5


Хотя в нем потенциально есть работа с периферийными устройствами, текущие реализации заточены под мобильные устройства или аудио/видео,  и для наших задач не подходят от слова «совсем».

WebUSB


https://wicg.github.io/webusb/ — во-первых, не все устройства подключаются через USB. Во-вторых, поддержки WebUSB на текущий момент нет нигде, кроме экспериментальной feature в Chrome. Так что тоже нам не подходит.

ActiveX


Способ старинный и проверенный – делаются обертки над драйверами, устанавливаемые как локальные OCX или DLL, обращение к которым идет через ActiveXObject:

var printer = new ActiveXObject("LocalPrinterComponent.Printer");
printer.print(data + "\r\n");


Но он работает только в IE и Windows. Несмотря на попытки сделать технологию ActiveX переносимой,  Microsoft отказалась от развития ActiveX в пользу технологии плагинов.

Плагины браузера


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

Нативные компоненты на Java


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

Applet


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

И что же делать?


В итоге мы остановились на следующей архитектуре работы с периферийными устройствами:



Клиентское приложение в JavaScript обращается к локальному сервису через модуль работы с периферийным API, который является обвязкой над клиентской частью socket.io.

На рабочие места устанавливается node.js, который запускается под сервисной учетной записью при старте операционки. В node.js работает наш модуль bootstrap, отвечающий за загрузку npm-модулей для работы с периферийными устройствами с сервера в локальную файловую систему. Клиентский код генерирует event, в качестве атрибутов передается код и версия модуля, который работает с устройством, вызываемый метод и его параметры:



Также bootstrap отвечает за работу с платформенными сервисами (выгрузку логов с рабочего места по запросу администратора и т.п.)

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





Модуль работает с низкоуровневым API операционной системы или драйвера через npm модуль node.js «ffi»:



Когда клиентское приложение обращается к bootstrap, передавая модуль и версию, bootstrap проверяет локальное хранилище. Если нужного модуля нет в локальном хранилище, он выкачивается его с сервера. Таким образом, централизованно устанавливаются только драйвера и node.js с bootstrap’ом, а npm-модули для работы с устройствами скачиваются в рантайме. Но данная функция вряд ли будет использоваться в промышленной конфигурации, так что предполагаем, что при удаленной инсталляции драйверов устройств на рабочие места сотрудников будет устанавливаться и соответствующий npm модуль периферийного API, представляющий собой JS bundle.

Решение пока еще не реализовано, мы работаем над этим и обязательно расскажем о результатах в другой статье. А пока хотим спросить у вас, что вы думаете о выбранном подходе? Какие подводные камни нас ждут? Приглашаем всех принять участие в дискуссии в комментариях.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330374/


Метки:  

Карьера в Digital: что такое, кому подойдет, где начать

Четверг, 15 Июня 2017 г. 17:05 + в цитатник
Сегодня расскажем о карьере в digital: что это такое, какие есть направления, что нужно знать, кому будет интересно и где уже можно начать работать. Разложили по полочкам и делимся с вами!



1. Что это такое-то?


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




На самом деле всё просто. Диджитал – это технологии (кэп, да), которые используются в маркетинге для продвижения продуктов и/или услуг.


2. И что тут может быть интересного?! (Осторожно! Занудство mode on)


Диджитал — перспективная отрасль. В Cossa провели исследование, где проанализировали вакансии в диджитал направлениях и рассказали о средней заработной плате специалистов в Москве. Помимо материального, сами подумайте, у каждой компании есть продукт/услуга, который они продают. Вся выручка компаний зависит от продаж (не будем говорить про инвесторов и сложные вещи). Маркетинг и реклама — главные инструменты стимулирования спроса. Никакая компания не выживет без маркетинга, потому что конкуренты не спят! И чтобы вас запомнили (а вам оно надо) нужно постоянно о себе напоминать. Иначе ваша компания умрет банкротства, а вы — от голода.


// Аналитика


«Вот это поворот!» — скажете? Даже тут нужны аналитики: CTR, CTB, CTI, CTR, конверсия, посещаемость, охват, вовлеченность и еще куча важных рекламных показателей. Здесь явно нужен тот, кто способен запоминать расшифровки, и чей мозг не взорвется от множества информации.


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




Если нужно подтянуть знания — ловите материалы из нашей библиотеки:


Бесплатные курсы по SQL: всё, что вы не знали о базах данных


11 курсов Excel для начинающих и гуру


51 бесплатная книга о Data Science



// Интернет-маркетинг


«Креаааатииив» — мы просто кричим вам! Это направление пропитано идеями, новинками, трендами и бесконечными потоками информации. SMM, медиа, инфоповоды, мемы, гифки, видео, писать классные материалы и подавать полезные материалы своим читателям. Вы — голос компании в информационном поле! Именно вы познакомите с продукцией компании и расскажете о том, чем можете быть полезны.


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




Скорее смотрите, какие навыки вам пригодятся, ищите себя и ловите полезные материалы:


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


9 инструментов для тех, кто пишет


22 онлайн курса, о которых вы не знали


// Web-разработка


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


Кажется, что создание сайта случай вообще единичный — один раз сделал и поддерживай. Рынок быстро насытится и все эти web-мастеры остануться без работы, но вы ошибаетесь. Все меняется: появляются новые технологии, тренды, интерфейсы, платформы. Компании расширяются и создают несколько сайтов в зависимости от целей: карьерные, клиентские, партнерские. Старые сайты должны обновляться, чтобы держать свои позиции. Думаем, что вы точно не захотите пойти на собеседование или купить продукцию в
Space Is The Place :D


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




И немного полезных ссылок по теме:


7 курсов по web-программированию


15 ресурсов для изучения программирования


10 курсов, чтобы стать «тыжпрограммистом»


3. Ну и кому оно надо-то?


Если вы интересуетесь медиа, интернет СМИ, рекламой, маркетингом, понимаете (ну, или очень хотите разобраться) в таргете, метрике и прочих сложных инструментах. Вы постоянно интересуетесь происходящим. Вы в курсе событий интернет-тусовки: знаете все тренды и даже присматриваетесь к картинкам в постах.

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


4. Я готов, куда идти?


Когда нет опыта, самый крутой путь — стажировка. Банк «Открытие» запустил набор в
research лабораторию и принимает заявки до 20 июня! Здесь подтянете знания, станете частью диджитал команды, будете работать над созданием реальных продуктов и получить редкий опыт.

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

https://habrahabr.ru/post/330976/


Метки:  

[Из песочницы] Запуск Bare-metal приложения на Cyclone V SoC

Четверг, 15 Июня 2017 г. 16:43 + в цитатник

Введение


Для некоторых людей FPGA SoC является чем-то недоступным пониманию и данная статья должна исправить это недоразумение. Разберем создание программы с нуля, от пустого проекта, до горящего светодиода. Для начала скажу, что проект выполнялся на отладочной плате DE1-SoC, и вы можете с легкостью адаптировать его для других плат с плисами фирмы Аltera, если разберетесь с данным руководством. Начнем!

Создание прошивки FPGA


Для создания прошивки нам, очевидно, потребуется проект Quartus. Но создадим этот проект не стандартным способом (через project wizard), а через утилиту, которая шла в комплекте с платой DE1-SoC



Эта утилита генерирует top-level файл написанный на Verilog с объявлениями выбранных элементов. Нам потребуется CLOCK, HPS (SoC), кнопки, светодиоды. Нажимая Generate получаем проект Quartus. Главное преимущество создания проекта этим способом, это то, что нам не придется назначать пины FPGA в Pin Planner, потому что утилита сделала это за нас, тем самым сэкономила уйму времени.



Добавим генерированный файл к проекту.

Следующим шагом будет создание системы в QSYS. На всякий случай кратко поясню суть происходящего. Cyclone V SoC не просто FPGA, внутри ее структуры находится двухъядерный процессор Cortex-A9 с разными модулями, такими как USB порты, Ethernet, SPI, SD/MMC и т.д. По-простому, можно представить это как микроконтроллер внутри FPGA. Создавая систему в QSYS мы связываем HPS (hard processor system) систему с элементами, синтезируемыми в FPGA, используя готовые ядра (IP cores). QSYS позволяет без лишних забот соединить разные ядра через шины Avalon-MM или AMBA AXI, нам не нужно в ручную писать код, QSYS генерирует его за нас. Заходим в QSYS и выбираем во вкладке IP cores нашу HPS систему. В настройках системы нам потребуется только Lightweight H2F Bridge во вкладке FPGA interfaces.



Во вкладке Peripheral Pins выберем SD/MMC (с карточки мы будем загружать программу) и UART. Позже расскажем об этом подробнее.



Во вкладке HPS clocks оставляем все по умолчанию. Во вкладке SDRAM необходимо заполнить все поля значениями skew и так далее. На сколько я понимаю, они зависят от трассировки линий от микросхемы до SDRAM. У меня не получилось найти какого-либо документа, в котором есть эти данные для DE1-SoC, поэтому взял их из готового проекта для моей платы (это был SOC-Computer в примерах Altera University Program). Я сохранил эти настройки как персет, чтоб больше не заполнять их, вы можете видеть его в правой колонке. Далее в IP cores найдем PIO, это будут наши светодиоды и кнопки.



Для настроек кнопок выберем Input и ширину 4 бита так как 4 кнопки всего



Для светодиодов соответственно Output и ширина 10.



Соединяем полученную систему как показано на рисунке. Можем изменить имена PIO чтоб было понятнее и красивей. LWH2F bridge является связующим звеном между HPS и FPGA (так же назначает мастером HPS), так как PIO — это элементы выполняющиеся в «ткани» FPGA. Кликнем два раза на надпись «Double click for export» напротив external connection, что-бы вывести из системы выводы PIO светодиодов и кнопок. Позже вы увидите в коде top-level файла генерированной системы эти выводы. Поскольку доступ к PIO, да и ко всем остальным ядрам, в HPS осуществляется по адресам, необходимо назначить их. Это можно сделать автоматически, нажав Assign base addresses.

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





Указываем путь и желаемый язык кода. Я предпочитаю Verilog. После этого QSYS можно закрыть. В окне Quartus появляется следующее сообщение:



Давайте сделаем то, что нас просят.



В окне Files видим наш hps_system.qip. Раскроем его и видим top-level файл системы.



Все, что мы выбрали в системе QSYS теперь в этом файле. Теперь нужно просто вставить этот модуль в изначальный файл. Это и будет top-level файлом нашей прошивки FPGA.



В нем мы приписываем выводам HPS системы изначальные I/O пины этого файла. Напоминаю, что ничего не нужно назначать в Pin Planner, все уже сделано при создании проекта утилитой. Но необходимо назначить пины HPS, это делается следующим образом:





Как только это сделано можно компилировать прошивку. Важно заметить, что если были допущены какие-либо ошибки (я, например, забывал поставить точку в объявлении hps_system возле сlk_clk и hps_io_hps_io_… видно из рисунка) необходимо заново выполнять tcl скрипты. Даже если все написано правильно, и вы запустили tcl скрипты, но после запуска компиляции вы получаете ошибку, стоит попробовать еще раз запустить скрипты, ничего не меняя в коде. Мне помогало, не знаю, чем объяснить данную особенность.



И так, с прошивкой закончили! Теперь приступаем к созданию Preloader.

Создание Preloader


Процесс загрузки HPS имеет несколько стадий, попробуем разобраться в них. Стоит заметить, что Cortex-A9 является процессором для приложений, буква «А» в названии означает Application, и в первую очередь предназначен для работы с использованием ОС, например Linux. Поэтому, строго говоря, идея запуска Bare-Metal программ может показаться странной, но в некоторых случаях это необходимо. К тому же, естественно, такая возможность есть, но разработчику надо понимать процесс загрузки хотя бы на базовом уровне.

Сразу после включения выполняется код расположенный прямо на Flash памяти Cortex-A9 называемый BootRom. Вы не можете изменить его или даже посмотреть его содержание. Он служит для первичной инициализации и в следующем этапе передает процесс загрузки в SSBL (Second Stage Boot Loader называемый коротко Preloader). Что необходимо знать для понимания процесса — это то, что код BootRom, в первую очередь, выбирает источник загрузки Preloader, ориентируюсь на внешние физические пины BSEL. В DE1-SoC изначально выбрана конфигурация пинов для загрузки с SD карточки, и изменить это на, например, QSPI или NAND flash, не припаивая дополнительно переключателя и пары резисторов, невозможно. Поэтому в QSYS мы выбирали во вкладке Peripheral Pins пины SD карты. Есть так же вариант загрузки не из внешних источников, а из памяти, созданной в FPGA, с предварительно загруженным туда кодом.

И так после выполнения кода BootRom начинает загружаться Preloader, необходимый для настройки Clock, SDRAM и прочего. После начинает выполняться программа.

Для создания Preloader потребуется SoC EDS, уверен вы уже скачали его с сайта Intel FPGA.



Программа работает из командной строки. Для начала создадим BSP написав соответствующую команду «bsp-editor».



В этом окне нажмем New HPS BSP.



Необходимо указать путь к {Project directory}/hps_isw_handoff/ и нажать ОК, остальные параметры менять не нужно.



Выбираем настройки spl.boot в котором указываем источник откуда будет загружаться Preloader. В нашем случае это SD карта, поэтому выберем BOOT_FROM_SDMMC. Будем использовать вариант загрузки Preloader и нашей программы при котором на флешке как минимум 2 раздела – раздел не отформатированный с id=A2, для Preloader, и раздел с системой FAT32, для программы. Есть другой вариант без разделения флешки на разделы, так называемый RAW format, но наш вариант на мой взгляд проще. Отформатировать таким образом флешку можно любой программой (я использовал Mini partition tools 9.2). Или можно использовать уже собранный образ в папке ...\embedded\embeddedsw\socfpga\prebuilt_images\sd_card_linux_boot_image.tar.gz и записать его на флешку через Win32DiskImager.



Выбирем FAT_SUPPORT, FAT_BOOT_PARTITION 1, FAT_LOAD_PAYLOAD_NAME .img. Не будем использовать WATCHDOG_ENABLE и EXE_ON_FPGA (мы же не собираемся загружать Preloader с FPGA).



Здесь очень тонкий момент, на котором я попался и целый месяц искал проблему, когда только знакомился с SoC. Serial Support означает, что уже Preloader будет использовать UART модуль для вывода диагностических сообщений прямо во время загрузки. Semihosting означает, что во время вывода этих диагностических сообщений, они будут автоматически выводиться в окне debugger при отладке. Использование в самой программе этой функции очень удобно, она позволяет выводить в окно debugger все, что написано в функции printf без дополнительного написания кода. Если в настройках QSYS в HPS не указать использование UART, но поставить галочку Serial Support в BSP — такой Preloader работать не будет. Если убрать Serial Support, при этом оставив Semihosting, также ничего работать не будет, по крайней мере у меня так было. Так что можете поэкспериментировать или просто оставьте галочки, если хотите, чтоб все точно работало. Нажимаем Generate, затем Exit.



Теперь поменяем рабочую папку SoC EDS командой cd "<указываем полный путь до генерированных файлов BSP (по дефолту это …software/spl-bsp)>" Для сборки Preloader выполним команду make. Ожидаем. В конце процесса в паке получаем нужный файл preloader-mkpimage.bin.

Это собранный файл, в котором находятся одинаковых 4 образа Preloader. Командой mkpimage, выполненной в SoC EDS, можно разобрать этот файл на составляющие (отдельные образы), а затем собрать уже из других конфигураций (с разными образами Preloader). Другие конфигурации могут быть совершенно разными (разные системы в QSYS), то есть получаем отдельные файлы preloader-mkpimage.bin с 4 одинаковыми образами (по 64кб каждый), разобрали их на составляющие и теперь можно собрать новый preloader-mkpimage.bin с разными образами (например, с разными источниками загрузки). Это делается для надежности. Допустим так случилось, что какая-то сила не позволила загрузиться с первой попытки, с первого образа. Тогда начинается загрузка второго образа, а он, например, у нас немного другой, для аварийной ситуации. Если и этот не удалось загрузить, то переходим к третьему и так далее. Но это уже не предмет нашей задачи, скорее лирическое отступление от темы, так что продолжаем!

Вставим флешку с уже подготовленным разделом A2 и запишем на нее наш preloader-mkpimage.bin командой «alt-boot-disk-util -p -a write –d ». SoC EDS должен указывать на путь к папке где лежит файл Preloader. Все почти готово для написания программы! Лишь создадим header файлы с дефайнами QSYS элементов для удобства. Поменяем путь SoC EDS, указав к файлу прошивки, и выполним команду sopc-create-header-files .sopcinfo. На выходе получаем несколько файлов, посмотрев содержание которых станет понятно зачем они.



Программа


Для написания и отладки программы производитель рекомендует использовать среду DS-5 Eclipse. Рекомендуется запускать Eclipse через SoC EDS командой «eclipse&» (знак "&" в конце команды ставится для того, чтобы окно SoC EDS было активное после открытия Eclipse).
Для тех, кто уже знаком с ARM и когда-либо писал для такой архитектуры программы, сложности заканчиваются. Для незнакомых с ARM сложности продолжаются.

Создадим пустой «C» проект. Там будет окно выбора компилятора, тут каждый волен выбирать и разбираться с тем, что ему больше подходит. Мне, как новичку в ARM больше пришелся по вкусу Arm Compiler 5, главным образом из-за относительно простого синтакиса scatter файлов, используемых для размещение написанной программы в разных частях программы (один только вид синтаксиса linker script у GCC меня пугает). В настройках проекта все выглядит тривиально. Укажу лишь на данную команду.



Сделайте тоже самое. Это конвертирует axf формат в формат bin. Нам потребуется это позже для записи программы на флешку. Напишем уже наконец ее.



Тут даже stdio.h не потребуется. Эта программа просто присваивает содержимое в памяти по адресу KEYS_BASE для адреса LEDS_BASE. Нажав кнопку на плате, светодиод тот час погаснет.



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



Не забудьте прошить плату перед загрузкой программы и отладкой!



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







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



После того, как вы убедились, что программа работает корректно, можно создать образ программы для самостоятельной загрузки. Для этого из папки Debug с проектом DS-5 возмем файл .bin и через SoC EDS конвертируем его в .img формат. Это делается командой «mkimage -A arm -O u-boot -T standalone -C none -a 0x00100000 -e 0x00100000 -n „baremetal image“ -d .bin .img.» где "-a" адрес куда загружать, а "-e" точка входа программы.

Точка входа также может задаваться в самом проекте, если используются вектора прерывания, у нас они не используются, поэтому не задаем. Я ориентируюсь на то, какие адреса я задавал в scatter файле, и пишу такие же в этой команде. Перед выполнением не забудьте указать путь к bin файлу в SoC EDS командой cd "<папка с файлом>". Название img файла должно совпадать с тем, что вы указали в BSP editor в FAT_LOAD_PAYLOAD_NAME.

Теперь просто скопируем файл на флешку в fat раздел, как обычный файл. Вставив флешку в DE1-SoC можно видеть выполнение программы.

Заключение


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

Список литературы


  1. Cyclone V Hard Processor System Technical Reference Manual
  2. Altera SoC Embedded Design Suite User Guide
  3. HPS SoC Boot Guide — Cyclone V SoC Development Kit
  4. Bare Metal User Guide
  5. SoC-FPGA Design Guide
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330974/


Метки:  

Как работает закупщик

Четверг, 15 Июня 2017 г. 16:03 + в цитатник


Задача отдела закупок — обеспечивать наличие товара, формировать ассортиментную матрицу, курить матанализ логистики и вести документооборот про всё это.

Закупщик стремится увеличить прибыль сети за счёт оптимального использования всех возможных ресурсов. Это достаточно хардкорная задача оптимизации, потому что требуется серьёзный матаппарат для предсказания спроса, а затем — для определения, что, как и когда брать. Бывали случаи, что нам было проще закупить товара больше, чтобы получить скидку на объём, отработать праздники, а затем уничтожить «избыточный» товар, который обеспечивал объёмную скидку. Списывать (в том случае мы отдали позиции в детские дома) было выгоднее, чем хранить и медленно продавать — аренда склада тоже имеет цену. Это единственный выброс, обычно всё гораздо спокойнее, но часто приходится лавировать между условиями, что лучше — месячная отсрочка платежа или дополнительные 5% скидки. И ответ далеко не всегда однозначен.

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

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

Работа с поставщиком


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

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

После этого начинаются продажи. Закупка распределяет товар по магазинам (мы, например, в своё время серьёзно ошиблись с моей книгой «Бизнес как игра» по мотивам постов с Хабра — вот тут в посте я описывал, почему люди пошли по рознице, а не в интернет-магазин). Следит за тем, чтобы где надо были презентационные образцы, чтобы автор потенциально хитовой игры проехал по магазинам и поучил продавцов играть сам, чтобы уровень брака ни в жисть не превышал 1% и так далее. Закупка же отзывает товар, если он плохо идёт, она же постоянно бьётся за условия, выгрызает первую экспортную поставку прямо со склада таможни, делает много чего ещё.

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

Дальше — аналитика. Самые главные показатели — это ABC-анализ (доля товара в обороте) и, собственно, скорость оборачиваемости. Если товар не пошёл (от всего поставщика) — то закрывается договор, остатки собираются по сети и возвращаются поставщику, либо передаются в опт (чтобы забрала какая-то другая сеть с хорошей скидкой). У нас так было с игрой «50 оттенков серого» — никто толком не знал, зачем мы её ввели в ассортимент, но предполагалось, что будет дикий спрос к фильму (тогда ещё первому). Мы видели график спроса на книгу «Вий», и решили, что это сработает. С «Вием», кстати, прямо анекдот — тогда в книжных в кинообложке Гоголя разобрали как горячие пирожки, а в обычной, без отсылок к фильму, вообще никак не тронули. Так вот не сработало, а у поставщика в прайсе из игр только эта, и ещё порядка 3500 наименований товаров для сексшопа. Максимум мы могли оттуда ещё подарки на новый год конкурентам купить (как Чичваркин сделал), но мы не настолько злобные.

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

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


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

Сначала ищется товар, а потом разбираемся, у кого мы можем достать и на каких условиях. Хороший поставщик с неинтеренсым товаром — не вариант. Например, «фиджет-кубы» — товар, который собрал кучу денег на Кикстартере, и который китайцы начали производить ещё до окончания кампании. Это абсолютно бесполезная штука, которая стоит 300-1400 рублей в зависимости от качества. На нём кнопки, крутилки, переключатели и т.п., — всё, чтобы занять руки. Ира получила образец, решила, что это самый идиотичный товар в мире и… вдруг подсела.



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

Вторая история — вот эти открытки из Китая и Вьетнама:



Я охотился за ними уже года три, даже пытался уговорить подругу привезти их чемодан. Не для того, чтобы заработать, а просто потому, что они совершенно волшебные, и хочется такой товар поставить в магазин, чтобы люди радовались и удивлялись. В Китае они стоят в розницу 100-200 рублей, во Вьетнаме — 60-150 рублей, а у нас — 450-600. Я готов продавать их за 250 рублей за штуку, ну максимум 300. Выше — ИМХО, уже наглость. И вот поэтому, наверное, мы их не поставим, или поставим очень ограниченной партией.

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

Другие задачи


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

Итого


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

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

Народу со стороны кажется, что у закупщика постоянный праздник — ведь они сидят обложенные кучей игр и часто тестируют товар. Подарки разворачивают. За день три коробки подарков могут рассмотреть и испортить, проверяя на прочность. И на выставках за шторкой у поставщиков бухают. Тяжёлая опасная работа.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/328654/


Метки:  

Стартап дня (май 2017-го)

Четверг, 15 Июня 2017 г. 15:44 + в цитатник

Продолжая серию дайджестов «Стартап дня», сегодня я представляю самые интересные проекты за май. Если хотите ознакомиться с остальными, то прошу в мой блог. Записи доступны в Facebook, ICQ и Телеграм.


1. WayUp


Американская вариация на тему поиска работы для миллениалов — стартап WayUp. Принципиально всё как в старом добром Хедхантере: есть профили соискателей, есть объявления о вакансиях, обе стороны могут искать друг друга по фильтрам, деньги берутся с работодателей, для кандидатов всё бесплатно.


image


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


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


Деньги WayUp берет за подписку, раньше пробовали брать за кандидата, но в итоге сменили модель на более привычную. Инвестиций проект поднял $27,5 млн, из них 18,5 — на днях. Любопытно, что за неизвестную часть «старых» девяти он успел купить клона-конкурента.


2. MasterClass


image


Хотите ли вы поучиться актерскому мастерству? Скорее нет. Хотите ли вы поучиться актерскому мастерству через интернет? Очевидно нет. А если через интернет, но у Дастина Хоффмана? И всего за $90? Кажется, это предложение звучит уже куда интереснее.


Так MasterClass и работает. Он находит звезд первой величины в каких-то не жизненно необходимых, но приятно-интересных почти любому человеку дисциплинах: кроме Хоффмана это Сирена Вильямс в теннисе, Кристина Агилера в пении, Гордон Рамзи в готовке, и записывает с ними серии видео-уроков о секретах мастерства. Доступ к урокам продается за те самые $90 (часть из них, естественно, достается преподавателю), кроме видео студент получает домашние задания и кросс-проверки их другими учениками. Да, в 2017-ом году можно задавать и проверять домашние задания даже по теннису — надо просто свои движения на видео записывать, а потом в систему загружать.


Некоторым везунчикам задания проверяет звезда, их ошибки освещаются публично для всех обучающихся — это и количество контента в курсе увеличивает, и некую лотерейную мотивацию при покупке создает, «вдруг и на меня сама Сирена посмотрит!»


Курсы в систему добавляются медленно, сейчас их всего четырнадцать, год назад было семь, два года назад — пять. Исходя из динамики предыдущих инвестиций, выходит, что стоимость производства роликов и разовый гонорар звезде — где-то миллиона полтора долларов за курс, а последнего раунда в $35 миллионов хватит ещё на 25 новых тем.


3. Tagged


image


Стартап Tagged сам по себе совершенно неинтересен. Это старый дейтинговый сервис, нечто типа нашей дамочки.ру из начала нулевых (или конца девяностых?..). Мамбоподобная механика сочетается с нехитрыми играми, сейчас ещё что-то типа Тиндера рядом добавили, все обвешано-перевешано рекламой и платными функциями, но свои пользователи находятся и свои деньги оставляют, проект уже десять лет как прибыльный. Таких в сети много, знакомства — тема вечная, а существование лидера не убивает возможности для выживания аутсайдеров. Впрочем, всем бы быть такими аутсайдерами — выручка у них порядка 50 миллионов долларов в год, тема не только вечная, но и богатая.


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


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


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


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


4. Upskill


image


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


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

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


5. Carvana


image


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


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


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


Всё начинается с безграничного выбора. Сейчас на сайте выставлено 7000 автомобилей, доступных каждому жителю зоны доставки. Это, конечно, немного по сравнению с любой доской объявлений, но куда больше, чем может предложить дилер в своем салоне. В каждую машину можно залезть внутрь с помощью красивой 3D-модели на Flash. 2017-ый год, стартап, Flash — сочетание выглядит несколько странно, но ни на каком auto.ru никогда не будет и такого.


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


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


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


Через интернет авто можно и продать, но цены будут не самыми лучшими, и это не слишком популярная возможность. Основной источник автомобилей для Carvana — дилерские B2B-аукционы и подобные олдскульные инструменты. Экономика стартапа пока не сходится, он уже научился извлекать прибыль из каждого проданного автомобиля, но общие расходы это не окупает. Впрочем, ежегодный рост продаж около 300 %, эффект от каждой продажи тоже улучшается, ещё пара лет — и компания в целом должна стать прибыльной. За 2016-й год через Carvana прошло около двадцати тысяч автомобилей, капитализация стартапа сейчас $1,7 млрд, до IPO в компанию вложили $300 млн инвестиций.

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

https://habrahabr.ru/post/330926/


Метки:  

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

Четверг, 15 Июня 2017 г. 15:35 + в цитатник
image

С 23 по 25 июня в Университет Иннополис приедут 589 человек из 50 регионов страны на Всероссийскую Робототехническую Олимпиаду. Состязания разделены на 8 категорий, среди которых две новые — соревнования автономных беспилотных летательных аппаратов и роботизированный тетрис.

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

Руководитель Лаборатории интеллектуальных робототехнических систем Александр Климчик:
«Основная сложность заключается в удержании баланса летательного аппарата и чётком следовании заданной траектории. Дополнительные сложности возникают, когда летательный аппарат должен обходить препятствия в отличном от горизонтального положения. Участники подобных соревнований отрабатывают навыки пилотирования летательных аппаратов, знакомятся на практике с основными законами физики и находят новые конструктивные и программные решения для летательных аппаратов, которые затем могут внедряться в серьезные проекты».

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

«Летательные аппараты могут быть разной конструкции: самолеты, винтовые аппараты (вертолеты и мультикоптеры), махолеты и дирижабли. У нас нет ограничений на используемую платформу, поэтому участники сами решают, из чего делать аппараты. Размер — до 1 метра в каждой проекции, вес — до 500 грам, скорость — до 10 метров в секунду. На робототехнической олимпиаде в нашей стране подобные соревнования дронов пройдут впервые», — рассказывает руководитель отдела проектных олимпиад Университета Иннополис Алексей Хабибуллин.

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

Олимпийцы поборются за медали в номинациях: «Природно-ориентированный туризм»; «Углеродная нейтральность»; «Чистая и возобновляемая энергия»; TetraStack; «Футбол роботов»; «Роботы для устойчивого развития»; «Персональные интеллектуальные робототехнические системы для обеспечения комфортной жизнедеятельности»; «Манипуляторы: сортировка»; «Локализация: карта»; SLAM (определение положения автономными устройствами на неизвестной карте); «Автотранспортные», «Водные» и «Летательные интеллектуальные робототехнические системы».

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

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

Университет Иннополис проводит Всероссийскую Робототехническую Олимпиаду с 2014 года. В прошлом году на площадке ИТ-вуза собралось 557 детей из 51 региона страны. Победители олимпиады участвовали в Федеральных учебно-тренировочных сборах на базе университета, где была сформирована сборная России на Всемирную олимпиаду роботов. На международных соревнованиях в индийской столице Нью-Дели наши спортсмены завоевали 4 медали в «Футболе роботов», «Творческой» и «Основной» категориях. В 2017 году финал Всемирной олимпиады роботов пройдет в Коста-Рике.

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

https://habrahabr.ru/post/330972/


AgeHack — первый онлайн-хакатон по продлению жизни на платформе MLBootCamp

Четверг, 15 Июня 2017 г. 15:17 + в цитатник


Сегодня, 15 июня, стартует чемпионат на платформе ML Boot Camp, посвященный проблемам здравоохранения и долголетия человечества. Чемпионат организован нами совместно с Insilico Medicine в сотрудничестве с Республиканским центром электронного здравоохранения при Министерстве здравоохранения Республики Казахстан. О том, почему это не очень обычный для нас конкурс — под катом.


Это уже третий по счету чемпионат в этом году, но он совершенно особенный по очень многим причинам. Во-первых, это первый чемпионат, который мы проводим не в одиночку — конкурс проводится в тесном партнерстве с Insilico Medicine и Республиканским центром электронного здравоохранения при Министерстве здравоохранения Республики Казахстан.


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


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


В-четвертых, призы. Наши постоянные участники уже в курсе сложившейся схемы «top6 призовых мест + 50 сувенирных». Но есть небольшой сюрприз, об этом ниже.


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


Задача «Наличие ССЗ»


В рамках конкурса участникам нужно предсказать наличие сердечно-сосудистых заболеваний по результатам классического врачебного осмотра. Датасет сформирован из 100.000 реальных клинических анализов, и в нём используются признаки, которые можно разбить на 3 группы:


Объективные признаки:


  1. Возраст
  2. Рост
  3. Вес
  4. Пол

Результаты измерения:


  1. Артериальное давление верхнее и нижнее
  2. Холестерин
  3. Глюкоза

Субъективные признаки:


  1. Курение
  2. Употребление Алкоголя
  3. Физическая активность

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


Все показатели даны на момент осмотра. Теперь немного необычного.



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


Данные поделены в соотношении 70/10/20. Тренировочная выборка состоит из 70 тысяч результатов, еще по десяти тысячам считается публичная метрика, доступная участникам в ходе соревнования. Оставшиеся 20 тысяч отправились в скрытую проверочную выборку, подсчет метрики по которой и определит победителей в финале.


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


Призы


Распределение шести призовых мест в этот раз выглядит так:


Top1: MacBook Pro
Top2: NVIDIA 1080ti
Top3: NVIDIA 1060
Top4-5-6: WD My Cloud 6 TB


По традиции, 50 лучших участников получат майки с символикой чемпионата.


Участники с наиболее интересными для организаторов решениями получат возможность стажировки или сотрудничества с Mail.Ru Group, Insilico Medicine и Министерством здравоохранения Республики Казахстан. Кроме того, специальным призом от жюри является поездка в Астану для личной встречи с министром здравоохранения Республики Казахстан.


Сообщество MLBootCamp


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


Регистрация


Чемпионат стартует уже сегодня, в 18:00 по Москве. Регистрируемся здесь. Желаем удачи!

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

https://habrahabr.ru/post/330960/


Метки:  

Облако как «новое электричество»

Четверг, 15 Июня 2017 г. 15:02 + в цитатник
Что только не называли «новым электричеством», отсылая к «старому электричеству» как к чему-то, бесспорно прогрессивному, даже революционному, чье значение не открывалось сразу и всем. Облачные технологии сравнивают с электричеством не случайно: по сравнению с «заземленными» технологиями, они как лампочка и лучина. Этим интервью мы открываем мини-серию публикаций, доказывающих, что облако лучше, чем телега сервер под столом. О собственном незаурядном опыте использования облачных технологий рассказывает Станислав Желязков, главный консультант Lumagate и активный участник сообщества systemcentercentral.com. Читать далее

https://habrahabr.ru/post/330806/


Метки:  

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

Четверг, 15 Июня 2017 г. 14:36 + в цитатник
Измерять эффективность рекламных кампаний по закупке трафика все более или менее научились. Многие знают, как посчитать ROI и умеют применять когортный анализ. Но когда вы добавляете ретаргетинг, все становится сложнее.

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




Чистый User Acquisition


Рассмотрим для начала простой пример, когда мы делаем только User Acquisition, без ретаргетинга. Допустим, у вас приложение по доставке еды. В январе вы привлекли 10,000 установок за $10,000. Всего в течение года 600 пользователей из этих 10,000 сделали первый заказ (конверсия 6%). Кто-то из этих пользователей сделал всего 1 заказ за год, кто-то 20, но в среднем получилось, что каждый из этих 600 пользователей сделал 4 заказа за год. Средний заработок с одного заказа получился $5. ROI по рекламной кампании за первый год составил 120%.



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

ROI 120% это хорошо, но хочется больше, и вы решаете применить ретаргетинг. Строго говоря, у вас есть два варианта, на кого таргетироваться:

  1. На тех, кто не сконвертировался даже в первый заказ (будем растить C1).
  2. Тех, кто сделал первый заказ (будем растить Orders per user).

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

Чистый ретаргетинг


Для начала рассмотрим пример, когда мы таргетируемся на пользователей, которые уже точно не сконвертируются сами. Например, мы привлекли 10,000 пользователей год назад. 600 из них сконвертировались в первые заказы. Оставшиеся 9,400 с тех пор так ни разу и не сделали первый заказ. Скорее всего, они так никогда и не сделают этот заказ, если мы ничего не предпримем, потому что прошло уже много времени. В этом случае мы готовы платить за возвращение этих пользователей практически столько же, сколько за нового пользователя.

Вот как будет выглядеть наша модель в этом случае:



Мы разделили 10,000 пользователей, привлеченных год назад, на две группы: 600 и 9,400. Во второй группе мы считаем косты на трафик равными нулю — ведь до этого мы уже потратили $10,000 и получили 600 платящих пользователей. Оставшиеся 9,400 пользователей — побочный эффект от этой кампании.

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

User Acquisition + Retargeting


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

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

Вот как будет выглядеть в этом случае наша модель:



Здесь мы можем сравнить и увидеть, что в итоге CAC (Customer Acquisition Cost) в группе с ретаргетингом получился ниже, чем в группе без ретаргетинга, и ROI выше. В этом случае можно говорить о том, что ретаргетинг эффективен.

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

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

Что еще важно учитывать


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

2. Если затраты на ретаргетинг находятся в пределах 1-2% от общего бюджета, то A/B тест проводить нет смысла — на таких маленьких цифрах вы ничего не увидите. Однако при таких объемах в большинстве случаев можно пренебречь и каннибализацией.

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

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

5. Опасно смешивать разные когорты пользователей. Те, кто пришел к нам в этом месяце и те, кто пришел 12 месяцев назад — разные аудитории. За тех, кто пришел давно, и до сих пор не сделал ни одного заказа, мы готовы платить как за нового пользователя. За тех, кто пришел недавно, мы готовы платить меньше, потому что они вероятно сконвертируются и так.

Заключение


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

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

https://habrahabr.ru/post/330970/



Поиск сообщений в rss_rss_hh_new
Страницы: 1437 ... 1008 1007 [1006] 1005 1004 ..
.. 1 Календарь