...

суббота, 21 апреля 2018 г.

[Из песочницы] Export to Excel from JS

Введение


Всем привет! Сегодня я опишу довольно тривиальную, но ни где не освященную тему экспорт из страницы данных в Excel. На мое удивление сам процесс не описан ни где в полной мере и так приступим.

Все написано до нас


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

Все написано до нас подход 2


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

Что нужно:

  • Современный браузер (проверьте в старых)
  • Отформатированная таблица
  • Colspan, rowspan, border — знание табличной верстки
var tableToExcel = (function() {
                var uri = 'data:application/vnd.ms-excel;base64,'
                , template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head>{table}'
                , base64 = function(s) { return window.btoa(unescape(encodeURIComponent(s))) }
                , format = function(s, c) {              
                        return s.replace(/{(\w+)}/g, function(m, p) { return c[p]; }) 
                }
                , downloadURI = function(uri, name) {
                    var link = document.createElement("a");
                    link.download = name;
                    link.href = uri;
                    link.click();
                }

                return function(table, name, fileName) {
                        if (!table.nodeType) table = document.getElementById(table)
                                var ctx = {worksheet: name || 'Worksheet', table: table.innerHTML}
                        var resuri = uri + base64(format(template, ctx))
                        downloadURI(resuri, fileName);
                }
        })();  

    // params: element id, sheet name, file name
    tableToExcel('resultTable','Смета', 'Ремрайон_смета.xls');


Форматирование


Оформите саму сетку в таблице с помощью атрибутов colspan, rowspan, добавьте стили и после этого вызывайте генерацию файла.

В заключение


Если понравилась статья, могу ответить в комменты после получения приглашения.

Let's block ads! (Why?)

Как IaaS помогает программистам-фрилансерам: опыт 1cloud

По данным глобального опроса Payoneer, программисты-фрилансеры зарабатывают больше многих других специалистов, работающих «на удаленке». Достаточно вспомнить историю бывшего инженера Google Джеймса Найта (James Knight). После ухода из компании 27-летний девелопер смог увеличить свой заработок вдвое, параллельно путешествуя с женой по Европе.

Фриланс постепенно становится не просто «символом дауншифтинга» — разработчики-фрилансеры зарабатывают не хуже своих коллег из корпораций, зачастую работая с большим комфортом. Но популярность этого формата приводит к растущей конкуренции среди самих фрилансеров — к примеру, на фриланс-бирже Toptal.com отбор проходят лишь 3% кандидатов.

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

Одной из таких технологий является IaaS.


/ Flickr / Wall Boat / PD

1. Тестирование в облаке: качественный QA позволяет не потерять клиентов


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

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

Помочь с решением этой проблемы способен IaaS-провайдер, который дает возможность быстро развернуть необходимую тестовую среду. Технический специалист и резидент Quora Спенсер Грин (Spencer H. Greene), например, отмечает, что нагрузочное тестирование приложений в системе, которая один в один повторяет целевую, позволяет наиболее точно оценить их «работоспособность».

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

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

Согласно исследованию Gartner, в 2015 году уже 80% организаций в той или иной степени задействовали облачные структуры для своих проектов. Разработка и тестирование в облаке постепенно становятся естественной практикой в ИТ-индустрии, а потому скоро могут стать стандартом и для фрилансеров.

2. Масштабирование: когда клиентом становится крупная компания


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

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

IaaS-провайдеры также предоставляют дополнительные сервисы, которые могут помочь разработчику еще лучше адаптироваться под конкретную задачу. Например, в 1cloud существует услуга «Объектное хранилище», с её помощью клиенты могут хранить практически неограниченное количество данных. Это могут быть как резервные копии файлов, так и статическое содержание веб-сайтов или записи систем видеонаблюдения (если вы, например, работаете над созданием систем обеспечения безопасности).

3. Удаленный доступ: на случай, если дома «отключили свет»


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

В конце января один из хостинг-ресурсов LKML — LKML.org — оказался недоступен. Сервер, на котором размещался сайт, стоял дома у разработчика проекта Джаспера Спаанса (Jasper Spaans). Когда Джаспер находился в отпуске, у него дома отключили свет, и из-за скачка напряжения материнская плата сервера сгорела. Поэтому он не смог подключиться удаленно и поднять сайт.

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

  1. Разработчик не тратит время на настройку защищенного удаленного доступа.
  2. Инфраструктура облачного провайдера обладает высокой доступностью — 99,9% и более. При этом провайдеры, в том числе и 1cloud, предлагают денежную компенсацию в случае нарушения заявленных показателей доступности и требований SLA.

Причем IaaS-провайдер может обеспечить несколько вариантов удаленного доступа на случай неработоспособности одного из них. Недавно Microsoft заявили об обнаружении уязвимости в протоколе удаленного рабочего стола (RDP). Компания выпустила «заплатку», однако она блокирует доступ к системе, на которой патч не был установлен. При этом справедливо и обратное — обновлённая операционная система «отказывается» показывать свой рабочий стол не обновлённому RDP-клиенту.

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

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


/ Flickr / Seeweb / CC BY-SA

4. Защита данных: бэкапы сохраняют время и нервы


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

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

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

5. Коллаборация: если нужно работать над одним проектом в команде


Согласно исследованию Intuit, к 2020 году в США фрилансеры составят 40% рабочего населения. Малые бизнесы будут формировать целые сети фрилансеров, нанимая «удаленых специалистов» из самых разных сфер. В то же время 80% больших корпораций планируют значительно увеличить использование нештатной рабочей силы.

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

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

P.S. Ранее мы рассказывали, как компании сэкономить деньги в облаке, и c какими трудностями веб-студий и франчайзи «1С» помогает справиться IaaS-провайдер.

Let's block ads! (Why?)

[Из песочницы] Прогулка по быстрому, безопасному и почти законченному веб-сервису на Rust

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

Изображение: Pexels

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

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

Швеция и отказ от наличных


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

Источник: Forex Bonuses

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

Главные тезисы противников «цифровизации» финансов


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

Его поддерживает бывший депутат Европарламента от Пиратской Партии Швеции Кристиан Энгстрем, который убежден в том, что «если вы контролируете сервера Visa и MasterCard, Швеция у вас в руках».

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

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

Перспективы


Согласно опросам общественного мнения, в настоящий момент четверть граждан Швеции (25%) поддерживает полный отказ от наличных денег, в то время как почти семеро из десяти опрошенных предпочли бы сохранить возможность пользоваться различными средствами оплаты. Тем временем, парламент страны проводит внутренний анализ предложенных центробанком законодательных инициатив — депутаты рассмотрят все возможные сложности, связанные как с использованием наличных, так и с отказом от них.

Другие материалы по теме финансов и фондового рынка от ITI Capital:


Let's block ads! (Why?)

[Перевод] Составляем карту всего Интернета при помощи кривых Гильберта

Интернет велик. Очень велик. Вы просто не поверите, насколько умопомрачительно он велик. В смысле, вам может показаться, что диапазон блоков /22, который вы получили как локальный интернет-регистратор (LIR), это очень много — но в масштабах всего Интернета это так, орешки.

Конечно, на самом деле, он оказался не настолько уж и велик — не просто же так нам потребовался IPv6. Однако, это уже другая история.

Дело в том, что IPv4 (самая широко используемая версия протокола IP) задает лимит адресов, равный 2³². Это означает, что у вас есть примерно 4,2 миллиарда IP-адресов, с которыми вы можете работать — хотя по правде это не совсем так, поскольку крупные секции недоступны для использования:

Диапазон IP Применение
0.0.0.0/8 Локальная система
10.0.0.0/8 Локальная LAN
127.0.0.0/8 Loopback
169.254.0.0/16 Link Local
172.16.0.0/12 Локальная LAN
224.0.0.0/4 Мультивещание (Multicast)
240.0.0.0/4 “Для использования в будущем”
Диапазоны адресов (показанные в виде записи с применением бесклассовой адресации, CIDR) перечисленные выше, для нас «убираются» — а это 588 316 672 адресов, или примерно 13% от всего количества адресов.

Однако, учитывая, что у нас остается 3 706 650 624 адресов, это вроде бы не так уж и много, и находится в идеальной достижимости для отправки пакета каждому из них.

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

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

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

Днем позже я отправил 3,7 миллиарда пакетов и получил большущий текстовый файл. Теперь нам остается лишь найти способ нарисовать эту карту!

Знакомимся с кривыми Гильберта


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

К счастью, математика спешит нам на помощь — на этот раз в виде параметрических кривых Пеано (space filling curves):

image

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

image

Еще больше времени у меня ушло, прежде чем я догадался, что мы снова можем отобразить эту же анимацию в одном измерении, «распутав» ее:

image

В общем, теперь когда мы разобрались, как работают эти графики, можем применить их к IP-адресам.

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

cat ping.txt | pcregrep -o1 ': (\d+\.\d+\.\d+\.\d+)' | ./ipv4-heatmap -a ./labels/iana/iana-labels.txt -o out.png

Эта команда отрисует кривую Гильберта при помощи градинта, показываещуго, как много систем находятся онлайн в тех /24

И так, позвольте представить вам — карта IPv4 Интернета по состоянию на 16 апреля 2018 года:

image

Вы можете кликнуть по изображению и открыть несжатую версию в полном разрешении — только учтите, что весит оно 9 Мб.

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

image

В 2012 году RIPE еще даже не трогала 185.0.0.0/8, позже это станет диапазоном, который они будут использовать для последних распределений, и будут давать лишь /22 от пространства IP каждому новому члену RIPE. Из-за этого диапазон 185.0.0.0/8 выглядит странно на фоне других диапазонов и в нем нет массовых аллокаций, поэтому он выглядит весьма «подтянутым» на фоне всех остальных.

RIPE — не единственные, кто полностью использовали диапазоны за прошедшее время. Ниже мы видим три других различных интернет-регистраторов (RIR), которые потребили свои диапазоны за прошедшие 6 лет:

image

В придачу ко всему этому, я также просканировал несколько диапазонов IP в APNIC (Asia-Pacific Network Information Centre) каждые 30 минут в течение 24 часов. Данные, полученные мною из этого эксперимента, позволяют вам увидеть, как Интернет «дышит» по мере того, как клиенты выходят в онлайн утром и уходят в оффлайн ночью:

image

Самым интересным в этой «гифке» является то, как выглядит динамический пул IP от ISP, показывающий клиентов, выходящих онлайн на короткий промежуток времени, и затем присоединяющихся и получающих новый IP-адрес (вот почему более активные IP-адреса «переезжают» в течение дня):
image

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

image

Let's block ads! (Why?)

Личный опыт: выступления и вебинары VAS Experts о СКАТ, CG-NAT и DPI-системах

Сегодня мы подготовили компактную подборку лекций и вебинаров наших экспертов. В них мы говорим о DPI-системах, платформе СКАТ DPI и тонкостях работы таких решений в сети провайдера.


/ Flickr / rawpixel.com / PD

Обработка потока данных на примере DPI: внутренняя архитектура и решения


Это — выступление Максима Хижинского на конференции C++ CoreHard Autumn 2017. Максим — ведущий инженер-программист C++ нашей компании и известен как один из разработчиков платформы СКАТ DPI, отвечающий за создание функций CG-NAT и BRAS. Конференция, на который выступил Максим проходила в Минске.

В презентации раскрыта суть модели обработки потоков данных Apartment Model, которую используем мы в VAS Experts, и предъявляемые к ней требования (например, исключение понятия «управления потоками»). Максим также говорит о компонентах модели (акторах) и приводит примеры их псевдокода: как они «общаются» между собой, какие методы используют.


Какие еще моменты затрагивает выступление:
  • распараллеливание — почему количество потоков не должно превышать число ядер, и чем грозит создание потоков «на лету»;
  • работа Apartment Model — с одним или несколькими apartments;
  • распределение памяти, отложенные действия и отслеживание внешних событий.

СКАТ для уведомления абонентов: маркетинговые кампании


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

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



О чем еще мы говорим в этом вебинаре:
  • подключение системы глубокого анализа трафика;
  • разметка трафика и расстановка приоритетов;
  • шаблоны поиска (и как их использовать).

Фильтрация по черным спискам Роскомнадзора


Вебинар посвящен спискам Роскомнадзора (HTTP URL, *domain.com, HTTPS URL, IP, Newcamd), и способам блокировки адресов с помощью СКАТ (по SNI, IP или сертификатам SSL). Здесь мы говорим о способах подключения СКАТ — установка «в разрыв», асимметричная схема — и даем рекомендации относительно того, какие схемы подключения СКАТ использовать не стоит.


Что еще обсуждается в презентации:
  • «Ревизор» Роскомнадзора;
  • работа со списками РКН (с примерами);
  • настройка мониторинга СКАТ с помощью шаблона Zabbix.

Вебинар: СКАТ DPI в роли BRAS


Здесь мы говорим о функционале BRAS на базе DPI: поддержке многопользовательского режима, назначении и изменении опций тарифов по времени суток и балансу абонента. Также затрагиваем тему различий L2 и L3 BRAS.

Например, L3 BRAS взаимодействует с абонентами через промежуточные маршрутизаторы, поэтому «не видит» оригинальных MAC-адресов. Выдача IP-адресов по этой схеме осуществляется или статически в настройках сети, или на коммутаторах доступа через DHCP Relay. L2 функционирует на канальном уровне, поэтому для идентификации абонентов использует и IP- и MAC-адреса, а также номера сетей VLAN или QinQ.



Что еще есть в презентации:
  • функции PCRF-сервера как «прослойки» между fastdpi-сервером и radius-сервером;
  • настройка СКАТ для работы в качестве L2/L3 BRAS в сетях провайдера;
  • пошаговый алгоритм внедрения BRAS.

DPI двойного назначения: для бизнеса и государства


В этой презентации покажем, какие типы систем понимаются под термином COPM: телефония (COPM-1), выборочный контроль пользователей интернета (COPM-2) и статистика о пользователе (COPM-3). На примере выделим основные различия между СОРМ-2 и СОРМ-3 и расскажем, как их реализуем мы в VAS Experts с учетом законодательства (в частности, ФЗ-374).



Какие еще темы мы обсуждаем на этом вебинаре:
  • обзор главных игроков DPI-рынка в России и возможности DPI-продуктов;
  • сравнение собственного DPI-движка и готовых зарубежных разработок;
  • архитектура и технологии продукта СКАТ DPI: из чего состоит и как работает;
  • CG-NAT и его возможности: full cone, hairpinning, журналирование трансляций и др.



P.S. Пятерка наших тематических дайджестов на Хабре:

Let's block ads! (Why?)

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

Изображение: Pexels

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

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

Швеция и отказ от наличных


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

Источник: Forex Bonuses

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

Главные тезисы противников «цифровизации» финансов


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

Его поддерживает бывший депутат Европарламента от Пиратской Партии Швеции Кристиан Энгстрем, который убежден в том, что «если вы контролируете сервера Visa и MasterCard, Швеция у вас в руках».

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

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

Перспективы


Согласно опросам общественного мнения, в настоящий момент четверть граждан Швеции (25%) поддерживает полный отказ от наличных денег, в то время как почти семеро из десяти опрошенных предпочли бы сохранить возможность пользоваться различными средствами оплаты. Тем временем, парламент страны проводит внутренний анализ предложенных центробанком законодательных инициатив — депутаты рассмотрят все возможные сложности, связанные как с использованием наличных, так и с отказом от них.

Другие материалы по теме финансов и фондового рынка от ITI Capital:


Let's block ads! (Why?)

Открытая трансляция главного зала DotNext 2018 Piter

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

Какие доклады попали в открытую трансляцию? Полный список с описаниями — под катом, а перед ним отметим пару вещей. По итогам двух предыдущих DotNext Дилан Битти оказался явным зрительским фаворитом, поэтому откроет DotNext выступлением о том, как работают привычные нам технологии. А буквально вчера, когда доклад уже был полностью готов и согласован, Дилан открыл для себя тонкости кодировки KOI8-R и теперь увлечённо переделывает часть презентации!

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


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

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

Dylan Beattie
Дилан — системный архитектор и разработчик, за жизнь успевший поучаствовать во множестве проектов, от небольших вебсайтов до огромных распределенных систем; от легаси с двадцатилетней историей до самых новейших разработок. Сейчас он работает архитектором в Spotlight и занимается решением сложных задач в современных распределенных системах. Параллельно с основной работой Дилан активно участвует в комьюнити, является организатором London .NET User Group и в 2016 году участвовал на множестве конференций, включая Oredev в Швеции, NDC в Лондоне, Сиднее и Осло, Tampere Goes Agile в Финляндии и BuildStuff в Литве и Украине.



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

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

Andrei Alexandrescu
Андрей Александреску — эксперт по множеству вещей, включая дизайн и реализацию программных систем, дизайн языков программирования, библиотек, всех аспектов C++ и D, машинному обучению и обработке естественных языков. Его творческий подход к решению проблем, глубокие знания и харизма сделали Андрея Александреску одним из самых желанных спикеров на конференциях.

Книги и статьи Андрея оставили свой след в истории индустрии. Он является автором таких бестселлеров, как «Modern C++ Design», «C++ Coding Standards» (в соавторстве с Гербом Саттером), «The D Programming Language», а также статьи вроде «Simplify Your Exception-Safe Code — Forever» (в соавторстве с Petru Marginean) и «Mojo: Move of Joint Objects». Полный список статьей есть по ссылке.



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

Sasha Goldshtein

Саша Гольдштейн — CTO в компании Sela Group, региональный директор и MVP в Microsoft, автор книг в Pluralsight и O'Reilly, международный консультант и тренер. Саша — автор книг «Introducing Windows 7 for Developers» (Microsoft Press, 2009) и «Pro .NET Performance» (Apress, 2012), известный блогер и контрибьютор в OpenSource-проекты, автор множества обучающих курсов, включая .NET Debugging, .NET Performance, Android Application Development, и Modern C++. Его работа как консультанта в основном связана с распределенными архитектурами, отладкой продакшна, диагностикой производительности и разработкой мобильных приложений.



Как «всем известно», распределённые транзакции умерли с приходом эры интернета. Книги по MTS, MSDTC, J2EE пылятся в музеях, пока программисты пишут облачные сервисы в борьбе с eventual consistent-хранилищами данных типа Cassandra и Mongo. Для финансовых транзакций остаётся бессмертный SQL.

Команде, работающей над фреймворком Orleans, не привыкать ставить под сомнение общепринятые «истины». Она уже показала, что распределённые объекты при правильном применении могут успешно использоваться для построения эффективных масштабируемых приложений, предложив убедительную альтернативу подходу Erlang/Akka к модели акторов.

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

Сергей Быков

Сергей Быков начинал проект Orleans в Microsoft Research и продолжает руководить его разработкой в open source в подразделении Xbox. До прихода в Research Сергей проработал в различных группах Microsoft, от BizTalk и Host Integration Server до встроенных операционных систем для кассовых терминалов и Bing. Удручающая ситуация со средствами разработки для облачных сервисов и распределённых систем побудила его заняться проектом Orleans с целью качественно повысить производительность труда программистов в этой сфере.



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

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

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

Raffaele Rialdi
Raffaele Rialdi — Senior Software Architect, работающий в том числе как консультант, спикер и тренер. Начиная с 2003 года, он является Microsoft MVP в категории Developer Security. Его увлечение делами сообщества сделало его членом правления UGIdotNET, президентом DotNetLiguria и сооснователем Italian C++ User Group. В данный момент он работает архитектором и разработчиком бэкендов энтерпрайзных проектов с особым фокусом на генерации исходников приложений в области кроссплатформенной мобильной и IoT-разработки, на языках C# и C++.



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

Важно: для понимания доклада необходимо знать основы параллельного программирования, включая Monitors, Semaphores, read-write locks, атомарные операции (Interlocked) и т.д.

Дмитрий Иванов

Техлид JetBrains Rider, разработчик ядра ReSharper: структур данных, кэшей, многопоточности. В компании JetBrains с 2011 года, до этого руководил разработкой серверных решений в Yota. Окончил матмех СПбГУ в 2006 году. Регулярный спикер DotNext.



Для большинства разработчиков использование expression tree ограничивается лямбда-выражениями в LINQ. Зачастую мы вообще не придаем значения тому, как технология работает «под капотом».

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

  • устранение дублирования кода в LINQ;
  • метапрограмирование;
  • кодогенерация;
  • транспиляция;
  • автоматизация тестирования.

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

Максим Аршинов
Соучредитель казанской аутсорс-компании «Хайтек Груп». Кроме ведения бизнеса преподает в «Высшей школе информационных технологий и информационных систем» Казанского федерального университета и пишет на Хабре. По запросу «Как писать тесты» в Google первым результатом выйдет его статья.

Суммарный стаж в программировании и управлении разработкой — 10 лет. За это время прошел путь от программиста в казанской веб-студии до руководителя отдела качества в финансовой индустрии. Работал в организациях из Казани, Москвы, Питера и Антверпена (Бельгия).

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



HTTP/2 не только ускоряет загрузку веб-страниц, но и оптимизирует взаимодействие микросервисов в распределённой системе. Наша инфраструктурная команда использовала HTTP/2 в новом сервисе и столкнулась с интересными проблемами. Вас ждет увлекательная fail story про производительность, магические константы и утечки unmanaged-памяти.

Цель доклада — поделиться опытом использования HTTP/2 для оптимизации межсервисного взаимодействия в условиях .NET framework и Windows.

Доклад будет интересен любителям детективных историй про производительность; разработчикам микросервисов под .NET; людям, которые занимаются нагрузочным тестированием HTTP-сервисов.

Ключевые слова: .NET, C#, HTTP/2, WinDbg, microservice architecture, long polling, load testing.

Евгений Жиров
C# разработчик в инфраструктурной команде СКБ Контур. Занимается разработкой и поддержкой общих библиотек, системы распределенных трассировок, другими высокопроизводительными сервисами.

Интересуется созданием надежных и масштабируемых HTTP-сервисов, написанием производительного кода, C# и .NET, робототехникой и IoT. Евгению нравится делиться знаниями с другими, поэтому он прокачивает навык публичных выступлений и преподает несколько курсов для студентов в УрФУ.



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

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

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

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

  • Использование метрик для обнаружения ошибки;
  • Что мы можем или не можем получить из профилировщика, на основании чего можно делать хорошие предположения;
  • Погружение в структуры данных CLR с помощью декомпилятора, WinDBG и SOS, для проверки предположений;
  • Автоматический анализ дампов памяти с помощью ClrMD, и то, как писать свои собственные инструменты, когда WinDBG оказывается несостоятельным.

Christophe Nasarre / Criteo

Christophe Nasarre 25+ лет работал над разработкой и поставкой софта для стека Microsoft, был техническим редактором в MSPress, Addison-Wesley и других издательствах с 1996 года, участвуя в написании таких книг, как «CLR via C#» и последних редакция «Windows Internals».

Обычно он рассказывает об инструментах и полезных идеях о .NET и Windows и ведет свой блог. У него есть опыт докладов как внутри Microsoft, так и на публичных событиях вроде Microsoft TechDays.

Kevin Gosse / Criteo

Kevin Gosse использует технологии Microsoft .NET в течение 10 лет, он занимался разработкой серверов, клиентов и мобильных приложений. Сейчас в Criteo он занимается вопросами масштабирования, отладки и оптимизации.



Микрооптимизации в движке хранения RavenDB vNext критичны для достижения 50К+ запросов на запись в секунду на среднестатистическом сервере. В этом докладе мы рассмотрим использование новых интринсик, добавленных в CoreCLR 2.1 в контексте реальных примеров узких мест на критическом пути выполнения. Будут затронуты довольно хардкорные темы вроде архитектуры процессоров и её влияния на поведение кэшей (коэффициент промахов-попаданий, пойзонинг), префетчинг и т.п. Доклад предназначен для инженеров, занимающихся микрооптимизациями в высокопроизводительных приложениях.

Federico Lois
Federico — сооснователь R&D-компании Corvalius и аналитической компании Coidealike. Он работал над производительностью алгоритмов в течение последних десяти лет, включая как обычные процессоры, так и специализированное железо вроде GPU. Он имеет опыт в различных сферах, начиная от практики повышения производительности банковских приложений, вплоть до оптимизации движков баз данных.



.NET Core — это «новый» способ сборки .NET-приложений, хорошо работает с облаками и контейнерами. В докладе посмотрим, как можно использовать эти технологии для сборки приложений, базирующихся на контейнизированных микросервисах. 20% слайдов и 80% кода.

Edwin van Wijk
Эдвин работает в IT с 1999, сейчас — solution architect в Info Support. Microsoft MVP в категории Visual Studio and Development Technologies.



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

Виталий Егоров

Специалист по связям с общественностью российской частной космической компании Dauria Aerospace. Блогер Zelenyikot, популяризатор космонавтики в интернете, основатель сообществ «Открытый космос» в соцсетях суммарным количеством более 1 млн подписчиков. В 2013 году сумел обнаружить на поверхности Марса посадочный модуль советской автоматической межпланетной станции «Марс-3». Инициатор проекта создания лунного микроспутника для съемки мест посадок «Аполлонов» и «Луноходов».


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

Let's block ads! (Why?)

[Перевод] Составляем карту всего Интернета при помощи кривых Гильберта

Интернет велик. Очень велик. Вы просто не поверите, насколько умопомрачительно он велик. В смысле, вам может показаться, что диапазон блоков /22, который вы получили как локальный интернет-регистратор (LIR), это очень много — но в масштабах всего Интернета это так, орешки.

Конечно, на самом деле, он оказался не настолько уж и велик — не просто же так нам потребовался IPv6. Однако, это уже другая история.

Дело в том, что IPv4 (самая широко используемая версия протокола IP) задает лимит адресов, равный 2³². Это означает, что у вас есть примерно 4,2 миллиарда IP-адресов, с которыми вы можете работать — хотя по правде это не совсем так, поскольку крупные секции недоступны для использования:

Диапазон IP Применение
0.0.0.0/8 Локальная система
10.0.0.0/8 Локальная LAN
127.0.0.0/8 Loopback
169.254.0.0/16 Link Local
172.16.0.0/12 Локальная LAN
224.0.0.0/4 Мультивещание (Multicast)
240.0.0.0/4 “Для использования в будущем”
Диапазоны адресов (показанные в виде записи с применением бесклассовой адресации, CIDR) перечисленные выше, для нас «убираются» — а это 588 316 672 адресов, или примерно 13% от всего количества адресов.

Однако, учитывая, что у нас остается 3 706 650 624 адресов, это вроде бы не так уж и много, и находится в идеальной достижимости для отправки пакета каждому из них.

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

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

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

Днем позже я отправил 3,7 миллиарда пакетов и получил большущий текстовый файл. Теперь нам остается лишь найти способ нарисовать эту карту!

Знакомимся с кривыми Гильберта


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

К счастью, математика спешит нам на помощь — на этот раз в виде параметрических кривых Пеано (space filling curves):

image

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

image

Еще больше времени у меня ушло, прежде чем я догадался, что мы снова можем отобразить эту же анимацию в одном измерении, «распутав» ее:

image

В общем, теперь когда мы разобрались, как работают эти графики, можем применить их к IP-адресам.

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

cat ping.txt | pcregrep -o1 ': (\d+\.\d+\.\d+\.\d+)' | ./ipv4-heatmap -a ./labels/iana/iana-labels.txt -o out.png

Эта команда отрисует кривую Гильберта при помощи градинта, показываещуго, как много систем находятся онлайн в тех /24

И так, позвольте представить вам — карта IPv4 Интернета по состоянию на 16 апреля 2018 года:

image

Вы можете кликнуть по изображению и открыть несжатую версию в полном разрешении — только учтите, что весит оно 9 Мб.

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

image

В 2012 году RIPE еще даже не трогала 185.0.0.0/8, позже это станет диапазоном, который они будут использовать для последних распределений, и будут давать лишь /22 от пространства IP каждому новому члену RIPE. Из-за этого диапазон 185.0.0.0/8 выглядит странно на фоне других диапазонов и в нем нет массовых аллокаций, поэтому он выглядит весьма «подтянутым» на фоне всех остальных.

RIPE — не единственные, кто полностью использовали диапазоны за прошедшее время. Ниже мы видим три других различных интернет-регистраторов (RIR), которые потребили свои диапазоны за прошедшие 6 лет:

image

В придачу ко всему этому, я также просканировал несколько диапазонов IP в APNIC (Asia-Pacific Network Information Centre) каждые 30 минут в течение 24 часов. Данные, полученные мною из этого эксперимента, позволяют вам увидеть, как Интернет «дышит» по мере того, как клиенты выходят в онлайн утром и уходят в оффлайн ночью:

image

Самым интересным в этой «гифке» является то, как выглядит динамический пул IP от ISP, показывающий клиентов, выходящих онлайн на короткий промежуток времени, и затем присоединяющихся и получающих новый IP-адрес (вот почему более активные IP-адреса «переезжают» в течение дня):
image

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

image

Let's block ads! (Why?)

[Перевод] Врываемся в 2018 год с очередным большим релизом: выпуск версии 11.3 языка Wolfram Language и Mathematica

[Из песочницы] Автоматизация UI-тестирования на PhoneGap. Кейс платежного приложения

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

Но довольно метафор.

Дано: PhoneGap-приложение с iframe, внутри которых загружается сторонний сайт; стаж тестировщика 1,5 года; стаж программиста 0 лет.

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

Решение: много костылей, регулярные обращения к программистам за помощью.

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

О проекте


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

Компания, где я работаю QA-специалистом, разработала для сервиса кроссплатформенное мобильное приложение. Клиенту нужно было сделать MVP, чтобы проверить ряд гипотез, и «кроссплатформа» была единственным способом быстро и недорого исполнить это желание.

Почему возникла задача автоматизировать UI-тесты?


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

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

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

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

А всё ли можно автоматизировать?


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

Как мы представляли себе реализацию


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

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

Я толком не представлял, что получится.

Начал с того, что составил тест-кейсы для проверки оплаты:

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

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

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

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


Звучало неплохо, но только звучало.

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


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


    В первый раз скрипт выполняет эти действия и у него получается то, что нужно. Но когда вы снова запускаете скрипт, он переключает клавиатуру с кириллицы на латиницу, не может найти на клавиатуре русскую букву и падает. Поэтому скрипту нужно было задавать условия с помощью кода, например, «попытайся найти русскую букву, если ты не можешь, переключи клавиатуру». Вроде бы простая вещь для того, кто давно программирует, но не для меня на тот момент.
  • Скрипт не понимает, прогрузился ли вызываемый элемент, открылся ли он, видно ли его; он просто нажимает на кнопки, которые записал рекордер. Поэтому к скрипту нужно где-то добавлять задержки, скролл или ещё какие-то дополнительные действия, а это снова требует опыта в программировании.
  • Отсутствие ID у элементов. Для чего они нужны? Допустим, у вас в приложении есть кнопка login. По названию этой кнопки скрипт может не понять, что вы хотите нажать именно на неё. Наличие ID у элементов решает эту проблему.

Appium может найти элемент по названию, но бывает так, что у элемента даже его нет. Например, в приложении есть корзина, и кнопка корзины обозначена иконкой; она не подписана, у неё нет никакого названия — ничего. И чтобы скрипт её нажал, он должен как-то её найти. Без названия это никак нельзя сделать, даже перебором — скрипту не к чему обратиться и он не может нажать на кнопку корзины. А если бы у корзины был ID, его бы увидел рекордер в процессе записи действий, и скрипт смог бы найти кнопку. Решение нашлось неоднозначное.

В нативной разработке большинству элементов присваивается ID, к которому рекордер обращается без проблем.

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

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

  • Отсутствие ID у элементов замедляет работу автотестов. Почему? Названия элементов на экране может повторяться (например, два поля ввода имеют одинаковое название, но разное предназначение). А так как тест в поисках нужного элемента перебирает все с определённым названием, он может обратиться не к тому элементу, который ему нужен. Если находить элемент по имени напрямую, проблема не решается, т.к. имя не уникально. Поэтому скрипт должен сравнивать не только названия элементов, но еще и их тип. Из-за всего это тесты проходят медленно.
  • Этот проект нельзя было запускать на эмуляторе — только на физическом девайсе. Для поддержки — это класс для отображения веб-страниц в приложениях WKWebView, используемого в проекте, необходимо, чтобы внутри физического девайса разворачивался localhost. Тесты на эмуляторе работали бы в четыре раза быстрее, но это не наш случай.
  • Рекордер создавал тесты неструктурированно и не в рамках одного проекта. Если бы я писал этот код с нуля, как это делают программисты, то все тесты были бы в одном файле, и каждый из тестов был бы описан отдельным циклом в рамках одного проекта. А рекордер генерирует каждый тест как отдельный файл, и в нем нет циклов. В итоге все тесты я получал как отдельные куски кода, которые я не мог объединить и сделать для них структуру.
  • Отчёт о результатах теста, или логирование. Тут всё просто: его не было. Я знал о существующих средствах вроде py.test или TestNG (зависит от языка программирования, на котором написаны тесты), но они были рассчитаны опять же на структурированный проект, а это снова не про нас. Поэтому подключить что-то готовое было нельзя, а в том, чтобы писать своё, у меня не было опыта совсем. В итоге я пришёл к следующему: того сообщения, которое мне показывал терминал, когда тест падал, вполне достаточно, чтобы понять, какой элемент тест не нашёл и почему.

Я в любом случае проверял ошибку вручную. Логи были, в минимальном своём исполнении, но были. И то хорошо.

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

Итоги


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

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

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

Let's block ads! (Why?)

пятница, 20 апреля 2018 г.

Может поможет

Есть предложение. В связи с блокировками, давайте все массово просто попросим. Я понимаю, смешно. Но что то делать нужно. На https://rkn.gov.ru/ в самом низу есть "«Сообщить об ошибке (Ctrl + Enter)». Давайте писать. Просто писать.
Я писал о docs.mongodb.com.
Присоединяйтесь. Вдруг что то измениться?

Let's block ads! (Why?)

[Перевод] Learn OpenGL. Урок 5.3 — Карты теней

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

with_shadows_and_without

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

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

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


Содержание

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

Карты теней

Идея, лежащая в основе карт теней, достаточно проста: мы рисуем сцену с точки зрения источника света. Всё, что мы видим, освещено, остальное — в тени. Представьте кусочек пола с большим кубом между ней и источником света. Так как источник света "видит" куб, а не кусочек пола, эта часть пола будет затенена.

shadow_mapping_theory

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

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

Возможно Вы уже читали про тест глубины: перевод на хабре, оригинал. Значение в буфере глубины- это глубина фрагмента из точки зрения камеры, ограниченная значениями от 0 до 1. Что если мы отрендерим сцену с точки зрения источника света и сохраним значения глубины в текстуру? Таким образом мы получим наименьшие значения глубины, которые видно с точки зрения источника света. Кроме того, значения глубины показывают поверхности, ближайщие для источника света. Такую текстуру называют картой глубины (depth map) или картой теней (shadow map).

shadow_mapping_theory_spaces

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


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

Прим. переводчика — openGL отсекает поверхности, которые слишком далеко (z > 1) или слишком близко (z < 0 или z < -1 в зависимости от настроек)). Матрица камеры выбирается так, чтобы z координата для объектов на сцене была в этом интервале, иначе мы их не увидим. С математической точки зрения позиции нет, но реально позицией камеры можно считать точку, которая при рисовании отображаеся в маскимально близкую точку по центру экрана

На картинке справа мы видим тот же самый свет, куб и наблюдателя. Мы рисуем фрагмент поверхности в точке P, и нам надо определить, находится ли он в тени. Для этого мы переводим P в координатное пространство источника света T(P). Так как точка P не видна из точки зрения света, её координата z в нашем примере будет 0.9. По координатам точки x,у мы можем заглянуть в карту глубины и узнать, что ближайшая к источнику света точка — С с глубиной 0.4 Это значение меньше, чем для точки P, поэтому точка P находится в тени.

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


Карта глубины

В первом проходе мы сгенерируем карту глубины. Карта глубины — это текстура со значениями глубины, отрендеренная с точки зрения источника света. Мы потом будем использовать её для вычисления теней. Чтобы сохранить отрендеренный результат в текстуру, нам понадобится кадровый буфер (framebuffer): перевод на хабре, оригинал.

Сначала создадим кадровый буфер для рисования карты глубины:

unsigned int depthMapFBO;
glGenFramebuffers(1, &depthMapFBO);

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

const unsigned int SHADOW_WIDTH = 1024, SHADOW_HEIGHT = 1024;

unsigned int depthMap;
glGenTextures(1, &depthMap);
glBindTexture(GL_TEXTURE_2D, depthMap);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
             SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

Создание карты глубины не выглядит сложным. Так как нас интереуют только значения глубины (а не цвета r,g,b,a), мы указываем формат текстуры GL_DEPTH_COMPONENT. Устанавливаем высоту и ширну текстуры 1024*1024 — это будет размер карты глубины.

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

glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

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

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

// 1. сначала рисуем карту глубины
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
    glClear(GL_DEPTH_BUFFER_BIT);
    ConfigureShaderAndMatrices();
    RenderScene();

// 2. рисуем сцену как обычно с тенями (используя карту глубины)
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    ConfigureShaderAndMatrices();
    glBindTexture(GL_TEXTURE_2D, depthMap);
    RenderScene();

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


Пространство источника света

Единственное, что неизвестно в коде выше — что делает функция ConfigureShaderAndMatrices()?
Во втором проходе она работает как обычно — устанавливает соответствующие матрицы вида и проекции для камеры и матрицы модели для объектов. Однако, в первом проходе мы используем иные матрицы для проекции и вида: для рисования сцены с точки зрения источника света.

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

float near_plane = 1.0f, far_plane = 7.5f;
glm::mat4 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane);

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

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

glm::mat4 lightView = glm::lookAt(glm::vec3(-2.0f, 4.0f, -1.0f),
                                  glm::vec3( 0.0f, 0.0f,  0.0f),
                                  glm::vec3( 0.0f, 1.0f,  0.0f));

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

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

glm::mat4 lightSpaceMatrix = lightProjection * lightView;

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


Рендеринг в карту глубины

Когда мы рендерим сцену для источника света, нам нужны только координаты вершин и ничего больше. Для такого простого шейдера (назовём его simpleDepthShader) напишем вершинный шейдер:

#version 330 core
layout (location = 0) in vec3 aPos;

uniform mat4 lightSpaceMatrix;
uniform mat4 model;

void main()
{
    gl_Position = lightSpaceMatrix * model * vec4(aPos, 1.0);
}

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

#version 330 core

void main()
{
    // gl_FragDepth = gl_FragCoord.z;
}

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

Рисование в буфер глубины превращается в следующий код:

simpleDepthShader.use();
glUniformMatrix4fv(lightSpaceMatrixLocation, 1, GL_FALSE, glm::value_ptr(lightSpaceMatrix));

glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
    glClear(GL_DEPTH_BUFFER_BIT);
    RenderScene(simpleDepthShader);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

Функция RenderScene принимает шейдер, вызывает необходимые для рисования функции и утснавливает матрицы модели при необходимости.

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

shadow_mapping_depth_map

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

#version 330 core
out vec4 FragColor;

in vec2 TexCoords;

uniform sampler2D depthMap;

void main()
{
    float depthValue = texture(depthMap, TexCoords).r;
    FragColor = vec4(vec3(depthValue), 1.0);
}

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

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


Рисование теней

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

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;

out VS_OUT {
    vec3 FragPos;
    vec3 Normal;
    vec2 TexCoords;
    vec4 FragPosLightSpace;
} vs_out;

uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform mat4 lightSpaceMatrix;

void main()
{
    vs_out.FragPos = vec3(model * vec4(aPos, 1.0));
    vs_out.Normal = transpose(inverse(mat3(model))) * aNormal;
    vs_out.TexCoords = aTexCoords;
    vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0);
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}

Из нового здесь — дополнительный вектор FragPosLightSpace на выходе вершинного шейдера. Мы принимаем ту же самую lightSpaceMatrix, что использовалась в первом проходе для рисования глубины, и с её помощью переводим вектор в пространство источника света. Вершинный шейдер передаёт во фрагментный одновременно коодринаты вершины и в пространстве текущей камеры (vs_out.FragPos) и в пространстве источника света (vs_out.FragPosLightSpace).

Мы будем использовать фрагментный шейдер, основанный на световой модели Блинна-Фонга. Во фрагментном шейдере мы найдём значение shadow — оно будет равно 1.0, если фрагмент находится в тени и 0.0 для освещённого. Результирующие diffuse и specular цвета (диффузное и зеркальное освещение) будут домножаться на (1.0 - shadow). Тени редко полностью чёрные по причине непрямого освещения, поэтому фоновое освещение будет присутствовать независимо от тени.

#version 330 core
out vec4 FragColor;

in VS_OUT {
    vec3 FragPos;
    vec3 Normal;
    vec2 TexCoords;
    vec4 FragPosLightSpace;
} fs_in;

uniform sampler2D diffuseTexture;
uniform sampler2D shadowMap;

uniform vec3 lightPos;
uniform vec3 viewPos;

float ShadowCalculation(vec4 fragPosLightSpace)
{
    [...]
}

void main()
{
    vec3 color = texture(diffuseTexture, fs_in.TexCoords).rgb;
    vec3 normal = normalize(fs_in.Normal);
    vec3 lightColor = vec3(1.0);
    // ambient
    vec3 ambient = 0.15 * color;
    // diffuse
    vec3 lightDir = normalize(lightPos - fs_in.FragPos);
    float diff = max(dot(lightDir, normal), 0.0);
    vec3 diffuse = diff * lightColor;
    // specular
    vec3 viewDir = normalize(viewPos - fs_in.FragPos);
    float spec = 0.0;
    vec3 halfwayDir = normalize(lightDir + viewDir);
    spec = pow(max(dot(normal, halfwayDir), 0.0), 64.0);
    vec3 specular = spec * lightColor;
    // calculate shadow
    float shadow = ShadowCalculation(fs_in.FragPosLightSpace);
    vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular)) * color;

    FragColor = vec4(lighting, 1.0);
}

Это по большей части копия шейдера, который мы использовали в примере с освещением: перевод на хабре, advanced lighting.

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

Чтобы проверить, находится ли фрагмент в тени, приведём позицию к в пространстве источника света к нормализованным координатам. Когда мы возвращаем позицию вершины в gl_Position в вершинном шейдере, openGL автоматически делит x,y,z на w, чтобы корректно работала перспектива. Так как FragPosLightSpace передаётся не в как gl_Position, нам придётся сделать это деление самим.

float ShadowCalculation(vec4 fragPosLightSpace)
{
    // perform perspective divide
    vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
    [...]
}

Получаем позицию фрагмента в пространстве источника света.


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

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

Текстурные координаты лежат в интервале [0,1], а координаты видимых фрагментов при рендеринге принимают значения в [-1,1]. Приведём их к интервалу [0,1]:

projCoords = projCoords * 0.5 + 0.5;

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

float closestDepth = texture(shadowMap, projCoords.xy).r;

Чтобы получить глубину текущего фрагмента, мы просто берём его координату z в пространстве источника света.

```с
float currentDepth = projCoords.z;

После этого простым сравнением ```currentDepth``` и ```closestDepth``` можно определить, является ли наш фрагмент ближайщим или лежит в тени.

```c
float shadow = currentDepth > closestDepth  ? 1.0 : 0.0;

Весь код функции ShadowCalculation получается таким:

float ShadowCalculation(vec4 fragPosLightSpace)
{
    // perform perspective divide
    vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
    // transform to [0,1] range
    projCoords = projCoords * 0.5 + 0.5;
    // get closest depth value from light's perspective (using [0,1] range fragPosLight as coords)
    float closestDepth = texture(shadowMap, projCoords.xy).r; 
    // get depth of current fragment from light's perspective
    float currentDepth = projCoords.z;
    // check whether current frag pos is in shadow
    float shadow = currentDepth > closestDepth  ? 1.0 : 0.0;

    return shadow;
}

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

shadow_mapping_shadows

Если Вы сделали всё правильно, вы увидите тени на полу и кубиках (впрочем, с некоторыми артефактами). Исходный код демо.


Улучшаем карты теней

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


Муаровый узор

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

shadow_mapping_acne

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

shadow_mapping_acne_diagram

(Прим. пер. — для описания эффекта автор использует термин "shadow acne". Я не нашёл устоявшегося перевода. Я мог бы перевести это как "теневые точки", но в русскоязычном интернете по этому термину всё равно ничего не найти)

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

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

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

shadow_mapping_acne_bias

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

float bias = 0.005;
float shadow = currentDepth - bias > closestDepth  ? 1.0 : 0.0;

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

float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005);

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

shadow_mapping_with_bias

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


Эффект Питера Пэна

(Piter Panning)

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

shadow_mapping_peter_panning

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

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

shadow_mapping_culling

Чтобы убрать эффект Питера Пэна, мы отсекаем фронтальные грани на первом проходе. Обратите внимание, что необходимо включить GL_CULL_FACE.

glCullFace(GL_FRONT);
RenderSceneToDepthMap();
glCullFace(GL_BACK); // возвращаем в первоначальное состояние

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

(Прим. пер.- конкретно в этом примере нет ничего страшного в том, что отсечение полностью уберёт пол, так как ниже него нет объектов, и не важно, есть под ним тень или нет.)

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

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

shadow_mapping_outside_frustum

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

Хотелось бы для таких фрагментов возвращать глубину 1.0 — это значит, что они никогда не будут в тени (так как у видимого объекта глубина не может быть больше единицы). Для этого мы установим цвет края для текстуры и остановим опцию "GL_CLAMP_TO_BORDER"

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
float borderColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);

Теперь, если мы читаем значение из карты глубины по координатам вне интервала [0,1], мы будем получать в ответ глубину 1.0, из за чего значение shadow в шейдере будет 0.0. Теперь сцена выглядит лучше:

shadow_mapping_clamp_edge

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

Для таких областей координата z в пространстве источника света оказывается больше, чем 1.0, в этом случае опция GL_CLAMP_TO_BORDER нам не помогает. Мы сравниваем реальную глубину (которая больше 1.0) со значением из карты глубин (в текстуре глубины хранятся значения не больше 1.0) — поэтому далёкие участки будут в тени.

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

float ShadowCalculation(vec4 fragPosLightSpace)
{
    [...]
    if(projCoords.z > 1.0)
        shadow = 0.0;

    return shadow;
}

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

shadow_mapping_over_sampling_fixed

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


PCF

Percentage-closer filtering

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

shadow_mapping_zoom.png

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

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

Ещё одно (частичное) решение проблемы с зазубренными краями теней — PCF (Percentage-closer filtering), содержащий различные функции фильтрации, которые дают нам мягкие тени. Идея состоит в том, чтобы выбирать несколько значений из карты глубины — каждое с немного отличающимися координатами. Для каждого значения мы проверяем, находимся в тени или нет. Потом усредняем все результаты и получаем красивые мягие тени.

Простая реализация PCF — просто выбрать соседние тексели в карте глубины и усреднить результат:

float shadow = 0.0;
vec2 texelSize = 1.0 / textureSize(shadowMap, 0);
for(int x = -1; x <= 1; ++x)
{
    for(int y = -1; y <= 1; ++y)
    {
        float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
        shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
    }
}
shadow /= 9.0;

Здесь textureSize — вектор из двух чисел, ширины и высоты текстуры глубины. Единица, делённая на этот вектор, даёт размер текселя на текстуре. Мы будем смещаться на этот размер, чтобы читать значения из соседних текселей. В этом примере мы берём 9 значений в окрестности точки (x,y), проверяем на наличие тени и в конце делим на количество проверок, получая усреднённое значение.

Если использовать больше точек и/или изменить texelSize, то можно улучшить качество мягких теней. Ниже картинка с тенями с простым PCF фильтром:

shadow_mapping_soft_shadows

Издалека тени выглядят намного лучше. У них нет резких краёв, но если приблизиться, то всё ещё видно артефакты (впрочем, 9 оттенков тени разнообразнее одного). В целом, PCF даёт хороший результат в большинстве случаев.

Исходный код примера

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


Перспективная проекция vs ортографическая

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

shadow_mapping_projection
Для перспективной проекции позиция источника света реально важна (в отличие от источника направленного освещения). Перспективную проекцию чаще всего используют для точечных источников света, а ортографическую — для направленного света.

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

#version 330 core
out vec4 FragColor;

in vec2 TexCoords;

uniform sampler2D depthMap;
uniform float near_plane;
uniform float far_plane;

float LinearizeDepth(float depth)
{
    float z = depth * 2.0 - 1.0; // Back to NDC
    return (2.0 * near_plane * far_plane) / (far_plane + near_plane - z * (far_plane - near_plane));
}

void main()
{
    float depthValue = texture(depthMap, TexCoords).r;
    FragColor = vec4(vec3(LinearizeDepth(depthValue) / far_plane), 1.0); // perspective
    // FragColor = vec4(vec3(depthValue), 1.0); // orthographic
}

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


Дополнительные ресурсы:


Оригинальная статья

Let's block ads! (Why?)