понедельник, 20 мая 2013 г.

Немного о взаимодействии приложений в Tizen

Исходный код, описанных здесь примеров можно найти на Github

Нередко возникают задачи, когда вашему приложению необходимо использовать функционал сторонней программы или наоборот, оно может выполнять некую обработку, необходимую другим приложениям. Примерами таких взаимодействий могут быть: использование стандартного медиа прогрывателя для воспроизведения видео или аудио, перевод текста, если в системе установленно приложение для этих целей, отправка смс сообщения  и т.п. В этой заметке я постараюсь немного описать модель таких взаимодействий под платформой Tizen.
Рассмотрим 2 приложения TizenSender и TizenReceiver. Первое будет отправлять текст, а второе, соответственно, получать, обрабатывать и возвращать ответ Sender'у. Для обмена подобными сообщениями, приложение, которое посылает информацию, в нашем случае это TizenSender должно иметь привелегию http://tizen.org/privilege/application.launch в manifest.xml файле. В свою очередь, приложение, которое экспортирует возможности для других, должно содержать в своем манифесте AppControl с идентификатором операции, которую оно выполняет. Этот идентификатор имеет формат web uri сохраная при этом даже префикс http. Не понятно для чего было изменять стандарты принятые, например, в android, но разработчикам Tizen видней. В нашем случае мы зададим operationId, как http://example.org/tizen/communication.
В целом схема взаимодействия выглядит примерно так:
Начнем разработку примера с TizenSender'а, отталкиваясь от схемы выше. Для данных целей, нас интересуют 2 метода из класса AppManager из Tizen SDK это:
static AppControl* FindAppControlN (const AppId &appId, 
                   const Tizen::Base::String &operationId)
static Tizen::Base::Collection::IList* FindAppControlsN (const Tizen::Base::String *pOperationId, 
                   const Tizen::Base::String *pCategory, 
                   const Tizen::Base::String *pDataType, 
                   const Tizen::Base::String *pUriPattern)
Или один из AppControl:
static result FindAndStart(const Tizen::Base::String &operationId,
              const Tizen::Base::String *pUriPattern, 
              const Tizen::Base::String *pDataType, 
              const Tizen::Base::String *pCategory, 
              const Tizen::Base::Collection::IMap *pExtraData, 
              IAppControlResponseListener *pListener)
Первый метод принимает во входные параметры appId, и operationId. Т.к. appId уникальный, вы можете быть уверены что вам вернется AppControl нужного приложения. Второй метод пытается подобрать исполнителя только по operationId, и соответственно возвращает коллекцию всех возможных вариантов. Ну и последний метод, по сути своей, это совокупность метода номер 2 плюс метод Start. Он также попытается найти обработчик по operationId, но выбор подходящего приложения предоставит пользователю. Аргументы передаются через коллекцию ключ-значение extraData. Для стандартных контролов необходимые элементы коллекции предопределены. Для ваших собственных можно использовать любые произвольные, главное чтобы они были согласованы.
Пользовательский интерфейс будет простым, на 2 действия. Найти список приложений готовых обработать данный AppControl. Нужен чтобы убедиться, что TizenReceiver установлен и готов к работе. И второе, отправить введенное пользователем сообщение.
Далее переходим к TizenReceiver. Как уже указано выше, первое о чем стоит позаботиться, это добавить наш operationId к AppControl'ам в файле манифеста. 
Теперь необходимо зарегистрировать объект реализующий интерфейc IAppControlProviderEventListener:
void TizenReceiverApp::setAppControlListener(Tizen::App::IAppControlProviderEventListener* listener) {
 using namespace Tizen::App;

 AppControlProviderManager* mgr = AppControlProviderManager::GetInstance();
 AppAssert(mgr);

 result r = mgr->SetAppControlProviderEventListener(listener);
 TryReturnVoid(!IsFailed(r), "Setting AppControlProviderEventListener failed. Error: %s",
   GetErrorMessage(r));
 AppLog("AppControlProviderEventListener was registered successfully");
}
Здесь есть небольшой нюанс. Метод SetAppControlProviderEventListener должен быть вызван до того, как сработает OnAppControlRequestReceived, точнее в App::OnAppInitializing. Иначе запрос будет просто проигнорирован. При этом нужно учесть, что после того как приложение будет инициализировано и UI контролы будут созданны указатель на коллекцию параметров extraData уже будет недействителен.
Возвращение результата, происходит аналогично, через коллекцию типа ключ-значение, с помощью метода OnAppControlRequestReceived класса OnAppControlRequestReceived. В итоге получаем:
Принцип работы прост. Запускаем TizenSender, пишем какой-либо текст в поле Message и жмем Send text. Должно открыться окно TizenReciver, в котором будет видно пришедший, и слегка измененный текст, который будет ответом. После нажатия Send response мы должны снова вернуться в окно TizenSender. Это все, остальные подробности нужно искать в документации.

суббота, 27 апреля 2013 г.

Tizen. Чиним сеть в эмуляторе на Ubuntu 12.10 и 13.04

Установка Tizen SDK проходит без каких-либо дополнительных телодвижений, и в целом вопросов не возникает. Однако если вы хотите получить приемлемую скорость работы эмулятора, необходимо настроить работу kvm в вашей системе. Для этого можно воспольpзоваться руководством с ubuntu help. Именно так я и поступил, и в итоге получил работающий эмулятор с неработающей сетью. Дальше о том, из-за чего это произашло и как все это починить.
В документации по эмулятору есть пункт посвященный работе сети. Если коротко это выглядит так:  
Теперь необходимо убедиться что в нашей системе все работает, как задумано. Для начала проверим настройки сети внутри гостевой системы. Быстрей всего это можно сделать через командную оболочку. Для этого достаточно нажать правой кнопкой на главном окне Tizen эмулятора и выбрать пункт Shell. Дальше мы получаем самый обычный linux терминал с правами root, поэтому можем посмотреть состояние сети через
    sh-4.1# ifconfig eth0
    eth0      Link encap:Ethernet  HWaddr BC:AE:C5:8F:80:A3  
          inet addr:10.0.2.16  Bcast:10.0.2.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:273877 errors:0 dropped:0 overruns:0 frame:0
          TX packets:210089 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:26920315 (25.6 MiB)  TX bytes:16432995 (15.6 MiB)
    sh-4.1# route -n
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use     Iface
    0.0.0.0         10.0.2.2        0.0.0.0         UG    0      0        0 eth0
    10.0.2.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
    10.0.2.2        0.0.0.0         255.255.255.255 UH    0      0        0 eth0
Видим  что это вполне согласуется со стандартным положением из документации Tizen OS, эмулятор имеет адресс 10.0.2.16 и в качестве маршрута по умолчанию использует 10.0.2.2. Теперь необходимо убедиться что настройки хоста соответствуют данным требованиям. Для этого в его терминале
    $> sudo ifconfig 
    virbr0    Link encap:Ethernet  HWaddr 5e:9e:72:a5:d8:d4  
          inet addr:192.168.122.1  Bcast:192.168.122.255  Mask:255.255.255.0
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
    ...
    $> sudo iptables -t nat -L -n
    Chain PREROUTING (policy ACCEPT)
    target     prot opt source               destination         
    
    Chain INPUT (policy ACCEPT)
    target     prot opt source               destination         
    
    Chain OUTPUT (policy ACCEPT)
    target     prot opt source               destination         
    
    Chain POSTROUTING (policy ACCEPT)
    target     prot opt source               destination         
    MASQUERADE  tcp  --  192.168.122.0/24    !192.168.122.0/24     masq ports: 1024-65535
    MASQUERADE  udp  --  192.168.122.0/24    !192.168.122.0/24     masq ports: 1024-65535
    MASQUERADE  all  --  192.168.122.0/24    !192.168.122.0/24  
Видно, что нужный интерфайс хоста указывает не на тот адресс, и NAT работает не на ту подсеть соответственно. Решить данную проблемму можно воспользовавшись интерфейсом virsh
    $>sudo virsh    
    virsh # net-destroy default
    virsh # net-edit default    
В появившемся окне редактора, нужно заменить значения ip адресса интерфейса и пула адрессов.
      1 
      2   default
      3   b202ae10-db4a-c6ff-9988-4a2a197f92ba
      4   
      5   
      6   
      7     
      8       
      9     
     10   
     11 
Например так
      6   
      7     
      8       
      9     
И переконфигурировать интерфейс
    virsh # net-start default
    Network default started

Еще раз через iptables и ifconfig проверяем, что все параметры установленны верно. Можно убедиться в работе из гостевой ОС ping 10.0.2.2.
Однако, остается еще одна проблемма, при данных настройках, в эмуляторе не определен dns сервер. Поэтому пинг до того же google.com не проходит. Это можно решить используя свой resolv.conf. В консоли хостовой машины:
     $> echo "nameserver 8.8.8.8" > resolv.conf
     $> ~/tizen-sdk/tools/sdb -e push resolv.conf /etc/resolv.conf
    resolv.conf                     0 KB/s (19 bytes in 0.049s)
     $> rm resolv.conf 
Все. Проблемма исправлена, можно разрабатывать сетевые приложения :). PROFIT!

суббота, 1 сентября 2012 г.

Раскрашиваем NLog

Добавлю к предыдущему посту. Сегодня я написал первый в своей жизни VB скрипт:) Не без помощи конечно(спасибо мемберам с cyberforum). Для кого-то код будет ламерским, но он решает свою задачу. Стандартно NLog выводит информацию в файл в формате:
2012-08-31 12:08:06.6573|DEBUG|EVC.prog|Evconfig object was created. Start load configuration
2012-08-31 12:08:06.6846|DEBUG|EVC.Class1|Some additional information
Однако искать предупреждения или ошибки в куче такого текста, очень уныло. Вот я и решил немного подсластить пилюлю и раскрасить его. А делаю это так. Выводим в файл с расширением .log, ставим в свойствах файла Open with Excel. Теперь в самом Excel нужно добавить пару макросов. Как их добавлять можно легко найти на просторах интернета, отмечу лишь что лучше их сохранять в personal workbook. Тогда они будут доступны всегда, а не для определенного документа. Варианта открытия может быть 2. Первый, вы уже открыли Excel и теперь хотите импортировать в него лог. Тогда текст VB скрипта для макроса будет таким:
Sub LogView()
Dim OpenDialog, i, d
OpenDialog = Application.GetOpenFilename("Log Files (*.log),*.log,All Files (*.*),*.*", , "Выберите лог файл")
If OpenDialog = "False" Then Exit Sub
' Импортируем указаный файл, разделение по столбцам по символу |
Workbooks.OpenText Filename:=OpenDialog, Origin:=xlWindows, _
    StartRow:=1, _
    DataType:=xlDelimited, _
    TextQualifier:=xlDoubleQuote, _
    ConsecutiveDelimiter:=False, _
    Tab:=False, _
    Semicolon:=False, _
    Comma:=False, _
    Space:=False, _
    Other:=True, OtherChar:="|"
' Пробегаемся по всем строчкам и раскрашиваем в зависимости от содержания
Dim cl As New Collection
 cl.Add RGB(245, 245, 220), "INFO"
 cl.Add RGB(224, 255, 255), "DEBUG"
 cl.Add RGB(255, 127, 80), "WARN"
 cl.Add RGB(255, 69, 0), "ERROR"
 cl.Add RGB(255, 0, 0), "FATAL"
 On Error Resume Next
For Each i In Columns("B").SpecialCells(xlCellTypeConstants)
    i.EntireRow.Interior.Color = cl(i.Value)
Next
Err.Clear
On Error GoTo 0
' Еще раз разделим первый столбец. Отдельно дата, отдельно время.
 Columns("B").Insert    
    Columns("A:A").Select
    Application.DisplayAlerts = False
    Selection.TextToColumns Destination:=Range("A:B"), _
        DataType:=xlFixedWidth, _
        OtherChar:="|", _
        FieldInfo:=Array(Array(0, 5), Array(10, 1)), _
        TrailingMinusNumbers:=True
    Application.DisplayAlerts = True
' Ширина столбцов на любителя
    Columns("A:A").ColumnWidth = 11
    Columns("B:B").ColumnWidth = 15
    Columns("C:C").ColumnWidth = 15
    Columns("D:D").ColumnWidth = 30
    Columns("E:E").ColumnWidth = 100
' Тут сделаем выравнивание и установим wrap text для последнего информативного столбца
    With Columns("E")
        .HorizontalAlignment = xlGeneral
        .VerticalAlignment = xlBottom
        .WrapText = True
        .Orientation = 0
        .AddIndent = False
        .IndentLevel = 0
        .ShrinkToFit = False
        .ReadingOrder = xlContext
        .MergeCells = False
    End With
    With Columns("B")
        .HorizontalAlignment = xlCenter
        .VerticalAlignment = xlBottom
        .WrapText = False
        .Orientation = 0
        .AddIndent = False
        .IndentLevel = 0
        .ShrinkToFit = False
        .ReadingOrder = xlContext
        .MergeCells = False
    End With
    Range("A1").Select
End Sub
Для простоты добавил русских комментариев. Теперь другой вариант, когда вы открыли лог, запустился Excel, но все в одном столбце:
Sub ConvertToLog()
Dim i,d
Columns("A:A").Select
Application.DisplayAlerts = False
Selection.TextToColumns Destination:=Range("A1"), DataType:=xlDelimited, _
    TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=True, _
    Semicolon:=False, Comma:=False, Space:=False, Other:=True, _
 OtherChar:="|", _
 FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 1), Array(4, 1)), _
    TrailingMinusNumbers:=True
Application.DisplayAlerts = True
Dim cl As New Collection
 cl.Add RGB(245, 245, 220), "INFO"
 cl.Add RGB(224, 255, 255), "DEBUG"
 cl.Add RGB(255, 127, 80), "WARN"
 cl.Add RGB(255, 69, 0), "ERROR"
 cl.Add RGB(255, 0, 0), "FATAL"
 On Error Resume Next
For Each i In Columns("B").SpecialCells(xlCellTypeConstants)
    i.EntireRow.Interior.Color = cl(i.Value)
Next
Err.Clear
On Error GoTo 0
    Columns("B").Insert 
    
    Columns("A:A").Select
    Application.DisplayAlerts = False
    Selection.TextToColumns Destination:=Range("A:B"), _
        DataType:=xlFixedWidth, _
        OtherChar:="|", _
        FieldInfo:=Array(Array(0, 5), Array(10, 1)), _
        TrailingMinusNumbers:=True
    Application.DisplayAlerts = True
' Set width
    Columns("A:A").ColumnWidth = 11
    Columns("B:B").ColumnWidth = 15
    Columns("C:C").ColumnWidth = 15
    Columns("D:D").ColumnWidth = 30
    Columns("E:E").ColumnWidth = 100
' Set text wrap
    With Columns("E")
        .HorizontalAlignment = xlGeneral
        .VerticalAlignment = xlBottom
        .WrapText = True
        .Orientation = 0
        .AddIndent = False
        .IndentLevel = 0
        .ShrinkToFit = False
        .ReadingOrder = xlContext
        .MergeCells = False
    End With
    With Columns("B")
        .HorizontalAlignment = xlCenter
        .VerticalAlignment = xlBottom
        .WrapText = False
        .Orientation = 0
        .AddIndent = False
        .IndentLevel = 0
        .ShrinkToFit = False
        .ReadingOrder = xlContext
        .MergeCells = False
    End With
    Range("A1").Select
End Sub
Дополнительно я назначил хоткеи CTRL+SHIFT+L и CTRL+SHIFT+K и добавил их в Quick Access Toolbar.
Над цветами еще конечно можно поработать, но остальное очевидно. Profit!

четверг, 30 августа 2012 г.

Логируем правильно

Недавно меня посетила мысль, которая должна появляться в голове каждого программиста при создании нового проекта. Чтобы избежать тупняка при появлении какого-нибудь бага, надо правильно логировать. Простенький логер написать дело 30 минут, но его проблема будет как раз в том что он простенький. Чтобы добавить способности современных логеров, вроде асинхронной записи, ротации, настройки через файл конфига придется повозиться. А значит отнять время у себя и проекта. Или можно взять готовое и не изобретать велосипед.) Именно так я и сделал. Выбор пал на NLog . Честно, хватит пары часов чтобы прочитать львиную долю  документации на сайте проекта, и еще меньше, чтобы начать им пользоваться. Все выше перечисленное в нем из коробки и еще куча полезных фишек.
Единственный для меня минус оказался лишь в том, что для беты VisualStudio 2012 которой я уже начал пользоваться удобный snippet не установился. Причина скорей в том, что из мануала я узнал, что такой есть и под VS 10 прекрасно работает, а в 12 нет. Т.е. убивала именно несправедливость:) Но это я переборол. Блокнот, пару минут гугления и созрело:

    
        
Nlog snippet logger Create log object using LogManager class from NLog namespace.
NLog <![CDATA[private static NLog.Logger log = NLog.LogManager.GetCurrentClassLogger();]]>
Складываем это в файл с расширением .snippet, и экспортируем в студию. Потом разошелся и добавил еще один, чтобы не забыть заполнять нужные поля в .config файле. Этот добавляем в xml снипеты

    
        
NLog config nlog Create nlog section for .config file
NLog <![CDATA[ ]]>
В последнем снипете в поле filename перед {shortdate} надо ставить $. Я этого делать не стал по 2 причинам. Во первых этот символ надо как-то экранировать, чтобы VS его считала обычним текстом, а во вторых для любого нового проекта папку для лога нужно будет указывать заново. Типа напоминалка получается. И все. Теперь можно логировать со спокойной душой.

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

Обновляем железо. Новый SSD для моего Lenovo Y460


В последнее время цены на рынке SSD ползут вниз, и твердотельные накопители становятся, не большой роскошью, а осязаемым пунктом в обновлении вашего ПК.
Твердо решив купить себе SSD, я как и многие другие встал перед выбором, платить кучу денег за вместительный накопитель или довольствоваться малым обьемом скоростного диска за приемлемые деньги. Так как той самой кучи у меня пока нет, мне оставался только второй вариант. Я стал смотреть в сторону 40-60 Гб дисков, при этом внутреннее чувство жадности заставляло меня тщательней мониторить рынок в поисках выгодного предложения.
Блуждая по интернет магазинам, я наткнулся на интересную альтернативу. Серию дисков Seagate Momentus. Некий компромисс между ценой и скоростью. Компания Segate в этой линейке объединила две технологии вместе, создав стандартный HDD диск с вместительным SSD кэшем на 4 или 8 гб. Выгода в объеме очевидна, диски выпускаются вариантами от 320 до 750 Гб, к тому же никаких специальных драйверов они не требуют и будут работать на всех современных платформах. Вариант вроде бы отличный, но быстрым кэшем управляет прошивка жесткого диска и никакого доступа из вне к нему нет. Она помещает в кэш самые часто используемые файлы, ускоряя тем самым работу системы. Естественно что с таким принципом работы, никакие синтетические тесты не покажут прирост производительности да и будет ли он ощутим, откровенно не понятно. Я взял на заметку этот вариант и продолжил изучение дальше.
Все это время мне не давала покоя мысль, о том что мой ноутбук, Lenovo Y460, в одной из своих комплектаций идет с 2-мя дисками. Несколько раз я его разбирал, чтобы убедиться что там нет никакого пустого места. Думал что 2-й диск может идти вместо CD-RAM, но эта версия тоже не работала. CD-RAM там тоже был. Я скачал service guide и тут стало понятно, что я отстал в технологиях. В магазинах уже давно лежат SSD-диски формата mSATA, о котором я слышал первый раз. Ноутбук был вскрыт, разъем найден

Оказалось мне не обязательно отрывать от сердца свои 500 Гб, чтобы добавить скоростной SSD :) Подобных дисков в магазинах пока не очень много, чему я, кстати, удивлен. Ведь это идеальный вариант, совместить быстрое с емким.
Я сделал выбор. Он пал на OCZ NOC-mSATA-60G. Размером чуть больше SD карточки, диск имеет 60 Гб памяти со скоростью 260/280 Мб/c на запись/чтение.

Существенным недостатком оказалось только отсутствие нужных болтиков в комплекте. В закромах мне удалось найти один который подошел, но думаю и он удержит платку, которая по ощущениям весит грамм 10.
3-й день обкатываю свою покупку на Windows 8, полет отличный. Делать тесты пока просто влом, но субъективно работать стало быстрей. Включение порядка 20 сек, выключение около 5. В общем доволен как слон. :)