...

суббота, 21 декабря 2013 г.

Орагнизация TCP/IP стека для МК на основе embedded module Tibbo

В данной обзорной статье речь пойдет об организации TCP/IP стека в вашем проекте на основе встраиваемых модулей от Tibbo Technology.

TCP стек от Tibbo


В большинстве случаев разработчики работают со своим МК через последовательный порт. Это не всегда удобно. Часто возникает задача «общаться» с устройством удаленно через TCP/IP стек. Проблема поднимается не впервые. Например, вот здесь эта задача озвучена и решена с достаточно подробным описанием. В приведенной ссылке даже есть фото и краткое упоминание о модуле Tibbo EM100. Дело в том, что наша компания является официальным представителем Tibbo Technology в РФ. Поэтому немного обидно, что о нашем продукте упомянули вскользь, не раскрыв полного функционала и возможностей продукта. Я прекрасно знаю эту оранжевую коробочку под названием EM100, которая выпускается уже более 10 лет и которая используется на многих объектах наших бескрайних просторов. Но уже много воды утекло с момента релиза данного модуля. Попробуем создать TCP/IP стек для устройства на основе модуля Tibbo, параллельно рассмотрев возможности продукта.


Создание TCP/IP стека.




Для решения данной задачи нам понадобяться:

Embedded Modules (EM) — это небольшие интеллектуальные модули, которые имеют собственный микропроцессор, минимально-необходимую обвязку для быстрого старта, различные встроенные протоколы. Как правило, в EM запущена собственная операционная система, которая не предназначена для общения с человеком. Цель использования EM – это в максимально короткие сроки и с минимальными затратами организовать тот или иной функционал в устройстве.

Семейство продукции Embedded modules от Tibbo Technology – это достаточно мощные модули, на которых уже реализованы физический уровень интерфейса 10/100 BaseT, организована полноценная работа со стеком протоколов TCP/IP, имеется от 1 до 4 последовательных портов UART на CMOS уровне (TTL совместимый). В продаже существуют модификации модулей с интегрированным разъемом Ethernet и без него. Для подключения последнего требуются внешние разъем и трансформаторы или разъем со встроенными трансформаторами. Размеры модулей варьируются, но в целом они не превышают (или превышают незначительно) разъем RJ45.


Я выбрал модель EM1206+RJ203, краткие характеристики:



  • Основан на высокопроизводительном 88МГц ASIC (T1000) контроллере.

  • 100BaseT со встроенной трансформаторной развязкой (при использовании RJ203).

  • До 1024КБ flash-памяти под прошивку, приложения и данные.

  • 2КБ EEPROM для хранения данных.

  • Часы реального времени (RTC)..

  • До 17 линий ввода/вывода общего назначения (включая 8 линий прерываний).

  • Линии контроля двух внешних светодиода состояния.

  • 4 встроенных светодиода:

  • зелезный и красный светодиоды состояния;

  • зеленый и желтый светодиоды статуса Ethernet.

  • Программно-контролируемый встроенный PLL.

  • Надежные схемы сброса питания.

  • Питание: 230мА @ 3.3В (100BaseT режим, PLL включен).

  • Размеры: 33.2x18.1x5.5мм.




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

Последовательный порт, необходимо завести на контроллер вашего устройства. Как я писал выше, на модуле EM1206 реализован CMOS уровень (TTL совместимый), так что линии Rx/Tx можно подключать к ножкам МК напрямую.


image


Удобное ПО для работы




В программной части все еще проще. Запускаем Device Explorer, который автоматически обнаруживает модуль в сети (средствами посылки UDP broadcast запросов), заливаем прошивку. Процесс занимает не более 1 минуты.

image

Запускаем DS Manager, который позволяет задать параметры как последовательного интерфейса, так и параметры хоста:


image


Краткие характеристики прошивки Serial Over IP:



  • Полно- и полудуплексные режимы последовательного порта.

  • Доступные режимы: сервер, клиент и сервер/клиент.

  • Множество других опций для соединений последовательного порта и ethernet.

  • 8КБ на каждый буффер приема/передачи (в каждом направлении по одному буферу).

  • Конфигурация хранится в EEPROM.

  • Детальная индикация состояния через светодиоды.

  • Настройка через RS232 или ethernet (UDP команды, Telnet).

  • Удаленный контроль всех линий ввода/вывода (включая RTS, CTS, DTR и DSR).

  • «On-the-fly» комманды для конфигурации последовательного порта.

  • «Модемные» команды через RS232 для управления сетевым соединением.

  • Прямое управление ADSL модемом.

  • Поддержка UDP, TCP, ARP, ICMP (ping), DHCP, PPPoE, LCP протоколов.

  • Встроенный веб-интерфейс управления.


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


Опционально, при использовании внешнего модуля расширения GA1000, устройства Tibbo позволяют передавать данные по беспроводному протоколу передачи данных WiFi (802.11 b/g). GA1000 элементарно подключается к контроллеру, через утилиту DS Manager можно настроить параметры подключения к точке доступа.


image


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


И это все? А где же интеллект, где же логика?


Embedded modules Tibbo являются свободно программируемыми, при этом процесс отладки и загрузки приложений осуществляется через сеть Ethernet. Программирование осуществляется на объектно-ориентированных событийных языках Tibbo Basic и Tibbo C. Для кодирования используется среда разработки от производителя Tibbo IDE. На устройствах запущена операционная система TiOS, которая обрабатывает приложение, написанное пользователем и скомпилированное в мнемокоды в режиме интерпретации. Модули имеют на борту линии ввода/вывода общего назначения (GPIO), что позволяет использовать embedded module как микроконтроллер.


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

Таким образом, функционал модуля определяется только разработчиком. Если не достаточно прошивки SoI, откройте исходники и добавьте то, что нужно. Или с нуля создайте собственный проект. Например, реализовав протокол вашего контроллера и подцепив к проекту OPC Server (от Tibbo), устройство можно подключить к SCADA системе, а используя программную библиотеку AggreGate Agent появляется возможность подключения устройства к AggreGate Server.


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

Инициализируем последовательный порт:



sub on_sys_init
ser.num=0 'выбираем первый последовательный порт
ser.rxbuffrq(4) 'Резервируем 4*256-16=1008 байт под буфер прием последовательного порта
ser.txbuffrq(4) 'Резервируем 4*256-16=1008 байт под буфер передачи последовательного порта
.....
sys.buffalloc 'Зарезервируем выделенные буферы памяти.

ser.num=0
ser.flowcontrol=DISABLED 'Выключаем управление потоком
ser.baudrate=12 'ser.div9600/(38400/9600) устанавливаем скорость 38400
ser.enabled=YES 'Включаем порт
.......
end sub


Инициализируем сокет:



sub on_sys_init
.....
sock.num=0
sock.rxbuffrq(4)
sock.txbuffrq(4)
.....
sys.buffalloc


sock.num=0
sock.protocol=PL_SOCK_PROTOCOL_TCP
sock.localportlist="2000,3000"
sock.inconmode=PL_SOCK_INCONMODE_ANY_IP_ANY_PORT
sock.targetip="192.168.1.100"
sock.targetport="1001"
end sub


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



sub on_ser_data_arrival
'Событие. Активируется при появлении данных на последовательном интерфейсе.
dim s as string

s = ser.getdata(ser.rxlen)
If val(s) > Critical_value then
sock.num = 0
sock.connect
sock.setdata("Внимание! Превышение параметра: "+s)
sock.send
end if

end sub


Как видно, программирование совсем не сложное.


Если вам не хочется возиться с распайкой, можно использовать готовые отладочные платы, например, EM500EV:


image


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


Замена МК на Tibbo embedded module.




Если в вашем проекте не требуется мгновенных реакций, то Tibbo EM можно использовать как главный микроконтроллер вашего устройства. Модули создавались для максимально удобной работы с ними. Свободное программирование на высокоуровневых языках Tibbo Basic и Tibbo C позволят сократить время на разработку прошивки в разы. Как пример, можно рассмотреть создание собственного контроллера доступа с реле для управления замками или подключения внешней сигнализации, веб-сервером, последовательными портами, беспроводной связью WiFi, клавиатурой и дисплеем (принципиальные схемы можно скачать здесь):

image


Последовательные порты могут использоваться для подключения считывателей RFID или магнитных карт. Реле служат для управления замками. Нужно открыть дверь? Для этого необходимо активировать реле. Из принципиальной схемы мы видим, что реле у нас «висит» на линии DTR0 (или GPIO5), нужно активировать эту линию:



....
io.num = PL_IO_NUM_5
io.enabled = YES
io.state = LOW
....


Наконец, если вам интересно создание собственных устройств автоматизации, посмотрите обзор аппаратной платформы автоматизации TPS от Tibbo.


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


P.S.

От нашей компании предоставляется услуга бесплатного тестирования всей линейки оборудования Tibbo.

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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


Продолжаем изучать Love2d



В предыдущем посте я рассказал как рисовать картинки и, вроде как, объяснил, что к чему в love2d. Сегодня я решил написать змейку, всем, кому интересно, прошу под кат. Запускаем Sublime Text 2, как его настроить для love я говорил в прошлый раз, но сегодня мы будем использовать дебаг, но для этого нужно написать три лишних строки, а именно: Рядом с main.lua, в корне каталога проекта, создаем файл и называем его conf.lua. Этот файл предназначен для настроек игры. В нем пишем такой вот код:

function love.conf(t)--Функция которую вызывает love перед стартом приложения
io.stdout:setvbuf("no")--Из-за особенностей Sublime Text, точнее его консольки нужно вызывать эту функцию,
--иначе все что мы выводим с помощью print(str) будет выведено после закрытия игры.
end




Подробнее об этой функции можно почитать здесь, и здесь.
Ну а теперь код



Подготовим переменные:

local apple, snake, direction, width, height, delay

function love.load()
width = love.graphics.getWidth() / 10 --функция возвращает длину окна на данный момент
print("Width " .. width) --дебаг вывод
height = love.graphics.getHeight() /10 --тоже самое но с высотой
print("Height " .. height)
apple = { x = math.random() % width; --теперь нам нужно яблоко генерируем X-координату
y = math.random() % height; } --и Y..
snake = { } --инициализируем таблицу с координатами змейки
for i = 1, 10 do --первые 10 клеток надо с чего-то начинать
point = { x = 10; --и снова X..
y = 10 + i; } --и Y..
table.insert(snake, point) --вписываем в таблицу
end
direction = "up" --змейка должна куда-то ползти
delay = 0 --змейка не должна быть нервной
end


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


Теперь нам нужно рисовать змейку (Пока что без яблока);



function love.draw()
love.graphics.setBackgroundColor(0, 0, 0) --чистим экран
for k, v in pairs(snake) do --отрисовываем каждую клетку
love.graphics.rectangle("fill", v.x * 10, v.y * 10, 10, 10)
end
end


А теперь должны возникнуть вопросы, а именно:

1) А какого цвета будет змейка

2) (Менее интересный) Что это за функция love.graphics.rectangle?


Ответы:


1) Она будет рисоваться белым, этот цвет ставиться внутри сишного кода перед вызовом love.draw(). Если вам этот цвет можно поставить вот таким вот методом:



love.graphics.setColor(0, 0, 255) --змея будет синей




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

2) Как можно догадаться из названия, эта функция рисует четырехугольник цвета заданным этой функции. Первый аргумент — режим рисования, может быть «fine» или «line». Второй и третий аргумент координаты, Четвертый и пятый — размеры, подробно вот тут.
Можем продолжить



Ура! У нас есть неподвижная змейка, давайте потыкаем ее исправим это, напишем Update функцию:

function love.update(dt)
if delay % 2 == 0 then --лечим нервы
table.remove(snake, table.getn(snake)) --убираем последний элемент и добавляем его в начало

if direction == "up" then --ну тут вроде все ясно, обрабатываем направление
table.insert(snake, 1, { x = snake[1].x;
y = (snake[1].y - 1) % height; })
elseif direction == "down" then
table.insert(snake, 1, { x = snake[1].x;
y = (snake[1].y + 1) % height; })
elseif direction == "left" then
table.insert(snake, 1, { x = (snake[1].x - 1) % width;
y = snake[1].y; })
else
table.insert(snake, 1, { x = (snake[1].x + 1) % width;
y = snake[1].y; })
end
end
delay = delay + 1
end




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

function love.keypressed(key) --здесь мы будем смотреть какая кнопка была нажата
--выглядит стремно, но правильно
if key == "up" then
direction = "up"
elseif key == "down" then
direction = "down"
elseif key == "left" then
direction = "left"
elseif key == "right" then
direction = "right"
elseif key == "escape" then
love.event.quit()
end
end




Теперь мы управляем происходящим, но где цель? Добавим-же яблок:

Во-первых, яблоко у нас есть, но мы его не рисуем, добавим прорисовку в love.draw():

love.graphics.setColor(255, 255, 0) --мне нравиться голд
love.graphics.rectangle("fill", apple.x * 10, apple.y * 10, 10, 10)




А теперь нам надо рости:

if delay % 2 == 0 then --лечим нервы
if apple.x == snake[1].x and
apple.y == snake[1].y then
apple = { x = math.random(width); --вот и поели, генерируем X-координату
y = math.random(height); } --и Y..
else --не сразу ясно, но понятно
table.remove(snake, table.getn(snake)) --убираем последний элемент и добавляем его в начало
end

if direction == "up" then --ну тут вроде все ясно, обрабатываем направление
table.insert(snake, 1, { x = snake[1].x;
y = (snake[1].y - 1) % height; })
elseif direction == "down" then
table.insert(snake, 1, { x = snake[1].x;
y = (snake[1].y + 1) % height; })
elseif direction == "left" then
table.insert(snake, 1, { x = (snake[1].x - 1) % width;
y = snake[1].y; })
else
table.insert(snake, 1, { x = (snake[1].x + 1) % width;
y = snake[1].y; })
end
end
delay = delay + 1


Ползаем, контролируем, поедаем, все готово!


Спасибо, за прочтение.

Исходники, с небольшим аддономом.


Буду благодарен за любые отзывы (:


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


[Из песочницы] Интеграция аутентификации Symfony2 и трекера Jira

Здравствуйте, Хабросообщество. В этой статье хочу рассказать, как можно подружить известный фреймворк Symfony2 и не менее известный трекер Jira.

Зачем связывать Jira и Symfony2?




В компании, где я работаю, возникла необходимость связать систему саппорта и трекер задач через API, чтобы заявки от клиентов могли быть легко преобразованы в тикеты. Первостепенной проблемой, которая встала на нашем пути, была интеграция аутентификации Jira (использовался механизм “Basic Authentication”) и системы безопасности Symfony2. Для понимания механизмов аутентификации и авторизации фреймворка необходимо ознакомиться с официальной документацией: http://symfony.com/doc/current/book/security.html.


Что нужно для создания нового типа авторизации в Symfony2?





  1. Token, который будет хранить введенную пользователем информацию при аутентификации.

  2. Listener, необходимый для проверки авторизованности пользователя.

  3. Provider, непосредственно реализующий аутентификацию через Jira.

  4. User Provider, который будет запрашиваться Symfony2 Security для получения информации о пользователе.

  5. Factory, которая зарегистрирует новый способ аутентификации и авторизации.


Создаем Token




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

<?php

namespace DG\JiraAuthBundle\Security\Authentication\Token;

use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;

class JiraToken extends AbstractToken
{
protected $jira_login;
protected $jira_password;

public function __construct(array $roles = array('ROLE_USER')){
parent::__construct($roles);
$this->setAuthenticated(count($roles) > 0);
}

public function getJiraLogin(){
return $this->jira_login;
}

public function setJiraLogin($jira_login){
$this->jira_login = $jira_login;
}

public function getJiraPassword(){
return $this->jira_password;
}

public function setJiraPassword($jira_password){
$this->jira_password = $jira_password;
}

public function serialize()
{
return serialize(array($this->jira_login, $this->jira_password, parent::serialize()));
}

public function unserialize($serialized)
{
list($this->jira_login, $this->jira_password, $parent_data) = unserialize($serialized);
parent::unserialize($parent_data);
}

public function getCredentials(){
return '';
}
}


Реализация Listener




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

<?php

namespace DG\JiraAuthBundle\Security\Firewall;

use DG\JiraAuthBundle\Security\Authentication\Token\JiraToken;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Psr\Log\LoggerInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener;

class JiraListener extends AbstractAuthenticationListener {
protected function attemptAuthentication(Request $request){
if ($this->options['post_only'] && 'post' !== strtolower($request->getMethod())) {
if (null !== $this->logger) {
$this->logger->debug(sprintf('Authentication method not supported: %s.', $request->getMethod()));
}

return null;
}

$username = trim($request->get($this->options['username_parameter'], null, true));
$password = $request->get($this->options['password_parameter'], null, true);

$request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username);
$request->getSession()->set('jira_auth', base64_encode($username.':'.$password));

$token = new JiraToken();
$token->setJiraLogin($username);
$token->setJiraPassword($password);

return $this->authenticationManager->authenticate($token);
}
}


Авторизация в Jira. Provider




Пришло время самого главного — непосредственной отправки данных в Jira. Для работы с rest api трекера написан простой класс, который подключается в виде сервиса. Для работы с API Jira используется библиотека Buzz.

<?php

namespace DG\JiraAuthBundle\Jira;
use Buzz\Message;
use Buzz\Client\Curl;

class JiraRest {
private $jiraUrl = '';

public function __construct($jiraUrl){
$this->jiraUrl = $jiraUrl;
}

public function getUserInfo($username, $password){
$request = new Message\Request(
'GET',
'/rest/api/2/user?username=' . $username,
$this->jiraUrl
);

$request->addHeader('Authorization: Basic ' . base64_encode($username . ':' . $password) );
$request->addHeader('Content-Type: application/json');

$response = new Message\Response();

$client = new Curl();
$client->setTimeout(10);
$client->send($request, $response);

return $response;
}
}


Provider должен реализовывать интерфейс AuthenticationProviderInterface и выглядит следующим образом:



<?php

namespace DG\JiraAuthBundle\Security\Authentication\Provider;

use DG\JiraAuthBundle\Entity\User;
use DG\JiraAuthBundle\Jira\JiraRest;
use DG\JiraAuthBundle\Security\Authentication\Token\JiraToken;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;

class JiraProvider implements AuthenticationProviderInterface {

private $userProvider;
private $jiraRest;

public function __construct(UserProviderInterface $userProvider, $providerKey, JiraRest $jiraRest)
{
$this->userProvider = $userProvider;
$this->jiraRest = $jiraRest;
}

public function supports(TokenInterface $token)
{
return $token instanceof JiraToken;
}

public function authenticate(TokenInterface $token)
{
$user = $this->checkUserAuthentication($token);
$token->setUser($user);

return $token;
}

public function checkUserAuthentication(JiraToken $token){
$response = $this->jiraRest->getUserInfo($token->getJiraLogin(), $token->getJiraPassword());
if(!in_array('HTTP/1.1 200 OK', $response->getHeaders())){
throw new AuthenticationException( 'Incorrect email and/or password' );
}
$userInfo = json_decode($response->getContent());
$user = new User();
$user->setUsername($userInfo->name);
$user->setBase64Hash(base64_encode($token->getJiraLogin() . ':' . $token->getJiraPassword()));
$user->setEmail($userInfo->emailAddress);
$user->addRole('ROLE_USER');
return $user;
}
}


Как видно из реализации — данные о пользователе хранятся в сущности User. Этого можно не делать, чтобы Doctrine не создавала лишнюю таблицу в базе данных, но в будущем можно в данную таблицу складывать информацию о пользователях из Jira, чтобы подстраховать себя от временной недоступности трекера. Подобная “страховка” выходит за рамки статьи, но может быть весьма полезна.


Предоставление информации об авторизованном пользователе




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

<?php

namespace DG\JiraAuthBundle\User;


use DG\JiraAuthBundle\Entity\User;
use DG\JiraAuthBundle\Jira\JiraRest;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;

class JiraUserProvider implements UserProviderInterface {

private $jiraRest;

public function __construct(JiraRest $jiraRest){
$this->jiraRest = $jiraRest;
}

public function loadUserByUsername($username)
{
}

public function refreshUser(UserInterface $user)
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}

$decodedUserData = base64_decode($user->getBase64Hash());
list($username, $password) = explode(':', $decodedUserData);
$userInfoResponse = $this->jiraRest->getUserInfo($username, $password);
$userInfo = json_decode($userInfoResponse->getContent());

$user = new User();
$user->setUsername($user->getUsername());
$user->setEmail($userInfo->emailAddress);
$user->setBase64Hash($user->getBase64Hash());
$user->addRole('ROLE_USER');
return $user;
}

public function supportsClass($class)
{
return $class === 'DG\JiraAuthBundle\Entity\User';
}
}


Заполнение конфигурации




Для использования созданных классов необходимо их зарегистрировать в конфигурации в виде сервисов. Пример services.yml приведен ниже. Отмечу, что параметр jira_url должен быть определен в parameters.yml и содержать url адрес до Jira.

parameters:
dg_jira_auth.user_provider.class: DG\JiraAuthBundle\User\JiraUserProvider
dg_jira_auth.listener.class: DG\JiraAuthBundle\Security\Firewall\JiraListener
dg_jira_auth.provider.class: DG\JiraAuthBundle\Security\Authentication\Provider\JiraProvider
dg_jira_auth.handler.class: DG\JiraAuthBundle\Security\Authentication\Handler\JiraAuthenticationHandler
dg_jira.rest.class: DG\JiraAuthBundle\Jira\JiraRest

services:
dg_jira.rest:
class: %dg_jira.rest.class%
arguments:
- '%jira_url%'

dg_jira_auth.user_provider:
class: %dg_jira_auth.user_provider.class%
arguments:
- @dg_jira.rest

dg_jira_auth.authentication_success_handler:
class: %dg_jira_auth.handler.class%

dg_jira_auth.authentication_failure_handler:
class: %dg_jira_auth.handler.class%

dg_jira_auth.authentication_provider:
class: %dg_jira_auth.provider.class%
arguments: [@dg_jira_auth.user_provider, '', @dg_jira.rest]

dg_jira_auth.authentication_listener:
class: %dg_jira_auth.listener.class%
arguments:
- @security.context
- @security.authentication.manager
- @security.authentication.session_strategy
- @security.http_utils
- ''
- @dg_jira_auth.authentication_success_handler
- @dg_jira_auth.authentication_failure_handler
- ''
- @logger
- @event_dispatcher


Регистрация нового метода аутентификации и авторизации в Symfony




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

<?php

namespace DG\JiraAuthBundle\DependencyInjection\Security\Factory;

use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;

class JiraFactory extends AbstractFactory {

public function __construct(){
$this->addOption('username_parameter', '_username');
$this->addOption('password_parameter', '_password');
$this->addOption('intention', 'authenticate');
$this->addOption('post_only', true);
}

protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId)
{
$provider = 'dg_jira_auth.authentication_provider.'.$id;
$container
->setDefinition($provider, new DefinitionDecorator('dg_jira_auth.authentication_provider'))
->replaceArgument(1, $id)
;

return $provider;
}

protected function getListenerId()
{
return 'dg_jira_auth.authentication_listener';
}

public function getPosition()
{
return 'form';
}

public function getKey()
{
return 'jira-form';
}

protected function createListener($container, $id, $config, $userProvider)
{
$listenerId = parent::createListener($container, $id, $config, $userProvider);

if (isset($config['csrf_provider'])) {
$container
->getDefinition($listenerId)
->addArgument(new Reference($config['csrf_provider']))
;
}

return $listenerId;
}

protected function createEntryPoint($container, $id, $config, $defaultEntryPoint)
{
$entryPointId = 'security.authentication.form_entry_point.'.$id;
$container
->setDefinition($entryPointId, new DefinitionDecorator('security.authentication.form_entry_point'))
->addArgument(new Reference('security.http_utils'))
->addArgument($config['login_path'])
->addArgument($config['use_forward'])
;

return $entryPointId;
}
}


Для регистрации в бандле, необходимо в метод build у класса бандла добавить строку



$extension->addSecurityListenerFactory(new JiraFactory());


Окончательное внедрение




Все, теперь мы готовы тестировать работу с Jira. Добавим созданный JiraUserProvider в security.yml в секцию providers в виде строк

jira_auth_provider:
id: dg_jira_auth.user_provider


Далее необходимо добавить в firewalls новую секцию, полагая, что все страницы, адреса которых начинаются с /jira/ по умолчанию закрыты от неавторизованных пользователей:



jira_secured:
provider: jira_auth_provider
switch_user: false
context: user
pattern: /jira/.*
jira_form:
check_path: dg_jira_auth_check_path
login_path: dg_jira_auth_login_path
default_target_path: dg_jira_auth_private
logout:
path: dg_jira_auth_logout
target: dg_jira_auth_public
anonymous: true


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



- { path: ^/jira/public, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/jira/private/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/jira/private(.*)$, role: ROLE_USER }


PS




Весь код, приведенный в статье, можно установить в виде бандла из пакета «dg/jira-auth-bundle» в composer. Для работы бандла, необходимо зарегистрировать его в AppKernel.php и добавить секцию

_jira_auth:
resource: "@DGJiraAuthBundle/Resources/config/routing.yml"
prefix: /jira/


в routing.yml. После этого можно зайти на страницу /jira/public и протестировать авторизацию через Jira.


Для закрепления материала




В Symfony Cookbook есть так же инструкция, как внедрить аутентификацию через сторонний веб сервис.

Надеюсь статья будет вам полезна!


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


Программирование под iOS. Для профессионалов


сегодня в 20:45


Вчера я писал про программирование под Android и следовательно как контр-аргумент привожу в пример книгу "Программирование под iOS. Для профессионалов" Это третье издание книги со значительными дополнениями.

image Эта книга, выходящая уже в третьем издании и удостоенная премии «Jolt Productivity Award 2012» по версии Dr. Dobb's Journal, научит вас всем тонкостям разработки приложений для устройств на iOS (iPhone, iPad и iPod Touch). Основанное на учебном курсе известного образовательного IT-проекта Big Nerd Ranch, это практическое пособие содержит ключевые концепции программирования в iOS, множество примеров кода и упражнений по разработке приложений. Новое издание охватывает версии iOS 5 и Xcode 4.3.


Оглавление

Отрывок

Купить



608 страниц отличного текста и живых примеров


Прототип ISBN 978-0321821522 — iOS Programming: The Big Nerd Ranch Guide


Приятное дополнение — скидка по купону — fa1cb2f — 20%, и еще — подписываясь на наш фейсбук вы будете первыми кто узнает о наших новинках!




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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


[Из песочницы] Алгоритм кластеризации данных FTCA

Предисловие




Гуляя по англоязычным просторам интернета в поисках решения одной из наболевших тем на работе, наткнулся на очень интересный алгоритм под названием «Fast Threshold Clustering Algorithm». Данный алгоритм кластеризации, что примечательно, появился сравнительно недавно, а именно в ноябре этого года и автором является Дэвид Варади. Ссылка на первоисточник будет доступна в конце статьи.
Для начала, что такое кластеризатор?



image

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

Существует множество разнообразнейших алгоритмов кластеризации начиная от обычного K-means в разнообразных реализациях и интерпретациях, и заканчивая более сложными для понимания алгоритмами, которые могут включать суффиксные деревья (STC) или высшую математику (Lingo, SVD и т.п.). Часто встречающаяся проблема, которая настигает определенный алгоритм это ошибка. Ошибка всегда имеет место быть, особенно, когда мы говорим о документах в которых содержится реальная речь человека. Ведь, машине не всегда понятно, куда засунуть сказку про Царя Салтана: к политике, к истории древней Руси или создать кластер? Объекты могут иметь много связей с другими объектами по разным свойствам. С этой задачей бьются до сих пор.


А как избегать этой ошибки?



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



Алгоритм описанный Дэвидом, привлек мое внимание не своей асимптотикой или применимостью на практике, а своей простотой. Он построен на обычной человеческой логике и очень схож с остальными алгоритмами.

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

F(x,y) = F(y,x) = ...%

— функция корреляции двух объектов.

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


Далее, введем понятие средней похожести:

G(x, y1, y2, ..., yn) = SUM(F(x,yk))/n

— эта функция показывает насколько объект похож на группу других объектов.


Алгоритм




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

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

Создаются кластера по следующему алгоритму:

1) Сортируем объекты.

2) Берем первый и последний объект из нашего отсортированного списка.

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

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

3) Если остался один объект — создаем отдельный кластер и пихаем его туда. В противном случае возвращаемся на шаг 1.


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

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


Итоги




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

Обычная асимптотическая сложность алгоритма равна O(n^3) от, возможно, сложной функции F(x,y). При использовании матрицы корреляции мы получим O(n^2) по скорости и O(n^2) по памяти, что выбьет все пробки у оперативной памяти. Вот такие пироги, дорогие Хабрачане.

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


Источник: cssanalytics.wordpress.com/2013/11/26/fast-threshold-clustering-algorithm-ftca/


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


[recovery mode] Математические рисунки для начинающих

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

image



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

image

Сердечко из сердечек, заданное математическими формулами (график построен в программе Wolfram Mathematica):

image


А это формула любви… к диффурам!

Рассматривается автономная система из 2-х дифференциальных уравнений 1-го порядка.

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

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

image


Математическая эмблема МакДональдса:

image


Квадратная академическая шапочка:

image


График, имеющий вид логотипа ДВФУ:

image


Вот сам логотип:

image


Трёхмерный логотип ДВФУ.

image

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

Справа — поверхность вращения графика функции f(z) вокруг оси Oz.

Поверхности слева задаются параметрически. Берётся кривая на поверхности (заданная в цилиндрических координатах), делающая один оборот вокруг поверхности, и каждая точка этой кривой дополняется точками с таким же значением полярного угла φ и со значениями z в окрестности данного значения. Точно так же берутся ещё две похожие кривые.

Для построения графиков использовался пакет Wolfram Mathematica.


Ну, и наконец, вот как можно превратить логотип ДВФУ в ёлочку.

image


Напоследок приведу несколько математических рисунков, иллюстрирующих какие-то математические понятия.


Свойство подобия матриц: единичная матрица подобна только самой себе.

image


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

image


Следующая картинка позволяет запомнить элемент объёма в сферических координатах:

image


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

image


Производная произведения трёх функций:

image


Поворот комплексного числа z на угол фи вокруг начала координат против часовой стрелки.

image


image


Таким образом, математические картинки — это интересное творчество, которое может помочь в изучении математики.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


[Из песочницы] All that merge: рибосомная архитектура программного кода


сегодня в 19:35


image

Все начинается с примера.



w // хост (мама)
{
function table(qIn = {})
{
q["tit"] = "Wargana"; /** init-секция */
q["render"] = function(q){ };

qIn = merge(q, qIn); /** merge-секция */

if (q["m"] == "img") q = merge(q, img.table(qIn), qIn);
if (q["m"] == "map") q = merge(q, gmap.table(qIn), qIn);

q["render"](q); /** action-секция */

}
}

img // плагин 1 (дочка)
{
function table(qIn = {})
{
q["render"] = function(q) { return "img" + qIn["tit"]; };
return q;
}
}
gmap // плагин 2 (дочка)
{
function table(qIn = {})
{
q["render"] = function(q) { return "map" + qIn["tit"] + this.markers(qIn); }
return q;
}
function markers(qIn = {})
{
q["layer"] = function(q) { };
qIn = merge(q, qIn);
if (q["layerM"] == "cadastr") q = merge(q, cadastr.markers(qIn), qIn);
return q;
}
}

cadastr // плагин 2.1 (внучка)
{
function markers(qIn = {})
{
q["layer"] = function(q) { return "cadastr"; }
return q;
}
}

/** client-секция, примеры */

w.table({m : "img"})
w.table({m : "map", layerM : "cadastr"})
w.table({m : input("tableM"), layerM : input("gmapLayerM")})





  1. Дефолтизация параметров. Init-cекция задает дефолтное значение параметров, чтобы гарантировать безопасную работу act-секции. При этом merge-секция дает возможность сколь угодно сложно перекрывать параметризацию act-секции.

    q = merge(q, ..., qIn);



    qIn в самом конце merge списка это гарантия высшей власти клиента: насколько сложно код бы себя ни параметризировал, клиент всегда может «навязать в свою игру».

    Главное в схеме «Init — Merge — Act» — это связка «Init — Act», а Merge — вторичен.



    function some(qIn = {})
    {
    q["render"] = function(q){ return 1; };
    q = merge(q, qIn);
    return q["render"](q);
    }




    Предельная инит-генеалогия:

    function some(){ q["render"] = function(q){ return 1; }; return q["render"](q); }




    Выход в ноль:

    function some(){ return 1; }


  2. Сеточная плагинизация. Используя принцип «Init — Merge — Act» структура кода может вязаться до любого уровня вложенности, потому что аргументы merge()'a внутри тоже строятся по принципу IMA.
    Интерфейсы


    function some(){ q["isGrab"] = 0; q = merge(q, plugSome(q)); }
    function plugSome(qIn = {}) { q["isGrab"] = 1; q = merge(q, qIn); return q; }




    Init-секция up-хоста some() дефолтизируя параметр isGrab для гарантии безопасной работы своей act-секции (которой в данном случае вообще нет), одновременно задает интерфейс для всех своих плагинов, подключаемых в merge-секции.

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


    Неймспейсинг



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

    modHost
    {
    function a(){ q["isGrab"] = 0; }
    function b(){ q["isGrab"] = 0; }
    }




    Благодаря этому устраняется задача вводить modHost.isGrabA, modHost.isGrabB

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


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



    function a(){ q["isGrab"] = 0; q = merge(q, b(q)); }
    function b(qPro = {})
    {
    q["isGrab"] = !qPro["isGrab"]; ;
    /* q = merge(q, qIn); */
    return q;
    }




    Типизация



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

    if (input.getM() == "img") imgW.table({width : 50})



    vs

    w.table({m : input("m"), width : 50})



    Хостов (как единиц смысла верхнего уровня) становится меньше, параметров становится больше.


Как добавить функционал





  1. скопипастить init-секцию из хоста мамы;

  2. cоздать хост плагина дочки и вставить туда скелет init-секции хоста мамы;

  3. перепрошить каждый параметр или выкинуть, если дефолтное значение устраивает;

  4. добавить в merge-секцию хоста мамы условный мерж на хост дочки;

  5. в клиентской секции использовать новые доп.параметры привычных хостов.


Как убрать функционал





  1. отключить дочку от мамы в merge-секции;

  2. потереть хост дочки.


Как произвести кросс-мерж



function some(qIn = {})
{
// init-секция
q["total"] = 15;

// merge-секция

// q = merge(q, someA(), someB(), qIn);
qA = someA();
qB = someB();
qPro["total"] = q["total"] + qA["total"] + qB["total"] + qIn["total"];
q = merge(a, someA, someB, qIn, qPro);

// act-секция

}




Developers, stick with Russians – work in London




Переводы с

карты на карту


Переводы

через QR-Код


Новая функция

«Мой контроль»




Возьми Lumia 925 на тест-драйв сейчас.




Впечатляющие возможности

в стильном тонком корпусе из металла




Boomburum

исследует LTE


Эволюция средств связи

в путешествии по России




Проблемы коммуникации внутри бизнеса?



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

и выиграй Xbox




Нет времени

на счета?


MasterCard

Mobile



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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


[Из песочницы] Своя GTA San Andreas на iOS



Всем доброго времени суток! Меня зовут Министр Ада и сегодня мы посмотрим на недавно вышедшую GTA San Andreas для iOS.

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

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

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





Немного истории



Когда в 2005 году вышла GTA San Andreas моддинг игр серии GTA уже шел полным ходом. Были и моды небольшие в виде нового транспорта, и глобальные в виде изменения ещё и территорий игры. Спустя некоторое время появились небольшие программы, которые позволяли редактировать архивы этой игры. И понеслось.

Меня всегда не удовлетворял тот стандартный низко полигональный транспорт во всех сериях игры и благо с этим справились, так же появились моды на машинки для GTA SA. И вот вышла GTA SA на iOS. И я подумал, а почему бы не поставить туда новые машинки? Я подумал и начал делать.
Много действия



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

Папка gta3sa.app — корень игры, тут полностью все файлы игры. Посмотрев структуру GTA SA для PC можно увидеть, что там используется небольшая иерархия папок, которая хранит в себе файлы по категориям:

  • Anim

  • Audio

  • Data

  • Models

  • Movies

  • Text


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


И так, я задался провести небольшую работу в виде замены машинки для игры. Вспоминая опыт былых лет по редактирования PC версии GTA SA, было решено проверить внутреигровой архив gta3.img в котором должны храниться не только 3d-модели и текстуры машин, но и другие файлы отвечающие за устройство игрового города. Файл был успешно найден и вес его составлял 276 мб, для сравнения тот же файл, но для PC версии игры весит порядка 900 мб. Для нас это значит, что безусловно файлы в архиве значительно ужаты.


Внутреигровые архивы формата *.img в то время спокойно открывала утилита IMGTool 2.0, но погуглив увидел, что сейчас есть более усовершенствованная программа для этих действий, которая отличается более быстрым исполнением своей работы. Имя её GTA-SA Crazy IMG Editor.


Я попытался открыть игровой архив gta3.img через эту программу… И да! Он открылся! Формат не поменялся! Значит есть надежда, что и остальные форматы не трогались! Используем поиск и ищем машину picador, это машина Райдера, вашего соседа.


Экспортировав файл машины picador.dff, файл модели машины, я пока не нашел файл с текстурами той же машины. PC версии игры как модель, так и текстуры всё лежало в gta3.img . К сожалению, посмотреть в 3d-редакторе файл машины не получилось, файл был заблокирован, но всё так же не терял надежды и продолжил свою работу.


По своей натуре я любитель немецкого автопрома, поэтому было решено заменять игровую модель на BMW 750Li 1995 года.

Специально для игры была найдена машина с небольшим полигонажем, относительно, за авторством Greengiant. Ссылку прилагать не буду, всё находиться в интернете за пару минут. Машину заменил, настройки машины прописал в файлах vehicles.ide, handling.cfg, carcols.dat. Инструкция по установки машины прилагается в самом архиве с моделью. Вот и всё, машина настроена и заслана в игру. Теперь изменённые файлы либо упаковываем снова в IPA архив, либо через программу iTools , открываем папку приложения GTA SA и в самом приложении находим папку gta3sa.app в которую и копируем наши изменённые файлы. Запускаем игру и смотрим, что у нас вышло:



Так же можно заметить, что чуток просел FPS, это из-за сравнительно большего полигонажа модели, но думаю, что на аппаратах от Apple более мощных, чем мой iPhone 4s никаких заметных проседаний FPS быть не должно. Вес этой модели 1,5 МБ, до этого ставил BMW X5 весом в 3 МБ, проседание FPS было более заметное, но играбельное.



Машина есть, едет правильно, не переворачивается, но где же текстуры? Рядом с img архивом лежал файл с расширением pvr.dat, немного погуглив я узнал, что для этого формата уже написан редактор — TXDFucker за авторством Nick7 aka Lego. C его помощью я импортировал в игру текстуры машины. Хотя тут тоже есть свои подводные камни, в игре есть ограничение на количество текстур, т.е. что бы импортировать 8 текстур машины, мне пришлось удалить 8 текстур из игры. Не особо понятно, для чего сделано такое ограничение, ну да Бог с ним. И вот, что получилось в итоге:









Так же на одном форуме по серии игр ГТА пользователь с ником Frank.s написал, что у него получилось очистить карту ГТА и приложил не только скриншот, но и файлы с очищенной картой.


image


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

Конечно, может старые модели iphone не потянут новых модов, но с новыми устройствами от Apple всё должно быть хорошо.

Моддинг для устройств на Android немного отличается, но не значительно, поэтому потенциал в использовании модификаций на этой системе будет больше, чем на iOS.


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


Всего вам самого наилучшего! Спасибо!

Хочу отдельно поблагодарить Nick7, который помог мне с добавлением текстур в игру.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


Как обмануть своих покупателей на Aliexpress

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

image



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

Итак, решил я купить Android TV.


Опыта работы с приставками на arm’е у меня не было, поэтому я задал вопрос на Тостере и по ответам понял, что есть смысл купить Minix NEO X7. Такой аппарат быстро нашёлся на Aliexpress за 127$ ещё и с бесплатной доставкой DHL. Подобными предложениями я уже успешно пользовался, поэтому никаких подозрений у меня не возникло. Вначале.


Затем я обратил внимание на отсутствие отзывов и решил купить дороже, но в другом месте. Каково же было моё удивление, когда вечером у товара появилось 2 положительных отзыва от заказчиков их США. “Рискну” — решил я и сделал заказ.


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


Итак, представляю вашему вниманию письмо

The shipping agency told us that the order has been processed and send out for you. And you can trace it in a few days due to

China customs are very busy during the last quarter of every year.(The system notices there are very bad logistic jams caused by seasonal rush, all information will be delayed by ten days or more.sorry for any inconvenience this may cuase.)

Important information you should know, many shipments, especially the Trade Marked Items have got detained by China Customs

these days according to latest Customs report data, so this parcel has to be shipped by post instead of dhl to avoid

any detainment by Customs. Meanwhile it will cause slow delivery for this shipment, approximately the order will come to you in 3 or 4 weeks later, we will partially refund you for the shipping fee balance for the slow delivery. Now you can open a dispute and ask for the shipping charge balance, fill in the amount to 55 as the reimbursement for the slow delivery, (you can find a dispute button and click it to open a dispute now, or may be in a few days later if you can't find this button now.)We will accept it then you can get the money back immediately.

Thanks for your understanding and sorry for any inconvenience this may cause.

Please feel free to let me know if you have question.

Best regards.



Следом за ним мне пришло ещё одно

For the shipping charge balance refund of 55 usd, please find the dispute button of this order on your page, find it and click it, then you might have two questions to answer.

The first one is: Did you receive you goods? (Choose YES,if you choose NO,then the system stop you to continue)

The second one is: what is you solution? then you just need to fill in: refund the shipping charge balance caused by different slow delivery method.

please feel free to let me know if there is anything else i can do for you.



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



  1. Регистрируется новый продавец.

  2. Создаётся лот с нереально низкой ценой и предложением отправки с помощью DHL или Fedex.

  3. Ордер помечается как отправленный обычной китайской почтой и указывается невалидный Tracking Number.

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

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

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


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


image

В интернете я нашёл описание этой схемы именно в таком варианте. Как улучшить эту схему?

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

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

4. Узнать реальную стоимость отправки DHL в страну клиента и предложить ему именно такую сумму (я проверил, доставка DHL обошлась бы мне в 60$).


Самое узкое место в этом случае остаётся пункт 5, потому-что клиент не дурак и не будет помечать неотслеживаемый товар как полученный. Я не нашёл упоминания об этом, но я предложил бы китайцам отправлять какой-нибудь другой товар с реальным Tracking Number.


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


И самое главное, почему я понял, что я стал жертвой до того, как мошенник завершил свою схему: ребята, проверяйте Tracking Number. Информация о том, как правильно его проверить не является ни для кого секретом (а мне, по долгу службы, приходилось не раз писать валидаторы). Вот какой номер предложил мне мой “продавец”:


RB466759764HK

Умножив первые 8 цифр на множители (8, 6, 4, 2, 3, 5, 9, 7) и сложив результаты получим 255.

Остаток от деления 255 на 11 равен 2.

Вычитая 2 из 11 получим 9. Это и должна быть девятая цифра, которая в указанном коде равна 4.



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


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


image

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


Личные финансы на основе GnuCash

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


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

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

  • Личные финансы это монитор вашей деятельности, не более и не менее, это не панацея от всех болезней. Это достаточно кропотливый, нудный и постоянный процесс. Нельзя вести учет финансов урывками. Введёте мусор и в будущем будете анализировать мусор!






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

Установим программу:



sudo apt-get install gnucash libdbd-mysql mysql-server


Добавим базу данных:



mysql> create database gnucash;
mysql> grant all on gnucash.* to gnucash@localhost identified by 'PASSWORD' with grant option;
mysql> flush privileges;
mysql> exit


После установки нужно создать статьи расходов и доходов. Удалите все статьи которые предложит программа. И выделите 3 основные группы в доходах и расходах.

1. Операционные статьи — это статьи которые описывают повседневную

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


2. Инвестиционные статьи — это статьи разовых платежей. Например, вы купили квартиру в кредит и выплатили банку сразу 20% стоимости. Этот платеж будет относиться к инвестиционной деятельности. Вы инвестировали в квартиру, а вот ежемесячные выплаты будут относиться уже к операционной деятельности. Вклад в банк является инвестиционной деятельностью.


3. Финансовые статьи — это проценты от банка, покупка обязательств и т. д.


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


Вот собственно и всё, шаблон создан и учет начал вестись.

image

image


Проблемы


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


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


2. Как быть с проектами. Вот вы фрилансер или разработчик создали свой проект, как учитывать эти расходы? Очень просто они все относятся к инвестиционной деятельности. Да, да. Всё просто.


3. У меня есть счета в электронной валюте ฿ как быть?

В последнее время у всех на устах популярный биткоин, но такой валюты в GnuCash нет. Для этого придется залезть в исходники и поправить 2 файла iso-4217-currencies.scm и iso-4217-currencies.c которые находятся в '/gnucash-2.4.7.1b/src/engine/'



;; non-standard/private - Not ISO4217

( "Bitcoin" "bitcoin" "satoshi" "ISO4217" "BTC" "nil" 100000000 100000000 "BTC" )



{
const char *fullname = "Bitcoin";
gnc_commodity *c = gnc_commodity_new(book,
CUR_I18N(fullname),
"ISO4217",
"BTC",
"nil",
100000000);

if(!c) {
PWARN("failed to create commodity for currency %s", fullname);
} else {
if(!gnc_commodity_table_insert(table, c)) {
PWARN("failed to insert %s into commodity table", fullname);
}
}
}


и нужно будет подправить файл /usr/share/xml/iso-codes/iso_4217.xml в системе



<iso_4217_entry letter_code="BTC" numeric_code="nil" currency_name="Bitcoin"/>


Собираем исходники



sudo apt-get build-dep gnucash
sudo sh configure --prefix=/opt/gnucash
sudo make
sudo make install


Буду рад ответить на ваши вопросы.


Ссылки:

www.gnucash.org/

wiki.gnucash.org/wiki/GnuCash

www.ashep.org/tag/gnucash/

wiki.gnucash.org/wiki/SQL


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


Телевизор в интернете — обзор плееров

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

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

Начну свой обзор с популярной программы просмотра телеканалов на мобильных устройствах — SPB TV. Здесь сразу же напрашивается, чтобы отметить, один недостаток – просмотр только на мобильных устройствах. Но сначала поговорим о плюсах. Главный плюс, конечно, – это мультиплатформенность. Плеер работает на всех платформах, среди них: iOS, Android, Windows Phone, Symbian или Blackberry. Также плеер подходит для Smart TV – это сегодня достаточно актуально. Если говорить о предлагаемом контенте, то каналов на SPB TV около 200, при этом есть как бесплатные, так и платные подписки, при этом создатели утверждают, что их цены ниже, чем цены за обычное домашнее телевидение. Предлагаемые каналы являются и российскими и зарубежными. И все эти плюсы достаются пользователю бесплатно. Перечисленные преимущества плеера отмечают создатели на своем сайте, что же сказать о недостатках. Первое, существуют существенные проблемы с обновлением программы, пониманием потребностей пользователя. После последних обновлений программы, возникают существенные проблемы с проигрыванием каналов, многие каналы исчезли, многие вообще не работают. Не очень удобный пользовательский интерфейс, нет регулировки яркости. Конечно, после жалоб пользователей, разработчики должны исправить эти недостатки, но пока это есть, это является существенным минусом программы. Часто возникают проблемы с просмотром российских каналов за рубежом.

Следующим мобильным телевизором хочется рассмотреть Crystal TV – «это телевизор для твоего мобильного телефона, планшета или ноутбука». Поддерживаемые платформы iOS, Android, Windows, Mac OS X, Windows Phone, Symbian. Здесь сразу на лицо выступает преимущество перед SPB TV, и это преимущество – возможность просмотра телеканалов через Crystal TV на компьютере. По сравнению с предыдущим сервисом, здесь меньше телеканалов для просмотра, но все самые популярные каналы эфирного вещания имеются (в базовом пакете 32 телеканала, нет НТВ, Рен ТВ, ТНТ, ТВЦ). Самым большим недостатком является платное распространение программы. В целом сервис прост для использования, есть функция картинка в картинке. Огромный недостаток это очень маленькое количество бесплатных каналов (около 4-х), здесь под замком даже те каналы, которые ловит обычная антенна. Зато хорошее качество видео, т. к. программа умеет подстраиваться под ширину пользовательского канала интернет-доступа и транслировать видео соответствующего качества, что не мало важно, ну и есть функция картинка в картинке. В общем, если вам не жалко денег, чтобы платить за сервис, который может предоставляться совершенно бесплатно, то устанавливайте и наслаждайтесь просмотром.

Telebreeze Player. Поддерживает самые популярные платформы: Windows, Linux, MacOS, Android, iOS. Также здесь выделяется плюс, что программа может работать как на ПК, так и на мобильных устройствах. Но по сравнению с предыдущими рассмотренными программами не подходит для Windows Phone. Это, конечно, является конкурентным недостатком плеера. Сервис имеет 15 телеканалов в свободном доступе, различных тематик. Достоинствами плеера можно выделить удобный, красивый внешний вид, возможность проигрывания видео из архива. Особым плюсом плеера выделяется вещание по пиринговой системе. В этом случае на качество проигрывания видео не особо влияет скорость интернет провайдера, поэтому можно покойно наслаждаться просмотром любимого канала. Также создатели используют технологию адаптивного вещания, что тоже существенно повышает качество проигрывания картинки.

image

RusTV Player. В своем арсенале имеет более 300 российских и зарубежных телеканалов. Существенный недостаток программы в поддерживаемых платформах — Windows 7 / Vista / XP, программа подходит только для ПК. Здесь разработчики позаботились о дизайне, программа содержит около 60 тем оформления. Также в наличии есть 33 радиостанции, создатели радуют разнооборазием. Очень простой и удобный интерфейс, но программа существенно уступает по качеству воспроизведения видео, большое количество сбоев при запуске и просмотре телеканалов.

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















































































КритерийSPB TVCrystal TVTelebreeze PlayerRusTV Player
Общее количество телеканаловОколо 200Около 6015Более 300
Количество телеканалов в свободном доступе393215Более 300
Поддерживаемые платформыiOS, Android, Windows Phone, Symbian, BlackberryiOS, Android, Windows, Mac OS X, Windows Phone, SymbianWindows, Linux, MacOS, Android, iOS, Smart TVWindows 7/ Vista/ XP
Стоимость использованияБесплатноБесплатноБесплатноБесплатно
Полноэкранный режим++++
Функция картинка в картинке++--
Возможность проигрывания видео из архива-+++
Адаптивное вещание+-+-
Возможность сортировки каналов++++
Использование пиринга--+-

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


Врач-линуксоид или сисадмин-кузнец? Экосистема Хабрахабра


Начну с небольшого лирического отступления...




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

Моносистема — неустойчива. Любой внешний фактор может вывести ее из равновесия. Вернуть в точку баланса можно только затратив массу усилий.


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

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

Какое это имеет отношение к Хабрахабру?




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

Я внезапно задумался о том, есть ли вообще среди нас «чистые» IT-специалисты. В теме про 3D печать на базе сварочного аппарата внезапно выясняется, что среди нас есть профессиональные сварщики, люди, у которых есть своя кузница, руководители химических заводов. Если говорить о себе, то я профессиональный врач, закончивший ВУЗ, интернатуру и ординатуру. При всем при этом IT стало для меня чем-то гораздо большим, нежели простое хобби. Linux, 3D-дизайн, администрирование небольших сетей — все это входит в круг моих интересов. Почти каждый из нас — странный сплав совершенно не сочетаемых, на первый взгляд, интересов. Именно это делает наше сообщество таким гибким и адаптивным.


Небольшой опрос




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

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


[recovery mode] Вышел Asterisk 12


сегодня в 15:07


image

Вышла новая версия платформы для развертывания системы IP-телефонии.

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


Основные изменения таковы:



  • Новый драйвер канала SIP основанный на SIP библиотеке PJSIP — chan_pjsip. Этот канальный драйвер не заменяет стандартный SIP драйвер, а является его альтернативой.

  • Новая шина внутренних сообщений — Stasis

  • Большая работа проделана в направлении стандартизации и улучшении AMI

  • Значительно переработан Bridging API используемый в ConfBridge, это включает в себя большие изменения в AMI, CDR и CEL. Так же удален ставший ненужным драйвер канала chan_bridge.

  • Новые механизмы ARI (Asterisk REST Interface) — этот механизм позволяет управлять каналами, мостами, конечными точками, медиа-трафиком и другими фундаментальными вещами. Пользователи ARI могут разрабатывать коммуникационные приложения использующие HTTP REST интерфейс и получать JSON события о объектах через WebSocket соединение.

  • Добавлена поддержка IPv6 в драйвер канала IAX2.

  • Удален драйвер канала chan_agent. Он заменен приложениями AgentLogin и AgentRequest.

  • Консультационный перевод средствами Asterisk обзавелся новым функционалом.

  • Добавлена поддержка кодеков VP8 и Opus в режиме проксирования.

  • Глобальная переработка системы парковки вызовов. Весь функционал вынесен из ядра в отдельный модуль res_parking.


Спецификации AMI v2

Спецификации Asterisk 12 CEL

Спецификации Asterisk 12 CDR

Полный список изменений Asterisk 12

Руководство по переходу на Asterisk 12





Developers, stick with Russians – work in London




Переводы с

карты на карту


Переводы

через QR-Код


Новая функция

«Мой контроль»




Возьми Lumia 925 на тест-драйв сейчас.




Впечатляющие возможности

в стильном тонком корпусе из металла




Boomburum

исследует LTE


Эволюция средств связи

в путешествии по России




Проблемы коммуникации внутри бизнеса?



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

и выиграй Xbox




Нет времени

на счета?


MasterCard

Mobile



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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.