Онлайн IDE — дань облачной моде или неизбежность среды разработки?

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

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

Не так давно, болтая на отвлеченные темы с братом-программистом, совершенно неожиданно для себя услышал такую фразу «ИДЕ в браузере пока что не вписывается в мое мировоззрение». И знаете, если бы услышал такое заявление от вечного джуниора-падавана, то вряд ли бы удивился. Но, услышал я это от программиста с 15-летним опытом разработки под всевозможные платформы и на всевозможных языках, включая многими забытый Ассемблер. И туговатостью взглядов ближайший родственник, вроде бы, также не отличается, ибо в ИТ закрытость для всего нового смерти подобна. Заинтересовавшись причинами чуть ли не агрессии по отношению к онлайн IDE, задал ему пару вопросов, которые впоследствии обсудил с ещё парой-тройкой опытных программистов. Ниже представлена компиляция весьма любобытных ответов.

Ты не скрываешь свой скепсис по отношению к «клауд» средам разработки. В чем причина?

Программист – существо ленивое, и если что-то работает хорошо, то зачем его улучшать и «чинить»? Существует проверенный временем механизм. Какие причины должны заставить меня переходить на cloud IDE, тратить время на освоение новой среды и рисковать сроками сдачи проектов?

То есть, позиция из разряда «я не знаю, хорошо это или плохо, но я все равно не хочу попробовать»?

В каком-то смысле, да! Знаешь, программисты редко когда хотят выглядеть глупыми, а именно так многие из них и выглядят, впервые попадая в новую online IDE.

Да, уж, похоже, что ты действительно скептически настроен!

Хм, ну посуди сам. Кому нужна эта Интернет зависимость? Да, конечно, сейчас покрытие есть почти везде. Но, что если я хочу «покодить» в пути? Мобильный Интернет не предлагать! Ну, пусть даже и есть стабильный Интернет, то часто сервера IDE находятся в США, а мы то в Европе… и от latency, ну, никуда не денешься. Плюс ко всему онлайн ИДЕ часто написаны на джаве, а она неслабо грузит мой бук. Система становится менее отзывчивой. Понимаешь, о чем я?

Более чем! Выходит, что нет никаких условий, при которых бы ты задумался об использовании «IDE в браузере». Те же Cloud9 и Exo IDE предлагают вполне неплохой набор инструментария и возможностей. Может, рискнешь?

Риски тут не причем. Я хочу, чтобы все работало идеально, и компромиссы меня не устраивают. Покажи мне онлайн IDE, в которой я могу с легкостью переключаться с PHP, CSS и Java Script на Android and iOS разработку, и я буду в первых рядах фанатов IDE в облаке. А пока что такие среды мне очень напоминают Google Docs, где нужно ждать пару секунд после практически каждой операции.

Я устал от критики! Давай искать положительные стороны. Неужели, ты не видишь таковых?

Ну, ты не подумай, что я «старый хрыч», не желающий открывать для себя ничего нового. Преимущества есть. Я, например, использую в работе 4 машины. Офис, дом, лэптопы в дороге. Синхронизация и прочие «прелести» присутствуют. А когда «летит» машина на Ubuntu, то день, мягко говоря, идет под откос. Поэтому, конечно, возможность работать с проектами с любой машины и на любой «оси» — штука заманчивая. Как бы тебе корректней сказать…Я не то, что против онлайн ИДЕ, а, скорее, не вижу за ними реальной перспективы завтрашнего дня. То есть, рано или поздно, я думаю, вся наша программерская братия будет кодить в облаке.

Совсем другое дело! То есть, шансы есть?

А то! Cloud IDE – отличные варианты для разработки веб-приложений. Но, увы, пока что веб-приложения не могут удовлетворить весь спрос на рынке. Мы продолжаем писать приложения под конкретные платформы, а лучше всего – девайсы. Но, как я уже сказал, рано или поздно разработка веб-приложений, совместимых со всеми платформами, станет реальностью. Уже все к этому идет. Вот это и будут золотые времена online IDE.

**************

Этот разговор напомнил мне попытки тестя прокатиться на авто с коробкой автомат. «Не оно», — сразу сказал тесть. «Почему? Беспокоитесь об экономии бензина или не любите, когда нет контроля над двигателем?» — спросил я. «Не-а! Я привык дергать ручку».

Есть ли у online IDE будущее? Моё мнение – однозначно, есть! Почему online IDE — это удобно? Возможно эти два видео частично ответят на довольно непростой вопрос. С Новым Годом, ИТ-сообщество! Позволяйте новшествам нарушать покой вашей жизни, хабралюди!

ссылка на оригинал статьи http://habrahabr.ru/post/164497/

Что такое TCHAR, WCHAR, LPSTR, LPWSTR,LPCTSTR (итд)

Многие C++ программисты, пишущие под Windows часто путаются над этими странными идентификаторами как TCHAR, LPCTSTR. В этой статье я попытаюсь наилучшим способом расставить все точки над И. И рассеять туман сомнений.

В свое время я потратил много времени копаясь в исходниках и не понимал что значат эти загадочные TCHAR, WCHAR, LPSTR, LPWSTR,LPCTSTR.
Недавно нашел очень грамотную статью и представляю ее качественный перевод.
Статья рекомендуется тем кто бессонными ночами копошиться в кодах С++.

Вам интересно ??
Прошу под кат!!!

В общем, символ строки может быть представлен в виде 1-го байта и 2-х байтов.
Обычно одно-байтовый символ это символ кодировки ANSI- в этой кодировке представлены все английские символы. А 2-х байтовый символ это кодировка UNICODE, в которой могут быть представлены все остальные языки в мире.

Компилятор Visual C++ поддерживает char и wchar_t как встроенные типы данных для кодировок ANSI и UNICODE.Хотя есть более конкретное определение Юникода, но для понимания, ОС Windows использует именно 2-х байтовую кодировку для много языковой поддержки приложений.

Для представления 2-х байтовой кодировки Юникод Microsoft Windows использует UTF16-кодирование.
Microsoft стала одна из первых компаний которая начала внедрять поддержку Юникода в своих операционных системах (семейство Windows NT).

Что делать если вы хотите чтобы ваш С/С++ код был независимым от кодировок и использование разных режимов кодирования?

СОВЕТ. Используйте общие типы данных и имена для представления символов и строк.

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

char cResponse; // 'Y' or 'N' char sUsername[64]; // str* functions  (с типом char работают функции который начинаются с префикса str*) 

На этот!!!

wchar_t cResponse; // 'Y' or 'N' wchar_t sUsername[64]; // wcs* functions   (с типом wchar_t работают функции который начинаются с префикса wcs*)  

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

#include<TCHAR.H> // Implicit or explicit include TCHAR cResponse; // 'Y' or 'N' TCHAR sUsername[64]; // _tcs* functions  (с типом TCHAR работают функции который начинаются с префикса _tcs*) 

В настройках проекта на вкладке GENERAL есть параметр CHARACTER SET который указывает в какой кодировке будет компилироваться программа:

Если указан параметр «Use Unicode Character set», тип TCHAR будет транслироваться в тип wchar_t. Если указан параметр «Use Multi-byte character set» то тогда TCHAR будет транслироваться в тип char. Вы можете свободно использовать типы char и wchar_t, и настройки проекта никоим образом не повлияют на использование этих ключевых слов.

TCHAR определен так:

#ifdef _UNICODE typedef wchar_t TCHAR; #else typedef char TCHAR; #endif 

Макрос _UNICODE будет включен если вы укажите «Use Unicode Character set» и тогда тип TCHAR будет определен как wchar_t. Когда же вы укажите «Use Multi-byte character set» TCHAR будет определен как char.

Помимо этого, для того что была поддержка нескольких наборов символов используя общий базовый код, и возможно поддержки много языковых приложений, используйте Специфические функции (то есть макросы).
Вместо того чтобы использовать strcpy, strlen, strcat (в том числе защищенные варианты функции с префиксом _s), или wcscpy, wcslen, wcscat (включая защищенные варианты), вам лучше использовать функции _tcscpy, _tcslen, _tcscat.

Как вы знаете функция strlen описана так:

size_t strlen(const char*); 

И функция wcslen описана так:

size_t wcslen(const wchar_t* ); 

Вам лучше использовать _tcslen, который логически описан так:

size_t _tcslen(const TCHAR* ); 

WC это Wide Character (Большой символ). Поэтому, wcs функции будут для wide-character-string (то есть для большой-символьной-строки).Таким образом _tcs будет означать _T символьная строка. И как вы знаете строки с префиксом _T могут быть типа char или wchar_t.

Но в реальности _tcslen (и другие функции с префиксом _tcs) вовсе не функции, это макросы. Они просто описаны как:

#ifdef _UNICODE #define _tcslen wcslen  #else #define _tcslen strlen #endif 

Вы можете просмотреть заголовочный файл TCHAR.H и поискать там еще Макро описания похожее на вышеупомянутое.

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

Вы спросите почему они описаны как макросы, а не как полноценная функция ??
Причина проста: Библиотека или DLL могут экспортировать простую функцию с тем же именем и прототипом (Исключая концепцию перегрузки в С++).
Для примера если вы экспортируете функцию:

void _TPrintChar(char); 

Как должен вызывать ее клиент ?? Как:

void _TPrintChar(wchar_t); 

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

Для этого мы сделаем две различные функции:

 void PrintCharA(char); // A = ANSI ( для однобайтового) void PrintCharW(wchar_t); // W = Wide character (для двухбайтового) 

И простой макрос скроет разницу между ними:

#ifdef _UNICODE void _TPrintChar(wchar_t);  #else  void _TPrintChar(char); #endif 

Клиент просто вызовет функцию как

TCHAR cChar; _TPrintChar(cChar); 

Заметьте, что TCHAR и _TPrintChar теперь будут сопоставимы с UNICODE или ANSI, а переменная cChar и параметр функции будет сопоставим с типом данных char или wchar_t.

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

Приведу пример с SetWindowText:

// WinUser.H #ifdef UNICODE #define SetWindowText  SetWindowTextW #else #define SetWindowText  SetWindowTextA #endif // !UNICODE 

Есть только несколько функций у которых нету таких макросов, и они только с суффиксом W или A. Пример тому функция ReadDirectoryChangesW, которая не имеет эквивалента в кодировки ANSI.


Как вы знаете, мы используем двойные кавычки для представления строк. Строка представленная в этой манере это ANSI-строка, на каждый символ используется 1 байт. Приведу пример:

“Это ANSI строка. Каждый символ занимает 1 байт.” 

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

L”Это Unicode строка. Каждый символ которого занимает 2 байта, включая пробелы. ” 

Поставьте спереди L и вы получите UNICODE строку. Все символы (Я повторяю все символы ) занимают 2 байта, включая Английские буквы, пробелы, цифры и символ null. Объем данных строки Unicode всегда будет кратен 2-м байтам. Строка Unicode длиной 7 символов будет занимать 14 байтов. Если строка Unicode занимает 15 байтов то это не правильная строка, и она не будет работать в любом контексте.

Также, строка будет кратна размеру sizeof(TCHAR) в байтах.

Когда Вам нужно жестко прописанный код, вы можете писать код так:

"строка ANSI"; // ANSI L"строка Unicode"; // Unicode  _T("Или строка, зависящая от компиляции"); // ANSI или Unicode // или используйте макрос TEXT, если вам нужна хорошая читаемость кода 

Строки без префикса это ANSI строки, с префиксом L строки Unicode, и строки с префиксом _T и TEXT зависимые от компиляции. И опять же _T и TEXT это снова макросы. Они определены так:

// УПРОЩЕННАЯ #ifdef _UNICODE   #define _T(c) L##c  #define TEXT(c) L##c #else   #define _T(c) c  #define TEXT(c) c #endif 

Символ ## это ключ(token) вставки оператора, который превратит _T(«Unicode») в L«Unicode», где строка это аргумент для макроса- если конечно _UNICODE определен.
Если _UNICODE не определен то _T(«Unicode») превратит это в «Unicode». Ключ вставки оператора существовал даже в языке С, и это не специфическая вещь связанная с кодировкой строк в VC++.

К сведению, макросы могут применятся не только для строк но и для символов. Например _T(‘R’) превратит это в L’R’ ну или в просто ‘R’. Тоесть либо в Unicode символ либо в ANSI символ.

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

char c = 'C'; char str[16] = "Habrahabr"; _T( c ); _T(str); 

Строки _T( c); _T(str); отлично скомпилируются в режиме ANSI, _T(x) превратится в x, и _T( c) вместе с _T(str) превратятся просто в c и str.
Но когда вы будете собирать проект в режиме Unicode код не с компилируется:

error C2065: 'Lc' : undeclared identifier error C2065: 'Lstr' : undeclared identifier 

Я не хотел бы вызывать инсульт вашего интеллекта и объяснять почему это не работает.

Существует несколько функций для конвертирования Мульбайтовых строк в UNICODE, о которых я скоро расскажу.

Есть важное замечание, почти все функции которые принимает строку или символ, приоритетно в Windows API, имеют обобщенное название в MSDN и в других местах.
Функция SetWindowTextA/W будет классифицирована как:

BOOL SetWindowText(HWND, const TCHAR*); 

Но как Вы знаете, SetWindowText это просто макрос, и в зависимости от настроек проекта будет рассматриваться как:

BOOL SetWindowTextA(HWND, const char*); BOOL SetWindowTextW(HWND, const wchar_t*); 

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

HMODULE hDLLHandle; FARPROC pFuncPtr;  hDLLHandle = LoadLibrary(L"user32.dll");  pFuncPtr = GetProcAddress(hDLLHandle, "SetWindowText"); //значение pFuncPtr будет null, потому что фунций с названием SetWindowText даже не существовало  

В библиотеке User32.DLL, имеются 2 функции SetWindowTextA и SetWindowTextW которые экспортируются, то есть тут нет имен с обобщенным названием.

Все функции которые имеют ANSI и UNICODE версию, вообще то имеют только UNICODE реализацию. Это значит, что когда Вы вызываете SetWindowTextA из своего кода, передавая параметр ANSI строку — она конвертирует ANSI в UNICODE вызывает SetWindowTextW.
Реальную работу (установку заголовка/названия/метки окна) делает только Unicode версия!

Возьмем другой пример, который будет получать текст окна, используя GetWindowText.
Вы вызываете GetWindowTextA передавая ему ANSI буфер как целевой буфер.
GetWindowTextA сначала вызовет GetWindowTextW, возможно выделяя память для Unicode строки (т.е массив wchar_t).
Затем он с конвертирует Unicode в ANSI строку для вас.

Эти ANSI в Unicode преобразования не ограничение только GUI функций, а так работает все подмножество Windows API функций, которое принимает строки и имеет два варианта.
Приведу еще пример таких функций:

  • CreateProcess
  • GetUserName
  • OpenDesktop
  • DeleteFile
  • итд

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

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

Замечание: Есть еще одно описание типа: имя ему WCHAR – оно эквивалентно wchar_t.


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

// ANSI символы foo_ansi(char*);  foo_ansi(const char*);  /*const*/ char* pString;   // Unicode/wide-string  foo_uni(WCHAR*);  wchar_t* foo_uni(const WCHAR*);  /*const*/ WCHAR* pString;   // Независимые  foo_char(TCHAR*);  foo_char(const TCHAR*);  /*const*/ TCHAR* pString; 

После чтения фишек с TCHAR, вы наверное предпочтете использовать именно его. Существуют еще хорошие альтернативы для представления строк в вашем коде. Для этого надо просто включить Windows.h в проект.
Примечание: Если ваш проект включает windows.h (косвенным или прямым образом), вы не должны включать в проект TCHAR.H.
Для начала пересмотрим старую функцию, чтобы было легче понять. Пример функцию strlen.

size_t strlen(const char*); 

Которая может быть представлена по другому.

size_t strlen(LPCSTR); 

Где LPCSTR описан как:

// Simplified typedef const char* LPCSTR;   

LPCSTR понимается так.
• LP — Long Pointer (длинный указатель)
• C – Constant (константа)
• STR – String (строка)
По сути LPCSTR это (Длинный) указатель на строку.

Давайте изменим strcpy в соответствие с новым стилем имени типов:

LPSTR strcpy(LPSTR szTarget, LPCSTR szSource); 

szTarget имеет тип LPSTR, без использования типов языка С. LPSTR определен так:

typedef char* LPSTR; 

Заметьте что szSource имеет тип LPCSTR, так как функция strcpy не модифицирует исходный буфер, поэтому выставлен атрибут const. Возвращаемый тип данных не константная строка: LPSTR.

Итак, функции с префиксом str для манипуляции с ANSI строками. Но нам нужна еще для двух байтовых Unicode строк. Для тех же больших символов имеются эквивалентные функции.
Для примера, чтобы посчитать длину символов больших символов(Unicode строки), вы будете использовать wcslen:

size_t nLength; nLength = wcslen(L"Unicode"); 

Прототип функции wcslen такой:

size_t wcslen(const wchar_t* szString); // Или WCHAR* 

И код выше может быть представлен по другому:

size_t wcslen(LPCWSTR szString); 

Где LPCWSTR описан так:

typedef const WCHAR* LPCWSTR; // const wchar_t* 

LPCWSTR можно понять так:
LP — Long Pointer (Длинный указатель)
C — Constant (константа)
WSTR — Wide character String (строка больших символов)

Аналогичным образом, strcpy эквивалент wcscpy, для Unicode строк:

wchar_t* wcscpy(wchar_t* szTarget, const wchar_t* szSource) 

Который может быть представлен как:

LPWSTR wcscpy(LPWSTR szTarget, LPWCSTR szSource); 

Где szTarget это не константная большая строка (LPWSTR), а szSource константная большая строка.

Существует ряд эквивалентных wcs-функций для str-функций. str-функции будут использоваться для простых ANSI строк, а wcs-функции для Unicode строк.

Хотя Я уже советовал что надо использовать native Unicode функции, а не только ANSI или только синтезированные TCHAR функции. Причина проста — ваше приложение должно быть только Unicode-ным, и вы не должны заботится о том что спортируются ли они для ANSI. Но для полноты картины я и упомянул эти общие отображения (проецирования)!!!

Чтобы вычислить длину строки, вы можете использовать _tcslen функцию (макро).
Который описан так:

size_t _tcslen(const TCHAR* szString);  

Или так:

size_t _tcslen(LPCTSTR szString); 

Где имя типа LPCTSTR можно понять так
LP — Long Pointer (Длинный указатель)
C — Constant (Константа)
T = TCHAR
STR = String (Строка)

В зависимости от настроек проекта, LPCTSTR будет проецироваться в LPCSTR (ANSI) или в LPCWSTR (Unicode).

Заметьте: функции strlen, wcslen или _tcslen будут возвращать количество символов в строке, а не количество байтов.

Обобщенная операция копирования строки _tcscpy описана так:

size_t _tcscpy(TCHAR* pTarget, const TCHAR* pSource); 

Или в еще более обобщенной манере, как:

size_t _tcscpy(LPTSTR pTarget, LPCTSTR pSource); 

Вы можете догадаться что значит LPTSTR ))


Примеры использования.

Во первых приведу пример нерабочего кода:

int main() {     TCHAR name[] = "Saturn";     int nLen; // Or size_t      lLen = strlen(name); } 

На ANSI сборке, этот код успешно с компилируется потому что TCHAR будет типом char, и переменная name будет массивом char. Вызов strlen для name тоже будет прекрасно работать.

Итак. Давайте с компилируем тоже самое с включенными UNICODE/_UNICODE (в настройках проекта выберите «Use Unicode Character Set»).
Теперь компилятор будет выдавать такого рода ошибки:

error C2440: 'initializing' : cannot convert from 'const char [7]' to 'TCHAR []' error C2664: 'strlen' : cannot convert parameter 1 from 'TCHAR []' to 'const char *' 

И программисты начнут исправлять ошибку таким образом:

TCHAR name[] = (TCHAR*)"Saturn"; 

И это не усмирит компилятор, потому что конвертирование из TCHAR* в TCHAR[7] невозможно. Такая же ошибка будет возникать когда встроенные ANSI строки передаются Unicode функции:

nLen = wcslen("Saturn"); // error: cannot convert parameter 1 from 'const char [7]' to 'const wchar_t *' // Ошибка: не могу с конвертировать параметр 1 из 'const char [7]' в 'const wchar_t *' 

К сожалению (или к счастью), эта ошибка может быть неправильно исправлена простым приведением типов языка C.

nLen = wcslen((const wchar_t*)"Saturn"); 

И вы думаете что повысили уровень своего опыта при работе с указателями. ВЫ не правы -этот код будет давать неправильный результат, и в большинстве вы будете получать Access Violation (нарушение доступа). Приведение типов таким образом это как передача float-переменной когда ожидалось(логически) структура размером 80 байт.

Строка «Saturn» это последовательность 7 байт:

‘S’ (83) ‘a’ (97) ‘t’ (116) ‘u’ (117) ‘r’ (114) ‘n’ (110) ‘\0’ (0)

Но когда вы передаете тот же набор байтов в wcslen, он рассматривает каждые 2 байта как один символ. Поэтому первые 2 байта [97,83] будут рассматриваться как один символ имеющий значение 24915(97<<8 | 83). Это Unicode символ ???.. И другие следующие символы рассматриваются как [117,116] и так далее.

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

TCHAR name[] = _T("Saturn"); 

Который будет транслировать в 7 или в 14 байт, в зависимости от компиляции.
Вызов wcslen должен быть таким:

wcslen(L"Saturn"); 

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

lLen = strlen ((const char*)name); 

На сборках Unicode переменная name будет размером 14 байт (7 unicode символов, включая null). Так как строка
«Saturn» содержит только Английские символы, которые можно представить используя ASCII кодирование, Unicode символ ‘S’ будет представлен как [83, 0]. Следующие ASCII символы будут представлены как нули. Заметьте сейчас символ ‘S’ представлен как 2-х байтовое значение 83. Конец строки будет представлен как 2 байта имеющее значение 0.

Итак, когда вы передаете такую строку в strlen, первый символ (то есть первый байт) будет правильным (‘S’ в случае с ‘Saturn’). Но следующий символ/байт будет идентифицирован как конец строки. Поэтому, strlen вернет неправильное значение 1.

Как Вы знаете, Unicode строка может содержать не только Английские символы, и результат strlen будет еще более неопределенным.

Короче говоря приведение типов не будет работать.
Вам придется, либо представлять строки в правильной форме, или использовать функции конвертирования ANSI в Unicode, и обратно.


Теперь, Я надеюсь Вы понимаете следующий код:

BOOL SetCurrentDirectory( LPCTSTR lpPathName ); DWORD GetCurrentDirectory(DWORD nBufferLength,LPTSTR lpBuffer); 

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

TCHAR sCurrentDir[255];  // Передавайте 255 а не 255*2  GetCurrentDirectory(sCurrentDir, 255); 

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

LPTSTR pBuffer; // TCHAR*  pBuffer = new TCHAR[128]; // Выделяет 128 или 256 байт, в зависимости от компиляции. 

Но если вы используете функции выделения памяти, такие как malloc, LocalAlloc, GlobalAlloc, и т.д., вы должны указывать количество байт!

pBuffer = (TCHAR*) malloc (128 * sizeof(TCHAR) ); 

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

P.S.

В заключение чтобы немного расслабить свой мозг, привожу ролик «Один день из жизни сисадмина»
Всех с НГ!!!

ссылка на оригинал статьи http://habrahabr.ru/post/164193/

Анализ и оптимизация одного запроса в EclipseLink

В этой статье я решил собрать несколько полезных практик, которым я научился за два года работы с ORM фреймворком EclipseLink на основе реального примера.
Статья расчитана на тех, кто уже работал с фрэймворком основанным на JPA, будь то Hibernate или OpenJPA.


Проект, примеры которого я буду приводить, основан на Spring.

Проблема:

Имеются следующие таблицы

ARTICLES -> Article.java ( ID int, NAME varchar );  ARTICLE_ROLES ( ARTICLE_ID int, ROLE_ID int );  ROLES -> Role.java ( ID int, NAME varchar );

ROLES представляет собой стандартную lookup table, с малым количеством строк.

Соответственно, в entity Article мы определяем связь через JoinTable:

@ManyToOne(optional = false, targetEntity = Role.class, fetch = FetchType.EAGER, cascade = { 			CascadeType.MERGE, CascadeType.REFRESH }) @JoinTable(name = "ARTICLE_ROLES",  			   joinColumns = {@JoinColumn(name = "ARTICLE_ID", referencedColumnName="ID", nullable = false},  			   inverseJoinColumns = {@JoinColumn(name = "ROLE_ID", referencedColumnName = "ID", nullable = false)}) 	public Role getRole() { 		return role; 	}

Теперь мы определяем query — getAllArticles следующим образом:

@NamedQuery(name = "Article. getAllArticles", query = "SELECT s FROM Article s")

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

Анализ проблемы:

Для начала, воспользуемся PerformanceMonitor’ом EclipseLink’а, чтобы замерить сколько запросов к БД реально проходят через JPA.
Проще всего включить его через persistence.xml

<persistence> … 		<properties> … 			<property name="eclipselink.profiler" value="PerformanceMonitor"/> 			 		</properties>   	</persistence-unit> </persistence>

Но persistence.xml у нас может быть общим и для тестов, и для аппликации.
Зато beans.xml у них разный. Так что для тестов достаточно прописать в нем:

<bean id="entityManagerFactory" 		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 	…         <property name="jpaPropertyMap">         	<map>         		<entry key="eclipselink.profiler" value="PerformanceMonitor" />         	</map>         </property> 	</bean>

Небольшое примечание

Не пытайтесь включить Profiler через анотацию @PersistenceContext, таким вот образом:

@PersistenceContext(properties={@PersistenceProperty(name="eclipselink.profiler",value="PerformanceMonitor")})  protected EntityManager em;

Этот способ не сработает.

Теперь воспользуемся нашим профайлером в тесте.

PerformanceMonitor profiler = (PerformanceMonitor)em.unwrap(Session.class).getProfiler();

PerformanceMonitor содержит в себе Map, в котором он хранит всю информацию, начиная с общего количества запросов к БД, и заканчивая временем для каждого.
Нас интересуют два конкретных параметра: Counter:ReadAllQuery и Counter:ReadObjectQuery.
Получим их и сравним до и после

 Long before = profiler.getOperationTimings.get("Counter:ReadAllQuery") + profiler.getOperationTimings.get("Counter:ReadObjectQuery "); em.createNamedQuery("Article.getAllArticles").getResultList(); Long after = profiler .getOperationTimings.get("Counter:ReadAllQuery") + profiler.getOperationTimings.get("Counter:ReadObjectQuery ");

Чтобы обнаружить, что разница составляет не 1, как можно было бы ожидать, а 10001. Ой.

Дело в том, что не смотря на fetch = FetchType.EAGER, при использовании JoinTable JPA решает генерировать запрос для каждой строчки, чтобы получить соответствующий объект Role.

Решение, первая версия:

Добавим Hint, указывающий JPA как приносить данные

@NamedQuery(name = "Article. getAllArticles", query = "SELECT s FROM Article s", hints = { 		<b>@QueryHint(name = QueryHints. FETCH, value = "s.role")</b>)

Рассмотрим синтакс этого hint’а.
Часть до дочки должна соответствовать alias’у объекта в тексте query.
Если по ошибке воспользоваться другой буквой, Hint не сработает, молча, не выбросив ошибку.
Часть после точки должна соотвествовать имени member’а внутри Article.

public class Article { 	… 	Role <b>role</b>; 	… }

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

Решение, вторая версия.

Дело в том, что QueryHints.FETCH переписывает запрос на использования JOIN’а. Но если в JOIN TABLE нет соответствующей строки (у статьи не определена необходимая роль), то не вернется и основная строка.
К счастью, на этот случай есть QueryHints.LEFT_FETCH.

Финальное решение будет выглядеть так:

@NamedQuery(name = "Article. getAllArticles", query = "SELECT s FROM Article s", hints = { 		@QueryHint(name = QueryHints.LEFT_FETCH, value = "s.role")) 

Один запрос к БД, все объекты, без нужны менять текст query как таковой.

ссылка на оригинал статьи http://habrahabr.ru/post/164495/

Практический опыт. Как наконец-то начать заниматься своим здоровьем?!

Я давний подписчик блога «GTD» (уст. Учись работать).

С 2008 года я активно читал статьи на Хабре и в Интернете и пробовал:
— вставать в 5:00 утра
— делать зарядку
— обливаться холодной водой
— бегать по утрам
— питаться правильно (особенно зачитывался вегетарианством)
— вести свои делало системе GTD (Things установлен на всех гаджетах 🙂
— выключать компьютер вовремя (хотя бы до 23:00)
— ну и конечно, сделать 100 отжиманий и кубики на животе…

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

Что изменилось в январе 2012 года?

Рано… в начале кратко обо мне:
— Возраст: 26 лет;
— Семейное положение: женат (с февраля 2012), детей пока нет;
— живем отдельно в Санкт-Петербурге.
— Работаю в офисе 5 дней в неделю с 8:30 до 17:30.
— Иногда задерживаюсь, но не часто.

Если Вы считаете, что опыт Вам не подойдет… Ваше право, но мы же понимаем, что оправдания всегда можно придумать? Я их придумывал лет 5. Я знаю о чем говорю.

У каждого есть один Большой Понедельник в году

Все мы планируем изменить нашу жизнь со следующего понедельника. Даже если приняли это решение во вторник. Круче понедельника, — только «с 1 числа следующего месяца», обычно мы придерживаем такие формулировки для РЕАЛЬНЫХ изменений в своей жизни.

Но только 1 раз в год… можно ПО-НАСТОЯЩЕМУ изменить себя — 1 ЯНВАРЯ*.
Примечание: обычно когда мы говорим с 1 января мы имеем в ввиду со 2го или 3го. По определенным причинам.

С такими мыслями я сел за рабочий стол 1 января

Встретив свой 26й новый год, я после праздников как и многие стал задумываться, что изменить в своей жизни. На глаза попалась книга Глеба Архангельского «Тайм-драйв. Как успевать жить и работать» (Я думаю на полках многих из вас пыляться книги о том «Как стать богатым и успешным», «Как все успевать» и т.д. Мы их с интересом читаем приговаривая»да, да, как точно и надо обязательно попробовать». Но уже реализовав все прочитанные советы в своей голове, — нам уже скучно делать это в реальности).

Важное дополнение: Будет, я думаю, честно уточнить, что первопричиной было то, что я встал на весы 2 января и увидел цифру в 89 кг (рост 190)… мой вес всю жизнь был около 81-83 кг. Дополнительная мотивация была в отражении в зеркале. Это и стало главным толчком к тому, что пора что-то менять.

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

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

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

Сделать обзор жизненных целей не так-то просто.

  1. Во-первых, не всегда понятно, чего хочешь.
  2. Во-вторых, страшно привязывать цели к срокам. Приходиться честно смотреть на свою жизнь и сталкиваться с неприятными истинами. Например, что хождение в спорт-зал последние 5-6 лет не приблизило меня к какой-то цели, а я просто топтался на одном месте в своей физической форме. А если я хочу добиться каких-то результатов, — мне нужно РЕЗКО увеличить нагрузки и частоту занятий.
  3. В-третьих, тяжело начать нести ответственность за свою жизнь. Одно дело мечтать о квартире, машине, спортивных достижениях, изучениях новых языков и путешествиях… другое дело, — отложить развлечения и, засучив рукава, заняться делом.

Моя карта долгосрочных целей

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

Тут нет никаких ограничений в ваших фантазиях.

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

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

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

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

Да, я женился в феврале 2012 года, практически доделал ремонт в квартире (пишу вам из кабинета на балконе, мечту о котором подарил Boomburum в своей статье несколько лет назад), да я сменил работу в 2012 году… но это во многом стечения обстоятельств. Строка же спорт и здоровье — всегда в ваших личных руках.

Моя цель — стать Железным человеком!

Рождение цели

Осенью 2011 года я на тренировке в клубе услышал разговор:

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

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

И тогда я набрал Ironman в Google и открыл Youtube… это сильно изменило мою жизнь.

Триатлон Ironman («Железный человек»): плавание — 3,86 км, велогонка — 180 км и марафонский бег — 42,195 км;

Круто подумал я… и нарисовал круг Ironman в строке спортивных целей в возрасте 30 лет.

Хм… сейчас, подумал я, я могу пробежать максимум 6 километров, плавая только брассом, а на велосипеде проезжаю максимум 50 км за раз… но так как в 30 лет я собрался пройти такую дистанцию, то в 29 лет я должен уже спокойно проходить Half-Ironman (плавание — 1,93 км, велогонка — 90 км и бег — 21 км;), в 28 хорошо было бы уже пробежать марафон в 42 км…

И нарисовав эти цели, я осознал, что придется начать тренироваться уже сейчас =)

Как ее достигнуть?

Задача: подготовиться за 4-5 лет к соревнованию Ironman:

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

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

Мне нужен был тренер, чтобы задача получила свое решение.

Варианты?

1. Персональные тренировки
Наверное наилучший вариант, но явно самый не бюджетный. В нашем фитнесе World Class тренировка стоит около 2000 рублей. Минимум разумно будет заниматься с тренером 3 раза в неделю… 2000 х 3 х 4 = 38 000 рублей в месяц. Не мой вариант.

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

2. Секции для любителей спорта

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

3. Удаленный тренер

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

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

Как это происходит?

Изначально ты заполняешь анкету о себе (обычные данные, проходишь небольшой тест с пульсометром и заполняешь результаты, пишешь занимался ли спортом, результаты с соревнований, какие у тебя есть возможности: фитнес с бассейном, стадион рядом, велосипед или велостанок, какой пульсометр). А также пишешь свои цели «ХАЧУ БЫТЬ КАК АЙРОНМЭН» =)

Потом взаимодействие происходит по следующей схеме:

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

Например:
— понедельник день тяжелый готов только поплавать легкое ОФП
— вторник свободен
— среда свободен
— четверг иду в театр вечером, готов заниматься 40 минут утром
— пятница свободен
— суббота выходной, не хочу вообще заниматься
— воскресенье свободен, хотел бы побегать в парке утром

2. Воскресеньем вечером ты получаешь план тренировок (что когда и в каком объеме делать).

3. Все что непонятно выясняешь по телефону или электронной почте.

4. Все что понятно делаешь =)

5. После каждой тренировки вечером выгружаешь данные из своего пульсометра на сайт (Garmin Connect, polarPersonalTrainer, Trainingpeaks.com) и пишешь комментарии по самочувствию и по ощущениям нагрузки.

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

Плюсы:
— небольшая стоимость в месяц;
— персональный план под твои возможности (как физические, так и временные);
— дополнительная мотивация. Даже персональному тренеру ты можешь позвонить и сказать «Макс, плохо себя чувствую, давай перенесем тренировку на следующую неделю — запара на работе». А вот написать вечером в воскресенье, «Юлиан, в общем из 5 запланированных тренировок я сделал только 2… остальные я… как-то устал… настроения не было…». Стыдно в общем писать такое.

4. Готовые платные планы по персональным тренировкам

На многих сайтах можно купить готовый план по подготовке к соревнованиям, в т.ч. по триатлону.
Например, на trainingpeaks.com.

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

Так я стал заниматься удаленно с тренером.

Как занятия спортом изменили мою жизнь?

1. Всегда ощутимые цели в жизни

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

Так я знаю, что в следующем году 7 апреля мы с девушкой летим в Париж на мой первый марафон 42 км.

А еще переплытие Босфора, многодневная командная велогонка ТрансАльп, марафон Пустыни в Сахаре — открывается целый мир, о котором ты раньше даже не задумывался!

2. Самочувствие и график дня

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

В результате прекрасно высыпаюсь, великолепно себя чувствую. Когда утренние тренировки легко заставить себя встать и в 5:00 утра. Потому что ты понимаешь — зачем ты это делаешь.

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

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

Любимая пока сомневается, не принимая активного участия в моих соревнованиях, только как болельщик.
Но не смотря на это уже пробежала на соревновании 5 км в Риге и ей понравилось… значит рыбка-то клюнула!

Резюме

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

Проснитесь 2 января, хорошо выспавшимся или не очень. Но главное в хорошем настроении.

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

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

А в любительском спорте — все зависит только от тебя лично!

Рекомендуемые источники (для почитать подробнее):
1. Youtube.com (поиск: ironman thiatlon. maraphon de sables)
2. Trilife.ru (отчеты);
3. Cycleon.ru (пример удаленного тренинга);
4. Trainingpeaks.com (самый функциональный сервис для триатлетов, онлайн дневник + готовые планы);

С новым ГОДОМ!

p.s.если будет интерес к данной теме, то могу рассказать о том, как начинал заниматься и рассказать о тех соревнованиях, в которых участвовал, а также написать самые интересные соревнования в мире, которые по-настоящему задают вопрос «А смогу ли я?».

ссылка на оригинал статьи http://habrahabr.ru/post/164463/

Самодельная подсветка на квадрокоптер

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

Набросал картинку поведения:

Далее собираем схему. Понадобится AVR-контроллер, способный работать на 16МГц, 4 NPN транзистора (например BC846B ), светодиодные ленты на 12Вольт, кварцевый резонатор 16МГц, два конденсатора 22пф, 5 резисторов 1кОм, кнопка без фиксатора или перемычка. Перемычка служит для включения режима настройки границ срабатывания стиков (с учетом триммеров) и настройки реверса газа и тангажа (делалось под мою аппаратуру Turnigy 9x и полетные контроллеры КК и Multiwii; в других конфигурациях, возможно придется переписывать код). Под использование других напряжений и светодиодных лент необходимо сделать выбор других транзисторов.

Электрическая принципиальная схема:

Использовал контроллер ATMega8A из более дешевых и доступных. Для прошивки можно использовать как и «5и контактный» программатор, так и любой другой. Я использовал USBasp, т.к. он поддерживается средой разработки Arduino. На плате сделал разъем внутрисхемного программирования. Для начала необходимо выставить фьюзы на контроллере для использования внешнего кварца на 16МГц. Выставлял программой «eXtreme Burner – AVR». Фьюзы Low EF, High C9. Конденсаторы у кварца лучше использовать 22пф, но также должно работать и от 18 до 30пф. При подключении к приемнику коптера сделал параллельные соединения проводников, которые подключил к своему устройству. Запитал устройство (+5В) от свободных контактов приемника, светодиодные ленты(+11,1) от платы разводки питания с аккумулятора на регуляторы двигателей.

Устройство нуждается в настройке. Рекомендую производить со СНЯТЫМИ пропеллерами во избежание чего-либо ужасного, т.к. может сработать арминг во время движения стиков. Лучше даже будет откинуть проводки, идущие на полетный контроллер или обесточить его.
Порядок настройки:

  1. Установить перемычку на устройство при отключенном питании.
  2. Включить пульт, приемник и устройство.
  3. Передние светодиоды моргнут длинным и коротким (_.) и затем все будут быстро моргать короткими (………) в течение 15 секунд.
  4. Во время моргания надо по два раза отвести стики в крайние угловые положения. Я делал вращательными движениями против часовой стрелки.
  5. При завершении моргания светодиоды погаснут и задние моргнут длинным и коротким (_.), показывая конец установки границ и запись в EEPROM. После этого перемычку снимаем.
  6. Далее отведено 5 секунд для установки реверса газа, в течение которых, по необходимости установить перемычку. Если перемычка не установлена, то реверса не будет, соответственно включится установка реверса, если перемычка установлена. По истечении 5и секунд мигнут левые передние и задние светодиоды. В этот момент опрашивается перемычка и производится запись в EEPROM. После этого перемычку снимаем.
  7. Далее отведено 5 секунд для установки реверса тангажа, в течение которых, по необходимости установить перемычку. Если перемычка не установлена, то реверса не будет, соответственно включится установка реверса, если перемычка установлена. По истечении 5и секунд мигнут правые передние и задние светодиоды. В этот момент опрашивается перемычка и производится запись в EEPROM. После этого перемычку снимаем.
  8. Отключаем устройство и включаем со снятой перемычкой.
  9. Далее необходимо проверить работу. Начинаем повышать газ и подсветка должна загореться. Если загорается при нижнем положении стика и гаснет при верхнем, то необходимо провести реверс настройки газа. Проверяем реакцию на отклонение рыскания. Подсветка должна мигать кругом в сторону отклонения. Проверяем отклонение крена и тангажа отдельно, а также вместе по диагоналям. Если при отклонении стика тангажа вверх, мигают задние светодиоды и отклонении вниз – передние, то необходимо провести реверс настройки тангажа. Для реверса необходимо опять выполнить настройку с начала(пункт 1).

После настройки, если снимались провода или питание с полетного контроллера, вернуть все подключения. Если после настройки устройства проводилось триммирование стиков, то настройку следует сделать повторно.

Первый вариант прошивки. Hex файл по ссылке:
r_lights_v1.rar

Мигание при рыскании не очень понравилось. Сделал, чтобы мигали все одинаково 10мс вкл/10мс выкл. По крену и тангажу очень даже понравилось.
Второй вариант прошивки — версия без вращения по рысканию.
r_lights_v2_wo_yaw.rar

Плюсы:
Динамичное оформление коптера.
Настройка реверсов.

Минусы:
После триммирования необходимо заново выполнить настройку.

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

Если кому интересна идея – пробуем и делимся отзывами!

ссылка на оригинал статьи http://habrahabr.ru/post/164491/