вторник, 3 января 2017 г.

SciPro: продолжаем обрабатывать данные на Python

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

SciPro (Scientific Processing) - рабочее название проекта, о котором я уже писал здесь. За прошедшие годы был расширен набор методов, появилась наследственность, расширены функции импорта и пр. Однако, общий принцип принцип остался неизменным - это объектно-ориентированное представление данных. Вот эти объекты:
  • Spectrum - данные формата (x,y), где 'x' может быть задан в длинах волн или частотах, а 'y' в линейном или логарифмическом масштабе
  • Oscillogram - (x,y), где 'x' - время, 'y' - линейный масштаб
  • Field - поле, формат (x,y), где 'y' - массив комплексных чисел, задающийся в алгебраическом или экспоненциальном виде
  • FROGTrace - работа с исходными данными FROG, здесь 'x' - двумерный массив длин волн и временных отстроек, а 'y' - массив измеренных интансивностей (также двумерный).
Кроме методов обработки у объектов также переопределены операции сложения, умножения и пр., что позволяет реализовывать конвеерую обработку данных последовательным вызовом соответствующих методов. На текущий момент данный набор перекрывает большинство моих повседневных потребностей в оперативной обработке и анализе, и я планирую развивать его по мере необходимости и возможности.

пятница, 20 марта 2015 г.

Cross-platform GUI for an OceanOptics spectrometers

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

Среди основных функций:
  • График спектра с измерениями и экспортом 
  • Установка времени экспозиции
  • "Динамическая экспозиция"
  • Корректировка шумового фона по показаниям с "тёмных пикселей" и/или по "сырому" измерению.
  • Усреднение получаемых спектров
  • Потоковая запись спектров в отдельные бинарные файлы
Найти всё это можно здесь. По ссылке есть исходники и готовые сборки для Windows 7 и Linux (обе для 32-х битных систем). Я надеюсь, что эта сборка может оказаться полезной для кого-либо, кроме меня, и буду благодарен за любую обратную связь :).

пятница, 4 ноября 2011 г.

Khameleon tutorial

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

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


Подсунуть конфиг можно разными способами:
1. Через параметр -c (./idpws -c <имя_файла>.cfg). 
2. Сделать симлинк <имя_файла>; на idpws и запускать с симлинка.
3. Запускать idpws без параметров, при этом в качестве .cfg.user будет использоваться khameleon.cfg, а в качестве системного - имя, указанное параметром cfgfile (не рекомендуется).


Теперь, собственно, про конфиги самого tutotial-а. Рассмотрим их в порядке возрастания сложности:


1.  echoterm.cfg
В файле записана конфигурация для соединения друг с другом двух модулей через конвеерный (stream) интерфейс. Рассмотрим значение каждой строчки.  


Раздел [connections] содержит описание соединений между модулями. Формат записи:
<номер_cоединения>="id_<модуль_от>;signal_<имя_сигнала>;id_<модуль_куда>;slot_<имя_слота>" 
Здесь мы фактически средствами Qt соединяем сигнал одного модуля со слотом другого. Предусмотрено по одному сигналу и одному слоту на модель типа stream: через "ImReady(QVariantList)" модуль передаёт обработанные данные дальше по цепочке, а через "StartProcessing(QVariantList)" принимает данные от другого модуля. Есть ещё дополнительный сигнал "myStatus(QString)", через который может передать какие-либо сообщения в главное окно программы, где для этого предусмотрен объект logger со слотом  append(QString), или timedAppend(QString), если требуется отобразить время прихода сообщения.
 

Далее раздел [widgets]. Здесь мы пишем какие виджеты из каких модулей следует выносить на главное окно. Формат следующий:
id_<модуль>\<параметр>=<значение>
 Параметры бывают следующими: wmain, woptions, где значение, это имя заголовка окна или заголовка блока, содержащего данный виджет; wmainobjname  должно быть уникальным для каждого модуля и используется при сохранении параметров окна; woptionsdialog (true или false) - следует ли отображать виджет опций в меню опций программы; ismainlayout (true) - применяется только к одному виджету wmain, и говорит о том, что он является главным, остальные - перемещаемые.



Идём далее, раздел [modules]. Именно в этом разделе мы указываем, какие именно модули необходимо загрузить, а также присваиваем им id. Параметр id должен начинаться с нуля, быть уникальным для каждого модуля, и возрастать без пропусков (т.е. после 3 не может следовать 5). Формат записи как и в предыдущей секции:
id_<модуль>\<параметр>=<значение>
 Отличие в том, что се параметры в данной секции начинаются с точки, что говорит о том, что они являются скрытыми параметрами модуля.  ".name" - имя загружаемого модуля, ".thread" - номер потока, в который необходимо поместить данный модуль. Кроме этих параметров каждый модуль может содержать любое число индивидуальных скрытых параметров. От обычных параметров их отличает то, что они задаются в файле конфигурации и не могут изменятся во время работы программы.  Они предназначены для того, чтобы задавать конкретный вариант поведения модуля в конкретной конфигурации.


Уф, с этим файлом разобрались. Дальше будет проще и интереснее одновременно.


2.  echotermfb.cfg
Пример соединения модуля, использующего потоковый (конвейерный) интерфейс, и модуля, работающего через пропагаторы. Здесь в пояснении нуждается секция [connections], а именно, 2-ое и 3-е соединения. Остальные секции повторяют прошлую конфигурацию.  
02="id_0;out;id_1;slot_StartProcessing(QVariantList)"
03="id_1;signal_ImReady(QVariantList);id_0;in"
Во втором соединении вместо указания сигнала для первого модуля используется имя пропагатора, out. Важно, что в конструкторе модуля в лист "hpg" должен быть довавлен элемент, с именем "out" и значением NULL (создание объекта пропагатора происходит в ядре во время соединения модулей). Также важно заметить, что для связки таких разнородных модулей можно использовать только пропагаторы без обратной связи, сигнал пропагатора "trySendList(QVariantList)", чтобы ловить пришедшие данные, и функцию "sendList(QVariantList)", чтобы отправить данные.


3.  echofbterm.cfg
Это также пример соединения разнородных модулей. Разность в конфиге с предыдущим примером минимальна, но заинтересовавшиеся могут найти немного нового в исходных кодах модулей.


4.  echofbtermfb.cfg
И наконец, пример взаимодействия двух модулей через пропагаторы. Здесь в секции [connections] последние две строчки заменены одной, но в конце появилась подпись "feedback":
02="id_0;interface;id_1;io;feedback"
Что здесь написано: пропагатор модуля id_0 соединить с пропагатором модуля с id_1, при этом первый пропагатор должен дождаться ответа от второго, и только после этого вернуть управления модулю с id_0. В конструкторе модуля такой пропагатор мы уже должны поместить в лист "hpgfb", и вместо функций "send(QByteArray)" и "sendList(QVariantList)" следует использовать read, write, readList и writeList, в зависимости от передаваемых данных. Единственное требование, оба модуля должны быть согласованы по типу данных. Т.е., фактически мы получаем возможность читать и записывать данные в модуль, и неважно, что он работает в другом потоке и живёт своей жизнью. Если заглянуть в код "ведомого" модуля (из которого читают), то там можно найти вызов функции readReturn writeReturn, readListReturn или writeListReturn. Если это обработка ведомого пропагатора, то необходимо вызывать одну из этих функций перед возвратом из обработчика. Какую именно - зависит от выполняемой операции и от типа данных. Данные, положенные в эти функции "вернуться" модулю, вызывающему соответствующие ответные функции из своего пропагатора.


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


воскресенье, 11 сентября 2011 г.

Khameleon is opened

Наконец-то случилось то, о чём было уже столько разговоров! Исходники khameleon теперь доступны на sourceforge.  Теперь можно качать и пробовать, брать за основу уже написанные модули и писать свои, пробовать адаптировать её к новым девайсам. С чего начать? После сборки проекта скопируйте файлы из bin/configs/echo в bin и укажите имя конфига программе при запуске (./idpws -c echoterm.cfg). Здесь записана простейшая конфигурация системы - эхо и терминал завязаны друг на друга. Текст из поля уходит в эхо, а оттуда возвращается в терминал. Для демонстрации оба модуля грузятся в различные потоки. Они связаны простейшим интерфейсом (Stream), аналогичным первоначальной конвейерной концепции. Однако сейчас это не единственная возможность, но об этом в другой заметке, где я подробнее опишу на примерах различные варианты взаимодействия модулей друг с другом.

воскресенье, 30 января 2011 г.

Khameleon Modular System - возрождение

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

Не буду повторять проблемы, о которых я писал в предыдущем посте, а перейду сразу к решениям.
1. Взамен концепции конвейера пришла концепция взаимодействия модулей через т.н. пропагаторы. Термин пришел из ядерной физики, где он означает "переносчик взаимодействия", или, буквально, от английского "propagate" - распространение. Возможно термин притянут за уши, но лучшего определения той сущности, которую я хотел создать, не нашлось. Это не труба (pipe), из которой к вам сыпется некое нечто, и в которое вы ссыпаете всё скопом. Это не очередь запросов (queue) и не сигнал, в терминологии Qt. Основная идея пропагатора состоит в том, чтобы обеспечить максимально-абстрактный интерфейс взаимодействия модулей друг с другом. Пропагаторы разделены на два подкласса, feedback и stream, т.е. те, что ожидают ответа от модуля и возвращают его (подобно фукциям read/write в блокирующем режиме), и те, посредством которых можно организовать "массовую рассылку" или поток (подобно функциям send/received). Внутри модуля создаётся список пропагаторов, через которые происходит взаимодействие, а ядро при загрузке создаёт сами объекты, и связывает их с другими модулями. Т.о. каждый пропагатор может являться точкой входа в модуль. В примеру, модуль построения графиков multiplot имеет пропагаторы plotxy и collectxy. Первый отвечает за рисование графика целиком, второй - за накопление и отрисовку потока данных. Взаимодействие с пропагатором внутри модуля выглядит как обычный вызов функции.

2. Многопоточность. Так получилось, что реализация многопоточности оказалась тесно связанной с реализацией идеи о пропагаторах. Просто по тому, что сейчас feedback пропагатор реализован таким образом, что связывать им можно только модули, находящиеся в разных потоках, иначе будет deadlock.
Как это работает? При загрузке модуль может попросить (через параметр в конфиге) для своей работы отдельный поток. Ядро стартует новый тред со своим eventloop-ом, и помещает туда модуль, вызывая для него moveToThread(). Поскольку работа пропагаторов крутиться вокруг qt-ной концепции сигнал-слотов, то все вызовы между модулями из разных потоков полностью безопасны.

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

На данный момент могу сказать, что с введением многопоточности и концепции пропагаторов жить стало заметно легче, и уже в ближайшее время я выложу кратенький обзор нескольких практически применимых творений. Это будет простая оболочка для снятия данных с измерителя мощности ThorLabs, и ModBus Debugger - приложение для отладки устройств по протоколу modbus (постоянный мониторинг регистров, флагов, запись в регистры).

понедельник, 24 января 2011 г.

ftd2xx library on wine

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

И так, исходные данные, автокоррелятор от Avesta project, подключающийся к ПК по USB, и программа Irtac для работы с прибором. В приборе USB аппаратно реализовано на микросхеме ftdi, что можно увидеть по логам при подключении устройства (dmesg или /var/log/messages). Программа Irtac собрана под windows с использованием коммерческой (закрытой) версии библиотеки ftd2xx. Реализации под linux нет и ждать не приходится, а перегружаться в винду ради одного прибора очень не хочется, тем более, что реально он часто нужен не один. Есть вариант с использованием виртуальной машины. Неопенсорсный virtualbox умеет прокидывать USB в систему, и, например, поляриметр от thorlabs так заставить работать удалось. Ситуацию осложняет то, что такой трюк оборачивается существенными тормозами в работе виртуалки, и Irtac тупо отваливался по таймауту. Под wine сама программа хоть и запускается, но работать с прибором не хочет. Оно и понятно, если я ей подсуну windows-версию библиотеки, то она понятия не имеет как работать с USB  в linux, а linux-версию программа, собранная под винду, по ясным причинам подцепить не сможет. И так, выход один, надо разобраться, как wine реализует свои библиотеки, и написать обёртку (fake) для linux-версии libftd2xx.
Немного рутины - необходимо достать исходники установленного у вас wine, распаковать их, потом наложить прилагаемый патчик, запустить configure и make, а после скопировать ftd2xx.dll.so и ftd2xx.dll.fake в /usr/lib/wine/ftd2xx.dll.so и /usr/lib/wine/fakedlls/ftd2xx.dll (или куда там установлен штатный wine) соответственно. Как получился этот патч? Сначала всё казалось проще пареной репы: у нас есть две библиотеки с идентичными функциями, значит, нам даже толком не надо ничего писать, а просто "связать" вызов функции из программы с той же функцией в нативной библиотеке. Сказано - сделано. Но было бы наивно предполагать, что Irtac сходу заработает, тем более что мы не знаем функции, которые он использует. Для тестирования я взял примеры из libftd2xx1.0.2 и собрал их как под linux, так и под винду (через mingw). Тут количество используемых функций и порядок их вывода строго определён, и можно отследить на каком месте что у нас порушилось. На первом этапе я никак не мог понять, как сделать этот самый вызов функций с одним и тем же названием и метался от простой сборки wine-библиотеки статиком с libftd2xx.a до написания обёртки с другим именем, из которой бы цеплялась нативная библиотека, а сама обёртка использовалась бы нашей fake-либой, и тем самым, мы избежали бы конфликта имён. Однако, найти правильный путь помогла wine-библиотека glu32. Здесь реализовано фактически именно то, что нам необходимо. А вся изюминка кроется в том, что заголовки функций для windows- и для linux-версии ftd2xx немного различаются. Это обстоятельство учтено в ftd2xx.h, и правильные заголовки определяются при сборке define-ами. Нам же необходимо подставить define-ы, которые использовались бы при сборке под виндой (чтобы прога могла найти функции), хотя фактически, собираем мы всё это дело под пингвином. Однако, после этого прозрения вылезла ещё одна проблема, и заключается она вот в чём. Функции, которые мы хотим отдать wine-у на экспорт, перечисляются в ftd2xx.spec, где первый параметр - некий номер функции... Я не понимаю, что это за номер, но на практике, этот номер как-то связан с тем, где именно находится данная конкретная функция. И если мы правильно пишем обёртку, но вместо номера ставим "@", что в соответствии с http://linux.die.net/man/1/winebuild означает автоматическое присвоение номера, то не факт, что этот номер совпадёт с реальным. И при попутке запустить программу получим что-то типа:
wine: Call from 0x7ef918f0 to unimplemented function FTD2XX.dll.32, aborting
wine: Unimplemented function FTD2XX.dll.32 called at address 0x7ef918f0 (thread 0009), starting debugger...
Но это и ключ к разгадке! Если мы знаем, какую именно функцию дергает приложение, а это можно установить по исходникам, то данный вывод означает, что эта функция должна находится по номеру 32, но её там почему-то нет. Ставим этот номер вместо собаки в ftd2xx.spec, и двигаемся дальше...

В результате, я написал обёртки для всех функций из ftd2xx.h кроме тех, где есть префикс W32 (просто я не знаю как уложить их параметры в концепцию wine). Получилось 49 штук. Из них я подобрал номера к 16-ти. Для этой процедуры я использовал примеры  BitMode, EEPROM/read, LargeRead, Simple и Timeouts. Вывод данных программ был идентичен про запуске в linux, и при запуске под wine-ом. В качестве "прибора" использовалась заглушка в виде микросхемы FT232RL с замкнутыми Rx/Tx и питанием от USB-порта. В ближайшее время буду пробовать на реальном железе.
Использование данного материала, а также поправки и исправления неточностей только приветствуются.
Патчик можно найти тут.

upd: Оказывается, кое-что всё-таки "уже украдено до нас". Так, вот тут автор ещё в 2005 году сделал вызов основных функций через libusb. Кроме того, он приводит spec, в котором уже указаны адреса для всех функций.. Что-то мне подсказывает, что их узнать можно гораздо проще, чем я описал выше...

upd2: Поразительно, но мне действительно удалось заставить эту прогу корректно работать с прибором! Однако, обнаружилось пара нюансов:
1. Значения Timeout-ов и LatencyTimer пришлось увеличить в 10 раз возможно, хватило бы и пары, но взял с запасом, и оно попёрло)
2. Если функция FT_ListDevices используется с флагом FT_LIST_BY_INDEX, то перед вызовом нативной функции стоит вызвать FT_CreateDeviceInfoList, т.к. без этого всё или виснет или crash-ится.
Обновлённый патчик со всеми этими изменениями, а также с некоторым количеством отладочной информации для wine-1.2_rc7 можно найти тут.

понедельник, 26 июля 2010 г.

MacrOS: Simply RTOS on Macroses

При всей бесперспективности повторного изобретательства велосипедов порой от этого праздного занятия просто невозможно удержаться. И вот сейчас, зачем нужна ещё одна пародия на RTOS, когда их и так полно, и тут есть несколько соображений. Во-первых, как ни крути, а своя рубашка ближе к телу. И во-вторых, после некоторого опыта писания прошивок под наши приборы я осознал, что для полного счастья мне необходимо совсем не много. Это немного и является отличительной особенностью этого произведения.
1. Таймеры. Программные, с характерной длительностью в 10-тки миллисекунд. Неблокирующие ожидания таймаутов. Wait-ы внутри задачи.
2. Кооперативная многозадачность. В топку приоритеты, вытеснения, жесткое распределение времени. Специфика такая, что прошивка состоит из множества небольших задач, которые не несут сложных расчётов, а большую часть времени ждут срабатывания какого-нить флага или исключения, или запускаются периодически, чтобы собрать данные.
3. Быстрое определение необходимости выполнения задачи, и переход на следующую в противном случае. Переключение контекста - дорогостоящая операция, поэтому хотелось бы понять, чего ждёт задача и проверить это условие не входя в контекст задачи.
Вот, пожалуй, три вещи необходимые для счастья.
Про ОС как таковую тут можно говорить вообще с большой натяжкой. По сути нам необходим лишь планировщик, который бы в цикле перебирал все задачки. Прототипом был планировщик из какого-то примера с www.atmel.com. Уж очень мне нравится фирма, и даташиты толковые, и контроллеры функциональные, производительные и не обременённые ненужными сложностями, и примеры есть, и gcc умеет компилить под avr. Да, изначально MacrOS написана под AVR архитектуру, но для переноса на другой контроллер требуется лишь переписать код для миллисекундного таймера, т.к. всё остальное написано на C. Это в теории. на практике надо переписывать всё, что касается прерываний, а это usart, spi и т.д. Но вернёмся к планировщику. К сожалению, он не умел управлять контекстом, а наличие такой фичи сильно облегчает написание каждой отдельной задачи.
Спасение я нашел в функции setjmp. Но её использование весьма нетривиально, и именно тогда родилась идея, "а почему  бы не обернуть ей вызов в макрос?" Сказано - сделано. Заводим на каждую задачку структуру, типа task, в которой храним контекст и ещё немного инфы, о которой скажу позже. Пишем макрос MOS_Scheduler(), который сохраняет контекст текущей задачи и делает longjmp в контекст планировщика. Всё просто! Планировщик, получив управление, переходит к следующей задаче, сохраняет свой контекст и делает longjmp в следующую task-у, и так по кругу. Но это слишком медленно, учитывая что часто надо проверить лишь состояние флага. Делаем финт ушами, в структуру задачи добавляем ссылку на фнкцию checker и пару переменных, которые будем передавать этой функции как параметры. Одна - указатель, вторая - значение которое надо проверить. После в планировщике вместо перехода в задачу сначала делаем вызов checker-а, а по его результатам определяем, требуется ли переход в задачу. И тут Остапа понесло: можно сделать checker-ы разного типа, которые будут проверять налицие флага, отсутствие, делать anr или or со значением в указателе. В общем, практически всё, что может понадобится для отслеживания нажатия на кнопку или отслаживания аппаратного флага.
Теперь ввести таймеры не составит большого труда. Ставим условие, что проверка на таймаут должна быть быстрой, а старт можно сделать и дольше. Создаём массив с таймерами. Заводим глобальный счётчик, который инктементируется от аппаратного таймера. При старте высчитываем время, когда таймер закончится, и сбрасываем флаг таймаута. В прерывании от аппаратного таймера пробегаемся по всему массиву, и выставляем флаги для тех таймеров, чьи значения меньше текущего. Значения этих флагов позже проверятся в задаче, которая ждёт таймаута.
На этом пока всё, минимально-необходимое окружение есть. Надеюсь, что это окружение будет полезно не только мне, и велосипед приобретёт статус ноу-хау. Исходники имеются и будут выложены, как я приведу их в божеский вид. Нетерпеливым просьба писать на мыло.

воскресенье, 6 июня 2010 г.

О значении текущего момента

Рассуждая как-то о важности и значении поступка, вот этого, конкретного, который надо сделать здесь и сейчас, и который изменит всю мою дальнейшую жизнь, я вдруг набрёл на интересную аналогию, суть которой и хочу раскрыть далее.
Для начала опишу ситуацию более определённо. И так, основной вопрос состоит в том, насколько наше текущее положение здесь и сейчас определено всей предыдущей цепочкой наших решений и зависит ли это положение от каждого из этих решений в отдельности. Или, что то же самое, насколько поступок здесь и сейчас определяет то, куда мы попадём в будущем. Попутно также намечаются небольшие рассуждения о судьбе, силе воли, предназначении... А аналогия, от которой я буду отталкиваться, навеяна одной из лабораторных работ по моделированию хаоса в курсе MATLAB, преподаваемом по-моему на втором курсе ФФ. Суть работы в том, что мы берём небольшой ящик, помещаем туда несколько шаров, даём им какие-то начальные скорости и, запустив время, рисуем за каждым шариком его след. Если теперь через некоторое небольшое время мы остановим шарики, и заменим все скорости на противоположные (обратим время), то они пойдут в точности по своим следам, и, в конце концов, вернуться на свои исходные позиции. Соль заключается в том, что если подождать немного подольше, то шарики на свои позиции уже не вернуться, т.е. они фактически "забудут" то место, откуда они сюда попали. Потом мы говорим, что, начиная с этого времени, мы уже наблюдаем хаотичное движение шаров, которое невозможно рассчитать. В модели это происходит из-за ошибок округления при взаимодействиях шаров на малые углы. В реальности всегда есть трение, шероховатости, квантовая неопределённость, в конце-концов, да и количество шаров измеряется в ~10^23 шт.
А теперь представим, что мы, каждый человек в отдельности, представляет из себя такой шар, родившийся когда-то в каких-то начальных условиях, и взаимодействующий с огромным количеством других людей на протяжении всей жизни, с какими-то только вскользь, а другие накладывают существенный отпечаток на дальнейшую судьбу. А раз так, раз мы - это те же шары, то память наших взаимодействий также не безгранична, и чем дальше наши поступки в прошлое, тем менее они значимы для нашего положения здесь и сейчас. Или по другому, что бы мы сейчас не совершили, никому не известно, как обернётся для нас будущее.
Это следствие в своей непосредственной интерпретации оказывается весьма не радужным. В самом деле, это своего рода заявление на то, что самые отважные, смелые поступки, спустя время, не будут иметь никакого значения, а значит, и совершать их не было никакого смысла. И ничего не остаётся, как плыть по течению и ждать, пока кривая куда-нить не занесёт. Однако, мы ведь не бездумные шары, которые просто подчиняются законам. Здесь я хотел бы сделать акцент на том, что из шаров можно взять только механизм взаимодействия и его свойства. Я вовсе не утверждаю, что всё вокруг хаос без цели и смысла. Уж слишком такие утверждения противоречат самому факту нашего существования (жизни на земле). И в тоже время заострю внимание на том, что этот механизм закрывает для нас столь праздную возможность рассуждать в духе, что "вот я поступлю так-то, и жизнь изменится, изменится непременно, именно в этот понедельник"... А также менее безобидные рассуждения типа того, что "в 5 лет я встретил того-то, и эта встреча предопределила всю мою жизнь".
Можно предположить наличие некоторого течения в этом "хаосе", слабой и незаметной силы, которая становится заметной лишь в спорных ситуациях или на большом протяжении времени, и назовём это течение судьбой. Также важен тот факт, что "каждый шарик имеет своё течение". В этом случае получается, что хоть и от текущего решения практически ничего не зависит, но, в конечном счёте, это течение приведёт нас туда, куда нам было предопределено судьбой. В этом смысле, если человеку было суждено работать в науке, то всё сложится именно так, если же суждено быть строителем, то любые его действия в конечном счёте приведут его к этому. И кирпичи людям на головы также просто так не падают. Однако, всё равно грустно, надо добавить ещё один пункт.
Предположим наличие силы воли. Априори известно, что это величина, которая может изменяться в очень широких пределах. Если люди со слабой волей, тогда основную роль будет играть то незаметное течение, которое мы назвали судьбой. Но где же тогда окажется человек с сильной волей, если справедлив тезис о том, что его текущие старания ничего не значат. Ответ прост - воля есть по сути такое же течение, что и судьба, только в том направлении, в котором нам угодно. Но как же быть с решающими поступками, необходимость которых очевидна. Ведь нельзя искать только лёгкие пути, тогда это будет сравни безучастному следованию по течению. Делать смелые и отважные поступки необходимо, но на такой поступок необходимо ещё а) осознанно выйти, и б) осознанно принять решение. В случае утвердительного ответа на оба положения имеем дело с силой воли, в случае отрицательного - с действием судьбы.
И последнее, сухой остаток, что же надо делать, чтобы добиваться целей? Во-первых, не стоит слишком расстраиваться, если текущая ситуация несёт в другом направлении, здесь мало что можно предпринять. Но ветер изменится, и когда он изменится, важно вновь вспомнить о своей цели, и правильно сориентироваться в новых условиях. Поэтому, что необходимо во-вторых, так это думать о цели. Думать постоянно, подсознательно создавая тем самым то течение, которое приведёт нас туда, куда нам надо. Думать и совершать поступки, здесь и сейчас, каждый день. 

пятница, 22 января 2010 г.

Khameleon Modular System на пороге качественных перемен или полного фиаско.

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

И так, в основе проекта лежала концепция конвеерной обработки данных, где каждая цепочка конвеера предоставляет из себя независимый, динамически подгружаемый модуль, а связаны они между собой при помощи компактного (по определению, ограниченного и замкнутого) интерфейса. При реализации первой же конфигурации стало ясно, что одного конвеерного подхода крайне мало для реализации полноценной программы. И рушится всё на простой задаче реализации различных типов интерфейсов, т.е. на задаче ввода/вывода. Эта задача просто напросто подразумевает двухсторонний обмен сообщениями между модулями, который принципиально не уживается с конвеером, двигающимся в одну сторону. Тогда на этот факт небыло обращено должного внимания, и были введены кастыли в виде обратных запросов, что как-то спасло положение и ввело некую долю неразберихи. С ростом количества модулей эта неразбериха росла, как минимум, по квадратичному закону. В итоге, разобраться во всей этой паутени уже невозможно практически никому. Я сам, после перерыва в несколько месяцев, принял одну фичу за баг, и заслуженно, не имея при этом никакой возможности тот баг исправить, ибо рушилось многое. Ситуация усугубилась при попытке сконфигурировать систему для мониторинга и управления лазером, что находится сейчас в разработке. В этой задаче требовалось лишь отправлять и получать данные с прибора, обернув это в дружелюбный графический интерфейс. В интерфейс-то это обернулось, но внутренняя структура модуля ужасна, не очевидна и неэффективна. В результате имеем следующее. На данный момент в системе нет никаких инструментов для эффективной реализации интерфейсов ввода/вывода (com, ethernet and etc), а также протоколов. Модель конвеера для этого оказалась непригодной. Уже есть, однако, новые идеи, но о них в другой раз.

Передача сигналов между модулями. По поспешности и неопытности была выбрана обёртка QVariant. Однако лишь недавно я узнал, что QVariant не является по умолчанию зарегистрированным типом. Другими словами, Нельзя обернуть  QVariant в QVariant. А я то думал, отчего это у меня не работают отложенные конекты. Видимо, их реализация принципиально отличается от прямых конектов, которые работали. Следующий на примете тип QVariantList или QByteArray, но для их введения необходимо исправить все модули.

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

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

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

четверг, 21 января 2010 г.

Atmel AVR USI TWI library

Довелось на днях разбираться с тем, что предоставляет нам Atmel для взаимодействия с устройствами по протоколу i2c. Производители решили, что реализовывать раздельно spi и i2c много чести, и предоставили нам набор "рассыпухи", из которой мы сами можем соорудить один из интерфейсов, или придумать свой. Основные составляющие этого набора это сдвиговый регистр для обмена данными, 4-х битный счётчик для подсчёта количества пришедших бит, и различные переключалки, для выбора типа протокола, тактового генератора и пр плюс кое-какие прерывания под это дело. Всего три 8-ми битных регистра.
Первым делом глянул в гугл на предмет готовых реализаций i2c на этом железе. Однако, может я плохо смотрел, но нашел только исходники на аппаратный twi. Про TWI на USI были упоминания рядом с IAR (навороченная среда разработки под МК), но что тянуть палёный и непонятный код. Было решено написать "библиотечку" на основе исходников под аппаратный twi.
Собственно, оттуда я взял только названия функций. Посылка на старт, стоп и клоки реализуется программно, т.о. функции у нас получились блокирующие, и во время пересылки мы не сможем больше ничего сделать. Но поскольку twi мне нужен только для начальной конфигурации пары девайсов, то такая не совсем оптимальная реализация меня устраивает.
Во время внимательного чтения даташита на предмет управления с этими тремя регистрами, и попытки оживить всё это дело, обнаружилась пара совершенно тонких и не очевидных нюансов, ради которых и я пишу эту заметку. В документации о них написано весьма вскользь, и, как мне кажется зря.

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

2. Передача данных из USIDR на порт. Этот момент менее описан в доках, по этому за правильность не ручаюсь, но когда я сначала выставлял на порт SDA нуль, а после писал данные в USIDR и пытался их оправить, то линия никак на мои действия не реагировала. Однако, когда я сначала выставил на SDA единичку, после чего, чтобы таки выставить его в нуль для посылки start, записал 0x00 в USIRD, и только после этого записал адрес устройства и попытался его отправить, то всё заработало. Эврика, как говорится :) Итого, рецепт такой. Пишем в PORT на пины SDA и SCL по единичке, потом делаем их выходами, а после рулим через USIDR и флаг USITC из USICR.

Исходники можно взять по адресу http://galilley.at.nsu.ru/usitwi.tar.bz2, но есть нюанс, проверена работоспособность только функций на запись, функции на чтение не отлажены, хотя теоретически должны работать.

четверг, 14 января 2010 г.

linux4sam: соображения о работе с TFT LCD

Идея о подключении экранчика к плате на AT91SAM9260 уже давно витает в воздухе. Товарищь alfamayonez.ru, героически повторив подвиг народных умельцев, показал возможность работы по SPI с дисплеем от телефона samsung s65. Однако, такое решение сложно использовать для дальнейших разработок из-за ограниценных размеров самого экрана, а во вторых, чтобы это"счастье" обновлялось с более/менее приемлемой скоростью ему надо отдавать шину SPI полностью, а она нам может ещё понадобится. Вопрос состоит в следующем: можно ли, и если да, то как подключить к нашей плате дисплей размером в несколько дюймов, доступный для покупки, без больших костылей и без большой загрузки ЦП.
После двух дней изучения гугла на предмет того, что у нас вообще бывает, получилась вот какая картина.

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

2. Extended Bus Interface (EBI). Такая шина присутствует на нашем контроллере и предназначения для подключения переферии типа CompactFlash, SDRAM и много чего ещё (см. datasheet). На некоторых дисплеях, как правило до 3-5 дюймов, есть 16-ти битный параллельный интерфейс. И при беглом взгляде он совместим с EBI. Т.е. в таком случае получается всё просто, берём LCD с таим интерфейсом и напрямую вешаем на EBI. Проблема в том, что такая шина есть только у LCD с малой диагональю. По крайней мере я не видел этой шины у дисплеев с диагональю 7' и выше.

3. Ещё бывает RGB. Цифровой параллельный интерфейс, где используются несколько служебных сигналов, и шина данных, шириной в (разрядность одного цвета)х3, где за один такт передаётся значение R, G и B для одного пикселя. В подробности работы не вникал, но судя по всему, вывод на экран осуществляется просто последовательной передачей данных для каждого пикселя от первого до последнего. Если требуются большие расстояния, то можно постараться и найти чип для преобразования этой шины в LVDS и обратно. Ещё одна главная особенность состоит в том, что мы не можем напрямую подключить эту шину к нашему контроллеру, необходимо нечто вроде "видеокарты", которая и будет заниматься полной перерисовкой, а мы бы только записывали в память картинку, которую хотим отобразить. Первое, что пришло на ум, так это поискать интерфейс EBI<->RGB. Оказалось, что это вещь весьма экзотическая. Из того, что наиболее близко к этой задаче нашел решение на fpga от Altera (Avalon LCD Controller), но сомневаюсь что такую штуку будет легко достать. Т.о. в нашем случае остаётся практически один выход, это использование микроконтроллера со встроенным контроллером LCD. Благо, это уже не редкость, и имеется в МК тойже линейки AT91SAM9263.

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

пятница, 9 октября 2009 г.

Cross compiling toolchain своими руками

Любопытство, довольно затратная, но интересная штука. Если не отмахиваться от него, то можно давольно далеко зайти только от того, что тебе интересно. Just for fun, как говорят на западе :)

Необходимые пояснения
Когда я только-только начинал думать о кросс-компиляции, мне были непонятны самые элементарные вещи, наподобии того, как происходит эта компиляция, нужен ли специальный компилятор, если gcc знает разные архитектуры, то можно ли и как его научить собирать код под них?? Не помню, нашел ли я где-то прямой ответ, но если взять сухой остаток, то получим следующее:
Для сборки под другую платформу нам необходим компилятор, собранный таким образом, чтобы работать на нашей платферме, а генерить бинарники под целевую (target), что очевидно. Потом, есть архитектура как такавая (x86, ARM, etc..) и есть различные её модификации (или ядра, варианты, типы процессора, не знаю как точно назвать), это i386, i586, etc. Архитектура включает в себя модификации, т.е. компилятор, собранный под x86 теоретически умеет делать бинарник под любой процессор данной архитектуры. Что значит "компилятор, собранный под архитектуру"? Это значит, что тип архитектуры, под которую компилятор будет собирать бинарники, определяется при сборке компилятора. Есть комерческие кросскомпиляторы, которые, по всей видимости, представляют из себя обыкновенный gcc с особыми параметрами сборки и, может, какими специфическими патчами. Собственно вопрос, на который я хочу ответить - "можно ли, вряв исходники обычного компилятора, и того, что ему там ещё надо по зависимостям, собрать мало-мальски нормальный кросс-компилятор с нуля?". Ответ - да, и далее я приведу общую методику сборки, которая, я надеюсь, применима для сборки под любую другую архитектуру. В конце заметки есть ссылка на архив с поправленными/искалеченными spec-ами и дополнительными патчами. Я не буду отдельно расписывать параметры сборки, ибо это всё есть в spec-ах.

Используемые версии
И так, мы будем собирать rpm-based toolchain для ARM на базе ALTLinux. Пакеты, для сборки стянуты из актуальной версии branch 5.0. Вот то, что нам понадобится:
binutils-2.18.50.0.9-alt5.src.rpm
gcc3.4-3.4.5-alt8.src.rpm
glibc-kernheaders-2.6.27-alt3.src.rpm
glibc-2.3.5-alt5.src.rpm
gcc4.3-4.3.2-alt9.src.rpm
glibc-2.9-alt5.src.rpm

Собираем binutils
Тут всё просто, как оказалось в дальнейшем. Надо лишь дополнительноуказать опцию --target при сборке и указать пути. В нашем случае это arm-alt-linux-gnueabi-, если мы хотим получить eabi-компилятор. Везде далее в качестве target используется эта же цель. С путями получилась засада, с одной стороны я хз какие они должны быть, а с другой от версии к версии компилятора расположение файлов "по умолчанию" отличается, что тоже вносит путаницу. Я выбрал общий префикс для всех пакетов /usr/lib/arm, по которому располагается структура катологов компилятора. Похоже, что сами собираемые утилиты уже расчитаны на более тесную и корректную интеграцию с системой без дополнительного префикса, но я этот момент не осилил.

Собираем gcc-3.4
Отрезаем всё, что можно отрезать, и что мешается нам при сборке. Любой ценой собираем минимальный компилятор для С. Без плюсов, без библитек, без потоков. Для всего этого понадбятся glibc, которых у нас сейчас ещё нет. Почему версия 3.4, чуть позже.

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

Собираем glibc-2.3.5
Берём наиболее старую версию glibc, доступную в репозитории, исключительно из-за того, что онаотносительно легко собирается с потоками linuxthreads. Для сборки posix-потоков нужны glibc. Сборку надо пройти также любой ценой и любыми патчами/костылями. Более новую версию glibc-2.5.1 с linuxthreads мне собрать не удалось, а более старые не собираются gcc4 и выше, т.о. чтобы собрать glibc-2.3.5 нужен С-компилятор gcc-3.4.

Собираем gcc4.3
В черновую. Но тем не менее, сейчас нам окружение уже позволяет собрать C и C++ компиляторы с поддержкой потоков. При сборке компилятор ешё собирает какие-то свои либы, и возникли проблемы с передачей параметров этим либам. Кастыли в приложеном архиве эту проблему устраняют.

Собираем glibc-2.9
И наконец, делаем рывок, и собираем последнюю версию glibc. При первой сбрке придётся отрезать пару вещей (патчи прилагаются), чтобы эта сборка прошла. Неприятности вызваны тем, что gcc4 собран со старыми glibc, пересоберём его

Повторная сборка gcc4.3 и glibc-2.9
И последний этап - "чистовая" сборка компилятора и glibc. На этом этапе проблем быть практически не должно, а некоторые кастыли, которые мы подставили по glibc на прошлом этапе, сейчас можно убрать.
Да, я не говорю о том, что после очередной сборки пакета мы сначала должны его установить, и лишь после этого продолжать сборку другого пакета.
И так, сухой остаток, мы имеем  arm-binutils-2.18.50.0.9-alt5, arm- glibc-kernheaders-2.6.27-alt3, gcc4.3-4.3.2-alt9, arm-glibc-2.9-alt5, т.е. весьма свеженькие версии в toolchain, которые ещё и могут быть собраны с нашими специфическими опциями. Работа данного toolchain пока проверена только на сборке ядра 2.6.27 и busybox. Сборка этих компонент прошла успешно, вылезел ли что при дальнейшей эксплуатции - неизвестно.

Известные проблемы
При сборке пакетов не работает AutoReq, пришлось отключить. Пути, по которым устанавливается toolchain, хоть и являются болееменее рабочими, но не являются правильными. При сборке glibc нет возможности корректно запустить тесты, но вроде работает без них (знаю что косяк, но осилить не могу).
Спасибо за внимание, надеюсь что мой небольшой опыт сможет кому-нить помочь.
P.S. Да, обещанная ссылка - http://galilley.at.nsu.ru/toolchain-addon.tar.bz2

понедельник, 5 октября 2009 г.

MailMan для малого предприятия

Введение
Как часто вам приходилось рассылать объявления на несколько адресатов? Вести обсуждение проблемы по почте с несколькими людьми одновременно? Искать в архиве писем решение проблемы годичной давности?
Оказывается уже давно есть простые иструменны, эффективно решающие эти проблемы - списки рассылки (http://ru.wikipedia.org/wiki/Рассылка_электронной_почты).
Принцип работы прост - для того чтобы распространить сообщение между подписчиками необходимо отправить письмо на специальный e-mail рассылки. Примером рабочей рассылки может служить http://lists.altlinux.org/ где идёт постоянное обсуждение проблем разработки и использования решений ALT Linux между членами сообщества. Применение такого подхода в рамках малого предприятия позволит структурировать рассылку объявлений, вынести часть дискуссий в рассылку и даёт возможность каждому участнику быть в курсе.
Для полноценного использования списков рассылки требуется, чтобы сервис работал на сервере с доменным именем. Однако если с регистрацией заморачиваться не хочется, а рассылки хочется, то можно немного пораскинуть мозгами, и поднять сервис на локальной машине а пересылку писем вести через сторонний внешний сервер. В качестве сервиса управления рассылками выбран один из наиболее популярных - mailman, и о том, как заставить его корректно работать из локальной сети, и пойдёт речь дальше.

Необходимый инструментарий
В качестве дистрибутива я использую ALT Linux 4.1, где всё необходимое есть в пакетах, и это надо только поставить и настроить. И так, ставим MailMan. Он требует, чтобы на машине работал сервер электронной почты. Ставим postfix, на всякий случай прихватываем модули для авторизации по sasl и отправку через cyrus. Fetchmail для сбора почты снаружи и передаци её postfix-у.
Алгоритм следующий. Поднимаем postfix, учим его отправлять локальные письма напрямую, а все остальные через внешний smtp. Создаём необходимые ящики на любимом сервере, пусть будет example.mail.ru. Адресов понадобится несколько, хотябы 4 на одну рассылку. Объясняем postfix-у как авторизовываться на smtp в зависимости от отправителя. Настраиваем fetchmail на проверку новосозданных ящиков и запускаем демоном. Запускаем MailMan и настраиваем там нашу рассылку.

Настройка postfix
Тут наступил на давольно редкие грабли. Почтовый сервер, на котором я остановился, требовал авторизацию открытым текстом поверх TLS. Погуглив и почитав маны на postfix, понял, что обучить его этому нельзя. Решение следующее - запускаем демоном stunnel:

stunnel -c -r example.mail.ru:465 -d 11125
В /etc/postfix/main.cf добавляем следующее:

relayhost = [127.0.0.1]:11125 #хост для отправки писем наружу
mynetworks = 127.0.0.1 #обслуживаем только локальные запросы
smtp_sender_dependent_authentication = yes #в зависимости от отправителья выбираем аккаунт для smtp
sender_dependent_relayhost_maps = hash:/etc/postfix/sender_relayhost #список акаунтов
smtp_sasl_auth_enable = yes #говорим о необходимости регистрации
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd #перечисляем используемые аккаунты
smtp_sasl_security_options = #очищаем опции защиты
transport_maps = hash:/etc/postfix/transport #перечисляем случаи, когда почта должна доставлятся локально
myorigin = localhost #имя нашего хоста :)
recipient_canonical_maps = hash:/etc/postfix/recipient_canonical #таблица замен

alias_maps = hash:/etc/postfix/aliases, hash:/etc/mailman/aliases #файлы с алиасами

Тут стоит заметить, что если при отправке письма обратный адрес, и аккаунт, с которого произошла отправка, будут не совпадать, то такое письмо рискует попасть в спам. По крайней мере google делает именно так.
Далее полагаем, что мы хотим создать список рассылки mylists.

/etc/postfix/sender_relayhost
mylists@example.mail.ru [127.0.0.1]:11125 #адрес, на который необходимо посылать письма в рассылку
mylists-bounces@example.mail.ru [127.0.0.1]:11125 #отсюда будут приходить письма из рассылки
mylists-request@example.mail.ru [127.0.0.1]:11125 #отсюда будет приходить запрос на регистрацию
mylists-owner@example.mail.ru [127.0.0.1]:11125 #сюда надо писать письма для владельца рассылки
По хорошему, mailman-у надо ещё столько же адресов для связи с администраторами, можераторами и пр, но этот функционал мы не пользуем, и адреса, соответственно, не создаём.

/etc/postfix/sasl_passwd #перечисляем используемые аккаунты с явками и паролями
mylists@example.mail.ru mylists:xxx
mylists-bounces@example.mail.ru mylists:xxx
mylists-request@example.mail.ru mylists:xxx
mylists-owner@example.mail.ru mylists:xxx
/etc/postfix/transport
localhost.localdomain local: #письма с таким доменом отправляем локально
localhost local:

/etc/postfix/recipient_canonical
@yourhost.yourdomain @localhost.localdomain

Эта таблица требуется в том случае, если и машини есть какие-то абстрактные имя и домен, и они пролазят в заголовки писем или мешают корректно работать transport-ам. Хотя наверно это содержимое можно было бы просто перенсти в /etc/postfix/transport

На этом с отправкой писем вроде всё, хотя мелкие детали я мог и упустить. Перед дальнейшим шагом необходимо проверить работоспособность сервера, например mutt-ом. Перед запуском postfix не забываем сделать postmap для всех хешей.

Настройка fetchmail
Здесь всё гораздо проще.

cat ~/.fetchmailrc
set syslog
defaults protocol pop3, timeout 90, nokeep, fetchall, ssl
poll "example.mail.ru" user "mylists" there with password "infibgal" is mylists here;
poll "example.mail.ru" user "mylists-request" there with password "xxx" is mylists-request here;
poll "example.mail.ru" user "mylists-bounces" there with password "xxx" is mylists-bounces here;
poll "example.mail.ru" user "mylists-owner" there with password "xxx" is mylists-owner here;

Локально пользователей mylists* может не быть. Необходимые алиасы настраиваются автоматически mailman-ом при создании рассылки mylists.
Запускаем fetchmail демоном:
#fetchmail -d 90
Запуск можно прописать в загрузочные скрипты.

Настройка mailman
Теперь у нас всё подготовлено для работы mailman-а. Первоначальную настройку можно выполнить через alterator. При этом в качестве адреса веб-сайта обязательно указываем ip нашей машины, или ммылки внутри mailman-а не будут работать. В качестве адресо почтового сервера указываем example.mail.ru. Используемый MTA - postfix.

После этого идём не страничку админимстрирования http://наш.ip.ад.рес/mailman/admin и создаём первый список рассылки mylists. После этого обязательно обновим алиасы postfix-а, запустив newaliaces.
Теоретически, всё должно работать, если я не забыл каких-нить мелочей. Ура!

понедельник, 16 марта 2009 г.

linux4sam: загрузка по сети

И так, начинаем осуществлять обозначенный ранее план.
Очевидно, было бы глупо сразу бросаться и перепрошивать флешку, или, что ещё хуже, сам контроллер. Операции эти весьма рискованны, и потому будем начинать с наименее рискованных, тем более что u-boot предоставляет широкие возможности.
Речь пойдёт о загрузке по сети.
Сразу скажу, что более подробную информацию о опциях u-boot и его возмжностях можно найти в соответствующей вики на http://www.denx.de/wiki/DULG/Manual, а дальше же пойдёт описание того, с чем мне непосредственно пришлось столкнуться. И так, с помощью u-boot, похоже, можно загрузиться с чего угодно: встроенной NAND Flash, внешней MMC (слабо могу представить), COM-порта, Ethernet. Сама процедура загрузки состоит в следующем - с помощью команд u-boot копируем ядро и корневую файловую систему в память, и передаём управление на первый адрес. Чтобы ядро прошло эту процедуру, его после сборки надо обработать одной утилитой, идущей вместе с u-boot. О тонкостях компиляции ещё будет написано, по этому сейчас в это не углубляемся.
U-boot-у можно установить различные переменные окружения и последовательность команд, которые он выполнит перед тем, как передаст управления ядру. При этом загрузчик умеет сохранять внесённые параметры... видимо во флеш МК. И так, что же мы имеем:
U-Boot> printenv

bootcmd=run boot_df
bootdelay=3
baudrate=115200
tftp_update=tftpboot 20400000 zlinux; cp.b 20400000 c0038000 170000; tftpboot 20400000 rf
tftp_boot=tftpboot 20400000 zlinux; tftpboot 21100000 rootfs; bootm 20400000
boot_df=cp.b c0038000 20400000 170000; cp.b c01a8000 21100000 277fff; bootm 20400000
ipaddr=192.168.0.136
netmask=255.255.255.0
ethaddr=00:1f:f2:00:00:00
serverip=192.168.0.2
stdin=serial
stdout=serial
stderr=serial
- по сути, для нас подготовили три режима. 

    1. режим по-умолчанию - boot_df - копирования ядра и корневой фс в память с флешки и запуск. 
    2. tftp_boot - загрузка по сети - копирования ядра посредством tftp в память и запуск. tftp настроен в эмуляторе, но перенос его на локальную машину не составляет никакого труда - надо лишь установить сервер, настроить xinetd, положить образ ядра и фс в папку tftp. В принципе, пожно даже подцепить к этому делу dhcp, при этом вместо tftpboot следует пользовать bootp. 
    3. tftp_update - обновление ядра на плате - копирование с фтп в память, а из памяти во флеш, просто и удобно.

Надо сказать, что всё это скорее всего описано на соответствующем форуме, куда также можно обратиться.
Кроме таких плясок с перекидыванием ядра и rootfs туда-сюда есть ещё возможность подключить корневую фс прямо по сети посредством nfs, при условии что ядро сконфигурировано соответсвующим образом. Это наиболее интересный вариант, т.к. позволяет полностью избавиться на девайсе от всех устройств хранения, а сборку и отладку вести на "большой машине" оперативно контролируя результат.
И в заключении, ещё одна деталь - параметры ядра. Их также можно скормить u-boot, но разработчики почему-то пошли другим путём, и указали необходимые параметры непосредственно в ядро при сборке. По этому, скажем, если вы захотите изменить в памяти адрес расположения rootfs, то ядро будет в панике.

#cat /proc/cmdline
root=/dev/ram0 rw initrd=0x21100000,0x500000 console=ttyS0,115200 mem=32M
Продолжение следует.

суббота, 14 марта 2009 г.

linux4sam: введение

И так, не доведя до какого-либо конца ни одного из ранее начатых проектов, я подумал, что уже как-то тесновато на x86-ой архитектуре, и пора бы уже посмотреть в сторону чего-нить этакого.. И так, не вникая в детали и предыстории, в руках у меня оказалась оценочная плата на основе МК AT91SAM9260 архитектуры ARM с 32 Mb RAM и 4 Mb flash, с USB, Ethernet, COM, SPI и кучей DIO на борту. В качестве ОС на девайсе стоит Linux, а размеры платы всего 75x90 мм, что в совокупности открывает огромные возможности для практических применений. Но перво-наперво необходимо обеспечить базу, иначе говоря - производство подобных девайсов. Т.о. план на ближайшие 6-18 месяцев, это освоить производство подобных девайсов, прошивку и сбору ОС с нуля.
В комплекте с платой идёт компашка, на которой можно найти софт для прошивки (под винду, к сожалению, у нас-то весь процех будет построен на пингвине), софт для разработки и виртуальную машину с RHEL. Последнее представляет наибольший интерес. Внутри виртуалки находим архивы компилятора, u-boot, кое-какие патчи, настроенный tftp, и вроде какие-то ещё приятные мелочи. Т.е. в принципе, ничего того, чего нельзя было бы установить к себе в систему собственноручно из официальных источников.
По железу, прежде всего надо понять, как вообще всё это дело устроено. Если в плане написания софта и сборки дистриба всё более/менее понятно, то вот железо вызывает у меня много вопросов.
AT91SAM9260, контроллер с большим количеством встроенных интерфейсов, встроенным интерфейсом SRAM, CompactFlash, NAND Flash, ECC, и производительностью до 200 MIPS, т.е. проще говоря - "зверь-машина". Внешние интерфейсы являются определёнными, в том смысле, что USB он в любом случае USB, в отличии от количества памяти, которой может быть 32, 64 а может и больше, а может там ещё и флешка есть, а может и ещё какое устройство. Всё что касается подобных вопросов, отнесём к системной конфигурации МК. В datasheet эта часть называется EBI - External Bus Interface. Её настройка производиться путём установки нужных битов в нужных регистрах МК на самой начальной стадии после включения, т.е. на самом низком уровне. В этом состоит самая главная задача - привести схему в состояние, пригодное для дальнейшей работы, "опознать оборудование". Отвечающий за это код вынесен в отдельный проект - bootstrap. BootStrap собирается кроссплатформенным компилятором и прошивается непосредственно во внутреннюю память МК. После того, как bootstrap отработал, необходимо определить, откуда дальше вести загрузку, найти ядро, передать ему параметры и всё дальнейшее управление, сформировать адресное пространство. Этим занимается загрузчик u-boot. Его конфигурации будет посвящена отдельная заметка, когда до этого дойдёт дело. Сам же u-boot предоставляет возможность загрузки с NAND Flash, MMC, и даже по Ethernet. Прошивается загрузчик также во внутреннюю память МК, и принимает управление от bootstrap. И наконец, после того как отработает u-boot, уже начнётся загрузка ядра linux, а тут всё становится уже совсем просто и почти как на "большой машине".
Т.о. схема загрузки следующая bootstrap -> u-boot -> linux kernel.
Разбирать эту цепочку будем с хвоста, освоим загрузку по сети, пересборку ядра, создание окружения для кросскомпиляции.
Продолжение следует.

вторник, 14 октября 2008 г.

Khameleon Modular System: О взаимодействии модулей со средой

И так, имеется заранее определённый интерфейс взаимодействия модулей, реализованный через qplugin. Как выглядит загрузка модуля через QPluginLoader — вначале необходимо получить список файлов модулей, которые хотим загрузить, затем создаём pluginloader и скармливаем ему в цикле эти файлы. Далее pluginloader.instance() возвращает нам указатель на QObject. И теперь осталось лишь получить объект с описанным нами интерфейсом с помощью qobject_cast(plugin). Однако тут есть тонкое место, интерфейсов у нас несколько, и различаться они могут как по типам, так и по версиям.
Оказывается в Qt есть механизм QMetaObject, с помощью которого сам Qt получает инфу о сигналах, слотах и ещё кое-каких параметрах объекта. Но самое вкусное для нас лежит рядом, в QMetaProperty. С помощью макроса Q_PROPERTY можно добавить к объекту свои «свойства», доступ к которым будет открыт на стадии, когда модуль представляет из себя лишь абстрактный QObject. И именно в этот момент можно будет узнать что у нас за модуль в руках, и какой именно интерфейс для него следует использовать. Т.о. фактически интерфейс разбивается на две части: некий мета-интерфейс, т.е. По сути список properties который фиксирован раз и навсегда, и рабочие методы объекта модуля. Интересно, а работать это будет...
P.S. представленная информация, по сути, вольный перевод разных частей Qt Assistant, для уточнения деталей следует обращаться непосредственно к первоисточнику.

Khameleon Modular System: О взаимодействии модулей

Модули взаимодействуют посредством сигналов и контейнеров QVariant для обмена данными.
Скажем, есть модуль получения данных с какого-либо девайса. Как только получена порция данных, модуль пускает сигнал, который связан со слотами модулей, которым эти данные необходимы. «Среда» отвечает за правильное связывание сигналов со слотами. Передача данных от модуля к модулю происходит посредством структур, обёрнутых в QVariant. Формат структуры заранее не определён, но описан в документации к модулю.
После того как отработали модули работы с данными каждый также пускает сигнал и результат их работы передаётся следующим и т.д.
У каждого модуля есть некоторые внутренние параметры, определяющие его работу. Необходимо предусмотреть чтение этих параметров, а так же их изменение другими модулями в процессе работы. Скорее всего это будет нечто вроде QStringList, где последнее слово в строке определяет значение параметра. Должны быть реализованы функции чтения всего списка параметров, чтение значения отдельного параметра (по строке с названием параметра), а также установка значения параметра.
Модуль представляет собой отдельный класс-библиотеку-виджет со стандартизованным интерфейсом. Возможно указание версии интерфейса, что может потребоваться для дальнейших улучшений. Рабочий код модуля исполняется в отдельном потоке (thread). Каждый модуль внутри себя содержит список пунктов меню (QMenu) для добавления в главное окно приложения, QToolBars and etc.

Khameleon Modular System: О главном окне приложения

Главное окно изначально содержит только строку состояния и общую строку меню, содержание которой заполняется самими модулями. Центральным виджетом окна является либо TabWidget или MDI. Список всех доступных модулей должен быть вынесен в отдельное меню, либо диалоговое окно, откуда происходит добавление/удаления модулей на рабочую область. Возможно потребуется создание менеджера модулей, для добавления, удаления модулей и установления связей между ними. Визуальное оформление модулей представлено в виде Widget-ов, которыми заполняются dockWidgets расположение которых задаётся пользователем. Модули могут иметь свои popupMenu и/или свои диалоговые окна.
При запуске программы список модулей читается из конфигурационного файла, который формируется после работы менеджера модулей. На основе этого списка происходит динамическое формирование рабочего окружения.

Khameleon Modular System

Khameleon Modular System
Модульная Система для обработки данных с экспериментальных приборов, управления приборами.
И так, начинаем цикл заметок-соображений о subj-е. Что это вообще такое, и что из себя представляет... это нечто для обработки всего :)
На самом деле всё больше появляется экспериментальных приборов, очень тесно связанных с ПК и требующих порой весьма нетривиальной обработки первичных данных. При таком подходе львиная доля в реализации функционала устройства ложиться на ПО, что и хорошо и плохо одновременно... Почему хорошо, понятно - проще аппаратная часть, дешевле. Плохо же оттого, что софт можно написать и на скорую руку, и через жопу, и чтобы он при этом как-то работал, а количество потраченных нервных клеток конечного пользователя никого не интересует. Даже написаны специальные системы а-ля LabView, которые стоят кучу бабок и содержат в себе всего на все случаи жизни... Но от вида программ, написаных такими монстрами, от скорости их работы, костлявости и кривоты порой "слегка" подташнивает.
Т.о. данный проект в своём зачатке нацелен на "построение коммунизма", хоть и в пределах "своей квартиры", а дальше видно будет.

пятница, 3 октября 2008 г.

о природе мышления

Я мыслю, следовательно... хотя до следовательно ещё рано. Что вообще такое "я мыслю". Очевидно, мысление есть некий процесс, происходящий в наших головах.. хотя это всё-таки у кого как, но не суть. А вот в чём суть самого этого процесса? Что он из себя представляет, ну или хотя бы может представлять чисто гипотетически?
Обратимся к эволюции, ибо именно ей мы всем обязаны (что тоже спорно, но не будем останавливаться на таких мелочах). Итак, в ходе эволюции, или другими словами, естественного отбора, постоянно происходит соревнование между видами "кто лучше адаптируется" к текущим условиям окружающей среды. Это жестокие состязания, т.к. проигравшие навсегда уходят в небытие. И что мы имеем на сегодняшний день в результате этой долгой борьбы? А сегодня мы имеем вид, наделённый максимальным адаптационным потенциалом за всю историю планеты, т.е. человечество. При этом надо отметить, что физические данные человека весьма заурядны, весь этот огромный потенциал сконцентрирован лишь в его мозге. Так, запомнили, теперь возвращаемся к теме разговора.
Роль мозга определили. С другой стороны более/менее известно, что мозг представляет собой сеть нейронных клеток, которые сами по себе относительно просты, но являясь элементами сети способны выполнять задачи гигантской сложности.
Каждая сеть, кроме своих элементов характеризуется также связями этих элементов друг с другом. Связи эти, скорее всего, случайны и статистичны, и во всяком случае не определены раз и навсегда. Информация в сеть поступает от нервных окончаний, зрительных, слуховых и пр., которые уже тяготеют к определённым частям мозга, как некой субстанции. Таким образом, в самой сети как целом происходит формирование центров (зрительного, речевого), в которых "данные проходят предварительную обработку". И когда мы видим какой-то предмет, то данные о нём поступают в сеть, и формируют в ней связи между отдельными нейронами. Когда мы видим другой предмет, то он порождает другие связи, и очень важно, что все они существуют "внутри одного объёма", т.е. активно переплетаются друг с другом (если предметы похожи), вытесняют друг друга (когда мы забываем что-либо), отпечатываются тем сильнее, чем сильнее ощущения и чем из больших источников они получены (для лучшего освоения материала надо почитать, пощупать, попробовать сделать самому).
И теперь самое важное, к чему я веду всё это время. Итак, есть два предмета, и каждый порождает свои связи, пусть изначально совершенно различные, пусть даже в различных физических областях мозга. Но потом мы понимаем, что предметы-то похожи, что у них есть общие черты, и что они вообще есть проявления одной сущности! В этот момент образовавшиеся связи уже настолько переплетены друг с другом, что мы просто не в состоянии мыслить о каждом из этих предметов по отдельности!!!
Но что произошло между этими двумя крайними точками?! А между этими точками мы размышляли, используя какие-то дополнительные данные, опыты или просто более внимательно разглядывая и сопоставляя, т.е. мы мыслили! И что же произошло в результате? А в результате связи между нейронами "адаптировались" так, что представляют собой уже нечто целое и абсолютно неразрывное!!
Таким образом, процесс мышления в сущности есть процесс адаптации мозга, процесс адаптации связей между нейронами и приведение их к такому виду, чтобы все полученные данные укладывались в голове! Можно называть это смекалкой, аналитикой или ещё чем, но очевидно одно - тот человек побеждает, который способен выявить правильные взаимосвязи, построить правильную картину мира (слово "правильный" стоит воспринимать в относительном смысле). А в сущности, всё это лишь адаптация, и потенциал её не просто огромен... он ужасно огромен!!!
P.S. по мотивам рассказа Станислава Лема "Сумма технологий".
Выражаю искреннюю благодарность Алёне Ч. за сопоставление вышеизложенного с правилами русского языка.