CREATION DATE: 24-09-2003.
28.04.2005: ВСЕ "<A NAME=...>
" ОБЯЗАТЕЛЬНО
снабжать "</A>
"!!!
ChlIntl(const char *text)
, возвращающая
локализованную строку, если она найдена в таблице, или просто
text
в противном случае?
Chl{Start,Stop}Logging()
из istcc.c в
Chl_gui.c.
19.01.2004: для этого понадобится место, где
будут храниться log_{filename,timeout_id,period}
.
Понадобится и место для хранения WidgetID для knobprops. В oldChl это
все хранилось прямо в WindowInfo
; сейчас (в связи с
отдельным Xh) мы отошли от подобного извращения, и есть понятие
"private pointer", занимаемый в Chl под datainfo. А для всего
остального введен "priv2". Вот его-то и будем использовать для обоих
потребителей.
Для этого придется ввести еще один модуль -- типа Chl_priv2.[ch]. Единственный минус для knobprops -- весь список виджетов будет торчать в библиотеку...
20.01.2004: сделал Chl_priv2.[ch], унес туда менеджмент priv2.
И logging перенес в Chl_gui.c. Проверил -- вроде работает.
Заодно вставил поддержку логгинга (читай -- скопировал из istcc.c) в Chl_simple.c.
cda_getsrc_of_formula()
, которая возвращала бы номер
канала плюс "осмысленность" -- т.е., что он единственный.
29.04.2004: сделал поддержку в cda. Полноты ради
изготовил "полный/завершенный" набор --
cda_srcof_physchan()
и cda_srcof_formula()
;
первой передается cda_physchanhandle_t
, а второй --
формула. Остальные два параметра -- const char **name_p
и
int *n_p
, в которые возвращаются имя сервера (получаемое
от (cda_status_srvname()
) и физический номер канала. Код
возврата обеих функций: -1
-- ошибка, 1
--
все OK, 0
-- все корректно, но канал в формуле не
единственный и потому смысла операция не имеет.
29.04.2004: и в окно "Knob properties" тоже добавил. Сейчас вставил между Label и Value; если не понравится -- переставить легче легкого (сейчас единственный осмысленный кандидат -- после Flags).
Помечаем раздел как "done".
29.04.2004: несколькими часами позже: переставил-таки в после Flags.
05.04.2004 (тогда начато, реально записано 26.04.2004):
ChlPopulateWorkspace()
:
ChlSetGrouplist()
, ChlAddLeds()
,
ChlStart()
.
ChlGetMainsid()
, Chl{Get,Set}ChanVal()
плюс
ChlSetDataArrivedCallback()
.
Общее впечатление/осадок после всего этого процесса -- а нафиг нам вообще для не-chlclients нужен этот Chl? Все "добавленности", по крайней мере, как они есть/сделаны сейчас -- лезут глубоко во внутренности Chl'я...
29.04.2004: собственно, вроде основное сделано -- так что "done".
Собственно ИДЕЯ: по двойному щелчку на заголовке элемента он
"сворачивается"/"разворачивается" -- т.е., делается
XtUnmanageChild()
/XtManageChild()
его grid'у
и (опционально) hline'у. У Гусева в новом клиенте RFSYN это уже
реализовано.
22.04.2004: сделал, пока просто по одинарному щелчку. Wow!
Проблема: самая внешняя форма (которая child shell'а) размер при этом не меняет.
23.04.2004: разобрался с изменением размера: надо
было у внешней формы выставить XmNallowShellResize:=True
.
23.04.2004: а вот с double-click сложнее -- в
структуре XButtonEvent
просто нету поля "click_count".
Поиски в www и ньюсах привели меня к мысли, что ловить double-click
себе дороже. Похоже (если не произойдет чудо и не отыщется
волшебный метод), если совсем припрет, то надо будет помнить время
предыдущего click'а, и если разница между ними менее
XtGetMultiClickTime()
, то считать за double-click.
23.04.2004: есть еще мелкая "проблема" некоторой "некрасивости" внешнего вида свернутого элемента, но пока на нее можно забить -- когда придет в голову красивое решение, тогда и исправим.
23.04.2004: В общем, первоначальный вариант вполне работает, так что помечаем секцию как "done".
27.04.2004: Поговорил на эту тему с Гусевым и заимел еще несколько мыслей.
XDrawString()
просто НЕ ИМЕЕТ параметра
"поворот/направление". Единственное, кто это умеет -- Xft, но сей
зверь находится за пределами доступности Motif'а.
XmNlabelPixmap
.
XtManageChild()
2 раза, а XtManageChildren()
.)
XtChangeManagedSet()
, и не нужно химичить с тем, кто выше
кого. (Но для "вящей корректности" (на всякий случай) тот порядок
"кто сверху" стоит оставить.)
05.05.2004: в случае, если мы имеем в окне-форме несколько графиков и разрешаем пользователю менять размер окна, то возникают сложности при "отключении"/"включении" графиков (когда им делается XtUnmanageChild/XtManageChild).
Как выяснилось, проблема в том, что при изменении размера XmForm
трогает только поля core.{width,height}
, а при расчете
геометрии использует constraint.preferred_{width,height}
.
Последние в принципе прописываются при SetValues(XmN{width,height}), но
ведь после resize они и так уже такие, и из-за проверки на равенство
"утверждение" размера не срабатывает.
Очевидный выход (но для случая, когда соответствующий виджет
unmanaged) -- делать SetValues(width+1; width). Второй, "грязный", но
более эффективный способ -- "руками" лезть в constraint (через
Xm/FormP.h) и править там
preferred_{width,height}
самостоятельно.
14.03.2007: Сделано -- в Chl_histplot'е введена
функция AdjustPreferredSizeInForm()
, уставляющая
preferred_{width,height}
в текущие размеры. Заодно там же
реализована и технология CompressConfigureEvents()
,
позволяющая реально отрабатывать только ПОСЛЕДНЕЕ событие "изменение
размера", не тратя времени на отработку (перерисовку) всей цепочки
(там еще блокируется отрисовка до прихода того самого последнего
ConfigureNotify). Плюс -- RemoveExposeEvents()
,
удаляющая из очереди события Expose для указанного окна. 28.03.2007: см. также замечания за сегодня в разделе по
adc200 -- на тему КОМУ надо делать
AdjustPreferredSizeInForm()
. 02.05.2007: все вышеуказанные функции переехали в
Xh.
06.05.2004: появилась мысль, как имитировать "блокировку" обновлений на экране на время "перетряски содержимого" -- чтобы не мерцало (нужно скорее Гусеву, а не мне). Идея такая:
XmATTACH_FORM
(так что он всегда занимает всю форму),
причем так, чтобы он был САМЫМ ВЕРХНИМ -- т.е., в managed-состоянии
закрывал бы всех. (Естественно, надо также сделать
XmNtraversalOn:=False.)
None
-- чтобы X-сервер
не рисовал ему фон.
XmNbackgroundPixmap
, ибо
тогда Motif будет умничать, пытаясь что-то узнать про этот pixmap, а
"руками", через Xlib --
XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w), None)
)
XtManageChild()
этому
виджету, так что он как бы всех закрывает, но при этом никакой
отрисовки не происходит; по окончанию процесса вызываем
XtUnmanageChild()
, так что сервер генерит ExposeEvent и
все перерисовывается.
07.05.2004: сделал -- как прописано в проекте от
06.05.2004, с "теневым" заголовком и XtChangeManagedSet()
.
Насчет порядка "кто сверху" мудрить не стал, поскольку сетка делается
позже, а так бы ее создание пришлось перетаскивать.
Ма-а-ахонькая тонкость: оказалось, что описание
XtChangeManagedSet()
на man-странице не соответствует
реальности -- редкоиспользуемые параметры do_change_proc и client_data
стоят не после списков, а МЕЖДУ ними. Написан bug report
#1363.
08.05.2004: баг пофиксен в CVS'е Matthieu Herrb'ом.
08.05.2004: сделал-таки отработку именно по
double click (и ТОЛЬКО по левой кнопке). Для этого введена функция
CheckForDoubleClick()
, каковая и выполняет все проверки
(включая тест, что предыдущий щелчок был на этом же виджете).
08.05.2004: на "теневом" заголовке текст заключается в угловые скобки -- отличия ради. Кроме того, теневому заголовку ставится tooltip "Double-click to expand".
08.05.2004: теперь можно "сворачивать" и отдельные колонки. При двойном щелчке просто делается unmanage всех виджетов, имеющих тот же cellX, и заголовок превращается в "!".
Такое поведение включается только для MULTICOL-элементов -- у SINGLECOL нет никакого смысла отключать единственную колонку.
Проблема: антоновский XmSepGridLayout почему-то глючит... А при использовании Gridbox оно вообще SIGSEGV выдает...
Пара идей/возможностей (вообще-то -- надобность в них исчезающе мала):
18.05.2004: временно, пока А.Антонов не пофиксит SGL, сворачиваемость колонок отключена.
03.06.2004: SGL пофиксен, сворачиваемость вернута на место.
30.09.2004: а сегодня обнаружил, что в X.org этот же баг с man-страницей XtChangeManagedSet все еще есть. Тоже написан bug report -- #1498.
03.10.2004: баг пофиксен и в X.org, тем же самым Matthieu Herrb'ом. И нафига ж эти треклятые Keith Packard с Jim'ом Gettys'ом отделились от XFree86, а David Dawes умудрился им помочь?!
21.12.2004: сменил, что в свернутом состоянии теперь отображается не "<Заголовок>", а "{Заголовок}" -- по крайней мере, для шрифта Lucida-Sans так симпатичнее.
20.01.2005: имел еще один разговор с Гусевым, и получил новое знание:
Если виджету-форме, находящейся внутри Shell'а, уставлять перед
XtManageChild()
'ом размеры в (0,0), то она будет производить расчет своих размеров и уставит себя в "натуральный" размер.Это актуально, если в некоем окошке разрешен resize, но хочется, чтобы при каждом его появлении на экране ставился "натуральный" размер, а не тот, что остался с прошлого раза.
Вспоминая мою давнюю (еще времен oldcx'а/curcx'а, и кабы даже не uxil'а) попытку реализовать tooltip'ы -- там имелась проблема, что если делался "правильный", не-отбирающий-фокус shell, то размер (то ли его, то ли tipLabel'а) считался только при ПЕРВОМ появлении, а дальше -- оставался неизменным, независимо от содержимого метки. Так вот -- именно сегодняшнее знание, похоже, и было единственным недостающим элементом -- если б тогда я уставлял перед появлением обновленного tooltip'а чьи-нужно размеры в (0,0), то все бы корректно ресайзилось...
29.03.2007: кстати, вместо хитрого и конкретного
Chl'евского SwitchContainerFoldedness()
вариант в adc200
оказался намного общее и идеологически элегантнее -- он просто
"инвертирует" managedness всех подвиджетов. Ему указывается всехний
parent, а он проходится по списку XmNchildren[XmNnumChildren], добавляя
тех, кто сейчас XtIsManaged()
в массив to_hide[], а
остальных -- в to_show[].
03.04.2009: и сегодня вообще ВСЁ перевел на
вариант с "инвертированием" managedness всех виджетов -- оно теперь
называется XhSwitchContentFoldedness()
.
Но надо отдавать себе отчет, что с вышеописанным подходом прозрачный виджет-закрыватель (идея от 06.05.2004) работать не будет. Так что если все-таки захочется использовать ту "скрывающую" технологию -- то надо использовать старый подход.
logchannet_t.bininfo
.
02.05.2004: убрано.
12.05.2004: а что, если ввести еще дополнительное строковое поле "options", в котором указывать эти самые параметры в формате psp (типа "grid=vh hspace=p vspace=p")?
16.08.2004: надо-таки вводить elem.options. Это должна быть psp-строка, и там, в частности, будут возможны флаги типа nohtitles и novtitles.
19.08.2004: ввел поле
cxdata.h::elemnet_t.options
. Естественно,
CXSS_SUBSYS_VERSION_MAJOR
продвинута на единицу, до 3.
Добавлено также поле
Knobs_typesP.h::eleminfo_t.options
, а в
Cdr.c добавлено копирование его из исходной записи.
Сделал поддержку опций в ChlMakeElement()
-- оно
понимает "nocoltitles" и "norowtitles". Насчет
сетки, интервалов и тени пока не размышлял.
Кстати... А как насчет ввести еще и "notitle", и не маяться с caption==NULL?
20.08.2004: ввел флаг "notitle", так что теперь для отключения заголовка надо указать именно его. Совместимость со старым поведением сохранять не стал. Оно использовалось в db_{rfsyn,thermosm}.h, которые уже подправлены. 30.08.2004: надо было подправить еще и db_subharmonic.h -- сделано.
Итого -- цель, представляющаяся на данный момент разумной, достигнута, инфраструктура создана (понадобится -- добавим и махинации с сеткой, и с интервалами), так что помечаем как "done".
31.08.2004: добавил еще флаг noshadow --
в ChlCreateContainer()
под него уже был параметр. По нему
делается XmNshadowThickness:=0
. В будущем понадобится
иметь "умное умолчание" -- для нормальных элементов =0, а для вложенных
-- =1.
Заодно для удобства добавил ',' в качестве сепаратора.
05.10.2004: обнаружил, что забыл вставить в
CdrDestroyEleminfo()
освобождение строки
options
, введя тем самым memory leak. Пофиксено.
ChlMakeElement()
от создания содержимого --
чтобы можно было делать просто "контейнеры" с заголовками, умеющие
сворачиваться. Ну что -- вводим "ChlCreateContainer()
"?
19.08.2004: угу-у-у-у... А ведь надо, чтобы махинации с "содержимым" (корректный аттачмент его) производила эта же функция, но как она это сделает, если виджет-содержимое создает не она?
Напрашивающийся ответ -- еще одним параметром передавать указатель на функцию-callback, которая, будучи вызываема создавалкой контейнера, будет создавать "содержимое".
Кроме того, надо иметь параметр -- грохать ли тень (нужно для вложенных элементов).
22.08.2004: сделал, именно по такому проекту.
Введена публичная функция ChlCreateContainer()
и
тип-функции chl_cpopulator_t
. Перевел на эту архитектуру
ChlMakeElement()
. Заодно несколько перетряс порядок
остальных функций -- касающихся создания меток и алхимии с щелчками на
заголовках колонок.
Поставленная цель достигнута, так что помечаем раздел как "done".
А на будущее, замеченные неудобства/мысли про потребность в Knobs_typesP.h для
chl-не-simple-клиентов: эти самые клиенты (например, istcc)
вынуждены включать P.h-файл для того, чтобы расставлять элементы (нужен
доступ к eleminfo_t.container
), а теперь -- еще и чтобы
иметь доступ к полю grid
(надо б его переименовать -- в
"content"?).
Со второй проблемой справиться просто -- делаем методы для чтения и установки поля grid/content. А вот с "container" сложнее -- там же еще идет цикл по ним -- "пока не конец списка элементов". Делать функцию NextElement(e)?
И второе замечание: все-таки содержимое Chl_gui.c и объявления в Chl.h шибко бардачны и некрасивы...
10.01.2005: переименовывать "grid" в "content" нельзя, поскольку именно так уже называется поле, которое список knob'ов.
Порывшись в Лингве, нашел идею -- "innage". Т.о., получаем
совершенно уникальное ключевое слово. Итак -- теперь это поле
называется innage
.
Сделал пару "аксессоров" -- KnobsGetElemContainer()
и
KnobsGetElemInnageP()
(последний возвращает
указатель, так что можно и присваивать). Они пошли как раз в
ранее пустой Knobs_public.c.
И теперь клиенты переведены на сию архитектуру -- istcc.c,
vibro.c (хотя в нем и есть пока ссылка на
Knobs_typesP.h, но это из-за графиков), и даже
Chl_gui.c::ChlGridMaker()
.
А дальше -- ну какая, нафиг, "первая проблема"?!?!?! Ведь цикл-то идет совсем НЕ по элементам, а по группировке, которая относится совсем к другой епархии -- к Cdr.
04.06.2004: исправлено очень просто: на
knobProps.shell повешен XmNpopdownCallback
, вызывающий ту
же PropsCancelCB()
, что и по [Cancel], а в ней добавлен
вызов XtUnmanageChild(pdp->r.box)
.
Поскольку knobPropsRange создается сразу вместе с knobProps, то никаких проверок "а существует ли..." делать не надо.
Общий рецепт на будущее -- в "parent"-окнах при
закрытии делать XtUnmanageChild()
всех их child-окон.
13.03.2007: угу, а про r.yesno_box
тогда забыл -- позорище! Добавлено.
ChooseWColorsHelper()
первым
параметром вместо типа колоризации (int, LOGC_NNN) передавался
knobinfo_t*
-- т.е., вместо sk->color
стояло просто sk
.
10.06.2004: произошло это при вытаскивании
"создания заголовка" из ChlMakeElement()
в отдельную
функцию make_label()
-- примерно между 28.08.2003 и
17.10.2003 (это даты соответствующих BACKUP'ов).
Отдельного порицания заслуживает то, что
ChooseWColorsHelper()
вызывается вообще внахаловку -- это
раньше она присутствовала в Chl_gui.c, а теперь переехала в
Knobs_internals.c.
Случайную-то ошибку я исправил, а вот с тем, что функция переехала в другой файл -- надо разбираться.
20.08.2004: стало совершенно очевидно, что
ChooseWColorsHelper()
придется делать публичной функцией,
раз уж в Chl требуется иметь часть функциональности из Knobs'ов...
Итого -- вытащил прототип в Knobs.h, позаменяв все "Pixel" на "XhPixel". Надо б его еще переименовать как-нибудь поприличнее.
21.08.2004: переименовал в
ChooseKnobColors()
.
Засим помечаем раздел как "done".
16.06.2004: Старостенко заявил, что в магнитной системе надо иметь кнопку "сбросить все в ноль".
17.06.2004: у меня произошла идея:
а что, если задействовать поле "defval", и чтобы была кнопка -- "Выставить безопасные значения"Но Старостенко заявил, что нули -- это совсем НЕ безопасные значения. Просто -- они нужны.
Встречная идея: использовать-таки этот defval
, вот как:
добавить в knobinfo_t
еще одно поле --
"prevval
", и сделать зажимабельную кнопку "караул!", по
нажатию которой делается для каждого канала
ki->prevval=ki->curv; SetControlValue(ki,ki->defval);
а по отжатию --
SetControlValue(ki,ki->prevval);
17.06.2004: произошла еще одна мысль/запрос: что надо иметь возможность полученный и "нравящийся" режим сохранить одним нажатием, безо всякого спроса имени, а потом -- так же просто вернуться к этому сохрененному режиму.
В общем -- почти в точности то, что имел в виду Ю.И.Эйдельман много лет назад.
Сделать подобное будет очень просто -- сохранять (не в память!!!) не
спрашивая в файл с неким специальным именем, типа --
"_mode_SYSTEM_good.dat", при помощи
ChlSaveWindowMode()
, а по "восстановить" -- так же читать
этот файл.
Я дольше буду подбирать пиктограммы :-).
17.06.2004: и еще идея: сделать в дополнение к "обычным" режимам такие "favourites", они же "именованные режимы" -- по нажатию еще кнопочки выпрыгивает окно (меню?) с несколькими именами, типа "Нули", "Безопасный", "Максимальный", etc.
17.06.2004: и еще фантастическая идея: а что, если прописывать где-то в описании программы/группировки/элемента эти "спецрежимы" на "как бы языке" -- типа "chan1=val1; chan2=val2; etc."? Или -- при наличии вышеупомянутых favourites сие будет излишне?
17.06.2004: и еще: надо все события типа
"сохранен режим такой-то, прочитан режим такой-то" писАть в
eventlog-файл. Да хоть в текущей директории (где режимы). А для
защиты от недобросовестных операторов можно этому файлу сделать
"chattr +a
", чтоб нельзя было оттуда что-то удалять.
17.06.2004: сделал этот eventlog.
В Chl_simple.c завели функцию RecordEvent()
,
которая (сейчас) вызывается при записи/чтении режимов и пишет в файл
"events_SYSTEM.log" попрошенную информацию, префиксируя ее
текущим временем (от ctime()
).
03.05.2005: да, вытащил. Детали см. за сегодня.
Если делать -- то это тоже должно пойти в Chl_gui.c, и быть публичным (для всяких istcc).
31.08.2005: цвета сделал.
Изготовил публичную Chl_gui.c::ChlShowHelp()
.
Ради оной пришлось добавить в
Chl_priv2.h::priv2rec_t
поле
helprec_t hr
. Некрасивость -- оно "живет" прямо в
Chl_gui.c, что есть не совсем правильно. Особенность -- там
модальность окна уставлена в XmDIALOG_MODELESS
.
В Chl_simple.c вставил кнопочку [?] (слизал пиктограмму из %WINNT%\system32\progman.exe) и по ней вызов окошка подсказки.
После обеда: добавил кроме справки по цветам еще и справку по мыши/клавиатуре. Каждая из справок расположена в своем grid'е, который, в свою очередь, в XmFrame, а они запихнуты в форму.
Заодно: использовал в этом окошке заголовок -- не стал его убирать.
И решил, что буду ВЕЗДЕ в диалоговых окнах
использовать эту штуку -- XmMessageBox.Message. И поэтому ввел
в Xh_fallbacks.h еще один #define-тип шрифта --
XH_TITLES_FONT
, который сейчас отражается на
lucida-bold-24, и он уставлен в fallback'ах.
В остальном же -- "done".
Кстати, в CXv4 тут же надо будет отображать свойства самой программы (корневого инода).
12.11.2005: (реально идея возникла давно, еще до
поездки в Швейцарию) а не сделать ли функцию ChlShowHelp()
параметризуемой -- чтобы указывалось, какие именно части подсказки
создавать (цвета, клавиши), чтобы можно было ее использовать и в
не-Chl-программах (типа ipp).
Часом позже: да, добавил такой параметр, и оно вроде даже работает.
Вот только попытка вставить оное в ipp.c закончилась плачевно: ведь ipp-то -- не только не Chl-, но даже и не Cdr-приложение! В результате -- море "undefined reference" при линковке, да и вообще, фиг знает -- насколько бы успешно оно б работало...
Хотя, собственно -- а почему бы и нет? Ведь Chl'евость
ChlShowHelp()
'а заключается только в том, что оно
использует поле XhWindow.higher_priv2
, складывая туда
указатель на аллокированную структуру -- а поле реально только для
этого и сделано. Так что...
О, чудо!!! Вынес оную в отдельный файл Chl_help.c -- и теперь ipp и компилируется, и прекрасно работает -- help по цветам показывается. Аллилуйя!
С другой стороны, разделить их будет трудновато -- уж шибко содержимое Chl_gui.c поперезавязано.
12.12.2005: а вот нехрен, нехрен -- надо в CXv4 либо вообще все махинации с данными ПОЛНОСТЬЮ перевешивать на Cdr, либо сразу выделять в отдельный (от _gui) файл.
06.08.2006: да ну нафиг -- надоел этот пункт в режиме "несделанный"; с учетом недавно происшедшего -- появления Chl_app, вытряхивания многоколоночности в Chl_multicol.c -- оно становится уже слабоосмысленно, а в будущей версии и вове будет по-другому; так что -- "obsolete".
Надо добавить к ChlRunSimpleApp()
дополнительный
параметр -- defserver. Благо, кроме как в chlclient.c она
больше нигде не используется, так что проблем с совместимостью не
будет.
19.07.2004: сделал. Параметр
defserver
добавлен после argv[]
, перед
app_name
. Изменения коснулись Chl.h (прототип),
Chl_simple.c (вставлена проверка, что передавать дальше --
argv[1] или defserver) и chlclient.c (выкинуты махинации с
$CX_SERVER, вставлена передача info->defserver
напрямую).
Chl{Get,Set}ChanVal()
и, соответственно,
chanspec2knobinfo()
из Chl_gui.c в
Cdr.c.
Во-первых, оно просто напрашивается "по смыслу", а во-вторых -- тем
самым мы полностью инкапсулируем все махинации с "древесной структурой"
groupelem_t
, eleminfo_t
,
knobinfo_t
в одном месте -- Cdr.c.
22.10.2004: угу, вот и еще один аргумент "за": при сведении magsys+v1000+magcorr->nmagsys возникла необходимость иметь "глобальные", "внеэлементные" каналы. Дело в том, что каналы прыгали из элемента в элемент, и совместимость по файлам режимов сохранить не удалось.
А так -- введем, чтобы если имя канала начинается с '.', то она его при загрузке ищет не по текущему, а по ВСЕМ элементам.
(Необходимость обусловлена тем, что зачастую элементы -- это лишь способ управления позиционированием канала на экране, а к группировке они имеют слабое отношение. Например, для компактности все кладется в два элемента-колонки, но по смыслу они -- единая сущность.)
01.12.2004: приступил -- функция
CdrFindKnob()
, возвращает Knob
, а при
ненайденности -- NULL и уставляет errno
(ENOENT, ENOTDIR,
EISDIR).
Функция довольно монструозна -- она поддерживает оба случая:
1) FQKN -- тогда она последовательно идет по компонентам имени,
ныряя все глубже в дерево; 2) "глобальное" имя вида ".name" --
тогда она рекуррентно (при помощи helper'а RecFindKnob()
)
ищет оное во всех ветках.
02.12.2004: Прикол -- замечу, непредусмотренный заранее -- заключается в том, что с указанием "глобального" имени она прекрасно находит даже и неглобальные каналы, чьи "личные" имена совсем не начинаются на '.'. Пока что оставлю это как есть -- в качестве фичи :-).
Перевел Chl{Get,Set}ChanVal()
со старого внутренного
поиска -- chanspec2knobinfo()
, ныне удаленного, на
CdrFindKnob()
.
Часом позже: перевел на аналогичную рекуррентную архитектуру и
CdrSaveGrouplistMode()
.
Еще часом позже: перевел также и CdrLogGrouplistMode()
,
пока что БЕЗ выписывания полного имени канала в заголовке. Заодно
исправил давний ляп -- если units==NULL
, то в log попадало
"(null)", а сейчас стоит проверка, и даже скобочки опускаются.
03.12.2004: сделал многоуровневую загрузку
режимов. Собственно загрузкой теперь занимается функция
RecLoadLevel()
, которая еще более монструозна. Она
обрабатывает как "верхний" уровень (поиск по группировке), когда она
занимается также открытием файла, так и подуровни (поиск по содержимому
элемента). Каналы, чьи имена начинаются с '.', она находит
при помощи CdrFindKnob()
, а остальные (как и подэлементы)
ищет сама в "текущем" элементе.
Интересные детали:
CdrFindKnob()
во внутреннюю функцию
DoFindKnob()
, избавив последнюю от проверок на тему что же
найдено -- канал или подэлемент. Архитектура стала существенно
элегантнее -- правильное разделение обязанностей, елы-палы :-).
Есть одно ограничение: элементы самого верхнего уровня НЕ МОГУТ
быть глобальными: ведь их ищет DoFindKnob()
, возвращающий
knobinfo_t*
, а у них оного просто нету!
SkipIdentifier()
в
качестве допустимого символа в идентификаторе.
CdrFindKnob()
'у, но потом передумал и сделал рекуррентно
-- чтобы не возиться с вопросом, а сколько же надо отводить буфер,
чтобы влезла произвольная требующаяся вложенность.
BARK_AND_NEXT()
, с varargs'ами.
BTW, обнаружил в старом коде загрузки еще один идеологический ляп:
там считывалось значение для канала, делалось
SetControlValue()
, и лишь потом читались пределы. Но
считываемое-то значение могло выходить из "текущих" пределов, а
помещаться только в считываемые!
А в RecLoadLevel()
весьма ко двору пришлась концепция,
"если элемент-получатель -- NULL, то выполнять парсинг, но никуда
результат не складывать". Это полезно в ситуации, когда в файле
встретился элемент, которого у нас нет, но надо бы его содержимое из
файла корректно пропустить.
05.01.2005: удалил старый код
CdrLoadGrouplistMode()
, вместе с сателлиткой
RangeName2R()
.
05.01.2005: собственно, обозначенная цель достигнута, так что помечаем как "done".
XmNselectColor
на XmNunselectColor
, и вместо
XmNset=True
поддерживается состояние
XmNset=False
.
ChlAddLeds()
нет проверки, что
тулбарная кнопка "leds" создана.
04.08.2004: вставил в ChlAddLeds()
проверку, что потенциальный parent!=NULL.
В принципе, этого хватило, поскольку автоматически и srvcount'у
ничего не присваивалось, так что он оставался нулем, и
leds_update()
отрабатывала вхолостую. Но от греха
подальше вставил и туда проверку.
12.11.2005: молодец, блин -- в той проверке было
просто "return
", а оно вообще-то значение должно
возвращать. Почему не заметил -- вопрос. Вставил туда 0
.
ChlCreateContainer()
'ом, то теперь потребно
иметь и "ChlCreateSimpleContainer()", чтобы не надо было заполнять свой
eleminfo_t...
05.10.2004: сделано.
Есть некоторое "идеологическое" отличие от обычного
ChlCreateContainer()
: ему передаются два флага --
notitle
и noshadow
, а Simple-варианту -- все,
включая эти флаги и заголовок, указывается вместе в psp-строке.
Несмертельно, но -- просто отличие.
18.04.2011@Снежинск-каземат-11: а ведь API
ChlCreateContainer()
ВООБЩЕ НИКЕМ не используется...
(Об этом уже говорилось 28-03-2009 -- и что он вообще применялся только в старом istcc@2257.)
count%ncols==0
, т.е., если число каналов кратно числу
столбцов. А могут быть случаи, когда это неудобно -- например, каналов
7 штук, их бы упхать в матрицу 2x4, а -- облом.
Надо сделать так, чтобы оно корректно завершало цикл ДО
исчерпания ncols*nrows
, а по исчерпанию
count
. Соответственно подкорректировать и вычисление
nrows
.
30.09.2004: сделано. Вставлено, во-первых,
"умное" вычисление nrows
--
(count+ncols-1)/nrows
взамен простого
count/ncols
, а во-вторых -- дополнительное условие
завершения цикла по столбцам -- на превышение count'а.
'\n'
.
30.09.2004: точнее, обратил внимание, что там
используется просто XmStringCreate()
вместо
XmStringCreateLtoR()
. Естественно, поправил.
Заодно совершил набег на тему "где XmStringCreate*() не-LtoR". Обнаружил!!! Вот результаты:
XmLabel
на XmText
.
Вариант реализации -- вводим в
ChlPopulateWorkspace()
параметр is_vertical
,
а в Chl_simple.c::text2simpleopts[]
-- флажок
"vertical" (каковой можно будет указывать в
SimpleClients.lst).
21.10.2004: начал делать по вышеуказанному сценарию. Доп. параметр и опцию ввел, осталось только сделать "мясо".
Часом позже: все сделал. Имена переменных сменены с
"горизонтально-центрических" на "нейтральные": left->prev,
row->line, prevrow->prevline. А там, где стояли аттачменты
внутри контейнеров и самих контейнеров, вставлены дополнительные
проверки "is_vertical?A:B
".
А что, если -- когда первый символ tooltip'а '\n', то присваивать этот тултип метке, пропустив первый символ, а каналу -- не ставить вовсе?
25.10.2004: так и сделал. Первая часть -- в
Chl_gui.c::make_label()
, вторая -- в
Knobs_widgetset.c::CreateKnob()
.
Осталось только вытащить этот '\n' в какой-нибудь enum/#define.
26.10.2004: да, вытащил -- в Knobs.h,
KNOB_TOOLTIP_NOTIP_PFX
.
Вообще-то, недурственно бы иметь подобное и в cxdata.h -- дабы формально можно б было файлы писать БЕЗ "магических символов". Но пока это малоактуально.
23.05.2005: О! А не распространить ли этот принцип еще и на метки?
Сложность только в том, что мест, где в конкретных виджетах используется метка -- много. Сделать библиотечную функцию?
Часом позже: да, сделал библиотечную функцию --
GetKnobLabel()
, так что теперь путь стал открыт. Чем мы и
воспользовались -- фича теперь поддерживается.
Для корректности/общности символ переименован в
KNOB_LABELTIP_NOLABELTIP_PFX
.
05.11.2004: сделал. Поскольку в istcc.c свой код, то в Chl_simple.c вставил 60 секунд.
То ли выбирать направление в зависимости от направления группировки? То ли -- по параметру в options? А лучше -- по параметру, но в зависимости от направления выбирать значение по умолчанию.
15.12.2005: лучше-то лучше, но внутри элемента пока нет способа узнать major-direction группировки. Так что -- это "лучше" пока забудем.
А вот "по параметру" -- реализовал.
opts_fold_v
. А default=1
проставляется между bzero()
и psp_parse()
.
ChlMakeElement
/elemopts_t
и
ChlCreateSimpleContainer
/containeropts_t
--
ой, йо!... -- надо, ох надо "include" в PSP!
eleminfo_t.opt_fold_v
.
ChlCreateContainer()
теперь делает
XhAssignVertLabel()
только если
e->opt_fold_v!=0
.
В общем -- "done".
P.S. А на будущее (CXv4? :-) -- надо делать хорошо ПРОДУМАННЫЙ и
правильным образом ФРАГМЕНТИРОВАННЫЙ/
behaviour
, как он был предложен
27.09.2004.
13.01.2005: 1) сделал, только не дуплетами битов,
а триплетами -- поскольку разных комбинаций не 4, а целых 5: 0-default,
1-begin, 2-end, 3-center, 4-fill. Теперь "расположение" knob'а всегда
делается отдельной функцией PlaceKnob()
.
2) Ну сделал, и чо? Фишка-то все-таки в том, что лучше б указания о выравнивании поступали не от ручек, а с уровня выше -- т.е., все-таки нужно что-то типа поля "layoutinfo", где б это указывалось, например, в текстовой форме.
И даже больше (ох, переусложню все нафиг!!! :-): можно сделать ДВА уровня, по аналогии с WM_HINTS: 1) рекомендации конкретного типа-ручки (аналог PPosition), и 2) указания "пользователя" -- т.е., то, что указано в "layoutinfo". Естественно, у (2) всегда приоритет, оно обязательно, а (1) можно и вообще игнорировать.
23.07.2006: да, сделано. В основном -- "за
компанию", поскольку как раз введено поле placement
(так
решено в конечном итоге назвать layoutinfo
).
Итак -- оно вначале вычитывает из ki->behaviour
,
потом, если есть placement
, парсит его, и если в
полученные оттуда horz
и vert
не нули, то
заменяет значения ими; а уж потом принимает решения.
В общем -- "done".
options
-- "folded". (Это понадобилось в
первую очередь для МНТЦ/C13 -- чтобы панель КШД485 по умолчанию на
экране не маячила.)
15.04.2005: сделал.
SwitchContainerFoldedness()
, в которой теперь заодно есть
и проверки -- если хоть один из виджетов, меняющих managedness, ==NULL,
то ничего не делаем.
ChlCreateContainer()
еще параметр
folded
, при включенности которого в конце сразу и вызываем
сворачивание.
ChlCreateSimpleContainer()
/text2containeropts
и ChlCreateElement()
/text2elemopts
.
Кстати, а как будет обстоять дело с этим дублированием при переходе на CXv4 с плагинной архитектурой? :-)
16.04.2005: сначала подумал, что это вызвано моими недавними махинациями с "глобальными акселераторами", но нет -- так же прекрасно падает и chlclient на linac1, датированный 04.12.2004.
Просмотр "bt" в gdb показал какие-то приколы с
ProcessDrag()->XmeDragSource()->...strlen() -- похоже, падает при
пустой метке, если ее кто-то пытается "тащить" drag-and-drop'ом. Угу
-- проверил, оно так отлично падает и в остальных местах, где
XmToggleButton
с пустой меткой -- в
Knobs_alarmonoffled_widget.c и в LOGD_TEXTGPBL
.
Этот баг есть и в openmotif-2.2.3-6.FC3.1. Ну что, еще один кандидат для ихней Bugzilla? Хрю... Не выдержал -- написал письмо на yura@isc.com, может хоть соотечественник как-то прояснит ситуацию. И заодно -- wow!!! Нашелся Bug #1117, ровно об этой проблеме, помеченный by Yura за 2004-09-15 как fixed.
А на сейчас, если будет доканывать -- можно просто отбирать Button2Click у этих жертв уже известным способом.
22.09.2005: да, теперь уже и Гусев это у меня обнаружил. Так что -- надо сие прикрывать.
25.09.2005: да, ввел блокирование -- теперь по SIGSEGV не падает.
23.01.2006: дэбыл!!! В
Knobs_alarmonoffled_widget.c и в LOGD_TEXTGPBL
--
забыл!
Исправил -- просто скопировав handler и его уставление из Chl_leds.c.
{MODE,LOG,OPS}_PATTERN_FMT
? (И
не забыть уж тогда заодно RecordEvent()
утащить в
стандартную библиотеку).
03.05.2005: хорошенько подумавши, сообразил, что
по смыслу это как раз относится к Chl -- это ведь "интерфейс высокого
уровня", так что вынес их в Chl.h под именами
CHL_{MODE,LOG,OPS}_PATTERN_FMT
.
Далее -- унес в Chl_gui.c столь часто повторяющийся
SwitchLogging()
, превратившийся теперь в
ChlSwitchLogging()
и слегка упростившийся.
Затем туда же последовал RecordEvent()
,
трансформировавшийся в ChlRecordEvent()
.
Вышеупомянутые махинации были проделаны также со всеми программами из istc/xmclients/.
13.05.2005: кстати, а надо бы добавить еще один "шаблон" -- для сохранения не режимов, а неких намеряных значений, как, например, "отфильтрованности" в phm-tsyline.c. Самое разумное для таких целей -- префикс "data_", а имя -- CHL_DATA_PATTERN_FMT.
Десятью минутами позже: сделал. Именно так.
16.05.2005: сегодня перевел phm-tsyline.c на CHL_DATA_PATTERN_FMT.
А еще обнаружил, что старый вариант pyrotest.c -- который
был ДО введения CHL_*_PATTERM_FMT
, пользовался префиксом
именно "data_".
stored_app_name
.
Т.е., сильно нужен вызов типа "ChlInitWindow(const char *app_name)".
И ведь собственно -- после уноса в CXv4 element-plugin'ов в Knobs, именно всякие RecordEvent'ы etc. станут СУТЬЮ Chl'я.
И даже сейчас -- явно напрашивается, чтобы при загрузке/сохранении
режима XhMakeMessage()
и RecordEvent()
вызывались прямо Chl'ными функциями.
Окей -- пока, простоты для, сделаем "ChlSetSysname()".
03.05.2005: Стал делать -- поразмыслив, впихнул
char *sysname
в priv2 -- в начало, там оно лучше
смотрится, чем в datainfo.
Сделал -- оч-чень пользительная штука! Теперь "мозги" в
значительной степени переехали из Chl_simple.c в
Chl_gui.c. ChlRecordEvent()
и
ChlSwitchLogging()
теперь самостоятельно готовят имя
файла. Опять же, появился способ при сохранении режима писать в файл и
имя системы, что также и было реализовано (см. раздел по Cdr за
сегодня).
А на будущее, в CXv4 -- надо обдумать интерфейс Chl, при котором вызовы Xh из клиентов будут минимальны! И количество инициализационных вызовов самого Chl'я тоже минимально -- именно вроде "ChlInit...()".
08.07.2006: что забавно: в
subsysdescr_t
есть именно поле
sysname
, но оно никем не используется, и
ChlRunSimpleApp()
, которому оно не передается, вынужден
вместо него использовать app_name
. Ну дурь, да и только!
14.12.2007: для симметрии добавлена
ChlGetSysname()
-- она понадобилась в ndbp, для
сохранения/загрузки картинок.
#include "pixmaps/btn_print.xpm"
, и
закомментированную кнопку тулбара. А cmPrintMode
исчезнет
само, при стандартизации команд.
(Это был артефакт много-многолетней давности -- еще с автоматизации запяткинского клистрона; прекратил свою реальную жизнь он еще с curcx/ года два назад.)
DataInfoOf()
-- уж откуда они будут
брать свою информацию, их клиентов не касается.
Собственно, это и правильно: Chl -- единая библиотека, логически -- "объект", и пусть она свои данные держит в единой структуре.
В CXv4!
16.07.2006: да, а priv2 мы оставим для "клиентов" -- пусть программа также имеет возможность хранить свои per-window данные.
Чисто технически сворачивать их сразу после создания -- не проблема, как показало введение флага "folded".
Проблема -- как сие указывать в описании элемента? У нас есть
только поле colnames
.
Что, кроме опционального tip'а писАть там и опции, также через '^L' (MULTISTRING_OPTION_SEPARATOR)?
20.05.2009: ну, собственно -- вот ровно в струю тех advancements с обычными пунктами селекторов (см. за 12-05-2009). Так что надо формат "Метка[|Тултип[|Стиль]]" (где "|" -- MULTISTRING_OPTION_SEPARATOR) обобщать, и в соответствующие API добычи заголовков столбцов/колонок тоже это добавить. Правда, есть тонкость: ведь при взятии общей строки от базовой ручки, СТИЛЬ-то НЕ надо использовать! И как? Тоже махинировать с '\n'?
kpropsdlg_t.incdec_step
.
16.11.2005: сделал -- ввел строчку "State" между
Flags и Source, которая обновляется вместе со значением. Аналогично
bigval'у (откуда "мозги" и скопированы) -- обновление цвета
производится только при реальном изменении. Кроме цвета, естественно,
отображается еще и "надпись", поиск которой ведется по таблице (а не
индексирование массива самим colalarm_t
, что создало бы
проблемы при перетряхивании списка возможных состояний).
05.02.2007: стыдоба -- при том копировании мозгов забыл сделать отдельные поля deffg/defbg для этой строчки "State". Добавил отдельные поля col_deffg/col_defbg.
Вообще-то, более правильно иметь не "колонка меток для каждой колонки ручек", а более общее "колонка меток для каждых N колонок".
Что, в свою очередь, является более общим случаем нашего нынешнего
ELEM_MULTICOL
'а -- у которого словно
"число_колонок_на_метку==число_колонок_элемента".
Вариант решения (без введения нового типа элемента):
указывать параметр "число колонок на rowlabel" в старшем полу-int'е
параметра numcols
, и значение "0" означает
"cols_per_collabel=numcols" (да, хак!).
Замечание, так, на будущее: в CXv4 явно стоит эмулировать
ELEM_SINGLECOL
при помощи ELEM_MULTICOL
'а --
numcols:=1,nocoltitles.
12.12.2005: Потихонечку начал делать -- вопрос в "формулах".
Возникает, кстати, вопрос: а откуда брать тексты меток для "внутренних" колонок? Ну тут уж, естественно, без вопросов -- видимо, из соответствующих knob'ов.
13.12.2005: однако, сделал!!! Конкретика:
x + ((x / ncplc) + 1) * (opts.norowtitles == 0)
y==0
, а rowlabels -- при
(x%ncplc)==0
(оные создаются в колонке "x_knob'а-1").
make_label()
'у
передается ms:=NULL
, для чего...
get_label_and_tip()
вставлена логика, что
ms==NULL
означает "всегда использовать "?\v?",
т.е. брать и label, и tip от knob'а". 03.03.2006:
сию логику слудовало бы удалить -- ибо для n-этажности она нафиг не
нужна, но: такой подход правильнее -- теперь можно
colnames
/rownames
просто не указывать, делая
их NULL
, и тогда параметры будут браться из knob'ов (и не
надо маяться, подсчитывая, сколько же "?\f?"'ов следует
написать).
Так что -- считаем проект блистательно исполненным, и помечаем как "done".
15.12.2005: гы-гы-гы!!! А проблема-то имелась --
теперь ведь нет точного соответствия
gridx
<->elem_x
, так что при
разворачивании колонки оно стало брать не ту метку... Собственно,
таковая проблема наверняка имелась и раньше, при norowlabels
-- поскольку там стояло unconditional my_x-1
.
Так что -- вставил в LabelClickHandler()
трансляцию
grid_x->elem_x при помощи перебора. Теперь работает правильно. И
-- ради этого пришлось ввести eleminfo_t.opt_norowtitles
.
Вообще, что противновато -- получилось дублирование кода. Замечания "на будущее" (опять CXv4? :-), в дополнение к сегодняшним же в разделе "fold_v": надо б
opts
" -- возможно,
просто стандартизовать psp-парсинг, чтобы он выполнялся вовсе не
"элемент-плагином", а прямо "ядром" Knobs. Подобная идея уже записана
для обычных knob'ов за 03-07-2005, про «насчет "стандартизации
psp-парсинга параметров «компонентов»"».
20.01.2006: вылезло одно хрюкство: в однострочных сетках (обычно вложенных) с >1 колонками снизу "было лишнее место". Путем включения сепараторов стало видно -- это просто лишняя строчка сетки, ПУСТАЯ.
Общение с Антоновым выдало разгадку: просто при создании второго виджета, БЕЗ cell-координат, для него автоматом создается еще одна строка, становящаяся лишней после перепозиционирования, и она никогда не убирается.
Т.е. -- ДО унификации MULTICOL/SINGLECOL в
ChlMakeElement()
'е уставка размеров сетки делалась, а в
новой версии -- нет, попросту забыл.
Сделал -- теперь ПОСЛЕ населения содержимым (а раньше было ДО)
делается XhGridSetSize()
.
Неудобство -- пришлось, дабы знать ширину сетки в столбцах, "насчитывать" при населении максимальный-cellX. А все оттого, что нету отдельных координатных функций, которые бы позволили просто вычислить этот максимум как cell_x_of(ncols-1).
12.02.2006: ну бли-и-ин!!!
А кто, собственно, мешает просто уставлять самой метке в
XmNuserData
нужную информацию?
12.02.2006: и еще:
хотелось бы иметь "над-многоколоночность": чтобы многоколоночный поток также мог идти в несколько колонок.
Например -- вот сейчас вакуум, магнитная система и магнитная коррекция побиты на несколько элементов -- исключительно из-за того, что просто вертикально оно не влазит (а вручную "расставлять" во много колонок -- задолбешься).
И, кстати -- ведь в таком случае фича "число колонок на метку" становится слабонужной: она введена-то была исключительно для linmag'а ("им. Федора Еманова"), и те потребности как раз преотличненько покроются "переносом" многоколоночного потока.
Возможные проблемы:
Ответ: а брать из 1-й "макроколонки" -- ведь это ТЕ ЖЕ САМЫЕ колонки и, следовательно, метки.
LabelClickHandler()
'е)
будет понимать: а к чему она, собственно?
Ответ: через XmNuserData
.
Ответ: указывать число строк в макроколонке.
Ответ: можно меткам строк принудительно добавлять спереди по паре пробелов.
19.02.2006: более "философские соображения":
- Как уже было сказано, "число колонок на метку" нафиг не нужно, и -- по той простой причине, что перенос "потока колонок" и является самым натуральным решением!
- Более того, этот подход лежит "в струе" общего подхода к расположению элементов -- ведь группировка ведет себя совершенно аналогично, перенося "поток" элементов в указанных точках.
- И, код для такого "населения элемента с переносами потока" -- более красив и естествен, с точки зрения требующихся вычислений: там все легко и просто, "линейно", безо всяких дурацких исключений и необходимости хитро подсчитывать количество строк и столбцов сетки.
Попросто говоря, это -- простейшее расширение простого линейного многоколоночного заполнения.
- И ("главное"?) -- это и есть давным-давно требовавшееся решение проблемы/некрасивости "зачастую элементы -- это лишь способ управления позиционированием канала на экране, а к группировке они имеют слабое отношение" (22-10-2004).
Теперь спокойно можно и двойные элементы "Магнитная система" и "Магнитная коррекция" свести в один-элемент-на-каждую, и вакуум тоже сделать одним элементом каждый...
- Вот только один вопрос: если все так естественно и очевидно, то почему ж я до этого давным-давно-то не додумался, а? :-)
20.02.2006: приступаем. (Ежели что -- код "с ncplc" есть в BACKUP/cx_src.20060220_regular/.)
Первым делом упрощаем внутренности
LabelClickHandler()
'а -- теперь ему передается
колонка-в-элементе, так что ему не требуется производить хитрые
махинации на тему "а хто ж я такая?"
Далее -- изготовлена почти с нуля новая
ChlMakeElement()
, по проекту от 12-02-2006. И она
работает! И она настолько проще и изящнее предыдущей конструкции...
Эх!
Выкинул eleminfo_t.opt_norowtitles
, ныне более не
нужное.
А вот "добавление спереди пары пробелов" пока недореализовано -- уж
шибко замутно устроена make_label()
в отношении махинаций
со всякими "notip_pfx" (хотя параметр pfx
, указывающий,
что добавить перед непустой меткой, ей и добавлен, и он даже передается
нужными значениями).
21.02.2006: Философия 1:
make_label()
уж шибко навернута, у нее ТАКОЙ список
параметров, что черт ногу сломит. Параметры-то только что все
откомментированы, но от этого немногим легче.
21.02.2006: Философия 2: вот интересно -- а почему мне в обоих "грандиозных" случаях первым делом в голову приходит НЕоптимальное решение, а?
(Для вложенных элементов -- была вначале идея "потреблять" knobinfo'ы из потока parent-элемента; тут -- вначале сделал "n колонок на метку", вместо более естественного переноса?)
Мда, до гениальности мне пока далеко... :-)
Как бы то ни было -- я (кажется :-) успеваю вовремя увидеть правильный путь и все же перехожу на него. И то радость.
21.02.2006: воспользовался результатом -- перевел на n-подъездность программы linmag, vacuum и ringvac.
22.02.2006: да, старая мудрость остается верной -- если проблема сходу красиво не решается -- отложи ее, решение придет само.
Проблема визуального отделения перенесенных колонок от предыдущих
решается намного проще и элегантнее: а зачем вообще было добавлять
спереди пробелы? Ведь нам надо слева некоторый spacing, так? Вот
его и делаем -- добавляя некоторое количество пикселов к
XmNmarginLeft
метки. Это и есть естественное
решение.
Для вящей унификации сделал этот дополнительный интерфал равным
IEH_SPACING
, определение какового ради этого пришлось
вынести из *PopulateWorkspace()
(где он пребывал, по
крайней мере, еще со времен mapp/).
ВСЕ!!! ВОТ ТЕПЕРЬ -- ДЕЙСТВИТЕЛЬНО "DONE", ФИЧА РЕАЛЬНО ДОДЕЛАНА И ДОВЕДЕНА ДО "ПРОМЫШЛЕННОГО" ИСПОЛЬЗОВАНИЯ.
27.02.2006: имелись пара мелких багов, связанных с делением на ноль:
Вставлена проверка, что при nflrs==0 делается nflrs=1.
Вычисление вытащено из объявления, и теперь есть проверка
if(ncols==0)ncols=1
.
А вообще -- ну сколько ж можно говорить, что перед делением всегда надо убеждаться, что делитель не равен нулю!
ki->label==NULL
, то, как и указано в XmLabel(3Xm),
ставится не пустая метка, а имя виджета -- т.е.,
"rowlabel"
.
14.12.2005: ну исправил -- теперь
get_label_and_tip()
проверяет, что если в конце вышел
NULL
, то он возвращает ""
.
ABSTRZE()
/CNCRTZE()
. (Достало на примере
Chl_gui.c.)
14.12.2005: итак -- создал Chl_internals.h, который всего-то и содержит, что эти два макроса.
И пошел шерстить Chl_gui.c. Ох и мутотень!!! И, кстати --
поменял в начале файла в декларациях attach*()
"Widget
" на "XhWidget
".
20.12.2005: угу, эмулировать его как-то "кисло" -- шибко уж навернуто получается с вложенными элементами (да и у Антонова при этом кой-чего подглючивает :-), потому и захотелось такой тип элемента.
НО: и как, спрашивается, будем, например, указывать флажки "fromnewline" содержимым?
Одно радует: делать для такого элемента форму вместо сетки мы
можем -- просто изготовим в дополнение к ChlGridMaker()
'у
еще и ChlFormMaker()
, и указывать для
ELEM_POPULATOR
'а именно его.
15.03.2006: этого хотелось в основном ради программы linmag, где хоть так, хоть этак имелись проблемы с расположением разнородных и разноразмерных элементов -- маленького BeamPC+/BeamPC- в "остатках площади" от магнитных системы и коррекции.
Сейчас же (в основном -- благодаря многоподъездности) проблема решена -- те два канала просто подселены в конец магнитной системы.
Так что помечаем раздел как "withdrawn", а уж в "правильной" организации GUI все получится автоматом.
15.03.2006: изготовлено, в довольно примитивном
варианте -- это вовсе не те "регистрируемые таблицы плагинов", а лишь
возможность, при помощи ChlSetCustomElementsTable()
,
зарегистрировать ОДНУ таблицу нестандартных элементов.
Детали таблицы: там указывается идентификатор (look_id
)
и функция-"создатель" (create()
), являющаяся ЕДИНСТВЕННЫМ
"методом" (если надо -- пусть сама проставляет поле
e->emlink
); по параметрам и возврату она идентична
ChlMakeElement()
'у. Концом таблицы является строка с
look_id<=0
.
Детали работы ChlMakeElement()
'а: при
e->type!=ELEM_{SINGLECOL,MULTICOL}
оно производит поиск
по таблице, и если находит -- то вызывает создателя и тут же
отваливает, возвращая егошний код возврата; если же не находит -- то
уставляет type=ELEM_SINGLECOL
и далее идет как обычно.
Осталось проверить.
24.04.2006: да, проверил, на
work/tree/testtree.c::ELEM_TREE
-- вроде работает.
Заодно пришлось сделать публичной функцию "рождения" ручки --
GiveBirthToKnob()
, теперь она стала
ChlGiveBirthToKnob()
. И, кстати, теперь поле
eleminfo_t.innage
стало формализованным -- любой
компонент-контейнер ОБЯЗАН использовать его в качестве parent'а
содержащихся в нем ручек.
(Кстати, 14-10-2005@ICALEPCS-2005: "Для element-plugin'ов нужно ДВА вызова: GiveBirthToKnob() -- создать 1 knob, который el-plugin тут же засунет куда надо, и GiveBirthToAllKnobs() -- создать все вложенные knob'ы этого уровня, а el-plugin потом распихает все сразу.")
28.04.2006: ага, "innage" формализовано --
проблемы появились, блин!!! Проблема в TabStack'е -- там приходится
создавать под-ручки через промежуточную форму, а
ChlGiveBirthToKnob()
-то в ПРИНУДИТЕЛЬНОМ порядке
использует именно innage
. Вот и пришлось перед созданием
каждой под-ручки запихивать в innage
ссылку на форму этой
конкретной под-ручки...
"Done".
01.05.2006: угу, а для "хитрых" иерархий вроде XmTree/XmOutline все еще подзапутанней -- у них используются "чужие" виджеты, от элементов более высоких уровней. Так что -- фиг его знает, как ПРАВИЛЬНО это все организовать. Надо думать...
24.07.2006: забавно -- а почему у ChlGiveBirthToKnob()
ДВА
параметра? Ведь в этот момент элемент, к которому принадлежит
knob, уже можно узнать из k->uplink
, не так ли?
Ответ оказался крайне прост -- в
до-GiveBirthToKnob()
'ную эпоху поле uplink
по
какой-то придури заполнялось не в Cdr'е, а в
ChlMakeElement()
, и как раз ПОСЛЕ создания ручки (см.
STABLE/cx.20040802/); дурь эта была исправлена 27-09-2004 (см.
комментарий за эту дату).
Исправлять ситуацию будем?
26.07.2006: исправлено -- второй параметр убран,
оно честно берет ссылку на parent'а из k->uplink
, все
юзеры (в т.ч. linbpm_knobplugins.c) подправлены.
22.08.2006: ага, все -- work/tree/testtree.c был забыт. В нем тоже подправлено.
Это нужно, когда "родственные" ручки стоят рядом друг с другом не по горизонтали, а по вертикали; и по горизонтали идут "экземпляры" таких наборов.
И вообще, кстати -- переименовать в CXv4 "ELEM_SINGLECOL/ELEM_MULTICOL" в "ELEM_GRID".
04.05.2006: да, в linbpm такое нужно уже ДВАЖДЫ -- а все оттого, что родное направление тамошних графиков -- горизонталь, а не вертикаль.
Как флажок назовем -- "japanese"? Или, лучше, по аналогии с XmRowColumn -- orientation=horz (default) и orientation=vert.
16.05.2006: проще: что мы делаем? -- правильно, транспонируем элемент. Вот так флажок и надо назвать -- "transposed".
Такой флажок в elemopts_t
и
text2elemopts[]
введен.
17.05.2006: реализация также сделана. Краткие заметки по теме:
sethv()
, при включенном флаге
transposed
меняющая местами горизонтальное и вертикальное
числа. От этого-то усложнилось несильно.
opts.transposed
, уставляются надлежащие значения. И
даже более того -- в случае "обычной" n-этажности слева у не-первых
rowlabel'ов должен быть небольшой margin; в случае же n-корпусности это
нафиг не нужно.
ChlMakeElement()
опять стал огромным и
не-особо-то-красивым. Он опять достиг своего предела, дальше туда уже
фиг что внесешь. (Хотя -- кто знает? Может, опять, как с
n-этажностью, будет у меня озарение, которое все капитально упростит.
Какая-нибудь n-мерность с автоматическим переносом.)
(На всякий случай -- имеется копия предыдущего, "стройного" варианта в BACKUP/Chl_gui.c.20060517_pre_transposed.)
Как бы то ни было -- "done".
08.02.2007: там было забыто одно условие -- что в случае транспонированности колонкосворачивающими становятся метки строк, что есть полный бред.
Первоначально хотел поправить, делая в зависимости от флага
transposed сворачивающими либо одни метки, либо другие, но --
LabelClickHandler()
-то всегда берет строчку из
colnames
, он ведь про транспонированность-то нифига не
знает. Так что пришлось в случае транспонированности просто отключать
это нафиг.
(А как выглядело-то -- можно было сворачивать даже меткой из середины элемента, вау! Вот если бы как-нибудь передавать не ДВА параметра -- e@closure, col@userData, а три -- ms, col и sk... Или флаг transposed если б в элементе хранился...)
Занадобилось это для программы BPM нового поколения (linbpm) -- она Chl-based, и захотелось, чтобы "замерзали" не только графики, но и числа тоже.
04.05.2006: собственно, сразу было ясно, что
правильная точка для реализации этого -- Chl, и самое натуральное
решение -- в состоянии "freezed" просто не выполнять никаких действий
по приходу данных от cda; т.е., ничего не делать в
UpdateChannels()
.
Так что -- введен флаг datainfo_t.is_freezed
(default=0), а UpdateChannels()
теперь начинается с
if(di->is_freezed)return
. Для управления флагом
создана API-функция ChlFreeze()
, каковой и передается
True/False.
ChlPopulateWorkspace()
, что занимается раскладыванием
готовой группировки, в отдельную публичную функцию
ChlLayOutGrouping()
.
Проблема же -- в том, что там есть несколько СТАТИЧЕСКИХ переменных, а надо бы их сделать per-window. Добавляемся к priv2?
08.07.2006: да, надо именно так. Несколько замечаний:
cmChlNNN
.
ChlHandleStdCommand()
, возвращающая 1, если команда ей
известна, и 0 -- если нет.
Так что юзерские CommandProc'ы могут просто вызывать эту функцию
перед/после своего switch()
'а, по вкусу.
{load,save}_{dialog,nfndlg}
уходят в
priv2rec_t
.
stored_app_name
становится нафиг не нужной
-- у нас уже давно (с 03-05-2005) есть priv2rec_t.sysname
.
13.07.2006: сделал, работает. Т.е., все по
вышеозначенному проекту, плюс введены константы
cmChlLoad_FIRST
...cmChlLoad_LAST
и
cmChlSave_FIRST
...cmChlSave_LAST
, чтоб
программа могла отключать функциональность "save"/"load" по своему
усмотрению.
Мелкая деталь -- команды идут с 1000.
При переделке из CommandProc()
в
ChlHandleStdCommand()
стало совершенно и окончательно
очевидно, что надлежит избавиться от onewin
(точнее --
вначале забыл, так оно в не-simple-программах по SIGSEGV падало).
Теперь оно не static
, а стало просто переменной
win
в ChlRunSimpleApp()
.
На будущее же -- надо б будет сделать поддержку сохранения/чтения не режимов, а "вообще" -- как в istcc; т.е., не "mode", а "data".
Пока же -- достаточно, так что "done".
18.08.2006: "как-то" -- концептуально-то понятно как: надо иметь лишний вид knob'а, который бы и работал такой лампочкой, и впихивать его как-нибудь в группировку.
Дело в том, что никакого иного разумного решения не прослеживается, просто потому, что при "notoolbar" у Chl'я просто НЕТУ места, куда он мог бы упхать лампочку -- ведь весь workspace занят не им, и предсказать, где будет свободное место, он не сможет. А вот у самой группировки такая информация есть.
Проблема же -- в том, что knob'ы вообще-то НИЧЕГО не должны знать про Chl... Значит, реализовывать оное в виде knob-плагина в Chl'е, да?
31.08.2006: да, надо делать именно так. См. раздел о "Chl_leds_knob". Этот же пункт прикрываем, считая за "done".
29.07.2006: в Knobt_typesP.h были добавлены:
eleminfo_t.alarm_acked
.
knobs_emethods_t.AckAlarm()
-- сразу за
.ShowAlarm()
.
И -- изготовлен собственно метод E_AckAlarm_m()
. Он
вначале проверяет, нельзя ли вызвать метод AckAlarm()
parent'а (поскольку и ShowAlarm()
также передается
parent'у). И если нельзя -- т.е., оно уже и есть от "верхнего"
элемента -- то выставляет alarm_acked=1
и гасит подсветку.
В CycleAlarm()
же добавлено условие, что ничего не
делать не только при отсутствии alarm'а, но и при уставленном флаге
alarm_acked
.
Флаг alarm_acked
сбрасывается при появлении нового
alarm'а в E_ShowAlarm_m()
.
Заодно исправил старый "ляп": в
Chl_gui.c::E_ShowAlarm_m()
вызов такого же метода
parent'а был сделан халтурно -- просто вызывалось
E_ShowAlarm_m(ei->uplink,onoff)
. Заменил на
правильный, с проверками есть-ли-каждый-компонент вызов
ei->uplink->emlink->ShowAlarm(ei->uplink,onoff)
.
ChlMakeElement()
плюс
ChlSetCustomElementsTable()
.
E_Nnn_m()
в
Chl_E_Nnn_m()
.
get_label_and_tip()
надо
опубликовывать во внутренний интерфейс.
01.08.2006: поехали!
ChlMakeElement()
же остался простой перебор по
eleminfo_t.type
для выбора соответствующего типа
element-плагина, с вызовом его "создавальщика".
Практически аналог CreateKnob()
, только без тамошних
дополнительных действий. В частности, и поле emethods
пока
что ставится самими создавальщиками, и никакихх махинаций с
колоризацией и привешиванием тултипа не делается.
IEH_SPACING
/IEV_SPACING
и "прототипы"
attach_sss()
.
CheckForDoubleClick()
и get_label_and_tip()
,
переименовав их в _Chl_CheckForDoubleClick()
и
_Chl_get_label_and_tip()
соответственно.
21.03.2010: для возможности использования в не-Cdr'ных
программах (fastadc) _Chl_CheckForDoubleClick()
унесена из
Chl_gui.c+ChlI.h в
Knobs_internals.c+KnobsI.h и переименована обратно в
CheckForDoubleClick()
. Это полностью соответствует
ситуации с MotifKnobs_IsDoubleClick()
.
Chl_E_Nnn_m()
(из Chl_gui.h и
Chl_knobprops.h прототипы удалены).
ELEM_TABBER
(свежедобавленный в cxdata.h). Реально оное было с
минимальными изменениями взято из
work/karpov/xmclients/linbpm_knobplugins.c; linbpm теперь
можно переводить на ELEM_TABBER
.
В методах этого элемента в позициях ShowAlarm
,
AckAlarm
и NewData
стоят NULL
--
чтобы "визуальная иерархия" заканчивалась внутри закладок.
_Chl_std_elemopts_t
, таблица содержится в
Chl_internals.c (каковой пришлось создать ради этого), а
ссылку на нее отдает _Chl_GetStd_text2elemopts()
.
Заодно:
ChlSetChanVal()
переведена с тупого вызова
E_SetPhysValue_m(ki,v)
на "кошерный" вызов
ki->uplink->emlink->SetPhysValue(ki,v)
.
ChlCreateContainer()
была строчка
e->emlink=&emethods
(видать, оставалась с
момента добавления интерфейса контейнеров); убрана.
Таким образом:
В общем, "done".
21.02.2007: для возможности использования вне Chl
(точнее, пока что в ndbp_adc200_elemplugin.c) декларация
_Chl_CheckForDoubleClick()
вытащена в ChlI.h.
18.03.2007: поскольку XmTabStack появился только
в OpenMotif-2.2, то еще с полмесяца назад для компилируемости под
RedHat-7.1/OpenMotif-2.1.30 была добавлена условная компиляция -- в
Chl_tabber.c и в Chl_gui.c соответствующий код
включался только для (XmVERSION*1000+XmREVISION)>=2002
.
А сегодня переделал это на единый #define
XM_TABBER_AVAILABLE
, уставляемый в Chl_tabber.h
-- чтоб, если появится поддержка LessTif'а, все менять в ОДНОМ месте.
21.08.2006: сделал отображение и units тоже.
Собственно, просто внаглую воспользовался функцией
SetTextString()
, доступной в KnobsI.h. А та уж
так удобно устроена, что смотрит не на ki->is_rw
, а на
w::XmNeditable
(видимо, для всяких крутилок и бегунков в
режиме "value"), и, поскольку big.val
всегда
не-editable, то units добавляются всегда.
ChlGridMaker()
просто как константы 2
.
И точно так же надлежит опубликовать
IEH_SPACING
/IEV_SPACING
, дабы их могли
использовать и другие расстановщики.
31.08.2006: опубликовал. Для сего создал файл
include/ChlI.h.
IEH_SPACING
и IEV_SPACING
переименовались в
CHL_INTERELEM_H_SPACING
и
CHL_INTERELEM_V_SPACING
, а spacing'и между ручками
обозваны CHL_INTERKNOB_H_SPACING
и
CHL_INTERKNOB_V_SPACING
.
CHL_{MODE,DATA,LOG,OPS}_PATTERN_FMT
должны
определяться вовсе не в Chl.h (ибо они вовсе не привязаны к
GUI), а в каком-то другом месте. В Cdr'е, что ли?
Chl_E_SetPhysValue_m()
поддержку "невидимых" ручек --
которые есть в группировке, но не имеют knob'ов.
09.04.2007: понадобилось для ndbp_image_elemplugin: чтоб там были "shadow knobs", отражающие параметры большого канала -- для сохранения/восстановления режима.
А проблема заключалась в том, что для определения принадлежности
knob'а (XhWindow) использовался его indicator
, который у
"невидимых" ручек, естественно, NULL.
Решение -- оно идет вверх по иерархии (->uplink), пытаясь найти
хоть кого-то с container!=NULL
.
Конечно, халтура это все, и реально прямо по самому KNOB'У должно идтись вверх до надобности, и безо всяких widget'ов нарывать ссылку на datainfo/или-что-то-подобное. CXv4!
10.04.2007: еще мысли по теме:
elem_private
-- может,
использовать его?
Неа, нельзя -- ведь использование этого поля зависит от конкретного
типа элемента, а Chl_E_SetPhysValue_m()
сейчас всегда
используется один и тот же.
uplink->emlink->SetPhysValue()
принцип, похожий на
используемый в Chl_E_ShowAlarm_m()
-- пытаться идти вверх
по иерархии, пока не наткнешься на SetPhysValue()!=NULL
, и
тогда уж вызывать его. (Смысл -- можно уставлять ссылку на этот
метод только у верхнего узла иерархии.)
Но это уже, видимо, только в CXv4, где группировка также будет
просто элементом, также имеющим emlink
.
А вообще-то, конечно, не должно быть никакой привязки к GUI. Как? А просто -- то, что сейчас -- datainfo, должно стать свойством группировки, а вовсе не окна (XhWindow).
Но по минимуму надо постараться как-нибудь (_windowref?) убрать привязку к GUI -- XhWindowOf(indicator|container) -- уже сейчас, в CXv2.
07.07.2010: угу -- сегодня наткнулись на
неприятное последствие этой кривой архитектуры с нецелевым
использованием поля container
, при применении
ELEM_SUBWIN'а:
-- очевидно, для того, чтобыe->container = ABSTRZE(rec->btn);
Chl_E_SetPhysValue_m()
мог
добираться до верхушки РОДИТЕЛЬСКОГО окна.
TitleClickHandler()
'ом
XhSwitchContentFoldedness()
пытается добыть
XmNchildren/XmNnumChildren у не-Composite-widget'а, ничего не
вычитывает, поимевает мусор, и...
Пока-то выкручусь -- достаточно SUBWIN'ам всегда ставить "nofold", но это криво...
23.07.2010: а сегодня -- еще один прикол:
SIGSEGV'ится уже DisplayAlarm()
, ровно по той же самой
причине.
Пофиксил, просто вставив проверку на XtIsComposite()
.
И в XhSwitchContentFoldedness()
тоже. Прочих подобных
мест поиск вроде бы не нашел.
07.05.2007: вообще-то надо бы хорошенько подумать. Реально ли надо делать именно "callback по вводу юзером"? И как именно программа должна будет этим пользоваться? И -- ведь, по идее, "синтетические" ручки (те, которые без привязки к каналам) должны бы все равно как-то фунциклировать через локальные регистры.
Думать, думать, и еще раз обдумывать!
20.05.2008: хо -- а это оч-чень пересекается с
уже реализованной недавно фичей cda_set_lclregchg_cb()
!
Поскольку сейчас того вполне хватит, а в CXv4 вообще будут настоящие
локальные каналы, то тему можно закрыть с классом "obsolete".
07.05.2007: ну с указанием такого желания все просто -- вводим в Chl_internals.[ch] дополнительный ключик "sepfold".
Дальше -- пришлось ввести eleminfo_t.opt_sepfold
, и
присваивать ему opts.sepfold везде, где надо -- в
ELEM_MULTICOL_Create_m()
и
ELEM_CANVAS_Create_m()
, ну и
ChlCreateSimpleContainer()
тоже не забыл.
Ну а потом -- просто создаем вместо XmLabel соответствующий сепаратор. Единственное что -- длину сепаратора пришлось забить в 60, поскольку длина метки неизвестна.
"done".
А вообще -- давно пора перевести ChlCreateContainer()
с
кучи булевских параметров на один параметр "флаги".
CdrFindKnob()
-- ChlFindKnob()
. Она
позволяет получить прямой доступ к интересующему каналу.
13.07.2010: ага, а в cx-starter'е-то тогда забыл тоже увеличить -- бодро оно на liucc орало про "invalid local register"! Поправлено.
08.10.2014@Снежинск-каземат-11: поскольку в liu число регистров уже приближается к 1000, увеличиваем уже до 4000 -- скорее всего, более не потребуется.
(Тема тесно связана с флагом appopts_t.resizable
, а
косвенно -- с
placement
."horz=fill,vert=fill").
20.05.2008: сразу встает технический вопрос -- а
где указывать, что это надо делать? В
{elemnet_t,eleminfo_t}.options
нельзя -- поскольку они
принадлежат уже самим элементам и парсятся ими. А нужен аналог поля
placement
. Оным является -- хотя и рудиментарно -- поле
{groupunit_t,groupelem_t}.fromnewline
. Учитывая, что
де-факто всегда это поле или 0, или указывалось 1 -- можно переделать
его в набор битовых флагов, где младшим битом оставить "с новой
строки", а ввести еще по биту на "приаттачивать горизонтально" и
"приаттачивать вертикально". (Отдельный вопрос -- отношение этих
флагов к "направлению" is_vertical :-)
Естественно, этому надо будет и 4cx/src/lib/MotifKnobs/MotifKnobs_lrtb_grpg.c научить.
Несколькими часами позже: весь день промаялся, вроде как бы сделал, но -- опять глюки с ресайзингом в форме! А ловить-то resizeCallback -- нечем, ибо оный callback есть только у DrawingArea, которая тут отсутствует... :-(
А что сделал:
GU_FLAG_FROMNEWLINE
.
GU_FLAG_FILLHORZ
и
GU_FLAG_FILLVERT
.
ELEM_MULTICOL_Create_m()
&
Co.: там transposed
, тут -- is_vertical
, а
понятность в обоих случаях никакая...
03.08.2009: был ляп -- оно ведь force'ит newline
при помощи уставки prevline=line; line=NULL;
, а если
следующий элемент сам делает fronmewline -- то сие производилось
повторно, и ссылка на предыдущую строку забывалась.
Так что вставил в обработку GU_FLAG_FROMNEWLINE
проверку -- теперь оно делается только при line!=NULL
.
10.08.2009: и в
4cx/src/lib/MotifKnobs/MotifKnobs_lrtb_grpg.c фича также
портирована. Выглядит реализация намного красивее; учтены и местные
уроки -- там для управления переносом строки введена специальная
флаговая переменная force_newline
.
28.04.2011@Снежинск-каземат-11: проблема "опять глюки с ресайзингом в форме", похоже, решена -- надо элементу указывать флаг "one", и тогда всё аттачится и ресайзится (по крайней мере, по горизонтали) как надо. Проверено на liubpms.
(Оно должно было бы решаться
placement
."horz=fill,vert=fill", но сетка у нас
глючит, так что...)
Проверил, что самому последнему элементу-строке можно указывать
одновременно GU_FLAG_FILLVERT|GU_FLAG_FILLVERT
, и это работает.
Засим -- считаем первоначальную цель достигнутой, "done".
27.02.2009: понятно, что реализовывать это надо
полностью аналогично вчерашним параметрам hspc/vspc у элемента-сетки.
Параметры эти указываются уже у "программы", а их парсинг -- в
Chl_app.c. Задница же в том, что пришлось добавить параметры
hspc,vspc
в сравнительно публичные интерфейсы
ChlPopulateWorkspace()
и ChlLayOutGrouping()
;
но снаружи оно использовалось только в
vacclient_meat.c::CreateMeat()
и
cdmclient.c::main()
, которые поправлены
(содержимое ARCHIVE/ не считаем).
И в 4cx/ тоже добавил -- это также оказалось намного проще.
P.S. А для liucc это, оказывается, было не нужно -- там все "модуляторы" в одном элементе, и надо было править ЕГО hspc/vspc. Но уже сделано, и общности ради пусть будет.
28.03.2009: да, сделал -- добавил именно флажок
"nohline". Перевел оба интерфейса -- и
ChlCreateContainer()
, и
ChlCreateSimpleContainer()
(который, кстати, использовался
только в старом istc/, который 2257, а сейчас никем не
юзается).
Сама-то отработка флага несложная, главное было --
модифицировать всех юзеров -- поскольку добавился лишний параметр у
функции. Если вдруг захотим еще что добавить -- то надо будет
перевести ChlCreateContainer()
с кучи параметров на один
параметр "флаги". Например, когда понадобится флаг "nofold".
И еще, вследствие специфики функции
SwitchContainerFoldedness()
, nohline-элементы не будут
сворачиваться. Пока забиваем, но потом надо будет переделать.
И в 4cx/ тоже сделал.
28.03.2009: насчет "nohline-элементы не будут
сворачиваться" из-за специфики SwitchContainerFoldedness()
-- поправил, просто заменив его на
SwitchContentFoldedness()
из xmclients/*adc*.c (этот же
код также уже использовался в 4cx/'ном варианте --
MotifKnobs_internals.c
), который переехал в Xh,
превратившись в XhSwitchContentFoldedness()
.
04.04.2009: типа протокол:
ChlCreateContainer()
с
кучи параметров на один параметр flags
. Это и было
сделано -- флаги имеют префикс CHL_CC_
, а в 4cx/ --
MOTIFKNOBS_CF_
.
eleminfo_t
. Поправил, выкинув эти поля
opt_fold_v
и opt_sepfold
, и сделав обработку
аналогично прочим флагам (в 4cx/ именно так было сделано
изначально).
(Встаёт вопрос -- а какого лешего они вообще были сделаны полями, а не передавались параметрами? Не хотел список параметров перетрясать?)
(Заново эта потребность вылезла всё в том же пресловутом weldcc.)
25.06.2009: да, хочется-то хочется, но уж шибко это мерзко и муторно. И вот резоны:
Так что -- нафиг-нафиг, не будем делать, "withdrawn".
13.11.2009: хочется это для LIU-программы balkon -- там в одном окне есть несколько сравнительно слабосвязанных элементов, которые надо бы уметь сохранять/восстанавливать независимо.
Идея реализации -- завести маленькие пиктограммки "Save" и "Load", и чтоб они могли помещаться справа от метки элемента.
Следствия:
ChlCreateContainer()
, соответственно, жить оно будет в
Chl_gui.c.
flags
.
Детали проекта реализации:
13.12.2009: (ровно через месяц!) насчет того, как именно будут помещаться кнопочки рядом с заголовком -- см. в секции "Еще немного о красивостях" раздел за сегодня о помещении ручек/подэлементов рядом с заголовком.
18.08.2010: кстати, а собственно само сохранение
и считывание режимов per-element на уровне Cdr делается просто -- достаточно сделать публичные API-обёртки к функциям
RecSaveElement()
и RecLoadLevel()
.
А дальше вопрос будет уже в создании обвязки, обеспечивающей а) fdlg-"распознавание" таких "локальных" режимов; б) файловые диалоги.
И, кстати, отдельный вопрос -- не делать ли эти файлы "локальных режимов" годными для загрузки и напрямую в основную группировку тоже? Тяжковато, ибо варианты таковы:
Но тут проблема будет в случае, если кто-то из содержателей имеет не-IdentifierIsSensible()-имя: без такой обвязки элемент можно было бы сохранить/восстановить, а с ней -- фиг.
Итого: как сделать -- всё понятно (и со стороны Cdr, и со стороны размещения кнопок на элементе), но собственно КРИТИЧЕСКОЙ надобности сейчас нет, так что -- откладываем, а как понадобится, сделаем за день.
18.04.2011@Снежинск-каземат-11: последовательность действий:
_Chl_std_elemopts_t
+_Chl_GetStd_text2elemopts()
.
_Chl_containeropts_t
+text2_Chl_containeropts[]
.
_Chl_multicolopts_t
+text2_Chl_multicolopts[]
, оно
"включает" обще-контейнерные поля.
ChlCreateSimpleContainer()
собственная
"containeropts_t" была изведена в пользу стандартной INCLUDE-таблицы.
Посему -- сделаны значения:
CHL_CF_TOOLBAR
и CHL_CF_ATTITLE
--
долженствующие разрешать создание дополнительных формочек под эти две
сущности.
CHL_TITLE_SIDE_{TOP,BOTTOM,LEFT,RIGHT}
={0,1,2,3}, а во
флаги добавлено поле CHL_CF_TITLE_SIDE_MASK
, в которое те
значения попадают, сдвинутые на CHL_CF_TITLE_SIDE_shift
.
eleminfo_t
:
folded
", как
было в liu/fastadc/.
toolbar
и attitle
.
ChlCreateContainer()
-- махинации с собственно размещением
виджетов:
container
, а
в ttl_cont
.
09.11.2015: а вот это дало и минус: при моргании ALARM'ом заголовок теперь не мигает, поскольку не является непосредственным child'ом container'а.
Соответственно, caption аттачится к ним (а при отсутствии -- к форме), так что эффективно центрируется в отведённом ему месте.
Код стал ПРЕМОНСТРУОЗНЕЙШИМ -- толпа последовательных и вложенных if()'ов для аттачментов.
title_side
для передачи
через флаги во всех юзеров вставлена.
19.04.2011@Снежинск-каземат-11: продолжаем:
ELEM_MULTICOL_Create_m_as()
& Co.:
nflrs
берется не из битов 16:31 ncols, а из
битов 16:23.
nattl
, беримая из битов 24:31. При наличии
notitle она обнуляется.
e->count
теперь используется
ngrid
=e->count-nattl.
kl
также берётся как content+nattl.
14.07.2014: Решение просто: надо nattl сохранять в privrec'е и при разворачивании учитывать.
ELEM_CANVAS_Create_m()
внесены аналогичные
изменения (хотя и в меньшем объёме).
ChlPopulateContainerAttitle()
-- набивает виджет
attitle, горизонтально или вертикально.
ChlGiveBirthToKnobAt()
, создающая
ручку/подэлемент в УКАЗАННОМ parent (вместо
всегда-в-e->innage
).
Хотя мини-тулбар и не работает (он -- прерогатива предыдущего раздела), всё остальное есть, так что "done".
21.04.2011@Снежинск-каземат-11: тонкость-неприятность -- по дурости контейнеру attitle делалось XmNtraversalOn=False, так что заголовочные ручки не фокусировались (для LOGD_TEXT -- это критично).
Пофиксил.
(Что интересно, сам контейнер заголовка -- фокусировке вовсе не препятствует.)
26.09.2011@Снежинск-каземат-11: довольно уродливо выглядит то, что поля заголовков (title) ОТЛИЧАЮТСЯ от полей обычных ручек -- в результате ручки в заголовке с заголовком не выровнены.
Улучшаем:
27.09.2011@Снежинск-каземат-11: на эту тему см. раздел Chl_submenu за сегодня.
14.06.2012: и в 4cx/ скопировано, вместе с обновлённым Xh_fallbacks.h. Что занятно -- elemCaption уменьшился, а collabel сегодня ровно на столько же по высоте увеличились, так что общая высота элемента осталась той же.
25.09.2013: тогда была забыта проверка на тему "nattl>count" (актуально при занулении count в целях отладки; оно стало падать). Добавлена.
14.06.2015: неприятно, что в attitle НЕ работает placement/layinfo. Конкретно не хватает возможности делать vert=fill vseparator'ам -- они были б весьма полезны в однострочном "элементе" для разделения смысловых групп ручек.
ChlGetGrouplist()
-- чтоб сторонние knob-плагины могли
пользоваться Cdr_script'ом.
ChlIsReadonly()
-- аксессор к флагу
is_readonly
, для fastadc/pzframe-knob-plugin'ов.
Это коснулось fastadc_gui.c, его будущего варианта yzframe_gui.c, liu/y/manyadcs_knobplugin.c, а за компанию -- и Chl_histplot.c.
Смысл его заключается в следующем: стандартизация функциональности, располагавшейся в первую очередь в Chl_simple.c, чтоб примерно ею же могли пользоваться и другие программы.
Непосредственной причиной стало желание избавиться от постоянно повторяющихся всяких {linbpm,linipp}_{meat,gui}.c, которые реально были просто первым подходом к использованию ЕДИНОГО приложения с plugin-архитектурой. И, соответственно, теперь, после сбора их опыта (на тему "что можно унифицировать"), настало время двигаться дальше. В том числе, и в сторону обозначенной 03-05-2005 парадигмы с "ChlInit...()".
(BTW: это как бы продолжение работы, начатой описанными выше в
общем разделе Chl за 28-06-2006 и 08-07-2006 "опубликованием"
ChlLayOutGrouping()
и
ChlHandleStdCommand()
. То было сделано под потребности
vacclient'а -- весьма специальной (из-за двуликости) программы, а
нынешнее -- уже для всех остальных не-simple.)
19.07.2006: итак, последовательность действий была такой:
ChlRunSimpleApp()
было
обобщено и помещено в функцию ChlRunApp()
-- каковая
исполняет ВСЕ стандартные действия по "раскрутке" программы (в т.ч.
fallbacks и регистрацию Xt-окружения для cda). (Всякие
специфичности, типа загрузки группировки и регистрации
plugin-компонентов, должны делаться ПЕРЕД ее вызовом.)
Были добавлены параметры toolslist
,
CommandProc
,
newdataproc
+userptr
, и все параметры
пересгруппированы по "смыслу" -- сначала
argc
+argv[]
, потом описание окна/GUI, а в
конце -- описание "данных".
И -- ChlRunSimpleApp()
теперь содержит просто ее вызов,
с указанием СВОИХ тулбара и обработчика команд.
P.S. Да, этот интерфейс НЕ подходит для многооконных программ, но -- у них обычно несколько иное устройство, да и -- когда они последний раз требовались? :-)
ChlHandleStdCommand()
со своими
helper'ами.
Так что Chl_simple.c стал маленький-маленький и простенький-простенький -- переходник-wrapper.
RecordEvent()
и
его использование сами переехали в _gui из _simple прошлой весной).
Ради них пришлось перетащить в Chl_priv2.[ch] и
datainfo_t
+DataInfoOf()
. Реально шаг к
объединению этих структур в одну.
Так что -- в Chl_app.c теперь и живут всякости, которые "составляют суть Chl'я".
А в Chl_gui.c остались три класса вещей:
get_label_and_tip()
, которой, наверное, уже давно икается
-- в "liblogchans").
ChlSet{Sysname,Server,PhysInfo,Grouplist}()
,
ChlFreeze()
, ChlSetDataArrivedCallback()
,
ChlGetMainSid()
, Chl{Get,Set}ChanVal()
).
Собственно -- это и есть то, что желалось 13-07-2004 унести в отдельный модуль _data. И "поперезавязанность" уже практически исчезла.
Подперетряхнул порядок всех этих вещей -- чтоб шло пологичнее, "по
функциональным группам". Все окей -- кроме одного: бедненькая
UpdateChannels()
вынуждена вызывать
CycleAlarm()
для каждого элемента. А по-хорошему-то это должно делаться прямо при
ОБСЧЕТЕ -- тот самый метод knobs_emethods_t.NewData()
пусть и делает.
Также кардинально перекурочил файл Chl.h. Он был результатом долгой эволюции -- полным бардаком. Теперь все упорядочено по группам -- как в .c-файлах.
Итак -- "done".
28.07.2006: да, перевел вызов
CycleAlarm()
в метод NewData()
.
28.07.2006: сделано.
cmChlFreeze
.
ChlHandleStdCommand()
.
ChlFreeze()
появилась возможность указывать не
точное состояние, а "переключи" -- -1
. Так что теперь --
и это надо бы сделать общим принципом! --
>0
-- включить, 0
-- выключить,
<0
-- переключить.
ChlRecordEvent()
-- он не добавлял в конце "\n".
Раньше-то его использование было ограничено внутренностями самого Chl'я
(которые передавали "\n" сами), а вот сегодня наконец-то
появилось первое внешнее применение -- сообщения о работе с фоном в
linipp.
24.08.2006: что ж, исправил ситуацию -- теперь он сам добавляет перенос строки, а из старых вызовов он был удален.
P.S. Собственно, общее правило -- нефиг! Плюс -- унификация с
XhMakeMessage()
.
ChlRunApp()
-- отсутствует флажок compact,
который бы маппировался на XhWindowCompactMask
.
Сделал.
ChlRunApp()
отсутствовала
проверка результата вызова ChlPopulateWorkspace()
-- стыд
и позор! В результате группировка с глючками приводила к появлению
окна с оформлением -- тулбар, statusline, ..., но без содержимого.
Исправил.
ChlRunApp()
; но оно слишком велико. Так что --
просто добавил к ChlRunApp()
параметр
fallbacks
(после options
), который если NULL
-- то используются обычные фоллбэки. Точек использования
ChlRunApp()
было немного, все они подправлены.
21.05.2007: Проблема только -- что этот ключик
легче отрабатывать ChlRunSimpleApp()
'у, поскольку он тогда
мог бы подменять toolslist[]
, но -- он ведь является
просто wrapper'ом, и никакого отношения к ключикам не имеет...
Первоначально-то (29-04-2007) была мысля воспользоваться
XhHalvePixmap()
'ом, чтобы он сам кнопочки уменьшал, но --
тот проект с треском провалился.
Но потребность-то никуда не делась, надо задачу как-то решать...
06.06.2007: вставил в ChlRunApp()
проверку, что при srvspec[0]=='-'
просто ругаемся.
И создание таких клиентов является довольно мутной работой -- оно сводится к копированию кода из "наиболее недавнего" такого же, с внесением некоторых исправлений/добавлений "в соответствии с современным пониманием".
А ведь явно напрашивается стандартизовать эти общие для
мультиклиентов действия: сделать еще один уровень-надстройку над
ChlRunApp()
-- назовем ее
ChlRunMulticlientApp()
, в которую бы поместить то, что
делают такие клиенты (по-хорошему надо постараться свести к ней же и
clhclient.c+Chl_simple.c).
04.05.2008: по здравому размышлению стало ясно,
что clhclient.c+Chl_simple.c сводить не надо -- если
куда и сводить, то к обычной ChlRunApp()
; с multiclient же
-- это ортогональные задачи. А пока вообще можно не трогать (главный
резон -- разделение при линковке: сейчас-то пиктограммы и тулбар
содержатся в отдельном .o-файле).
Что же до реализации заявленной задачи: лучше это произвести в
отдельном модуле, из тех же соображений линковки -- чтоб
CdrOpenDescription()
не был обязательным.
Хотя, по еще более здравому размышлению -- с учетом пренебрежимо малых объемов этих отдельно-влинковываемостей, на якобы проблему можно забить и действительно внести ВСЕ в Chl_app.c. (Реально объемы стали бы важны в v4, с ее навороченным модульным загрузчиком, но там вся эта тема вообще неактуальна по определению.)
Еще соображение: первоначально хотел дать
ChlRunMulticlientApp()
кучу параметров, включая таблицы
специфических ручек/элементов. Но подумал -- а нафиг? Ведь все эти
регистрации -- по одной строчке, и делаться могут (как и смена
Xh-цветов) в самом начале программы, ибо никак не завязаны с порядком
иных действий. Так что -- делаем МИНИМАЛЬНЫЙ список параметров.
И еще: как-то надо поддерживать возможность указания доп. параметров в командной строке -- ну там, имя файла для загрузки...
05.05.2008: да, сделал, в простейшем варианте. А именно:
client SYSNAME [srvref] [file_to_load]
оно пока не поддерживает, а токмо через symlink --
sysname [srvref]
Но это вопрос нескольких часов мутроного кодирования -- зато сразу ВСЕ
программы-юзеры научатся.
ChlRun{,Simple,Multiclient}App()
сводить к друг дружке, а
сделать некую глобалистичную функцию, выполняющую ВСЮ работу (ну, кроме
загрузки группировки), а этих всех сделать wrapper'ами к ней.
Очень-очень похоже на execve()
с кучей ее frontend'ов.
20.06.2009: сегодня на
ChlRunMulticlientApp()
переведены практически все
обозначенные в исходном ТЗ программы. И chlclient, и ndbp, и
ippclient, и bpmclient. Вновь создаваемые мультиклиенты также делаются
на нем.
Никакой "глобалистичной функции" делать не стал -- удобнее оказалось именно как описано в первоначальной пляжной идее.
Хотя супер-задачи (типа загрузки файлов и запускан напрямую) пока и не сделаны, но сделано основное -- так что помечаем как "done".
14.06.2010: хочется всё-таки уметь и загружать файлы, и уставлять прямо из командной строки значения в каналы. В двух целях:
Для этого можно из файлов режимов выкусывать все "настоящие" каналы, оставляя только такие локальные.
Как это можно реализовать:
RunFastADC()
& Co. -- с
PK_xxx.
p_xc
фтопку);
srvspec
, потом между проходами
"реализовывать" программу, а на 2-м проходе -- уже уставлять значения и
грузить файлы.
ChlLoadWindowMode()
, так что можно смело этот параметр
выкидывать.
22.06.2010: да, сделано, ровно по указанному проекту.
Единственное что -- уж очень уродско и наколенно это всё выглядит, особенно рукодельный парсинг USRTXT-knob'ов.
20.05.2008: сделал -- ввел appopts-флаг
"resizable", при котором бит XhWindowUnresizableMask
сбрасывается.
(Поскольку в 4cx/ вообще пока win_options не используется, то forw-портировать пока некуда, но в будущем -- потребуется.)
21.05.2008: да, и в 4cx/ также портировал.
04.08.2008: так и сделал, параметр
logperiod (диапазон 0-86400 секунд). Сохраняется параметр в
свежедобавленном поле winlogrec_t.defperiod
.
Заодно пришлось в ChlRunApp()
параметр
equals_c
поменять с '=' на ':',
поскольку в mkclient.sh парсинг сделан настолько по-простому,
что '=' в значениях не пропускает.
Проверил -- работает.
04.04.2010: ага, "работает" -- щаз!!! Конкретно
в случае linvac-программ -- НЕ работало. Из-за того, что linvac всё
делает в обход ChlRun*App(), то поле defperiod
и не
инициализировалось, оставаясь равным 0. Ну оно и ставило нулевой
таймаут, и начинало сыпать лог невообразимыми пачками (да еще и без
заголовков, ибо -0 НЕ <0).
Корень-то проблемы в том, что
Но пока просто перенес проверку if(period==0)period=DEF_LOG_PERIOD в
собственно ChlSwitchLogging()
.
Проверил -- работает.
04.04.2010: да, собственно -- теперь при логгинге выдаются и миллисекунды, через точку (и в числе секунд, и после stroftime() -- аналогично canmon'у (но в том МИКРОсекунды)).
Вышеописанная проблема и вылезла при проверке работающести.
01.07.2010: ну не очень-то 04-04-2010 была
пофиксена работа logperiod'а -- в ChlRunApp()
имелась
странная строка
так что ЛЮБОЙ указанный период сводился к 1 секунде. Поправлено на нормальное условное присвоение.lr->defperiod = opts.logperiod > 0;
01.07.2010: собственно, ради чего я туда полез: Карнаев хочет иметь протокол с частотой ВЫШЕ 1Гц -- для ращенкятника, там хочется 5Гц. А у нас всё в целочисленных секундах...
По факту -- хочется иметь 2 фичи:
(Да, имеет смысл только для первичных серверов, с aux бесполезно.)
Как можно сделать:
Первые 2 пункта реализованы -- так что, в принципе, для текущих потребностей достаточно. (Кстати, единественным внедеревным файлом, использующим данный API, оказался impacis10.c, где пришлось заменить 3 на 3*1000.)
(Оно хоть и отдельным файлом, но по смыслу -- часть Chl_app'а, так что описано здесь.)
19.06.2009: туда переселен
toolslist[]
из Chl_simple.c, и для доступа к нему
создана ChlGetStdToolslist()
. Смысл -- чтобы
программам-которые-почти-как-chlclient, не приходилось бы иметь у себя
копию определения стандартного тулбара для "простых" программ, а все бы
пользовались одним и тем же.
Кстати, Chl_simple.c теперь имеет совсем смешной размер.
Впору его/ChlRunSimpleApp()
вообще прикончить, перенеся
содержимое в сам chlclient.c -- которого перевести на
ChlRunMulticlientApp()
.
20.06.2009: да, сделано -- копированием скелета содержимого из weldclient.c. Что ж...
Аминь!Эпитафия
Chl_simple.c родился в далекиее времена, когда клиенты делались вручную или копированием, и никакой внятной инфраструктуры не существовало. В те дикие годы именно он был на острие прогресса, стоял у основ нынешнего Chl'я. Именно из его тела вышла практически вся функциональность Chl_app'а.
Chl_simple.c, ты прожил свой век, потихоньку уменьшаясь, отдавая свой код в общее пользование, и наконец уменьшился до нуля. Спасибо тебе, и R.I.P.!
28.12.2009: предварительные соображения:
Идея решения довольно проста:
надо при запуске вызывать считывание некоего файла-режима со специальным именем, но при этом блокировать собственно отправку данных.Это решалось бы прямо в Chl'е -- поскольку Cdr вызывает
ki->uplink->emlink->SetPhysValue()
, каковой по
факту является Chl_E_SetPhysValue_m()
, то можно б было в
оном проверять некий флажок в datainfo_t
-записи окна. Но
не всё так просто:
SetControlValue()
ВСЕ РАВНО
будет вызывать у каждой ручки
ki->vmtlink->SetValue()
, так что в ней отобразится
неиспользуемое значение.
Chl_E_SetPhysValue_m()
и регистровые параметры также будут
проигнорированы (а не только запись в каналы). А иначе -- уставка
регистров ведь делается формулами, а там может затесаться и запись в
каналы.
Посему, реально работоспособным будет лишь такой вариант:
SetControlValue()
-- видимо, поменять параметр
fromlocal
на source
, с вариантами
SCVS_FROMSERVER=0, SCVS_FROMLOCAL=1, SCVS_FROMCONFIG=2.)
appopts_t
добавляется флажок типа
"loaddefsettings", при указанности которого в options оно загрузит файл
в таком полу-режиме.
30.12.2009: хорошенько подумал -- да ну его нафиг!!! Столько мучений и лазанья в самые глубокие внутренности библиотек ради довольно-таки дурацкой потребности, которая вылезает ТОЛЬКО в не-DataBase-endbled-системах, причем ТОЛЬКО для локальных-стендоподобных программ. Так что -- "withdrawn".
Хотя в CXv4, конечно, надо будет сию потребность учитывать и предусмотреть параметр "flags/options" для всех участвующих в загрузке функциях, чтоб можно было указывать "не исполнять запись в каналы".
26.02.2012: по многочисленным просьбам публики (в лице Феди) всё же возвращаемся к этому вопросу. Только подход выбираем иной: не "протыкать что-то там" и не "не исполнять запись", а ограничивать, какие классы данных будут актуализированы после чтения из файла. Протокол:
options
с битовыми
флагами.
CDR_OPT_NOLOAD_VALUES
и
CDR_OPT_NOLOAD_RANGES
(они на всякий случай идут с 31
вниз, так что не пересекаются со всякими READONLY).
options
функциям
ChlLoadWindowMode()
, CdrLoadGrouplistMode()
и
её подчинённым RecLoadLevel()
, ParseUsrTxt()
,
ParseChanVal()
.
ParseChanVal()
содержит проверки. Причём
incdec_step и grpcoeff отнесены к VALUES.
CDR_OPT_NOLOAD_RANGES
.
*_FMT
).
27.02.2012: продолжение:
CHL_RNGS_PATTERN_FMT
.
loaddefsettings
теперь сохраняется в свежесделанном
datainfo_t.is_protected
. И при загрузке обычных режимов
CDR_OPT_NOLOAD_RANGES
указывается лишь при is_protected.
В противном случае в не-loaddefsettings-программах диапазоны вообще никогда бы не могли быть считаны из файла.
...но в actionknobs лишь-значения грузятся ВСЕГДА -- в том смысл.
Проверено (на widgettest) -- вроде пашет.
ChlRunApp()
'ом парадигму запуска приложений с
нынешней
prog [DEFSERVER]на
prog {ARGUMENTS}-- аналогично fastadc_common-программам, где среди ARGUMENTS могут быть и defserver, и имя файла для загрузки, и прочие команды.
В таком случае можно было бы (через cx-starter-*.conf) указывать параметры/команды "при старте загрузи такой-то файл" и "при выходе сохрани екущий режим туда-то".
09.04.2010: пара замечаний насчёт того, КАК надлежит сохранять режим:
rename()
его в нужный.
И вообще -- а настолько ли правильно ВСЕГДА при выходе записывать текущие настройки? Вот взяли, поигрались, а вдруг испортили хорошие настройки -- и чё? Может, всё-таки записывать именно ПО КОМАНДЕ? Ну или, как минимум, при записи нового файла -- переименовывать старый в что-нибудь-такое, что будет доступно из окна загрузки режимов?
26.05.2010: еще в продолжение: пусть в командрой строке среди ARGUMENTS можно будет
указывать и "запиши в такой-то канал то-то" -- например, в
формате ИМЯ_РУЧКИ=ЗНАЧЕНИЕ (где ИМЯ_РУЧКИ указывалось бы в формате для
ChlSetChanVal()
/CdrFindKnob()
).
Сие будет полезно для случаев небольших-стендиков, когда надо прописывать в железо некоторые "стабильные" параметры (например, в КШД485@ЛИУ -- рабочий и удержательный токи).
06.08.2010: странно, что тогда (06.07.2010?) не записал, но -- "парадигма" на таковую от fastadc_common давно заменена. Естественно, с некоторыми отличиями. Детали:
PK_SWITCH
.
PK_PARAM
заменён на PK_SETTING
, с
соответствующим изменением эвристики определения.
PK_BIGC
сменено на
PK_SERVER
.
Кстати, последней каплей для реализации этого стала просьба Довженко -- ему нужна программа-стенд для испытания/настройки источников, которая бы грузила бы коэффициенты из файла. Вот как раз сделанное все его потребности и покрывает.
В общем:
03.02.2011@Снежинск-каземат-11: с тех пор старый вариант еще
оставался в коде за-#if-0
'енным. Выкинул.
21.09.2011@Снежинск-каземат-11: за компанию с добавлением
ключика "-readonly" также сделано, что при неизвестном ключе оно
ругается и exit(2)
.
Но оно нужно совсем не везде. Так что -- введём ключик freezable, который будет разрешать наличие этой кнопочки.
06.09.2010: делаем.
Xh_ACT_NOP
, на который
заменяется код у кнопки "Freeze" для её отключения.
appopts_t.freezable
и
соответствующий ключик в text2appopts[]
.
Работает, "done".
25.12.2011: ага, и оно просто внаглую лезло в toolslist[], даже не глядя, есть ли он вообще.
Исправлено, и заодно добавлено форсение opts.notoolbar=1 при toolslist==NULL.
27.06.2012: тьфу ты, это втихушку повлияло на linipp, где давно имелась кнопка "freeze" -- она стала отключаться. Решил тривиально -- добавлением туда ключа freezable.
ELEM_CANVAS
:23.07.2006: соображения по теме:
InputOnly
-окнами. И свои размеры прямоугольник и эллипс
при рисовании должны вычитывать из виджета, а не просто помнить
уставленное им.
Таким образом, помимо собственно технически-ремесленной стороны, есть три крупных вопроса и один мелкий:
XmForm
).
XmATTACH_OPPOSITE_WIDGET
.
XmATTACH_OPPOSITE_FORM
, хотя и
фиг знает зачем.
Можно еще NNN дополнить опциональным суффиксом -- чтобы кроме пикселов мочь указывать в "условных" единицах, например, как в Форточках -- в четвертинках размера шрифта.
InputOnly
-окна: просто втихушку подменить Motif'у окно
может оказаться не столь просто. В пределе, возможно, придется
изготовить собственный виджет на основе XmPrimitive
-- но
это-то не проблема.
ExposeEvent
-то будет приходить ЕМУ!!!
Видимо, как-то им придется договариваться -- например, у Canvas'а
заводить список-программу команд рисования.
27.07.2006: протокол, как все реализовывалось:
24.07.2006: этот новый модуль Chl_canvas.c изготовлен. И собственно
ELEM_CANVAS
бОльшей частью реализован.
- Методы объекта -- берутся от обычных
ELEM_SIGNLECOL
/ELEM_MULTICOL
, ради чего их --E_SetPhysValue_m()
,E_ShowAlarm_m()
,E_NewData_m()
-- пришлось опубликовать в свежесозданном Chl_gui.h.- I: Работа с аттачментами сделана точно по проекту в пункте I (за вычетом "четвертинок").
- II: для InputOnly-окон сделан собственный специальный виджет (см. соответствующий раздел за сегодня).
- Для соблюдения интуитивного порядка визуализации (кто-поверх-кого) ручки создаются в обратном порядке -- с конца списка (в X11 кто первый -- тот и выше).
25.07.2006:
- III: рисование на окне parent'а -- очень просто, мы ловим у формы-canvas'а
ExposeEvent
, и по нему производим рисование. Рисование "чего" -- см. ниже.- IV: параметры же, соответственно, не то что унифицированы -- они просто парсятся одним и тем же куском кода ВСЕ И ВСЕГДА.
- Поддержка логических цветов (%nnn) пока не сделана. А сами цвета -- по умолчанию для фона берется powerpoint'овский #bbe0e3 (он и хорошо заметен, и неярок), а для бордюра -- черный.
- Декоративные виджеты работают в одном из двух режимов:
- Внутри
ELEM_CANVAS
'а -- тогда они используютXmInputOnly()
и сами нифига не рисуют.- Внутри другого элемента -- тогда они "имитируют" canvas, используя
XmDrawingArea
и рисуя на ней.- Canvas же, в
CanvasExposeHandler()
'е, проходится по списку всех своих child'ов, и если проверяемый -- knob-декорация, то "она" отрисовывается на canvas'е.- (Процесс опознания canvas'ом декораций и декорациями canvas'а производится по полям
knobinfo_t.vmtlink
иeleminfo_t.emlink
соответственно -- они сравниваются с искомыми.)- Само же рисование производится одной и той же функцией
DrawDecor()
, которую вызывает и самодеятельная ручка для своей DrawingArea, и canvas. Просто кромеki
передается еще параметрon_parent
, который если 1, то рисование идет наXtParent(ki->indicator)
(т.е. -- на canvas'е), с прибавлением координат виджета.В privrec'е каждого knob-декорации записываются:
- Тип декорации (
shape
) и заполненность (filled
).- Ширина бордюра-обводки. (Замечание: пока она работает как булевское: 0 -- нет, >0 -- есть.)
- Цвета -- точнее, pixel'ы и GC'ы -- для самой фигуры и бордюра
- Некие type-dependent параметры -- такие, как список точек для многоугольника/ломаной.
- (Размеры прямоугольника и эллипса берутся из размеров виджета.)
- Текст предполагается возможным иметь у любой декорации -- со строкой, беримой из
ki->label
, и отдельно указываемым позиционированием в стиле "аттачментов"/"геометрии"; это будем делать завтра.26.07.2006: сделал поддержку текста. Это просто писец какой-то -- со всеми этими
XFontStruct
'ами иXmFontList
'ами, которые надо загонять один в другой, но при этом НЕ загонять в GC! Шаманство, танцы с бубном, еще только заячьей лапки не хватает...По делу:
- Отдельно уставляются параметры fontmod (normal, italic, bold, bolditalic) и fontsize (tiny, small, normal, large, huge (слизано с TeX'а) -- отображаются на 8, 10, 12, 18, 24).
- Выравнивание для многострочных -- textalign (left, center, right).
- Позиционированием управляет параметр textpos, имеющий формат [xxx]H[yyy]V, где H -- тип горизонтального позиционирования (l=left, c=center, r=right), V -- вертикального (t=top, m=middle, b=bottom), а xxx и yyy -- опциональные смещения (от l/t и c/m -- в плюс, от r/b -- в минус).
Либо можно указывать ключевое слово NONE -- тогда текст подавляется (а по умолчанию он есть, если есть метка).
Парсинг там примитивнейший -- никакой проверки ошибок. По умолчанию выходит "0c0m".
Кроме того, сделал поддержку эллипсов -- они по параметрам полностью совместимы с прямоугольниками. Единственная проблема -- эти долбанутые алгоритмы: контурный и закрашенный эллипсы рисуются РАЗНЫМИ алгоритмами, так что закрашенный слегка торчит наружу от точно такого же контурного.
26.07.2006: поддержка ломаных, а также обычных и закрашенных многоугольников также сделана. Детали:
- Набор точек указывается в параметре points, и имеет вид "+X+Y{/[.]+X+Y}". Т.е. -- в стиле "геометрии", точки разделяются слэшем. Опциональная точка (".") указывает, что координаты -- не абсолютные, а отсчитываются от предыдущей. На месте "+", естественно, может стоять и "-".
Этот немного странный подход -- со слэшем вместо напрашивающихся вариантов "X,Y{;X,Y}" либо "+X+Y{,+X+Y}" -- обусловлен тем, что запятая уже сейчас используется в качестве разделителя в widdepinfo, да и точка с запятой также может получить где-нибудь аналогичный статус.
- Внутренняя реализация определяется неким не самым очевидным набором соображений:
- Поскольку точная/актуальная позиция декорации на canvas'е становится известна только в момент рисования, то хранить абсолютные координаты точек -- нельзя. Надо уметь смещать всю фигуру на (x0,y0).
- (А еще возможна ситуация, когда рисуем не на canvas'е, а на DrawingArea самого виджета. Тогда ничего смещать вообще не надо.)
- Хочется мочь указывать не только абсолютные координаты точек, но и относительные.
- X11 поддерживает режим "относительных координат" в операциях
XDrawLines()
иXFillPolygon()
-- режимCoordModePrevious
.- В X11 нет операции "нарисовать замкнутый многоугольник", в отличие от "закрасить замкнутый многоугольник" --
XFillPolygon()
. Зато в качестве такой операции работаетXDrawLines()
, если последняя точка совпадает с первой -- в man-странице битым текстом сказано, что это специально поддерживается, и даже ни один пиксел не будет отрисован дважды.Посему избран следующий подход:
- Храним именно относительные координаты (первая точка, естественно, абсолютная). Используется именно
CoordModePrevious
.И при отрисовке прямо в массиве к координатам первой точки прибавляем (x0,y0), так что вся фигура смещается. После отрисовки -- вычитаем обратно.
- (Локальное рисование -- частный случай, проблем не создающий.)
- Относительные координаты поддерживаются автоматически -- они и оказываются основными.
- НЕ относительные координаты приводятся к относительным -- парсер постоянно помнит абсолютные координаты предыдущей точки.
- Хранится не n, а n+1 точек -- последняя совпадает с первой. (Естественно, последняя парсером уставляется относительно предпоследней, т.е., последней из указанных.)
- Введена поддержка "толстых" линий -- параметр thickness. Он отражается на GC-параметр
line_width
, причем только от значений 2 и выше (1 считается за 0, что для X11 -- "особый случай").Есть некая халтура -- в размеры фигур толщины не входят. Стоило бы включать, но фиг знает как именно их учитывать... Деленные пополам?
За компанию доделана и реальная поддержка толстых бордюров -- там просто точно так же ставится параметр
line_width
.- Также для "толстых" линий можно указывать тип соединений (углов) -- по умолчанию это "угол" (GC-параметр
join_style
ставится вJoinMiter
), а указание флага rounded приводит к "закруглениям" (JoinRound
), позволяя тем самым рисовать "водопроводные трубы".- Таким образом, тип LOGD_PIPE/SHAPE_PIPE, хоть и поддерживается, но является просто синонимом LOGD_POLYLINE/SHAPE_POLYLINE.
Насчет более интеллектуальных/упрощенных/гибких аттачментов имеется идея:
А что, если ввести понятие "якорей", которые могут прилагаться к любой фигуре в некотором количестве, и сводятся реально к указанию неких оффсетов от краев фигуры. А уж аттачменты других knob'ов считывают эти оффсеты и прибавляют их к своим. Сие позволило бы несколько упростить всякие там "аттачменты к середине трубы". Указывать якоря можно было бы как anchors=name:+X+Y{;name:+x+y}, а ссылаться на них -- вместо просто SIBLING писать "SIBLING.anchorname".
Кстати -- проверил, как работает мерцание красным фоном при alarm'е. Что удивительно -- правильно!!! Т.е., по уставке ресурса
XmNbackground
, похоже, вызывается не просто методexpose
, а делается "честно"XClearArea()
....да, порылся в исходниках -- src/xc/lib/Xt/SetValues.c::
XtSetValues()
-- там действительно делаетсяXClearArea()
.
Таким образом, проект "ELEM_CANVAS" можно считать реализованным. Из незавершенностей/несовершенств:
В общем -- "done".
Когда декорация принимает решение "сменить свой
вид", то она уставляет некий флажок в privrec'е. Метод же
NewData()
canvas'а проходится по всем своим
knob-декорациям, и проверяет этот флажок. Как только натыкается на
уставленный -- для всех декорации начиная с этого вызывает перерисовку
(тем самым, если кто-то накладывается сверху на изменившуюся декорацию,
то он также будет перерисован); флаги же при проходе сбрасываются.
Здесь используется тот факт, что NewData()
вызывается
ПОСЛЕ обсчета knob'ов.
Во-первых, вопрос указывания таких вещей: кроме ширины и высоты писать также угол поворота?
Во-вторых, собственно "менеджмент" -- отрисовка, реакция на события, и проч. Как бы это сделать, обойдясь БЕЗ шейпованных окон, а? Разве что работать именно совсем "врукопашную", как бы на поверхности canvas'а...
14.07.2007: в результате имеем такие соображения:
1. Менять свою видимость в зависимости от канала/формулы. (В случае с turnmag'ом завели бы 4 хреновинки на одном месте, каждая из которых бы показывалась только когда в концевиках соответствующий ей код.)
- и/или -
2. Менять внешний вид в зависимости от канала/формулы -- "анимация". (В случае с turnmag'ом завели бы 4 кадра, по количеству вариаций из 2 бит.)
FindChild()
не проверял ident!=NULL
и
SIGSEGV'ился -- что за позор!
ELEM_SINGLECOL
/ELEM_MULTICOL
в отдельный
модуль, получивший название Chl_multicol.c.
01.08.2006: сделано.
08.02.2007: сделано, хотя слегка и хаковато: при
уставленном clone_color
оно проверяет, что если
sk
представляет из себя не-подэлемент и при этом не-NOP
(проверки взяты из Cdr), то ему навешивается event handler на
функцию-обработчик, скопированную из Knobs_internals.c.
29.03.2007: угу, а еще ведь и СЮДА надо было
добавить проверку на Shift -- что вызывать ToHistPlot()
.
Может, просто Mouse3Handler()
опубликовать в
KnobsI.h, а?
Да, опубликовал под именем KnobsMouse3Handler()
, и
перешел на нее.
12.02.2007: оказалось все просто -- при ошибке
делается bzero() того, куда парсятся опции, а fold_h -- это как раз 0.
Так что для решения проблемы пришлось после bzero() поставить и
opts.fold_v=1
.
Ошибка при дизайне этих опций была в том, что значение опции по умолчанию НЕ является нулем. Так что, на будущее: надо ВСЕГДА подбирать значения опций так, чтобы все умолчания имели нулевые значения.
04.04.2009: да, конкретно с fold_v ляп исправлен
-- теперь у нас поле fold_h
, по умолчанию равное 0.
sk=e->content+x
при x>=count...
16.02.2007: а вот и нет -- в этом новом алгоритме
расстановки (с эмуляцией SINGLECOL'а MULTICOL'ом, и уж тем более в
варианте с n-этажностью) метки колонок создаются в основном цикле
расстановки ручек, а там есть условие
y*ncols+x < e->count
, так что до
x>=count дело никогда не дойдет. Посему -- "withdrawn".
26.02.2009: единственный способ указать
нестандартные интервалы -- это в options
, а за них
отвечают общие для всей Chl _Chl_std_elemopts_t
и
text2_Chl_std_elemopts[]
. Так что добавлены поля
hspc
,vspc
и одноименные ключики. И: значения
по умолчанию -- -1
, а не 0
, т.к. 0 -- вполне
легитимное (пустое) расстояние; поэтому в юзеров стандартной
структуры (multicol и canvas, а за subwin всё делает именно multicol)
добавлено hspc=vspc=-1 при ошибке парсинга.
А для передачи этой информации ChlGridMaker()
'у
воспользовался ранее пустовавшим параметром userptr
-- в
нем передается &opts
.
И в 4cx/ тоже добавил -- там с options
схема намного
более правильная и простая. Единственный вопрос -- а
насколько в 4cx/ надо ТАК делать? Или же правильнее взвалить
это на "стили"?
27.02.2009: совсем забыл -- там же еще
*_INTERELEM_H_SPACING
используется. Так что -- ввел в обе
версии еще поле colspc
с одноименным ключиком.
01.02.2011@Снежинск-каземат-11:
ELEM_MULTICOL_Create_m_as()
, и ей теперь передаётся 3-м
параметром _Chl_std_elemopts_t *opts_p
.
ELEM_MULTICOL_Create_m()
же теперь просто парсит параметры и
вызывает _as()
.
auxlabel
из
_Chl_std_elemopts_t
, теперь его парсит сам
Chl_subwin.c (и параметр переименован в просто label).
31.08.2006: ввел LOGD_SRV_LEDS=4000
.
И саму хреновинку сделал -- в файле Chl_leds_knob.c.
Единственная проблема -- когда она ставится в своем собственном элементе, то занимает слишком много места (точнее, пустого места для нее отводится слишком в избытке). Так что надо упихивать прямо в какой-нибудь элемент.
(Да-а-а, а если б ее еще можно было подселять прямо в заголовок элемента -- совсем бы было роскошно...)
Засим -- "done".
27.09.2006: ай, маладца-а-а!!! А keepalive-таймаут-то не работал! Так что при скопычивании сервера индикатор продолжал обманчиво показывать зеленый!
Оказалось все просто: ему передавалась ki
вместо
ожидаемого rec
, так что и sid был несколько миллионов, и
count тоже. Вот фигня и происходила (и почему оно не segfault'илось?
Загадка...).
09.11.2006: сделал. Собственно, все было
довольно просто: во-первых, ресурс XmNdialogStyle
уставлять в XmDIALOG_MODELESS
(вместо былого
XmDIALOG_PRIMARY_APPLICATION_MODAL
), а во-вторых, в
Chl_E_ShowBigValWindow()
при
XtIsManaged(pdp->big.box)
теперь делается не return, а
XtUnmanageChild()
.
В принципе, приемлемо. Первоначальная цель -- работа с программой
при появленном окошке -- достигнута. Есть, правда, какие-то
странноватые глючки при повторном нажатии на Ctrl+MB3: если окно bigval
находится поверх основного -- то иногда подтормаживает; плюс, если
ПЕРВЫЕ сколько-то раз появившееся окно передвинуть, то оно перепоявится
опять в центре, а потом перестает. Эти два глючка как-то явно связаны
между собой; попытка вызывать XSync()
ситуацию не чинит.
Впрочем, поскольку в будущем перейдем с замены отображаемого в bigval knob'а на возможность множественного отображения, то на это все можно забить.
09.11.2006: мыслишки в сторону:
knobinfo_t
), да еще и вводить некий дополнительный метод в
knobs_emethods_t
-- "обновить bigval", который надо будет
вызывать из CdrProcessKnobs()
.
Впрочем, с точки зрения эргономики будет именно держать все bigval'ы в одном окне. На этом и порешим.
06.02.2007: сделано, в одном окне. Кнопки [^], [v], [-] позволяют передвигать строчки вверх/вниз и удалять их.
Теперь надо проверять на юзерах.
Кстати, в старой версии был memory leak в Chl_E_ShowBigValWindow() -- не делалось XmStringFree().
09.03.2007: уже больше месяца работает, вполне успешно, так что -- "done".
Единственная проблема: хоть ловить-то это в
Knobs_internals.c::Mouse3Handler()
несложно, но
метода "knobs_emethods_t.ShowMedVal()" у нас нету -- увы. (Как и
параметра "степень увеличения" у .ShowBigVal()
.)
dlgrec_t
*rec
/ThisDialog()
,
XhCreateStdDlg
/no-rec->shell
.
24.08.2009: да, сделал. Не стал, как хотел
сначала, выпендриваться и внедрять "правильную" архитектуру от новой
версии, а просто занулил флаг XhStdDlgFModal
у основного
диалога (у range и yesno -- пока нет) и проверку
XtIsManaged()
. На вид -- всё окей.
22.12.2010@Снежинск-каземат-11: Сделано -- в самый верх таблицы был добавлен pth_dpy.
Фокус в том, как для него добывается значение.
GetTextColumns()
+1. Чтоб юзеры имели право на опечатку --
а то сегодня умудрились в поле "%5.0e" (вакуум) ввести что-то типа
1e-044, которое туда почему-то не влезло, и отображалось как "1e-04".
02.03.2011: угу, добавлено +2 колонки.
29.05.2013: имя поля при этом сменено с
nam_lbl
на nam_dpy
.
Если раньше они отображались в окне, а менялись в отдельном под-окне, да еще с дополнительным "Are you sure?", что безмерно раздражало, то теперь прямо в самом окне отображаются текстовыми полями, прямо в которых и менябельны.
27.06.2014:
RangeDpy
.
kprops_range_t
kpropsdlg_t.z
.
Плюс дополнительно поле is_closing
.
#if
'ами
RANGES_INLINE
-- при окончательном переходе старое
выкинуть будет просто.
12.11.2015: а тут некая проблема: при /fixedranges
(DATAKNOB_B_RANGES_FXD
) диапазоны не показываются вовсе, что
неприятно.
PropsOkCB()
, если chg стоит, то
делается ExtractDoubleValue()
с проверкой, и если бо-бо,
то окно остаётся на экране.
ExtractDoubleValue()
-- можно использовать
свежевведённую арифметику.
XmNlosingFocusCallback
, делающий холостой вызов
ExtractDoubleValue()
-- чисто для бибиканья, но ничего не
запрещающий.
is_closing
-- оно
выставляется перед прятаньем окна.
28.06.2014: всё равно сделано, и проверено -- подсвечивает (причём БЕЗ глюков, характерных для обычных окон; загадка...).
28.06.2014: парой часов позже: там-то допилено, а вот
тут оказалось хреновастенько -- делает XhShowHilite()
при
начальном прописывании значений, когда не надо. Исправить-то это
несложно -- ввести в kpropsdlg_t
флажок "is_initializing";
но ЗАЧЕМ? Ведь семантика тут отличается от Knob-text'овской, так что
-- просто закомментировано.
min>=max
-- что раньше показывалось как
"None".
27.06.2014: эта же архитектура внедрена и в v4.
Тонкая проблема, присутствовавшая и раньше, но замеченная лишь сейчас: если в каком-то из введённых чисел синтаксическая ошибка, то по нажатию [OK] оно бибикнет и оставит окно, но предыдущие числа УЖЕ БУДУТ складированы в ручку-клиент!
Надо PropsOkCB()
сделать 2-стадийной: на 0-м
этапе только вычитывать числа из текстовых полей, но СКЛАДИРОВАТЬ в
ручку только уже на 1-м.
22.06.2014: да, сделано 2-стадийное считывание; проблема ушла.
12.03.2007: (сделано за прошедшие несколько дней, но записываем только сейчас) модуль называем так же, как в свое время в istc/vibro, поскольку тот был его предком.
Summary:
knobs_emethods_t
добавлен метод
ToHistPlot()
.
Chl_E_ToHistPlot()
(чей код живет в
Chl_histplot.c), и ссылка на него вставлена во все таблицы
elem-plugin'ов.
"Недостаток" по сравнению с istc'шным вариантом -- график ОДИН, а не множественные. Но:
И еще: я-то рисую "нахаляву", XDrawPoint+XDrawLine*(n-1), а
по-хорошему надо бы набивать массив из XPoint
'ов и
отдавать его ОДНОМУ XDrawLines()
-- это должно сильно
экономить (пакетирование плюс меньший объем пересылки серверу). Гусев
так и делает. Но: у меня и так все отлично работает, и мерцания почти
не видно! И даже resize отрабатывается НЕСРАВНЕННО быстрее гусиного
(или дело в том, что у него pixmap?). Так что -- пока в
XDrawLines()
глубокого смысла нету.
РЕАЛЬНО же существующая проблема -- что довольно хренова компоновка окна: там XmMessageBox, с кнопкой [Ok] снизу, в который вставлена XmForm. Поэтому --
Так что -- надо радикально переделывать компоновку.
29.03.2007: поскольку уже не первую неделю используется, и приемлемо работает, то -- "done", а остальные улучшения отдельным разделом.
dlgrec_t
*rec
/ThisDialog()
.
29.03.2007: заиспользовал флаги
XhStdDlgFNothing
и XhStdDlgFNoMargins
, так
что осталась только РЕАЛЬНО полезная площадь, а вместо кнопки [OK]
сделал свою кнопку [Close], приаттаченную в правый нижний угол, и
уставленную как defaultButton у box'а.
18.04.2007: исправил "погано-ресайзовость" -- вместо былого одинакового аттачмента виджетов graph и axis (слева к форме, а справа к grid), с различием только в оффсетах (у axis -- нули, у graph -- m_lft и m_rgt) перешел к новой модели.
Она заключается в том, что горизонтальные оффсеты -- ВСЕГДА нули, а "место" под подписи образуется при помощи пары дополнительных виджетов (w_l и w_r), служащих только для расчета геометрии, но невидимых, поскольку располагаются "под" axis. Этим виджетам и задаются в качестве ширин m_lft и m_rgt, а уж graph и axis приаттачены к ним: graph -- обычным образом, а axis -- через OPPOSITE_WIDGET.
Строго говоря -- повезло! С человеческой-то точки зрения принципиальных отличий между старым и новым вариантами нету, и я уже был готов к тому, чтобы перейти на более муторную (хотя и более корректную!) схему с двумя ВЛОЖЕННЫМИ drawingArea -- axis, которая ловит resizeCallback и надлежащим образом переделывает своего child'а graph.
Со вздохом облегчения -- "done".
XDrawLines()
, это должно СУЩЕСТВЕННО снизить нагрузку на
сервер.
07.05.2007: поскольку в принципе количество точек может быть довольно большим, а как-то к чему-то (DEF_RINGSIZE тут, и MAX_PIX_W в adc200) привязываться не хочется, то простейший общий алгоритм таков:
10.05.2007: да, сделано. Функция "выталкивания"
именуется FlushXLines()
. Хотя, насколько именно такой
вариант меньше грузит процессор -- ХЗ, ибо linvac, к примеру, в top'е
выглядит одинаково, что старый вариант, что новый.
На будущее, кстати, для уменьшения возможности мерцания:
можно было бы стирание старого и отрисовку сетки также засунуть в
отдельную функцию, которую вызывать перед САМЫМ первым
FlushXLines()
-- это потенциально уменьшло бы интервал
между стиранием и отрисовкой. Хотя, скорее всего -- Xlib'овская работа
с буфером, скорее всего, и так достаточно умна.
Итого -- "done".
09.03.2010: во всех экземплярах
FlushXLines()
имелся дурацкий ляп: ставилось
*npts_p=0
вместо надлежащего =1
, и в
результате на границе -- где выталкивался буфер -- образовывался
разрыв, т.к. следующий сегмент НЕ соединялся с выталкиваемым. Не
замечал же я этого в основном потому, что редко когда графики бывали
шире 1000 пикселов (размер буфера).
Исправил.
01.09.2007: да, сделал, полный комплект: и поле
"Hist. frqdiv." в окошке "Knob Properties", и ввел
knobinfo_t.histring_frqdivchg
, отражающий
модифицированность -- и работающий ровно так же, как и флаги
измененности для диапазонов, и поддержку в режимах --
сохранение/считывание frqdiv'а в Cdr.
30.08.2008: кстати, в недавно проведенных измерениях какого-то бочонка Корепановым и Баком -- программа korepanov8hrs -- эта frqdiv показала себе очень полезной: у них родная частота сервера была 1Гц, и с ней же надо было складывать лог в файл, а вот на экранном графике хотелось видеть побольше -- например, час, так что значение 4 было в самый раз.
Хреново только, что нельзя указывать это значение в самом описании группировки. Ну да, понятно, что дело в нежелании в очередной раз менять формат _db.so-файлов, и что в CXv4 оно будет, но все равно -- жаль!
19.04.2012: по просьбам трудящегося Еманофеди добавил возможность вместо непрерывной линии отрисовывать точками.
histplotdlg_t.wide
на
histplotdlg_t.mode
.
FlushXPoints()
, плюс тип
do_flush_t
.
03.05.2012: естественно, такие тощенькие точки его не устроили.
Сделан всё-таки 4-й вариант "***". Реализован отдельной функцией
FlushXBlots()
(blot -- клякса), все точки рисуются
поштучно, в цикле, в виде "ромбика". Делается это через
XDrawLines()
, 5-точечным массивом, с
CoordModePrevious
(так мы только 0-й точке присваиваем
координаты очередной точки в цикле, а остальные 4 прописываются заранее
-- {(+1,+0), (-1,+1), (-1,-1), (+1,-1)}).
03.04.2008: первый приходящий в голову вариант -- раз уж эта потребность настолько эндемична для данной программы, то сделать реализацию конкретно в ней.
А потом договорились до другого варианта: не делать НЕСКОЛЬКО окон, а разбить ОДНО окно по вертикали на несколько зон -- при помощи XmPanedWindow.
"Типа проект":
Потенциальные вопросы:
Нет, видимо, не "разреженная хрень", а поступать так же, как и раньше -- просто вместо одного массива "до 5 каналов" иметь N массивов "до 5 каналов" каждый (например, N=4), из которых "включено в отображение" может быть от 1 до N. Соответственно, с самими этими массивами обращаться так, как сейчас обращаемся с каналами внутри панели -- так же сдвигать в памяти при удалении "коллеги", и т.д. и т.п.
Вот только времени на все это сейчас нету :-)
02.07.2009: пара замечаний:
Вопрос -- КАК указывать логарифмичность канала? Пара идей, обе кривоватые, хотя и рабочие:
20.04.2010: идея: а чё б для логарифмического отображения каналов (конкретно этот вакуум на сварке) просто САМО ЗНАЧЕНИЕ не сделать логарифмом (перевести канал на LOGK_CALCED и делать CMD_LOG10 (которую ввести))?
Вот только неясно -- а насколько адекватно это будет? Ведь нынче вакуум отображается как 1E-NN (аналог 1*10-NN), а с логарифмом получится просто -NN.M.
Да, позвонил Цыганову -- он говорит, что их и нынешнее устраивает. Надо б еще разок глянуть на живой установке.
02.03.2011: да, НАДО внедрять логарифмичность в histplot'е, а то на сварке с вакуумом уже совсем задолбало (еще Аркадий Спесивцев добавил "фана" -- хочет чё-то видеть, а что -- не понимает).
План таков:
DrawAxis()
.
04.03.2011: сделано, по вышеприведённому проекту. Тонкости:
log()
нельзя, поскольку при
значении, стремящемся к 0, логарифм стремится к бесконечности. Потому
введено ограничение -- 1e-100.
ShouldUseLog()
вернёт "нет", так что график будет
рисоваться линейно.
Вообще, возможно, это даже слишком сильное ограничение -- поскольку log(1e-100)=-230, и даже log(1e-300)=-690. Но пусть -- такого ограничения достаточно (как минимум для вакуума), так спокойнее, да и пусть свобода для погрешности останется.
y
за
пределами [-32767,+32767], то он вгоняется в этот диапазон, воизбежание
проблем с 16-битной системой координат X11.
DrawAxis()
сделан двойной пересчёт -- оно
делает RESCALE_VALUE()
в диапазон ЛОГАРИФМОВ от пределов,
а потом берет обратно exp()
, и уже его пишет.
На сварке вроде функционирует адекватно -- так что "done".
09.11.2011: а вот фиг "Логарифмический режим использовать при формате 'e'": на сварке Семенов и Спесивцев хочут иметь отображение в e-формате, но ЛИНЕЙНЫЙ график.
Так что надо еще что-то думать...
15.11.2011: пообщался на эту тему со Старостенкой и Цыгановым (первый как экспертофизик, второй -- как эксперт по Семенову и сварке). Результаты:
С другой стороны, в момент "начала работы", или когда ситуация НЕ является стабильной, нужно именно логарифмическое отображение.
По последнему пункту: выглядит оно разумно, но уж очень муторно было б реализовывать: просто выделенным случаем -- хрен, а делать именно два РАЗНЫХ КАНАЛА -- и юзеры приподзапутаются, и всё равно остаётся проблема, что отображать-то надо в формате "%e", но линейно...
Если б у нас была полноценная навороченная программа типа striptool'а -- она бы это всё умела сразу. Но это не так, и ваять прямо сейчас такие навороты -- неохота. Так что надо делать простым способом...
08.07.2009: да, можно будет сделать так:
08.12.2009: да, Семенов и Co. по-прежнему хотят. Причём -- чтоб можно было смотреть, например, за последние M часов, и при надобности -- чтоб можно было смотреть нужное место детально. И еще: им частоты 1Гц мало, надо б около 10Гц.
05.03.2010: другой вариант этой потребности, от Логачева: вот сейчас в weldcc показывается за 12 последних минут (700 пикселов); а чтоб можно было включать также и за последний 1 час и 3 часа. А более мелкий масштаб -- вроде не нужен.
Ну что -- можно сделать селектор (рядом с кнопкой [Wide]), который бы выбирал масштаб. Несколько тонкостей:
07.03.2010: да, решение вообще-то тривиально:
x_scale
,
а при отрисовке вместо "-x" использовать "-x/x_scale".
08.03.2010: и еще:
09.03.2010: приступаем к реализации.
x=grf_w-count+t
-- никакое "-x/x_scale" не прокатит:
поскольку деление (count+t) при (t>=count-x_scale) даст 0, и станет
x=grf_w, что неприемлемо.
Так что надо переходить на отрисовку справа налево -- при этом арифметика становится корректной.
Перешел -- проверил, вроде работает.
(А та отрисовка слева-направо -- шла еще со старого istc'шного xmclients/Chl_histplot.c.)
DEF_RINGSIZE
с 1000
на
86400
.
Селектор управляет свежедобавленным полем
histplotdlg_t.x_scale
, суя туда 1, 10, 60.
P.S. Селектор сделан simple-knob'ом!
/rec->x_scale
, а в выводе подписей к осям --
*rec->x_scale
.
count
-- была
grf_w
, а теперь -- grf_w*rec->x_scale
(это
вначале забыл, и оно всегда рисовало слишком мало точек).
Глядя на нынешнюю таблицу масштабов, лезет мысль: а может, секунды вообще не печатать?
Но тут же встречная мысль: а ведь при base_cycle_size<1000000 оно вполне может давать и не-нулевое поле секунд...
P.S. Проверил по рекомендациям гуру -- разделитель минут и секунд ОБЯЗАТЕЛЬНО должен быть двоеточием, его НЕЛЬЗЯ делать точкой...
P.P.S. Но мерцает оно при нескольких десятках тысяч точек просто страшенно. Увы, тут уж ничего не сделаешь...
11.03.2010: заканчиваем:
cyclesize/1000000
. А cyclesize берётся не от
[0]-го ki, а от mainsid'а.
(Плюс -- в самом цикле делается проверка, не вылезла ли результирующая координата за границу, и если да -- то цикл заканчивается. По-хорошему -- вообще переделать бы организацию цикла, ну да ладно, потом.)
Короче -- добито.
08.02.2011@Снежинск-каземат-11: имелся ляпсус: размер-цикла-сервера оно узнавало далеко не сразу, а попозже -- уже ПОСЛЕ первой отрисовки осей. И в результате потом, по Expose, оно перерисовывало НОВЫЕ временнЫе подписи прямо поверх старых.
От перерисовки-другого-поверх избавился просто -- завёл дополнительно
histplotdlg_t.prev_cyclesize
, куда сохраняю последний-известный
cyclesize, и при несовпадении текущего с предыдущим форсим
do_clear=1
.
Но осталась проблема: оно ничего этого не сделает вплоть до первого
DrawAxis()
после получения первых данных, которое, увы, может
вызваться очень нескоро. Это следствие нашей
криво-хачной модели хранения и отрисовки истории...
23.03.2011@Снежинск-каземат-11: юзеры (в первую очередь на сварке и вакууме) давно хотят иметь возможность прокручивать график далеко назад. Это можно сделать -- потребны 2 вещи:
Начинаем с простого -- со 2-го пункта :-). Вводим
histplotdlg_t.def_histring_size
, а в Chl_app.c
добавлен парсинг его под именем histring_size (максимум --
86400*10).
12.07.2011: а теперь приступаем и к 1-му пункту -- собственно скроллируемость. Раз полноценный, годный сюда, Xh_viewport+Xh_Plot пока не готов, то делаем по образу и подобию Xh_viewport+liu/plot/Zz_plot.
Сейчас сделано:
SetHorzbarParams()
плюс HorzScrollCB()
.
P.S. По реализации оного, с x_mul/x_div, можно будет и в liu/{plot,y}/ горизонтальное масштабирование сделать аналогично.
14.07.2011: готово, скроллится. Что для этого сделано:
CalcPlotParams()
пробегает по всем отображаемым
ручкам, находит максимальный histring_used
, ставит
horz_offset=maxpts/x_scale, и вызывает SetHorzbarParams()
,
возвращая его результат (который, впрочем, никем не используется).
DisplayPlot()
и
XScaleKCB()
.
DrawGraph()
используется dash_ofs
(как в liu/
).
Вообще, конечно, выглядит оно всё преотвратнейше:
- где счётчики цикла идут в реальных "координатах" (т.е., в отсчётах в буфере), а где в экранных (со сдвигом на
horz_offset
);- где всё считается в отсчётах (t), а где в экранных пикселах (т.е., уже в масштабированных координатах);
- где расчёты и сравнение делаются в "прямых" координатах (в t -- т.е., как бы по времени начиная с начала буфера), а где -- как бы в экранных (т.е., уже отзеркалено).
А ведь давным-давно, еще при написании какого-то графического редактора (пиктограмм?) на БК-0010.01 я сделал вывод -- надо стараться ВСЕГДА работать в реальных координатах, пересчитывая их в экранные по мере надобности.
(Ну или, на крайняк, уж пусть в экранных; но не винегретно с одними и другими сразу, что приводит к путанице. Впрочем, в экранных почти нереально -- лимиты-то определяются в первую очередь реальными.)
Короче -- надо, понаступав тут на грабли, разработать внятную модель, годную как для прямого (осциллограммы), так и для обратного (самописцы) рисования, и реализовывать её в Xh_plot'е. Главное -- модель должна быть ОБЯЗАТЕЛЬНО ПРОДУМАНА ЗАРАНЕЕ.
А так -- первоначальная цель достигнута, скроллится, можно смотреть как за несколько часов, так и любой фрагмент детально, так что сей раздел "done".
14.07.2011: P.S. а вот «в liu/{plot,y}/ горизонтальное масштабирование сделать аналогично» не получится. Этот цирк с конями надо переделывать радикально -- просто переписывать с нуля, делая при этом сразу 3 запланированных вещи:
Но этому будет посвящен уже другой раздел, а тут -- навеки "done".
10.03.2010: делаем:
histplotdlg_t.show[]
.
RenewPlotRow()
добавлен еще параметр
showness
, который и уставляет show[]
плюс
состояние чекбокса.
Засим -- "done".
01.08.2011: была мелкая дурь -- для каналов с пустыми метками чекбоксики становились микроскопическими. Потому, что оно ставило метку "", а она имеет нулевой размер.
Пофиксено -- теперь ставится метка из пробела.
Ну что -- видать, нужно изготовить пиктограммку "маленькая дискетка" и уметь прилеплять кнопульку с нею слева от нижнего control'а ([Close] или Wide).
02.08.2010: угу, и Довженко тоже хочет, для своих стендово-отладочных программ.
Интерфейс-то для этого сделать можно -- аналогично обычному сохранению; хоть муторновато (команды мочь ловить -- или, правильно перенаправлять histplot'у; а их еще и НЕСКОЛЬКО может быть!).
А вопрос еще в другом -- КАК сохранять в файл данные, у которых разная длина колонок? Или в те колонки, которых на каком-то времени еще нету, писать NaN?
30.09.2010: да, и Ращенко на стенде тоже это надо -- чтоб экспортировать ИМЕЮЩИЕСЯ графики для анализа в Excel'е.
07.10.2010: за позавчера и сегодня сделал. Highlights:
XhFindFilename()
, по шаблону
CHL_PLOT_PATTERN_FMT
-- "plot_%s_*.dat".
Засим считаем раздел реализованным.
(Потребность возникла для первой версии стенда у Ращенко.)
18.04.2010: несколько замечаний:
13.07.2011: повторные мысли в ту же сторону:
А как бы всё-таки научиться иметь МНОЖЕСТВЕННЫЕ панели "histplot"? Чтоб, например, если есть несколько под-окон, то каждое могло бы включать историю для СВОИХ ручек.
По пунктам:
02.10.2010: технический вопрос -- а как/где эти значения показывать?
Ответ может быть таков: сделать селектор (рядом с селектором "временной масштаб"), где можно было бы выбрать режим отображения чисел: текущее/под_курсором/мин/макс/ср.квадр.
Это заодно решает и старую проблему (за ??.??.200?) "как посмотреть, какое значение было тогда-то".
Главный корень зла -- что обновление/сдвиг истории происходит ПО ПРИХОДУ ДАННЫХ ОТ СЕРВЕРОВ. В результате -- толпа косяков:
В общем, очевидно, что нынешняя модель -- крайне неудачна, от неё сплошные сложности (правильно -- она ведь выбиралась под односерверные программы, да и вообще весь этот механизм был сплошным "врЕменным" хаком, чисто для быстро и удобно посмотреть; и не претендовал на "правильность" в стиле прочих striptool'ов и data-archiever'ов).
Мысль: надо сделать ход песцом и перейти от сдвига/обновления по приходу данных на ПЕРИОДИЧЕСКИЙ -- просто по таймеру.
(Тут, конечно, есть недостаток -- вместо нынешнего гарантированного попадания в историю ровно всех данных (по определению) схема становится более вероятностной -- поскольку таймер сдвига будет срабатывать в достаточно произвольной фазе относительно прихода данных. Но: а) кого это волнует? б) плюсы перевешивают этот микроскопический минус.)
13.07.2011: проект реализации:
knobinfo_t.hist_next
.
AddToHistPlot()
). При этом:
CdrProcessKnobs
.
.config
, в которую и класть.
Проблема только в точном отсчёте времён -- у нас ведь
интерфейс не cxscheduler с возможностью указывать момент сработки, а
Xt, требующая интервал в миллисекундах. Ну, всё просто -- копируем
мозги из
Xh_cxscheduler.c::sl_enqueue_timeout_at()
.
(В идеале -- надо сразу делать с масштабированием как в "минус" (сжатие), так и в "плюс" (растягивание) -- чтоб потом этот код можно было унести в Xh_plot.)
.config
.
Все указанные вещи были описаны в своих подраздельчиках, но, поскольку работа эта совместная, то реализацию (переписывание с нуля) отражать будем здесь.
14.07.2011: начинаем потихоньку, с простого --
сделан пункт 3a: knobinfo_t.hist_next
,
CdrActivateKnobHistory()
,
CdrHistorizeKnobsInList()
.
row
, а
row%XH_NUM_DISTINCT_LINE_COLORS
.
01.08.2011: делаем:
chanGC[]
и
chan_finfo[]
добавлено
%XH_NUM_DISTINCT_LINE_COLORS
.
MAX_HISTPLOT_LINES_PER_BOX
и
XH_NUM_DISTINCT_LINE_COLORS
.
chanGC[]
и
chan_finfo[]
теперь определяется количеством цветов, а не
графиков.
Ну чо -- работает. Вот только подписи к вертикальным осям стали совсем нечитаемыми -- слишком много. Видимо, надо разносить по чётные и нечётные налево/направо.
24.08.2011: кстати, там не хватало psp-параметров
plot9...plot16. Сделаны, причём хитро -- они
"активируются" только при достаточном
MAX_HISTPLOT_LINES_PER_BOX
, иначе alias'ятся на [0].
24.08.2011: да, приступаем к окрасивливанию подписей.
Переделано аналогично, стало намного симпатичней.
Что разносить налево/направо -- не такой тривиальный вопрос:
Посему -- делаем оба варианта. Какой вариант использовать --
определяется переменной vlplace
enum-типа
vlplace_t
:
VLPLACE_BOTH
-- всё и слева, и справа.
VLPLACE_EVENODD
-- нечётные слева, чётные справа.
VLPLACE_HALVES
-- первую половину слева, вторую
справа.
Первый вариант используется при числе меток до 6 включительно, иначе -- какой-то из остальных (пока hardcoded).
Сделано. Технология -- массивы[2]; [0]:слева, [1]:справа; плюс циклы [0...1]: а) при выборе количества строк и б) при отрисовке. Выбор, ставить ли подпись с конкретной стороны, сделан хитрым многоэтажным if'ом, отдельно рассматривающим каждый вариант vlplace.
Проверил, работает. Засим раздел "done". (Но еще надо будет это в liu/.../Zz_plot.c добавить.)
24.08.2011: часом позже: да, и в liu/plot/Zz_plot.c тоже добавлено (но пока не в y/!). Проверено на adc812me -- стало существенно лучше.
02.11.2011@Снежинск-каземат-11: в продолжение темы:
MAX_LINES>XH_NUM_DISTINCT_LINE_COLORS
.
Добавляем поддержку большого количества линий сразу в оба liu/{plot,y}/Zz_plot.c.
%XH_...
в fastadc_gui.c,
где выбирается цвет для метки/чекбокса.
#if
'оиваемые списки.
Намного практичнее была бы возможность указывать что-то типа "addplot=...", чтобы оно само находило первую свободную ячейку. Очевидно, что для этого потребуется PSP-плагин (а вот тут-то и пригодился бы предложенный 26-12-2005, но так и не реализованный value-parsing-support-API в PSP).
24.08.2011: да, сделано, через плагин. Ключ
назван add, сам парсер -- AddPluginParser()
.
Проверено, работает. Позиционные спецификации также оставлены -- чтоб можно было указывать список "вразброс".
18.05.2012@Снежинск-каземат-11: делаем:
histplotdlg_t.timeupd
.
FastadcBigcEventProc()
.
Стало намного приятнее.
Кстати, и Chl_bigvals тоже касается.
16.10.2012@Снежинск-каземат-11: делаем. Все тамошние проверки
заменены на простой прямой вызов KnobsMouse3Handler()
. В
Chl_bigvals.c
аналогично.
По одной из горизонтальных осей подписывать не относительное время (сколько-то назад), а абсолютное (HH:MM:SS).
Чтоб удобнее было понимать, когда что произошло, а не заниматься в уме вычитанием сдвигов из текущего.
Сложность же -- в таком случае надо будет ось с "настоящим временем" перерисовывать каждый раз.
04.03.2013: вот опять та же идея пришла в голову -- по результатам решулярного просмотра графиков сварки "за вчера". Было б удобнее сразу видеть время, а не вычитать 14 часов из текущего.
Вопрос -- КАК это регулировать?
Само-то это действо не особо сложно, но вот КАК (куда) и КОГДА запоминать, а главное -- КОГДА ВОССТАНАВЛИВАТЬ (при запуске?)?
Надо сделать ключик, чтобы [Save], [Mode], [XScl] расставлялись не снизу вверх, а справа налево.
31.08.2016@Снежинск-каземат-11: сделано.
Ключик назван "horz_ctls", реализация тривиальна -- на каждый из управляющих элементов по if()/else'у, а можно б было сделать парой указателей на функции, которым при вертикальной ориентации присваивать {attachbottom,attachtop}, а при горизонтальной -- наоборот.
rec
, а не "pdp", "pdp->big", "hpp", и чтоб тип этого rec'а
можно было всегда указывать как dlgrec_t
, в начале каждого
из Chl_***.c-файла typedef'я его с нужного конкретного типа.
13.03.2007: да, вынес. С точки зрения
переименований -- да, теперь используется dlgrec_t *rec
,
плюс, функция "доступа-к"/создания диалога называется
ThisDialog()
. Кроме того, перешел на
XhCreateStdDlg
и избавился от помнимой
rec->shell
.
02.04.2007: неа, не перенести.
Во-первых, проблемно -- в bigvals нету формы, а непосредственно в box вставляется сразу сетка.
Во-вторых, Малютин сообщил, что с введением графиков большими числами пользоваться практически перестали -- они стали слабо нужны ("большие числа -- это для ламеров").
Плюс, в конце концов, окно bigvals, в отличие от графиков, скорее врЕменное, а не постоянно-нужное, так что на нем особо экономить смысла нет.
Так что -- "withdrawn".
14.06.2007: ага, записать-то записали, а сделать -- так и не сделали. Видать, не надо...
helprec_t
в Chl_help_types.h (чтоб было как у
остальных диалогов) и перевел модуль на dlgrec_t
*rec
, XhCreateStdDlg
/no-rec->shell
.
Смысл -- чтоб simple-knobs-программы могли использовать help с описанием кнопок, не смущая юзеров неработающими сочетаниями.
Поскольку просто взять и добавить кнопку на тулбар нельзя (она тогда ВЕЗДЕ добавится), то придуман такой выход: вводим специальный knob-plugin "mode button", который грузит режим, указанный в widdepinfo, а для Cdr является NOP'ом.
15.03.2007: протокол реализации:
LOGD_MODE_BTN=5000
.
В linmag вставлена красная кнопка [!000!], ко которой грузится mode_linmag_zero.dat (первоначально на ней был тултип "Устаростенить все в ноль, нах!").
Ввести специальный тип подэлемента -- ELEM_SUBWINDOW, который в своем parent'е генерил бы XmPushButton [...], а собственно свое содержимое располагал бы в дополнительном popup-окне, которое бы и появлялось по нажатию на кнопку.
08.05.2007: делается просто "на раз":
И она же ставится tooltip'ом на кнопку (in conformity with cx-starter's approach).
Кстати, а не ввести ли в CXv4 еще одно текстовое поле -- "text2"? Или обходиться каким-нить ключиком в widdepinfo/options?
19.05.2007: собственно, оно ведь работает -- так что "done".
19.05.2007: кстати, ELEM_SUBWIN -- величайшее изобретение после LOGT_SUBELEM!
В принципе -- ничего сложного, просто надо завести свою
elem-methods-table (вместо используемой сейчас multicol'овской) и
сделать _NewData_m(), которая бы вызывала
Chl_E_NewData_m()
, а затем махинировала бы с
цветосостоянием. Состояние ALARM надо показывать как на кнопках --
изменением foreground'а (или и флаг LIGHT тоже показывать? Неа, не
выйдет -- CDR_FLAG'а такого нету. Ну и фиг с ним :).
20.05.2007: сделал, примерно по вышеприведенному
проекту. Отличие от проекта -- что ALARM показывается красным
background'ом (как в cx-starter'е), а не foreground'ом. В силу
особенностей функционирования CdrChooseColorState()
ALARM_ALARM ловится отдельно, как в cx-starter'е, проверкой rflag'ов.
И, поскольку отображение всех флагов совмещено на одной кнопке, то
RELAX и OTHEROP не проверяются -- поскольку тогда пришлось бы еще
выпендриваться с приоритетами (реально -- проверять, что
colstate==COLALARM_NONE).
21.05.2007: да, добавил-таки поддержку и RELAX и
OTHEROP -- просто ПЕРЕД вызовом ChooseKnobColors()
проверяется, если colstate==COLALARM_NONE, то при наличии битов
RELAX/OTHEROP colstate переделывается на соответствующий. Приоритет --
у OTHEROP'а, он перебивает RELAX'а, но -- горит OTHEROP всего один
цикл, в силу своей реализации (этот флаг в cda одноразовый, а
дальнейшее обеспечивается attn_colstate'ом в Cdr).
Вот только почему-то после погасания лампочки оно целый цикл НЕ зажигает RELAX, а только ЧЕРЕЗ цикл. Почему бы?
А вообще -- конечно, корень проблемы кроется именно в хитрой взаимозавязанности rflags и colstate (как достало-то!!!).
18.05.2012@Снежинск-каземат-11: сделал, чтобы VIC-кнопки и в нажатом состоянии оставались зелёными (по просьбе Ярика Куленко, ему так в liu более удобно).
Для этого просто закомментирована первая строчка, именно ради LOGC_VIC делавшая исключение. Дало ровно нужный эффект (хотя я и не вполне понимаю, почему).
_Chl_std_elemopts_t
поле
char auxlabel[100]
и отразить оное в
text2_Chl_std_elemopts[]
.
19.05.2007: сделал, это было несложно.
Что странно -- [Esc] отрабатывается почему-то ровно как надо: если
она приводит к CancelInputEditing()
, то автоматом гасится,
а если нет -- то прикрывает окно. То ли дело в action'е
process-cancel()
? Загадка...
21.05.2007: начал разбираться...
Первой же идеей было гасить соответствующий XEvent при отработке в
TextChangeCB()
. Поскольку это НЕ event-handler, то
никакого continue_to_dispatch там нету, поэтому попробовал просто тупо
уставлять keycode:=0.
Не помогло, стал выяснять почему, и -- задница:
дело в том, что XmText'ов actionВот так по-дурацки устроена обработка событий в Xt/Motif...Activate()
после вызова callback'ов просто молча передает этот event своему parent'у, дергая_XmParentProcess()
(что эффективно просто вызывает методparent_process
) с process_type:=XmINPUT_ACTION и action:=XmPARENT_ACTIVATE, а ужBulletinBoardParentProcess()
, к которому все в конечном итоге и сводится в этой ситуации, в ответ на XmINPUT_ACTION+XmPARENT_ACTIVATE просто дергает actionarm_and_activate
наличествующего defaultbutton'а, НЕ ГЛЯДЯ на содержимое event. Короче -- с event'ом можно сделать что угодно, и он уже НИКАК ни на что не влияет...
Так что -- единственным доступным способом является погасить нафиг default'ность кнопки [OK].
22.05.2007: так и сделал -- воспользовался
свежевведенным как раз для этого флагом
XhStdDlgFNoDefButton
, а чтоб кнопка не выглядела куце,
заменил [OK] на [Close].
28.02.2009: теоретически -- да, можно было бы
просто скопировать из button'а соответствующие куски кода. Но -- ведь
subwin использует стандартную структуру
_Chl_std_elemopts_t
!
01.03.2009: йоу, всё просто -- заменяем один
вызов psp_parse()
на два
psp_parse_as(...,PSP_MF_SKIP_UNKNOWN)
. Минус, конечно,
есть -- оно не будет ругаться на реальные ошибки, но тут уж увы...
Короче -- так и сделал. Практически весь код, относящийся к декорированию, скопирован из Knobs_button_widget.c, с минимальной заменой -- "opts" на "buttonopts" и "w" на "rec->btn".
Оно-то работает, но ведь эта же строка options
используется и ELEM_MULTICOL
'ом, а он-то ругается на
незнакомые параметры... Решение хачно, но просто: вводим специальный
псевдопараметр "SUBWIN_PARAM_SEP", который как бы отделяет
button-параметры subwin'а от обычных параметров. И парсинг специфичных
параметров делается сначала, а потом оно strstr()
'ом ищет
этот псевдопараметр и пишет '\0' в его начало.
Ну и симуляцию knobinfo_t.color
тоже сделал -- параметр
"logc".
Засим -- "done".
P.S. Одно радует -- в MotifKnobs_subwin_cont.c все эти извращения не понадобятся.
01.02.2011@Снежинск-каземат-11: избавляемся от "SUBWIN_PARAM_SEP":
subwinopts_t.elem
, и оттуда уж передаются в
ELEM_MULTICOL_Create_m_as()
.
_Chl_std_elemopts_t.auxlabel
превратилось в
subwinopts_t.label
, и сам параметр теперь называется
label.
14.07.2010: ёпт, а что ж мне это самому-то в голову не пришло!!! Идея-то очевидная, и полностью кореллирует с поведением сворачиваемых-по-заголовку элементов.
Сделал. Заодно и в 4cx/.
P.S. Если где-то всё-таки понадобится старое поведение -- то всегда можно добавить булевский флажок, определяющий его: например, onoff=0/steady=1.
22.07.2010: публика в лице Корепанова возмущается, что кнопки открытых окон никак не выделяются.
А ведь они правы -- идея-то (теперь уж точно!) очевидная, что при открытости под-окна надо кнопку как-нибудь маркировать.
Самое разумное -- инвертировать её, по аналогии с toolbar'ными и с choicebs.
23.07.2010: за эти два дня сделал, хотя и не без маеты:
ReflectShowness()
, сравнивающая
текущее значение XtIsManaged()
с хранимым в
rec->is_pressed
, и при их несовпадении инвертирующая.
Эта функция вызывается из ShowHideSubwin()
, плюс -- из
специально для этого созданного CloseCB()
, вешаемого на
parent-shell box'а (аналогично тому, как это сделано в
Chl_knobprops.c).
XhInvertButton()
было
бы нельзя.
Была мысль сделать в стиле CHOICEBS -- "dlyd_clrz" (delayed colorization), но тут такой подход неприемлем, т.к. кнопки должны ВСЁ ВРЕМЯ показывать текущее состояние, в т.ч. и в "нажатом" виде. Посему:
DoColorize()
.
is_pressed
вместо
defbg
используется defam
(каковой добывается
при создании кнопки).
ReflectShowness()
инвертирование делается так:
XhInvertButton()
;
DoColorize()
.
Вроде работает -- в понедельник опробуем на живых клиентах.
26.07.2010: да, попробовал -- вроде хорошо работает.
12.08.2010: дополнение: для того, чтобы
еще-ни-разу-не-обойденные кнопки были цвета JUSTCREATED, в конец
Create_m() вставлен вызов DoColorize()
. Иначе оно
окрашивалось только после нажатия.
21.07.2010: да-а-а... Ну -- делаем:
XhStdDlgFResizable|XhStdDlgFZoomable
.
С обычной сеткой это проделать не удалось -- там при включении horz=fill и/или vert=fill у единственной (точнее, и так самой широкой) клетки слетает башня, и ставится какой-то коцаный размер.
Посему пришлось извращаться:
_Chl_std_elemopts_t.one
и
соответствующий psp-флаг "one".
ELEM_MULTICOL_Create_m()
, на который
это свалилось. При наличии флага one
и при
e->count==1
он
ChlGridMaker()
использует
ChlFormMaker()
(свежесделанную, хотя раньше была
аналогичная).
А дальше уж оказалось, что XmMessageBox (точнее, его XmBulletinBoard'овская сущность) как раз нужным образом взаимодействует с содержимым на тему ресайзинга.
Вообще, конечно, по результатам этого хаченья стало очевидно, что для SUBWIN'а выбрана неправильная модель. Надо было его делать не как
"а в под-окошке он ведёт себя как ELEM_MULTICOL"а"у ELEM_SUBWIN'а должен быть ЕДИНСТВЕННЫЙ child, который уже может являться хоть MULTICOL'ом, хоть CANVAS'ом, хоть вообще просто ручкой"-- в стиле Shell'а.Но сейчас-то столь радикально менять правила уже поздно -- слишком во многих местах он есть. Вот в v4 -- там да, так и будет.
На вид -- вроде задача решена, пусть и кривовато.
28.04.2011@Снежинск-каземат-11: а эта фича оказалась ОЧЕНЬ полезна не только для под-окон, но и для резайзабельных обычных окон -- когда элемент с ключиком FILLHORZ. Она решила тамошнюю глобальную и первоначально казавшуюся тупиковой проблему.
14.09.2010: готово. Опции называются compact (как и в Chl_app) и noclsbtn соответственно, и указываться должны в разделе subwin-specific -- после SUBWIN_PARAM_SEP.
Собственно отсутствие кнопки [Close] особо не мешает -- окно отлично закрывается как по клавише Esc, так и средствами window manager'а.
29.03.2012: тип назван
ELEM_SUBWINV4
, реализация живёт тут же. Собственно вся
работа перетащена в DoDreate()
, принимающая также флаг
v4
, при котором она вместо вызова
ELEM_MULTICOL_Create_m_as()
сама делает
ChlGiveBirthToKnob(e->content)
(проверив вначале, что
count>0
).
SUBWINV4_START()
сделан (параметр count
в нём
оставлен -- чтоб можно было отключать содержимое; а вот
ncols
убран).
Примерно такое уже было в istc -- как раз тамошний Chl_histplot.c.
14.05.2007: как делать -- понятно:
CdrFindKnob()
, находит этих жертв в группировке
и запоминает ссылки на них, так что пользуется именно ИСХОДНЫМИ
knobinfo'ами.
.placement
ключик horz=fill, чтобы график был
по ширине окна. И оно должно корректно отрабатывать такой
принудительный resize.
25.06.2009: угу, и на сварке -- в weldcc -- такое тоже нужно. Поскольку с предыдущей записи прошло более двух лет, то вот сводка последних мыслей на эту тему:
ToHistPlot()
(на самом верхнем уровне), и тогда
добавление/удаление каналов с этим "встраиваемым" самописцем будет
устроено точно так же, как и с обычным окном Shift+MB3.
Правда,
Chl_E_ToHistPlot()
, в отличие
Chl_E_*Alarm_m()
и всех методов в CXv4, НЕ пытается найти
"самый верхний определенный метод по иерархии ручек/элементов", а
делает всё сам.
Но и это решаемо, довольно просто, и вот как:
И вот после всех этих типа-сильно-умных соображений вылазит еще одна, кристалльно-простая и очевидная мысль: а может, вставить реализацию встраиваемого-histplot'а прямо в обычный Chl_histplot.c? И тогда:
А отличаться будет только "обвязка" -- создание оформления и реакция на всякие resize.
25.06.2009: P.S. Кстати, а ведь если эту штуку сделать, то будет в точности то, что было в старом-старом cmapp (или mapp) -- та программа для запяткинского клистрона.
30.07.2009: да, сварганил именно так -- с
реализацией LOGD_HISTPLOT
прямо в Chl_histplot.c.
Детали:
CreateHistPlotContent()
, в ThisDialog()
же
осталось только создание оформления (окна, формы, кнопки [Ok], ...).
AddToHistPlot()
.
rec->box
-- при
==NULL
считается, что график встроен, и НЕ делается
попыток появлять/скрывать окно.
ChlFindKnob()
,
так что халявные короткие ссылки .ИМЯ поддерживаются).
Из недоделанностей/недостатков:
25.08.2011: пытался разбираться -- непонятно... Есть
подозрения, что это может быть а) результат разного вычисления Y
-- в одном случае зеркаленье прямо в RESCALE_VALUE(), а в другом --
отдельное вычитание; не оправдалось; б) погрешности округления в
RESCALE_VALUE()
-- хбз, как проверять.
25.08.2011: неа, НЕ надо. Более актуально сделанное сейчас разнесение подписей к разным каналам налево/направо.
31.07.2009: продолжение:
CreateHistPlotContent()
добавлен параметр
flags
, флажки имеют префикс CHPC_
.
19.04.2012: в связи с переходом от "wide" к
"mode" ключики также переделаны -- режим определяется lookup-параметром
mode (значения -- line/wide/dots);
отключение -- nomode; флаг -- CHPC_NO_MODE_SWCH
.
Поскольку упомянута эта проблема была именно тут, и первые попытки с ней разобраться тоже были тут, то здесь и продолжим разборки, только уже в отдельном пункте.
17.12.2018: итак:
RESCALE_VALUE()
всё корректно, а проблемы в её использовании.
20.01.2019: косяк был в histplot.c.
30.12.2018: по некоторому размышлению, есть трое "подозреваемых", почему имеем это расхождение в 1 пиксел при округлении:
RESCALE_VALUE()
, а
для осей/сетки -- сначала считаются, и потом делается отражение.
Краткий анализ:
Но просмотр кода вроде бы опровергает эту версию: там идут 2 подряд варианта вычисления --
y = grf_h-1 - RESCALE_VALUE(i, 0, v_tick_segs, 0, grf_h-1);
(т.е., с "отдельным отражением потом") и
y = RESCALE_VALUE(i, 0, v_tick_segs, grf_h-1, 0);
(т.е., с отражением, совмещённым с перемасштабированием); результирующий
является второе, но, как показывает отладочная печать, они дают одни и те же
числа.
RESCALE_VALUE()
устроена таким образом, что деление является последней операцией, а
следовательно, отбрасывание будет выполняться в тот же момент.
Получасом позже: а вот и да, именно оно!!!
Авотфиг! Ибо:
Т.е., будет только усложнение, но не будет результата.
И калькуляция координат для сетки прямо в "номерах" тут как раз к месту.
Продолжаем думать и тестировать...
Выходит, что оно округляется ВВЕРХ?!
RESCALE_VALUE()
значение
сначала переделываем из i
в (double)i
.
...и-и-и -- да!!! В результате получаем именно 299, 199, 99!
Ещё немного материала для размышления:
y = RESCALE_VALUE((double)i, 0, v_tick_segs, grf_h-1, 0);
y = grf_h-1 - RESCALE_VALUE((double)i, 0, v_tick_segs, 0, grf_h-1);
-- дала тот же результат.
А если отделить зеркаленье -- выполнять его "позже", уже ПОСЛЕ вычислений и приведения к целым?
Делаем --
y = grf_h-1 - (int)(RESCALE_VALUE((double)i, 0, v_tick_segs, 0, grf_h-1));
-- voila, получаем опять 300,200,100.
RESCALE_VALUE()
(печатать части вычислений), чтобы понять,
где/когда/как/что там получается и что отбрасывается.
i
на (double)i
; но так не хотелось бы...)
31.12.2018@дома: ещё можно твикнуть вычисления не осей, а ГРАФИКОВ: разделить скейлинг и отражение, чтобы СНАЧАЛА вычислялись координаты в пикселах, и только ПОТОМ делалось бы отражение по вертикали. Смысл в том, что тогда округление (при отбрасывании дробной части) будет производиться "вниз" (для положительных -- в сторону нуля), так же, как и при расчёте координат осей.
03.01.2019: пробуем.
Да -- тоже эффект есть!
Итак, у нас теперь есть уже 2 гарантированно работающих способа решения проблемы:
double
.
Но надо б поразбираться, чтобы понять, где же происходит перескок на 1 пиксел, и постараться найти ещё 3-й вариант.
Часом позже:
(double)
и без него -- образуется в момент
ДЕЛЕНИЯ.
i=4 v_tick_segs=8 grf_h-1=399
что даёт:
y = grf_h-1 - (int)(RESCALE_VALUE((double)i, 0, v_tick_segs, 0, grf_h-1));
даёт
399 - (int)(4. * 399 / 8 + 0) = 399 - (int)(1596. / 8) = 399 - (int)(199.5) = 399 - 199 = 200
y = grf_h-1 - RESCALE_VALUE(i, 0, v_tick_segs, 0, grf_h-1);
получаем
399 - (4 * 399 / 8 + 0) = 399 - (1596 / 8) = 199 = 200
Т.е., результат ОДИНАКОВ.
(double)
, т.е., кода
y = RESCALE_VALUE((double)i, 0, v_tick_segs, grf_h-1, 0);
результат
4. * -399 / 8 + 399 = -1596. / 8 + 399 = -199.5 + 399 = 199.5
приводящий к 199.
y = RESCALE_VALUE(i, 0, v_tick_segs, grf_h-1, 0);
имеем
4 * -399 / 8 + 399 = -1596 / 8 + 399 = -199 + 399 = 200
Надоела уже эта маета до чёртиков -- делаем!
y = grf_h-1 - RESCALE_VALUE(i, 0, v_tick_segs, 0, grf_h-1);
В т.ч. и в отрисовке axis -- DrawAxis()
и
DrawPlotAxis()
, где УЖЕ было именно так, т.к. там отражение
делается с учётом m_top
(margin top -- смещения виджета
"график" внутри виджета "axis").
y = grf_h-1 - (int)(RESCALE_VALUE(v, mindisp, maxdisp, 0, grf_h - 1));
В нем -- пока что -- будут два вида функциональности: взаимодействие с пользователем (диалог "сделать запись...") и собственно функция отправки.
13.09.2007: добавлена пиктограмма btn_elog.xpm -- перышко с подписью "e-log" (выглядит, надо сказать, мерзковато).
02.07.2009: поскольку оно так пока и не
используется (да и не работает), то строку cmChlDoElog
в
Chl_stdtoolbar.c::stdtoolslist[]
закомментировал.
12.05.2012@Снежинск-каземат-11: да, делаем -- понадобился для под-окошка liubpms в db_liu.h.
ELEM_ROWCOL=16
.
ROWCOL_START()
.
XmPACK_COLUMN
.
ncols
поддерживается, отображаясь на количество
колонок (XmNnumColumns
).
Теперь о грустном: изменение размера (путём тягания за границу окна) оно поддерживает, но:
На будущее -- мож, захочется мочь указывать интервал между клетками (к
сожалению, это ОДИН параметр XmNspacing
).
И уж точно будет желание разобраться с ресайзингом (хотя как, и возможно ли в принципе -- непонятно; ask Гусси? Мне-то казалось, что он умеет ресайзить всё пропорционально...).
Пока же --
12.05.2012@Снежинск-каземат-11: нашел: XmColumn, а не XmRowColumn!!! Одноколоночный, и пропорциональность поддерживает при XmNstretchable=True. Одна проблема: утверждается, что XmNstretchable -- только CG, без S, так что не удастся созданному-в-другом-месте виджету уставить stretchable...
12.05.2012@Снежинск-Снежинка-305-душ-вечер: а ведь ВСЁ желаемое (растягивание "поперёк" по максимальному, пропорциональное растягивание, ...) должен уметь виджет-сетка XmSepGridLayout. Нынешняя неумеющесть -- просто результат бага, причём наверняка не супер-сложного (некорректный обсчёт при fill). Ну что, потратить несколько дней на его исправление? Тогда можно будет Chl_rowcol уволить нафиг...
07.01.2011: ниже приведён протокол записей и идей, стоящих за этой штукой.
Началось всё с потребности "по простому" сделать запись одного и того же в кучу ручек с примерно одинаковыми именами (по некоему шаблону) -- например, для одновременного включения всех модуляторов. Потом же вылезло, что может понадобиться выполнять ЭТО в несколько этапов, с паузами.
Итак:
22.12.2010@Снежинск-каземат-11: fastadc/globact_knobplugin.c: сделал такую штуку a-la ACTIONKNOB: -- выглядит как кнопка, а выполняемое действие -- уставляет значения в кучу ручек, указанную в widdepinfo.
- формат -- "ИМЯ1=ЗНАЧЕНИЕ1 {ИМЯN=ЗНАЧЕНИЕN}
- в именах могут использоваться группы, как в shell'е -- {ALT1,ALT2,...}, групп может быть много -- получается как бы "перемножение".
- реализовано -- рекуррентно, функцией
DoExpandAndSet()
.А ведь если эту технологию продвинуть, и сделать возможность (уже в v4?) заготавливать целые сценарии -- что-то записать, подождать, еще что-то...
Единственное что -- это то ли кореллирует, то ли интерферирует с функционалом формул, и, так же, как и формулы, в принципе может быть скрещено с seqexecauto.
23.12.2010@Снежинск-каземат-11: сегодня вылезла уже конкретная потребность в "сценариях" с групповыми командами: для плавного включения модуляторов (включить все *-A, через 2 секунды все *-B, ...).
СЕЙЧАС "сценарии" придётся вешать на какой-нить knobplugin (видимо, сделаем в дополнение к GLOBACT'у) -- чтоб он брал программу-сценарий из widdepinfo, и прямо там можно б было писАть команды типа "pause 2", да еще и возможность тормозить сценарии нужна... Ох...
Понятно, что нынешние GLOBACT и этот "исполнитель сценариев" надо будет переселить в Chl, для общей доступности.
А надо будет разработать именно ОБЩИЙ механизм.
23.12.2010@Снежинск-каземат-11: план реализации более общего компонента в Chl:
- Видимо, достаточно будет ОДНОГО knobplugin'а -- ведь простая групповая уставка -- это сценарий из одного шага.
- Вместо нынешнего сплошного потока делаем "набор команд, разделяемых пробелами".
- Одна из команд -- "sleep(seconds)"
- Еще "команда" -- которая интерпретируется только при начальной загрузке/инициализации -- это требование сделать справа от этой кнопки кнопку "stop".
Или, может -- как раз сделать общую "команду" style, в которой указывать PSP-строку, а уж в ней -- и create-stop, и цвет, и прочее...
- Естественно, на время исполнения кнопку надо бы disable'ить -- чтоб второй раз не нажали (хотя всё равно надо во время исполнения не отрабатывать activate).
Неа -- лучше пусть она "загорается" (как лампочка) на время исполнения.
- Еще надо бы иметь возможность использовать -- как для записи, так и в качестве аргумента sleep -- не только константы, но и значения регистров. Цель -- чтоб язык сценариев был настраивабелен at-run-time.
- Еще надо (БЫ!) там же иметь команду "прочитать ручку в регистр" -- чтоб было подобие обратной связи.
- Но проблема в том, что наверняка захочется мочь и вычисления какие-то произвести... А сейчас -- никак, вот с языком формул всё было бы как надо...
- Возможный обходной путь: создавать "невидимые" ручки, исключительно как "обработчики входных значений", и тогда можно будет что-то считать, потом "уставить" в эту ручку, а она уже результат своей деятельности положит в регистр -- который доступен дальше, и уж с ним можно делать что угодно.
- Естественно -- в такой кривой архитектуре нужна будет команда пересылки между регистрами.
- Параллельная идея: надо сделать специальный тип элемента: "невидимый", который генерит виджет 1*1 пиксел, а содержимое не создаёт -- для хостенья невидимых ручек-вычислителей. Тогда подобные невидимости можно будет использовать и для маппирования scalar-alias'ов параметров больших каналов в доступное для исполнителя пространство. 23.12.2010: сделан, ELEM_INVISIBLE.
Замечание: идея "scenario" выглядит почти величайшим изобретением со времён seqexecauto (хотя и далеко не столь красивым и однозначным -- а скорее слегка уродливоватым).
24.12.2010@Снежинск-каземат-11: еще:
- А ведь достаточно ввести в scenario команду load_mode -- и actionknobs станет избыточной.
- Вообще, вопрос на засыпку: а точно ли "язык сценариев" пересекается по назначению с формулами? Ведь формулы -- они локальны для ручки, а сценарии -- имеют возможности адресации к другим ручкам.
Да, в сценариях надо бы иметь возможность формульных вычислений, но вообще-то сценарии -- НАДМНОЖЕСТВО формул.
Или, по-другому: формулы работают на уровне cda, а сценарии -- на уровне Cdr.
25.12.2010@Снежинск-Снежинка-утро-перед-отъездом: проблему "в как сделать в v2 - LOGD_READ/WRITE или NOP" решить просто: надо отделить исполнитель от ручки, и тогда можно реализовать хоть оба варианта.
А в v4 надо его делать совсем отдельной сущностью - именно в Cdr'е.
- Дерганье юзером - как угодно.
- Вот как его с формулами скрещивать?
25.12.2010@поезд:
- Идея: а можно ведь сделать язык сценариев подвидом/надмножеством языка формул - чтоб "вызывальщик" регистрировал "расширение", которое и вызывалось бы из сценария по команде set-knob-value. Проблема в другом - чтоб можно было из формул указывать на необходимость временно приостановить исполнение по команде sleep.
В любом случае, идеальный вариант - реализовать исполнителя формул через "контекст", который можно тормознуть в любой момент (как сделано в sea). Тогда можно будет реализовывать параллельное исполнение, сопрограммы и т.п.
- Вопрос только - в способах взаимодействия сопрограмм, ведь легко только вызывать другие формулы-сценарии "с нуля", а вот реализовывать listen/accept, или точки входа, или рандеву - едва ли оправданно.
- А вообще, чем умничать, пытаясь сразу придумать идеальный вариант, надо просто сделать первую версию в v2 (для liucc, наверняка напросятся и другие применения, типа сварки), и по опыту её эксплуатации многое станет яснее.
- Замечание: а ведь "сценарии" позволили бы просто и естественно выполнять много вещей, которые на формулах делаются, но извратно - типа ресета источникам.
28.12.2010@душ-дома: а ведь реально это не "сценарии", а "скриптинг" - и получится скриптинг, встроенный в DM - чего у epics'оидов вроде пока нету.
Очевидно, что система скрптинга должна быть независимой от GUI.
02.01.2011: ...а можно и прямо к v4-формулам подселить возможность уставки в соседние ручки - "внешними" командами, коие всё равно будут.
Только как бы при этом формулы сделать сценариями - хбз, ибо они непрерывны, а понадобится иметь "контекст" и возможность "прерывания" (отдачи управления), "продолжать", "тормозить", а еще вопрос, что делать при попытке повторно запустить уже исполняющуюся формулу...
10.01.2011: как вариант "взаимодействия": а если ввести правило, что запись 1 в ручки-сценарии -- это запуск, а 0 -- стоп?
10.01.2011: ага, ну просто замечательно -- а КТО будет ловить эти 1/0?! Или предполагается, что будет вызываться метод SetValue_m()?
12.02.2011@Снежинск-каземат-11: КТО -- тривиально: если в v4 сценарии будут интегрированы с формулами, то сами эти "сценарии-формулы" и будут получать на вход параметр со значением 1 или 0. А задачей "ручки-сценария" будет сделать 2 кнопки, генерящие эти значения (кстати, CHOICEBS тут отлично подойдёт просто автоматом).
РЕАЛЬНОЙ ПРОБЛЕМОЙ станет только то, как "рестартовать" вроде бы уже исполняющийся сценарий. Или сделать сценарии как бы "отдельной" сущностью (3-й тип в cda -- после каналов и формул), которая и будет разбирать эти 0/1, правильно их обрабатывая либо игнорируя (при остановленности/запущенности); а на чтение она могла бы отдавать "статус исполнения" -- 0 или 1. Реализовать такое очень просто и вроде даже элегантно; вопрос только, чем аукнутся вводимые при этом ограничения. 21.01.2011: кстати, а ведь давным давно, в 1990-х, еще в первой версии, поддерживавшей формулы, БЫЛА первоначально мысль иметь в формулах команды доступа к соседним logchanbin_t. Но потом стало ясно, что это функционал совсем другого уровня, а не cda.
А теперь вот -- новый виток развития :-)
22.01.2011: стыд и позор -- наткнулся на старющий, начатый 26-01-2007, раздел "О некоем скриптовом языке для автоматизации однообразных циклических и последовательных процессов управления". Ведь там описаны многие релевантные вещи... 08.09.2013: да, всё дальнейшее обсуждение будет там.
09.02.2011@Снежинск-каземат-11: а ведь в v4 сценарии удобно бы мочь исполнять при "инициализации" -- как планируется иметь формулы-инициализаторы (пока, кстати, еще не сделанные!!! ключевое слово --
at_init
), так и сценарии были б полезны.Это еще один аргумент за сращивание сценариев и формул в некий "скриптовый язык СУ".
13.02.2011@Снежинск-утро-в-машине-на-полигон: а ведь можно даже схалявить -- просто регистрировать язык скриптинга как дополнительный "плагин-тип" формул, например -- #!script (это и получился бы "3-й тип в cda").
Но это всё-таки плохая идея -- т.к. в скриптах нужны и все возможности формул по вычислениям и добыче данных из каналов.
Так что правильный вариант -- это ФОРМУЛЫ наделять возможностью "полу-возврата", чтоб можно было реализовывать sleep и прочие приколы. Например, сделать, что она возвращает слово флагов, где один бит -- это REFRESH, другой -- RUNNING, etc.
Технический вопрос лишь в том, как поступать при попытке запуска уже исполняющейся формулы-сценария, чтобы мочь корректно отлавливать те run=1/stop=0.
Уф, вроде -- всё.
23.12.2010@Снежинск-каземат-11: начинаем:
LOGD_SCENARIO=5001
16.01.2011: не надо никаких «"команд" style» (23-12-2010). Есть вариант проще и элегантнее:
парсить widdepinfo обычным psp -- просто сделав плагин, дёргаемый по ключевому слову scenario=, и съедающий всё до '\0' -- он при этом будет делать проверку, а в "своё" поле складывать указатель на начало сценария.
Тогда не потребуется никаких извратов типа "доп.команда style" -- всё будет аналогично обычным ручкам.
Замечание: конечно, тут мы пользуемся тем фактом, что widdepinfo (options) существует не только в момент создания ручки, но и всё время её жизни. Формально это вроде нигде не постулировалось, но по факту именно так. И едва ли когда изменится.
27.10.2011@Снежинск-каземат-11: снова наткнулся на эту "особенность", и провёл отдельное расследование, закончившееся тут. Отрадно, конечно, что мы уверены в корректности такого поведения, но всё же это очень некрасиво. (А наткнулся при исследовании, как можно б использовать Cdr_script в liu/plot/key_offon_knobplugin.c.)
19.01.2011: для того, чтобы не использовать в C??_script.[ch] Xt'шный API таймаутов (как изначально планировалось), будем делать как в tsycamlib'е: если код возврата >0, то это время, которое надлежит "подождать" перед вызовом "продолжения".
Скорее всего, так и надо будет оставить и в дальнейшем -- даже на cxscheduler не затачиваться.
19.01.2011: а ведь реально сей механизм скриптинга ВООБЩЕ НИКАК не привязан ни к Chl, ни к Cdr. Это именно просто "script engine" общего назначения, и жить он должен рядышком с seqexecauto.
Назовём его "Lightweight Script Engine", файлы --
lightscript_engine.[ch], префикс --
lse_
/LSE_
.
А конкретно Cdr-script (со специфичными командами типа set), возможно, будет реализовываться специальным Cdr-модулем -- как раз напрашивается Cdr_script.
19.01.2011: P.S. забавненько -- из мелкостенкиного запроса, первоначально родившего globact, появляются аж 4 сущности -- Chl_scenario плюс "мясо" его функционала fqkn, lse, Cdr_script.
21.01.2011: предварительный вариант сделан -- недурненько, пашет. Теперь надо б под-украсивить:
lse_stop()
и гасить
подсветку.
Для начала надо б хоть просто stderr-диагностику сделать
А потом -- думать о чём-то lasterr-подобном.
25.01.2011: да, сделано и подсвечивание на время исполнения, и гашение+торможение по ошибкам и окончанию.
Также добавлено парсенье декораций и их использование -- скопировано
из button_widget. Поскольку кнопок двое (start+stop), то декорирование
вытащено в отдельную функцию TuneButton()
; она прям
напрашивается на унос в KnobsI.h+Knobs_internals.c.
На данный момент заявленные цели достигнуты, так что "done".
Причина -- чтоб можно было такие кнопки помещать в ELEM_SUBMENU.
Нужен как контейнер для ручек, требующихся в качестве только либо обработчиков, либо целей для чтения/записи сценариями и прочими частями программы, но должных быть недоступными для пользователя.
23.12.2010@Снежинск-каземат-11: Делаем:
ELEM_INVISIBLE=13
.
Chl_E_SetPhysValue_m
-- он необходим для отработки
ручек-писателей, все прочие методы NULL.
Осталось проверить.
Замечание: а не будет ли эта штука де-факто частичным ответом на вопрос 01.06.2005:"не потребуются ли нам "невидимые объекты" -- как в LabView таймеры"?...
21.04.2011@Снежинск-каземат-11: Замечание по "мозгам":
Впрочем, нынешние сценарии никакого внешнего управления и не поддерживают -- токмо юзерское.
14.12.2011@Снежинск-каземат-11: проверил. Имелся баг -- оно
забывало проставить e->emlink=&invisible_emethods
, так
что реально оно как бы и НЕ работало (из сценария key в liucc -- просто
физически не проходило уставление значения). Пофиксил, теперь работает, так
что можно считать за "done" (но в этом раздельчике его ставить некуда
:-)).
И особенно полезной эта штука будет при возможности располагать часть ручек в заголовке.
(Да, конечно, засовывать в подменю можно будет только то, что понимается Motif'ом -- кнопчатости, метки и alarmonoffled'ы, но тем не менее.)
20.04.2011@Снежинск-каземат-11: Делаем:
ELEM_SUBMENU=14
.
Ну и мерзко ж в Motif'е организована работа с меню!!!
21.04.2011@Снежинск-каземат-11: продолжаем:
Так и сделали -- параметром.
ncols
используется: при
ncols>1 делается многоколоночное меню (уставляется XmNnumColumns=ncols,
XmNpacking=XmPACK_COLUMN).
Естественно, ни nflrs, ни nattl не поддерживаются :-).
22.04.2011@Снежинск-каземат-11: заканчиваем:
Как бы то ни было -- оно вполне работоспособно, так что "done".
27.09.2011@Снежинск-каземат-11: слабокореллирующесть определялась в первую очередь тем, что у виджета стояло имя "submenu_btn".
29.09.2011@Снежинск-каземат-11: попробовал улучшить внешний вид -- добавлять справа треугольничек через XmNcascadePixmap.
Нарисовал треугольничек -- submenu_triangle.xpm, вставил создание пиктограммы (с упрозрачниванием) и уставление её виджету. А вот хрен -- почему-то не работает, просто не появляется...
Отличия в том, что уберём нафиг "дисэйбленье" веток, зато добавим сворачиваемость.
28.08.2013: аллокирован
ELEM_OUTLINE
, создан файл, сделана начальная набивка.
Из-за Motif bug #1234, он же Bug 74502@RH-7.3, исправленный не вполне ясно в какой версии, пришлось завести "внутреннюю" директорию Xm/ с XmStrDefsI.h внутри.
29.08.2013: идеологически всё совсем не тривиально. Сделать сразу, в лоб, правильный/идеальный элемент -- хбз как.
Но для rfsyn'а такое не подходит -- там и корни веток тоже должны быть листиками, представляющими поля для ввода задержек.
.placement
у чайлдов
указывать, что вот данный -- это начало новой ветки (и как --
fixed/foldable/folded), а вот этот -- последний в вышестоящей.
Но тут мало того, что "указания" будут множественными (корень ветки может одновременно быть последним у родителя); так еще и просто такая структура тоже кривовата -- чисто идеологически, и просто неудобно у ЧАЙЛДОВ указывать последнесть (вот если б маркером каким-то отдельным, типа "}", но то уже как раз обычная структура вложенностей).
Ну и что делать?... Воспользоваться подходом "ждание преполняет"?
В Motif оное есть, только б понять, кого использовать -- XmPaned или XmPanedWindow, и в чем вообще между ними различие?
23.10.2014: приступаем.
ELEM_SPLIT
= 18.
You may use Paned widget instead. It has almost the same functionality as PanedWindow. PanedWindow is deprecated as duplicating widget of Paned.
Так что останавливаемся на XmPaned.
23.10.2014: оформляем:
24.10.2014:
ELEM_SCROLL
= 19.
Проще пока не заморачиваться (Motif славен своими глюками на тему geometry management), а дождаться реального примееения и тогда добить.
Frame is most often used to enclose other managers when the application developer wants the manager to have the same border appearance as the primitive widgets. Frame can also be used to enclose primitive widgets that do not support the same type of border drawing. This gives visual consistency when you develop applications using diverse widget sets.
Проверить -- это не то ли самое, случаем, что требуется нам для Dial widget'а?
15.02.2004: ну, проверил -- естественно, не то. Кто б сомневался...
Вывод: поручить Саше Антонову провентилировать возможность сделать "специальный" подвид XmDrawingArea/XmForm -- чтоб оно рисовало прямоугольник выбранности. Либо, как вариант, выяснить, что именно надо сделать, чтобы навесить такую функциональность на произвольный экземпляр subclass'а XmManager'а.
12.08.2007: действительно -- давно напрашивается решение: сделать собственный виджет-subclass XmDrawingArea, который бы предоставлял XmNfocusCallback и XmNlosingFocusCallback. И не маяться со всей той алхимией с ручной ловлей фокуса.
И, кстати, можно даже не от DrawingArea -- чтобы не решать проблему "менеджеры не фокусируются", а сразу от Primitive. Там тоже не сахар, конечно -- какое-то шаманство с методом focusChange в некоем BaseClassRec, но все же реализовабельно.
01.07.2008: только не Primitive, а XmManager -- см. на эту тему раздел по XmFocusAwareManager.
ExtractDoubleValue()
появилась и вне
lib/Knobs. Посему вытащил ее в Xh, переименовав в
XhTextExtractDoubleValue()
.
Заодно (для единообразия) переименовал также
XhSetTextCursorCallback()
в
XhTextSetCursorCallback()
.
25.01.2004: заодно добавил в
ExtractDoubleValue()
, что при ошибке оно "фокусирует"
виджет-текст.
Некрасиво! (Хотя, с другой стороны -- оно показывает, что "эту кнопку только что нажали".) В принципе, конечно, не смертельно, но -- именно некрасиво. Если можно пофиксить -- надо фиксить.
Вопрос -- как фиксить? Пока в голову приходит идея, что так же, как
делается "унифицированная бибикалка (нынешний LOGD_ALARM)". Т.е.,
ввести в knobinfo_t
дополнительное поле, например,
"i_m_a_button
", в которое
Knobs_button_widget::create_m()
будет прописывать
True, а колоризатор в Cdr тогда станет рассматривать кнопки как
readonly-каналы и на предмет "otherop" игнорировать.
23.08.2004: проблема решена в рамках
knobinfo_t.behaviour
-- флаг
KNOB_B_IS_BUTTON
.
knobinfo_t
.
Резон -- есть уже вторая программа, в которой требуются элементы
управления с функциональностью Knobs, никак не привязанные к Chl
(программа -- istcc.c
, первой была
sukhphase.c
), но возиться с созданием инфраструктуры,
требуемой libKnobs, нет никакого времени.
18.04.2004: проект решения: делаем файл Knobs_simple.c, с функцией типа
CreateSimpleKnob(??Knob?? k, XhWidget parent, int look, const char *params, SOME-CALLBACK_INFO)
В params
будет передаваться информация, которая обычно
идет в полях knobinfo_t
, и передаваться будет в формате
"param=value {param=value}", т.е., например, "minval=1
maxval=10".
Блин, самое фиговое -- понадобится неслабый парсер, с возможностью кавыченья строк, а в строках -- кавыченья кавычек. Впрочем, нечто подобное надо будет иметь для парсинга auxinfo. Кроме того, насчет кавычек: где-то такой парсер уже делался, много лет назад... Ага, нашел -- в ucam/mkdb.pl (скрипт, компилировавший .uds-файлы; первая найденная версия с таким интеллектом -- от 09.10.1997), он еще очень творчески искал начала комментариев с "#", чтобы оне были вне кавычек.
Окей -- вначале склепаю парсер для auxinfo, а там, приобретя опыт, посмотрим.
19.04.2004: вышеописанная потребность послужила "предпоследней каплей" -- будет библиотечный модуль paramstr_parser.[ch], который сделает нам счастье.
01.05.2004: понадобилось в
knobinfo_t
добавить пару user-private-pointer'ов --
privptr1
и privptr2
. Они нужны, чтобы
хранить callback и usrptr для simple-knob'ов.
01.06.2004: итого -- за прошедшее время
CreateSimpleKnob()
уже сделан, в таком варианте:
Поскольку наконец-то заработалCreateSimpleKnob(const char *spec, XhWidget parent, Knob *k_p, simpleknob_cb_t cb, void *usrptr)
psp_parse()
, удалось
проверить. Работает!
02.08.2004: обнаружил, что в описании параметров
для CreateSimpleKnob()
отсутствовали типы (look) ARROW_NN.
Видимо, потому, что они были реализованы ПОСЛЕ (легко можно проверить
по датам).
Добавлены -- в psp_looks[]
.
02.08.2004: BTW, не
понимаю: почему CreateSimpleKnob()
возвращает
int=0/-1, и имеет Knob *k_p
, вместо того, чтобы
возвращать как раз Knob либо NULL при ошибке? И вообще, почему там
вместо толковой обработки ошибок просто стоят "/*!!!*/"?
05.10.2004: перевел
CreateSimpleKnob()
на возврат самого свежеиспеченного
Knob
'а вместо int=0/-1, а параметр r_p
,
соответственно убрал. В настоящее время был только один клиент --
nipp/ipp.c
, в котором все легко подправлено. Файл
doc/lib/Knobs.html также подправлен.
Заодно сделана и обработка ошибок -- проверяется и результат
malloc()
, и результат CreateKnob()
'а.
eleminfo_t
.
06.05.2004: понадобилось добавить некоторое количество widget-полей (для "щелканья" по заголовкам элемента).
06.05.2004: заодно обнаружил некое поле
"widget_private
". Пока что оно как-то совсем не
требовалось, так что поменял его имя на "zzzzz...".
06.05.2004: и еще заодно обнаружил, что поле
container
определялось как XhWidget*
вместо
просто XhWidget
. Исправил.
06.05.2004: выкинул этот
eleminfo_t.[zzz_]widget_private
к черту. Реально
понадобится -- вставим.
30.06.2004: вчера обсуждал с Гусевым проблему показа статусов ошибок на графических элементах в его программах. Он пришел к той же схеме, что и я: при возникновении "блокировок" можно нажать на кнопку-label, появится окошко с расшифровкой (набор лампочек-чекбоксов, нужные -- подсвечиваются) и кнопкой [Reset]. (Поскольку я ему показывал свою реализацию, то, возможно, его вариант навеян моим.)
Я ему предложил (хи-хи! сможет он это сейчас сделать, как же!) альтернативный, более "легкий" для пользователя вариант: что расшифровка показывается в tooltip'е при наведении мыши на кнопку, а [reset] делается сразу её нажатием, безо всяких дополнительных окон.
Но это-то ладно, а сегодня пришла в голову мысль, как Я
могу сделать аналогичное "легкое" для пользователя отображение
расшифровки rflags: у нас ведь кроме tooltip'ов предусмотрен
statusline, и еще с незапамятных времен (1997/1998гг.) имеется
специальный интерфейс XhMakeTempMessage()
(как раз для
tooltip'оподобных целей -- hint'ы для пунктов меню). Так вот: а
что, если при EnterNotify
смотреть, что если
ki->colstate==COLALARM_HWERR
(или как корректнее
определять "побордовелость"?), то сваливать в некую строчку список
"кратких" имен имеющихся проблем (*_strrflag_short()
) и
делать ею XhMakeTempMessage()
.
С этой идеей есть две проблемы:
LeaveNotify
?) и при исчезновении проблемы (а
вот это чуть сложнее -- вопрос, куда вставить проверку, "на что
повеситься"). Но это-то вопрос технический.
Отсюда вывод: а на Knobs ли это вешать? Или -- лучше на Chl (в
сразу после CreateKnob()
)? Это тоже имеет свои
"интерфейсные" проблемы (в какой момент делать
AddEventHandler()
-- колоризация-то и HookPropsWindow()
сейчас делаются именно в Knobs), но хотя бы "ложится в струю".
22.11.2004: вот-вот -- а Гусев-то воспользовался
данным советом! В программе "Storage Ring Power Supply Control" я сегодня
обнаружил такую фичу: когда некий "канал" получает событие
EnterNotify
, то в statusline отображается "статистика" по этому
каналу. Правда, она там так и остается висеть навечно, пока не
перепропишут.
07.07.2006: ну что ж -- теперь и у нас подобная фича используется в vacclient'е. Технология -- очевидная:
EnterNotify
помещаем в некую статическую переменную
(должна бы она быть per-window) указатель на соответствующий
knob, и вызываем функцию DisplayBarStats
-- "отобрази
статистику через XhMakeTempMessage()
".
LeaveNotify
уставляется строка "",
восстанавливающая основное сообщение.
NewDataArrived()
вызывается та же
DisplayBarStats
(если в статической переменной NULL -- то
просто ничего не делает).
07.11.2004: сделал -- теперь в
HookPropsWindow()
вместе с правой кнопкой мыши
перехватывается и клавиатура. Причем -- обрабатываются ВСЕ осмысленные
комбинации кнопок: Alt+Enter, Alt+KP_Enter и Shift+F10 вызывают окошко
"Properties", а Ctrl+ПРОБЕЛ -- окошко "Big value".
Кстати, корректности ради вставил
*continue_to_dispatch=False;
и в
TextUpDownHandler()
.
И пошел дальше: у нас еще давным-давно, в
oldcx/Documentation/TextInputIdeology.txt, было прописано, что
по кнопке <Esc> должна делаться отмена ввода, произведенного
пользователем в текстовое поле. Вот это и сделано -- добавлено в
TextUpDownHandler()
. Теперь, если нажимается <Esc>,
БЕЗ модификаторов (Shift, Ctrl, Alt), и поле находится в состоянии
"редактируется пользователем", то делается
CancelInputEditing()
, иначе -- даже событие не съедается.
Еще замечание на будущее: а как оно у нас будет реагировать на стрелки с keypad'а, а?
07.12.2004: проверил, на FC3 -- ну никак и не реагирует :-)
05.01.2005: вставил поддержку и keypad'а --
просто дополнительные проверки. Заодно добавил их и в
Knobs_dial_widget.c::ArrowsHandler()
.
05.01.2005: кстати, обнаружил еще тогда же, в декабре, что они не работают вообще в Motif'е, и даже написал на эту тему bugreport 142116.
А сегодня изготовил патч для той проблемы -- добавку к определениям виртуальных keysym'ов osfNNN в lib/Xm/Transltns.c.
12.01.2005: ой бли-и-и-иииииин.........
Посмотрел я, попробовал в FC3 и поддержку KP_{Up,Down}
в
Knobs_internals, и просто пропатченные Motif'овские программы.
Коротко -- полный зад!!!
Более длинно -- почему-то в Motif'е ВСЕГДА доставляются keysym'ы KP_{Arrows}, без разницы, включен ли NumLock или нет. А в цифры он уж, похоже, сам транслирует. Частенько подглючивая. И -- поведение программы зависит от того, был ли включен NumLock в момент ее запуска.
24.01.2005: Bingo!
Полез в xc/programs/xev/xev.c -- оказывается,
он пользуется XLookupString()
, а вовсе не
XKeycodeToKeysym()
(которому я неясно из каких
соображений передаю параметр index=0).
А еще обнаружилась некая функция XtTranslateKeycode()
.
Но она с какого-то бодуна добавляет к не-keypad-стрелкам 0x10040000.
Часом позже: ааааа!!! Нашел!!! Этот префикс -- от osf-keysym'ов,
имеющих имена типа osfXK_NNN
. Они содержатся в
<Xm/VirtKeys.h>
.
Так что -- можно преспокойненько переходить на
XtTranslateKeycode()
, и проверять на ТРИ keysym-кода --
обычный XK_NNN, keypad'ный XK_KP_NNN, и osf'ный osfXK_NNN.
По-хорошему, надо оставить ТОЛЬКО osf-keysym'ы, но фиг знает, когда же
в Motif'е реально появятся virtual bindings для KP_-кодов.
Несколькими минутами позже -- все сделал, оно работает. Сравнение идет со всеми тремя вариантами :-) УРААА!!! Keypad-стрелки работают!!!
Так что -- теперь можно со спокойной душой разбираться с добавлением KP_-клавиш к стандартным virtual bindings и писать bugreport, если что-то будет глючить.
КСТАТИ: а ведь
трансляция в KbdHandler()
'е (где F10, пробел, etc.) также
сделана некорректно -- ее б тоже перевести.
Получасом позже: перевел. Ну и if
'чик же там получился
-- о-е-ей!!! Вывод -- оно дублирует функциональность, заложенную в
идеологию VirtualBindings, так что по-хорошему каждое действие должно
бы было вызываться ОДНИМ osf-keysym'ом... Но увы! :-)
31.01.2005: а тот патч для
lib/Xm/Transltns.c::_XmVirtKeys_fallbackBindingString[]
по-прежнему толком не работает -- ну глючит что-то в Xt'шном коде,
мухлюющем с NumLock'ом...
21.02.2005: так, полуслучайно, заметил, что Esc
перестал работать как Cancel. Ну так, еще бы -- ведь теперь, после
Xt'шной трансляции, мы получаем не XK_Escape
, а
osfXK_Cancel
. Что ж, вставил и его в if() в качестве
возможной альтернативы.
02.07.2004: добавил функцию
SetKnobValue(k,v)
в Knobs_simple.c (просто
скопировал кусочек из Cdr). Она потребуется только пользователям
simple-knobs, а Cdr, вследствие своей "отвязанности" от реализации
Knobs, всегда будет лазить в методы напрямую.
03.08.2004: сделал. Всего-то --
XhWidget GetKnobWidget(Knob k)
; испытал на
nipp/ipp.c -- работает. И в доке ее описал.
knobinfo_t.indicator
определялось как
"XhWidget *
" вместо "XhWidget
".
23.08.2004: естественно, это была ошибка.
Расследование по {BACKUP,STABLE} показало, что оно вкралось с
момента появления Knobs -- т.е., когда logchanbin_t
превратился в knobinfo_t
(STABLE/cx.20030828_Knobs). Ошибка была совершенно
безопасной, поскольку в любом случае получаем поле-pointer. А не
заметил я проблему, видимо, из-за большого количества warning'ов, плюс,
из-за того, что так и не порасставлял везде, где надо,
CNCRTZE()
/ABSTRZE()
(а в Chl и Knobs этих
макросов и вовсе нету...).
24.08.2004: ввел SetKnobState()
,
просто скопировав из Cdr.c::SetColstate()
. Оно
уже используется в новом ipp.c.
А вот поддержку "otherop" нормальную сделать сложнее -- которая 5 секунд держится -- что теперь, вытаскивать еще море всякого из Cdr в отдельную библиотеку?
knobs_vmt_t
-- PropsChanged, который вызывается при
изменении одного из полей
rmins[]
/rmaxs[]
/incdec_step
.
Ему надо передавать, в дополнение к ki, еще и старый вариант -- типа
"old_ki", чтобы компонент мог обнаружить, что именно изменилось.
05.10.2004: И ЕЩЕ: надо бы
ввести в knobs_vmt_t
еще и метод Destroy
--
на будущее, когда может появиться программа, вычитывающая описания из
БД на лету, и по ним по мере надобности создающая и удаляющая элементы.
05.01.2005: "родил" эту пару методов -- они
теперь присутствуют в knobs_vmt_t
. Им дозволяется быть
просто NULL
-- т.е., ничего не делающими, так что именно
такое тривиальное изменение и проделано со всеми виджетами.
Вставлен даже вызов метода PropsChg()
, и причем даже
только если пользователь реально что-то менял.
Теперь осталось только сделать реально работающие методы там, где они нужны -- для dial'а и slider'а.
19.04.2005: и чего я этим маялся?!?!?! Не буду делать!!! Только когда перейдем на CXv4 -- вот тогда и подумаем, какие вообще методы должны быть у knob-plugin'ов разных типов.
20.05.2005: "не буду"? :-) Прошел ровно месяц, и воспользовался -- теперь текстовое поле этот метод поддерживает, для перещелкивания пометки "загруппировано".
20.07.2006: давным-давно PropsChg() используется, так что -- "done".
Теперь, с введением
elemnet_t/eleminfo_t.options
и
subsysdescr_t.options
, совершенно очевидно, что
logchannet_t/knobinfo_t.widdepinfo
-- тоже по смыслу есть не
что иное, как options
.
Пока что не вижу глубокого смысла переименовывать, но если попадет
шило в одно место -- то это несложно, везде переименовываем, делаем
#define widdepinfo options
, и вводим в
text2knobinfo[]
дублирующую строчку.
16.01.2005: а вот и хренушки!!!
widdepinfo
-- это никакое не options
(в
крайнем случае -- widparams, но не более того). Потому, что имя
"options" стопроцентно уходит под концепцию, которая сейчас условно
именуется "layoutinfo", а реально -- "опции внешнего вида данного
объекта, в основном используемые его parent'ом".
11.04.2005: а вот и фигушки!!! Как раз
оно очень даже options
-- в свете "обобщенных"
подсистем/элементов/ручек, где аналогичные поля служат ровно для того
же, т.е., для указания неких "widget"-dependent параметров. И там же,
рядышком, будет некое поле, содержащее "подсказки" для parent'а.
CreateKnob()
возвращала значение
"ki->indicator!=NULL
", т.е., 1 при успехе, 0 при
обломе. А везде было рассчитано на протокол "0:успех,-1:облом".
Сейчас, когда вставил реальную проверку в
CreateSimpleKnob()
, это вылезло.
05.10.2004: исправлено.
26.10.2004: готово -- сделал в
Knobs_typesP.h константы KNOB_VALUE_LIT_MASK
и
KNOB_VALUE_DISABLED_MASK
. И заменил ими "1" и "2" в
Cdr.c, Knobs_{alarmonoffled,button}_widget.c.
ki
может меняться по ходу дела...
07.11.2004: сделал, теперь значением 0 у нас
обладает именно COLALARM_UNINITIALIZED
. Посмотрим, где
это может вылезти боком -- в принципе, нигде не должно, но если будет
-- то это проявится ошибка, которую надо будет пофиксить.
LOGC_IMPORTANT
,
а LOGC_HILITED
-- нет, он просто имеет фиолетовый
foreground вместо обычного черного. Или это так и правильно, так и
должно быть? Надо бы для себя понять, и, в любом случае, отразить это
в doc/lib/Knobs.html.
07.11.2004: Стал разбираться -- заглянул (в порядке углубления в древность) в BACKUP/, curcx/src/chl/chl_internals.c, oldcx/src/chl/chl_data.c, cmapp/chl_data.c, mapp/uxil_data.c, drc/old_drc.c.
Что обнаружил ("что показали археологические изыскания" :-):
colstate
по-прежнему
мерялось цифрами 0, 1 (yellow) и 2 (red). Выбор цветов там выглядел
весьма запутанно -- условная конструкция с 3-уровневой вложенностью.
И, похоже, уже тогда "яркие" цвета ставились только для
LOGC_IMPORTANT
...
switch(newstate)
для выбора bg, fg же выбирался
отдельно, своим "switch(bi->color)
". А старая
3-уровневость была закомментирована в "#if 0". И там тоже
"яркость" ставится только для IMPORTANT.
Так что -- "яркость только для IMPORTANT" была задумана с самого начала.
Посему -- эта особенность отражена в doc/lib/Knobs.html, раздел же помечаем как "done".
N.B. Вот она, гадостность клиент-серверных программ: все бинарники от старых вещей (начиная с drc) у меня есть, а фиг запустишь -- им еще сервер раскочегаривать надо...
Widget
и XhWidget
.
05.01.2005: ввел в Knobs_internals.h
макросы CNCRTZE()
и ABSTRZE()
(скопировал из
Xh_utils.h), и начинил ими все подряд. Плюс, все методы,
указываемые в vmt, теперь "кошерные" -- в их определениях всегда
указывается XhWidget
, а уж они сами дальше делают
конверсию.
12.11.2005: продолжаем пьянку -- исправлено одно место в Knobs_widgetset.c. Иных таких косяков пока вроде больше нету.
Colorize
был бы эквивалентен стандартному --
CommonColorize_m
?
14.01.2005: да, наверное, можно -- достаточно в
CreateKnob()
проверять, что если
Colorize==NULL
, то уставлять это поле в
CommonColorize_m
.
17.01.2005: сделал, проверил, работает.
Дальше еще вопрос -- а стоит ли заменять везде ссылку на
"CommonColorize_m
" на NULL
? Пока решил сего
не делать, ограничившись "неофитами"
Knobs_{label,sep}_widget.c.
25.12.2005: вообще-то такой подход -- с занесением в ЧУЖУЮ VMT неких указателей -- несколько некорректен: ну НЕХОРОШО пытаться туда что-то писать -- а вдруг vmt расположена в readonly-секции?!
С другой стороны -- это наиболее эффективный метод. Да и работа аналогична тому, как действует компилятор, создавая VMT -- если у некоего объекта/класса метод отсутствует, то проставляется указатель на соответствующий метод ближайшего предка, где этот метод определен; в нашем случае -- без наследования -- это как раз и соответствует прописыванию стандартного fallback-метода.
CancelInputEditing()
всегда сбрасывал значение в curv
, что есть неприятно.
Сейчас, по опыту сегодняшних же разборок с "изменением кучи
связанных каналов стрелкой в одном из них", ввел интеллект,
возвращающий к "наиболее недавнему значению" -- с использованием, при
надобности, userval
.
Явно -- надо распространить принцип, используемый текстовыми полями (разные имена -- "input" и "value") на ВСЕ Knob'ы, чтобы в fallbacks'ах можно было указывать РАЗНЫЕ ресурсы.
06.05.2005: ну да -- сейчас сделал CTL3D'зацию onoff'ов, так вылезло неуместным потемнением еще много у кого...
06.05.2005: собственно -- потребность провернуть такое разделение окончательно стала очевидна после экскурсии (05.05.2005) к нам на тему магнитной системы для Беркаева, Валишева и Шатунова-jr. Прямо Беркаев сразу заметил, что ему нравится такой интерфейс, где сразу видно, какие каналы -- для отображения, а какие -- могут и управляться.
Кстати, именно после этой экскурсии во мне и окрепло желание "добить" групповое изменение каналов.
11.05.2005: да, еще несколько дней назад понял, что ничего сложного в сим не будет, просто подшаманить имена виджетов и добавить ресурсов. Но как-то все времени на праздниках не было :-)
Вначале придумал некий "план по именованию" -- по аналогии с разделением на "input" и "value" дать остальным такие же парные имена. А потом поразмыслил -- лучше ведь унифицировать, чтобы названия имели вид "BASE_i" (Input, read/write) и "BASE_o" (Output, readonly). А то задолбешься вспоминать, как же кто называется.
Итак, в принципе сделано. Highlights:
16.05.2005: короче -- пора помечать как "done".
knobinfo_t.incdec_step_fixed
-- оно у нас есть с давних
времен (с когда -- почему-то не записано...), но сейчас пора бы
уже превратить его в флаг в поле behaviour
.
17.05.2005: да, ввел
KNOB_B_INCDECSTEP_FXD
, модифицировал на его использование
Knobs_selector_widget.c (единственного клиента) и
Chl_knobprops.c, а incdec_step_fixed
-- удалил.
28.04.2007: ага, "единственного" клиента -- а button и alarmonoffled?! Вот сегодня-то в них и добавил.
behaviour
-- а не поделить ли это поле
на две 16-битных группы, и просто мирно линейно добавлять к ним по мере
надобности? Естественно, это уже опять в CXv4.
23.05.2005: сделал оный. И перевел на него "клиентов" -- их было-то всего ничего: dial, slider, text.
24.09.2006: легкое дополнение: при ошибке теперь
делается не просто очистка -- bzero()
-- rec'а, а
"инициализация умолчаниями": если ошибка -- PSP_R_USRERR
,
то оно вызывает psp_parse("", ...)
, тем самым заполняя
структуру умолчательными параметрами; иначе -- если PSP_R_что-то-другое
-- то тогда уж bzero()
.
23.05.2005: метод сделал. Клиенты:
alarmonoffled, button(+), dial(+), label, light, selector(+),
slider(+), text(+). Те, что помечены "(+)" -- в них сходу
использовалось ki->label
напрямую, а остальные и так
были "умные", и они только сократили объем кода.
Собственно -- а что мешает уже прямо сейчас сделать зачатки плагин-архитектуры? Т.е., чтобы программа могла зарегистрировать свою таблицу "виджетов" (да, пока что -- по идентификаторам), и Cdr+Chl+Knobs уж спокойненько создавали и вставляли бы эти специфические компоненты наравне с обычными.
(Вопрос только -- а как насчет simple-интерфейса, где параметр look= пока что сделан lookup'ом? В принципе, ничто не мешает перевести его на плагин...)
23.12.2005: Изготовил все, кроме поддержки в simple-интерфейсе.
Действия были произведены следующие:
knobs_widgetset_t
-- элемент списка,
содержащий указатель на массив knobs_widgetinfo_t
, плюс
поле next
.
KnobsGetVmtBy{ID,Name}()
теперь содержат
два вложенных цикла: цикл, перебирающий списки, и цикл поиска внутри
списка.
KnobsName2ID()
, с
аналогичным содержимым -- для psp-плагина в simple-интерфейсе.
KnobsRegisterWidgetset()
, при этом они попадают В НАЧАЛО
списка, так что имеют приоритет.
knobs_widgetinfo_t
перенесено
в Knobs_typesP.h, где оно вместе с
knobs_widgetset_t
и KnobsRegisterWidgetset()
образует отдельную секцию -- "extendability".
Естественно, в CXv4 надо будет сделать регистрацию таблиц
"по-правильному" -- с полем magicnumber
(как сейчас
реализована работа с таймаутами в cxscheduler'е), и reference count
поддерживать, как записано в проекте от 20-10-2005 (см. NOTES.html).
Но пока и так сойдет.)
Теперь -- надо проверять.
25.12.2005: ню-ю-ю... :-) Проверил, при помощи этого самого ausacc'а -- работает!!! Йоу, ya-ho-o-o-o-o!!!
26.12.2005: сделал также и
LookPluginParser()
и перевел все на него. Работает.
Собственно, такая архитектура более правильна, чем lookup-таблица --
поскольку теперь исключено дублирование информации и потенциальные
несоответствия между двумя таблицами.
Из "недостатков" -- теперь look= должен указываться просто словом, кавыченье невозможно. Впрочем -- особо-то и не требовалось (у меня look= нигде не кавычился).
Как бы то ни было -- "done".
CDR_FLAG_ALARM_MASK
), color
(CDR_FLAG_COLOR_MASK
), syserr
(CDR_FLAG_SYSERR_MASK
).
31.01.2006: впрочем -- спешить с этим не будем, как раз и подождем до следующего клиента "типа ipp" (видимо, это будет ausacc).
08.06.2006: потребность-то вылазит, но не с тем, про кого думали, а с linbpm'ом.
Идея же -- вообще-то, очевидная (уже с полмесяца) -- заключается в следующем.
ChooseColorState()
-- выбор состояния на основе флагов.
А они при инициализации составляют таблицу из GC, индексируемую по colstate'ам. И при рисовании просто выбирают из таблицы надлежащий GC. 04.08.2006: сделано.
16.06.2006: "опубликовал"
ChooseColorState()
, переименовав его в
CdrChooseColorState()
. И параметр
Knob k
теперь имеет право быть NULL
--
тогда на тему "attn" просто не проверяется.
16.07.2006: мдя, вот только при
k==NULL
не работает не только OTHEROP, но и RELAX! (А
он-то для вакуума очень даже нужен.)
В общем -- старая проблема с непотребными взаимоотношениями между флагами и состоянием. Сейчас ясно, что она состоит из двух аспектов:
CDR_FLAG_ALARM_ALARM
не соответствует никакое
состояние -- это как бы "нормально".
Это, конечно, имеет свою причину: сей флаг как бы ортогонален/параллелен обычной колоризации, и отображается он отдельно -- ЗНАЧЕНИЕМ лампочки либо цветом КНОПКИ в cx-starter'е (а не лампочками состояния), но все же -- кривовато.
Вывод: надо разработать СТРОЙНУЮ схему флагов и колоризации для использования в CXv4.
05.07.2008: угу, сделано:
LOGC_HEADING
.
XH_COLOR_FG_HEADING
и
XH_COLOR_BG_HEADING
.
ChooseKnobColors()
:
вставлен case LOGC_HEADING
в селектор по "расцвечиваниям".
Выяснилось, правда, что с "icon="-метками эта фича не работает, но то уже ихний баг.
04.03.2009: оказалось, что тогда я забыл
Xh-related-часть в 4cx/ портировать -- сейчас сделал (в выборе цветов,
в MotifKnobs_ChooseColors()
, естественно, кусок
"color/zzzzzz" пока пустой, так что туда портировать некуда :-)).
29.10.2008: да, сделал -- все это в Knobs_internals.c.
TextWheelHandler()
,
реагирующий на Button4 (вверх) и Button5 (вниз).
HandleTextUpDown()
.
KbdHandler()
переименован
в KnobsKbdHandler()
-- консистентности ради, а
TextUpDownHandler()
-- в TextKbdHandler()
,
корректности для.
Проверил -- работает. Осталось это портировать и в 4cx/.
30.10.2008: да, портировал. Кстати, в 4cx/ УЖЕ
использовались имена TextKbdHandler()
и
MotifKnobs_CommonKbdHandler()
.
Knobs_colors_lkp[]
.
06.03.2009: живет она в Knobs_internals.c, а объявляется в KnobsI.h, так что видна только "ручкоподобным". Что интересно -- оказывается, вполне катит объявление вида
т.е., при ОБЪЯВЛЕНИИ массива можно его размер не указывать -- и это работает!extern psp_lkp_t Knobs_colors_lkp[];
Идея такая: таблица ОДНА на ВСЕ случаи, и для bg, и для fg, и для
light, и для selcol. Вариант "default" в ней имеет значение -1, так
что при использовании юзеры этой таблицы должны проверять -- если
значение <=0
, то оно неуказано и надо брать своё
умолчание.
Так что теперь можно будет спокойно добавлять цвета по мере надобности в ЕДИНУЮ точку, и они сразу станут доступны везде.
Собственные таблицы отовсюду поубирал; в частности, Knobs_alarmonoffled_widget.c изрядно упростился.
4cx/ too. Хотя там, если по-правильному, это вообще должно сидеть в менеджменте стилей.
10.03.2009: проапдейтил эту таблицу, представив в ней весь набор XH_COLOR_JUST_xxx, расширившийся и упорядочившийся сегодня.
GC
, а его в публичном интерфейсе Knobs вроде как быть не
должно.
Идея решения проблемы: завести отдельный .h-файл "интерфейс knob-плагинов", назвав его, например, KnobsI.h ("I" -- "internals"), и уж в нем публиковать подобные вещи. (Туда же можно отправить и практически все содержимое Knobs_internals.h -- оно фактически и является тем самым "внутренним интерфейсом knob'ов".)
04.08.2006: сделал KnobsI.h, переселив в
него все содержимое Knobs_internals.h (оный остался пустым и,
видимо, подлежит удалению). И переселил в
KnobsI.h+Knobs_internals.c функцию
AllocStateGCs()
из vacclient_meat.c (где она
"родилась и выросла").
Так что теперь она и используется во всех заинтересованных knob-плагинах.
Кроме того, всем теперь стали доступны
Короче -- "done".
ParseWiddepinfo()
(в Chl_canvas.c был собственный
экземпляр) и CNCRTZE()
/ABSTRZE()
. С
последними есть фишка -- бедный Chl_canvas.c включает ОБА
файла с их определением -- Chl_internals.h и
KnobsI.h. Но это не есть проблема, препроцессор
переопределения допускает :-)
16.08.2006: писец -- там параметр
"w
" передавался как Widget
, вместо
долженствующего XhWidget
. Исправил (хотя дело и мутное).
07.03.2007: имелся за-а-амечательный баг -- там
использовался цикл до countof(table)
, при том, что
table
-- это параметр, объявленный как GC
table[]
; в результате table[COLALREM_JUSTCREATED] пропускалось.
Огрехи переноса, блин!!! Пофиксил.
KnobsMouse3Handler()
(чтобы всякие Chl'евские и прочие
element-plugin'ы могли навешивать его на метки строк/колонок).
05.06.2007: разобрался. Просто кнопков ActivateCB() вызывает SetControlValue(), а тот, в свою очередь, дергает ручков SetValue_m() с запрошенным значением -- чтобы оно отобразилось в ручке в нужном виде (необходимо в первую очередь для LOGD_TEXT*).
НО: для кнопок-то это совсем лишнее!!!
И непонятно скорей другое -- почему эффект бывал только ИНОГДА, а не всегда?
06.06.2007: решение-то оказалось довольно простым
-- вставил в SetControlValue()
перед вызовом SetValue_m()
дополнительное условие: при fromlocal проверяется флажок
KNOB_B_IS_BUTTON
, и если он есть -- то НЕ вызывать уставку
значения в ручке.
Как-то кривовато, на мой взгляд. Повезло, конечно, что этот флажок делает практически то, что надо -- он стоИт как раз у тех типов ручек, для которых и не надо вызывать "обновление", но это скорее совпадение.
Так что, "на будущее" -- думать, думать, и еще раз думать...
25.08.2011: вроде конкретно эта проблема и решена
16-12-2010 при реализации KNOB_B_IGN_OTHEROP
. Так что
теперь B_IS_BUTTON занят как раз ЭТИМ аспектом.
CreateSimpleKnob()
делать ему
SetKnobValue(,CURRENT_KNOWN_VALUE)
.
В первую очередь это касается декоративно-управленческих ручек (типа чекбоксов включения видимости всяких фич). Иначе -- чекбоксики-то теперь создаются в состоянии INDETERMINATE, а поскольку меняются только юзером (а не программно), то и выглядеть будут уродсковато (вылезло на чекбоксах отключения реперов в nadc333).
(Да, думал вставить SetKnobValue(,0)
сразу в
CreateSimpleKnob()
, но это неправильно -- тогда не будут
видны огрехи в программах.)
09.12.2010@Снежинск-каземат-11:
Knobs_wdi_equals_c
,Knobs_wdi_separators
,Knobs_wdi_terminators
)
и заполнены в Knobs_internals.c.
ЗАМЕЧАНИЕ: Knobs_simple.c использует ДРУГИЕ сепараторы.
01.02.2011@Снежинск-каземат-11: там в .c
-файлах оно
было объявлено как extern
, на что gcc ругался в стиле
warning: 'Knobs_wdi_equals_c' initialized and declared 'extern'
Так что extern
убран.
01.02.2011@Снежинск-каземат-11: делаем.
Knobs_buttonopts_t
.
_Knobs_GetStd_text2buttonopts()
Knobs_colors_lkp[]
также сделано
стандартный Knobs_sizes_lkp[]
.
TuneButtonKnob()
.
TUNE_BUTTON_KNOB_F_NO_PIXMAP
.
TUNE_BUTTON_KNOB_F_NO_FONT
(т.е., чтоб химичило только с цветом
фона).
Переведённая на этот функционал клиентура:
ARROW_Create()
уставляет размер самостоятельно -- поскольку там нет никакого fontList; (а
раньше оно, оказывается, bg не использовало).
Но в сумме, конечно, видно, что всё это -- лишь жалкая пародия на надлежащую реализацию стилей.
18.04.2011@Снежинск-каземат-11: заменил
_Knobs_GetStd_text2buttonopts[]
на
text2Knobs_buttonopts[]
.
15.12.2011@Снежинск-каземат-11: к Knobs_sizes_lkp[] добавлена альтернатива "tiny" - 8.
31.01.2005: дык, еще бы! Ведь поле
curv
никогда не уставляется, а механизм "usertime" --
очень даже работает. Что делать будем? "Руками" в
Simple_SetPhysValue()
прописывать curv
?
И, кстати, синхронный вопрос: а как насчет вообще поддерживать механизм "usertime" в simple-интерфейсе?
Несколькими часами позже: поразмыслил -- и:
curv
надо прописывать в
SetKnobValue()
, т.е., по ПОЛУЧЕНИЮ данных, в точной
аналогии с тем, как это делается в Cdr.
SetKnobValue()
вызываем vmtlink->SetValue()
руками? Ведь если
использовать SetControlValue()
, то почти автоматически
станет работать вся алхимия с "usertime". Для полноты картины надлежит
вызывать его так же условно, как и в Cdr -- проверяя, если вдруг время
редактирования еще не истекло.
Так и сделал. И в дополнение к usertime, также добавил (опять же
скопировав из Cdr) сброс поля wasjustset
.
А в ipp.c-то стоит собственная проверка на тему "если у нас запрос на запись еще висит, то ручку не трогать". А так -- теперь и без той проверки все должно работать нормально.
21.02.2005: сегодня испытано на том же Чугуеве -- все окей, проблема решена.
Но, возможно (именно ВОЗМОЖНО, а не обязательно) стоит в
CancelInputEditing()
проверять еще и
wasjustset
, и если да, то ничего не делать. ПЛЮСЫ:
(сомнительные :) будет "красиво" работать даже кривоватая ситуация,
когда SetKnobValue()
для некоего knob'а не вызывается
вовсе (у Чугуева такое есть). МИНУСЫ: тогда возможен сценарий, что
юзер введет число, нажмет Enter, потом начнет вводить еще чухню, нажмет
Esc -- и ничего не отменится! Это ведь будет неправильно. Так что --
пожалуй, НЕ БУДЕМ заходить столь далеко и поощрять нарушения "протокола
Knobs".
Итого -- помечаем раздел как "done".
11.04.2005: насчет сомнений типа "а не
проверять ли в CancelInputEditing()
еще и
wasjustset
" -- нет, не проверять, теперь уже точно.
См. раздел на тему CancelInputEditing()
от 11.04.2005.
LOGD_RADIOBTN
), что "протокол wasjustset" может мешать,
если Knobs используется для ЛОКАЛЬНЫХ ручек, которые никак не связаны
ни с какими каналами/атрибутами никакого реального прибора, и никакого
периодического обновления данных не происходит.
Надо иметь возможность этот "протокол" отключать (естественно, только через simple-интерфейс).
23.04.2005: ну и что -- вводим еще какой-нибудь
флажок в knobinfo_t
, означающий "никогда не уставлять
wasjustset
", и некий ключ-флаг в
Knobs_simple.c::text2knobinfo
, позволяющий сие
уставить?
24.04.2005: битовые флаги в PSP не предусмотрены;
парсинг ведется прямо в knobinfo_t
; заводить лишнее поле
только лишь для такого одноразового флага -- жаба душит.
Решено: в качестве этого флага "для парсинга" используем сам
wasjustset
, потом утаскиваем из него в
behaviour
, а его -- :=0.
25.04.2005: сделал -- ввел флажок
KNOB_B_NO_WASJUSTSET
(отодвинув
KNOB_B_{HALIGN,VALIGN}_*
), при котором оба
SetControlValue()
перестают взводить
wasjustset
в 1.
И в simple-интерфейсе флаг "local", использующий, по
вышеприведенному проекту, поле wasjustset
для парсинга.
Считаем, что "done".
26.04.2005: проверил, работает. Так что -- действительно "done".
SetKnobValue()
, который сейчас существует
в ДВУХ идентичных экземплярах), и в Knobs_simple, и где еще
понадобится в будущем.
И уж в этом-то интерфейсе будет зашита вся умность типа
wasjustset
.
06.02.2006: на эту тему -- которая вообще-то шире -- все "обсуждение" идет в разделе начатом 17-05-2005.
06.08.2006: так что этот пункт с чистой совестью маркируем "obsolete".
CreateSimpleKnob()
-- добавлен параметр
options
(сразу после spec
), и можно указывать
флаг SIMPLEKNOB_OPT_READONLY
, приводящий к принудительному
"ro".
Смысл -- возможность централизованно форсить readonly (нужно для fastadc/pzframe).
31.05.2004: итого:
list_count
-- тут warning
был правильным. При widdepinfo=="" оно содержало бредовое значение.
Вставлено =0
.
SetValue_m(ki,ki->curv)
.
ki->label
не использовалось -- метка бралась
только из widdepinfo (атрибут "#L"). Это явный рудимент времен, когда
widdepinfo еще не существовало и вместо него использовалась label.
Поправлено -- при label!=NULL она копируется во внутренний буфер, в
который потом складируется содержимое "#L" (т.о., последний имеет
приоритет).
ActivateCB()
, чтобы оно
не давало warning'а.
22.11.2004: ввел новые два #-спецификатора -- "#H"
(horizontal) и "#C" (compact, vertical). И сделал, что уставляется корректный
XmNorientation
, а для compact еще и XmNspacing:=0.
Единственная неприятность -- почему-то метка центрируется, вместо того,
чтобы быть left-aligned. Это какие-то странные приколы с принудительным
alignment'ом у XmLabelGadget'а -- более того, editres ресурса "alignment"
вообще не видит, хотя в man-странице он обозначен. Оказалось достаточно
просто явно, "руками", прописать метке
XmNalignment:=XmALIGNMENT_BEGINNING
.
13.01.2005: ну сделал, а что толку? :-) Работает, но не слишком-то это красиво -- т.е., не всегда стОит делать такую уставку. По-хорошему, вопросы расположения ручек должны решаться другим механизмом -- ВНЕ компетенции самих ручек, уровнем выше.
17.03.2005: @KEK: Вечером того же дня: о -- там можно это делать просто селектором: когда он readonly, перетаскивать всю толщину его shadow в margin'ы, так что он сам будет сливаться с фоном. И, как-нибудь отбирать у него при этом события "mouse click".
04.04.2005: а селектор-то вообще тупой, он даже XmNtraversalOn:=is_rw не делает!!! И кстати -- из-за XH_COLOR_CTL3D_* он НЕ будет сливаться с обычным фоном -- увы.
13.04.2005: сделал -- при readonly и
XmNtraversalOn:=False
, и "отбирание" события "Button1
click", и "перекачку" shadowThickness в marginHeight. И -- да,
действительно, из-за CTL3D он выделяется.
11.05.2005: сегодня ввел разделение на "_i" и "o", после чего "проблема CTL3D" исчезла. Так что -- "done".
14.07.2007: ввел.
21.06.2006: разобрался -- все очень просто: безпараметровые кдючи ТОЖЕ требуют завершающего '\v'. Глупо, но факт -- в противном случае они просто "съедают" все до этого самого '\v'.
Чтобы работали оба варианта, вставил в код всех таких опций строчку
if (*p == '#') strend_p = p - 1;
Все рудименты, рудименты, блин!
21.01.2007: пошарился в CascadeBG*.[ch]
-- вопрос крайне темный. Там есть некая хрень,
CBG_Cascade_width()
, отображающаяся на
cascade_rect.width
, но вот как именно оно высчитывается --
я что-то недовъехал.
В Motif-FAQ же просто рекомендуют "Set XmNcascadePixmap on the option menu's cascade button gadget" (ну и куда -- заводить специально для этого pixmap 1*1 пиксел?).
Мдя, с имеющимся уровнем "прозрачности" Motif'а, похоже, проще забить на проблему...
12.05.2007: в общем ("общечеловеческом"? "общекорректном"? ...?) случае попытка решить эту проблему открыла бы банку с пауками (can of worms): ведь в идеале надо уметь динамически (at run-time) указывать, что такой-то пункт надо задисэйблить/раздисэйблить. Как это делать -- не то что в нынешнем протоколе, а даже в cx-proto/v4 -- фиг знает.
Но сейчас-то легко можно сделать простенький "хак", который отлично решит имеющуюся проблему: ввести некий префикс "текста пункта", который будет указывать, что этот пункт надо сделать insensitive. Самое тривиальное -- в качестве такого префикса использовать '\n'.
Так и сделал -- работы было минут на пять. "done".
Например, чтобы картинка бралась, если "метка" имеет вид "=#!=ИМЯ_КАРТИНКИ" (опциональный '\n' в начале также разрешается).
Тогда -- были бы и селекторы "визуальные", и readonly-статус-отображаторы тоже. И в choicebs такую функциональность надо добавить. И в обычный LOGD_BUTTON тоже. И в LOGD_HLABEL.
P.S. Это, плюс еще планируемый LOGD_IMAGE явно показывают, что надо заиметь вызов "XhAssignPixmapFromFile()".
28.03.2008: итак -- сделано. Протокол реализации (оно о куче мест, но раз уж начали отсюда -- то здесь все и опишем):
27.03.2008: вставил поддержку этой фичи в LOGD_BUTTON (там было быстрее всего), оно пользуется свежесделанной
XhAssignPixmapFromFile()
.Собственно "префикс" определен в Knobs.h как
#define KNOB_LABEL_IMG_PFX_S "=#!="И очевидно, что c пиктограммабельностью LABEL'ов никакой LOGD_IMAGE и не нужен. Разве что -- если сделать специальный шибко умный компонент, который бы умел показывать и анимированные GIF'ы тоже.
28.03.2008: а собственно "мозги", диагностирующие префикс и дергающие налепление пиктограммы, переселены в Knobs_internals.c::
MaybeAssignPixmap()
-- для максимального упрощения использования этой фичи.Добавлена поддержка в LOGD_SELECTOR, LOGD_CHOICEBS и LOGD_HLABEL.
Имеется некоторая "идеологическая некрасивость": что пиктограмма, через этот префикс, для не-списочных ручек -- BUTTON и LABEL -- указывается в поле
label
, используемом также и grid'ом.Осталось несколько вещей:
- ???
- Добавить ПОИСК пиктограмм, при помощи
findfilein()
-- чтобы оно брало не только в текущей директории, но и в директории, откуда программа запущена, и в ~/pult/icons/ (каковую надо еще ввести).- Портировать в 4cx/.
02.04.2008: насчет "поиска" пиктограмм -- еще 28.03 стало ясно, что нынешняя схема -- когда Xh'ная функция добавляет расширение, и как бы она же должна бы делать поиск -- кривовата. И правильнее -- повесить добавление расширения и поиск (это связанные вещи -- буфер результирующего имени файла-то нужен для обеих) как раз на
MaybeAssignPixmap()
.В общем -- так и сделал: эти задачи теперь повешены на
MaybeAssignPixmap()
и ее подручнуюmaybeassign_check_proc()
. Причем технология -- извратная оптимизирующая: основная функция имеет буфер и передает указатель на него проверяльщице, и та именно в нем формирует имя файла дляaccess()
'а. Поэтому при "успехе" не надо заново формировать имя, а достаточно использовать то, что уже сготовлено в буфере.Следствие: можно превратить
MaybeAssignPixmap()
в "мультиплексор", определяющий, какого "загружальщика" вызвать по расширению результирующего файла. Так можно будет заиметь поддержку других форматов -- JPG, GIF, PNG, ... Хотя, скорее, правильным будет все-таки переложить эту задачу наXhAssignPixmapFromFile()
-- чтобы сосредоточить "мозги" «загрузчика картинок» в одной точке, сделав их доступными РАЗНЫМ применениям. Впрочем -- тут возможна куча вариантов и нюансов, так что -- отложим до момента, когда это реально потребуется.P.S. Да, ищет оно в стандартном списке мест, аналогичном тому, где роются и остальные клиенты
findfilein()
'а. Может -- вообще макрос сделать, такого вида:(надеюсь, какими-нить макросами для "конкатенации" пользоваться не придется)?#define CX_SEARCH_LIST_FOR_FINDFILEIN(subdir) \ "./", "!/", "!/../"subdir, "$PULT_ROOT/"subdir, "~/pult/"subdir"03.04.2008: а насчет некрасивости с использованием поля
label
-- так это тоже легко:
- Вводим таким ручкам widdepinfo-параметр "image", с типом MSTRING.
- И функции
MaybeAssignPixmap()
передаем уже его, а не label.- И еще -- поскольку у этого параметра префикс "=#!=" будет не к месту, то функции "а не присвоить ли..." понадобится еще аргумент -- "проверять ли префикс".
07.04.2008: кстати, проверил -- работает, на всей шобле HLABEL/BUTTON/SELECTOR/CHOICEBS. Для этого:
- Завел директорию lib/icons/, с добавлением ее в EXPORTSTREE (так что -- будет pult/lib/icons/).
- Создал chlclients/knobtest, на которой и проводились испытания.
А вот теперь возникает мысль -- не дополнить ли, общности ради, аналогичной фичей и обитателей alarmonoffled? А там придется слегка помудрить с indicatorSize -- аналогично тому, как при пустой метке.
И -- в любом случае пора переходить на widdepinfo-указание пиктограммности.
Несколькими часами позже: да, перевел указание имени пиктограммы на widdepinfo. Т.о., исчезла проблема использования поля
label
не по назначению; применение же префикса "=#!=" лимитировано list-ручками (SELECTOR и CHOICEBS).Следствие: а не захочется ли ввести сию фичу для прочих ручек, ИСПОЛЬЗУЮЩИХ метку -- как то DIAL, *SLIDER, и даже тот же SELECTOR? Нет уж, нафиг-нафиг!
08.04.2008: вставил поддержку и в alarmonoffled тоже. Да, там пришлось слегка похимичить с
XmNindicatorSize
-- но попроще, чем при label=="", поскольку какая-то метка-то есть -- так что просто смотримXmNindicatorSize
при labelType==XmSTRING, а потом уставляем ее же после labelType:=XmPIXMAP; естественно -- все это пошагово.30.06.2010: по просьбе Роговского в путь поиска добавлена также и include/pixmaps -- в тех же вариантах, что и lib/icons.
Теперь отдельная задача -- заиспользовать это все, и посмотреть/понять, как эти СУЩЕСТВЕНННЫЕ дополнения скажутся на общем виде, подходе и наполнении ПО управления.
08.04.2008: господи, до чего ж это все надоело за прошедшие почти полмесяца!!! Краткое резюме -- задуманное выполнено, и даже перевыполнено. Увы, введение widdepinfo-параметра "icon=" породило некоторую некрасивость (особенно с учетом имеющегося в современном Motif'е типа XmPIXMAP_AND_STRING), но совсем нефатальную, так что можно на нее забить. Что же до "посмотреть/понять" -- ох, ПОТОМ!!!
"done".
05.07.2008: ага, ща-з-з! При ki->color!=LOGC_NORMAL (точнее, для не-стандартных фонов) оно глючит -- оставляет стандартный фон.
Явно потому, что MaybeAssignPixmap()
вызывается ДО
первой "колоризации"...
13.05.2009: база -- добыча и парсинг этих style-strings -- скопирована из choicebs. А вот РАСЦВЕЧИВАНИЕ -- сделано "вручную":
int *bgs
--
массив индексов фонов для каждого пункта, плюс cur_bgidx
.
cur_bgidx
прописывается новой функцией
HandleBgIdxChange()
, которая при newidx!=oldidx вызывает
Colorize_m()
.
HandleBgIdxChange()
вызывается при смене значения
селектора -- в ActivateCB()
и SetValue_m()
(как раз у них есть "текущее значение" прямо числом, а не
виджетом).
Colorize_m()
же при cur_bgidx>0
уставляет фоном cascade-button'а именно этот "текущий цвет", а не
обычный результат ChooseKnobColors()
.
Хотя ringcamsel не переведен на этот вариант, но оно проверено -- работает, так что "done". 26.02.2013: ringcamsel too.
P.S. И, вроде бы, для simple-knobs также должно работать корректно
-- поскольку при создании делается cur_bgidx=bgs[0]
, а в
CreateKnob()
вызывается колоризация, так что сразу будет
стоять нужный цвет.
Это нужно для sukhphase -- чтобы полностью перевести его на libKnobs.
Встает только вопрос -- размер будем делать фиксированным (как у Dial), или позволим указывать в widdepinfo?
09.02.2004: обнаружилась неиспользуемая с давних
времен константа LOGD_SCALE
.
Как бы то ни было, она оставлена на месте, а добавлены константы
LOGD_HSLIDER
и LOGD_VSLIDER
.
23.02.2004: начал делать -- изготовил Knobs_slider_widget.[ch] и зарегистрировал его в Knobs_widgetset.c.
Оно пока практически ничего не умеет -- только создает слайдер. Надо еще думать о:
01.05.2004: посмотрел man на XmScale, и, похоже,
никак нельзя управлять тем, что отображается на тамошнем лэйбле. Более
того, даже заглянул в motif/lib/Xm/Scale.c -- там тупо
вызывается их внутренняя GetValueString()
, и все.
Так что, единственный выход -- вычитывать поле "precision" из
даденного нам dpyfmt
, и 1)ставить
XmNdecimalPoints=precision; 2)"мухлевать" со значением, подсовывая
XmScale'у его *(10^precision), а получаемое перед отдачей "клиенту" --
делить на то же число.
(Маленькое "но": парсингом dpyfmt и приведением его к
гарантированно корректному виду сейчас занимается Cdr, так что -- при
CreateSimpleKnob()
надо подумать...)
04.05.2004: пытался понять, как же корректно
заставить XmScale по стрелкам меняться именно на
incdec_step
. Может, как-то и можно, но... У реального
XmScrollBar'а есть параметры XmNincrement
и
XmNpageIncrement
, но XmScale их очень эффектно прячет,
выставляя наружу лишь какой-то странный XmNscaleMultiple
,
а внутри себя производит махинации с реальными параметрами...
Так что -- пока игнорируем incdec_step
,
потом посмотрим...
04.05.2004: бета-версия готова. Highlights:
XmNdecimalPoints
, плюс
мухлюется в 10^precision раз при передаче значения виджету и при
получении от него.
SetValue_m()
вставлять
round()
перед преобразованием к целым -- иначе оно как-то
умудрялось округлить 3.0{0-в-периоде} до 2 (причем, 3.0 получалось как
0.3*10, а 0.3 бралось из виджетового 3/10.0).
'.'
и делаем strtol()
за ней.
13.09.2004: собственно: а чего это я
маюсь с XmScale? Взять обычный XmScrollBar
, и самому
организовать label и отображение значения (а оное -- можно даже и
поверх slider'а). И все проблемы махом исчезают.
14.09.2004: начал переделку на вариант с
XmScrollBar
. Компонент состоит из трех частей: собственно
scrollbar, опциональная метка и опциональный индикатор.
Помнится, еще в sukhphase была потребность иметь "комбинированный" компонент -- ScrollBar+Text. Так как насчет иметь возможность вместо "value" ставить виджет "input"? При этом сам scrollbar можно делать и не-traversable.
BTW, а можно ведь и у LOGD_DIAL поступать так же -- иметь "вводибельное" поле, а прмоугольник фокусированности вокруг циферблата не рисовать. (Вместо предполагавшейся хитрой логики с "появлением по нажатию")
И очевидно, что засим как раз и вытащатся все махинации с XmText в отдельный интерфейс.
15.09.2004: сделан начальный вариант нового LOGD_*SLIDER. Highlights:
21.09.2004: примерно доделал "ввод и отображение". Итак:
XmNincrement
) также прекрасно реализовался.
XmScrollBar
, в отличие от
XmScale
, проблемы с отображением min!=0, то мы сдвигаем
для виджета диапазон так, чтобы он всегда начинался с 0. И,
соответственно, в SetValue_m()
и ChangeCB
делается сдвиг.
(BTW, как показало разбирательство в lib/Xm/Scale.c, он ВСЕГДА ставит своему scrollbar'у диапазон 0-1000000000/*SCROLLBAR_MAX*/ и сам маппирует на него то, что указано в [XmNminimum,XmNmaximum].)
19.11.2004: вносим некоторые изменения, обусловленные, в основном, отдельным цветом для нажимабельностей:
Плюс, теперь уставляются цвета и контейнеру -- чтобы оно краснело "все целиком". Забавно, что при этом остаются тени (?) у поля "value".
21.11.2004: стал разбираться с вяканьем -- понял,
что я, мягко говоря, очень вольно обращаюсь с понятием "XmNsliderSize"
-- всегда ставлю его в 30, вне зависимости от масштаба (sf), диапазона
и т.д. -- короче, я неправильно его понял. (Собственно, в man'е
четко написано, что "XmNvalue: Specifies the slider's position, between
XmNminimum and (XmNmaximum-
(Вообще-то это скорее ляп именно Motif'а, причем теоретическо-методологический (и идеологический тоже :-): помнится, в моей турбопаскалевской библиотеке GREMLIN были ИЗНАЧАЛЬНО заложены три вида "бегунковых" -- OSlider, OScrollBar и OProgress (плюс визуально отличающийся подвид OSlider'а -- OChooser). OSlider отличался от OScrollBar'а тем, что имел бегунок ФИКСИРОВАННОГО размера, а не пропорционального -- поскольку у этих вариантов РАЗНЫЕ области применения. А в Motif'е аналога OSlider'а нету -- вот и приходится его имитировать при помощи обычного scrollbar'а...)
Так что -- теперь ввел параметр "процентный размер бегунка"
ssizepcnt
, по умолчанию равный 30, а для термометра -- 0,
и из него вычисляется a_ssize
(при ==0 он делается =1, для
термометра), каковой потом и указывается в качестве
XmNsliderSize
. Плюс, в качестве XmNmaximum
теперь ставится не просто "масштабированный максимум", а
max_i(ki)+a_ssize
-- тем самым теперь ВСЕГДА резервируется
место под бегунок.
Ругань исчезла -- вроде все ОК.
22.11.2004: вставил первый вариант корректного центрирования scrollbar'а -- в варианте "default", т.е. left-to-right. Сделать это пришлось кривовато -- центрирования в форме нету, а вариант "вычитать размеры формы и виджета а потом поставить его в (formHeight-itsHeight)/2" не работает оттого, что от формы вычитывается высота 0 (ох, блин, как меня этот XmForm задрал...). Так что -- сделал по совету из Motif-FAQ, через ATTACH_POSITION.
Часом позже: сделал еще вариант компоновки -- "compact". Для HSLIDER'а она ставит label,sb,value друг под другом, для VSLIDER'а -- sb справа, а слева к нему друг под другом приаттачиваются label и value.
23.11.2004: для компенсации легкой некорректности вычислений формы при центрировании перешел с topAttachment'а на bottomAttachment (там был сдвиг на один пиксел -- из-за того, видимо, как именно форма считает координату "50%"). ВНИМАНИЕ: offset при этом все равно надо ставить отрицательный, т.к. он считается в другую, по сравнению с top, сторону.
23.11.2004: собственно, эта животина уже все умеет -- так что помечаем как "done".
04.01.2005: удалил код старого варианта слайдера, который работал с XmScale.
22.11.2004: Это обеспечивалось устаревшим
начальным значением XmNvalue
-- min_i(ki)
,
которое было раньше, с XmScale. А теперь нужно было ставить
-ofs_i(ki)
, что и было сделано.
17.01.2005: обнаружился еще один ляп: начальное значение вполне может быть ВНЕ отображаемого диапазона, куда его надлежит в таких случаях загонять принудительно.
Вставил "принуждение" взамен былого безусловного
-ofs_i(ki)
.
17.01.2005: ну что, назовем его "rtl"?
Парой часов позже: да, так и сделал -- это ключик "rtl". Порядок такой: [label][value][scrollbar] (так что метка и значение идут словно одной строкой (хотя интервал между ними и есть)).
А мудрая функция, которая расставляет подвиджеты, теперь устроена
так: вначале виджеты забиваются в массив row[]
в
надлежащем порядке (в зависимости от opts.layout
), а затем
содержимое этого массива расставляется слева направо.
17.01.2005: готово -- делов-то...
17.01.2005: ну сделал. А противоположный, дефолтный параметр, назвал "=", чтоб его указать никак нельзя было.
18.04.2005: переименовал "=" в
"withlabel" -- для унификации с LOGD_TEXT*
.
10.05.2004: в принципе, можно изготовить
"выпендреж общего характера" -- типа кнопки, которой можно назначить
произвольную картинку (реализовывать -- не то на
XmDrawnButton
, не то на
XmPushButton
@XmNlabelType=XmPIXMAP
).
Но вообще-то существует ведь XmArrowButton
, который
исполняет РОВНО ТЕКУЩУЮ ПОТРЕБНОСТЬ. Лучше на нем и сделать, надо
будет только повозиться с выставлением размера в соответствии со
шрифтом обычных кнопок. А в остальном -- оно будет унифицировано с
обычным LOGD_BUTTON, и жить будет в том же
Knobs_button_widget.c.
10.05.2004: по минимуму сделано. Введены
LOGD_ARROW_{LT,RT,UP,DN}
, в Knobs_button_widget.c
добавлена поддержка этих кнопок-стрелок, они зарегистрированы в
Knobs_widgetset.c (кстати, оный переведен с "ручного"
заполнения таблицы на макрос KNOB_LINE
), добавлена пара
строк в Xh_fallbacks.h (впрочем, отражающих поведение по
умолчанию).
Плюсы: сделано влет, буквально за двадцать минут, и уже работает.
Минусы: уродски все-таки выглядит Motif'овский
XmArrowButton
-- при нажатии меняется только тень стрелки,
а у кнопки она остается как есть.
Недоделки: по счастливому совпадению,
размер этой кнопки точно совпадает с нужным нам при шрифте
lucidasans{,typewriter}-12. А вообще-то надо ввести код, который бы
имитировал геометрические вычисления XmPushButton
(спрашивая ресурсы fontList, *margin*, etc. у Xrm "от имени" виджета
"push").
02.06.2004: кстати, насчет "уродского поведения XmArrowButton": а как насчет при этом принудительно делать swap(topShadowColor,bottomShadowColor)?
Получасом позже: сделал. Как-то странно вызываются эти arm/disarm callback'и -- при уводе мыши и вводе ее обратно на тело кнопки, пока кнопка мыши еще не отпущена, они НЕ дергаются, а вызываются ТОЛЬКО при нажатии (arm) и отпускании (disarm). А вот стрелка -- она-то реагирует на увод/ввод. Мдя-я-я-я... :-)
10.06.2004: так, между делом, выяснил, что
"счастливое совпадение" умолчательного размера XmArrowButton с нужным
нам обеспечивается той же причиной, по которой счастливо подходил
умолчательный размер у IncDecB: в
lib/Xm/ArrowB.c::Initialize()
стоит фразочка
if (request->core.width == 0) new_w->core.width += 15;
и аналогично для height
. Видимо, именно этот кусок я в
свое время скопировал в IncDecB.c. А при shadowThickness=2 и
highlightThickness=1 (используемых прямо в Core для установки
минимально осмысленного размера виджета) имеем (2+1)*2+15=21, что и
есть требуемая высота для lucidasans{,typewriter}-12.
11.08.2004: насчет самоопределения размера:
существует функция XmStringExtent()
, которая отлично
возвращает размеры указанного текста для указанного шрифта.
16.08.2004: у-ф-ф-ф!!! Сделал-таки, повозившись пару дней, самоопределение размера по размеру такого же XmPushButton'а.
Мда, нелегкое это дело -- понимать, как правильно сделать под
Motif'ом. Самое хитрое было -- разобраться, как же "спросить ресурсы
от имени кого-то". В конечном итоге наткнулся на функцию
XtGetSubresources()
, служащую официально для получения
ресурсов для под-виджетов. А реально -- она делает ровно нужную
работу. Поиск GoogleGroups на тему "xtgetsubresources" дал
очень хороший комментарий.
Немного помучался, и теперь оно работает.
Итого -- помечаем раздел как "done".
is_rw
.
23.08.2004: вообще-то очевидно, что КНОПКИ в
режиме readonly просто бессмысленны. Но -- корректности для стоит
иметь поддержку !is_rw и в них. (Уже и сейчас ничего плохого
не будет -- запросы на запись в readonly-каналы срезаются в
SetControlValue()
; а надо бы резать и в
Chl_gui.c::E_SetPhysValue_m()
тоже (минутой позже
-- вставил такую защиту)!)
Что же до решения обозначенной в заголовке проблемы, то она решена.
При создании ставится XmNsensitive:=ki->is_rw
, а в
SetValue_m()
вставлена дополнительная проверка.
08.11.2004: А почему, собственно,
sensitive:=False, а не игнорировать нажатие?! И достаточно ставить
XmNtraversalOn:=is_rw
.
Несколькими часами позже: переделал по этому варианту -- вроде все ОК.
05.01.2005: доперло, где могут быть полезны не-rw кнопки-стрелки: например, для вещей вида того самого УШД, если нам НЕ нужно иметь именно управление, а нужна только индикация концевиков -- тогда стрелочки будут работать просто как лампочки нужной формы.
24.10.2004: только что проверил -- вполне пристойно выглядит стрелочка, у которой "тело" красное вместо черного. Неидеально, конечно, но все же. Надо будет только посмотреть, чтобы оно корректно обращалось с вариантами HILITED/IMPORTANT.
26.10.2004: мдя, придется для этого вводить privrec, в котором хранить старое состояние...
03.10.2004: все сделал как написал -- и что? Конфликтует с колоризацией :-). Теперь надо еще туда встраиваться :-)
BTW, вставил еще и в Cdr "предсказание" -- теперь всем
кнопкообразным автоматом ставится
KNOB_B_IS_LIGHT|KNOB_B_IS_BUTTON
.
07.10.2004: пофиксил. Теперь, собственно,
SetValue_m()
только принимает решение о том, что состояние
изменилось, и если да -- то вызывает Colorize_m()
. А уж в
том, являющемся почти копией стандартного
CommonColorize_m()
, вставлена проверочка -- если "is_lit",
то он делает fg=XhGetColor(XH_COLOR_JUST_RED)
.
Соответственно, вследствие сотрудничества со стандартным механизмом
колоризации, нам НЕ потребовалось самостоятельно узнавать и хранить
default (unlit) foreground.
Единственная "не-совсем-красивость" -- что краснеет всегда цветом JUST_RED, а он совпадает с BG_IMP_RED. Впрочем -- нефиг делать красный диапазон у "краснеющей лампочки".
18.02.2007: проблем: оно там вызывает для подсветки
Colorize_m()
не безусловно, а только если значение
изменилось. НО!
Colorize_m()
-то проверяет не по переданному "новому"
значению, а по ki->curv
, которое в
Simple_SetPhysValue_m()
не уставляется (для унификации с
Cdr-вариантом).
В результате возникает ситуация (при использовании
simple-протокола): кнопка нажимается из не-горящего состояния,
SetValue_m()
решает, что надо б подсветить, и вызывает
колоризатор -- но тот-то видит, что curv==0 и нифига не подсвечивает!
А потом, когда приложение вызывает SetKnobValue()
, то
last_lit уже уставлено в 1, и опять же перерисовки не делается!
Правильный выход -- вытащить мозги колоризатора в отдельную функцию,
которой из SetValue_m()
передавать v
, а из
Colorize_m()
-- ki->curv
. Так и сделал --
проблема ушла.
(А обходной вариант, который сначала спас
ndbp_adc200_elemplugin.c::RunKCB()
--
предварительный безусловный вызов SetKnobValue(,0)
-- при
этом ki->curv
и отображаемое значение
синхронизовывались.)
Мммм... А ведь по крайней мере при !is_rw
общая тень точно не нужна совсем. Может, сделаем, что при этом она
автоматом отключается?
16.04.2005: да, сделал что при
!is_rw
внешняя тень автоматически отключается. Нормально,
почти нормально. Только опять, как и с readonly-селектором, вылазит
фон CTL3D.
Плюс еще что -- из-за наших махинаций (см. за 02.06.2004) с topShadow<->bottomShadow выглядит, что оно даже и не нажимается вовсе, пока курсор в сторону не уведешь. Для readonly-то это нормально, а вот для обычных, нажимабельных стрелок -- будет плохо.
17.04.2005: ну последняя проблема-то нехитрая.
Ввел внутри функции флаг has_shadow
, и если он 0, то
убираю тень, а иначе (и только иначе!) уставляю каллбэки на махинации с
тенями.
11.05.2005: в связи с разделением на _i- и
_o-виджеты проблема с CTL3D исчезла. Итого -- "done", а на
"отдельное указание noshadow" пока забъем -- при надобности
получать has_shadow
через psp не проблема.
XH_COLOR_JUST_RED
, а хочется
-- мочь указывать, чтоб оно бывало и зеленым, и оранжевым, и синим.
Что -- делаем psp-параметры-флаги "red" (default), "green", "amber", "blue"?
И, кстати, можно на такую же идеологию перевести alarmonoffled -- чтобы НЕ БЫЛО отдельных типов led'ов для разных цветов, а указывался бы цвет (для alarm'ов это, конечно, малонужно :-).
28.05.2006: да, для alarmonoffled'а сделано. Ежели чего -- можно скопировать оттуда.
27.01.2007: да, скопировал. Понадобилось для управления системой выпуска пучка в linmag'е -- чтоб стрелки, когда мотор едет в соответствующем направлении, зеленели.
KNOB_B_INCDECSTEP_FXD
(и в button, и в arrow).
11.12.2007: да, сделал. Для этого ввел троицу widdepinfo-параметров: bg={green|amber|red|blue}, size={small|normal|large|huge} и bold. Если параметр не указан, то он идет за -1 и ничего не делается, а если указан -- то делается соответствующий XtVaSetValues(). Параметр "bold" работает только при указанном "size".
01.04.2014@Снежинск-каземат-11: добавлен еще один размер -- giant, 34pt. В основном для кнопок-стрелок, в первую очередь для [>] в elem_dl200_slow (с текстом вообще не факт что покатит, русского шрифта такого размера я не делал).
P.S. И, кстати, а на стрелки-то указание параметра size, работающего с обычными кнопками, НЕ влияет -- делалось уже позже. Хотя это б нас наполовину спасло.
17.11.2010: да, сделано -- полностью аналогично LOGD_BUTTON'у. Просто при указании size оно само создаёт XFontStruct/XFontList, и пере-добывает размеры уже с него.
01.06.2004: в ipp.c понадобилось сделать
"включатели" окошек сделать "более заметными". Поскольку
XmToggleButton
в принципе умеет "заполнять" не только
индикатор, но сразу всю площадь под лэйблом, решено было сделать
поддержку такого поведения.
Итак: если в widdepinfo
содержится магическое слово
"button" (case-insensitive), то включается "полнокнопочное"
поведение. Это заключается в том, что
XmNfillOnSelect:=True
,
XmNindicatorOn:=XmINDICATOR_NONE
,
XmNalignment:=XmALIGNMENT_CENTER
,
XmNshadowThickness:=1
, а XmNmarginHeight-=1
.
03.06.2004: поменял "button" на "panel".
22.08.2004: проблемы с "panel":
Странность в том, что при множественных кликах callback вызывается только ОДИН раз... И это как-то связано с "множественностью" таких кнопок. Надо б разобраться, как XmToggleButton'у объяснить, что он ВСЕГДА именно toggle, а не radio...
Часом позже полуразобрался: проблема возникает, когда
XmNindicatorType=XmONE_OF_MANY_ROUND
(хотя в man'е и
написано, что "This value specifies only the visuals and does not
enforce the behavior", это явно не так. Убрал для panel'а такую
уставку, и оно прочухалось...
23.08.2004: итого -- со второй проблемой вчера разобрался, а что касается первой, то разобрался сегодня :-).
Итак: странно, что эта проблема проявилась именно только с panel'ом.
Это был старый ляп, еще со времен, когда я подгонял высоты всех
виджетов для выравнивания по baseline'у. В Xh_fallbacks.h
были прописаны значения "*.onoff.margin{Top,Bottom}:0". А
реально они оба имели значение 2. Почему? Из-за того, что оно само
себе вычисляет значение XmNindicatorSize
из размера
label'а, и, дабы поместилось, "Note that a change in this
resource may also cause a change in the values of the inherited
resources XmNmarginTop, XmNmarginBottom, and
XmNmarginLeft" (цитата из man XmToggleButton).
Так что, использованное решение: изменил в Xh_fallbacks.h значения с 0 на 2, так что теперь и обычные работают нормально, и в варианте "panel" все ОК.
А что осталось ненравящимся -- махинации с "marginHeight--; shadowThickness++;". Совсем-то корректным будет создавать panel-виджеты с другим именем ("onoff_p"?) и сделать им свои отдельные fallback'и.
08.11.2004: насчет "странностей с ONE_OF_MANY_ROUND": похоже, это действительно признанный баг Motif'а, то ли #1188, то ли #1154. В любом случае, оба этих бага исправлены в OpenMotif-2.2.3.
07.08.2006: имеется у panel'а один пресерьезнейший недостаток -- он НЕ КОЛОРИЗУЕТСЯ. Фокус в том, что у toggleButton'а в таком режиме само понятие "background" отсутствует -- есть только сплошной unselectColor/selectColor. И все.
В принципе -- не ахти какая проблема, но просто несколько неприятно и сбивает с толку.
30.08.2009: ага, а в OpenMotif-2.3@CentOS-5.2 оно наоборот -- ТОЛЬКО колоризуется: метка ВСЕГДА отрисовывается на цвете фона, а *selectColor используется только для кусочков сверху и снизу... Похоже на bug#1395.
12.20.2010@Снежинск-каземат-11: а можно ли как-то обойти этот баг, чтоб он не светился?
10.01.2011: неа, судя по bugs.morifzone.org -- фиксов-под-глючную-версию нет, а просто якобы исправлено 22-04-2008 в 2.3-branches.
Надо бы проверить в RHEL6 -- вдруг оно НЕ пофиксилось. Отдельный вопрос -- откуда там брать OpenMotif...
22.04.2011@Снежинск-каземат-11: неа, это явно другой баг, т.к. в 2.3.1-5.el5_5.1 оно НЕ пофиксилось. Но в RHEL6/SL6 проверить всё равно надо -- тем более, что там OpenMotif есть в дистрибутиве.
"&1 -- подсвечиваться, &2 -- disable'иться"вместо былого
"!=0 -- подсвечиваться"
Замечание: это может где-нибудь вылезти боком, особенно со старыми отокаревскими драйверами.
С другой стороны, ненужный и давно забытый LOGD_SCALE идет как раз за блоком "LOGD_*LED". Ну что -- займем этот код под LOGD_BLUELED?
22.09.2004: так и сделал.
26.09.2004: кстати, доописал его в doc/lib/Knobs.html.
LOGD_ALARM
совершенно неприспособлена к тому, чтобы быть r/w -- по нажатию на себя оно
значение прописывает, но ShowAlarm()
не вызывает, причем это
справедливо как для включения, так и для выключения.
27.09.2004: вообще-то к этому можно отнестись и так: ALARM -- это виджет, предназначенный ТОЛЬКО для отображения, и делать его R/W нет никакого смысла.
А на случай, если все-таки захочется пофиксить -- причина такого
"недоросльского" поведения совершенно понятно: значение
XmNset
меняется в двух разных местах -- в
ChangeCB()
и в SetValue_m()
, а проверка на
смену значения и вызов ShowAlarm()
имеется только в
последней.
Попробовал вставить в ChangeCB()
принудительный
"возврат" в текущее значение перед вызовом
SetControlValue()
(которое бы и инициировало
контролирующийся переход) -- хрен, что-то не срослось, глючит еще пуще
прежнего: оно умудряется вызвать ShowAlarm() с параметром onoff=1 на
один раз больше, чем с onoff=0, и так навечно залипает в бибикающем
состоянии.
28.09.2004: вообще-то по здравому размышлению
стало ясно, что вместо всей этой алхимии надо "вернуться к корням". А
корни проблемы -- в том, что проверка смены состояния ведется путем
сравнения текущего и "делаемого" значений XmNset
. А
XmNset
трогается не только нами, но еще и Motif'ом.
Таким образом, проблема решится, если поддерживать САМИМ
текущее (точнее -- "предыдущее", для SetValue_m()
)
состояние, и сравнение производить с ним.
Для сего придется завести privrec.
08.11.2004: (реально -- неделю назад) вроде бы вся алхимия с alarm'ом теперь перекинута на Cdr, так что и эта проблема должна была решиться. Надо проверить.
08.11.2004: проверил. Действительно, все ОК. Единственная оставшаяся проблема -- с ONE_OF_MANY_ROUND, но это уж баг Motif'а. Так что -- помечаем как "done".
А что, если в таком случае ставить при создании
XmNlabel="пробел"
, затем вычитывать
XmNindicatorSize
, а потом ставить метку в ""?
Судя по man'у, после первого же прямого указания
XmNindicatorSize
он перестает вычисляться автоматически на
основе метки.
29.09.2004: попробовал. Работает. Хотя и несколько не совсем так, как следовало бы.
"Не совсем так" -- из-за того, что если уставлять
XmNindicatorSize
в то же значение, что уже есть, то ничего
не происходит. Это потому, что замечается не наличие его в списке
параметров, а именно ИЗМЕНЕНИЕ -- в
ToggleB.c::SetValues()
стоит проверка
Так уж устроены Xt/Motif. С аналогичным приколом уже приходилось сталкиваться и мне -- в инспирированных Гусевым разборках с XmForm на тему изменения размеров окна и включения/отключения графиков (см "Chl:05.05.2004"), и Антонову -- что для дерганья geometry manager'а приходилось щелкать borderWidth'ом.if (newcbox->toggle.indicator_dim != curcbox->toggle.indicator_dim) { newcbox->toggle.indicator_set = TRUE; }
Я решил эту проблему "рекомендованным" же способом -- перед общим
большим модифицирующим XtVaSetValues()
отдельным вызовом
делаю XmNindicatorSize:=1
.
И второе -- при пустом тексте надо еще ставить
XmNspacing:=0
, чтобы справа лишнего "холостого" поля не
выделялось.
30.09.2004: еще один ляпсус: почему-то при отсутствии метки высота всего агрегата становится меньше на 1 пиксел (сверху). Что-то с margin'ами? Опять искать ляп в Xh_fallbacks.h?
А, нет! Дело в том, что при оно всегда пытается увеличить
XmNmargin{Top,Bottom}
так, чтобы имелось место под
индикатор. И при отсутствующей метке (точнее, при ее высоте в 0
пикселов) оно увеличивает их размер (в данном случае) с 2 до 8. Но
результат этот не тождествен тому, что будет при НАЛИЧЕСТВУЮЩЕЙ метке.
Временно вместо пустой строки ставим пробел, при
XmNspacing:=0
бесполезное поле справа при этом маленькое,
а вообще-то надо б подумать, как бы еще покорректнее пошаманить со
всякими высотами, чтобы ToggleButton был доволен.
01.10.2004: сделал-таки "по-умному": высчитываю
высоту строки из одного пробела при имеющемся fontList'е, и добавляю ее
половинки к maringTop'у и marginBottom'у. Причем, добавляю не просто
половинки, а "корректно": mt+=lh/2; mb+=lh-lh/2; -- т.е., добавляется
именно всегда полная высота, один нечетный пиксел из-за деления на
двойку НЕ теряется. Все ресурсы, включая label:="", ставятся в едином
вызове XtVaSetValues()
.
Итого -- теперь все в полном ажуре.
(Понадобилось -- в phm-tsyline.c, поскольку от обычного
XmRowColumn-based radiobox'а там толку все равно никакого, приходится
делать весь менеджмент руками, а еще и создать
XmToggleButton
без метки -- отдельная проблема...)
23.04.2005: введен новый код --
LOGD_RADIOBTN
. Произведена его регистрация везде, "где
надо" -- сделана функция RADIOBTN_Create_m()
, создана VMT,
проставлены ссылки в Knobs_widgetset.c и
Knobs_simple.c.
Вставлена поддержка в собственно DoCreate()
--
LOGD_RADIOBTN
создаются синенькими и круглыми. Ради
круглости даже исправил старый хак, что круглыми делались те, которые
KNOB_B_IS_ALARM
-- теперь оно смотрит по
the_look
.
В phm-tsyline.c уже используется.
Все вроде ОК, вот только подглючивает оно как-то -- из-за круглости, что ли: почему-то можно выбранную кнопку и отключать тоже...
Разобрался в причине: мешает "протокол wasjustset". Пришлось в
phm-tsyline.c ставить два вызова SetKnobValue()
подряд -- функция SetKnobValueSure()
:-).
А сам-то новый knob-виджет отлично работает, так что помечаем как "done".
06.05.2005: так, для галочки -- проблема с "wasjustset" была исправлена еще 24.04.2005.
06.05.2005: переделал DoCreate()
так, чтобы LOGD_RADIOBTN
он создавал не круглым, а
ромбовидным.
SetValue_m()
что XmNset:=is_set?XmSET:XmUNSET, а в
ChangeCB
-- v:=info->set==XmUNSET?0:CX_VALUE_LIT_MASK.
CX_VALUE_LIT_MASK
.
А сегодня прикинул: если делать замену для олеговых "maha" и "odgirl", переключающих каналы для гусиных adc200 (gussel), то там надо ОБЯЗАТЕЛЬНО иметь разноцветные radiobutton'ы -- синие и красные (для разных линий графиков).
28.05.2006: сделал. Ввел нормальный psp-парсинг
поля widdepinfo
, и там lookup-параметр
color={default|green|amber|red|blue}. Заодно, кстати, флажок
panel также перевел на PSP.
10.04.2007: для повышения полноты (а точнее --
для tsycam'а) добавил еще цвет "black". Естественно, он актуален
ТОЛЬКО для alarmonoffled'а -- ни в button'ах, ни в actionbuttons ему
делать нечего. Ради него пришлось вводить
XH_COLOR_JUST_BLACK
:-).
SetValue_m()
.
10.06.2006: еще вчера заметил, что linmag почему-то постоянно жрет память. По 4K-странице в несколько секунд. В результате за неделю оно сожрало почти всю память.
Стал разбираться -- заодно обнаружил, что такое же поведение имеет место при зажимании кнопки Tab, либо при фокусировании/дефокусировании окна -- т.е., когда текстовые поля получают фокус. Но это, похоже, отдельный баг, и, к счастью, видимо, специфичный для CX/Xh/Knobs -- поскольку тестовые программы его не вызывают.
В конце концов, найдя при помощи ltrace на живой программе адрес,
откуда вызывается SYS_brk() (просто malloc()
она
почему-то не показывала, хотя он там и есть), приаттачил к той же
живой программе gdb, и поставил на этот адрес breakpoint. Обнаружилось
-- что в call chain'е перед malloc()
'ом значится
XmGetVisibility()
, про который известно, что у него
имелась утечка, пофиксенная в OpenMotif-2.2.3 --
bug#1134.
:-(
Казалось бы -- полная задница, хоть вешайся... Но: выше в цепочке
обнаружился
Knobs_alarmonoffled_widget.c::SetValue_m()
.
Следовательно -- можно ослабить эффект, если начать вызывать уставку
значения через XtVaSetValues()
ТОЛЬКО в случае, если
текущее значение отличается.
Так и сделал. Теперь memprof показывает, что утечек нету. Считаем, что типа "done".
(BTW, но это я лишь "регулярную" утечку так прикрыл; а если просто зажать стрелку вверх на наборе onoff'ов либо кнопок, то раз в 3-4 шага (!) отводится по новой 4К-странице. Но уж с этим ничего поделать нельзя.)
11.06.2006: слегка "хрен вам": разбирательство показало, что баг НЕ БЫЛ пофиксен в 2.2.3, а лишь в 2.3.0. Там вообще какая-то мистика с ревизиями -- детали см. в моем комментарии к тому же bug#1134.
12.06.2006: запустил программу с локально-откомпилированным OpenMotif-2.2.3 с фиксом, и -- утечки исчезли!!!
Далее же -- поскольку ситуация совсем непотребна, то написал на эту тему RedHat/Fedora bugreport #194843, с предложением выпустить update для pre-FC5-систем. Что интересно, как следует из #171053, в RedHat'овском 2.1.30 этот баг уже подфиксен, но только для RHEL.
Кстати, некоторый общеполезный результат от сей работы:
О средствах: то, чем пользуются "все нормальные люди" называется valgrind -- http://www.valgrind.org/. И он даже входит в состав дистрибутива системы начиная с FC3, а раньше был в contrib/libc6/.
А я же при поиске концов этой утечки долго пытался найти какой-нибудь программный инструмент для автоматизации поисков. Даже искал слово "leak" в выводе rpm -qip ПУТЬ/К/ДИСТРИБУТИВУ/*.rpm -- без толку. Точнее, нашел вообще какой-то непонятный mtrace, а главное -- memprof, при помощи которого и проводил анализ, но кнопка "leak" там как-то малополезна, а под FC3 оно вообще не желает работать.
26.06.2006: писец -- багрепорт #194843 куда-то исчез, говорит "Invalid Bug ID"/"Bug #194843 does not exist.". И куда делся?!?!?!
29.06.2006: спросил у notting'а и twoerner'а, в чем же дело -- ну Bill Nottingham и ответил, что The bugzilla DB went kablooie on 2006-06-13, losing information filed between 2006-06-08 and then..
Посему переизготовил багрепорт -- #197193 "Memory leak in XmGetVisibility() still present in 2.2.3".
29.07.2006: для этого в ChangeCB()
просто был добавлен дополнительный if()
(весьма
монструозного вида), который для KNOB_B_IS_ALARM
-ручек,
при горящести alarm'а вызывает
ki->uplink->emlink->AckAlarm()
.
Заодно обнаружил, что в ChangeCB()
при восстановлении
значения XmNset
в !ki->is_rw
-ручке
уставлялось в просто ki->curv!=0
, вместо "кошерного"
(((int)(ki->curv)) & CX_VALUE_LIT_MASK) != 0 ? XmSET : XmUNSET
Поэтому вытащил хитрое выражение, которое !=0
, в отдельную
функцию IsLit()
, и везде, в т.ч. в
SetValue_m()
, теперь используется она.
И вылезла некая проблема -- почему-то нажатие не всегда отрабатывало
как acknowledgement... Разборки показали, что дело опять в Motif bugs
#1188/#1154 (в OpenMotif-2.2.3 все окей). Так что -- для версий
<2.2.3 дополнительно уставляется обработчик
XmNdisarmCallback
, содержащий тот же самый
if()
. Теперь все работает.
31.07.2006: сделал, что теперь ЛЮБОЙ
led/onoff-подобный понимает указание цвета. Техника -- оно выбирает не
цвет, а его селектор (XH_COLOR_nnn), ставя для ONOFF'а по
умолчанию -1
, и уставляет
XmNselectColor:=XhGetColor(selcol)
только при
selcol>=0
.
XmNindicatorOn==XmINDICATOR_CHECK_BOX
. Полезно для
включателей индивидуальных каналов в adc200.
28.08.2006: а надо ли? Реально -- эти хреновинки всегда рисуются цветом foreground'а...
И, в любом случае, если делать -- то, наверное, дополнительными widdepinfo-ключиками, а не отдельным типом отображатора. Тогда можно и всякие CROSS поддерживать...
11.09.2006: да, нафиг реально не надо. И не только из-за принудительного использования цвета foreground'а, а еще и из-за того, что и просто обычное указание цвета (в widdepinfo-параметре "color") имеет тот же самый эффект -- все равно цветные маркеры отображаются ТОЛЬКО во включенном состоянии.
26.02.2007: сделано следующим образом: в
widdepinfo можно указывать параметр victim=ИДЕНТ, где ИДЕНТ --
имя (поле ident
) ручки-"жертвы".
В DoCreate()
делается поиск по
ki->uplink->content[]
(к моменту рождения ручек вся
иерархия уже создана, так что ссылаться можно и на следующие ручки), и
если жертва находится -- то ссылка на нее сохраняется в
XmNuserData
(угу, widget_private
у
лампочкообразных пока не используется).
В SetValue_m()
же при смене состояния делается
XtSetSensitive()
для жертвы.
Пока единственный баг -- если при запуске из сервера считывается 0 (XmUNSET), то disable жертве не делается, поскольку предыдущее помнимое значение -- также XmUNSET. Пока забиваем, считая это за "фичу".
"Done".
03.03.2009: во-первых, вставил, что при ненайденности victim'а оно выдает ругательство на stderr. А то при опечатке просто ничего не работало, а понять, в чем дело -- было почти нереально.
Во-вторых, поправил тот баг, что при старте со значением 0 не делался disable. Очень простым и элегантным способом -- теперь ВСЕМ alarmonoffled'ам при создании ставится XmNset:=XmINDETERMINATE, так что оно не равно ни XmSET, ни XmUNSET, и при первом вычитывании уставляемое значение всегда не равно текущему. (Да, если юзер успеет щелкнуть -- то какое-то значение будет, но тут уж сам дурак. Хотя по-хорошему надо бы ввести флажок "жертва потрогана" :-).) И в 4cx/ too.
03.03.2009: часом позже -- наткнулся на незамеченный ранее баг: если быстро щелкнуть два раза, то состояние disable'нности victim'а рассогласовывается с состоянием выключателя. Видимо, следствие взаимодействия механизма wasjustset с проверкой is_set!=cur_set... (Оно, конечно, приводится в чувство еще одним переключением, но все же...)
04.03.2009: как все-таки исправить последний баг? А очень просто: надо влиять на жертву при ЛЮБОМ переключении выключателя -- и по приходу данных, и сразу по нажатию юзера. В таком случае очевидным образом гарантируется соответствие состояния выключателя и жертвы. Дополнительный бонус -- пропадает проблема "...юзер успеет щелкнуть...", да и никакой флажок "жертва потрогана" уже не нужен.
Так и сделал. Махинации над жертвой вынесены в отдельную функцию
SetVictimState()
, вызываемую из обеих точек. На вид
проблемы исчезли.
4cx/ too. Там, кстати, УЖЕ имелся флажок
alarmonoffled_privrec_t.cur_set
, который изначально
ставится в -1
, так что там использование XmINDETERMINATE
теперь стало окончательно излишним :-). Хотя разнообразие, вносимое во
внешний вид, вполне оправдывает его использование.
KNOB_B_IS_LIGHT|KNOB_B_INCDECSTEP_FXD
. Там даже
B_IS_LIGHT не было!!! (С другой стороны -- отдельный вопрос: а ЧТО
такое B_IS_LIGHT, и зачем он нужен? В bigfile'ах на эту тему ничего
внятно не сказано...)
08.05.2009: как вариант -- можно ввести параметр
"offcol", который бы влиял на XmNunselectColor
.
Отдельный интересный вопрос -- а как это будет работать в варианте с
panel?
Получасом позже: да, сделал, совершенно тривиально. Работает. И с panel проверил -- тоже работает как надо, расцвечивается нужным образом (правда, состояние INDETERMINATE игнорирует -- воспринимает как UNSET, но это неважно).
И в 4cx/ too. Засим -- "done".
08.05.2009: да, ввел еще один параметр shape, со значениями default, circle и diamond; вариант "square" делать не стал. Работает нормально, кроме стандартной проблемы с circle, когда дурит выбор (не развыбирается); но круглые и нужны-то ТОЛЬКО для лампочек.
Традиционно, 4cx/ too; "done".
12.11.2010: что-то всё-таки не слава богу с этим circle: в liucc они иногда подглючивают, и почему-то оно выглядит как 0, в то время как в ручке значение -- 1.
Возможно, какие-то гадостности с radio-behaviour, которое в Motif'е определяется именно формой, XmONE_OF_MANY_ROUND. В 4cx/-то проблем меньше -- там текущий вид не берется из виджета, а помнится в privrec'е
А то сейчас на сварке актуально зеленение по пропаданию блокировок, но блокировки эти сделаны НЕ LOGD_ALARM'ами, а обычными LOGD_GREENLED с "offcol=red,shape=circle" (и там вообще alarm -- при ==0, а не ==1).
Вопрос причем очень глубокий, касается НЕ только alarmonoffled'а, а скорее Cdr'а, и в CXv4 тоже должно быть сделано в этой же струе.
25.07.2009: решение очевидно: надо делать ДВА РАЗНЫХ behaviour-бита:
В CXv4 -- практически то же самое, с поправкой на тамошнюю более streamlined/straightforward архитектуру.
P.S. А то, что на сварке alarm при ==0 -- ну надо приводить к стандартному, при помощи phys{r=-1,d=-1}.
25.08.2009: авотхрен -- там privrec'а-то нету...
25.08.2009: privrec-то, конечно, там надо бы завести.
Но вот как именно реализовывать это "залипание" лампочек -- отдельный вопрос. Есть куча возможных сценариев. И, с другой стороны -- а насколько это РЕАЛЬНО надо?
По-хорошему -- надо бы реализовать предыдущий пункт, с отдельным RELAXER'ом, и тогда его будет как раз вполне достаточно.
(Понадобилось для liucc -- чтоб сделать там мелкие panel'ы статуса БЗ1 и БЗ2)
16.05.2012@Снежинск-каземат-11: мелкий хак в продолжение -- делаем, чтобы для размеров (size) меньше normal тень лампочки становилась вдвое меньше.
Халтурновато, конечно -- во-первых, вообще что отсюда лезем в компетенцию Xh_fallbacks.h, а во-вторых, как делается проверка: что "size<=10". Уполовинивание делается только при толщине>1.
11.12.2010@Снежинск-каземат-11: сделал:
Недостатки/разборки на будущее:
И уж в 4cx/ оно НЕ идёт -- пока не будет причёсано до неотвратного вида.
14.09.2004: стал лечить. Первое побуждение -- применять к incdec-виджету (если наличествует) те же самые цвета -- оказалось неразумным: у поля ввода-то фон белый, а у стрелок такого быть не должно.
Второе побуждение -- ввел в text_privaterec_t
еще пару
полей -- incdec_def{fg,bg}
, и с ними производятся
махинации аналогично обычным wdci.{deffg,defbg}
.
Помогло -- и в widgettest'е даже стрелочки из черных стали голубенькими.
DoCreate()
после XtMalloc()
не делалось
bzero()
. Из-за этого стало валиться в core после введения
поля grpmrk
. Поправил.
11.04.2005: А по-хорошему надо б сделать аудит
ВСЕХ мест, где вызывается XtMalloc()
.
Чуть позже: да, навел аудит. Везде все окей --
Knobs_text_widget.c был единственным проблемным местом. (Есть
еще пара мест, где результат используется под строку, а не под
структуру, но и там сразу же делается str*cpy()
, так что
все корректно.)
Естественно, не безусловным порядком визуализировать
ki->label
-- это бы крупно сломало нынешние программы, у
которых часто метка стоит при первом виджете строки -- не используемая
им, но, по "?" копируемая в заголовок строки элементом.
18.04.2005: да, очень, очень этого хочется. Сейчас -- для программы istc/xmclients/phm-tsyline.c. Оно там очень уж удобно для вложенных многоколоночных элементов.
Как это сделать не-по-умолчанию -- просто, иметь в widdepinfo флаг, например, "withlabel".
А по-хорошему -- пора бы уже ВСЕГДА использовать privrec, и отказаться от хитрой идеологии, что для не-decorated-виджетов privrec не используется, неудобна она!
Так, шальная мысль -- а не выпендрится ли и не сделать, чтобы для readonly-полей label клался бы прямо в само поле, перед числом?
Неа, не стоит -- много хлопот доставило бы такое решение.
Итак, часом позже: все сделано. Введена пока что просто
strcasecmp()-проверка widdepinfo
на тему
"withlabel". И вставлена архитектура "линейного заполнения
слева направо" с переменной left
.
Кстати, после проверки многострочных меток пришлось вставить
форме-контейнеру XmNshadowThickness:=0
.
Вообще, многое было взято из Knobs_slider_widget.c --
благо, они теперь внешне очень похожи. И есть у них общий вопрос --
какой делать alignment у метки? По образцу selector'а сделал
XmALIGNMENT_BEGINNING
.
Также избавился от старой алхимии с условным заведением
privaterec'а. Существенно упростило код :-). И для корректности
переименовал поле input
в text
.
Испытано на phm-tsyline. Работает, и оно очень удобно.
Что ж -- фича, казавшаяся не слишком очевидной, дошла до состояния "done" довольно быстро.
(Страшным голосом, шепотом: надеюсь, ТУТ мы не захотим делать параметр "layout"? :-)
06.05.2005: в связи с обобщением механизма
"groupable" добавил в DoCreate()
нормальный psp-парсинг.
27.08.2006: (еще более страшным голосом) а параметр "layout" добавить-то придется! Для использования на canvas'е -- чтобы автоматом ставилась метка над числом.
11.05.2005: в конце концов было провернуто то,
что давным-давно напрашивалось -- теперь CreateTextValue()
и CreateTextInput()
не сами создают виджет, а вызывают
единую функцию MakeTextWidget()
, каковая его и создает
унифицированным образом, в зависимости от параметра rw
лишь меняя кое-какие параметры.
Именно в нее и переехали все "проблемные" определения из Xh_fallbacks.h.
Ага, и вылезло в других программах -- istcc.c, phm-tsyline.c -- курсоры там видимы, и поля traversable... Впрочем, это уж их проблема, и в них должна исправляться, что и сделано.
10.02.2006: да-да, и метку расцвечивать тоже забыли!!!
23.05.2006: сделал -- теперь форма колоризуется.
Заодно нашел баг, что полю form_w()
никогда не уставлялось
значение (из-за чего при расцвечивании получали SIGSEGV) -- поскольку в
DoCreate()
имелась собственная переменная
container
, на которую только и делались ссылки.
И метка также колоризуется.
Внешний вид теперь стал более целостным -- ручка смотрится как единое целое. "Done".
P.S. А почему форма не колоризовалась -- ответ очень простой и касается эстетики: текстовое поле в колоризованном варианте теперь имеет однопиксельную обводку, что выглядит менее "красиво", чем когда оно, со своими 3D-тенями, словно "висело" само.
Ну что, сделаем? Это ж тривиально!
08.06.2006: типа проект.
SetControlValue()
, и потому загрузка режима --
не влияет.
TextChangeCB()
в дополнение к
SetControlValue()
"вдвигаем" число в эту FIFO.
TextUpDownHandler()
ловим также Ctrl+Z и
Alt+Backspace, и при их появлении делаем "Undo".
CancelInputEditing()
. А одно -- ну и фиг с ним, брать это
одно число. (Это почти то же самое, что Esc, но если число менялось
еще кем-то -- то запомнено будет другое.)
Вечером, несколькими часами позже: сделал, ровно по вышеприведенному
проекту. Внутренние функции StoreUndoValue()
и
PerformUndo()
. Поля в knobinfo_t
поименованы
undo_fifo[]
и undo_fifo_used
.
"Ответ 1": 0 -- ничего не делает, иначе -- возвращает к
undo_fifo[undo_fifo_used-1]
.
"Ответ 2": для эмуляции Гусева стрелки вверх/вниз работают в точности, как ручной ввод числа с последующим [Enter] -- т.е., undo делается на 1 шаг назад.
В общем -- "done".
20.03.2007: неа, вышло совсем даже не совсем done -- две проблемы:
Ну эта-то проблема решилась просто -- в PerformUndo()
вставлено undo_fifo_used--
и "обратный сдвиг стека"
[0]=[1].
А вот что делать с первой проблемой...
Вообще -- это я просто явно лоханулся тогда с разработкой модели, ведь fifo из ДВУХ элементов (из которых второй -- мимо кассы) уже должно было насторожить!
Правильная-то модель такая: в
StoreUndoValue()
надобно запоминать то число, которое
имеется в этот момент в ki->curv
и взводить некий
флажок "ki->undo_val_saved".
Так что -- переделываем!!!
Несколькими минутами позже: переделал. Работает. Во всех интересующих ситуациях работает. И, что характЕрно -- реализация на порядок проще первоначальной, безо всяких там FIFO. Еще одна отсылка к выводу от 21-02-2006/22-02-2006 на тему "первым делом в голову приходит НЕоптимальное решение"/"если проблема сходу красиво не решается -- отложи ее, решение придет само"...
Вот теперь -- действительно "done"!
Причем: эта утечка имеется, даже если оставить ОДНУ ручку LOGD_TEXT. А вот в тестовой программке из Brain Motif -- никакой утечки не наблюдается...
10.06.2006: разборки показали, почему в Brain Motif никаких утечек нет: у нас эти утечки имеются при наличии флажка "groupable" -- т.е., опять же в присутствии XmToggleButton'а.
Еще более дальнейшее разбирательство -- с отключением всех подряд
callback'ов и event-handler'ов показало, что все дело -- в
XhTextSetCursorCallback()
, который приводил к дерганью
ресурса XmNcursorPositionVisible
.
Вот только ТУТ его поймать на XmGetVisibility()
не
удалось. Или это другой memory leak?!
10.06.2006: при запуске с локально-собранным libXm.so от 2.2.3 с фиксом для этого бага -- данная утечка также исчезла.
Так шта, несмотря на то, что поймать нам ее не удавалось (ну да,
ловил не вполне тем способом, и в результате на SYS_brk()
всегда попадался кто-то другой -- ну совпадало так), утечка это та
же самая.
"Done".
15.02.2007: да, для началу сделал флажок "compact" -- ибо захотелось в ndbp иметь как раз метку-сверху-числа.
16.02.2007: а поскольку никакие другие варианты с текстовым полем и не нужны, то закрываем данный вопрос и считаем за "done".
03.11.2007: да, сделал. Детали:
XH_COLOR_INGROUP
,
отображающийся на "#82D2FF".
02.01.2008: и в 4cx/ также это все портировал.
03.03.2009: да, сделал. Прилепляется ТОЛЬКО для
rw-ручек и с непустым ki->units
; причем, надо
ОБЯЗАТЕЛЬНО указать "withunits" в widdepinfo. И
колоризуется тоже.
04.03.2009: и в 4cx/ too. Там при наличии оно приделывается по умолчанию, а для отказа надо указать в options флаг "nounits".
Суть в том, что при надобности добавить к уставке, например, 30, не надо делать вычисления в уме, а просто прямо в поле к уставке дописывается "+30", и оно само вычисляет нужное значение. А еще можно писать "*0.9" и тому подобные удобности.
31.03.2014@Снежинск-Снежинка-504(ночь): в принципе, сделать можно сравнительно просто (и без привлечения всяких библиотек, как предлагали Стенка+Фатыкин):
Достаточно вExtractDoubleValue()
чуток добавить интеллекта -- чтоб он послеstrtod()
вместо требования обязательного конца строки смотрел бы, если там дальше идёт знак арифметической операции -- +,-,*,/ -- то попробовать от-strtod()'ить еще одно число, и его сочесть с первым указанной операцией.А в случае + и - разрешить еще указывать в конце числа %.
И в 4cx/ не забыть скопировать!
16.06.2014: сделано.
22.07.2014: считаем за "done".
22.04.2004: разобрался. Проблема появилась при
добавлении PropsWindow, когда всем подвиджетам чохом делалось
HookPropsWindow()
. А прикол в том, что у dial'а подвиджет
"title" может и не создаваться, в результате оно передавало w==NULL.
Вывод на будущее: тщательнЕе надо, тщательнЕе!
11.05.2005: угумс, "done".
KNOB_B_HALIGN_FILL
), и пытались бы стать квадратными?
29.08.2005: попробовал самооквадрачивание на тестовой программе. В "самодельном" варианте -- на чистом Motif, ~/work/motif_tests/test_drawresize.c, что-то не пашет. А вот на базе Xh (с его хитрыми вложенными формами и "правильными" их аттачментами) -- вполне, хотя и подглючивает в ситуации, когда окну делается zoom; видать, какие-то запросы там не удовлетворяются, и форма подсходит с ума.
30.08.2005: пообщался с Антоновым. Первая половина резюме -- "фиг знает", вторая половина -- "скорее всего, работать будет".
Маленькое замечание: чтобы не возникало цепной реакции (пример -- в
квадрате 2x2), следует ввести флажок, который взводить перед
XtVaSetValues(,XmNheight,...)
и сбрасывать после него, а
при входе в callback проверять его. Как вариант -- сделать не флаг, а
счетчик, чтобы, например, 5 итераций оно б допускало.
31.08.2005: ну попробовал, по вышеописанному сценарию -- со счетчиком до 5, включается флагом "square", и что? Глючит оно, по черному! Причем глюк -- явно антоновской сетки.
16.09.2005: да, у Антонова глюки, конечно, были, но... Основная проблема оказалась в XmForm, которая к подобным фокусам относится крайне никак, и попросту даже не пытается замечать, что ее содержимое хочет ресайзиться. Это легко проверить в тесте ~/work/motif_tests/xh_drawresize/forcedresize.c, причем в зависимости от наличия либо отсутствия там виджета junk она глючит по-разному...
Так что -- withdrawn... :-(
17.09.2005: в принципе-то, конечно, хреново, что форма такая мерзкая, но...
Во-первых, возможно, что-то и придумается, как обдурить форму, либо сделать с другими Motif-виджетами, без применения формы.
Во-вторых, все прекрасно можно сделать на сетке. В 3 строки: 1 -- опциональный заголовок, 2-- крутилка, 3 -- опциональная строка со стрелками и числом. Строку, которой требуется хитрая геометрия -- чтобы число растягивалось -- можно сделать формой, с такими же аттачментами, как сейчас.
Ну а в-третьих -- ну хватит уже возиться с этими крутилками! Ну не пользуемся же мы этой типа красотой почти никогда!!!
Хотя сейчас пообщался со Старостенкой на тему "а нужны ли когда-нибудь крутилочки и каков их глубокий смысл".Во-первых, в его возлюбленном LabWindows есть 2 вида крутилок: диск с точкой, за который где мышью возьмешься, от того места смещением он и крутится, и стрелочка, которая прыгает. Можно у нас так и сделать --
LOGD_KNOB
двигается, аLOGD_DIAL
-- прыгает, как сейчас.Во-вторых, глубокий смысл их использования -- в наглядности. А) когда изменяемое значение по смыслу есть нечто в градусах (какой-нибудь поворот, аль фаза, как у нас в subharmonic); b) в случае "мощности" -- например, мощность клистронов -- тогда отклонение стрелки от максимума хорошо заметно.
Так что -- крутилочки-то мы поддерживать будем, но они -- скорее second/third-priority.
01.09.2005: да, поэкспериментировал -- и
выяснилось, что события-то FocusIn
оно в вышеописанной
ситуации получает (хрен знает почему, кстати -- видимо, какие-то
последствия умолчательного FocusFollowsMouse), а вот
FocusOut
-- нет.
Может, ну ее нахрен, XmDrawingArea
-- перейти на
XmDrawnButton
, который НЕ manager, а?
02.09.2005: Кстати, у DrawingArea есть еще какой-то inputCallback, который вроде как тоже связан с фокусом.
17.09.2005: что обидно -- у XmBulletinBoard есть
XmNfocusCallback
(хотя аналогичный для УХОДА фокуса
почему-то отсутствует), а у XmDrawingArea -- нету...
Заглянул в lib/Xm/BulletinB.c -- там в том, как это реализовано, черт ногу сломит...
13.08.2007: собственно, а как бы нас спасла
XmDrawnButton
? У нее-то ведь тоже
focusCallback/losingFocusCallback нету! Разве что отнаследовать от
нее, но зачем? Тогда уж сразу от XmDrawingArea.
01.07.2008: нас спасет XmFocusAwareManager -- см. раздел по нему.
19.05.2009: побудительным мотивом стала программа nliustend -- где, в стиле Леши Панова, LOGD_DIAL используются для отображения времени работы. А с полем, прилепленным слева (а не по центру под циферблатом) это выглядело уродски.
Так что сделал горизонтальное центрирование по образцу slider'а -- через Attachment=ATTACH_POSITION, Position=50, Offset=-wid/2. Пара замечаний:
(Хотя, в принципе, проблема решаема -- помещать val_w в дополнительную формочку, которую аттачить как раньше, а центрировать уже внутри неё.)
21.05.2009: всё оказалось просто -- там при создании циферблата делается XmNsensitive:=ki->is_rw. И даже нашел упоминание об этом в bigfile.html за 24-03-2003, из которого ясно видно, что это не что-то глубоко обусловленное, а просто халтура.
Так что сделал "по-правильному" -- вместо XmNsensitive в is_rw
уставляется теперь XmNtraversalOn, а в обработчиках клавиатуры и мыши
вставлена проверка: при !is_rw просто сразу делается
return
.
Но вылез другой прикол: теперь может рисоваться кружок
сфокусированности -- и это у non-traversable widget'а!!! Ну чо,
лишний раз убеждаемся, что вся эта алкомахинация с ловлей фокуса "на
коленке" -- халтура, шитая белыми нитками. Но для устранения эффекта
просто вставил проверку на !is_rw и в FocusHandler()
.
Пятью минутами позже: выкинул эти проверки, а теперь просто все 3 обработчика (фокус, клава, мышь) вешаются только при is_rw.
Теперь ведет себя пристойно, так что -- "done".
P.S. А всё потому, что dial'ы использовались только в subharmonic'е, а ro-dial'ы -- вообще нигде!
21.05.2009: да, сделал по-простому -- вставил
проверку, что если cur_v(ki)==v
, то ничего не делать.
Хотя вещественные числа на равенство вроде и нельзя сравнивать, но в
данном случае -- во-первых, числа из одного источника (и, проходя одну
цепочку арифметики, просто ДОЛЖНЫ совпадать и побитово), а во-вторых,
максимум, что может случиться -- ну перерисуется лишний раз, а это
нефатально.
И -- просто ничегонеделанье при совпадении дало побочный эффект:
при нулевом значении текстовое поле как было "------", так и оставалось
дальше. Так что переставил проверку, что именно ПЕРЕРИСОВКА делается
только при v!=old_v
.
Замечание на будущее: если будет делать расцвечивание и диска
тоже, то надо не забыть вставить вызов DialDraw()
в
Colorize_m()
-- поскольку если раньше оно бы просто
перерисовалось на следующем цикле, то теперь -- уже фигушки.
13.01.2005: И еще -- чтобы можно было иметь
"метку поверх сепаратора".
14.01.2005: реализованы в простейшем варианте -- пока просто сепараторы, без метки, в Knobs_sep_widget.c. Вариант именно простейший -- возможности указать тип сепаратора нету (да и не нужна она, IMHO).
Оно ВСЕГДА выставляет флаги
KNOB_B_VALIGN_FILL|KNOB_B_HALIGN_FILL
, так что сепаратор
всегда растягивается до максимума и центрируется.
Во всех необходимых местах они зарегистрированы --
cxdata.h::LOGD_{HSEP,VSEP}
,
Knobs_{widgetset,simple}.c.
Да -- и декоративный виджет НЕ ДОЛЖЕН вызывать себе
HookPropsWindow()
!
16.01.2005: итого:
В принципе -- это совсем даже и не проблема: во-первых, может, оно так и к лучшему -- выглядит пристойно, а во-вторых, уж если припрет, то вполне можно сделать элемент с "norowtitles", тогда, при отсутствии сопутствующей метки, высота сепаратора сбрасывается до 4 пикселов (собственно shadowThickness=2 плюс свой highlightThickness=1).
(Если б у SGL была поддержка COLSPAN (а у Gridbox она есть --
gridWidth
, то можно б было ввести какой-нибудь
KNOB_B_-флажок, по которому уставлять координату на 1 меньшую, и
COLSPAN=2.)
12.11.2005: использовал LOGD_SEPARATOR в одноколоночном элементе БЕЗ меток строк ("norowtitles") -- отлично! (istc/xmclients/[lebed::]phm_db.c::"Пимпочки") На вид занимает ровно правильное количество места.
Несколькими минутами позже: елы-палы -- так у них highLightThickness не отключено!
Уставил его в 0 -- стало еще более правильное количество места :-)
13.01.2005: да, видимо, стОит иметь отдельный
виджет, безо всяких там сепараторов. Ибо сепаратор должен бы будет
(пока, по крайней мере) выставлять флажки
KNOB_B_HALIGN_FILL
либо KNOB_B_VALIGN_FILL
, а
метке это ни к чему.
И, как и сепаратор, его хочется иметь как обычным горизонтальным, так и вертикальным -- благо, поддержка вертикального текста у нас уже есть.
14.01.2005: в первом приближении сделано -- коды
LOGD_{HLABEL,VLABEL}
. И зарегистрировано везде, где надо.
Оно "косит" под "rowlabel", поскольку для того уже имеются все нужные fallback-ресурсы на тему margin'ов, а "ручка"-метка -- по смыслу почти то же самое, что и rowlabel.
Из минусов:
widdepinfo
.
24.01.2005: и даже тут же и исправил: подшаманил
XhAssignVertLabel()
(см. там комментарий за это число), и
сделал метод Colorize()
, который сначала вызывает
стандартный COmmonColorize_m()
, а затем -- повторно
дергает XhAssignVertLabel()
. И все стало ОК!
Раньше-то от этого проблем не было, а теперь, с введением оптимизации по "точкам изменений" -- метки навсегда остаются JUSTCREATED.
28.04.2011@Снежинск-каземат-11: короче -- пока отключаем у меток колоризацию, вставив в начало "return". Если где-то это вылезет -- будем разбираться.
Тогда б сходу решилась проблема разделенности программ отображения (tvtime, гусиный adc200) и переключения (camsel, gussel).
29.05.2006: соображения по теме:
XReparentWindow()
-- у
нас есть пара мест, где можно посмотреть: FvwmButtons (но он-то
работает модулем в кооперации с WM'ом) и plugger (у него есть директива
"swallow", которая, к примеру, захватывает gnumeric).
А можно ль как-то мониторировать факт изменения списка окон?
31.07.2006: а ведь можно не только захватывать, а еще и иметь параметр exec="command" -- чтобы в случае ненайденности "жертвы" в момент рождения knob'а запускать ее.
Замечание: в таком случае надо будет корректно уставлять той
программе $DISPLAY -- на случай, если сама программа запущена с ключом
-display ЧТО-ТО-ДРУГОЕ
(исп.
XDisplayName()
).
Да, а насчет "поиска" -- видимо, использовать код
SearchForWindow()
из cx-starter.c.
Соответственно, "жертву" указывать можно будет либо через
app_name=, либо через title=.
По смыслу это -- практически то же самое, что onoff или селектор на 2 положения, но вид должно иметь совсем другой: этакая "ручка", или "выключатель", как для комнатного освещения. Чтобы жмешь на одну его сторону -- включается (1), на другую -- выключается (0). Т.е., нечто вроде группы из 2 radiobutton'ов, только работающих как ЕДИНЫЙ объект (а не как сейчас у нас). В LabVIEW таких штучек -- хренова гора вариантов, разных видов и в разные стороны (хотя они поддерживают и просто "щелчок"-переключение, а не только детерминированные нажатия).
В Motif'е ничего подобного и близко нету. Так что -- придется делать вручную, через XmDrawingArea.
(Понадобилось для включения/выключения ("малютинизации") пучка на линаке -- там хочется иметь этакий "рубильник".)
18.01.2008: посмотрел, как аналогичные вещи сделаны в MEDM'е. Там есть объект, именуемый "Choice Button" -- он представляет из себя линейку кнопок, как на радио, из которых нажата может быть только одна; "нажатость" кнопки -- сменены тени и armed/baackground. По смыслу -- аналог селектора. Так вот -- если вариантов всего ДВА, то эта хрень становится как раз "двухпозиционным включателем".
Т.е., простейший ответ на задачу "как бы реализовать включатель?" -- это именно пара кнопок (работающие как единый объект), из которых в конкретный момент нажата только одна.
А вообще можно реализовать и более полный аналог этого "Choice Button'а" -- чтоб работал как селектор, но на кнопках. Highlights:
XhInvertButton()
.
22.01.2008: за последние два дня сделал. Highlights:
А если захочется сготовить "матрицу-поток" -- то надо будет внедрять XmRowColumn.
26.04.2008: начал использовать (в tantal'е), и заметил странность: размер у choice-кнопок отличается от оного у обычных кнопок...
Потом доперло: ведь я дал этим кнопкам имя-класса "choiceItem", и они НИКАК не корректируют свои геометрические параметры относительно motif'овских умолчаний -- ибо в fallback'ах про них ничего и нету. Заодно, кстати, они и НЕ следуют "протоколу CTL3D".
Решение очевидно -- теперь ставятся те же имена-класса "push_i"/"push_o", что и кнопкам. Сделал, работает.
12.05.2009: замечание: кнопки имеют одинаковый размер только ПЕРПЕНДИКУЛЯРНО основному направлению, а вот вдоль -- нет. Если припрет -- то действительно надо будет переходить на XmRowColumn. Но -- похоже, просто незачем. И даже более того: в принципе можно было б сделать на сетке, в т.ч. и многоколоночные. Тогда будет прямо противоположный упор: одинаковый размер станет уставляться только в ОДНОЙ колонке/строке, а в РАЗНЫХ он будет делаться "оптимальным".
Кстати, давно уже работает -- так что весь раздел ставим в "done".
16.03.2013@утро,душ: зачем нам для "потока" какой-то XmRowColumn?! Да вручную делать -- по той же технологии, что LRTB! Слегка муторно, но зато всё под нашим контролем.
12.05.2009: да, сделал. Довольно просто. И
теперь элементы "линейки" аттачатся не к [y-1]
, а к
prev
. И -- метка НЕ колоризуется.
10.05.2009: можно сделать по "проекту"
17-04-2009...25-04-2009 с указанием "модификатора стиля" через
дополнительный MULTISTRING_OPTION_SEPARATOR (нынче это '\f', а
в CXv4 -- '\b'). Причем, для CHOICEBS это будет проще --
поскольку достаточно просто при создании уставлять
XmNarmColor
, а дальше всё сработает автоматом.
Правда, как выяснилось, в нынешнем cx/ селекторами и choicebs'ами и ПЕРВЫЙ-то OPTION_SEPARATOR не поддерживается. Т.е. -- и тултип не прилепишь (в отличие от разного рода label'ов, включая col- и row-titles).
Так что -- надо добавлять в selector и choicebs (поскольку в них практически идентичный кусок кода) поддержку тултипов, а потом уж и per-item-"стилей". Так что результирующий формат пункта списка (пофигу, полученного ли от #T или #F) будет выглядеть как МЕТКА[\fТУЛТИП[\fСТИЛЬ]].
10.05.2009: поддержка в Knobs_choicebs_widget.c сделана -- довольно просто:
l_buf[]
с 200 до 1000.
MULTISTRING_OPTION_SEPARATOR
'а, с целью добычи
tip_p
и style_p
.
itemstyle_t
с таблицей
text2itemstyle[]
, куда и psp-парсится
style_p
(ради чего скопирована вся троица
wdi_*
из ParseWiddepinfo()
). Единственный
параметр lit=ЦВЕТ отображается на поле armidx
.
tip_p
он используется для уставки
тултипа.
itemstyle_t.armidx>0
, то он используется
для уставки XmNarmColor
.
Voila! А дальше даже колоризация работает автоматом.
11.05.2009: добавляем в
Knobs_selector_widget.c. Куски кода скопированы один-в-один
(с заменой XmNarmColor
на XmNbackground
). И
тултипы, и расцветка пунктов выбора работают. Хотя вот расцветка
текущего -- естественно, нет...
12.05.2009: замечание на будущее: в принципе можно и для заголовков столбцов и колонок использовать то же правило -- чтобы через 2-й OPTION_SEPARATOR мог бы указываться бы стиль. Другой вопрос -- а надо ли, и где? 20.05.2009: да, надо -- хотя бы ради флажка "folded".
12.05.2009: и в 4cx/ в
get_knob_item_n()
поддержку добычи опциональной строки
стиля добавил (там идет через '\b').
13.05.2009: и в селектор добавил реальное расцвечивание; детали -- см. в его разделе за сегодня.
А вообще -- работает же, так что "done".
18.08.2012: сделана возможность менять foreground кнопок, но из-за механизма раскрашивания -- только всех сразу. Длается это параметром color=, причём указывать его надо ПЕРВОЙ кнопке.
Сделано исключительно ради селекторов ADC200 на пульту. Халтурно, но возиться неохота.
28.05.2013: добавлена также возможность менять
размер -- параметр size=; реализация скопирована из
TuneButtonKnob()
'а (вместе с
bold/medium).
Механизм-то понятен: мы что-то писали в "setoff", а в "cur" вроде бы нет, и cda видит, что значение поменялось -- следовательно, зажигает OTHEROP. Ей-то неоткуда знать, что каналы связанные...
04.06.2009: с одной стороны, аналогичная штучка у
нас уже есть -- KNOB_B_IS_BUTTON
, и уставка этого флажка
сразу решит проблему. С другой же:
KNOB_B_IS_BUTTON
используется в ДВУХ разных случаях:
1-я-то проблема решается просто -- придётся сделать widdepinfo-параметр, указывающий на необходимость взвести флажок. А в 4cx/, соответственно, специальный тип/ключик при описании ручки.
А вот 2-я -- явно надо делить флаг на ДВА: B_IS_BUTTON оставить как указание "не возвращать значение в ручку при уставке", а создать еще "B_IGN_OTHEROP", который бы отвечал за срезание OTHEROP.
И тут есть еще пара аспектов:
CX_VALUE_DISABLED_MASK
, который должен лишь менять вид
кнопки, но никак не оранжеветь её. Так что -- такую оптимизацию не
делаем.
BTW, не забыть эту всю замуть отразить и в 4cx/.
16.12.2010@Снежинск-каземат-11:
ChooseStateRflags()
теперь использует
B_IGN_OTHEROP (и НЕ использует B_IS_BUTTON вовсе!).
*_Create_m()
добавлено
уставление KNOB_B_IGN_OTHEROP.
Внедрение IGN_OTHEROP в 4cx/:
choose_knob_rflags()
переведена с B_IS_BUTTON на
B_IGN_OTHEROP.
CONTENT_fparser()
::"button" и
MotifKnobs_button_knob.c::EquipButtonKnob()
.
"done".
28.12.2009: покамест сделан только "скелет" -- по
образу LOGD_HLABEL'а создаётся и колоризуется XmLabel; плюс, сделан
метод PropsChg()
-- поскольку именно ТАК будет меняться
текст метки. Но вот появление по правой кнопке мыши окошка для
изменения метки -- пока НЕ сделано.
29.12.2009: дореализовал штуку полностью, теперь
по правой кнопке мыши появляется окошко редактирования, и по [Ok] в нём
метка меняется (оно просто вызывает свой PropsChg()
--
через VMT!). Да -- оно поддерживает ТОЛЬКО однострочные метки.
Считаем за "done".
26.03.2010: самое противное -- это проявляется ОЧЕНЬ иногда. При запуске программы по X11 с wolf на экран viper'а -- всё окей. На самом wolf -- тоже воспроизводимость странная...
XhTextExtractIntValue()
с той же самой функциональностью,
что и XhTextExtractDoubleValue()
.
06.02.2021: в ней тогда почему-то в вызове
strtol()
было использовано значение base=10, из-за чего
шестнадцатиричные числа считаются синтаксической ошибкой.
Тут-то, в CXv2, уже ничего исправлять не будем, а вот в CXv4'шной
MotifKnobs_ExtractIntValue()
переделано на base=0.
XhVMakeMessage()
(с
va_list
-параметром вместо "...
").
18.04.2004: сделал, при этом собственно
функциональность перекочевала именно в XhVMakeMessage()
--
благо, подобный опыт с logline()->vlogline() уже был.
Заодно обнаружилось: там РУКАМИ имитировалась strzcpy()
-- strncpy(buf,...)+buf[sizeof(buf)-1]='\0'!
XhMake{,Temp}Message()
определены без
__attribute__((format,...))
. Надо исправить!
19.04.2004: добавил.
21.04.2004: благодаря этому добавлению обнаружил, что gcc, оказывается, умеет вякать "zero-length format string".
08.05.2004: вставил в Xh_fallbacks.h
"условные" определения размеров XH_FONT_BASESIZE
и
XH_FONT_TINYSIZE
-- при помощи #ifndef
.
В принципе, в будущем можно также сделать "настраивабельными" и имена шрифтов (fndry).
12.05.2004: ню-ню, такого решения мало!. При уменьшении шрифта становятся слишком большими межкананальные интервалы -- их тоже надо резать, а как?
07.03.2007: положим, не "fndry", а "fmly", и -- сделал настраивабельными. Так же, как и "жирность" (medium/bold). Смысл -- для эмуляции гусиного стиля, с везде-жирным Nes Century Schoolbook.
Единственное -- вылезла проблема: с Schoolbook-bold-14 почему-то невыровнены по baseline метки и поля ввода...
09.03.2007: с вылезшей проблемой -- вообще цирк какой-то: оно подглючивает НА ЛЮБОМ шрифте, если его уставить и как "*fontList", и как "*XmText.fontList" одновременно -- КРОМЕ lucidatypewriter, на ней все тик-в-тик (даже на lucida и lucidabright отклонения туда/обратно бывают).
08.05.2004: ввел
XhGridGetChildPosition()
. Для подстилающего виджета TABLE
эта функция не реализована -- там с этим вопросом большой зад.
04.06.2004: вообще-то давно, еще со времен
увлечения FVWM'ом, я знал, что баг заключается в том, что Motif
почему-то ВСЕГДА ставит в WMSizeHints флажок PPosition (а сами
координаты там и так (0,0)). В FVWM для этого даже есть для этого
config-ручка -- NoPPosition
.
Окей, полез разбираться... Рытье в исходниках как lib/Xm, так и lib/Xt дало мало -- запутанно там все. Конечный результат тестов: надо как-то срезать флаг PPosition, который, как я убедился, оно действительно зачем-то ставит.
Чтобы не возиться самому, полез в Google Groups -- и нашел
соответствующий thread:
initial window placement weirdness.
И там имеется ответ некоего Scott J. Tringali, где, помимо просто кода,
имелась ссылка на NEdit. Bingo! Итого -- в исходниках NEdit-5.2.2
нашлась функция
util/misc.c::RealizeWithoutForcingPosition()
,
каковую я скопировал в Xh_window.c. Теперь в
XhShowWindow()
вызывается именно она.
И наступило нам счастье!!!
AddMotifCloseCallback()
. Просто перенес все внутренние
функции-утилиты в начало файла и сделал их все static.
12.06.2004: разбираясь для Антонова с
XtRealizeWidget()
, обратил в его исходниках внимание на
упоминание "XtHooksOfDisplay()". И возникла у меня мысль -- это,
значит, оно некий hook вызывает при realize'ньи каждого виджета, так?
А что, если на это повесить присвоение красного курсора?
13.06.2004: угу, сделал. Изготовил пару xbm'ов
-- arrow32_{src,msk}.xbm, скопировав их из шрифта cursor и
увеличив вдвое, и вставил в
Xh_app.c::XhInitApplication()
привешивание к
XtNchangeHook
. А в ChangeHookCB()
при
надобности создаю этот "большой красный курсор", и если виджет --
подкласс Shell'а, то прилепляю ему это чудо (насчет Shell'а -- это
оптимизация/защита: 99.9% виджетов НЕ указывают себе курсор, и X-сервер
прется вверх по иерархии до первого указанного).
Причем вся эта функциональность заключена в
#ifdef TOUCH_CURSOR
, так что легко отрубается.
Единственная (полунадуманная) проблема: сей код НЕ-многодисплеен.
Несколькими часами позже, побывав на пляже и поразмыслив: а ведь
правильнее-то будет создавать курсор прямо в
XhInitApplication()
, и передавать его в
ChangeHookCB()
через client_data/closure.
Сделал.
Тут, конечно, есть свой подводный камень: bitmap'ы-то для курсора делаются на DefaultRootWindow, каковое, соответственно, указывает на DefaultScreen; а что, если приложению взбредет в голову часть окон открывать на другом screen'е? Впрочем, для нашего пульта это абсолютно неактуально.
И, кстати, мысля: ведь можно будет вытащить этот "умный"
код в отдельную небольшую bigredcur.so, и ставить ее всем,
запускаемым из cx-starter'а, в $LD_PRELOAD. А в ней сделать
свою XtOpenDisplay()
, вызывающую
dlsym(RTLD_NEXT,"XtOpenDisplay")(...)
, и потом вешающую
hook. Тогда большая покраснелость будет даже в гусиных программах.
Еще парой часов позже: кое-где все-таки так поставленный курсор не используется. А именно -- в меню (в т.ч. в селекторах) и, как показало разбирательство в man-страницах и исходниках Motif'а, в tearoff'ах. Собственно, можно, конечно, и для тех случаев решить проблему, но -- ведь эта красность -- просто махарайка, времянка, пока на пульту не сменится система и не появятся новые X'ы, в которых уже можно настроить ВСЕМ схему с большими и красными курсорами.
15.06.2004: еще дополнения:
XCreatePixmapCursor()
освобождение исходной пары Pixmap'ов через XFreePixmap()
.
#if
", а вместо этого засунул просто установку обычного
XC_top_left_arrow
, но с последующим
XRecolorCursor()
в красный цвет.
15.06.2004: еще часом позже: подшаманил-таки увеличенный курсор -- вручную "украсивил" его в xpaint'е, стараясь передать ту же "идею", "впечатление", "дух", что и в исходном курсоре, просто в увеличенном размере (пригодились навыки создания русских шрифтов -- деятельность по подгонке по пикселу очень похожая).
04.10.2004: еще дополнения: изготовил
bigredcur.so. Прекрасно работает и с гусиными программами, и
со всякими nedit, xclock, xsontsel, etc. Одно ограничение: не работает
с теми программами, которые делают exec чего-нибудь не-Xt'шного.
Пример -- xterm. Причина очевидна -- мы пытаемся подсунуть so-шку при
помощи LD_PRELOAD=.../bigredcur.so
, то она суется и
консольным программам, в которые влинковаться ну никак не может.
13.10.2004: в принципе, проблему нелинковабельности можно обойти хоть и муторно, но просто:
вызывать все потребные нам X/Xt-функции не напрямую, а
добывая их через dlsym()
.
25.10.2004: сделал, идея оказалась работоспособной. Муторно, правда, было вбухивать толпу описаний с параметрами, изымаемыми из man'а. Но получилось. Вылезла только пара неожиданностей:
_XtCheckSubclassFlag()
. Сделал соответствующий
символ и с-alias'ил на него искомое --
#define _XtCheckSubclassFlag ptr__XtCheckSubclassFlag
XtStrings[]
. Также пришлось
делать aliasing --
#define XtStrings ptr_XtStrings
А фишка-то в том, что XtStrings[]
-- это НЕ массив
указателей на строки, это просто строка -- куча подстрок,
разделенных '\0'. А определения-ссылки на этот массив -- содержат не
НОМЕРА строк, а их offset'ы. Как я понял, эта вся алхимия генерится
специальной программой из некоего исходного файла имен...
В общем -- можно начинать прикалываться над гусиными программами... :-)
01.07.2004: это, очевидным образом, следствие
того, что при щелчках по заголовкам элементов и колонок они
схлопываются и теперь окно меняет размер. Т.е. -- выставили
XmNallowShellResize=True
, вот оно стало и на statusLine
тоже откликаться.
Пофиксено тривиально -- при создании statusLine ему ставится
XmNrecomputeSize=False
.
02.07.2004: а ведь при этом практически исчезла (обратно:) возможность посмотреть, что прячется за правым срезом statusline. Разве что средней кнопкой мыши перетащить текст куда-нибудь.
Может, переделать statusline с XmLabel на XmText?
06.01.2005: "переделал", теперь он умеет
использовать и XmText. Это определяется в #define USE_XMTEXT
1/0
(0 -- использовать XmLabel). Что занятно, высота строки
уменьшилась на 4 пиксела -- оказалось, что у label'а стоит
XmNshadowThickness=2, а тень-то не рисуется.
По умолчанию сейчас стоит USE_XMTEXT=1
.
17.02.2005: ой, барашек... Забыл там при
создании XmText'а сделать XmNeditable:=True
. Исправил.
28.06.2006: не, ну воистину барашек! Это
выразилось в подкосе XhMakeTempMessage()
.
XhMakeMessage()
, умудрился забыть поменять в уставке
строки виджету mainmessage
на
tempmessage
.
XhMakeMessage()
для окна без statusline" (см.
bigfile.html), то про XhMakeTempMessage()
забыл.
Мда, самая "крупная" проблема с Xh_statusline.c за все время его существования :-). Так что на собственны раздел этот файл так и не тянет :-). Короче -- все исправил.
07.07.2004: даааа... На тему "*.collabel" в fallback'ах вообще ничего не было, так что мало того, что брались умолчания от XmLabel, так еще и shadowThickness=2.
Исправил -- скопировал строчки от rowlabel, опустив при том margin{Height,Top,Bottom}.
14.06.2012: сейчас скопировал и [почему-то] опущенные тогда margin{Height,Top,Bottom} -- иначе фигово смотрелось, не выравнивались collabel'ы с rowlabel'ом вмещающего элемента.
Короче -- теперь ВСЕ "места"-метки и прочие отображаторы заточены под одинаковые размеры, чтоб всё со всем сочеталось.
XhVMakeTempMessage()
, по аналогии с
XhVMakeMessage()
?
11.07.2004: сделал, стандартным уже способом --
вытянув функциональность именно туда, и оставив в
XhMakeTempMessage()
просто va_start/.../va_end.
И так же заменил "имитацию" strzcpy()
на нее саму.
Что не понравилось: там имеется
vsprintf()
, после которого и идет strzcpy()
.
Может, корректнее-таки перейти на "сразу
check_snprintf()
", или завести какой-нибудь zsnprintf()?
win->workSpace
. Надо бы сделать функцию,
которая для указанного XhWindow вернет его workSpace.
03.08.2004: сделал,
XhWidget XhGetWindowWorkspace(XhWindow window)
.
Понадобилось также изготовить
XhWidget XhGetWindowAlarmleds(XhWindow window)
,
и XhWidget XhGetWindowShell(XhWindow window)
(для передачи Xt'ям при создании popup-окон).
Проверил на nipp/ipp.c -- вполне работает. И cx-starter.c тоже перевел. А потом избавил от Xh_typesP.h и Chl_{gui,knobprops,simple}.c (в последнем оно и так не требовалось).
И Knobs_text_widget.c, в котором уже также давно было не нужно (с когда, интересно -- там стоял комментарий "That's due to too much internals in _text_widget; should be moved to another file"... С перехода на Chl_knobprops.c?).
Далее -- tsycam.c. И istcc.c.
И даже sukhphase.c, которого, если он реально понадобится, придется переписать почти с нуля, учитывая Knobs, и Xh'ные интерфейсы цветов и файловых диалогов.
XhACT_FILLER
-- XhXXX_TOOLFILLER()
.
07.08.2004: теперь все, что до него, аттачится как и раньше -- влево. А все, что после -- аттачится вправо. Чтобы не было наползания "растущих справа" и "растущих слева", между ними вклеивается пустой виджет (Core).
Вроде корректно отрабатываются граничные случаи -- когда FILLER идет либо первым, либо последним в списке.
Опыты показали, что лампочки (LEDS) лучше все-таки оставлять слева -- последними. А справа -- после FILLER'а -- только "дверь".
04.01.2005: удалил из Xh_toolbar.c старый вариант, который не поддерживал FILLER.
21.08.2004: сделал --
XH_FIXED_BOLD_FONT
и XH_TINY_FIXED_BOLD_FONT
определяются так же, как и их не-BOLD
-версии, только
вместо "medium" стоит "bold".
XhWindow*Mask
, и слегка удивился.
Что такое XhWindowOnTopMask
? Оно мало того, что нигде
не используется, так его еще и в доисторическом mapp/ нету, а
есть только начиная с oldcx/*/chl.h, и там оно реально тоже
нигде не встречается, кроме как в определении.
Есть также некая XhWindowSecondaryMask
-- она реально
поддерживается в Xh_window.h, приводя к флагу
XmNtransient:=True
, но никем не используется. Видимо,
делалось для ipp-подобных (но в старом ipp -- не используется). Когда
оно появилось -- вопрос, но в oldcx аналогичная (но не идентичная!)
функциональность висела на флаге XhWindowTitleOnlyMask
.
05.10.2004: несколько дней назад (точнее -- 30.09.2004) Саша ввел возможность указывать ширины/высоты места под сепараторы независимо от наличия этих самых сепараторов.
Так что надо теперь добавлять вызов
"XhGridSetCellspacing()
" (а в идеале -- и переименовать
XhGridSetDistances()
в
XhGridSetCellpadding()
).
Итак:
XhGridSetDistances()
переименована в
XhGridSetPadding()
.
XhGridSetSpacing()
. Пока она
работает только с SGL.
Итого -- по минимуму, уже получилось, и во вложенных элементах все выровнялось с обычными.
Правда, есть два замечания:
22.10.2004: еще 08.10.2004 Саша вроде как что-то подфиксил. Проверил -- стало получше, с Editres'ом не глючит, значение 2 тоже работает пристойно. Но большие значения (>=5) дурят, да и 0 почему-то принимается как default=2.
08.11.2004: неделю назад (01.11.2004) Антоновым глюк был исправлен (проблема с "большими значениями" заключалась в том, что при ширине/высоте в 1 виджет справа/снизу всегда добавлялся spacing).
Итого -- помечаем раздел как "done".
25.02.2005: и-го-го!!! Вылезли еще баги,
связанные явно все с теми же модификациями. Это проявилось в "новом"
ipp.c, который я захотел перевести в XmRowColumn'а на сетку, поскольку
XmRowColumn при XmNpacking=XmPACK_COLUMN
делает ВСЕ
элементы ВСЕГДА одного размера и по ширине, и по высоте, а мне
захотелось один элемент в последней строчке (цилиндр Фарадея) сделать
пониже -- там нет графиков, а только одна текстовая строка.
Так вот -- при добавлении хотя бы одного виджета сетка там
разлеталась на пару десятков тысяч пикселов. Этот баг был исправлен
быстро -- где-то что-то не инициализировалось нулями (а Xt, похоже,
bzero()
не делает...).
А потом обнаружился еще баг: почему-то при установке spacing (sepWidth/sepHeight) >0 оно стало без спросу увеличивать размер клетки на величину, примерно пропорциональную spacing'у. С этим сходу разобраться не удалось, он будет рыться дальше послезавтра.
27.02.2005: итого -- он произвел очередные багфиксы, и вторая из вышеупомянутых проблем также исчезла.
Напрашивается вывод: модель функционирования XmSepGridLayout создавалась два года назад исходя из некоторых требований. Теперь же требования несколько изменились (alignment, filling, cellpadding, cellspacing), и исходная модель стала неадекватной. И реально сейчас Антонову приходится именно бороться с этой моделью, приспосабливаясь под нее и обходя ее "недочеты". По-хорошему стоило бы просто переписать виджет с нуля, с учетов нынешних требований (а еще можно добавить colspan и rowspan, да еще и указание background'а отдельным строкам/колонкам), но -- времени жалко.
Йок!
20.10.2004: ввел флажок
XhWindowCompactMask
(по-хорошему, надо б его назвать
"CompactWorkspace", но это вылезет за все разумные размеры).
Обнаружил, что там и так при XhWindowRowColumnMask
ставится wsOffset=2
. Стал разбираться -- это явно
следствие того, что XmRowColumn имет
XmNmargin{Width,Height}=3
. Оказалось достаточно уставить
0. Для удобства селектор wsClass
изничтожен и теперь в
разных случаях виджеты создаются отдельно.
В общем, сделал теперь, что В ЛЮБОМ СЛУЧАЕ вычитывается
XmNshadowThickness
parent'а в fst
, и
workSpace attach'ится на это расстояние плюс либо 7, либо (в случае
компактности) 0. По вертикали fst
прибавляется только в
случае непосредственного прилегания к краю формы (т.е., если
toolbar/menu/statusLine отсутствуют).
Заодно выяснилось, что toolbar и statusLine аттачатся
unconditionally на расстоянии 1, что не есть гуд. Вставил туда
вычитывание fst
из window->mainForm
и
аттачмент на этом расстоянии взамен былого 1.
Кстати, появилась возможность убрать хак из cx-starter.c, сбрасывавший все аттачменты workSpace'а в fst. Хак убран.
Итак, все получилось -- ура, товарищи!
21.10.2004: исторические разборки показали, что
флаг XhWindowRowColumnMask
появился (май 1999г.?) в oldcx
для поддержки концепции "dashboard" (chl_dashboard.c). Оная
концепция (имя слизано, видимо, с Norton/Symantec Dashboard for
Win-3.x) никогда у нас не использовалась, а вводилась для того, что
сейчас делает cx-starter.
18.07.2005: сделал, хотя конкретно Grid и не проверял -- куда он нафиг денется? Заодно перетряхнул собственно флаги и рассовал их по группам.
0) обнаружил, что XmRowColumn сходит с ума при
XmNmarginHeight>8
и/или
XmNmarginWidth>8
. Bugreport писАть будем?
12.07.2005: хренушки -- не могу этот баг воспроизвести...
13.07.2005: Yes!!! Удалось-таки!!! Смысл -- в том, что в условиях
Xh почему-то при XmNmarginWidth>8
и/или
XmNmarginHeight>10
XmRowColumn перестает корректно отдавать parent'у свой размер.
15.07.2005:
bug#1298.
20.10.2004: BTW, оказывается, у Motif'а теперь есть Bugzilla -- bugs.motifzone.net!
15.11.2004: кстати, стоит написать еще про несколько багов:
1) что в man-страницах по XmStringDraw*() говорится про
"window" вместо "drawable";
12.07.2005:
bug#1290.
18.07.2005: patch submitted. The patch does NOT touch
TabVal(), which is a bit bad: its use is safe in
OptLineMetrics()
and LineMetrics()
, but isn't
in DrawLine()
...
2) про то, что окна появляются в (0,0) -- т.е., что зря указывается PPosition; 12.07.2005: bug#1291.
3) невозможно сказать "man Shell" из-за Shell.3pm (рекомендация -- XmShell.3). 12.07.2005: bug#1292. 18.07.2005: patch submitted. Also for "man Object".
22.11.2004: и еще один уже не bug, а enhancement:
4) сделать для формы XmATTACH_CENTERED в качестве
возможного значения для
XmN{left,right,top,bottom}Attachment
.
29.11.2004: и еще пара багов
5) не описано, что XmFileSelectionDoSearch(,NULL) пересчитывает "текущую" директорию;
6) то, что глючит XmNdirSpec при PATH_MODE_RELATIVE. 13.07.2005: bug#1294.
04.01.2005: и еще давний-давний баг:
7) что кнопки Home и End в окошке выбора файла и производных от него зачем-то всегда перехватываются списками, даже когда фокус у поля ввода. 12.07.2005: bug#1293.
06.01.2005: и еще пара багов:
8) KP_стрелки (RedHat Bug#142116); 16.07.2005: bug#1300.
9) Xinerama -- она в Motif'е вообще никак. 13.07.2005: bug#1295.
10.01.2005: и еще:
10) Глючащий drag-and-drop при использовании механизма languageproc и LC_CTYPE=ru_RU.KOI8-R. 15.07.2005: bug#1296.
10.01.2005: и еще, давно просившийся:
11) XmForm почему-то считает циклической зависимость по аттачментам, если "замыкание" происходит по перпендикулярным направлениям. Хотя горизонталь и вертикаль должны бы рассчитываться независимо. 16.07.2005: bug#1299.
03.05.2005: и сегодняшний:
12) почему-то XmLabel при XmNlabelType==XmPIXMAP и НЕуказанном XmNinsensitivPixmap делает stipple цветом не фона, а foreground'а, что есть совсем бессмысленно. 15.07.2005: bug#1297. 22.04.2011@Снежинск-каземат-11: в openmotif-2.3.1-5.el5_5.1 проблемы уже нет.
16.07.2005: one more, long-known:
13) криво форматированная таблица в "man VirtualBindings". 16.07.2005: bug#1301. 18.07.2005: patch submitted. Also for "man Object". 20.07.2005: fixed in the CVS HEAD.
12.12.2010@Снежинск-каземат-11: почему в CentOS-5.2/openmotif-2.3.0 selector_i.OptionButton'ом игнорирует CTL3D-фон?
(Все прочие CTL3D -- работают, а вот этот -- рисует общим фоном.)
22.02.2011: дятел -- это же OpenMotif bug#1356, репорченный МНОЮ аж 01-09-2006, и пофиксенный 26-12-2006 in CVS HEAD!
А вообще см. обсуждение в разделе по Xh_color.
22.04.2011@Снежинск-каземат-11: и еще парочка:
14) У XmNfillOnSelect=True-XmToggleButton'ов в НЕнажатом состоянии основная часть отрисовывается фоном parent'а (в нажатом -- всё корректно).
15) Время от времени OptionButton'ы (наши LOGD_SELECTOR) перестают нажиматься -- вообще во всей программе. В соседних программах продолжают работать.
16) tearoff'нутые менюшки используют для заголовков окон неправильный charset -- похоже, всегда 8859-1.
17) При перетаскивании средней кнопкой мыши любой пиктограммы (например, toolbar-кнопки [?]) на statusline программа срубается с диагностикой:
Motif Error in file DropTrans.c at line 572: A DropTimer is already active, zsh: segmentation fault ./liucc
Этот баг был в CentOS-5.2'шном openmotif-2.3.0-0.5.el5.i386.rpm, замечен 02-02-2011. А вот в 2.3.1-5.el5_5.1 -- уже нету.
13.11.2004: некоторое время назад занимался поиском готовых решений, но они так ни к чему толковому и не привели. Поскольку в основном требуются вертикальные МЕТКИ, т.е., СТАТИЧНЫЙ текст, то, обсмотрев существующие варианты реализации, решил, что надлежит делать по такой схеме:
Пишем текст на обычном pixmap'е горизонтально, потом
делаем оттуда Ну так что -- изготавливаем вызов типа
XGetImage()
, при помощи
XGetPixel()
+XPutPixel()
поворачиваем его
на другой XImage, потом создаем еще один pixmap, и делаем
XPutImage()
на него. Результирующий pixmap и возвращаем,
а уж потом программа вольна делать с ним что угодно -- чаще всего
ставить как XmNbackgroundPixmap
кому-нибудь.
что ли?
XhVertStringDraw(...)
14.11.2004: что меня смущало -- что официально Xh
вроде как не должен показывать связь с Motif'ом, а тут -- пришлось бы прямо
в Xh.h делать #include<Xm/Xm.h>
.
А потом пришла мысля: функция должна указанному виджету
(метке или потомку) уставлять (с помощью XmNlabelPixmap
'а)
вертикальным текстом указанную строку:
XhAssignVertLabel(XhWidget lab, const char *text)
Начал реализацию -- ох и муторно это! :-)
15.11.2004: продолжаем...
НИКОГДА НЕ НАДО делать XmFontListFree()
с тем, что было считано
из виджета.
Сие знание получено на собственном опыте: вначале, положившись на Кена Ли
(Motif FAQ, Q204) я делал XmFontListFree()
, и оно падало. А
потом обнаружил у него же статейку (на тему memory leaks in Motif), где
полее точно говорилось, что ТАКОЕ -- делать не надо.
Ближе к вечеру: му-р-р-р!!! Сделал! Оно вертикалится, пока только в Chl, но это уже не принципиально.
Еще чуть позже: реально сделал и функцию
XhAssignVertLabel()
. Работает. Одно "ограничение": если
что-то по ходу дела обламывается, то эта функция НЕ уставляет
XmNlabelPixmap
, и не делает
XmNlabelType:=XmPIXMAP
, так что клиентам стоит предварительно
уставить хоть какую-то "обычную" метку.
В остальном -- все fine, так что помечаем раздел как "done".
17.11.2004: ага, щас -- done... Была "несколько недоделана" многострочность. Во-первых, вместо центрирования стояло выравнивание по левому краю. А что важнее -- оно НЕ очищало всю область...
18.11.2004: проблему с "центрированием" исправил
-- ввел третий параметр, alignment
: <0 -- влево, ==0 --
по центру, >0 -- вправо.
Что будем делать с заполнением? Проблема в нахождении функции, которая б закрасила компонентом background, а не foreground...
Часом позже: проблема решена. Там есть еще второй GC, используемый
при копировании (его назначение -- быть GXCopy). Вот в него и добавил
foreground=bg
, так что со спокойной душой делаем
XFillRectangle()
.
область...
24.01.2005: возникло желание сделать так, чтобы
при реколоризации виджета "VLABEL" оно б корректно меняло цвет. А для
этого потребно в XhAssignVertLabel()
мочь обходиться БЕЗ
указания текстовой строки -- чтобы оно само вычитывало ее из виджета.
Сделал -- вычитывает. И это все даже работает.
Собственно, возник единственный вопрос: надо ли делать
XmStringFree()
, тому, что получаем от
XtVaGetValues(...,XmNlabelString,...)
. Ken Lee в MotifFAQ
говорит что "да", а вот два подряд вычитывания значения почему-то
возвращают один и тот же pointer (хотя должны бы два разных -- ведь это
должны быть результаты ДВУХ malloc()
'ов). В общем --
мрак. Сделал, как советует Ken Lee. Почему-то все работает :-)
01.03.2006: ЕЛЫ-ПАЛЫ!!! А ведь проверки-то не было -- что размеры >0! Так что при пустой строке имелось BadValue от X_CreatePixmap, value=0x0 -- проблема идентичная той, что за 19-11-2005.
Пофиксено -- теперь при <1 делается :=1.
29.11.2004: Оказалось, что все просто -- надо вызывать
setlocale(LC_ALL, "")
.
В принципе-то в man-странице по XtSetLanguageProc()
написано, что оно вроде как должно бы и само все делать, но...
Несколькими минутами позже: доперло! Чтобы этот механизм начинал функционировать, НАДО ОБЯЗАТЕЛЬНО вызвать сию функцию, иначе механизм сам, по умолчанию, не включается. Достаточно вызвать со всеми тремя параметрами равными NULL. Bingo!
Вставил этот вызов в самое начало XhInitApplication()
, ДО
ПЕРЕД XtToolkitInitialize()
.
10.01.2005: И-го-го!!! Хи-хи-хи!!!
Хрю-хрю-хрю!!! Заметил, что теперь, при наличествующем
XtSetLanguageProc(NULL,NULL,NULL)
, перестал работать
drag-and-drop: оно копирует мышкой из метки в поле ввода какую-то
чухню -- кабы не CTEXT как есть... При удалении локализации -- все
очухивается. Пишем еще bugreport?
10.01.2005: решение-то крайне простое: вытащить
lib/Xh/Xh_globals.h в include/. Собственно -- так и
сделал. Теперь следующий шаг -- пройтись по всем клиентам и поизводить
в них эти extern
'ы.
Сделано, список жертв включает: Chl_includes.h, Knobs_includes.h, cx-starter.c, tsycam.c, {,old_}sukhphase.c, pult/fedors/demag.c, pult/xmclients/ipp.c, istc/xmclients/{istcc.c,vibro.c}.
20.01.2005: оставался еще один -- vacclient.c. Проапдейчен.
10.01.2005: мне это требовалось еще полгода назад -- для нового ipp, чтобы при загрузке сохраненной картинки можно было перейти в "замерзшее" состояние.
А сегодня А.Чугуев захотел, чтобы можно было кнопочку делать СРАЗУ нажатой (доп. параметр у макроса?).
По-хорошему, надо б иметь Xh-вызов, который ВСЕ "чекбоксистости" переводит в указанное состояние. Более простая пара вызовов -- 1) получить XhWidget кнопки с указанным cmd плюс 2) перевести кнопку в указанное состояние.
24.01.2005: изготовил всю инфраструктуру --
теперь каждой кнопке назначается свой privrec, в котором записываются
тип и команда; сохраняется этот privrec в XmNuserData
. И
даже destroyCallback ставится, который делает ей
XtFree()
. И перевел toolCB()
на такую
архитектуру, взамен былого хранения типа в userData.
И ввел интерфейс XhSetCommandOnOff()
, который должен
уставлять все кнопки (а в будущем -- и пункты меню и т.д.) с указанной
командой в указанное состояние (нажато/отжато). Но само содержимое
пока сделать не успел -- завтра.
07.02.2005: ага, "завтра" -- щас! Через две недели :-)
В общем, еще тогда стало ясно, что для реализации этой
функциональности без хитрого рекуррентного обхода дерева виджетов надо
где-то хранить windget-id собственно контейнера-parent'а,
непосредственно содержащего все кнопки (дабы просто пройтись по всем
его child'ам). Очевидно -- рядышком с window->toolBar
.
Вставил таковое поле -- toolHolder
.
Опосля чего все сделал: ввел в privrec еще поля magic
(для корректности) и pressed
(текущее состояние --
отражает ненажатость=0/нажатость=1). И собственно
XhSetCommandOnOff()
населил кодом, который получает список
детишек и идет по ним, проверяя что (кнопка, и magic совпадает, и
чекбокс, и та команда, и не то состояние) и тогда инвертирует.
Замечание -- вот забыл после XtMalloc()
'а сделать
bzero(privrec)
, и долго разбирался, почему ж оно глючит --
поле pressed
содержало бредовое значение :-).
Так что помечаем раздел как "done".
Кстати, обнаружил там нафиг не нужное поле
_WindowInfo.toolSepr
, которое лишь заполнялось, но,
естественно, никогда на него никто не ссылался. И аналогичное поле
_WindowInfo.statsSepr
. Раскопки показали, что это
артефакты еще времен uxil'а -- т.е., 1997/1998гг. Выкинул за
ненадобностью обоих.
02.05.2005: собственно, УСТАВЛЯТЬ destroyCallback я тогда забыл. Сейчас сделал (хотя зачем он нужен? :-).
cmNNN
, чтобы не писать в каждой программе
примерно одни и те же определения.И заодно -- подобавлять некоторые "часто использующиеся" .xpm-файлы в cx/src/include/pixmaps/ -- типа кнопок "старт", "пауза", ...
02.05.2005: pixmap'ы свалил -- btn_{pause,getbkgd,rstbkgd}.xpm.
04.05.2005: также извел целиком директории pixmaps/ в {pult,istc}/xmclients/, переведя тамошних клиентов на стандартизованные пиктограммы.
А насчет стандартизации cmNNN
-- решил не связываться,
слишком много возни было бы, как у меня в {cx,pult,istc}/, так
и у Чугуева. Отложим до CXv4 -- там стандартизуем сразу.
А в остальном -- "done".
08.04.2005: В принципе, поле
actdescr_t.hotkey
существует с незапамятных времен (оно
перекочевало туда еще из древнего cx/oldcx с "chl", где было рассчитано
для никогда не существовавшего меню). Вопрос только -- как сделать,
чтобы эти "translations" были глобальными в рамках окна -- куда их
лепить? Что-то на эту тему имеется в Motif FAQ... Ключевые слова --
XtAugmentTranslations()
,
XtParseTranslationTable()
и XtAppAddActions()
.
10.04.2005: хрена лысого... Помаялся, и опять -- знакомая по прошлой борьбе с Motif'ом картина: глухой тупик, куда и как рыть -- непонятно. Проблема именно в том, что НЕЯСНО кому и как можно было бы вставить эти translations.
Нашел только в GoogleGroups какое-то странное упоминание о "global accelerators". Но более нигде такое словосочетание не встречается.
Единственное, что вселяет надежду -- ведь меню-то как-то умудряются
это делать. Так что это в принципе возможно. Правда, не исключено,
что это сделано при помощи XtGrabKey()
. Следы оной я в
lib/Xm/RCMenu.c нашел.
А еще вроде как есть XtInstallAccelerators()
, делающая
не вполне понятно что, но
рекомендуемая к использованию
(Еще чуть позже стало-таки ясно, что это -- не путь, а надо
пользоваться именно XtGrabKey()
.)
Короче -- та часть, которая относится к Xh'у, практически готова -- кроме собственно реакции на событие. Остальное делать я пока, пожалуй, не буду -- слишком муторно, и результат явно не стоит затрачиваемых усилий.
13.04.2005: потыркался еще. Хммм... Мда...
Попробовал XtGrabKey()
. Напрямую. Безо всяких
translations, указав принудительно keycode=68/*F2*/ и
modifiers=AnyModifier. Почему-то не грабится, даже если сделать
"мишени" (самой кнопке) XmNtraversalOn:=True... Полез рыться в Google
Groups. там нашлась наводка на (сюрпри-и-из!) опять NEdit. Залез в
исходники -- да, что-то там есть, в nedit-5.4/util/misc.c.
BTW, в NEdit'е, похоже, есть своя реализация tooltip'ов -- там это именуется "calltips", поскольку используется для контекстных подсказок по функциям, и лежит в source/calltips.c.
02.05.2005: от греха подальше сделал в
Xh_{app,toolbar}.c
#define TRY_HOTKEYS 0
и засунул все махинации на
эту тему внутрь #if TRY_HOTKEYS
, чтоб не светились
зря.
08.02.2008: в ту же степь -- в Motif FAQ есть некий вопрос 132 "Why can't I use accelerators on buttons not in a menu?", в ответе на который рекомендуется использовать собственный message loop, в котором и отслеживать нажатия hotkeys. Плюс, там же есть ссылка на ftp://ftp.informatik.rwth-aachen.de/pub/packages/Mnemonic/Mnemonic.tar.gz
11.04.2005: хе, все крайне просто -- так себя
ведут только unresizable-окошки, оттого, что у них отдельно уставляется
XmNmwmFunctions
, и в нем указываются только
MWM_FUNC_MINIMIZE|MWM_FUNC_CLOSE
. А есть еще флажок
MWM_FUNC_MOVE
, наличие/отсутствие которого не волновало
twm/fvwm-клоны, но вот XFCE/Gnome/KDE и IceWM его очень даже учитывают.
Пофиксено.
14.09.2005: тьфу ты, а ведь уставка
MWM_FUNC_*
есть еще в куче мест -- в диалоговых окошках.
В основном это касается Chl -- 4 места в Chl_knobprops.c и
одно в Chl_gui.c (окно help).
Короче -- также пофиксено.
Но: еще в конце августа, при махинациях с окошком help (см. ShowHelp) возникла мысля:
Явно надо сделать Xh-интерфейс к диалоговым
окнам, в виде нескольких функций. Первая -- создать диалоговое
окно, и ей указывать (флагами): список кнопок, которые должны быть;
должна ли быть сама message-string; resizable/non-resizable, zoom'able;
а вот MWM_DECOR_TITLE|MWM_DECOR_MENU
и
MWM_FUNC_MOVE|MWM_FUNC_CLOSE
должны ставиться всегда.
Вторая -- уставить message-string. А третья, которая "получить
widget-ID того, в кого надлежит вставлять содержимое" -- не нужна,
поскольку нам именно этот ID (box'а) и возвращают.
Плюс, на даньнейшее будущее (с прицелом на время, когда будет GRL) надо продумать простенький интерфейс и к callback'ам, show/hide, etc.
В общем, простенький интерфейс -- как в первом абзаце -- стоит сделать при следующей же потребности в очередном диалоговом окошке.
19.09.2005: следующая же потребность уже возникла -- окошко "Help" в cx-starter'е. Так что сделано -- см. раздел "Xh_stddlg".
XhSetCommandOnOff()
, возможность сделать
disable/enable (sensitive) кнопкам с указанной командой.
Смысл -- для "красивой" работы кнопок типа "поехали"/"пауза" и "однократно" -- чтобы при нажатой кнопке "Поехали" кнопка "Однократно" была б отключена.
02.05.2005: угу -- сделал, функция
XhSetCommandEnabled()
. Проверил на adc333.c --
работает (а чему б там глючить? :-).
XhSetCommandEnabled()
-- надо добавить в
XhAssignPixmap()
"интеллектуальное" уставление
insensitive-pixmap'а -- по тому, что внутри маски, а то оно stipple
делает шибко уж некрасиво -- по всему прямоугольнику.
02.05.2005: надо, надо -- только шибко уж нетривиально это делать, повозиться придется. Захочется -- сделаем, а пока и так сойдет.
03.05.2005: а почему, собственно, оно делает
stipple цветом foreground, а не background? Первой мыслью было --
оттого, что у XmLabel'а отсутствует background_gc; но реально-то он ей
и не нужен -- может просто взять
->core.background_pixel
.
Проблема -- в том, что оно делает Redisplay()
,
используя для stipple обычный insensitive_GC
(см.
комментарий "if no insensitive pixmap but a regular one, we need to
do the stipple manually, since copyarea doesn't").
На эту тему тоже будем писАть bugreport? (Я проверил -- сей баг НИКАК не упоминается в их Bugzill'е.)
03.05.2005: из вышеупомянутого вывод: а что, если уставлять XmNforeground:=XmNbackground? Попробовал -- работает!!! Ровно так, как надо. Так что помечаем как "done", хотя сие и чревато.
06.10.2007: тема всплыла снова, через два с половиной года. Баг-то в OpenMotif'е уже пофиксили (его номер -- 1297), в версию 2.3 этот фикс вошел, и в результате в FC5 (на машине Tino) задисэйбленные кнопки становятся вообще просто серыми, БЕЗ картинки. Это, похоже, так наш фикс интерферирует с официальным.
Кстати, фикс-то пошел в
Xh_utils.c::XhAssignPixmap()
(вот не записал
тогда, и пришлось долго искать "а где же"; в конце концов, diff спас --
между версиями до и после 03-05-2005).
Так что -- окружил уставку XmNforeground:=XmNbackground
#if
'ом, так что она теперь делается только для
версий <2.3. В 4cx/ также сделан back-port.
Пока не проверял, но сработать вроде бы должно (хотя все равно остается вопрос, ПОЧЕМУ произошло то, что произошло -- почему фиксы ТАК с-интерферировали?).
EnableCommands()
/DisableCommands()
, в его
gremlin'ской инкарнации --
EnableCommand()
/DisableCommand()
.
И с тамошними же диапазонами: 0..99 -- стандартные disableable-команды; 100..255 -- юзерские disableable-команды; 256..999 -- стандартные non-disableable-команды; 1000..oo -- юзерские non-disableable-команды.
Правда, там (по крайней мере, в GREMLIN'е) была еще более
продвинутая функциональность -- запоминание/стэкирование текущего
набора разрешенных команд в OSheet.Execute()
. Это,
конечно, не нужно -- в Motif'е совсем свои механизмы :-), а команды у
нас используются только на верхнем уровне.
А вот глобальные команды вводить будем уже с учетом диапазонов.
08.07.2006: вообще-то уже довольно давно было принято решение, что команды в CXv4 будут ТЕКСТОВЫМИ. Так что все вышеописанное -- мимо кассы. (А в современном CXv2 оно не особо-то и требовалось.) Так что -- "withdrawn".
XhACT_SEPARATOR
поддерживать собираемся?
03.05.2005: да, сделал. Даже в двух вариантах --
как просто пустоту (виджет Core
, как и filler), и как
вертикальный сепаратор. Выбирается условной компиляцией.
XmNorientation:=XmHORIZONTAL
.
Вставил.
11.05.2005: во-первых, переместил не-к-месту-присутствующие ресурсы для "value" в Knobs_internals.c.
Во-вторых, ушли где-то полтора-года-назад-устаревшие ресурсы для
окна шага -- "stepShell"/ В-третьих, туда же отправились и "hslider" с
"vslider".
В-четвертых -- проверил, что тут нет НИКАКИХ поведенческих ресурсов --
только геометрические, шрифтовые и метки/заголовки. Единственное
"исключение" -- "*.pathMode:PATH_MODE_RELATIVE".
XhShowWindow()
вызывается не после создания
всего содержимого, а сразу после XhCreateWindow()
, то
вылетает ошибка:
Собственно, возможно, это проблема даже не Xh, а Knobs, или Chl, или еще чего-нибудь.X Error of failed request: BadValue (integer parameter out of range for operation) Major opcode of failed request: 12 (X_ConfigureWindow) Value in failed request: 0x0 Serial number of failed request: 595 Current serial number in output stream: 596
05.11.2005: решил слегка протестировать -- убрал все содержимое, оставив лишь один-единственный knob...
Что странно -- падает оно так С ЛЮБЫМ наполнением: хоть с LOGD_TEXT, хоть с LOGD_ARROW_DN...
Дальнейшее разбирательство показало, что глючат даже не knob'ы, а что-то другое -- когда, подшаманив Cdr, удалось запустить программу с пустым элементом, падение осталось, с теми же самыми признаками.
Более того -- с пустым элементом оно падает даже при "правильном"
месте вызова XhShowWindow()
, но уже как-то по-другому --
по floating point exception, где-то в
XmSepGridLayout.c::Resize()
.
12.11.2005: еще 08.11 Саша исправил тот дурацкий баг с floating point exception -- просто он не проверял, что количество строк/столбцов 0, и делил на оное.
Проверил -- деления на 0 больше нету.
А вот проблема с "X_ConfigureWindow" -- осталась. При полном отсутствии собственно ручек -- есть только 1 элемент. Надо будет разбираться...
Несколькими минутами позже: виновный установлен -- это, естественно, XmSepGridLayout :-) При переключение на Gridbox все заработало -- оно прекрасно появляет окно и потом красиво его увеличивает по мере наталкивания новых виджетов (Gridbox-то падает в другом месте, но это к делу уже не относится :-).
19.11.2005: проблема исправлена.
BadValue
).
Но, по результатам "работы" с пустой сеткой, и с realize-до-наполнения:
Так что выводы: Саша пусть все-таки разберется, что ж там
подглючивает, но XhShowWindow()
будем вызывать уже ПОСЛЕ
заполнения окна.
23.11.2005: отправил в xorg@lists.freedesktop.org вопрос -- Why X protocol/server forbid zero width/height? -- там в результате развернулась нехилая дискуссия, одним из результатов которой стала выдача Jim Gettys'ом ссылки на бумагу "Why X Is Not Our Ideal Window System"; и вот в ней в §2.3 приводятся как раз те же резоны, что возникли у меня.
06.08.2006: вообще-то с первоначальной проблемой давно разобрались, так что, как давным-давно надо было -- "done".
CNCRTZE()
/ABSTRZE()
. В первую голову это
вылезло в Xh_window.c, на функциях
XhGetWindowNNN()
.
12.11.2005: прошелся. Функции-accessor'ы подправил. Заодно подкорректировал и весь Xh_window.c. Далее прошелся по Xh_utils.c... И все! В Xh на данный момент больше таких проблем нету!
GC
указывать и "функцию" (флаг GCFunction
, поле
XGCValues.function
) GXcopy
. А то бы, вдруг
(хотя и врядли, но все же) выдали нам GC со всеми указанными полями
совпадающими, а функцией -- другой. И были бы превеселейшие глюки!
07.08.2006: сделал. Это коснулось:
AllocXhGC()
.
AllocPixelGC()
.
(ой-е... А вот ее-то в
istc/xmclients/, где она и зародилась -- валом!)
AllocOneGC()
, имеющейся пока что во всех наших
qult/xmclients/.
А потом стал думать: но ведь, по логике, программа НЕ ДОЛЖНА пытаться указывать все-все -- а только нестандартности, так? А то ей еще и fill_style пришлось бы всегда указывать, и проч. Так что -- наверное, это излишне. И дальше править не стал.
Кстати, попробовал разобраться в логике в
xc/lib/Xt/GCManager.c -- нифига толком не въехал. Интуитивно
чувствую, что такой подленки оно вроде бы не подложит -- судя по тому,
что макросы CHECK()
+GCVAL()
имеют и параметр
default
, используемый при отсутствии бита в маске, и для
поля function оным default'ом является GXcopy
, но --
100%-уверенности нету.
Хотя очень, очень похоже.
CxWidget
.
06.04.2009: перевел/унифицировал Xh_stddlg.c.
17.07.2014: в Xh_types.h изменено
определение XhWidget
+CxWidget
для
идентичности с 4cx/ (прошлая дата на файле была 10-03-2009).
02.08.2015: в продолжение этой работы (побудительным мотивом стало желание собирать Xh_plot+Xh_viewport в 4cx/):
XhWidget
и XhPixel
повсеместно заменены на CxWidget
и CxPixel
.
А XhWidget
и XhPixel
(бывшие к тому
моменту typedef'ами на CxWidget/CxPixel) удалены.
Буквосочетание "XhWidget" осталось только в именах функций
XhWidgetOfPlot()
и XhWidgetOfMonoimg()
, где
его образование чуть иное и оно скорее к месту.
/usr/include/Xm/XmP.h:1279: warning: declaration of `context' shadows global declaration
По хорошему, надо бы просто переименовать шибко неконкретный символ
"context
" в "xh_context
", а заодно и
"charset
" в "xh_charset
" (этот-то нигде не
засветился, но -- за компанию!), и избавиться, наконец, от последнего
наследства Brain Motif.
P.S. Кстати, gcc-4.3.2 под FC10 почему-то ругательств не выдаёт, хотя аналогичная декларация в аналогичном месте XmP.h там есть -- то ли warning этот надо включать отдельным ключиком, то ли просто эта версия компилятора не считает нужным на такое ругаться (казалось бы -- да, это ж просто ОБЪЯВЛЕНИЕ, реальные имена параметров там ни на что не влияют)... С другой стороны, и в 4cx/ почему-то всё тихо...
06.04.2009: да, переименовал, и все использования проапдейтил.
Плюс, в нескольких не-Xh-утилитках использовалась своя переменная
context
-- она везде попереименована в
my_context
.
Всё, уж теперь-то ARCHIVE'ные исходники точно собираться перестанут :-)
XhAddSignal()
/XhNoticeSignal()
.
Смысл -- чтоб не только основная программа (де-факто -- Chl) могла б ловить команды, но и прочие потребители, хоть как-то имитируя v4'шную архитектуру с текстовыми командами и broadcast'ами по дереву.
Потребность конкретно сейчас -- для хитрого плагинчика в weldclient (для техпроцесса "3D-принтера", интерфейс к 3dp.sh), чтоб дать ему возможность сохранять наборы уставок в файлы.
XhCreateFdlg()
/XhFdlgNNN()
.
16.01.2004: перевел Chl_simple.c на XhFdlg.
11.06.2004: НЮ-НЮ!!! Недоделано оно было по-страшному!!! И вот почему.
XmFileSelectionDoSearch()
с
параметром dirmask, предварительно полученным при помощи
XtVaGetValues(,XmNdirMask,...), эта тварь (FileSB) всегда перепрыгивала
обратно в текущую директорию. Т.е., она использовала указанное в
качестве фильтра имен, а директорию -- всегда делала "", т.е., текущую.
Bugware, блин!!!
А чего в man-странице не было указано, так это того, что можно передавать dirmask=NULL, и тогда оно само возьмет текущие значения из диалога, и все будет OK.
Так что -- выкинуто к чертовой матери все на тему dirmask.
Но мало того -- даже ресурс самого диалога XmNdirSpec
,
долженствующий содержать ПОЛНОЕ имя файла, содержит тоже имя БЕЗ
директории -- это баг в Motif'е, спасибо Марку Эделю,
nedit-5.2/util/getfiles.c::HandleCustomExistFileSB()
.
Так что -- надо "руками" делать конкатенацию
XmNdirectory
и XmNdirSpec
.
Сделал, практически по той же логике, что и в NEdit'е. Что забавно, вставлять '/' между директорией и файлом не надо -- директория его уже и так содержит.
29.11.2004: проверил -- nedit, из которого частично брался менеджмент диалогов, таким не страдает. И с другой стороны, даже если ставить "*pathMode" в PATH_MODE_FULL вместо обычного PATH_MODE_RELATIVE, проблема остается.
Получасом позже: разобрался -- проблема именно из-за того, что мы делаем
XmFileSelectionDoSearch()
(тестировал на Brain's
motif/ch_7/fileSB.c). М-мать их...
Надо будет опять лезть в nedit...
Еще часом позже: ха-ха, слазил -- фиг! Он решает эту проблему радикально: КАЖДЫЙ раз создается новое диалоговое окно, а по завершению выбора файла -- оно прибивается.
И еще часом позже: а можно ведь делать
XmFileSelectionDoSearch()
с осмысленным и не-NULL dirmask,
пользуясь той же проверкой, что и в XhFdlgGetFilename()
.
29.11.2004: в конечном итоге стал все делать на основе XmSelectionBox; в частности, окошко SaveDlg -- как PromptDialog.
С LoadDlg же решил отступить от первоначального плана (когда под списком располагается value с приклеенным к нему справа push [...]). И сделал [...] обычной кнопкой, которую диалог сам запихивает в строку кнопок внизу, рядом с [OK]. А строку для имени файла сделал просто виджетом, безо всякой формы, и диалог прекрасно прилепляет ее прямо под списком.
30.11.2004: собственно -- сделал. Заметки по теме:
XhCreateLoadDlg()
--
передаются параметры pattern
-- шаблон имен, и
filter
-- функция, которая определяет, годится ли файл с
указанным именем, и если да, то возвращает его дату создания (читая
изнутрий файла) и комментарий. Эти параметры запоминаются и
используются в XhLoadDlgShow()
при подготовке списка. На
всякий случай перед вызовом фильтра делается "упреждающее чтение"
времени из st_mtime
файла.
qsort()
, сортируются по
убыванию даты (самые свежие -- вверху).
XhFindFilename()
, по указанному шаблону находящая
незадействованное имя.
Шаблон должен содержать '*', вместо которой и подставляется меняющаяся строка вида "_YYYYMMDD_NNN", где NNN идет от 000 до 999. Если все эти имена заняты, то вместо порядкового номера принудительно проставляется "ZZZ".
Реально количество цифр (сейчас 3) определяется enum'ом внутри функции, и, похоже, стоит-таки эту функцию вытащить куда-нибудь в libuseful, так, чтобы количество цифр было бы параметром.
Теперь надо будет запустить в эксплуатацию, и убедиться, что нет багов (а если есть -- исправить).
Что-то (чутье; и кто-то -- Старостенко) мне подсказывает, что реально господа физики комментарии вбивать не будут. И будут там только голые имена файлов...
Что ж -- им же хуже... 20.05.2013: неверно чутьё подсказывало -- пишут комментарии. В основном свои имена...
И еще: а не надо ль сделать кнопку "Сортировать по имени"?
24.12.2004: ввел, что при
flst_cmd==0
кнопка [...] не создается.
06.01.2005: собственно -- давно работает, проверено, так что помечаем как "done".
20.05.2013: в XhFindFilename()
имелась пара позорных багов:
maxserial
был не 1..NDIGITS, а
1..NDIGITS-1.
x>=maxserial
" вместо надлежащего
"x>maxserial
".
В результате последним номером становилось "098", а потом шло "ZZZ". Это обнаружилось в первый раз за девять с лишним лет -- старостенкины студенты, делая курсовик, сохранили толпу файлов на blm'е.
07.04.2005: точно, там был баг -- я
вставлял доп. условие, что длина имени не меньше суммы длины префикса и
суффикса, и вместо "<
" поставил ">=
".
Что интересно -- файл был помечен как "06.01.2005", и ровно за эту же дату выше имеется комментарий "давно работает, проверено" :-)
Может -- тоже вписывать в имя файла не только дату, но и время? Надобность в порядковом номере -- _NNN -- при этом почти отпадет, но оставить его стоит.
Надо огрокать плюсы и минусы такого подхода в их полноте (да, "Stranger in a Strange Land" читаю в оригинале...)
19.09.2005: сделал, почти строго по проекту. Передаются параметры "имя" (к которому добавляется "Shell" и "Box"), заголовок окна, опциональная (если не-NULL) метка для кнопки "OK", строка сообщения (если NULL -- виджет отключается) и флаги, определяющие набор кнопок, поведение (resizable, zoomable) и модальность.
13.03.2007: добавил "behaviour"-флажок
XhStdDlgFNoAutoUnmng
, при наличии которого делается
XmNautoUnmanage:=False.
13.03.2007: ввел флажок
XhStdDlgFNothing
, наличие которого отключает сепаратор
плюс автоматом сбрасывает все флаги наличия кнопок.
29.03.2007: и еще один флажок --
XhStdDlgFNoMargins
, уставляющий box'у XmNshadowThickness,
XmNmarginWidth и XmNmarginHeight в 0. Это занадобилось в histplot --
там вообще никакое оформление не нужно, и поля тоже не нужны.
13.03.2007: стал разбираться -- оказалось, что дело вовсе не в stddlg, поскольку xprop показал наличие указанных hint-флажков -- похоже, просто FVWM их игнорирует. Почему? Может, оттого, что окошко -- secondary/popup? И как сие обойти -- использовать другой Shell-класс, или XtVaCreateКакойНибудьДругойShell()?
22.05.2007: занадобилось -- ради ELEM_SUBWIN'а, чтобы по кнопке [Enter] окно само бы не захлопывалось. Да и для всяких ndbp_adc200_elemplugin'ов, где уставки будут в отдельном окошке, knob'ами, оно тоже понадобится.
Так что -- ввел флажок XhStdDlgFNoDefButton
, который
при уставленности приводит к XmNdefaultButtonType:=XmDIALOG_NONE.
Единственная гадость -- что кнопки в отсутствие default'ности выглядят мерзковато -- outline у них отделен от тени расстоянием XmNdefaultButtonShadowThickness, которое при сбросе default'ности XmMessageBox'у никуда не девается, но "вдавленность"-то НЕ рисуется.
Так что -- при уставленном XhStdDlgFNoDefButton
всем
кнопкам делается также XmNdefaultButtonShadowThickness:=0. Кнопка [OK]
стала после этого выглядеть как-то сиротливо и куцевато, ну да и ладно.
XhStdDlgShow()
вставить, кроме
XtManageChild()
, еще и XRaiseWindow()
.
26.02.2009: да, сделал.
Заодно выкинул проверку "if (XtIsManaged(dialog))
return;
" -- нафиг она не сдалась. Тем более, тогда ВООБЩЕ
НИЧЕГО б не делалось.
И в 4cx/ это добавлено.
XhStdDlgShow()
также и XSetInputFocus()
.
26.02.2009: а вот хрен -- оно почему-то начинает
падать по BadMatch
. В man-странице есть фраза "The
specified focus window must be viewable at the time XSetInputFocus is
called, or a BadMatch error results."; если вставить перед
XSetInputFocus()
небольшую паузу -- всё начинает работать,
и даже фокус переносит. Видимо, какие-то глюки с таймированием, и
почему-то окно не успевало фактически появиться на экране перед
фокусировкой (из-за перепасовки запроса window manager'у?).
Так что пока оставлен этот вызов закомментированным.
27.02.2009: погуглил по словосочетанию "xsetinputfocus badmatch", и нашел краткое обсуждение этого вопроса на freedesktop.org за декабрь 2007 -- "Raise/Map and Focus a window: BadMatch error". Там, в частности, некто Peter Harris из Hummingbird (это eXceed, да?) даёт такой хитрый рецепт (выделено мною):
... Instead, you should check for a MapNotify event on only the window you are interested in. Select StructureNotifyMask instead of ExposureNotifyMask to get this event.Note that you might be waiting for a long time if the window manager decides to start your window minimized, or (in the case of twm) if the user is away from the screen. It might be better to set a flag, and check the flag inside the main loop's MapNotify handler.
Заниматься ловлей MapNotify, да еще условной (не всегда ж надо фокусировать, а только при появлении) ради решения поставленной задачи -- это уж слишком, так что ставим "withdrawn".
Чуток позже, в обсуждении вопроса от того же человека за август 2008, Owen Taylor выдал некоторый рецепт, но он мало того, что нетривиален, так еще и требует EWMH (extended WM hints). Так что -- всё равно лесом...
И еще чуток позже: но если уж СОВСЕМ припрет, то всегда можно
последовать
рецепту
GNOME'овцев
за июль 2000 и "засинхронизовываться" путем вызова
XGetWindowAttributes()
и проверкой на
IsViewable
. Хотя это всё равно халтура -- возможен race
condition, когда окно станет не-viewable уже после проверки но до
вызова фокусировки; так что пришлось бы устанавливать error handler.
Короче -- по-прежнему в баню!
18.19.2009@ТНК: а что,
если сделать так: XFlush()
, потом перехватывать error
handler, делать XSetInputFocus()
, еще раз
XFlush()
, и возвращать error handler на место?
30.10.2009: ага, получилось -- только надо делать не
XFlush()
, а XSync()
(иначе -- падает, т.к.,
ответы-ошибки от сервера обрабатываются позже). А работа с error
handler'ом -- теперь стандартна, прямо в Xh.
Работает нормально -- безо всяких отлётов. Только пара тонкостей:
XtManageChild()
вместо XhStdDlgShow()
(кое
применялось ТОЛЬКО в subwin'е да в cx-starter'е (для help-окна));
пришлось исправить. С XhStdDlgHide()
аналогично (хотя уже
и неважно). 4cx/ too.
Так что -- меняем с "withdrawn" за 27-02-2009 на "done".
P.S. А под современным FVWM-2.4.20/2.5 еще надо проверить.
XhStdDlgFApply
и выкинул все связанные с ним упоминания из
Xh_stddlg.c.
26.02.2009: дело в том, что в используемом у нас
типе диалогового окна -- XmMessageBox
-- нету никакой
кнопки "Apply", так что это изначально никак работать не могло. Да и
не нужна нам эта кнопка вообще низачем.
А в 4cx/ оно было ровно так же покоцано изначально.
26.10.2010: провел разборки -- оказалось страннейшее поведение, похожее на заскоки TaskManager'а в WinXP (там при исчезновении задачи, на которой стоял курсор в списке, Esc также перестаёт работать. И в Help'е аналогично.).
А именно: поскольку те элементы создаются со свёрнутой панелью управления, то никаких сфокусированных виджетов НЕТ. И, видимо, оно в таких случаях вообще теряет keypress'ы.
Причём:
Рытьё по bugs.motifzone.net не дало ничего релевантного.
COLOR_NNN
, Xh_color.c.
31.07.2004: наипоследнейшая потребность возникла
при переводе программы "ipp" на Xh. Там требовалось некоторое
количество цветов, а вводить туда код, преобразующий имя цвета в
Pixel
, когда такое уже есть в Xh, как-то странно. Плюс --
пора же, наконец, все эти цвета доунифицировать!
Кроме того, еще энное время назад (в Бледе?) пришла в голову мысль: а что, если сделать цвета "палитрами"/"темами", чтобы их при желании можно было легко менять в одном месте, а уж это место пусть само добавляет в Xrm-БД соответствующие ресурсы. (Что-то не могу найти тогдашнюю запись этой идеи.)
Итого, выбрана следующая стратегия:
XH_COLOR_NNN
(для гарантированной нумерации, вместо
"постарайся-COLOR_NNN
былые циферки заменяются на эти
константы.
Pixel
,
изготавливаемый в XhAllocateColors()
.
Определяются строки БД не линейно, а "[XH_COLOR_NNN]=...".
И вообще -- стараемся иметь ПОЛНУЮ палитру (помним уроки MultiEdit'а :-).
XhSetColorBinding(name,defn)
, которая подменяет
стандартное определение указанным.
Если программа захочет менять палитру, то это должно делаться ДО
первого создания окна -- поскольку именно из
XhCreateWindow()
вызывается
XhAllocateColors()
.
31.07.2004: весь вышеобозначенный план, кроме Xrm-БД, реализован.
В имевшихся пределениях COLOR_NNN
просто
последовательные числа заменены на константы XH_COLOR_NNN
,
новых таких определений делаться не будет -- в будущих программах
надлежит использовать напрямую XhGetColor(XH_COLOR_NNN)
.
07.08.2004: да, теперь оно добавляет ресурсы в Xrm-БД само. Текущее состояние:
defcoldb[]
, и в конце
XhAllocateColors()
оно вызывает
XrmPutStringResource()
для всех этих пар.
XhCreateWindow()
вызов
XhAllocateColors()
переставлен в точку сразу после
создания shell'а -- чтобы даже tooltip уже пользовался этими
определениями (а shell'у окна цвета все равно не нужны).
А вообще-то, ничто ведь не мешает патчить Xrm-БД
еще ДО создания shell'а, а точнее -- вообще сразу из
XhInitApplication()
.
Видимо, стоит перед уставкой ресурса проверять,
нету ли уже такого в БД. Это надо делать, похоже, при помощи
XrmEnumerateDatabase()
-- которая является единственной
функцией, имеющей дело с не-полностью указанными ресурсами.
07.08.2004: несколькими часами позже:
defcoldb[]
, и из fallback'ов они убраны.
XhInitApplication()
, так что надо вызывать
XhSetColorBinding()
еще ДО нее.
13.08.2004: стал разбираться с "неперекрытием
ресурсов". Искал более-менее понятную документацию по
XrmEnumerateDatabase()
. Хм... Документации нету, нигде,
кроме того, что в man'е, зато есть "пример" -- appres.c,
функции DumpEntry()
, PrintBindingQuarkList()
.
В конечном итоге -- все сделал. Внутренности Xrm'а весьма хитрые,
так что моя функция-проверяльщик -- ResourceChecker()
--
сначала собирает в буфере спецификатор ресурса из кварков, а потом
сравнивает его с переданным ей именем кандидата на уставку.
(BTW, наверное, совсем уж корректным-то способом было бы изготовить
свою XrmDatabase, а потом сделать ее merge с имеющейся, отдавая
имеющейся приоритет --
XrmCombineDatabase(нашаdb,XtDatabase(dpy),False)
. Ну да
бог с ним...)
Итого -- цель полностью достигнута, помечаем как "done".
(Парой часов позже: обнаружил, что с самого "начала времен" (еще с uxil) у нас отсутствовало указание highlightColor, зато был ну уж совсем никак не нужный borderColor. Последний трогать не стал, но highlightColor добавил.)
16.09.2004: понадобилось заиметь еще один цвет --
для LOGD_*SLIDER/thermometer. Ввел XH_COLOR_THERMOMETER
,
по умолчанию маппирующийся на "#0000FF" (голубой), плюс ресурсную
строку "*.slider_thermometer.troughColor".
15.11.2004: в понятие "нажимабельности" входят: XmPushButton'ы, XmArrowButton'ы, селекторы, по возможности XmScrollBar'ы. Может, еще и xmToggleButton'ы?
Попробовал просто взять тамошний цвет для таких вещей -- слишком темный.
А вот bg=#c8c8c8, ts=#eeeeee, bs=#969696, ac=#e3e3e3 -- самое то.
Парой часов позже: ню-ню, селекторы (скоты!) делаются из gadget'ов, так что они колоризуются только скопом, включая метку. Ну и че -- делать им метку вручную?
С XmScrollBar'ами не легче -- там цвета теней-то общие для бегунка и "наружности"...
Ну что, забить на это все, что ли?
17.11.2004: поразмыслил -- совсем-то забивать не стоит, а надо сделать более другим цветом то, что можно -- push, arrow, selector, knob.
18.11.2004: ввел 4 дополнительных цветовых кода -- CTL3D_{BG,TS,BS,BG_ARMED}, присвоил им вышеуказанные цвета, и вставил определения ресурсов для push и arrow.
Мдяяяя... Откровенно говоря, смотрится это "не слишком-то"... Повтор старого вывода: с Motif'ом лучше сотрудничать, и если он чего-то ИДЕОЛОГИЧЕСКИ не умеет -- то хрена лысого его заставишь это делать. Вон -- как придется кувыркаться с вводом этой "затемненности" в LOGD_KNOB...
19.11.2004: еще некоторые выводы: просто "другим цветом для нажимабельностей" воспользоваться не удастся -- в SGI'шном Motif'е при "*sgiMode:true" не только ставятся красивенькие цвета, но и делаются двухступенчатые окрыглые тени, плюс имеется тоненькая черная обводка (то, чего так и не удалось добиться в обычном Motif'е).
А с другой стороны -- достаточно вместо "игры яркостью" сделать нажимабельности просто слегка другим (голубоватым либо зеленоватым) цветом -- и все очень даже сработает.
В общем -- сделал "зеленоватым", как основной фон в cx-starter'е. И стало значительно пристойнее! (Хотя и все равно малек топорновато смотрится, но тут уж -- Motif, увы...)
20.11.2004: подобрал еще один цвет -- светло-бирюзовый #c0e6e6 (сделан так: взят зеленоватый, уставлено blue:=green, а red потом малек скинуто, для круглости -- до 192). Весьма недурственно -- глаз на него цепляется, хотя и выглядит оно несколько аляповато.
Протестирован и #e1e9ed из темы "Orbit 3+1 1.2" от Mozilla. Неплохо -- светленький такой.
Попробовал также XV'шный серо-голубой #b2c0dc: немного темноват, но в общем-то весьма неплохо подходит -- дает как раз почти тот же визуальный эффект, что и просто более темно-серый у SGI.
21.11.2004: насчет "у селектора там gadget'ы, так что увы" -- ага, ща-з-з-з!!! Оно-то там, конечно, gadget'ы, но цветовые ресурсы -- очень даже воспринимают! Там есть какая-то концепция "XmLabelGCacheObjRec", и в этой вещи как раз хранятся цвета, получаемые из ресурсов -- так что эта хрень в смысле "а я тупая и все беру от parent'а" является gadget'ом лишь постольку поскольку.
Так что -- все прекрасно оцвечивается! Единственная проблема -- при появлении ресурсов эта дрянь перестала реагировать на перекрашивание parent'а, так что динамическая колоризация не работает...
22.11.2004: проверил в селекторе -- прямой
XtVaSetValues()
с XmNbackground
прекрасно
работает. Так что достаточно будет сделать СВОЙ метод Colorize, который
станет самостоятельно производить и махинации с фоном cascade_w -- и все
будет OK (естественно, придется ввести и privrec).
22.11.2004: устроил презентацию разных цветов "нажимабельности". Хмык...
Старостенко: "наименее мерзкие" -- это бледно-зеленый #dbe6db и мозилловый серо-голубой #e1e9ed. Он вообще хочет, чтобы нажимабельности были в ТОЙ ЖЕ серой гамме, что и основной фон, просто более темными (мнемоника: ручки обычно "залапывают грязными руками").
Консерватор-конформист Гусев вообще разорался в том стиле, что плохо все, кроме умолчательного варианта -- одинакового с основным цветом фона.
В общем -- пришли к тому, от чего начинали: нужен просто-чуть-более-темный цвет. Вариантов два -- либо вышеупомянутый #c8c8c8, либо чуть более светлый #cccccc.
23.11.2004: доделал все с селектором -- теперь там OptionButton(XmCascadeButtonGadget) в смысле цветов обрабатывается так же, как и обычные widget'ы -- и все работает.
06.05.2005: оставались неохваченными "onoff"'ы. Сделал. Хотя -- теперь такие внутренности будут много у кого, пока не перейдем на разное именование read-write- и readonly-виджетов...
21.06.2006: собственно -- уже больше года как сделано, так что "done".
03.02.2011@Снежинск-каземат-11: продолжение кувырканий с цветами в селекторах: в openmotif-2.3.0-0.5.el5@CentOS-5.2 селектор всё время остаётся одного цвета, а цвет меняет только "штучка" (чем-то похоже на bug#1395).
И что -- надо для ЭТОЙ версии Motif'а делать что-то иначе (коли вообще раскрашивание OptionButton'а является некоторым хаком)?
05.02.2011@Снежинск-каземат-11: а вот ПОСИНЕНИЕ -- почему-то работает (и "штучка" при этом -- другого, желтого/зелёного/... цвета)...
22.02.2011: (еще раз) дятел -- это же OpenMotif bug#1356, репорченный МНОЮ аж 01-09-2006, и пофиксенный 26-12-2006 in CVS HEAD!
Вывод: ничего там "иначе" не сделаешь, а просто надо проапдейтить OpenMotif. Вопрос -- НА ЧТО? Вроде в апдейтах CentOS есть 2.3.1-1, исходник которого от 06-10-2008, так что должен бы уже содержать фикс...
??.03.2011@Снежинск-каземат-11: проапдейтил на table.liu2.k11.local до 2.3.1-5.el5_5.1 -- фигушки, ничего не исправилось.
Попробовать собрать из исходников в .src.rpm?
18.04.2011@Снежинск-каземат-11: ага, "проапдейтил" тогда -- как
же! Лучше надо смотреть на диагностику:
Т.е. -- оно тогда просто не поставилось, из-за того, что надо синхронно
обновлять openmotif и openmotif-devel.
[root@table]~oper/201103bolkhov4sn# rpm -Uvh --test openmotif-2.3.1-5.el5_5.1.i386.rpm
warning: openmotif-2.3.1-5.el5_5.1.i386.rpm: Header V3 DSA signature: NOKEY, key ID e8562897
error: Failed dependencies:
openmotif = 2.3.0-0.5.el5 is needed by (installed) openmotif-devel-2.3.0-0.5.el5.i386
А вот при сборке из исходников -- проблемы пофиксены, всё хорошо расцвечивается.
19.04.2011@Снежинск-каземат-11: поставил 2.3.1-5.el5_5.1 -- всё пофиксилось, ура!
15.11.2004: попробовал найти -- пока облом. В качестве затравки пользовался ключевым словом "sgiMode" -- Google сходу не показал нужного, а Insight вообще "No Items Found using the specified search". Бум искать дальше...
В крайнем случае, можно будет просто таблицу составить, используя Sky как черный ящик-конвертер f(bg)=>{topShadow,bottomShadow,arm}.
Кстати, в самом-то Motif'е тоже есть менеджмент цветов --
XmGetColors()
etc., причем, можно даже задавать свою
процедуру-вычисляльщик.
13.06.2006: поскольку Sky помирает, и в любой момент может скопытиться вовсе -- несколько дней лежал, я уж думал что он скончался -- наконец-то дошли руки до вытягивания из него полного комплекта цветов.
Нарисовал на основе example'а "ch_11/allcolors.c" программку allcolors.c, по щелчку мыши перебирающую все градации серого -- #ShShSh, где Sh пробегает от 00 до FF (предполагается, что все три цветовых канала независимы). И прогнал ее:
Результат 1: первые две таблицы отличаются. Ну и фиг с ними :-)
Результат 2, хуже: выданный SGI Motif'ом "armColor" слабо коррелирует с тем, что мы видим на экране...
(Да -- все результаты складываются в ~/IDEAS/knowledge/motif-colors/.)
14.06.2006: высматривать нужные числа в файле -- не особо то удобно, так что накатал еще одну программу (на основе "ch_5/rowcolumn.c") -- 256btns.c, создающую 256 кнопок, с теми самыми 256 градациями серого в качестве цвета фона. А при запуске с argc>1 она меняет местами XmNbackground и XmNarmColor. И с нее сделал скриншоты: 256-linux-openmotif-2.2-bkgd.bmp.gz, 256-linux-openmotif-2.2-selc.bmp.gz, 256-sgi-motif-base-scheme-bkgd.bmp.gz, 256-sgi-motif-base-scheme-selc.bmp.gz (BMP -- чтобы без потерь, как в JPG, а потом за-gzip'овал, чтоб место не жрали; в PNG просто не уверен).
Проблема лишь в том, что XmNarmColor не имеет никакого отношения к тому, как подсвечивает кнопки SGI Motif!
15.06.2006: о! Придумал -- сжульничал: добавил
event handler, ловящий на любой из кнопок EnterNotify и LeaveNotify, и
передающий их, при помощи XtDispatchEventToWidget()
, всем
остальным кнопкам. Так что, все подсветилось --
256-sgi-motif-base-scheme-hilt.bmp.gz.
В общем -- цель достигнута, хоть и иным способом, потому -- "done".
19.11.2004: резво разобрался: дело в том, что
спецификации вида "*.nnn" попадают в БД в форме "*nnn", т.е. лишняя "."
выкидывается. А в ResourceChecker()
'е проверка делается
НАШЕЙ, ВНУТРЕННЕЙ спецификации с той, что лежит в БД. И она,
естественно, всегда не совпадала!
Так что -- попеределывал defcoldb[]
с "*.nnn" на
"*nnn", и проблема исчезла.
20.12.2004: ввел новый код --
XH_COLOR_GRAPH_REPERS
, он пока соответствует черному.
27.12.2004: ввел 5 новых кодов:
XH_COLOR_GRAPH_LINE{1,2,3,4,5}
. В стандартной палитре они
дают: синий, красный, темно-зеленый, золотой/темножелтый,
мутнобирюзовый.
Цвета подобраны так, что первые -- наиболее заметные и контрастные, к концу же бледнеют. Если программа сама подбирает цвета, то при превышении границы в 5 штук она может либо закольцовываться, либо ставить всем "лишним" 1-й цвет -- он самый контрастный.
27.02.2010: учитывая, что у ожидаемого блока ADC8-12ME-PCI аж целых ВОСЕМЬ каналов, то надо б и количество цветов для линий графиков также увеличить до 8...
10.03.2010: тёмно-зеленый, розовый/фиолетовый, коричневый?
Надо обращать внимание также на то, как они смотрятся в варианте "wide".
Несколькими часами позже: сделал:
XH_COLOR_GRAPH_LINE6
:
#EE82EE
-- "Violet" в X11;
XH_COLOR_GRAPH_LINE7
:
#006400
-- "Dark Green" в X11;
XH_COLOR_GRAPH_LINE8
:
#B26818
-- то же, что и JUST_BROWN, VGA'шный цвет 6.
С учётом ранее имевшегося цвета 5 "foggy lightcyan" получается как бы 2 группы цветов: первые 4 яркие и контрастные синий, красный, зеленый, золотой, а за ними еще 4 как бы менее ярких (или более бледных) их варианта (мутно-голубой, фиолетовый, темно-зеленый, коричневый).
Проверил в Chl_histplot.c -- вылезла побочная неприятность: при таком количестве линий подписи к вертикальным осям уже фигово помещаются -- начинают налезать. Чередовать их как-то, что ли? Или писАть реже?
12.06.2005: а-а-а... Ну блин, давным-давно, 03.02.2005 уже заиспользовал примерно этот цвет, правда, почему-то в варианте "#c0e6e6" -- но отличие видно, только если их рядом ставить, а так -- один черт.
Во-первых, черный цвет в качестве REPERS как-то не катит -- он забивает сами графики. Сделать темно-серый?
Во-вторых, темно-синие ("#0000C0") оси -- AXIS -- тоже как-то "не очень". Вот GRID -- можно, а оси -- неа. Они были сделаны такими для ipp, там-то -- с гистограммами -- окей, а вот для обычных графиков -- бе.
05.05.2005: да, сделал -- REPERS теперь "#808080"; AXIS -- черно-синие "#000080", а GRID оставил как есть.
В ipp.c же просто вставил
XhSetColorBinding("GRAPH_AXIS","#0000C0")
.
14.05.2005: давно уже подумалось -- а что я вожусь с этими градациями серого? Надо использовать цвет! Например -- болотно зеленый. Так сегодня и сделал -- "#80B080".
15.05.2005: и-го-го -- поменял на более светлый-бледный "#c0f0c0".
09.03.2006: сделал --
XhGetColorByName()
, XhStrColindex()
,
XhColorCount()
.
22.04.2006: Сегодня же решил "разобраться",
поставив в defcoldb[]
свои "правильные" ресурсы. Ага,
щас!!! Оказывается, эта мерзость еще и цвет ФОНА меняет -- ведь
экранные ресурсы-то вида "*XmForm.background" имеют приоритет перед
нашими "*.background", поскольку они более специфичны. Бл-лин...
Короче -- ну их нахрен, раз ставят такие дурацкие ресурсы -- пусть сами и маются. Возвращаемся к старому отношению -- "withdrawn".
05.05.2006: все-таки доразобрался, кто ж виновник. Точнее, не "кто" (до этого руки не дошли), а "где": есть такая пара директорий -- /usr/share/apps/kdisplay/app-defaults и /usr/share/control-center-2.0/xrdb, так вот в них энное количество файлов, которые содержат описания ресурсов плюс "ссылки" на цвета, похоже, меняемые в зависимости от "темы"; и еще упоминается $HOME/.grdb. Короче, содержимое этих директорий -- в топку!!!
И, кстати, можно просто поискать в системе все файлы "*.ad".
30.07.2007: кстати, в FC* у большинства есть еще одна проблема -- что из-за русской локали в качестве десятичного разделителя используется запятая -- ',', а не точка ('.'). В результате все нахрен едет -- и если для просто чисел еще можно было бы с этим смириться, то у нас ведь в файлах частенько запятая работает как разделитель, например, пары чисел.
Очевидные выводы:
(Хотя если вещественных чисел там нет, а только целые -- то и запятая будет безопасна.)
Во многих случаях можно применять либо точку с запятой, либо слэш ('/'). А для указания диапазонов -- просто сам напрашивается минус ('-').
LC_NUMERIC=C
перед запуском программы, либо вставив строчку
setlocale(LC_NUMERIC, "C");
в самое начало main()
-- еще ДО вызова инициализации
Xt/Motif/...
02.07.2009: вторая попытка борьбы с той дурью с
ресурсами: вставил в defcoldb[]
спецификации
"*statusLine.background" и "*text_o.background", со
значениями XH_COLOR_BG_DEFAULT
. Надо проверить --
сработает ли.
Получасом позже: проверил на FC11 у Феди-jr. -- работает, ура!
19.10.2009@ТНК: а вот в CentOS-5.2 доп.спецификации НЕ ПОМОГЛИ -- всё равно фон текстовых полей дурной.
20.10.2009@ТНК: неа, это ОБЩИЙ фон (XmForm) дурной.
Поскольку все возможные варианты имен объектов XmForm не перечислить, то правильное решение -- в топку те больные директории.
18.07.2006: сделал -- ввел новый индекс цвета
XH_COLOR_BG_TOOL_ARMED
, ресурс-строку
"*toolButton.armColor", смотрящюу на него, и сам цвет --
светлый зеленовато-голубой "#80ccf0".
06.08.2006: некоторое время как стало ясно, что следует заменить ярко-зеленый цвет #00FF00 ( ) на более "мягкий" #00C080 ( ), опробованный на vacclient'е.
А дополнительно вылезло -- что порядок-то цветов у нас странноватый: 1:красный, 2:синий, 3:зеленый. А следовало-то сделать бы наоборот, чтобы "раздражающий"/"опасный" красный использовался уж в самом крайнем случае.
Посему -- переставил, так что теперь идут 1:зеленый, 2:синий, 3:красный. Да, будут проблемы со старыми ipp-подобными программами -- в т.ч. с самим work/pult/xmclients/ipp.c. Но что уж тут поделаешь -- все равно им скоро на покой...
P.S. А там, где нужны КОНКРЕТНЫЕ цвета -- как синий и красный в ipp-спектрометре -- надо указывать именно эти цвета, а не условные индексы.
11.09.2006: Варианты (учитывая использование зеленовато-серого в cx-starter'е) -- либо какой-то просто "другой", либо теплый желтоватый:
К конкретному решению пока не пришел, но, видимо -- теплый желтый #f9eaa0.
10.11.2006: поскольку в adc200 "теплый желтый #f9eaa0" уже прижился, то его и считаем за стандарт. Остальные компоненты -- TopShadow:#fdf8e3, BotShadow:#bbb078, Armed:#fcf4d0.
Кстати, тогда, в сентябре, Саша Александров, увидев adc200 в этом цвете, спросил -- "это что, EPICS?". Оказывается, у них в SNS такой цветовой стандарт: все эпиксности -- желтые.
А так -- "done".
А в Xh_colors -- еще блок цветов (a-la Tek/Agilent).
И в adc200 иметь массив n2colidx[2][n], с которого и отводить цвета.
10.11.2006: в Xh_colors оное сделал. В дополнение к XH_COLOR_GRAPH_NNN теперь есть и XH_COLOR_BGRAPH_NNN.
XH_NUM_DISTINCT_LINE_COLORS
и
XH_NUM_DISTINCT_BAR_COLORS
.
10.04.2007: ну сделал его. Вопрос -- а еще JUST_WHITE не надо ли? Тогда уж просто полный репертуар из поговорки "Как Однажды Жак-Звонарь Городской Сломал Фонарь", плюс белый и черный.
03.03.2009: угу, надо даже не только радугу -- а еще и плюс коричневый цвет иметь, и плюс один-два серых. Вот сегодня занадобилось в liucc сделать кнопочку с коричневым фоном... Может -- вообще всю VGA'шную палитру сделать? :-)
10.03.2009: да, перетряс весь раздельчик "XH_COLOR_JUST_xxx" -- теперь там вначале идут 7 цветов радуги (кстати, "синий" именуется "indigo"), потом BLACK, WHITE, и GRAY, а за ними всякие "полезные" цвета: AMBER, плюс BROWN из CGA/EGA/VGA-репертуара. Прочие -- разные там "pink" -- решил пока не делать, от греха подальше.
За компанию ввел константы XH_FIRST_RAINBOW_COLOR
и
XH_NUM_RAINBOW_COLORS
.
Как водится, 4cx/ too.
#define
-спецификации COLOR_xxx
, чтобы
везде явно использовались XH_COLOR_xxx
и
XhGetColor()
(в 4cx/-то уже изначально этот артефакт
отсутствовал).
04.03.2009: из Xh.h удалил. И все места
использования подправил, местами меняя подбирая переменные с "col" на
"idx" -- наиболее это коснулось ChooseKnobColors()
и
cx-starter.c::EventProc()
. А в
Knobs_alarmonoffled_widget.c вообще баг был -- там по
недогляду ИНДЕКСУ могло присвоиться COLOR_BG_DEFUNCT
.
Снаружи же не-XH_-версия использовалась только в ndbp_image_elemplugin.c
10.03.2009: поскольку "более правильно" всё сделано в 4cx/, то берем за основу его. Добавляем в Xh_types.h строчку
typedef XhWidget CxWidget;
и теперь можно спокойнейше юзать исходники от более новой версии, что и
сделано.
10.03.2009: кстати, конкретно для
Xh_colors.c тот typedef совсем не нужен, ибо там теперь в
_XhAllocateColors()
вообще передается просто
Widget
. А CxWidget
на ДАННЫЙ момент не
используется нигде. Но сейчас начнет :-).
XhAssignVertLabel()
не делается XtReleaseGC()
ни с lgc
, ни с cgc
. Почему?
26.07.2006: посмотрел в исходники XFree86-3.3.*
-- xc/lib/Xt/GCManager.c: XtGetGC()
сводится к
тупому вызову
XtAllocateGC(widget, 0, valueMask, values, 0, 0);
а собственно реальная работа в XtAllocateGC()
и
выполняется. Так что -- похоже, XtReleaseGC()
просто
забыл, а надо делать.
Сделал. Забавно: с одной стороны, это -- "как правильно", а с другой
-- если раньше она всегда отдавала для каждого из этой пары по одному и
тому же GC (ну да, внутри, наверное, увеличивала
ref_count
), то сейчас -- ра-а-азные! Потому как между
вызовами успевает создавать другие виджеты, и для них другие GC.
XhProcessPendingXEvents()
, содержащая
while (XtAppPending(context) & XtIMXEvent) XtAppProcessEvent(context, XtIMXEvent);
Т.е., она обрабатывает все накопившиеся события от X-сервера.
02.03.2007: история такова:
process_pending_xevents()
, ровно это и
делающая.
Так что -- такая функция сделана, и ее вызов вставлен в
Chl_gui.c::EventProc()
, сразу за
UpdateChannels()
. Это помогло уже сейчас, а в v4, где ВСЕ
программы будут "Chl-based", вызовов из иных мест -- типа нынешних
ndbp_*_elemplugin.c::BigcEventProc() -- не понадобится и вовсе.
22.03.2007: увы, не так уж оно и помогает -- в linipp, когда оно пытается рисоваться 12 раз в секунду по 9 графиков через pixmap, тормоза все равно есть, словно выгребания XEvent'ов и нету вовсе... Со стороны сервера приколы, что ли?
24.03.2007: отдельный вопрос -- а ЧТО ТАКОЕ тип
события NoExpose
? Если просто читать документацию -- то
просто бред какой-то, нечто, чего НИКОГДА не должно бы быть, но
почему-то есть. И еще -- наткнулся в Google Groups на thread:
" XForms:
Big time lag using XCopyArea, and get_next_event() event backlog
problem." -- похоже, как раз наш случай. Оттуда следует, в
частности, что NoExpose
генерится в обязательном порядке в
качестве ответа на каждую XCopyArea()
.
Или -- попробовать вместо XFlush()
(сразу после
отрисовки) всунуть XSync()
?
XhInvertButton()
, если оная была дернута
из activateCallback'а, вызванного с клавиатуры -- оно как-то махинирует
с armPixmap/labelPixmap.
11.04.2007: судя по всему, оно САМО зачем-то меняет местами arm_pixmap и (ее внутренний) unarm_pixmap, причем при нажатии мышью это не сказывается, поскольку в момент вызова callback'а кнопка уже "отжата", а вот от клавиатуры -- очень даже сказывается.
Выглядит проблема так: при нажатии на такой XmPushButton пробелом его пары background/labelPixmap и armColor/armPixmap рассинхронизовываются, превращаясь в armColor/labelPixmap и background/armPixmap.
Тот прикол на тему "some callbacks seem to be NOT called" в Motif bug #1352 -- это явно проявление именно данного бага.
Написан багрепорт #1375
29.04.2007: как это можно реализовать -- по моей давней-давней (еще с конца 1980-х или начала 1990-х) идее: каждый пиксел уменьшенной картинки имеет R,G,B равные средним значениям соответствующих пикселов исходной картинки. В данном случае -- Chlf=(C1+C2+C3+C4)/4, где C -- это R, G или B.
Помаялся, сварганил функцию XhHalvePixmap()
, которая
возвращает уменьшенный pixmap (грохая исходник), а внутри реализует
ровно тот алгоритм: вычитывает исходник в XImage, потом его уменьшает
на XImage половинных размеров, и в конце делает XPutImage() половинного
XImage'а на половинный Pixmap.
Ну и дрянь же получилась!!! Во-первых, тормозит просто зверски, даже на локальном X-сервере (еще бы -- ТАКОЕ количество round-trip'ов, пропорционально количеству пикселов), а уж по сети -- вообще тушите свет... Во-вторых -- мерзко выглядят так отмасштабированные ПИКСЕЛЬНЫЕ ПИКТОГРАММЫ (GIMP, кстати, делает примерно то же самое).
Так что, абыдна, но -- withdrawn. Надо для mini-toolbar'а иметь отдельные мелкие пиктограммы. (А функцию не грохаю, оставлю for reference.)
28.10.2009: а вот сейчас -- грохаю, чтоб не мешалась под ногами при унификации Xh_utils.c с 4cx/.
Если что -- в BACKUP'е есть.
XhProcessPendingXEvents()
,
хитрые махинации с событиями и формой из Chl_histplot.c.
02.05.2007: Это:
XhCompressConfigureEvents()
-- грохает все события
Configure
, кроме последнего. Возвращает 0, если виджет
можно перерисовывать, и !=0, если в очереди еще осталось событие,
которое придет позже и в ответ на которое и надо будет перерисоваться.
XhRemoveExposeEvents()
-- грохает все события
Expose
для указанного окна. Надлежит вызывать ее в
ExposeCallback'ах, непосредственно ПЕРЕД отрисовкой.
XhAdjustPreferredSizeInForm()
. Смысл --
"обманывает" форму, подставляя ей в качестве preferred текущие
значения, так что в дальнейшем она будет считать текущие размеры
предпочтительными, что избавит от прыжков. Надлежит вызывать в самом
начале ResizeCallback'ов.
Непосредственно перетаскивание произведено, чтобы этими функциями могла пользоваться adc200.
30.06.2007: лабух!!! Последний параметр,
принимаемый функциями-проверяльщиками, передаваемыми
XCheckIfEvent()
-- это XPointer
, а не
XPointer*
! Заметил при добавлении этих функций в 4cx/.
Поправлено и там, и тут.
17.03.2010: изменения в
XhAdjustPreferredSizeInForm()
. Побудительным мотивом
послужило то, что, оказывается, в программах adc* эта штука не совсем
работала. А именно -- при появлении/исчезновении Xh-hilite окно
схлопывалось обратно в первоначальный размер.
Разборки нашли причину: AdjustPreferredSize делалось далеко не всей
"цепочке", а только собственно графику/axis и/или их
XtParent()'у/XtParent(XtParent())'у. А hilite является child'ом
window->mainForm
'а, и посему при его
появлении/исчезновении mainForm
делает пересчет размеров,
и, ничего не зная о ниже-вложенных виджетах, вертает всё на место.
Так что правильный сценарий -- проходиться ВВЕРХ ПО ВСЕМУ ДЕРЕВУ, и уставлять preferred_{width,height}=cur_{w,h} ВСЕМ, чей parent -- XmForm.
Что и сделано. Итак:
(Заодно, кстати, был исправлен изначальный ляп, что при вызове функции для topmost-widget, у которого XtParent()==NULL, оно бы падало по SIGSEGV -- теперь проверка делается.)
XhAdjustPreferredSizeInForm()
надо для
всех собственно подверженных ресайзингу виджетов -- т.е., graph, axis,
scroller (что во все ныне существующие графикопрограммы и
вставлено).
01.04.2010: после тех изменений стало всё хорошо в *adc*, но начались проблемы в программах с LOGD_HISTPLOT -- weldcc и kuznitsa: графики в них имели размер типа 1*1.
XhAssignPixmapFromFile()
-- чтобы назначала
метке/кнопке пиктограмму аналогично простой
XhAssignPixmap()
, т.е., с корректной трансляцией
"прозрачных" цветов none/#00FFFF и с добавлением при надобности
armPixmap'а.
27.03.2008: сделано. Поскольку
XhAssignPixmap()
уже было два
практически идентичных куска (назначение обычной и armed пиктограмм), а
XpmCreatePixmapFromData()
и
XpmReadFileToPixmap()
имеют одинаковые списки аргументов
(только 3-й у первой указывает на массив строковых данных в памяти, а у
второй -- на имя файла),
DoAssignPix()
, коей передается указатель на функцию
"добычи" (getter) и void*
'й тот самый 3-й аргумент.
XhAssignPixmap()
стала коротюсенькой.
XhAssignPixmapFromFile()
же отличается от своей
предшественницы тем, что к переданной строке добавляет ".xpm".
02.04.2008: поскольку добавление ".xpm" там было явно не к месту, то оно убрано -- теперь клиент обязан предоставить полный путь к файлу.
По-хорошему, надо бы возвращать код "успешности" -- тем более даже
DoAssignPix()
это уже делает -- чтобы более верхний
уровень мог бы ругнуться при надобности.
Несколькими минутами позже: да, так и сделал, для ОБЕИХ функций.
Старый, ранее за-#if0
'енный вариант, удален.
"done".
XhSwitchContentFoldedness()
. Сюда она переехала из
adc200.c, и используется и там, а теперь и в
Chl_gui.c.
4cx/ too.
XhStrDup()
.
И собственно унификация сделана -- теперь файлы идентичны.
XhENTER_FAILABLE()
и XhLEAVE_FAILABLE()
.
30.10.2009: перетащены из cx-starter.c, с несколькими модификациями:
XhLEAVE_FAILABLE()
возвращает "статус" -- 0 если
ошибки не было, и 1 -- если была.
was_X_error
прямо сразу, для проверки не
обломилась ли XGetWindowProperty()
. Переделал
по-правильному -- используется прямо её результат (Success/!=Success).
08.09.2006: наиболее правильным местом для этой
функциональности является Xh_window. Потому туда добавлены две новые
функции: XhGetWindowSize()
(берет размер от mainForm) и
XhSetWindowLimits()
.
Наличествующие проблемы/тонкости:
XhGetWindowSize()
программа
могла узнать получившийся по умолчанию размер окна, и разрешить
добавлять к нему сколько-то.
XSizeHints
, и там, в традициях Motif'а, уставляется флажок
PPosition
, при нулевых координатах, и окно оказывается в
(0,0). Хотя окно как бы уже и отображено на экране, при помощи
RealizeWithoutForcingPosition()
, но, видимо, из-за
буферизации, реально сервер подхватывает "последние" значения, т.е. --
PPosition+нули.
XFlush()
, которая лишь выплевывает буфер из клиента, а
XSync()
, каковая еще и дожидается обработки всех команд
сервером.
Насчет того, ЧТО надо уставлять: есть параметрыXmNbaseWidth
,XmNminWidth
,XmNmaxWidth
,XmNwidthInc
; для height аналогично. Если с min/max/inc еще все понятно, то что такое "base"?Оказалось, что это используется (в комплекте с inc) window manager'ом для "умного" отображения размеров окна. Считается, что
ТекущийРазмер = base + N*incи, к примеру, размер xterm'а в символах показывается именно так -- при помощи base автоматически учитываются всякие поля.Хотя base/min/max/inc могут находиться в довольно произвольном соотношении, в
XhSetWindowLimits()
принята следующая арифметика: inc=1, base=0 -- поскольку ни "символов-клеток" у нас нет, ни какие-то поля учитывать не надо, а пусть просто отображается реальный размер окна в пикселах, который юзер может менять в неких пределах.(А еще и
XmN{min,max}Aspect{X,Y}
-- но управление aspect ratio нас не интересует.)
Блин, вот если б это еще было внятно в какой-нибудь man-странице описано -- а то хренушки! Нашел в конце концов где-то в usenet'е. (Ну да, где-нибудь в ICCCM это, наверное, и описано, но туда, во-первых, мало кто вообще заглядывает, да и утонуть там можно...)
(Кстати, оно там вообще олигофрен -- ключик "-iconic" у xterm'а также игнорирует.)
17.10.2009@ТНК: это игнорирование "-iconic" -- известный баг window-manager'а Metacity, RedHat bugs #215175, #215759.
Насчет resize -- точно не знаю, но есть аналогичный bugreport #188576 "Doesn't follow Xt's size hints".
XhSetCommandOnOff()
и XhSetCommandEnabled()
кидались в бой, даже не проверяя, а есть ли toolbar вообще! Результат
-- SIGSEGV...
22.09.2006: исправил. долго ль было вставить
проверку про window->toolHolder==NULL
.
07.05.2007: ежу понятно -- надо вместо нынешних махинаций с xmNchildren toolHolder'а вводить некий per-XhWindow список, в который добавлять все кнопочки. И тогда функции создания "альтернативных" тулбаров будут просто так же добавлять кнопки в этот список, и эти кнопки далее ничем не будут отличаться от обычных.
XhACT_LABEL
, макрос XhXXX_TOOLLABEL(lab)
.
Служит для создания в тулбаре крупных меток-надписей. Потребность
такая возникла у fastadc_common-based программ: они шибко похоже
выглядят, так что чтоб различались.
06.09.2010: хм -- тогда сделал только в liu/xmclients/'ных программах, а в src/programs/xmclients/'ных -- забыл.
Сейчас добавил и в весь тот зоопарк.
Xh_ACT_NOP
. Смысл -- чтобы программа могла грохать часть
кнопок из заранее сделанного массива.
06.09.2010: Реализовано. Всё несложно, только
пришлось не просто пустой селектор в switch()
вставить, а
еще count--
-- т.к. оно в конце цикла безусловным
порядком делает count++
, и всё бы съезжало.
А макрос делать не стал, ибо незачем.
11.05.2007: сделал. Получился довольно
простенький модуль, примерно такого же размера, как и сам
cxscheduler.c. Функции sl_main_loop()
и
sl_break()
там отсутствуют, поскольку были бы не к месту
-- основным циклом занимается Xt.
Туда закладывалась идея, что "добавить" и "удалить" файловый
дескриптор -- операции нечастые, поэтому не обязаны быть быстрыми. А
вот обычная работа с дескриптором -- должна выполняться максимально
быстро, безо всякого поиска по таблице. К сожалению, это допущение
ломается функцией sl_set_fd_mask()
, которая может
вызываться довольно часто, для управления флажком SL_WR при
начале/окончании отправки больших пакетов... Еще один аргумент в
пользу того, что надо с прямой адресации по дескрипторам переходить к
"косвенной", по handle'ам.
Кстати, хорошо видно, что cxscheduler под Win32 (на completion ports) будет практически братом-близнецом Xh_cxscheduler'а.
14.05.2007: естественно, пока этот модуль жил в libXh, линковаться ничего не могло -- выходили циклические зависимости между библиотеками.
Так что вытащил его в свою, отдельную libXh_cxscheduler.a и сделал в Rules.mk макрос LIBXH_CXSCHEDULER.
21.10.2012@Снежинск-каземат-11: добавлено использование
свежевведённого SL_CE
. Вроде работает (@CentOS-5.2,
libXt-1.0.2-3.1.fc6).
08.11.2012: да, вполне работает и в RH-7.3, и в SL-6.3. Так что "done".
Всё, что удерживает от этого -- использование
xh_context
. Достаточно заменить фиксированные ссылки на
него на некую переменную, чьё значение клиентам указывать отдельным
вызовом, ПОСЛЕ инициализации Xh и еще ДО любых прочих действий.
04.01.2015: угу, указывать надо сразу после
вызова XhInitApplication()
.
Вот только мест таких -- слегка дофига (как в v2, так и в v4).
07.02.2008: собственно, оно сделано -- очень
легко и быстро. Технология -- 4 виджета (класс Core), образующие
стороны рамки, приаттачиваются в такие координаты в
mainForm
, чтобы обрамлять указанный виджет. Единственное
что -- неприятной была возня с XtTranslateCoords()
,
которая непроходимо неадекватна, в отличие от
XTranslateCoordinates()
.
Поскольку stacking order виджетов -- "кто раньше создан, тот и
выше", то XhCreateHilite()
вызывается из
XhCreateWindow()
сразу же после создания
mainForm
; иначе пришлось бы -- при создании
при-первом-использовании -- выпендриваться с mainForm'овым
XmNinsertPosition
.
И еще легкая халява -- поскольку прямоугольничек привязывается к
mainForm, а не к самому виджету, то при перетряхивании (например, при
сворачивании/разворачивании колонок или элементов) виджет съезжает, а
прямоугольничек остается на месте. Но, увы, человеческого решения для
этой проблемы нету -- поскольку виджет может располагаться внутри
произвольного менеджера, чаще всего -- сетки. Неприятность снижается
тем, что XhShowHilite()
вызывается при КАЖДОМ обновлении
ki->usertime
, так что при нажатии следующей же кнопки
прямоугольничек прыгает в правильное место.
XhShowHilite()
и XhHideHilite()
не проверяют
переданный им виджет на !=NULL...
25.03.2009: да, сделал проверку -- для этого
теперь еще и window = XhWindowOf(w)
делается не в
декларации, а дальше, после проверки.
XhCreateStdDlg()
) -- нет, и при данной схеме -- В ПРИНЦИПЕ
не могут, ибо привязаны к самому окну.
(И, кстати, фиг знает, почему она ПРОСТО НЕ ПОКАЗЫВАЕТСЯ, а не отображается, например, в бредовом месте основного окна.)
23.07.2010: продолжение этой же темы:
А сегодня понял -- это потому, что она пытается вывести hilite ровно в том месте mainForm'а, над которым располагается "обрамляемый" XmText; а оный лежит на экране за пределами основного окна -- вот оно и подгоняет размер mainForm'а так, чтобы типа "вместить" hilite.
Поскольку ни в Xt, ни в Motif'е не нашлось способа определить
"является ли виджет Y иерархическим потомком виджета X", то
просто была взята почти копия функции
Xh_utils.c::IsAncestorOf()
, с легкой адаптацией с
Window на Widget, и названа IsAncestorWidgetOf()
.
Засим подрыгивание основного окна исчезло.
15.06.2014: вроде сделано -- по образцу с cx-starter'а, но работает кривовато.
Но XmLiteClue-то ведь работает, значит как-то можно. Как?
...пока оставляем отключенным по #define USE_OVERRIDE 0.
28.06.2014: еще немножко поразбирался -- странно:
Типа протокола дальнейших разборок:
Вот же ж чёртовы хитрости Motif'а, нет бы всё сделать унифицированно...!!!
Еще пятью минутами позже разобрался: это прикол конкретно нового Chl_knobprops.c, с RANGES_INLINE -- оно делает XhShowHilite() в своём modifyVerifyCallback'е, коий вызывается при прописывании значений в поля.
Так что просто убрано использование hilite в Chl_knobprops (раз пока не умеет себя вести :D).
#define USE_OVERRIDE 1
-- т.е., переходим на новую версию.
Можно было бы поместить его в отдельную библиотеку, но, по аналогии с сеткой -- пусть будет модулем в Xh'е.
05.05.2008: кстати, надо бы битым текстом прописать пожелания/требования к этому компоненту:
Видно, что даже для УЖЕ имеющихся потребностей разнообразие совершенно недетское. Так что надо придумывать какую-то фрагментацию, "разделение обязанностей". Видимо, как-то отделить мозги, отвечающие за реакцию на resize, от мозгов рисующих. Насколько это удастся -- вопрос, поскольку задачи действительно реально сильно разные, и компоненты рисования графиков (в той же Delphi) -- одни из самых навороченных среди прочих компонентов.
08.05.2008: в продолжение --
Отдельная проблема тут -- что viewport-type график может иметь размер, превышающий наличествующий объем (например, ширина ADC -- 500px, а намеряно 100 точек). Что делать?
P.S. Да, кстати, по умолчанию все буфера будут считаться кольцевыми, чтобы легко делалась работа с "историей". Лишняя арифметика при этом пренебрежимо тривиальна, а параметры ring_start и ring_size будут по умолчанию уставляться в совместимые с не-кольцевыми буферами значения.
13.05.2008: еще парочка пожеланий, по опыту имеющихся программ:
Как это можно было бы реализовать --
FlushXLines()
, помнить также
"активный" GC, которым должны отрисовываться уже-аккумулированные в
массиве точки; при смене же "текущего" GC массив также будет
"выталкиваться".
Если первое делается на раз, то со второй фичей пока маяться совсем неохота -- муторно.
14.05.2008: приступил уже серьезно к реализации модуля. Сготовлен скелет функции создания. Взята технология из Chl_histplot: есть ДВЕ DrawingArea, плюс "поля" делаются при помощи 4 невидимых виджетов, к которым приаттачены plot_graph и plot_axis -- первый к внутренним краям, а второй к внешним (через OPPOSITE_WIDGET). Сами же правый и нижний невидимые виджеты прикрепляются внешними краями либо к форме, либо -- при наличии оных -- к scrollbar'ам.
15.05.2008: хорошенько подумавши: мы ведь имеем
кучу параметров в Xh_plot_t
и
Xh_plot_t.plots
; если первые еще упихиваемы в параметр
flags
, то вторые -- придется сваливать их менеджмент на
программу, что 1. нехорошо и 2. не позволит полностью
обсчитать геометрию прямо при создании.
Отсюда очевидная идея: пусть у
XhCreatePlot()
будет va-список параметров, в стиле Xt --
парами ПАРАМЕТР,ЗНАЧЕНИЕ, NULL-terminated. Исполняться его обработка
будет отдельной функцией, которую можно также будет вызывать и потом --
для изменения, например, dpyfmt'ов или пределов.
19.05.2008: только махонькая поправка: поскольку
обозначения параметров будут int
(а не
char*
) -- т.к. никакой сторонне-расширяемости особо не
надо (да числа и удобнее, очень во многом, особенно при указании
параметров графикам в виде ТИП_ПАРАМЕТРА+НОМЕР_ГРАФИКА), то и
список будет не NULL-, а 0-terminated.
19.05.2008: да, сделал эту инфраструктуру. Теперь буду потихоньку наполнять ее параметрами.
23.05.2008: несколько дней назад попробовал прикрутить scrollbar'ы, хотел на полном автоматизме -- чтобы они "длинными" сторонами аттачились к форме, а концами к "стратам". Хренушки -- эта долбаная форма опять обнаружила себе циклическую зависимость.
Решил проблему переходом на полу-ручные аттачменты -- концы аттачатся к форме, просто одновременно с уставкой длин "страт" и этим концам уставляются offset'ы. Уродство, конечно -- ведь приходится руками учитывать для нижнего и правого концов наличие перпендикулярного коллеги -- чтоб приплюсовать его толщину к offset'у. Но зато работает -- и то радость (в отличие от глючащего во многих вложенных формах ресайзинга).
19.12.2011@Снежинск-каземат-11: будем переименовывать в Xh_plot то, что выросло в liu/y/Zz_plot.[ch], а этот, так и близко не достигший рабочего состояния, изводим, ставя раздел в "obsolete".
17.12.2010@Снежинск-каземат-11(записано нигде не было, токмо мысли, так что записываю их 10.01.2010): некоторые примерно-приемлемые достижения в графикостроительстве были получены при реализации fastadc-графиков в liu/fastadc/.
Т.е., отдельно:
Отличие -- КАЖДЫЙ график может быть своего отдельного типа.
17.12.2010@Снежинск-каземат-11: начинаем начинять plotdata.h.
21.12.2010@Снежинск-каземат-11: на тему корректного изменения размера (пока что -- по опыту liu/fastadc/).
Задача: разобраться с аттачментами: чтобы при смене шкалы/ручки в строке оно бы меняло лишь размер полей-аттачментов, а НЕ увеличивало бы размер всего графика.
23.12.2010@Снежинск-каземат-11: идейка, как можно при скроллинге графика "сдвигать" фазу чёрточек на сетке, не лазя при этом в GC.dash_offset: можно ставить начальную координату линии "левее", в МИНУС-сколько-надо -- при этом лишняя часть просто не нарисуется (отсечётся), а фаза -- прокрутится. 31.01.2011@Снежинск-каземат-11: сделал -- работает!
29.01.2011@поезд-туда:
31.01.2011@Снежинск-каземат-11: кстати, о реперах: можно приподвыпендриться и сделать, чтобы при уставке/передвижении реперов еще и курсорчик менял бы форму. А именно:
XC_sb_up_arrow
.
XC_sb_left_arrow
.
XC_tcross
.
Главный вопрос тут -- выбор момента смены курсора. Ведь даже если курсор стоит на графике, то сфокусирован совсем другой виджет, и KeyPress-event НЕ попадёт графику. Так что:
04.02.2011@Снежинск-каземат-11: вот получается, что мы весь этот набор заклинаний -- в {Graph,Axis}{Exposure,Resize}CB() -- тащим уже в очередной новый файл; а ведь старые вопросы (включая проблемы с изменением размера не только в плюс, но и в минус) еще не решены до конца.
Тем самым мы действуем в рамках ООП -- инкапсулируем чётко ограниченный функционал, не примешивая туда никаких графиков, а уж графики как бы "наследуют" этот "класс".
Вопрос, конечно, не перекрывается ли этот "мультивиджет" с XmScrolledWindow и не использовать ли лучше её. Но, как показывает опыт реализации Knobs_slider_widget (первая попытка на основе XmScale) -- проще сделать самому, чем пользоваться какими-то Motif'овскими надстройками, связанными со скроллингом.
Назовём эту штуку "Xh_viewport".
13.02.2011@Снежинск-каземат-11: за это время модуль Zz_plot.[ch] совместно с сопутствующими потихоньку населяется и обрастает функционалом, хотя пока и не готов.
НО: пока что, для упрощения задачи, реализовываем старую модель отображения -- БЕЗ масштабирования по горизонтали, 1измерение=1px, и тем более безо всяких "резиночек". А потом, когда сделаем хоть так, можно будет и легко переделать -- ибо теперь ВСЁ знание о вписывании и масштабировании живёт исключительно в Zz_plot. 17.02.2011@Снежинск-каземат-11: замечание: но концепция "magn" при этом всё равно останется -- поскольку она позволяет менять масштабы ОТДЕЛЬНЫХ графиков.
15.02.2011@Снежинск-каземат-11: связка Zz_plot+Xh_viewport доведена до минимально работающего состояния -- рисует подписи, рисует графики, ресайзится, скроллируется.
Замечена же изрядная неприятность: мы напрямую используем координатную систему X11, с прямым соответствием измерение<->пиксел, но там ведь числа 16-битные, т.е., диапазон [-32768,32767], и при большом количестве измерений на экране начинается полный пипец.
Так что -- даже еще до перехода на "идеальную" схему с масштабированием надо бы как-то приподулучшить схему, чтоб оно пыталось X'ам сбагривать только "видимое" содержимое.
19.12.2011@Снежинск-каземат-11: да, сделано -- уже в liu/y/, и сейчас переехало в основное дерево. Это именно скорее первоначальная версия -- только для XH_PLOT_Y_OF_X -- но уже достаточная для собственно графиков от быстрых АЦП, так что считаем данный раздельчик за "done".
А можем мы просто постараться сделать так, чтобы точка, ДО изменения масштаба находившаяся в центре, и ПОСЛЕ также оставалась бы в центре?
Т.е., в XhPlotSetCmpr()
также уставлять и horz_offset (для
чего надобен бы какой-нить viewport-API), а если оно будет "за границей", то
XhViewportSetHorzbarParams()
"впишет" его в разумные пределы.
Что странно -- подписи к вертикальной оси при этом масштабируются нормально.
17.08.2012: с подписями стало ясно -- они УЖЕ считаются сразу в транслированных координатах (приведённых к "ноль в центре").
Для отрисовки же используются изначальные "сырые" числа, НЕ
проходящие через цепочку трансляции raw->pvl->dsp. Вместо этого
XhPlotOneDraw()
НАПРЯМУЮ сам делает RESCALE_VALUE()
сырого_значения*magn.
И что теперь -- вводить доп.параметр "истинный ноль", который
вычитать из числа перед домножением на magn? И куда вводить -- в
plotdata_t
или сразу в plotdata_range_t
?
Подумавши -- да, введём. Оно осмысленно только для int, плюс,
поскольку plotdata_t
-- это union, то делаем
plotdata_t.cur_int_zero
. И, кстати -- уставляться оно будет в
_info2mes(), просто в число, равное соответствующему ZEROn. Неа, не =ZEROn, а =255-ZEROn.
Часом позже: сделано, работает. Только формула хитроватая: делается
(iv - cur_int_zero) * magn + cur_int_zero
-- т.е., оно домножает именно РАССТОЯНИЕ ОТ НУЛЯ (делая сдвиг к нулю, а
потом результата обратно от нуля).
Конкретно с adc200, конечно, некая проблема -- что именно за число ставить. Сейчас сделано 255-ZEROn, но, возможно, надо 256-ZEROn -- мутновато там...
Пока считаем за "done", хотя на живой установке надо будет еще погонять, чтоб убедиться в корректности вычислений.
18.11.2013: что-то там всё-таки не так с nadc200: по ощущениям будто бы сдвиг нуля применяется к уже отмасштабированному числу, а не к исходному измерению...
05.03.2013: да, внутренняя реализация сделана
аналогично histplot'у, а выбор режима --
XhPlotSetLineMode()
.
Для связанной фичи -- возможности размещать нечто "поверх" графика,
в обеспечиваемом viewport'ом контейнере angle -- введена
XhAngleOfPlot()
.
В fastadc_gui.c использование вставлено -- создаётся переключатель режима отображения. Плохо лишь, что он "обычного" размера, а "small" choicebs'у указать нельзя никак.
Всё вместе работает.
28.05.2013: поскольку возможность указывать "size=small" появилась, то добавлено.
Причина -- часть контактов, идущих на измерения adc812me стоек, запаяна с перепутанной полярностью. И утверждается, что на обычном осциллографе такое инвертирование делается на раз.
А в Xh_plot вот сходу красивого быстрого решения не видно...
02.04.2014@Снежинск-каземат-11: (часом позже) результат разборок
и анализа: вертикальное масштабирование в конечном итоге сводится в параметр
magn
у функции-рисовальщика (в основном
XhPlotOneDraw()
), содержащий прямо коэффициент.
А если ввести возможность указывать дополнительный множитель для передачи в magn, который будет либо +1 (прямо), либо -1 (инверсия).
А собственно параметр (у fastadc) назвать straight/inverse.
Делаем:
one_plot_t.plrt
("polarity").
XhPlotOneSetInvp()
: invp==0 =>
plrt=+1 else plrt=-1, и в XhCreatePlot()
инициализация =+1.
DrawPlotView()
и DrawPlotAxis()
.
fastadc_dcnv_one_t.invp
.
FASTADC_GUI_CTL_LINE_INVP
.
ChnSwch_prm[]
и
Magn_prm[]
используются для одного и того же -- в них пишутся
{gui,nl}.
Поэтому вместо введения еще одного (Plrt_prm[]) оба старых сведены в один
Line_prm[]
(пишет туда любой из троицы, но пишет одно и то же).
Проверено (всё, кроме экранной ручки, никем не создаваемой) -- работает.
14.05.2014: подробности:
Вкратце -- оно для отображения в statusline пыталось печатать огромное (длинное) число в буфер char[100], которое его переполняло, перетирая стек. Ведь double может быть до e308, что в формате "%8.3f" даст 308+ символов.
(Да, проблема проявлялась из-за ошибки в manyadcs_knobplugin.c -- вместо double-данных брался мусор, но библиотеки ж должны защищаться от переполнения сторонними non-trusted данными.)
XhMakeTempMessage()
-- делается интеллектуально, с проверкой "а
осталось ли еще место в буфере".
Это сделано как в отображении для statusline, так и для реперов в pzframe/fastadc_gui.c, и еще в нескольких похожих местах.
21.05.2014@Снежинск-каземат-11: за вчера-сегодня доделан
перевод всего и вся на snprintf_dbl_trim()
.
Стал делать -- и тут началось...
19.09.2014: оказалось, что НЕТ способа указать
XhCreatePlot()
'у какие-либо параметры. Даже "nofold"
делался извратно -- подселялся к cpanel_loc
.
Стало ясно, что надо переделывать cpanel_loc
в более
общий options
. Что и было сделано.
Нововведённый флаг получил название
XH_PLOT_NO_SCROLLBAR_MASK
.
Хоть информация при растяжении не добавляется, но отрисовка линий между соседними x'ами действительно даёт не очень чёткую картинку.
Just for record: еще давно решено, что
fastadc_data_cmpr_factors[]
, и тогда растяжение делается в -N
раз.
x_cmpr=factor,x_xpnd=1
иначе
x_cmpr=1,x_xpnd=-factor
.
07.10.2014@Снежинск-каземат-11: да, и Ярик Куленко подтверждает про "полочку".
30.07.2015: в основном сделано, по тому самому проекту.
scale32via64()
.
25.08.2015: сделано -- чтобы при внедрении в v4 уже было бы всё готово "как надо".
grf_w
теперь используется rgt_lim = grf_w + plot->x_xpnd - 1
.
...а вот «указатели на начало данных аналогичным образом сдвигать "влево" на 1» совсем НЕ надо. Вначале попробовал, и долго не мог понять, почему оно неправильно рисует. Ведь ЭКРАННАЯ КООРДИНАТА-то уже сдвинута, а данные сами "сдвинутся" с ней (и при дополнительном -=1 указателю оно начинало брать из [-1]-й ячейки).
Засим считаем за "done".
Как 3-й и 4-й реперы ставить? Например, при нажатой Shift... (А вертикальные -- при Ctrl.)
Мрак...
09.10.2014@Снежинск-каземат-11: поразмысливши стало ясно, что нефиг такое усложнение -- надо liu'шные графики бить на 2 части каждый, и с частями уже обращаться как с обычными, где и одной пары реперов хватит. Подробнее об сим за сегодня в данном bigfile-0001 и в 201410-SNEZHINSK-ACTIVITY.txt.
Так что -- "withdrawn".
Это вылезло с ADC333 (в v4, с балакинским диссектором), у которого часть линий может отключаться. Выглядит странновато -- график один, а подписей толпа.
14.07.2016: причина нашлась в 201110-SNEZHINSK-ACTIVITY.txt за 13-11-2011:
- Кстати, а надо ли там вообще использовать .on? Казалось бы -- раз show включено, то и рисуй подписи, а .on пусть влияет уже только на графики. Так и сделал -- и в отрисовке, и в подсчёте.
Поскольку выглядит некузяво, то переделано на учитывание наличия данных.
Ну и заодно стало ясно, почему в своё время это было убрано -- потому, что при старте, когда данных еще нет совсем, то и подписей к вертикальным осям тоже нет совсем (ибо все on==0). Тоже, конечно, не шибко красиво, хотя и корректно :)
Сам модуль будет жить в Xh_viewport.c, а его интерфейс -- в
Xh_viewport.h (но никак НЕ в общем Xh.h). Его публичная
структурка -- Xh_viewport_t
.
Вот в том же Xh_viewport.h будет ПРЯМЕЙШЕЕ использование X11/Xt/Motif.
Поскольку Motif, похоже, действительно устаревший/умирающий тулкит, то под него никто никогда уже ничего толкового/доступного не сделает; а мы на нём засели, и на современные (пусть не самые красивые, но ЖИВЫЕ и развивающиеся) не переходим. Хотя, QWT -- тоже не сахар :-).
На эту мысль вчера навёл Панов, поинтересовавшийся, что же то за хрень такая из кучи прямоугольничков.
Всё стало изрядно красивее и элегантнее -- простой и очевидный код в Xh_viewport, а из fastadc_gui многочисленная ловля ButtonPress'ов убрана, вместе с
(Конечно, пришлось помучаться -- как водится, из-за шаманства с аттачментами, поскольку сам виджет cpanel использовался для приаттачивания gframe'а; раньше менялось содержимое cpanel (либо набивка, либо виджет folded), теперь же он стал исчезать целиком. Решение простое -- добавлен промежуточный sidepanel, используемый для аттачментов, и никуда не unmanage'мый, а махинации делаются по-прежнему с cpanel'ом. Недостаток -- что sidepanel полностью не исчезает, а остаётся тонкой полоской в 1 пиксел, но это уже прикол X11.)
05.03.2013: делаем просто -- viewport ВСЕГДА
создаёт пустой XmForm-контейнер, под названием angle (поле
Xh_viewport_t.angle
), только не-Managed. Кому надо (adc)
-- с-manage'ат его. Размещается angle аналогично [-][+], в
противоположном от того угле вдоль стороны, с которой cpanel.
Проверено -- работает.
03.04.2013: несколько замечаний:
Сами if()'ы, конечно, тоже усложнятся, но это мелочи по сравнению с аттаченьем в середине.
Конкретно иногда при переходе между десктопами (table/CentOS-5.2) НЕ обновляется график manyadcs. При том, что axis -- обновляется. И начинается это не сразу -- на свежезапущенной программе всё окей, а потом, в какой-то момент, перестаёт обновлять по Expose, а остаётся только по собственной инициативе.
22.05.2014@Снежинск-каземат-11: Натравливание gdb на живую программу --
-- показало, что там постоянно горит ignore_view_expose=1.gdb /proc/`/sbin/pidof liu`/exe `/sbin/pidof liu`
Точно какое-то нарушение баланса =1/=0 в ViewResizeCB()
. А
поскольку там всё ВРОДЕ БЫ просто (даже тривиально), то концы ведут к
XhCompressConfigureEvents()
. Но там тоже всё просто. Итак --
наверное, дело в сочетании с XhRemoveExposeEvents()
, уже опять
во viewport, в ViewExposureCB()
?
...а ведь раньше где-то такой баг уже встречался (в файлах нет -- видимо, в органайзере на E90). И на пульту частенько вылазит в adc200me или nadc200...
XH_VIEWPORT_NOFOLD
.
Был выпестован в v2hw/vcaming/ (в значительной степени копированием из ndbp_image_elemplugin.c), а теперь переехал в основное дерево.
10.07.2012: отличие от Xh_plot -- поскольку тут никакой стандартизации органов управления нету (типа реперов, многоканальности, ...), то оно создаёт голый отображатор; всё декорирование лежит на юзерах.
Хитрости типа мышемахинирования (отображение значения пиксела под курсором, выделение мышью) пока не сделаны, но делаться будут аналогично plot/viewport'ным.
19.07.2012: отображение значения пиксела под
курсором сделано, работает. Да и вообще в первом приближении (в
интересах ottcamv'шного zzz) работает, так что всё следующее -- уже
отдельными пунктами.
19.01.2013: ага, работало, только значение не
обновлялось с картинкой. Добавлен вызов ShowMouseStats()
в
XhMonoimgUpdate()
.
23.06.2016: одна "тонкость": отображается значение из
dsp_data
-- т.е., уже отнормализованное, а не исходное из
mes_data
. Как правильнее -- вопрос (оба?).
09.08.2012: добавлен еще параметр
"srcmaxval
" -- в нём можно передать, какое максимальное
значение теоретически возможно (с матрицы).
Если оно находится в пределах (0,(2^bpp)-1) (НЕ включительно), то при max_red это значение также показывается красным (при отключенном normalize).
Причина -- кривизна в использованной Оттмаром матрице: которая при 10 битах максимальное значение даёт 1022.
08.01.2014: добавлена поддержка 4-байтных данных, просто копированием функций от 2-байтных с заменой "int16" на "int32" (понадобилось для pzframes/tsycamv).
14.03.2020@дома-суббота: также добавлена поддержка 1-байтных, тоже копированием функций от 2-байтных с заменой "uint16" на "uint8" (понадобилось для img878).
18.01.2013: в принципе, это не особо сложно -- надо копирование из mes_data в dsp_data выполнять с соответствующим преобразованием, а не просто линейно.
Другое дело, что это только СЕЙЧАС копирование всегда делается
тупенькой PerformCopyMskd2()
, которую подменять несложно,
а вообще-то может быть и PerformUndefect*(), где маеты поболее.
24.01.2005: ну дык -- там вообще ничего нет на эту тему.
Сделал, подсмотрев технологию в Xm/ArrowB.c. Итого -- ввел
доп. поле insensitive_gc
, которое мэинтейнится наравне с
normal_gc
, и при отрисовке если не-sensitive, то
используется оно. Плюс, метод SetValues()
возвращает
True
и при смене состояния sensitive.
Единственное, что мерзко -- пришлось воспользоваться функцией
_XmGetInsensitiveStippleBitmap()
, которая приватна для
содержимого lib/Xm/, ибо содержится в ScreenI.h, НЕ
устанавливаемом в /usr/include/Xm/. Или это надо добывать
как-то в другом месте?
Чуть позже: да, нашел: оно достается при помощи ресурса
XmNinsensitiveStippleBitmap
объекта
XmScreen()
, который, в свою очередь, добывается при помощи
функции XmGetXmScreen()
. Ну и мудрилово!!!
Надо б еще на будущее как-нибудь корректно отрабатывать случай, когда sensitivity меняется при нажатой одной из кнопок.
Получасом позже: сделал, просто скопировав соответствующие строчки
из IncDecBDisarm()
: оно при смене sensitivity ВСЕГДА гасит
нажатость и автоповтор. Но -- не тестировал, шибко маяться бы
пришлось.
Вечером того же дня: протестировал -- работает, но обнаружилась
недоделка (ныне исправленная): в TimerProc()
надлежало
запрашивать таймаут не безусловно, а только если мы sensitive.
Итого -- считаем, что все сделано, и помечаем как "done".
20.12.2010@Снежинск-каземат-11: мотивировка -- Панов предложил сделать, чтобы реперы можно было двигать не только мышью на графике, но и с клавы. С клавы -- хрен, т.к. пришлось бы вводить механизм фокусировки реперов.
Но вот точную настройку сделать можно: если рядом с полями "время репера" разместить кнопочки [v\^].
Только для этого придется ввести в IncDecB возможность работать БЕЗ клиентского виджета -- т.е., не KeyPress'ы посылать, а вызывать callback'и.
InputOnly
.
24.07.2006: изготовлен, путем копирования нужных
компонентов из IncDecB{.c,.h,P.h}, и в Realize()
класс окна указывается InputOnly
. Помимо уставки в
Initialize()
значения core.depth=0
, "от греха
подальше" были предприняты специальные меры:
Initialize()
и SetValues()
принудительно сбрасываются в 0 traversal_on
,
highlight_on_enter
, highlight_thickness
, shadow_thickness
,
border_width
.
HighlightBorder()
и UnhighlightBorder()
,
которые пусты.
25.07.2006: проверил. Первоначально -- не
работало, давало BadMatch
. Разборки показали, что дело --
в значении value_mask
. В нем присутствовали передаваемые
из Xt битики WBackPixel|CWBorderPixel|CWColormap
, что и
приводило к ошибке.
Достаточно было передавать value_mask:=0
, но: путем
рытья по исходникам X-сервера (3.3.*) в
xc/programs/Xserver/dix/window. обнаружилось магическое слово
INPUTONLY_LEGAL_MASK
, которое и используется в
ChangeWindowAttributes()
-- если присутствуют не указанные
в этом слове биты, то функция отдает BadMatch
. Описание
"магического слова" --
Так что сделано оставление только этих битов.#define INPUTONLY_LEGAL_MASK (CWWinGravity | CWEventMask | \ CWDontPropagate | CWOverrideRedirect | CWCursor )
Гадость же в том, что НИГДЕ в man-странице по
XCreateWindow()
об этом ограничении сказано не было
-- только про то, что depth
должна быть 0, как и
border_width
. Да, в принципе, из man-страницы по
XChangeWindowAttributes()
можно было сделать такой вывод,
но, во-первых, он там неочевиден, а во-вторых, в нее еще надо было
залезть -- а по идее все ограничения должны быть перечислены в описании
самой XCreateWindow()
.
В общем -- работает, "done".
13.02.2008: имелся мелкий огрех -- в InputOnly.c, после копирования из IncDecB.c, оставались буквосочетания "incdecbutton". Они сменены на "inputonly"; в 4cx/ также проапдейчено.
30.06.2008: решение проблемы с отрисовкой круга фокуса у крутилок -- это просто сделать свой manager widget, который бы имел focusCallback+losingFocusCallback.
01.07.2008: видимо, наследовать надо от
XmManager. А вот где брать функциональность фокуса... Возможно,
ключевые слова -- Core и accept_focus
.
03.04.2009: СТОП! А если подумать хорошенько -- конкретно в случае dial'а, как нас спасет наследование именно от XmManager? В какое место иерархии его там вставить?
Из общих-то соображений -- да, такой класс был бы полезен. Но конкретно для LOGD_DIAL -- нет, поскольку кроме собственно dial'а в его форме еще может быть TextInput, который также фокусабелен.
Ну что, полезем в сашин код? А не дурканёмся?
28.02.2009: кстати, такая фича станет дикой
нагрузкой на Chl_multicol.c/MotifKnobs_grid_cont.c --
ведь учитывать эти colspan/rowspan при раскладке ручек по сетке тоже
довольно нетривиально. А еще и метки надо учитывать, и
многоподъездность, и проверять, чтобы ничей x+colspan
не
превышал numcols
, а y+rowspan
не превышал
nflrs
; да и вычисление nrows
(и
nflrs
по умолчанию) будет нетривиально.
Видимо, выход будет в создании массива флажков, в котором при размещении каждой ручки будут уставляться флажки, соответствующие занимаемым ею клеткам. Соответственно, следующие ручки будут "перескакивать" через занятые позиции. А еще надо будет проверять перекрытия (например, @1,0:rowspan=2 и @0,1:colspan=2 -- их концы пересекаются в клетке 1,1).
01.03.2009: а еще -- пофиксить бы его глючок, что если ВСЕ виджеты в колонке имеют halign=fill, то колонка вообще выглядит пустой (по крайней мере в матрице 2*2)...
05.03.2009: насчет "вычисление nrows
... будет нетривиально..." -- можно выполнять 2
прохода: на 1-м набирать статистику, а на 2-м -- уже реально создавать
и расставлять ручки.
05.03.2009: но есть еще отдельная задача -- а как бы этак делать, чтобы некая ручка могла занимать ЦЕЛУЮ СТРОКУ, включая колонку меток (левую)? Это может быть нужно для впихивания сепаратора во всю ширину сетки; причем сепаратор может быть как линией, так и меткой.
Возможно, надо всё-таки пообдумать какую-нибудь другую, более общую и навороченную, модель контейнера-сетки.
GU_FLAG_FILLHORZ
и
GU_FLAG_FILLVERT
в мае 2008г. обломилась из-за
невозможности ловить resize у формы.
А решение-то очевидно -- надо сделать свой виджет,
наследник XmForm, но с добавленным XmNresizeCallback
.
Ну и, полноты ради, можно ему также добавить и
XmNfocusCallback
с XmNlosingFocusCallback
.
И назвать оное -- XmForm2
. Это позволит поиском
находить ссылки на оба варианта.
??.12.2003: в какой именно день сделал -- не
помню, но функция CdrLogGrouplistMode()
сделана. Она
вызывается из ChlLogWindowMode()
, которая является просто
wrapper'ом.
30.11.2004: СО-О-ОБСТВЕННО! А почему это оно сейчас пишет заголовки только вида "ident(units)", в то время как oldChl'ный вариант писал "elem.channel"? А не сделать ли печать ПОЛНЫХ имен каналов?
Все равно надо будет адаптировать CdrLogGrouplistMode()
к вложенным каналам (сейчас оно про них не знает) -- тогда и сделаем.
26.04.2005: CdrLogGrouplistMode()
давным-давно -- еще 02.12.2004 -- переведено на рекуррентную
архитектуру. Но имена в заголовки пишет все равно лишь короткие :-).
26.04.2005: заодно пофиксил мелкий баг, что при units="" она писала в комментарий после имени канала пустые скобки "()".
24.01.2004: ввел в knobinfo_t
поля
curtag
и currflags
, и в
CdrProcessKnobs()
в них сохраняются полученные от cda
значения.
LOGT_WRITEM
и комментарий к
logchannet_t.type
: "read/writesimple/writecomplex".
Но ведь давным-давно (сейчас уж год как) "множественная запись"
сделана через формулы (.kind=LOGK_CALCED
), так что
артефакт LOGT_WRITEM/writecomplex давно пора удалять (возможно, где-то
еще остались аналогичные рудиментарные упоминания).
10.05.2004: еще прикол:
logchannet_t.revformula
почему-то была void*
вместо excmd_t*
. Поправил.
Специально проверял -- никаких дополнительных warning'ов при этом не
появляется (просто сделал diff двух логов от компиляции). И почему
было так -- непонятно (да еще с комментарием /*In fact, "excmd_t
*"*/
). Первая версия, в которой вообще revformula
найдена -- cx.20030416_pre-cxdata_use, и даже там нигде ничего
криминального с ней связано не было (да, понятно -- в Knobs-то
определение void*
, но это потому, что там CX вообще не при
делах).
11.06.2004: LOGT_WRITEM
удален.
Заодно слегка перетряхнул значения LOGT_NNN: теперь все "чтения" имеют
код 1NNN, а запись -- 2NNN. Смысл -- чтобы 1) можно было понять
r/w не сравнением со всеми возможными кодами, а просто по диапазону;
2) а вдруг захочется добавить тип записи, по чтению аналогичный
MINMAX и/или DEVN (бредовая идея...:-).
02.08.2004: еще заметил -- со столь же
незапамятных времен остался LOGC_CHECKED
, который есть
только в виде определения, но нигде на него проверок не делается. Уж и
не знаю, какие были мои глубокие на него задумки эээ... лет, кажется,
семь-восемь назад, при создании всяких drc/ekc/wcc, но они так и не
реализовались, а остались с тех времен только HILITED и IMPORTANT.
Итого -- LOGC_CHECKED
удален, в т.ч. и из
Knobs_simple.c, куда он успел пробраться за компанию :-).
CdrLoadGrouplistMode()
:
ведь парсинг параметров {norm,yelw,disp}_range там очень бы хорошо лег
как PSP_T_PLUGIN через psp_parse()
. И автоматически
получили бы расширябельность на возможные будущие параметры.
НО -- тогда образовалась бы зависимость Cdr от libuseful, а этого не хочется...
17.05.2005: а вот сегодня я добавлял парсинг
параметров grpcoeff
и grouped
-- и как меня
это достало! Все-таки НАДО будет перейти на psp, и фиг с ней с
зависимостью -- от psp и так реально везде все зависит.
psp_parse()
это соглашение уже
использовалось.
10.06.2004: это изменение коснулось
Cdr.c -- ChooseColorState()
,
SetControlValue()
, CdrCvtLogchannets2Knobs()
и (блин!) Knobs_internals.c -- те же
ChooseColorState()
и SetControlValue()
.
Заодно обнаружился давний баг в
Knobs_internals.c::ChooseColorState()
-- там при
наличествовавшем "желтом" диапазоне оба сравнения (не только на
"больше", но и на "меньше") делались с rmaxs
, так что
значение сразу и навсегда краснело.
31.01.2005: а вот и хрен-то там! В
Knobs_internals.c::SetControlValue()
я это забыл
исправить в одном месте -- где собственно и производилась проверка, что
после последовательного опроса нормального/желтого диапазонов мы
заимели хоть какие-то лимиты. Там вместо "minv<maxv
"
стояло "minv!=maxv
". Исправил.
На всякий случай натравил grep на все дерево, и обнаружил в еще
одном месте -- в Chl_knobprops.c::SnprintfRange()
стояло "vmin==vmax
" вместо "vmin>=vmax
".
Также исправил.
10.06.2004: вначале думал, проблема в Chl, а потом вспомнил/догнал/разобрался, что теперь это переехало в Cdr.
Итого -- в конце CdrCvtLogchannets2Knobs()
добавлена
инициализация colstate
из текущих свойств (которые, в
частности, включают нулевое значение) --
ki->colstate = ChooseColorState(ki, (time_t)0, ki->curtag, ki->currflags);
А собственно расцвечивание делает CreateKnob()
--
создавши виджет(ы), он вызывает метод Colorize
, ежели
таковой наличествует.
Надо только на будущее держать в уме, что мы
вроде подумывали всю колоризацию вообще вывалить на rflags -- то,
начатки чего сейчас есть в конце CdrProcessKnobs()
.
26.10.2004: АГА!!! Перевел все расцвечивание на Cdr -- так опять проблема проявилась: сразу при запуске канал rfsyn.*.ComplexOnOff НЕ желтеет (хотя в нем такое указано). А все потому, что решение по расцвечиванию принимаются теперь не "по месту", а на основании заранее посчитанных флагов.
Проблема решена: вычисление флагов вынесено в отдельную функцию --
ChooseStateRflags()
, которая вызывается и из
CdrCvtLogchannets2Knobs()
перед
ChooseColorState()
.
21.11.2004: ЕЩЕ ОДИН ПРИКОЛ: теперь при вводе значения виджета он резко перестает быть пожелтевшим/покрасневшим -- а все потому, что решение о покраске, видимо, принимается на основе этого свежевведенного значения, которое, естественно, не выходит за границы диапазонов.
24.11.2004: разобрался, в чем был косяк -- вовсе
не в текущем значении и диапазонах. Просто в
SetAttnState()
для onoff=0 вызывалась
ChooseColorState()
с rflags=0. При том, что там даже
комментарий был -- "!!!Shouldn't we use "true" flags?".
Поставил вместо 0 именно ki->currflags
-- все пришло в
чувство.
LOGT_DEVN
и LOGT_MINMAX
иметь еще тип
"LOGT_AVG
" -- для усреднения значения по нескольким
измерениям.
26.08.2004: потребность эта возникла в термостабилизации, ором г-на Лебедева. Дело в том, что у г-на Клющева имеется плавание выходного сигнала. Лебедев-то хочет, чтобы значение просто считывалось из АЦП несколько раз подряд и усреднялось.
Вопрос: а ПО СКОЛЬКИ циклам усреднять? Как при MINMAX? Долговато будет... И вообще, может, иметь поле для указания -- по скольки измерениям усреднять/минмаксить?
20.06.2004: сегодня пришел опять тот же Лебедев и провозгласил эту же потребность уже для проекта какой-то грейки-печки в Чемах.
Собственно -- а почему бы и нет, по тому проекту, что записан в
прошлом августе? Код хранения последних N измерений унифицировать с
LOGT_MINMAX
, а уж "конверсию" истории в текущее значение
-- делать разной.
Единственный вопрос -- ОТКУДА брать это самое N (которое в случае
MINMAX прописано просто константой 30 (и, исторически, используемое в
LOGT_DEVN
число NUMAVG
(sic!) также равно
30)).
17.01.2007: желание Старостенки: мочь посмотреть где-нибудь -- например, в окошке "KnobProps" -- стабильность любого канала. Т.е., либо среднее за некоторый период, либо среднеквадратичное отклонение. И чтоб еще можно было покрутить, по скольки измерениям.
wasjustset
?).
Надо б обеспечить такое же поведение (защиту?) и при refresh'е.
25.09.2004: стал разбираться.
Во-первых, в Chl_gui.c::UpdateChannels()
почему-то всегда вызывался
CdrProcessGrouplist(synthetic=>0)
. Это пофиксил.
Во-вторых, с удивлением обнаружил, что при проходе CdrProcess*()
сразу после нажатия (т.е., вызванном синтетическим обновлением)
почему-то ki->wasjustset==0
. Ура!!! Дело в том, что в
Knobs_internals.c::SetControlValue()
флаг
wasjustset
взводился уже ПОСЛЕ того, как вызывалось
ki->uplink->emlink->SetPhysValue()
(дергающее, в
свою очередь, UpdateChannels()
).
Пофиксил -- перенес этот вызов ...SetPhysValue()
в
самый конец в обоих инкарнациях SetControlValue()
. (Для
Cdr'ного варианта это тоже актуально -- при чтении режима).
Имеем только один мелкий недостаток: теперь, если revformula некоторым образом устраивает disable (как в camsel'е), то самому каналу-виновнику этот disable делается на цикл позже...
Кстати, заодно обнаружил, что в Knobs'овом варианте
SetControlValue()
по-прежнему оставалась старая проверка
наличия диапазона -- "min!=max" вместо "min<max". Пофиксил.
CdrProcessGrouplist()
возвращает rflags
только ПОСЛЕДНЕГО элемента, забывая остальные.
27.09.2004: там стояла тупая передача
rflags_p
дальше, в CdrProcessEleminfo()
.
Пофиксил -- вставлена корректная аккумуляция.
knobinfo_t.uplink
заполнялось не в CdrCvtLogchannets2Knobs()
, а в
ChlMakeElement()
.
27.09.2004: во-первых, перенес всю алхимию с
ShowAlarm() в Chl, а в
Knobs_internals.c::SetAlarm()
остался только
вызов его да "свое внутреннее" -- SetRelaxing()
.
Во-вторых, теперь проставление uplink'ов перенесено в
CdrCvtLogchannets2Knobs()
. Для этого пришлось поменять
интерфейс -- ей теперь передается дополнительный параметр
"holder
", которым и заполняется поле uplink.
(Поскольку никто, кроме самой Cdr этим интерфейсом не пользовался,
то изменение прошло безболезненно.)
ki->q
никогда и никем не заполняется. И как тогда
вообще работает (а работает ли?) "невырывающесть" ручек, а?
24.10.2004: полез рыться по BACKUP и STABLE --
выяснилось, что это ДЕЛАЛОСЬ РАНЬШЕ, при oldChl -- в
oldChl_data.c::CreateChannel()
. А при переходе
на связку Chl+Cdr+Knobs оно как-то тихо-мирно выпало. Хотя должно бы
присутствовать в нынешнем CdrCvtLogchannets2Knobs()
, так
же рядышком с cda_add_physchan()
. Ну, блин...
BTW, в любом случае остается старая проблема -- а что на эту тему с формульными ручками -- совсем никак?
26.10.2004: в преддверии близкого релиза просто
восстановил вызов cda_getphyschan_q()
сразу за
cda_add_physchan()
.
Но надо будет разобраться в ситуации и все протестировать!!!
07.11.2004: угу, глючить стало из-за этого -- LOGD_SLIDER в widgettest... Без уставки кванта -- не глючит... Хотя -- нет, это именно в widgettest'е халтурно сделано: там значения целые, а шаг -- 0.5. А cda отдает по умолчанию квант 1.0, в пределы которого, естественно, шаг 0.5 вполне укладывается. Кстати, если что -- cda позволяет указать квант <0, тогда он принудительно становится равным 0.
19.07.2006: ну елы-палы -- "протестировано" еще тогда, все нормально работает, так что ставим еще тогда напрашивавшийся "done".
Ну и что теперь -- вставлять сравнение с поправкой на q? А какие негативные последствия могут от такого вылезти?
29.11.2004: по-хорошему эти действия должны также производиться в Cdr -- ибо только там должен быть известен формат файлов режимов.
Сделана функция CdrStatGrouplistMode()
. Она читает первые
10 строк файла, вычленяя оттуда комментарий (если оный имеется) и
распарсивая время создания.
КСТАТИ: парсить дату, изготовленную через
ctime()
-- дело весьма неблагодарное, поскольку она может быть locale-dependent.
По-хорошему, надо бы писАть в файл время создания и числом time_t, и, соответственно, ввычитывать его и оттуда тоже.
Получасом позже: вроде сделал -- теперь дата в виде числа time_t и пишется, и вроде парсится. Надо проверить.
30.11.2004: проверил, на самих упрощенных диалогах -- все работает. Помечаем как "done".
30.11.2004: понятно, что для полноты картины
должен также быть wrapper ChlStatWindowMode()
. Оно
сделано, и Chl_simple.c переведено на нее.
09.12.2008: поскольку этот код с минимальными изменениями используется практически везде, где идет работа с файлами через simplified-file-interface, то запишем здесь:
Надо бы кроме числа-(Придумано было для потребностей ippclient'а, но очевидно полезно и для других тоже. И, справедливости ради, надо сказать, что в adc333.c::time_t
иметь в файле и человечески-читаемое обозначение времени. И, с учетом того, как устроен парсинг единственного параметра у "#!CREATE-TIME:", лучшее место для этого обозначение -- через пробел сразу после числа-time_t
. Парсер, видя, что после числа идет пробел, будет удовлетворен, а человек -- и дату сможет прочитать.
SaveToFile()
в точности такая
последовательность -- time_t,ПРОБЕЛ,ctime() -- уже использовалась; но
там чтения нету, только сохранение.)
Да, вставил в:
CdrSaveGrouplistMode()
тоже.
Одно ма-а-аленькое замечание: поскольку ctime
уже само
выдает в конце '\n', то завершать printf-формат для строки
crtime_lp_s
теперь не нужно.
CdrSaveGrouplistMode()
и комментарий.
29.11.2004: что ж, добавлен дополнительный параметр --
comment
. Изменение API повлекло за собой и соответствующее
изменение в API Chl. А то, в свою очередь потребовало подкорректировать
своего пользователя -- Chl_simple.c::CommandProc()
.
Комментарий в файл пишется "творчески" -- по символу, с проверкой, что
не-iscntrl()
, а если "да" -- то вместо него пробел.
11.01.2005: а с другой стороны -- ведь Cdr'у-то
совершенно неоткуда это имя нарыть. И даже более того -- эту
информацию неоткуда нарыть даже Chl_gui (если, конечно, не попрет
напрямую вычитывать XtName()
от оконного shell-widget'а).
У кого она есть -- так это у Chl_simple да у его коллег-клиентов.
22.04.2005: ага! А в
унифицированно-древесной структуре CXv4 как раз все будет -- имя
подсистемы будет просто полем ident
"корневого"
knobinfo
этой подсистемы.
03.05.2005: йоу!!! "Промежуточное" решение --
теперь у Chl_gui.c ЕСТЬ информация о названии системы.
Так что -- преспокойно можно вводить в
CdrSaveGrouplistMode()
параметр "subsys
".
Что и сделал -- ПЕРЕД параметром comment
. Дальше --
дело техники, модифицировал единственного пользователя,
ChlSaveWindowMode()
.
Так что -- "done".
(Но на будущее -- CXv4 -- стоит помнить, что писАть надо именно
реальное имя из корня дерева. Хотя -- и нынешнее subsys
также можно, в качестве ссылки на того, кто сделал файл.)
LOGK_NOP
, который был бы и не DIRECT, и не CALCED, а
просто -- никакой. Это нужно для всяких декоративностей -- метки,
сепараторы и т.д., чтобы не приходилось пихать им пустые формулы.
12.01.2005: только что посмотрел -- поле
kind
проверяется (LOGK_*
) только в
Cdr.c плюс в одном месте в Chl_knobprops.c (а в
Chl_gui.c его уже не осталось, хвала
CdrSetKnobValue()
:-). Так что -- все локализовано.
14.01.2005: ввел такой код в cxdata.h.
Затем совершил давно назревший логический шаг -- ввел функцию
CdrSrcOf()
, которая сама разбирается по типу
(kind
), cda_srcof_
-что вызывать. И на нее
переведен Chl_knobprops.c. Т.о., теперь ВСЕ проверки на тип
канала инкапсулированы в Cdr.
Далее -- слегка подтюкал в Cdr.c все места, где встречалось
LOGK_
, их оказалось всего несколько штук. Часть теперь
помечена комментариями на тему LOGK_NOP, а в остальных он проходит как
default/else, приравниваясь к LOGT_SUBELEM и, следовательно, вызывая
реакцию "низ-зя!".
21:05: Все, умаялся я, испытаю завтра.
ЗЫ: сходу -- надо обсмотреть, как с таким "ничем" обращаться при сохранении и загрузке каналов.
ЗЗЫ: а все-таки имеется некоторое пересечение по функциональности
между kind
/LOGK_*
и
type
/LOGT_*
...
17.01.2005: предварительные-то испытания
показали, что вроде как работает, да не в том дело -- по размышлениям
за 16-01-2005 стало ясно, что LOGK_NOP
надлежит переделать
в LOGT_NOP
.
13.03.2006: реально-то "NOP" используется уже больше года, и вполне успешно. Так что -- "done".
24.01.2005: Сие состояние означает, что ручка только что была создана, но никаких данных в ней У Гусева (читай -- в EPICS'е) это белый фон. У марк-плешковых ребят -- перечеркнутый круг поверх виджета.
У этого состояния должен быть наивысший приоритет -- ибо пока данных не приходило, ручки показывают "погоду" (нули), и смысла как-то подсвечивать их красным/желтым -- нету.
Кстати, а не использовать ли как раз в этих целях светло-бирюзово-голубой цвет?
03.02.2005: ввел новое состояние --
COLALARM_JUSTCREATED
, идущее между UNINITIALIZED и NONE.
Затем примерно реализовал задуманное -- и выставление нужного bg в Knobs::ChooseKnobColors() ("высший приоритет" там получился автоматически), и первоначальное присвоение colstate:=JUSTCREATED в Cdr, и собственно ввел сам цвет JUSTCREATED->#c0e6e6 в Xh.
Да, светло-бирюзово-голубой цвет смотрится хорошо -- он сочетается с нашей палитрой, и не бросается в глаза, но притом заметен. Хоть его и жалко тратить на такое, но это очень мнемонично -- среднее между обычным серым и "цветом гусиной кожи". А вот в остальном...
Оно как бы получилось. Но... Для начала -- расцвечивает оно даже сепараторы и метки (т.е., декоративные виджеты). Чтобы это подысправить, пришлось бы перекопать значильную часть кода Cdr, для более корректного отделения разнотиповых ручек (knob/nop/subelem). Что предполагается сделать через несколько месяцев -- когда буду перекурочивать все под унифицированную структуру "инодов", как описано в соответствующем проекте.
Так что -- либо грохнуть все сделанное нафиг (ох, как руки чешутся это сделать!!!), либо заморозить на эти несколько месяцев.
23.05.2005: что ж, прошло уже несколько месяцев,
я к существованию состояния COLALARM_JUSTCREATED
привык,
оно удобно. Так что -- де факто сие, как и предполагалось, заморожено
до CXv4, когда глюкавость исчезнет автоматически.
Так что -- "done".
24.01.2005: вопросов только два:
CXRF_NO_DRV
/"Driver loading
problem".
У text-widget'а можно ставить обычно-серый фон вместо белого (хотя где ж его брать? :-).
У разных объемообразных -- можно делать XmNsensitive:=False всяким стрелочкам (в т.ч. и стрелочкам [v\^] у text'а).
А вообще -- ЦВЕТОВОЕ ли это состояние? Или стоит ввести
дополнительное поле в knobinfo_t
?
OP_RETSEP
, который для
cda_execformula()
работает в точности как
OP_RET
, а остальные части cda обращают на него внимания не
больше, чем на NOP.
OP_RETSEP
. И если она
будет возвращать не-NULL, то Cdr будет считать результат за colformula,
knobinfo_t
поле вставить можно, это никакого
интерфейса не нарушит.
free(ki->colformula)
делать не надо.
(Реально идея эта пришла в голову еще где-то во время поездки в Японию -- в каком-то аэропорту...)
Что касается обращения с ki->colformula
:
LOGK_DIRECT
/LOGK_CALCED
.
Да, и LOGD_LIGHT
после этого всего станет совершенно
ненужным. (Кроме одной ситуации -- если он имеет тип
LOGT_AVG
или LOGT_MINMAX
-- такие вещи cda в
формулах никак не поддерживает. Но подобное у нас пока вроде не
встречается.)
20.04.2005: неа, выводить "второй источник данных" в "Knob properties" нет никакого смысла: ведь colformula почти всегда будет содержать как минимум ДВА канала (иначе какой в ней смысл?), что автоматически дает "N/A".
В тот же день после обеда: А вот второе, "колоризующее" значение -- возможно, очень даже имеет смысл выводить. Но пока никак -- оно нигде не сохраняется :-).
20.04.2005: да, модификации в саму cda для
поддержки OP_RETSEP
совершенно тривиальны. А новую
функцию-хак назвали cda_HACK_findnext_f()
.
В Cdr тоже никаких особых проблем. Просто взяли да сделали.
Итого -- оно работает, считаем, что технология проверена и себя оправдывает. Помечаем как "done".
22.04.2005: по поводу отображения "колоризующего" значения в "Knob properties":
knobinfo_t.cur_cfv
.
colformula
в
CdrProcessKnobs()
.
28.04.2005: кстати, сегодня убрал "наследование"
disp-range из {norm,yelw} в случае, если colformula!=NULL
.
Аналогично, оба экземпляра SetControlValue()
лимитируют
ввод также только если colformula==NULL
.
06.07.2005: вчера озаботился -- а чего ж это если (в magsys'е) "базовый" (управляющий) канал синеет, то и контрольный канал также синеет.
А сегодня разобрался -- да, в CdrProcessKnobs()
действительно стоИт учет тэга, получаемого при обсчете расцветки. И --
это правильно! Ведь иначе значение будет как-то расцвечиваться
(желтым/красным), но этот цвет смысла не имеет -- он получен на основе
устаревших данных. И эту устаревшесть показывать НАДО.
12.03.2007: тогда, при "интеллектуализации"
наследования disp-range из {norm,yelw}, немного переборщил -- внес ВСЕ
внутрь условия colformula!=NULL
. А надо было -- чтобы
[-100.0,+100.0] проставлялось при надобности в любом случае, а уж
наследование из {norm,yelw} -- просто предварительная альтернатива.
Так и сделал.
26.04.2005: ну прямо -- хоть поле элемента вводи,
"nonloggable", и соответствующий флаг в options
...
Ага, вот только options
-то парсится Chl'ем, а данная
фича -- уровня Cdr'а! И как это сочетать? Хрю...
08.08.2005: угу, только не "поле nonloggable", а
флажок в behaviour
. Причем вводить это чудо -- НАДО. А
уж откель оное должно заполняться -- вопрос отдельный.
10.12.2005: зачэм полэ, зачэм флаг? Сегодня возникло аналогичное желание -- чтоб некоторые каналы не сохранялись в режиме.
А можно ж делать так: если у канала нету идентификатора -- то и не сохранять и не протоколировать его. Ведь имя (ident) как раз и существует для адресации! Нет имени -- не надо сохранять!
10.12.2005: неа, так нельзя: istcc, phm-tsyline и lebed адресуются к каналам КШД по именам, а вот сохранять эти каналы -- не надо...
О! Можно расширить ту идею: НЕ сохранять если нет идентификатора, либо если он начинается с '-'.
12.12.2005: обдумал эту мысль.
В общем -- если не припрет, то ничего на эту тему пока не делаем (но держим в уме возможность такого решения "по-быстрому").
А в CXv4 -- сделаем "корректно", видимо -- тем же способом, как будет указываться "алармовость".
02.03.2006: да, надо делать уже сейчас -- хотя бы ради linmag'а, по проекту "пропускать при ident[0]=='\0' || ident[0]=='-'". Причем пропускать ЛЮБЫЕ knob'ы -- в т.ч. и элементы.
Так что --
IdentifierIsSensible()
.
CdrSaveGrouplistMode()
+RecSaveElement()
.
CdrLogGrouplistMode()
+RecLogElement()
.
WriteFile()
; именно в
ОДНО место -- только на верхнем уровне, чтоб пропускались "служебные"
элементы лишь группировки, а вот содержимое элементов пропускать нельзя
-- иначе все там поедет.
И, наконец -- в db_linmag.h элемент федорообразных вычислений теперь именуется "-fedcalc".
(А istc/xmclients/ трогать не стал -- во-первых, фиг уж с ними, а во-вторых, там СТОЛЬКО ссылок на элемент "kshd" по имени -- у-у-у!)
Короче -- на сейчас считаем "done".
06.04.2006: блин, там в
IdentifierIsSensible()
проверка была некорректной -- оно
сразу проверяло первый символ, а ведь поле ident
может
быть не только пустым, но и просто NULL
(имеет полное право, и
так и делается в impacis10.c!).
Вставил проверку.
RecSaveElement()
-- НЕ обрезались пробелы слева
при fprintf()
'ах диапазонов. Причем не обрезались еще
издавна, с самого появления этой фичи -- еще задолго до введения
fprintf_f_rtrim()
.
В принципе-то это несмертельно -- парсер в
ParseChanVals()
лидирующие пробелы допускает, но --
некрасиво.
17.05.2005: решение просилось само -- легкая
модификация fprintf_f_rtrim()
для пропуска пробелов, что и
было сделано.
Кстати, при желании можно будет в CXv4 даже перевести бибиканье на эти флаги. Только надо будет уметь детектировать "изменение" (либо хранить старое и иметь у элемента метод "Display", либо ввести метод "flags changed" -- последнее мне нравится больше).
23.05.2005: да, ввел поле
eleminfo_t.cmlrflags
. И затем мелкая модификация в
CdrProcessEleminfo()
.
Ежику понятно, что реально сие найдет применение только в CXv4, но сделать было несложно, и, может, оно еще успеет пригодиться.
12.03.2007: just for record -- «метод
"Display"»-то уже сделан, под именем NewData()
, еще
20-07-2006, и бибиканье на него переведено -- еще 28-07-2006.
30.05.2005: кстати, "реально" захотелось этого после легкого общения с гусиной программой kls, которая таких весчей вообче не делает. Там блокировка коротко квакнет -- и фиг поймешь, что же было.
Ввел поле knobinfo_t.attn_period
. Сравнение в
ChooseColorState()
и ChooseStateRflags()
теперь делается с ним, а не с ATTN_TIMELIMIT
(каковая
изведена). В интерфейс же SetAttnState()
добавлен
параметр period
, и создана для его вызывателей пара
констант -- RELAX_PERIOD=10
и
OTHEROP_PERIOD=5
.
Короче -- все готово.
За компанию обнаружил, что attn_time
почему-то
int
, а не time_t
. Исправлено.
16.06.2005: йоу!!! Насчет "другого цвета" -- придумал: это должно быть нечто коричнево-болотное. Первое же, что было подобрано в xpaint'е -- #B09422.
Йоу#2: помнится, забавные, примерно болотные цвета получаются, если xwd-дамп экрана с гусиными программами показать в xv -- xv почему-то меняет местами R и B. Так вот -- "цвет гусиной кожи" превращается в #a09b5f. А FVWM/AnotherLevel'овские заголовки активных окон -- в более ядовито-яркий #8b8b00 (fvwm'овская topShadow от него -- #c3c300).
Остается только вопрос выбора: какой из категорий ошибок {железной,программной} отдать какой из цветов {бордовый,болотный}.
28.06.2005: Мысли по теме:
28.06.2005: а сам проект -- реализуем. Поехали:
CXRF_SERVER_HWERR_MASK
и
CXRF_SERVER_SFERR_MASK
-- пока халявно, как 0-й и 1-й
байты.
CDR_FLAG_SFERR_MASK
(куда теперь
отнесен и CALCERR); он добавлен и к CDR_FLAG_SYSERR_MASK
.
colalarm_t
::COLALARM_SFERR
.
ChooseColorState()
(приоритет
-- у HWerr).
XH_COLOR_BG_SFERR
плюс строчка в
Xh_colors.c. Выбран fvwm'ный болотно-зеленый
#8b8b00.
ChooseKnobColors()
.
DisplayCurVals()
.
EventProc()
, с соблюдением
надлежащего приоритета.
DecodeData()
.
Проверил -- все работает. Итого -- "done".
CdrCvtLogchannets2Knobs()
, при count==0
возвращающий NULL
, не позволяет создавать пустые элементы,
что есть плохо.
05.11.2005: поправил -- теперь
CdrCvtElemnet2Eleminfo()
просто пропускает и вызов, и
проверку результата CdrCvtLogchannets2Knobs()
при
count==0
.
(Правда, почему-то это не помогло, что-то продолжило глючить, ну что ж -- будем разбираться :-) Как установлено давным-давно, глючил, как водится, антоновский виджет XmSepGridLayout, так что к Cdr продолжение отношения не имело.
Так вот -- можно ж просто ввести еще какое-нибудь цветосостояние, этакое "при-чтении-вышло-за-пределы", так что пользователь сразу будет видеть проблему.
Некоторые "детали":
SetControlValue(,,fromlocal:=1)
, к которому
оба варианта и сводятся).
knobinfo_t
--
"прошенное" значение, которое отображать в "knob properties".
31.01.2006: потому -- пробел убран :-).
03.06.2010: Карнаев попросил сделать, чтобы в log-файле кроме колонок "time()" и "strcurtime()" была также колонка "время с начала записи log-файла" -- так удобнее строить графики, а то "глобальное время" -- малополезно.
Вечером: да, сделал. Для этого пришлось добавить к
ChlLogWindowMode()
и CdrLogGrouplistMode()
параметр struct timeval *start
, плюс, естественно, в
юзерах -- Chl_app.c и cdrclient.c -- инициализацию
этого "времени начала логгинга" при включении протоколирования.
Выяснилось в конце концов, что в widgettest'е было ДВА канала с именем "IlimHW"#n, и оно просто при загрузке режима всегда находила лишь первый, а второго не замечала.
Вывод: вообще-то надо б такие очевидные вещи проверять -- нет ли дублирования имен!
02.03.2006: вариантов действий тут видется три:
CdrCvtLogchannets2Knobs()
-- и если идентификатор
"осмыслен" (т.е., непуст и не "-"), то проверять, нету ли уже такого, и
если есть -- то ругаться на stderr.
12.08.2010: ну вот -- опять наткнулся на такое: в liucc "почему-то не грузились режимы". Потому, что опять были дубликаты -- имена "_".
Вводим диагностику -- при $CDR_DEBUG_DUPS=1 оно выполняет проверку по варианту 2а.
Работает -- СТО-О-ОЛЬКО поналовило... Так что, подумал-подумал -- и сделал проверку принудительной, ВСЕГДА.
Засим, по прошествии почти четырёх с половиной лет, можно раздел считать за "done".
01.03.2013: а ведь теперь, с введением "прозрачных" контейнеров (с ident=":") проблема может вернуться. Проверять "параллельные вселенные" довольно нетривиально, и делать этого не хочется.
Просто пока закроем глаза на потенциальную проблему.
ident
(eleminfo_t
и knobinfo_t
)
используются сразу -- БЕЗ проверок, что они не NULL
.
Нехорошо!!!
29.05.2006: прошелся по всему тексту, попроставлял проверок на NULL. С остальными текстовыми "свойствами" проблем нет.
Еще раз убедился -- ну и угребищна же нынешняя хак-архитектура вложенных элементов!!!
CdrProcessEleminfo()
. "Пора" -- по куче причин: для
"умных" элементов типа дерева (22-05-2006), для просто
element-plugin'ов, для alarm'ов (19-07-2006).
20.07.2006: да, ввел -- как и планировал,
knobs_emethods_t.NewData()
. Ему передается также флаг
synthetic
.
Замечание: этот метод вызывается ПОСЛЕ обработки всех knob'ов -- чтобы в них уже были готовы новые данные.
22.03.2007: как хорошо, что в
NewData()
передается флаг synthetic
! А то в
linipp возникла проблема: основной-то сервер там опрашивается с
частотой 1Гц, а дополнительный (управляющий вводом датчиков) -- 10Гц; и
в результате оно пыталось все перерисовывать с частотой 12Гц (там был
еще один 1Гц-коннект). К счастью, обработка данных с ИПП делается в
element::NewData(), а не в knob::SetValue(), так что просто вставлено
условие -- при synthetic!=0 ничего не делать.
Но вообще-то более правильно на будущее (CXv4) придумать-таки корректную отработку -- чтобы SetValue()/NewData() для каждого "узла" (knob, bigc, user) вызывалась только про приходу конкретно ЕГО данных.
knobinfo_t.placement
; сейчас же, в преддверии возможного
появления ELEM_CANVAS
, оно стало совсем необходимым.
23.07.2006: да, изготовлено. В
knobinfo_t
оно вставлено в свою группу -- сразу после
comment
. А вот в logchannet_t
, ради
сохранения совместимости, увы, пришлось добавлять в конец. В Cdr
сделано собственно копирование и освобождение, наравне с остальными
текстовыми свойствами.
Как водится -- сделана продвижка версии, теперь
CXSS_SUBSYS_VERSION_MAJOR=4
, но перекомпиляция и программ,
и .so-описаний -- обязательна!!!
Кстати, в 4cx/src/include/Knobs_typesP.h поле
knobinfo_t.placement
уже имелось, также в конце, после
поля options
(нынешнее widdepinfo
).
ChooseStateRflags()
...
07.08.2006: ну и что --
ChooseStateRflags()
публиковать, что ли? Но тогда
придется дозволять ki==NULL
, и всю функцию придется
напичкать if()
'ами...
07.08.2006: ммм... Соображения:
CXRF_OVERLOAD
, и rflags с удаленным
CXRF_OVERLOAD
приводит к COLALARM_NONE
, то --
это "COLALARM_UNDERFLOW".
Вывод: ну его нафиг -- просто добавлять состояние (без поддержки) смысла нет, поддержку делать муторно, пользователей этой фичи -- с гулькин нос, так что -- ну ее нафиг, "withdrawn".
(А в linbpm'е реализуем "вручную".)
01.09.2006: withdrawn-то withdrawn, но вот не ввести ли стандартные Xh-цвета XH_COLOR_GRAPH_BARn_UNDERFLOW? А то иметь их в программах -- как-то неправильно, учитывая, что "базовые"-то цвета определяются именно в Xh'е...
03.09.2006: есть еще пара вариантов цветов, помимо более темных "базовых":
07.08.2006: ввел, долго ли --
eleminfo_t.elem_private
, сразу за wdci
.
(Ну да, как бы я без него linipp-то делал! :-)
В данном-то случае все отлично ложится на обычное расцвечивание (LOGC_VIC, norm=[0,7], yelw=[0,15]), хотя уже возникает проблема ограничения ввода (приходится использовать colformula -- тогда ограничение ввода отключается).
Но: и цвета могут потребоваться другие, и порядок может быть иной (едва ли так легко совпадет с зеленый,желтый,красный, которые просто цвета alarm'ов).
Вопрос -- как?
17.04.2009: мыслишка -- ведь кнопка селектора (OptionButton, тип XmCascadeButtonGadget) пытается копировать внешность того пункта меню, который сейчас выбран, правильно? А что, если б разным пунктам можно было ставить разный фон -- и тогда кнопка его бы копировала, эффективно делая сам виджет нужного, довольно свободно выбираемого цвета...
А конкретный цвет можно б было указывать как стиль -- через опциональный дополнительный ^L...
25.04.2009: попробовал -- фиг! Не копирует она цвета, ни foreground, ни background. Копирует только шрифт...
Так что если уж вопрет -- то указание-то через опциональные "style-strings" сделать можно, но уставлять оное главному виджету -- уже только вручную, отключая при этом колоризацию...
13.05.2009: да, сделал эту фичу и в селекторе тоже (детали см. в его разделе). Так что -- "done".
options
в
СТРОКОВОМ виде. А они ведь частенько ВЫЧИСЛЯЮТСЯ -- т.е., формулы, но
результат вычисления формулы в строку не засунешь...
Так что -- добавим к elemnet_t
и
eleminfo_t
целочисленное поле intparam
?
Вот только из-за этого опять придется
CXSS_SUBSYS_VERSION_MAJOR
двигать, а неохота...
20.03.2007: в принципе-то можно использовать поле
ncols
, но как-то это неправильно...
А писать бы надо! Благо, есть хоть и не-очень-портабельная, но используемая во всяких psp спецификация "%.*s".
23.05.2007: сделал, и все проверил.
CdrProcessKnobs()
при count<=o
делался
просто return
, а rflags оно прописать забывало, так что
там оставался мусор.
Заменил return на goto в конец, туда, где делается возврат.
CdrCvtGroupunits2Grouplist()
отсутствовала проверка, что
grouping!=NULL
. Спасибо Роговскому -- случайно натолкнул
на этот аспект.
20.11.2008: вставил проверку -- при
NULL
возвращается NULL
и
errno=EINVAL
. Заодно подобные же проверки добавлены и в
CdrCvtElemnet2Eleminfo()
и в
CdrCvtLogchannets2Knobs()
. А вот в троицы
CdrDestroy*()
и CdrProcess*()
пока вставлять
не стал -- надо подумать.
style
для ручек и
элементов (и в *net_t
, и в *info_t
),
аналогично тому, как 3 года назад добавили placement
?
Тогда можно будет, отработав стили в 4cx/, быстро back-портировать их и сюда...
28.03.2009: вставил пока что в
knobinfo_t
и eleminfo_t
-- при этом бинарная
совместимость остаётся. А как комплекс отключат -- добавлю и в
*net_t
.
21.10.2013: да сделано, сделано, в конце сентября.
Это изначально предусмотрено в v4, а тут понадобилось для liu -- любая программа, работающая более чем с одним крейтом, иначе сразу будет очень проблемной.
18.11.2009: идея-то довольно очевидна: мочь
как-нибудь "хаковато" указывать в elemnet_t
этот
sub-defserver, не трогая при этом формат структуры, а уж в
eleminfo_t
-то оно добавится без проблем (если вообще
нужно -- можно ограничиться его наличием лишь на время
CdrCvtElemnet2Eleminfo()
, а уж параметр-то
defsid
у всех CdrCvt*()
есть -- включая
CdrCvtLogchannets2Knobs()
, так что даже и API менять не
придётся).
Конкретная идея -- если .options
начинается
с "defserver=", то всё далее до " \t," или до
'\0' считается за ссылку на сервер, которая резолвится в
cda_serverid_t
и используется вместо
defsid
'а. И даже более того: можно весь этот кусок
выкусывать и в eleminfo_t.options
не класть -- чтобы не
смущать сами elem-plugin'ы и не заставлять их понимать-и-игнорировать
параметр "defserver=".
Короче -- всё выглядит просто, и может быть сделано за полчасика.
19.11.2009: реально чуток сложнее: чтоб для каждого элемента не плодились свои сервера, а использовались, при ранее-заведённости, уже имеющиеся, надо б иметь в cda публичный вызов, делающий то же, что сейчас при регистрации формул в командах _BYNAME_I -- "заведи для данного sid'а auxsid, либо отдай уже имеющийся если он есть".
20.11.2009: и будет еще одна проблема -- physinfo. Придется как-то её добывать/организовывать.
21.11.2009: сделал (пока без учета physinfo), по первоначальному проекту:
cda_add_auxsid()
.
cda_register_formula()
чохом ВСЕМ auxsid'ам данного
соединения делался cda_add_evproc()
. Несмертельно,
конечно -- в нём стоит проверка для недобавления уже имеющихся
нотификаторов, но всё же.
CDA_R_AUXDATA
(и к тому же у CdrCvtElemnet2Eleminfo()
нет никакой
информации о том, как уведомлять клиента), то
cda_add_auxsid()
при event_processer==NULL
сам указывает InternalAuxsrvNotifier()
.
Проверил -- вроде работает, такой сервер правильно "пропагируется" вглубь. И старые _BYNAME_I тоже работают (проверено на linipp).
Теперь впору -- для "законченности" картины -- было бы вводить возможность "трансляции":
cda_new_server()
'е
прозрачную замену: если есть переменная окружения с именем
CX_TRANSLATE_nnn (где nnn -- указанный spec
), то
использовать содержимое этой переменной вместо указанного.
cda_add_auxsid()
.
05.01.2010: всё уже сделано -- и поддержка списка physinfo (префикс '+'), и трансляция имён серверов (CX_TRANSLATE_nnn), и использующая "defserver=" программа (liu/floor). Так что -- "done".
21.10.2013: just for information: теперь есть уже
и поле elemnet_t.defserver
, но вряд ли оправданно
переходить на него (просто трата времени и ломание работающего кода).
Из актуальных потребностей:
logchannet_t.style
и
elemnet_t.style
(см. 27-03-2009);
elemnet_t.defserver
(см. предыдущий пункт -- чтоб с
options
не маяться);
subsysdescr_t.plugfiles
(чтоб
a-la-v4 в описании подсистемы указывать, какие ей нужны
файлы-реализаторы-плагинов -- тогда можно б было все программы свести к
chlclient'у плюс набор плагинов).
21.10.2013: да сделано, сделано, в конце сентября. Кроме plugfiles, которая ТУТ не к месту (уж в v4 будет).
(А еще раньше Малютин со Старостенкой приставали с аналогичной просьбой про вакуум -- они там тоже имеют привычку кабеля перетыкать.)
28.12.2009: естественно, стоит также и вопрос СОХРАНЕНИЯ этого барахла в файл и восстановления оттуда. То, как оно будет попадать в defaults-файл и когда оттуда считываться -- обсуждается в разделе по Chl_app за сегодня.
А вот КАК оно туда будет сохраняться и восстанавливаться обратно в ручки -- вопрос уже для Cdr. Идея такая:
LOGK_USERTEXT
-- по аналогии с
LOGK_NOP
, так что оно будет и не DIRECT, и не CALCED. И
все проверки будут рядышком, там же, где и на NOP.
label
.
label
и
вызывать метод PropsChg()
.
31.12.2009: вроде сделал.
LOGK_USRTXT
не пришлось -- там
код уже достаточно правильно организован, так что просто пропускает
всё, что не LOGK_DIRECT/LOGK_CALCED (и при этом не LOGT_SUBELEM).
is_subelement
на перечисление line_type
,
плюс, за компанию, и массив-имён-типов -- names[]
--
теперь ссылается на nnn_s, а не дублирует их).
Собственно парсинг строк .usrtxt делает функция
ParseUsrTxt()
-- оказавшаяся тривиальнейшей.
Проверил, работает -- "done".
CdrProcess*()
некий флажок. Добавлять еще параметр --
неохота, но можно превратить нынешний параметр synthetic
в
"options
".
11.05.2010: так и делаем --
synthetic
переименован в options
, введены
флажки CDR_OPT_SYNTHETIC
=1 и
CDR_OPT_READONLY
=2.
В cx-starter.c и descr2html.c добавлено
всегда-наличие CDR_OPT_READONLY
(а в cdrclient.c
-- нет, ибо это ПОЛНОЦЕННЫЙ консольный клиент).
В cda поддержка также вставлена, и надлежащая трансляция
CDR_OPT_READONLY
->CDA_OPT_READONLY
тоже,
осталось проверить.
P.S. И надо бы и на 4cx/ этот подход распространить.
13.05.2010: да, в тамошний
Cdr_treeproc.c::CdrProcessKnobs()
также добавлено
-- 3-м параметром. А в cda там пока обработка формул никак не
определена, так что и говорить не о чем.
03.06.2010: проверено, работает -- "done".
21.09.2011@Снежинск-каземат-11: близкая потребность -- надо уметь запускать ЛЮБУЮ Chl-программу в режиме "readonly".
(Конкретно тут понадобилось для новой версии beam, которая умеет сама отключать накал при превышении предела. Чтоб и программу запустить, но и она б ничего не могла напортачить.)
Для этого нужна возможность указывать в командной строке ключик "-readonly" (с дефисом -- поскольку иначе оно будет считаться ссылкой на сервер).
Делаем:
datainfo_t.is_readonly
.
ChlRunApp()
уставляет его по ключу "-readonly".
UpdateChannels()
включает
CDR_OPT_READONLY
(тем самым отсекается запись из формул).
Chl_E_SetPhysValue_m()
просто делает
return
(так отключается запись от ручек).
29.10.2011@Снежинск-каземат-11: а вот пункт
"Chl_E_SetPhysValue_m()
просто делает
return
" оказался лишним: тем самым мы отключаем не только
"запись от ручек", но и локальную активность программы (через регистры).
CDR_OPT_READONLY
в Cdr: в
CdrProcessGrouplist()
она есть, а в
CdrSetKnobValue()
-- нету.
CdrSetKnobValue()
добавлено поле options
(причём не в конец, а ПЕРЕД localreginfo).
cda_opts
, и при
LOGK_DIRECT при нём делается просто return 0, а при LOGK_CALCED -- оно OR'ом
передаётся в cda_execformula()
.
Chl_E_SetPhysValue_m()
, соответственно, передаёт ему
CDR_OPT_READONLY
при di->is_readonly
.
0
.
Надо будет и в v4 реализовать всю алхимию с readonly.
Побудительный мотив -- liucc: там сервера работают на 5Гц, и уже сейчас, при 4 рабочих стойках, там 5 серверов, вследствие чего оно обходит всё дерево по 25 раз в секунду -- в 5 раз чаще, чем нужно. А при 8 стойках будет 9 серверов (и 8 "лишних" обходов из 9). Содержимое там очень-очень развесистое -- и собственно knob'ов много, да и формул неслабое количество, так что на viper@PIII-1GHz оно жрет ~50% процессора (на wolf@Core2Duo-3GHz -- ~25% одного). И это у нас пока еще графиков нет, которым процессор будет сильно нужен.
05.08.2010: эта штука затронет аж 3 библиотеки:
Плюс -- надо бы в cda_eventp_t
вместе с reason
передавать и номер сервера. (А может, передавать оный sid как раз в
параметре sid
? Неа, не стоит -- надо добавлять еще один
параметр.)
Несколько соображений:
cmlrflags
(переименовать
бы их в просто currflags
, а? 06.08.2010: переименовал.).
Проект получается масштабным, но вроде реализуемым. Заодно, кстати, и потренируемся перед "кошерной" реализацией в CXv4 :-).
05.08.2010: приступаем:
cda_eventp_t
лишний параметр
"info", слегка меняем концепцию с параметром reason
:
теперь это будет при >=0 "sid-number", а при <0 -- что-то другое.
Учитывая, что ничего "другого" пока и нет, а нынешний
CDA_R_MAINDATA
=0 (CDA_R_AUXDATA=1), то всё вполне
совместимо и не-радикально. Тем более, что в большинстве программ в
event-processer'ах стоят для "сделать return" проверки либо
"reason!=CDA_R_MAINDATA" либо "reason!=CDA_R_MAINDATA &&
reason!=CDA_R_AUXDATA" -- первая вообще остаётся как есть, а вторая
заменяется за reason<0.
UpdateChannels()
правило, что 2-м параметром туда теперь передаётся не
synthetic
, а sid_n
(по OP_REFRESH -- -1).
synthetic
:
(Вообще, конечно, бибиканье+морганье по alarm'ам надо бы перевести на просто 1Гц-основу, но пока и так вполне катит.)
ChlSetDataArrivedCallback()
.
06.08.2010: продолжаем:
CdrProcess*()
добавлен параметр
cause_conn_n
-- по аналогии с 4cx/, перед
options
(в результате тут он получился первым).
eleminfo_t.conns_u
тот самый
указатель на массив булевских флагов. И safe_free() его в
CdrDestroyEleminfo()
не забыто.
CdrProcessEleminfo()
, если "не обрабатывать" -- то
хватаются старые флаги.
Кстати, подхват старых флагов добавлен и в
Cdr_treeproc.c::CdrProcessKnobs()
-- раньше
отсутствовал.
07.08.2010: майстрячим дальше -- теперь уже основное "мясо":
InternalAuxsrvNotifier()
'е поиск
по auxsidslist[]
-- добавлено поле
serverinfo_t.sid_n
, заполняемое в
cda_add_auxsid()
.
Замечание: это всё касается только стандартного использования
aux-серверов в Cdr и cda, когда работает
InternalAuxsrvNotifier()
. Если же клиентская программа
укажет свой processer, то, естественно, никакого reason>0 ей
прислано не будет -- она сможет получать уведомления о событиях данного
sid'а напрямую, в указанный processer (хотя, там есть свои
потенциальные косяки -- что если заказывать один и тот же auxspec
несколько раз с разными processer'ами, то реально зарегистрируется
только первый).
Функции -- cda_cnsof_physchan()
и
cda_cnsof_formula()
.
Идея такая -- в начале обхода элемента массив очищается, а потом идётся (рекуррентно!) по всем ручкам элемента, и взводятся соответствующие элементы в массиве, так что в конце цикла имеем надмножество всех используемых.
В дополнение к serverinfo_t.sid_n
добавлена также
"ссылка на parent-сервер" -- serverinfo_t.main_sid
. Так
что cda_cnsof_physchan()
тоже не занимается никакими
поисками "принадлежит ли вообще этот канал к указанному
main-серверу", а просто сравнивает.
09.08.2010: добиваем (в первоначальном варианте) -- собственно основная работа по определению "точек изменений":
CdrRealizeGrouplist()
, вызываемую из
CdrCvtGroupunits2Grouplist()
.
malloc()
'ится
булевский массив, затем он заполняется флагами.
conns_u=NULL
-- так что
подветка будет ВСЕГДА обрабатываться при обработке parent'а.
FillConnsOfElemInfo()
и FillConnsOfKnobs()
--
чья логика обхода дерева скопирована из CdrProcessKnobs()
,
с оговоркой в виде следующего пункта:
LOGT_SUBELEM
-- вставлено условие
else if (pass_n == REALIZE_PASS_2){НИЧЕГО-НЕ-ДЕЛАТЬ}
Собственно -- проверено, работает, потребление процессора viper'а снизилось с >70% до ~25%.
Имеет место, конечно, некоторая неоптимальность:
И пока в голову элегантного алгоритма не приходит -- лишь некое странное "аллокирование по мере надобности", когда malloc() делается на верхнем уровне, потом оно передаётся вниз, а далее буфер может "оставляться" там, и malloc()'иться новый. Запутанновато.
09.08.2010: P.S. При кодировании умудрился допустить пару дичайших ляпов:
cda_add_auxsid()
присвоения полей
sid_n
и main_sid
делались в
si->
вместо servers[auxsid].
.
В результате оно везде отдавало только 0-й (main) sid.
safe_free(info->conns_u)
забыл сделать
info->conns_u=NULL
.
В результате оно ориентировалось по какому-то мусору в heap'е, и была така-а-ая рождественская ёлка...
Если бы не они -- то вообще всё бы заработало сразу.
10.08.2010: чисто ради статистики: основной вклад в пожирание процессора вносил Motif: поскольку cx-starter, обрабатывающий ту же самую группировку, жрет ~5-7% против былых 12-16%.
04.04.2011: поскольку ни в одном *.c не
осталось ссылок на CDA_R_AUXDATA
, то этот символ удалён.
09.08.2010: поскольку оно вроде работает, и на вид ничему никак не мешает, то считаем за "done".
Если придёт в голову более хороший алгоритм оптимизированного поиска точек изменения -- вставим.
23.09.2010: кое-что всё-таки не работало.
Вылезшие симптомы и протокол расследования:
Однако, это явно было не так -- и ругани никакой при CDA_DEBUG_PHYSINFO=1 не выдавалось, да и оранжевела ручка исправно.
Однако отладочная печать, вставленная в тамошний
SetValue_m()
, показала, что после первого нажатия юзером
оно ВООБЩЕ ПЕРЕСТАЁТ ВЫЗЫВАТЬСЯ -- т.е., приходящие от сервера данные
ему не отдаются.
CdrProcessKnobs()
, НЕ сбрасывающем
wasjustset
при наличии CDR_OPT_SYNTHETIC
. Но
ведь у нас Chl_gui.c::UpdateChannels()
при ВСЕХ
sid_n!=CDA_R_MAINDATA передавал вниз "синтетичность" -- это наследие
старых времён, когда синтетическими считались не только
REFRESH-обновления, но и обновления от aux-sid'ов.
Итак, решение было очевидное: теперь синтетическими считаются ТОЛЬКО REFRESH-обновления. После этого всё заработало.
Неприятность сего видится только в одном: что теперь при непосредственном наличии в одном элементе ручек, связанных с РАЗНЫМИ серверами, они ВСЕ будут обновляться по приходу данных от ЛЮБОГО из серверов. И история -- тоже будет ехать с "повышенной" скоростью.
Причина в том, что точками изменения являются элементы, а не ручки -- В ОТЛИЧИЕ ОТ 4cx/. Впрочем, по факту в большинстве случаев каждый элемент будет вмещать ручки от одного сервера -- во-первых, потому, что defserver= может быть указано именно у элемента (CMD_{GETP,SETP}_BYNAME_I() используется очень редко -- лишь в linmag и linipp), а во-вторых, потому, что так логичнее.
В общем -- на эту неприятность просто забиваем. 14.06.2012: Ага, это вылезло в ippclient/linipp: там было рассчитано как раз на synthetic (см. 22-03-2007), а теперь его функционирование изменилось, так что всё опять попёрло с 12Гц. Пришлось в ippclient отключить double-buffering pixmaps -- тормозить стало меньше; но "тень" всё равно нефункциональна... Но ключевая проблема в случае ipp -- что сами IPP сделаны ЭЛЕМЕНТАМИ (в т.ч. включающими ручки сторонних серверов), а не РУЧКАМИ, как следовало бы по сути задачи.
16.12.2010@Снежинск-каземат-11:
eleminfo_t.conns_u
была int8*, хотя везде в
FillConns*()
-- uint8*. Переделал в uint8*.
RecLoadLevel()
отсутствовала проверка
ki->subelem->ident != NULL
перед
cx_strmemcasecmp(ki->subelem->ident, ...
в ветке "...or subelement's name".
DoFindKnob()
: полностью отсутствовала альтернатива
"...or subelement's name" (когда оно проверяет совпадение не только по
ki->ident, но и по ki->subelem->ident). В результате поведение
отличалось от поведения Save/Load, и в liucc, где в одном месте по
недосмотру ki->ident было "не тем".
22.12.2010@Снежинск-каземат-11: пофиксил и то, и другое.
29.12.2010: а ведь код-то в
DoFindKnob()
получился "не супер" -- при
ki->ident==NULL дальше оно уже и не пыталось проверять
ki->subelem->ident.
07.01.2011: если подумать, то правильный вариант -- просто изготовить функцию, "умно" сравнивающую len-строку с asciiz-строкой (с учётом возможных NULL и [0]==DELIM).
Так что -- сделал такую функцию NameMatches()
и перевёл
на неё те парные сравнения в DoFindKnob()
и
RecLoadLevel()
. Выглядеть стало намного лучше.
_CdrSetControlValue()
, предоставляющую со-библиотечникам
доступ к SetControlValue()
. Объявлена она в созданном для
этого Cdr_private.h, лежащем тут же.
21.01.2011: причина -- потребности Cdr_script.c, а из-за отсутствия у нас тут отдельной libdatatree таковой ПУБЛИЧНОЙ функции нету, как это ни глупо (отсюда постоянная маета с поддержанием двух почти идентичных копий в Cdr и Knobs).
29.08.2011: в связи с введением
datatree_SetControlValue()
сделанное тогда убрано, и
раздел "withdrawn".
22.01.2011: действительно, есть такие ляпы. Собственно segfault'ится cda, но проверки нужны в Cdr. Так что -- делаем:
CdrCvtLogchannets2Knobs()
:LOGK_DIRECT: по phys<0
выдаётся WARNING.
CdrCvtLogchannets2Knobs()
:LOGK_CALCED: теперь при
formula==NULL выдаёт WARNING; для revformula при is_rw -- тоже.
CdrProcessKnobs()
:LOGK_CALCED:
+"&&ki->formula!=NULL
".
Фигово, конечно, что все эти "WARNING" выдаются курица папой, даже без strcurtime(), но пока покатит.
В cda же добавлены проверки -- cda_srcof_formula()
,
cda_cnsof_formula()
, cda_execformula()
теперь
по formula==NULL делают return -1
. Оно, конечно, при
теперь-проверяющей Cdr не так важно, ну пусть будет (не Cdr единой...).
CdrFindKnobFrom()
, ведущую поиск не от верхушки (grouplist'а),
а ОТНОСИТЕЛЬНО -- от указанного элемента. Включая адресацию a-la
"../".
Побудительные мотивы:
(Правда, там сложнее -- нет возможности указывать ДВА privptr'а (grouplist и "self-location"), но с этим разберёмся.)
29.03.2011@Снежинск-каземат-11: соображения:
Схема именования и для каналов, и для ручек должна функционировать ОДИНАКОВО. Посему -- используем идеи и описания из обоих источников.
(Случай "/[a-z]+:/i=>глобальная" для ручек неактуален.)
Итого -- относительные ссылки вверх будут иметь вид ":..." (двоеточие, за которым следует нужное количество точек).
Вариант ":ИМЯ.ПОД-ИМЯ..." для просто относительных ссылок также легитимен.
Новая функция универсальнее CdrFindKnob()
-- с ТРЕМЯ
параметрами:
Knob CdrFindKnobFrom(groupelem_t *list, const char *name, ElemInfo start_point);
Заметки с реализации:
RecFindKnob()
переименована в
RecFindGlobalKnob()
-- чтоб не смущала своим названием.
DoFindKnob()
убран параметр namelen
,
который всё равно не использовался и только вызывал gcc'шный warning.
(Почему он вообще был введён в 2004-м -- теперь уже точно и не помню.
Кажется, для упрощения жизни RecLoadLevel()
'у -- чтоб тот
напрямую использовал содержимое line[]
, не суя туда
'\0'; но это так и не было доделано.)
DoFindKnob()
, отвечающий за поиск ручки в элементе
и его подэлементах, был вытащен в отдельную FindKnobInElem()
.
...неприятность в том, что теперь появилась зависимость alarmonoffled_widget.c/libKnobs.a от libCdr, что сразу же вылезло на всех не-Cdr Knobs-клиентах: cx/src/programs/xmclients/, liu/{xmclients,fastadc,plot}/. А всё из-за отсутствия libdatatree!!!
А может, пора уже, в конце концов, чёрт возьми, сделать и в CXv2 libdatatree -- хоть в простейшем варианте?
30.08.2011: в связи с наличием datatree зависимость от Cdr стала ненужной. Так что в cx/src/programs/xmclients/ она убрана еще тогда, 30-03-2011, а в прочих -- оставлена только для упрощения (поскольку там есть как Knobs-only (adc*), так и Chl-клиенты (liuclient)).
Как бы то ни было, исходная задача -- "done".
29.08.2011: еще тогда, в конце марта, все
функции, касающиеся поиска ручек по дереву, были сделаны в datatree,
тут же, в Cdr.c, ссылки переделаны на них, а местные версии
за-#if 0
'ены.
Сейчас же старый код удалён вовсе.
29.05.2014@Снежинск-каземат-11: сейчас вёрнута обратно
"адаптерная" CdrFindKnobFrom()
-- для "следующего уровня" в
виде ChlFindKnobFrom()
. Занадобилось для avgdiff_knobplugin.
Причина -- надо перегруппировать содержимое elem_dl200me_*.h, но чтоб старые режимы остались годными.
24.10.2012@Снежинск-каземат-11: пробуем. Хотя бы то, что делается сходу. Изменения коснутся как Cdr.c, так и datatree.c.
Замечание: элементы ВЕРХНЕГО уровня (которые groupunit_t) не могут быть прозрачными.
RecSaveElement()
: не выдаёт скобок элемента ".element
NNN {" и "}".
Также внутри НЕ делается +1 к уровню от предыдущего (точнее, делается -1 от переданного: который уже +1).
datatree_FindNodeInside()
.
RecLoadLevel()
собственный поиск по ручками
элемента заменён на неё.
FindKnobInElem()
.
datatree_FindNodeFrom()
в "walk upwards" вставлен
дополнительный пропуск ":"-элементов.
Плюс также в самом начале аналогично "корректируется"
start_point
, пря вверх, пока у текущего ident==":".
Chl_E_ShowPropsWindow()
при формировании строки "Path"
такие элементы пропускаются.
Вроде работает -- и режимы пишутся/грузятся, и внутригруппировочные ссылки пашут.
01.03.2013: да, давно работает, проблем не замечено, так что "done".
27.08.2013: идеологический вопрос -- а не считать ли прозрачными все имена, НАЧИНАЮЩИЕСЯ с ':', а не только равные ":"?
Собственно, вот этот факт и может представлять проблему -- ведь смысл в том, чтоб даже на "прозрачные" элементы можно было сослаться, а если механизм поиска по ссылкам на таких именах обломится (на относительных ссылках в текущий элемент -- точно да, т.к. оне выглядят ровно как ссылки вверх; использовать двойное двоеточие -- "::ИМЯ"?), то и весь смысл пропадает.
Конкретно в Cdr.c проблем нет -- там всё просто и очевидно (аж в целых двух точках).
Вот в datatree.c, конкретно в
datatree_FindNodeInside()
, похитрее...
16.04.2014: ПО-ХОРОШЕМУ, надо делать еще другое изменение: считать прозрачными и ПУСТЫЕ имена элементов, а не пропускать их вовсе, как сейчас. Такой подход выглядит логичнее.
А пропускать при сохранении -- только те, которые начинаются с
'-'; это определяется функцией
IdentifierIsSensible()
.
Но это уж в 4cx (аналогично пустым меткам строк/колонок, вместо "?", приводящим к копированию из ручек, и "!-!" в качестве указателя пустоты).
28.07.2014: да, НАДО считать прозрачными имена, НАЧИНАЮЩИЕСЯ с ':'.
(29.07.2014) сделано, проверки были в 4 точках. Теперь гонять и проверять, не вылезет ли чего.
28.07.2014: а можно ль при поиске мочь считать прозрачными корневые groupelem'ы с ':'?
(У Феди такая потребность возникла -- элементы верхнего уровня являются средством снижения бардака, а смысловой нагрузки их имена не несут.)
indicator
).
А чё б прямо сейчас не переделать ВНУТРЕННЮЮ реализацию,
чтоб из groupunit_t *grouping
генерилась уже одна Knob?
При этом корёжатся только API библиотек, а все старые _db.so-файлы останутся в силе.
...или всё-таки сильно проблемно, и проще будет уж полностью Cdr/Knobs поменять на v4'шные?
27.08.2013: не столько проблемно, сколько просто не стоит тратить время -- лучше использовать его как раз на допиливание новой архитектуры до юзабельности (в т.ч. на переделку под ppf4td). Так что -- отказываемся.
05.03.2014: а зря тогда отказался! Сейчас наткнулся на то, что из-за этого разделения onoff из одного groupelem'а не может иметь victim в другом -- поскольку не имеет возможности туда адресоваться.
Сейчас решаем проблему другим способом, чисто в интересах локальной задачи с victim'ом.
Идея: делаем возможность найти указатель на "группировку" -- первый из groupelem'ов -- от любого eleminfo. Просто чтоб оно прописывалось прямо в самом элементе, дополнительным полем.
Заметки по реализации:
eleminfo_t.grouplist_link
.
void *
, поскольку Knobs*.h у
нас формально не знают о существовании datatree и, соответственно,
groupelem_t
.
CdrCvt*()
--
конкретно прописывание делается...
CdrCvtElemnet2Eleminfo()
, для чего ему добавлен
параметр grouplist_link
.
CdrCvtLogchannets2Knobs()
доп.параметр --
для передачи нижестоящим CdrCvtElemnet2Eleminfo()
-- не
требуется, поскольку туда передаётся holder
, из которого
нужное значение добываемо.)
datatree_FindNodeFrom()
.
...а потом стало ясно, что в "клиенте" -- db_liu.h -- это реально бесполезно, поскольку там НЕСКОЛЬКО блокировок, а нельзя делать один victim у НЕСКОЛЬКИХ (они передерутся). Ну ничего -- всего-то надо будет кроме индивидуальных ввести также и кумулятивный статус, собирающий все те OR'ом, и уж он пусть имеет victim.
06.03.2014: оно всё равно не могло работать из-за хитрости в datatree -- что не искались "глобальные" многокомпонентные ручки (т.е., ручки с АБСОЛЮТНЫМИ именами, от "корня"). Это исправлено (см. раздел по datatree), и теперь всё работает -- проверено. Поскольку в заголовке пункта цель задана другая, то "done" туда ставить не будем, оставим "obsolete".
12.03.2014: самое смешное, что в конечном итоге
там (в db_liu.h) victim= убрано вовсе: поскольку оным является
кнопка (ARROW_RT), а они сами обеспечивают свою разрешенность на
основании CX_VALUE_DISABLED_MASK
.
reporterror()
(скопированный из cda.c).
27.08.2013: собственно, основной мотив -- есть возможность (слегка напрягшись) перевести сервер на v4'шную архитектуру под libsrv (на основе ppf4td), а тогда появятся server-side имена каналов, на которые можно ссылаться из клиентов, и занадобится таковая возможность в них.
Примерный план действий -- что и как можно будет сделать:
logchannet_t.style
и
elemnet_t.style
-- в концы структур. Никаких проблем не
составит, да и нужно скорее для галочки.
elemnet_t.defserver
-- чуток посложнее, и нужно
в основном для уменьшения кривизны, а не необходимо. Просто раз всё
равно формат менять -- то чё б не сделать.
logchannet_t.phys
сменить на более
хитрую конструкцию.
Это гарантирует генерацию ошибки при попытке откомпилировать старые (неисправленные) DB/*.h-файлы.
int
и char*
(для ссылки
по старому и новому стилю соответственно).
...а для ссылок из формул всё и так готово -- *_BYNAME_I, где и так указывается строка. Вопрос будет лишь в том, чтоб cda научить вместо номера канала уметь брать и символьное имя.
ЗЫ: это всё -- просто ПОДГОТОВКА, косметика, а основная работа по возможности использования резолвинга будет делаться в сервере, cda и cxlib'е (плюс чуток в Cdr).
11.09.2013: очередная смена формата -- муторно, поскольку СУ расползлась по куче мест (сварка, ВЭПП-4, ...).
Если же забить на пункты 1 и 2, ограничившись лишь п.3, то на 32-битных системах можно даже и без смены версии формата обойтись: считая, что если phys<100000, то это именно число, а если >=100000 -- то указатель (Linux вообще весь 0-й мегабайт не маппирует). Но нет, фиг -- у нас и 64-битные системы есть...
16.09.2013: неа, ведь если делать -- то не просто union, а более хитрую структуру селектор+union (заодно и даст ошибку компиляции на непеределанных группировках).
Выполняем подготовительные действия в cxdata.h:
PHYS_KIND_BY_ID
=1 и
PHYS_KIND_BY_NAME
=2 (обе НЕ =0!).
phys_rec
PHYS_ID()
и
PHYS_NAME()
.
После обеда: введён #define PHYS_REC, покамест равный 0, и в #if'ах по нему сделаны обновлённые внутренности -- чтоб можно было потихоньку группировки переводить:
style
в обоих.
elemnet_t.defserver
.
phys_rec pr
вместо phys.
CXSS_SUBSYS_VERSION_MAJOR
=5.
25.09.2013: продолжаем:
phys_rec
в
CdrCvtLogchannets2Knobs()
, с проверками.
PHYS_ID()
. Адская работёнка :)
А ошибки генерятся совсем другими вещами -- например, указанием
.minnorm=...
; ведь такого поля в phys_rec
нету.
Так что -- анализ логов с поиском "missing braces"...
Сделано, всё собирается и работает в обоих вариантах.
01.10.2013: на пульту поменяно на прошлой неделе -- вроде работает; в остальных местах поменяем позже, потихоньку, а раздел числим за "done" (хоть поля style и defserver, как и предсказывалось, просто не используются).
20.10.2013: состояние на текущий момент:
PHYS_REC
пока присутствует, что неприятно.
Почему-то у cangw-magsys постоянно была загрузка процессора под завязку. Настолько, что оно дико тормозило, каналы постоянно синели, а плавное изменение работало с дикими тормозами (а ИСТы не функционировали вовсе, из-за таймаутов по включению).
Стал разбираться.
Т.е., кто-то их постоянно опрашивал, а раз они несуществующие, то драйвер просто тут же отдаёт CXRF_UNSUPPORTED, и может это делать много.
Причём вовлечены были каналы НЕименованных устройств.
Расследование нашло виновника -- linmagx и cx-starter на linac2...
Причём, как выяснилось, проблема касалась и термостабилизации (cangw-thermo), но там всего 4 блока, так что загрузка висела ~20% вместо 6.
После убиения маразматиков всё нормализовалось.
24.09.2014: на днях история повторилась -- после переключения питания (на выходных было отключение) кто-то умудрился запустить cx-starter на linac2. Чтоб пресечь такую фигню на будущее, pult/ там переименована в pult.UNUSED/.
21.10.2013: а вот конвертации этих _net-полей в info_t-структуры никакого вообще не было.
eleminfo_t.defserver
(отсутствовавшее типа
потому, что сама концепция обрабатывается непосредственно в
CdrCvtElemnet2Eleminfo()
).
05.03.2014: очень, ОЧЕНЬ зря не приурочил тогда к несовместимому изменению формата subsysdescr еще и смену архитектуры дерева -- с NULL-terminated массива groupunit'ов на "общий корень". Не так уж много тогда было проблем внести это несложное исправление в несколько десятков db_*.h-файлов. (Хотя сейчас посмотрел -- нифига, около сотни. Тогда было напряжновато и те-то исправления вводить, а еще это -- хбз, как бы получилось. Но всё равно жаль...)
Сейчас же возникла потребность, а выпендриваться придётся по-другому (видимо, через ссылку на первый groupelem у каждого верхнего eleminfo (т.е., с uplink==NULL (Вечером: сделано, у ВСЕХ, а не только у верхнего.)).
15.06.2014: еще вопрос -- а чё тогда заодно не сделал 4-й/0-й диапазон -- ALWD? Уж оно совсем бы проблемы не составило...
Заголовочный файл -- fqkn.h, реализация -- fqkn.c.
16.01.2011: к его созданию привели следующие соображения:
Первоначальная мысль была назвать API "nameexp" или "knobname_exp" (префикс -- "kne_"), но потом подумалось, что сие действие явно будет не единственным по манипулированию с именами, так что -- пусть будет хоть и не совсем точное, но зато достаточно зонтичное "fqkn" (Fully Qualified Knob Name). Вероятно, что в v4 именно этот модуль будет отвечать за всякие относительности и вверховости (аналог "./" и "../" в файловой системе).
Замечание: API изначально делается просто для имён, никак не привязанный к семантике, так что должен подходить и для v2, и для v4.
21.01.2011: а ведь применимость этой вещи -- ШИРЕ, чем просто knob'ы. Еще и, как минимум, имена каналов в сервере.
Так что -- не переименовать ли сие в "fqcn" и не перенести ли в lib/misc/?
17.01.2011: вроде сделано -- с теми же способностями, что в globact, по тому же алгоритму, только проще и элегантнее.
21.01.2011: да, проверил -- пашет.
Конечно, сейчас оно совсем простенькое, ни ?/*/[], ни даже {X..Y}. Но нынешние задачи решает на все 100%, так что -- "done".
02.02.2011@Снежинск-каземат-11: "пашет", ну как же!
Видать, я потом пооптимизировал, уже после проверки, так что было всё плохо:
do_one_stage()
условие "обломимся по
!isletnumdot()" стояло ДО условия на '{', так что по оной оно
вместо обработки {}-списка считала имя добытым и заканчивала.
FindCloseBr()
оно умудрялось отлетать по ',',
считая, что '}' не нашлось.
Исправил, конечно, но отсутствие хоть какой-то внятной диагностики ошибок изряднейше напрягает.
Потому, что '-' -- вполне может быть просто куском имени, а вот последовательность ".." -- бессмысленна.
Вкратце -- это будет реализация скриптинга на базе lightscript_engine с добавлением команды set (позже -- возможно, и других, типа записи/чтения регистров).
21.01.2011: готово, работает. Фактически это просто мини-надстройка над LSE, реализующая свой набор команд, содержащий пока что одну-единственную команду "set".
У него даже никакого своего privrec'а нет, а в качестве privptr'а использует grouplist.
В первоначальном варианте оно использовало копию кода
ChlSetChanVal()
, но это оказалось не очень хорошо: при
этом не работали ограничения диапазонов. А надо, чтоб сценарии были
полностью аналогичны просто ручной уставке. Поэтому пришлось перейти
на _CdrSetControlValue()
(детали см. в общем разделе).
Побудительный мотив -- чтоб избавиться от появившейся позавчера зависимости Knobs-программ от libCdr.
Жить библиотека будет в lib/Cdr/, файлы -- datatree.[ch], libdatatree.a.
31.03.2011@Снежинск-каземат-11: населяем.
datatree_FindNode()
и
datatree_FindNodeFrom()
-- на замену CdrFindKnob
и
CdrFindKnobFrom
.
datatree_NameMatches()
вместо NameMatches()
.
datatree_SetControlValue()
-- с модификациями по образцу
4cx'ного set_knob_controlvalue().
Её надо бы заиспользовать вместо нынешней парочки в Cdr и Knobs, но это нетривиально и довольно муторно -- из-за танцев вокруг hilite. Сделаем позже, в спокойной обстановке.
Также пришлось в тучу Makefile'ов подобавлять ссылку на
$(LIBDATATREE)
25.08.2011: приступаем к заиспользованию
datatree_SetControlValue()
.
Еще тогда, в марте/апреле, была добавлена
set_knob_editstate_hook()
.
29.08.2011: продолжаем:
datatree_set_knobstate()
и
datatree_set_attn()
(эта парочка в Cdr была внутренними),
плюс datatree_choose_knobstate()
; ради последней пришлось
сделать #include "Cdr.h"
. 17.12.2012: поскольку все флаги еще летом переехали в
cx.h, то этот #include
убран.
HookPropsWindow()
добавлена регистрация
editstate_hook'а KnobsChangeHiliteState()
.
SetControlValue()
заменена на
эмуляцию -- вызов datatree_SetControlValue()
с бибиканьем
при >0.
Проверено -- hilite работает.
Выкидывать в пользу прямого использования datatree не стали:
if()
.
Кстати, по этому опыту ясно, что надо и в 4cx/ делать
MotifKnobs_SetControlValue()
. Часом позже -- сделано.
SetControlValue()
вообще
убрана, и везде вставлены прямые вызовы
datatree_SetControlValue()
.
_CdrSetControlValue()
выкинута
вместе с Cdr_private.h, а
Cdr_script.c::set_exp()
также переведена на
datatree.
CdrChooseColorState()
:
datatree_choose_knobstate()
;
SetColstate()
: datatree_set_knobstate()
;
SetAttnState()
: datatree_set_attn()
(SetRelaxing()
и SetOtherop()
оставлены для
наглядности);
ChooseStateRflags()
переехала в
datatree_choose_knob_rflags()
(заодно получив
предварительные проверки типа k!=NULL).
30.08.2011: проверено, вроде всё работает, правильно хайлайтится. Так что можно считать за "done".
datatree_
, поскольку у них в названиях фигурирует
_knob_
(унифицировано с 4cx/):
get_label_and_tip()
--
экс-_Chl_get_label_and_tip()
, плюс в
vacclient_meat.c и descr2html.c
тамошние
локальные версии убраны. В связи с этим Chl_gui.h
окончательно отправлен в отставку.
get_knob_label()
-- экс-Knobs'ов
GetKnobLabel()
.
get_knob_tip()
-- сделано для унификации, и даже
единственная точка применения -- CreateKnob()
--
переведена на него.
08.12.2011: собственно вопрос "да или нет" встал, когда захотелось в liucc сделать, чтобы ручки [Выкл]/[Вкл] у УБС'ов и/или БЗ1/2 при загрузке режимов НЕ меняли бы своего состояния. Это делается просто -- начать имя с '-', но ведь эти же ручки должны быть адресуемы из скриптов в элементе "Общее управление". Тут и встал тот вопрос. (Потом оказалось, что незагружаемость требуется не в УБС'ах, а в зарядных, но это уже не принципиально.)
Так вот: проверкой на этот префикс занимается
IdentifierIsSensible()
, применяемый только в
Cdr.c -- там оно влияет в основном на участие тестируемого в
сохранении и логгинге (даже на загрузку уже НЕ влияет).
ParseChanVals()
просто вызывает datatree_SetControlValue()
(а до-datatree
вызывала локальный SetControlValue()
, который в этом смысле
идентичен), а тот в любом случае делает
k->vmtlink->SetValue()
, и лишь ПОТОМ отваливает по
!k->is_rw
.
(!is_rw&&fromlocal)
заведомо бредово, и вполне можно
проверять его прямо в начале и отваливать.
25.10.2012@Снежинск-каземат-11: да, добавлено в начало, что по
(!is_rw&&fromlocal)
отваливать.
Readonly-ручки на экране из режима обновляться перестали.
А не вылезут ли какие косяки -- посмотрим.
P.S. Кстати, почему "такого не замечалось раньше" -- а на живой и нормально работающей системе этого обычно и не заметишь. Лучше всего видно на клиенте, у которого отсутствует сервер.
datatree_FindNodeInside()
для поиска имени внутри указанного
элемента с возможностью углубления в "прозрачные" (":")
подэлементы.
Сегодня неудобство вылезло на ВЭПП-4 в v4gid25x4n4. Там после включения каналы от vsdc2 оставались бордовыми из-за OVERLOAD (какой-то заскок коммутации), что смутило Утюпина, справедливо принявшего это за неработающесть устройства вообще.
01.11.2013: источников проблемы несколько, и фиг знает, кто из них первичнее и с кем воевать:
Поскольку проблема очевидным образом аппаратная; точнее, изначально была таковой (а сейчас, при просто глюках в софте той стороны, вполне может быть и программной).
Первоначально это определялось SF-флагом CALCERR, который лишен практического смысла при бредовом из-за аппаратной проблемы значении.
Потенциальные варианты решения:
Это в принципе можно повесить на
datatree_choose_knobstate()
. Технически не сильно сложно,
но для попременности придётся откуда-то брать flip-flop-флажок,
определяющий, кому из двух отдавать приоритет в конкретный момент.
И с флажком есть сложности -- КТО и КОГДА должен его переключать? Раньше хоть привязка к циклам была, причём они шли посекундно. А сейчас -- и циклы у разных серверов разные, и обновление частичное через conns_u. Разве что просто глобальный метроном в Chl, дрыгающий этот флаг раз в секунду.
Но моргание доставать будет преизрядно...
Предпочтительнее выглядит всё же вариант (а) -- он и проще в реализации, и менее раздражителен.
03.11.2013: из любопытства всё же реализовал вариант (b):
datatree_choose_knobstate()
, воизбежание многоуровневых
if()'ов сделан по таблице sf_hw_ff[2][2][2]
(реально
щелканье только в [1][1][x], в остальных случаях константы (а [0][0][x]
реально невозможна)).
set_datatree_flipflop()
.
ChlRunApp()
запускается метроном с циклом 2000ms.
Работает, забавненько. Естественно, только в v2; и НЕ в cx-starter'е (поскольку у того свои мозги выбора состояния (которых там 2 лампочки)).
04.11.2013: в сравнительно крупной программе -- v4gid25s с полутысячей ручек всё же начало помаргивать. Раздражает...
26.11.2013: а главная жопка -- похоже, с этим помаргиванием в Motif'е (2.2.2@RH-7.3) утекает память...
Так что временно отключено (принудительно flipflop:=0).
28.11.2013: а в 2.3.3@SL-6.3/64bit -- НЕ утекает.
А в 2.2 утекание есть и без этого перещелкивания, просто медленное -- по мере обычно индикации желтым/розовым.
FindKnobInList()
, используемой
datatree_FindNode()
и
datatree_FindNodeFrom()
.
06.08.2014: убрано правило "a global name may include only ONE component", и заменено на "если глобальное имя (начинающееся на '.') содержит более 1 компонента, то оно считается за FQKN -- т.е., полным именем ручки от корня".
Понадобилось для возможности адресоваться "к ручкам из других
groupelem'ов" -- иначе никак (т.к. НЕглобальные имена считались от
start_point
, а глобальные отказывались быть
многокомпонентными -- что и исправлено).
17.12.2012: технология проста:
CdrRegisterSimpleChan()
в формате SUBSYSTEM.CHANNEL.NAME,
а модуль по мере надобности грузит подсистемы (через descraccess),
затем ища в них указанные ручки (по имени CHANNEL.NAME).
Клиенту в качестве
"handle" возвращается прямо указатель на ручку (Knob
).
CdrGetSimpleChanVal()
и
CdrSetSimpleChanVal()
для чтения/записи.
Неясности/несделанности на текущий момент:
18.12.2012: по минимуму сделано -- надо проверять.
...чуть попозже -- проверил, чтение работает.
А вот запись работать не будет -- она СЕЙЧАС сделана через
datatree_SetControlValue()
(кое в v2 функционально только
для Chl-клиентов, из-за использования
uplink->emlink->SetPhysValue), а надо -- через
CdrSetKnobValue()
, что потребует чуть большей маеты.
19.12.2012: внутренности и концепция сильно переделаны.
simplesubsys_t.frs_cid
и
simplechan_t.nxt_cid
(канал добавляется по-простому -- в
начало списка).
EventProc()
'е оно прёт по списку и дергает все
не-NULL-callback'и.
Что неприятно -- никакой фильтрации по sid'ам/aux-sid'ам пока нет, так что для многосерверных подсистем оно будет вызывать callback'и ВСЕХ её каналов по приходу данных от ЛЮБОГО из серверов.
02.02.2013: еще на прошлой неделе проверено, что с python'ом оно стыкуется -- макеевский GUI-пример получал верные данные со сварки, python'овские callback'и вызываются.
Есть идейка о способе реализации ВЕКТОРНЫХ каналов в данной
концепции: поскольку все нынешние bigc-knobplugin'ы используют один и
тот же подход с bigc_n=ki->color
(он диктуется
fastadc/pzframe), то на сим факте и базироваться. Вопрос лишь в том,
где брать num_params и max_datasize -- ведь эта информация сейчас сидит
в самих knobplugin'ах. Или свалить это на клиентов?
11.06.2013: да, проверено, работает -- Федя уже активно пользуется, так что данный раздел (касательно скалярных каналов) ставим в "done", а векторными займёмся в следующем разделе.
Кстати, питоноподдержка живёт сейчас в cx/src/lib/4PyQt/cdr_wrapper.py, там же может быть собрана требуемая поддерживающая библиотека libCdr4PyQt.so, включающая libQcxscheduler.
bigc_n=ki->color
, datasize указывают клиенты).
19.06.2013: за прошедшие пару дней основа сделана. Для этого имевшаяся схема слегка переработана, так что поддержка "Sbigch" пользуется той же инфраструктурой "Subsys", что и обычные "Smplch" (но список ячеек свой). Так что сейчас и регистрация и callback'и работают.
Вопрос же дальше в выборе подхода общения с данными:
24.06.2013: всё больше склоняюсь к варианту (b) -- как в cda.
25.06.2013: вроде сделано (причём age и rflags при отдаче конвертируются в int'ы, с параметрами аналогично (вместо int32)).
Теперь вопрос делания соответствующего python'овского биндинга. В частности, там надо будет аккуратно обходиться с типом данных (byte/short/int).
26.06.2013: за компанию добавил функцию для ОТПРАВКИ данных (переходник к сделанному вчера в cda).
CDA_SERVERSTATUS_ALMOSTREADY
между
_FROZEN
и _DISCONNECTED
-- для будущей
ситуации, когда оно уже приконнектилось, но еще не запустило опрос, а
всего лишь вытрясает из сервера database-информацию (для начала --
что "этот канал принадлежит такому-то блоку"). Замечание:
но приоритет все равно должен быть у _FROZEN
-- при
ожидании БД-ответа более 5 секунд оно должно синеть.
И не забыть соответствующую поддержку в Chl_leds.c; там можно отображать такую ситуацию желтым (аналогия со светофором: "уже не красный, а желтый -- сейчас можно будет ехать").
08.12.2003: саму константу добавил. В
Chl_leds.c::leds_set_status()
пожелтение также
добавлено.
Заодно пришлось ОПЯТЬ раздвинуть набор COLOR_XXX_YYY
для добавления COLOR_JUST_YELLOW
-- в ином варианте
"гарантированно желтого" у нас не было.
ЗАМЕЧАНИЕ: может, пора-таки осознать опыт MultiEdit'а и сделать сразу "полную" палитру? /*23.08.2004: уже сделана :-)*/
В принципе, можно вполне эту радость реализовать, как в рамках cda, так и засунув в некую отдельную библиотеку (дабы в будущем можно было использовать как в интерфейсе к статической БД, так и для cda-программ напрямую).
11.01.2004: ммм... это... язык-то excmd позволяет несколько больше, чем дает обычная инфиксная нотация. Чуть-чуть приближается к его возможностям синтаксис Си, с его "побочными эффектами" (типа оператора "=").
Впрочем, это нормальная ситуация -- никакой язык программирования не позволяет использовать ВСЕ вычислительные возможности подстилающего ассемблера.
12.01.2004: Так что резюме: смиримся с этой "потерей" и изготовим язык со следующими свойствами:
Синтаксис | r/w | Действие |
---|---|---|
put PHYSCHAN=EXPR |
w | отправить значение в физический канал |
let REG=EXPR |
r/w | присвоить значение регистру |
refresh EXPR |
w | генерит OP_REFRESH |
return EXPR |
r | возвращает результат формулы |
"r" означает, что оператор разрешен в read-type формулах, "w" -- в write-type.
t0
..t99
(временные,
действуют в рамках этой формулы) и l0
..l99
("локальные" для окна/15.06.2007: а может, все-таки обойдемся без префиксирования каналов символом '$', оставив его для макрорасширений, а?
mod(x,y)
, "^" -- pow(x,y)
,
"^2" -- pow2(x)
, etc.
В принципе, можно было бы расширить дейкстровскую матрицу и на остальные операторы, в соответствии и их приоритетами в Си, но зачем? Это дало бы, во-первых, бардак с неочевидными приоритетами (как это и есть в Си), а во-вторых -- ненужное усложнение кода.
Единственная "засада" -- унарный "-". Но это надо поизучать алгоритм Дийкстры -- там оно или уже предусмотрено, или легко добавляется.
06.08.2006: насчет появившихся с тех пор префиксов (условного исполнения и т.п.):
allow_w
.
prglyput
.
if (expr)
COMMAND
.
29.01.2007: стоит компактно записать результаты размышлений за последние пару месяцев:
var ИМЯ
или
var ИМЯ[РАЗМЕР]
-- так объявляются массивы.
Соглашение "l0
..l99
" нафиг не нужно. А
вот временные (если они вообще нужны?) -- надо думать, как объявлять
или именовать.
29.01.2007: как можно бы прямо в нынешнюю
архитектуру впихнуть just-in-time-транслятор с нормального языка:
ввести дополнительный опкод OP_COMPILE_I
, которому
параметром-строкой указывать "программу".
Опкод уже ввел (='c'
), он сейчас приводит к ошибке --
возвращает NULL и описание "фича пока не поддерживается".
Нерешенные пока проблемы:
01.02.2007: ответы на вопросы предыдущего раздела:
09.06.2007: в свете желаемого введения TCL'я, и "множественных" формульных языков -- а что, если параметр OP_COMPILE_I сделать префиксируемым типом языка? Т.е.:
#!tcl ТЕКСТ-НА-TCL'е
- TCL'ный код.
#!fla ТЕКСТ-НА-ФОРМУЛЬНОМ-ЯЗЫКЕ
- обрабатывается по вышеприведенному проекту от 12-01-2004.
=КАНАЛ
- просто берется значение
канала (это будущий синтаксис для CXv4, где НЕ будет никакого
phys
, а пока что оно будет компилироваться просто в
CMD_GETP_???, CMD_RET).
=ВЫРАЖЕНИЕ
- упрощенный вариант синтаксиса "#!fla".
И, кстати, ежу ж понятно -- формулы МОЖНО и НУЖНО уносить в отдельную библиотеку. Причем -- опять же с плагин-архитектурой, чтобы эти "#!ЯЗЫК" можно было добавлять по мере надобности.
А вообще -- поддержку TCL'я-то (пока что НЕ для формул) можно будет худо-бедно ввести хоть прямо сейчас, в Chl.
15.06.2007: а лучше -- для простоты и
однозначности -- вариант =КАНАЛ
сделать так: у нас ведь в
исходных-формулах предполагается имена/номера каналов префиксировать
каким-нибудь символом, типа '$', так? Ну вот пусть
прямая-ссылка-на-канал и будет иметь вид $КАНАЛ
.
(А если имена каналов будут ка-есть, без префиксов -- то
==КАНАЛ
; мнемоника -- что "==" это как бы "тождественно
равно", т.е. "связать с каналом".)
P.S. А еще можно тогда заодно упростить создание "синтетических"
ручек -- отображаемых на регистр: ввести синтаксис
%РЕГИСТР
.
22.01.2004: точнее, таймаутами и слежением за дескрипторами.
А собственно режим SELECT будет прост -- при этом понадобится всего навсего
NnnnSignal()
, а прямо сразу в
точке, где обычно вызывается NoticeSignal()
, напрямую
дергать cda_signal_handler()
. Наверное, более
правильным/NoticeSignal()
на вызов внутренней функции, которая в
зависимости от режима выполняет либо то, либо другое.
cda_new_server()
;
cda_set_environment
-- аналогично.
Проблема: загрузка таблиц для OP_LINTRP
из файла
делается обычно отдельной функцией, а cx-starter/staromakh не будут
иметь никакой информации о необходимости вызова оной.
Предлагаемое решение: ввести дополнительный опкод
OP_LINTRP_BYFUNC
, который бы при обнаружении из
cda_register_formula()
приводил к вызову функции,
указанной в его операнде, а она б грузила файл и отдавала указатель на
местоположение. Cda же, как и в случае с OP_GETP_I
,
подменит и опкод, и операнд.
Замечание: дополнительный опкод можно сделать по аналогии с
OP_{GET,SET}P_BYNAME
-- взяв ту же букву, но в другом
регистре. Но надо б унифицировать, в каком регистре "исходный"
вариант, а в каком -- "рабочий".
Потенциальное осложнение: надо б сделать,
чтобы в таких случаях всегда возвращался указатель на
malloc()
'нутую область -- тогда в будущем, при появлении
"cda_destroy_formula()", можно будет просто делать free()
,
не задумываясь, откуда оно взялось, и не статический ли это массив.
Но муторно это!
19.01.2004: сделал typedef
excmd_lintrpld_func
. Ввел новый опкод
OP_LINTRP_BYFUNC_I='I'
, плюс соответствующий CMD_-макрос.
Поменял местами значения OP_{GET,SET}P_I
и
OP_{GET,SET}P_BYNAME_I
, так что теперь все "преобразуемые
во время cda_register_formula()" опкоды -- в заглавном регистре, а
"рабочие" -- в строчном. Заодно ("за компанию" :-) под горячую руку
попали и OP_SQRT
с OP_DUP
, сменившие регистр
с заглавного на строчный.
Код для конверсии вроде сделал. Он при ошибке (функция вернула
NULL) проставляет команду OP_NOOP
. Если хочется
произвести "глобальный аборт", то нехай функция просто сделает
exit()
.
Проверил на istc/xmclients/istcc.c
-- работает.
Возникшие по ходу дела мысли:
OP_CASE
, делать условное выполнение этой команды.
Замечание: похоже, надо все-таки в Chl обрабатывать флаг CALCERR отдельно от CXRF_SERVER_MASK -- т.е., не включать в биты в окошке "Свойства", а демонстрировать еще каким-нибудь фоном (коричневым? али чего?).
excmd_content_t
есть
место лишь под одно поле (это ж union), но физически память под 2
указателя там есть -- sizeof(double)==sizeof(void*)*2. Так что...
Делаем полем excmd_content_t
структуру с двумя членами, а
макрос CMD_LINTRP_BYFUNC_I()
становится
двухпараметрическим?
cda_register_formula()
), оно считает
таблицу из файла (и если что -- может обломить всю программу), а потом
станет лишь отдавать указатель на нее.
19.01.2004: изготовил инфраструктуру для
OP_CALCERR
-- опкод, макрос, поддержку в cda. В качестве
кода волюнтаристски выбрал '\a'
: мнемоника такая, что это
"звонок", сигнализирующий об ошибке. При ошибке конверсии
OP_LINTRP_BYFUNC_I
теперь генерится именно
OP_CALCERR
.
19.01.2004: несколькими часами позже: проверил, убедился -- работает.
20.01.2004: (навеяно предстоящим "унифицированным" введением директории ~/cx/settings/) еще мысль:
Мдяяяя...Ведь в 99% случаев нам нафиг не нужна никакая "особая" функция загрузки таблицы, а достаточно считать ее из файла (как в будущем она будет браться из БД).
Посему стоит ввести еще один "преобразуемый" опкод --
где "tableref" было бы неким "абстрактным" именем, которое могло бы сейчас являться компонентом имени файла (~/cx/settings/APPNAME/APPNAME_TABLEREF.lst), а в будущем -- именем-ссылкой в БД.OP_LINTRP_FROM(char *tableref)
Проблема 1: два регистра для опкодов уже использованы, где взять третий ;-)?
Проблема 2: понадобится ведь В CDA знать этот APPNAME...
20.01.2004: Резюме: пока махинации с OP_LINTRP_*_I заморозим. Секцию помечаем как "done", а решение насчет "делать ли _FROM или _BYFUNC и функцию с параметром" отложим до разбирательства с потребностями Феди-jr. по интерполяции кусочными сплайнами -- приобретем больше опыта.
19.04.2004: Губин намекнул -- вообще-то это аппроксимация, а никакая не интерполяция (баран я безграмотный...).
Итого -- заменяем термин "lintrp" на "lapprox" во всех именах
(опкоды, типы, ...), а "interpolation" на "approximation". Заодно
поменял опкоды с 'i'
/'I'
на
'a'
/'A'
(оный был свободен).
22.10.2004: пришло время, когда уже можно/нужно доделывать и интерполяцию (с загрузкой из файла), и сплайны. Highlights:
excmd_lapproxld_func
" на структуру, и
там указывать ДВА указателя -- на собственно функцию, плюс параметр.
27.10.2004: а вообще-то стОит сделать ДВА
параметра -- один pointer
и один int
. В
случае "LAPPROX_FROM" это будут имя файла и номер колонки, из которой
считывать значения -- чтобы можно было в один файл помещать таблицу для
нескольких связанных каналов сразу.
10.11.2004: насчет двух параметров: а ведь при этом у
нас размер excmd_content_t
резко увеличится: раньше там был
максимум double
(8) или int+pointer (4+4=8). А тут -- будет
12 байт. Криво...
(При реальной-то БД это все будет без разницы -- там все равно все будет передаваться единым текстовым потоком, с полями переменной длины...)
Похоже, надо в будущем "excmd_lapprox_net_rec" делать ссылку на вторую структуру -- в которой и складировать нужную информацию. Ведь аппроксимация -- вещь редкая, и ради нее тратить место жалко, лучше чуть усложнить код, но зато раз и навсегда решить проблему "доп. параметров".
Получасом позже: ввел в cxdata.h структуры
excmd_lapproxnet_rec
(содержит указатель на функцию и на
доп. структуру) и excmd_lapprox_info_rec
(собственно эта
доп. структура -- пока указатель плюс int).
30.11.2004: кстати, этакая "умная" мысля на тему разделяемых/общих таблиц:
Надо бы сделать, чтобы при поиске файлов таблиц делалось бы 2 попытки: вначале -- в текущей директории, а потом -- в "../common/".
24.12.2004: переопределил тип
excmd_lapproxld_func
-- теперь функция-загрузчик принимает
вместо былого void
параметры const void *ptr, int
param
. Единственные на данный момент клиенты istcc.c и
vibro.c подправлены.
Часом позже: потихоньку, через #if'ы, внедрил "новую" схему, с
"тремя параметрами" -- и в cxdata.h, и в cda.c.
Проверил -- параметры записываются, вычитываются и передаются
корректно. (Но навернутость там, особенно в определении
CMD_LAPPROX_BYFUNC_I()
-- просто дикая.)
Итак, приступаем к созданию стандартного/умолчательного загрузчика
таблиц -- lapprox_table_ldr()
. Где оно будет искать файл:
./%s.lst, ../common/%s.lst ("%s" -- запрошенное имя).
Пока никаких махинаций с $HOME/pult и/или $CX_ROOT не делаем.
BTW, Старостенко заявил, что очень полезным было б уметь оперативно показывать на экране калибровочную кривую.
25.12.2004: функция почти доделана -- реализованы поиск файла и парсинг (включая поиск N-й колонки). Осталось добавить отведение памяти.
Возникла пара мыслишек:
excmd_lapprox_rec
, и собственно
массив единым блоком -- чтобы потом можно было делать ОДИН
free()
. При использовании стандартного загрузчика -- он
это будет делать сам, а в остальных случаях -- можно копировать
результат.
27.12.2004: подпереопределил
excmd_lapprox_rec
для того, чтоб удобнее было прилеплять
массив прямо к самому описателю -- добавил в конец поле
data[0]
.
Итого -- lapprox_table_ldr()
готова, она работает.
Заодно обнаружил ляпсус в коде аппроксимации -- а что, если таблица
состоит менее чем из 2 точек? Вставил в
cda_do_linear_approximation()
проверку: при
npts==1
всегда возвращается сама эта точка, вне
зависимости от значения x
, а при npts<1
--
возвращается x
. В обоих случаях отдается статус "ошибка".
04.01.2005: с чистой совестью выкинул из
cxdata.h за-#if
'ленные старые определения,
поскольку имеется STABLE/cx.20041231/. Аналогично убран
соответствующий старый код из
cda.c::cda_register_formula()
.
13.01.2005: надо б при ненахождении файла как-нибудь ругаться на stderr.
Вечером того же дня: сделал, ругается.
26.04.2005: в "интеллектуальном" поиске файла в
cda.c::lapprox_table_ldr()
имелась глупейшая
ошибка: в "списоке мест" F_*
первое из списка шло просто,
без =F_FIRST
(как в CdrOpenDescription()
,
откуда идея была скопирована).
В результате оно умудрялось ПЕРВЫЙ раз отработать нормально, а потом -- всегда считывало тот же самый файл.
Исправлено.
31.10.2005: обнаружилась некоторая
пренеприятность: в cx-starter'е строчка "nmagsys" ВСЕГДА имела горящий
флажок SFERR. Как показало расследование -- оттого, что
cx-starter
запускается в произвольной директории, и
попросту не находит файла, указанного в
CMD_LAPPROX_FROM_I()
.
Решение: добваил в lapprox_table_ldr()
дополнительную
альтернативу в конец списка поиска -- F_ATHOME
, скопировав
оное из cx-starter.c::ReadConfig()
.
20.04.2007: давно раздражало, что при запуске программы не из текущей директории она не находит таблицы (которые лежат в той же директории, что и бинарник).
Очевидное решение: надо бы ввести еще один шаг при поиске --
F_ATARGV0, как в CdrOpenDescription()
. Вот только
проблема -- argv[0]-то в lapprox_table_ldr()
не
передается!
Впрочем -- под linux проблема решается очень просто, через
program_invocation_name
, которая в cda и так уже есть.
Так что сделал, практически копированием из descraccess.c.
А вообще -- надо все-таки повсеместно внедрять принцип "имя программы и отладочная информация" (26-01-2006), тогда проблема бы не возникла. (Хотя в любом случае добавить данной функции параметр argv0 пришлось бы.)
02.07.2009: вылез некрасивый ляп: если таблица
сортирована задом наперед, то интерполяция нифига не работает... Это
Жариков выдал калибровочную таблицу для Iсетки (WELD02_CHAN_MES_UP), а
она от больших чисел к малым. И оно в такой ситуации, проверяючи
(x<matrix[0])
, сразу брало 1-ю строчку и ставило
CALCERR.
По-хорошему -- надо бы сортировать свежезагруженную таблицу,
например, с помощью qsort()
.
Так и сделал -- оказалось очень просто, с учетом того, что у нас X и
Y идут друг за другом, так что использовал size=sizeof(double)*2 и
функцию compare_doubles()
, приведенную в libc.info. Пока
закомментировано, а завтра надо проверить.
03.07.2009: раскомментировал, проверил -- вроде работает.
cda_getphyschan{val,raw}()
НЕЛЬЗЯ использовать si->phys{codes,tags}
-- эти массивы
даются как буфера cx_getvset()
'у и могут измениться в
любой момент, так что мы отдадим "некогерентный" набор
значений/тэгов/флагов.
Надо будет добавить к chaninfo_t
поля
phys{raw,tag}
, заполнять их в DecodeData()
и
именно ТОЛЬКО ИХ отдавать из cda_getphyschan{val,raw}()
.
22.01.2004: и, кстати, в махинациях с DoubleBuffer есть некий комментарий-вопрос на тему "а нужно ли нам копировать старые коды/тэги/флаги в новые буфера". Если сделаем как "по уму", то будет не надо.
09.02.2004: ввел поля
chaninfo_t.phys{raw,tag,rflags}
, и теперь
getphyschan*()
читают именно их. А заполнение
производится в DecodeData()
.
Итого -- корректность достигнута.
Но общего впечатления удовлетворенности нету. Вроде бы доступ к
передаваемым cx_getvset()
'у полям есть в очень
ограниченном числе мест:
cda_addphyschan()
.
ActivateDoubleBuffer()
/FoldDoubleBuffer()
DecodeData()
-- там оно
безопасно, поскольку происходит между приходом данных и очередным
заказом.
RequestData()
.
Видимо, неудовлетворенность вызывают именно манипуляции с double-buffer -- копирование-то там осталось, хотя оно едва ли теперь нужно...
cda_getphyschan{val,raw}()
получилось бОльшая часть общего
кода, кроме того, они в Cdr вызываются парой.
Просто напрашивается -- надо сделать одну функцию, которая выполняет
оба действия, а эти две оставить просто wrapper'ами, подставляющими
NULL либо в vp
, либо в rp
.
22.01.2004: сделал,
cda_getphyschanvnr()
(Value aNd Raw).
05.02.2004: аналогично сделал сразу расшитыми
cda_setphyschan{val,raw}()
-- общая часть в
setphyschanvor()
(Value Or Raw).
NaN
, и если впрямую
смаппировать некий регистр на поле ввода (или мало ли зачем понадобится
умолчательное значение), то оно так и покажется -- "nan". Обнаружилось
это с клиентом istcc.c, где захотелось так сделать
искусственные каналы "напряжения для зума и фокуса".
08.04.2004: проект решения: делаем специальный
опкод -- OP_SET[LCL]REGDEFVAL[_I], который прописывает указанное
значение, только если isnan(regs_p[intv])
.
Вопрос только, какой теперь еще для ЭТОЙ операции опкод выбрать...
Парой минут позже возникла идея: а зачем, собственно, добавлять код операции? У нас же "номер регистра" и так побит на поля, и собственно номер занимает 16 бит, а остальные 16 -- "тип регистра". Так можно от "типа" откусить 8 бит, сделав их "кодом условия". Например, 0 -- безусловное присвоение, 1 -- если isnan().
19.04.2004: сделал. Откушены 8 бит, и введен
OP_ARG_REG_COND_MASK=0xFF000000
плюс
OP_ARG_REG_COND_INIT=1<<24
.
Ввел одну-единственную команду --
CMD_SETLCLREGDEFVAL_I
, поскольку для TMP-регистров она
только вредна, а не-_I
-вариант сделаем, если понадобится.
И ЕЩЕ: при чем тут была isnan()
? Ведь и так
есть "статус" -- regs_inited
, а значение
неинициализированного регистра вообще скорее будет не NaN, а очень даже
0.0.
24.09.2004: БЛИ-И-И-ИН... Обнаружился допущенный тогда ляп: вычитывание инициализирующего значения из стека было внутри if()'а, т.е., производилось только при неинициализированности. А в противном случае это значение оставалось лежать в стеке, засоряя/портя его...
Пофиксено.
NewDataProc()
обнаружилась такая последовательность:
То есть сразу после расшифровки данных отправляется следующий запрос, и лишь потом дается знать клиенту. Это "не есть гуд" в случае, когда на основе полученных данных клиент принимает какие-то решения с записью в каналы (т.е., в случае обратной связи) -- при вышеприведенной схеме будет отсрочка реакции на один цикл. Лучше вставить уведомление клиента ДО отправки запроса, чтобы дать ему возможность реагировать.DecodeData(si); RequestData(si); NotifyClients(si, CDA_R_MAINDATA);
08.04.2004: перенес, делов-то. Теперь надо следить за возможными отрицательными последствиями.
01.03.2007: да, возможные последствия -- пропуск "кадров". См. раздел о "cda_continue()".
DoLinearInterpolation()
. Очевидное решение --
экспортировать ее.
19.04.2004: экспортировал -- переименовал в
cda_do_linear_interpolation()
и объявил в cda.h.
Часом позже: переименовал в cda_do_linear_approximation()
.
cda_del_server()
, а
cda_stop_server()
: его программа содержит
переключатель Запущено/Остановлено, и в остановленном состоянии совсем
незачем занимать сеть неиспользуемыми данными.
27.04.2004: немного поразмыслил: а в чем проблема-то со stop'ом? Вроде все прекрасно под него ложится...
08.11.2004: проблема-то заключалась в том, что один
sid мог повлиять (посредством aux servers) на другие, что было коряво.
Теперь же, когда aux-sid'ы локальны, можно легко сделать
cda_stop_server()
.
18.02.2005: кстати, разбираясь с большими
каналами (точнее, с тем, когда их можно удалять) обнаружил, что
у нас
NewDataProc()
вызывает RequestData()
БЕЗУСЛОВНО! Это есть неправильно -- с такими фокусами никакой
stop() сделать не удастся.
Вообще-то более корректный подход -- пусть все, кому надо вызывать
RequestData()
, делают это безусловно, а уж она должна
проверять, что если !si->is_running
, то ничего не
делать.
Получасом позже: так и сделал. Теперь при вызовах никаких проверок
нету, зато сама RequestData()
проверяет, что и
is_running
, и is_connected
, а в противном
случае отваливает.
18.02.2005: СДЕЛАЛ!!! Наконец-то взял и сделал
вызов -- назвал его, для унификации-трехбуквенности,
cda_hlt_server()
.
20.02.2007: а ведь cda_hlt_server()
не работало бы. Тогда ведь нифига не проверял, но -- там собственно
уставка si->is_running=1
была забыта.
И еще -- для полной-то корректности вставлена проверка в
NewDataProc()
, что если !si->is_running
,
то ни декодирования пришедших данных, ни уведомления клиентов НЕ
делается -- чтобы "hlt" имел действие сразу, а не после прихода
следующей порции.
Также в RequestData()
вставлена дополнительная проверка
-- если уже si->req_sent
, то она ничего не делает. Это
чтобы вызов cda_run_server()
сразу после
cda_hlt_server()
(БЕЗ прихода данных между ними) не
пытался опять отправить запрос.
21.02.2007: начал использовать в ndbp -- там при при отключенном режиме running и при отсутствии oneshot соединение тормозится -- чтобы даже запросы не шли.
В общем -- "done".
27.04.2005: да, и еще аналогичное оранжевение в phm-tsyline.c с полем "ЦАП мотора"...
Это требуется для замены былой олеговой программы "candid", нынешнего chl-клиента "camsel". Там надо иметь progress-bar, бегущий с 40 секунд до 0.
Самое очевидное решение -- сделать OP_GETTIME, вызывающую
gettimeofday()
, и, т.о., возвращающую секунды с
01.01.1970.
А дальше спокойно можно этим числом манипулировать, в т.ч. вычитать
его из чего угодно, включая 0, не боясь переполнения -- ведь
реальная-то арифметика идет в double
.
22.09.2004: ввел OP_GETTIME='t'
и
CMD_GETTIME
, плюс поддержку опкода. В
cda_execformula()
есть оптимизация -- оно вычитывает время
в свою переменную и взводит флажок, и при повторном (в рамках той же
формулы) OP_GETTIME вернет уже полученное значение.
24.09.2004: проверил -- работает. Еще бы! :-)
27.06.2007: собственно -- а почему она отдает только секунды? У нас ведь есть потребность и в большей точности, а результат все равно вещественный -- так пусть она дробное число и отдает.
Так и сделал -- работы было на минуту.
Надо б разобраться!!! Вот только как...
14.05.2005: в принципе-то понятно, что
происходит: при отсутствии SO_KEEPALIVE
, если был послан
запрос, а после этого связь почему-либо тукнулась (упала машина,
отсутствие связи в течение N часов и с той стороны соединение закрыто,
а досюда FIN не дошел, etc.), то и cda+cxlib не шевелятся, и ядро
само тоже НИЧЕГО ПОСЫЛАТЬ НЕ БУДЕТ.
Вот так и получается -- что просто НЕТ причины-триггера для обнаружения обрыва.
Так что выходов два: либо включать SO_KEEPALIVE
, либо
иметь собственный "application-level ping". Кстати, ssh/sshd имеют и
то, и другое: флажок "KeepAlive" включает TCP-опцию, а
sshd'шный ключ "ClientAliveInterval" при !=0
включает app-ping (это работает только с протоколом версии 2, и по
умолчанию отключено).
И еще -- по умолчанию в ssh и sshd ключ "KeepAlive" включен, поскольку юзерам это удобнее. И какая-то RFC также рекомендует там поступать.
Так что, на сейчас напрашивается естественный выход -- надо включать
SO_KEEPALIVE
и в cxlib'е, и в cx-server'е (естественно,
только для TCP'шных соединений, для unix'ных это ни к чему).
14.05.2005: вставил в
cxlib.c::ConnectTo()
"условное" уставление
SO_KEEPALIVE
, при TCP-соединениях. Но лучше б это
выполнять не там, а когда коннект УСТАНОВЛЕН.
Получасом позже: БАРАН!!! У нас же сейчас в cxlib'е НЕ используется
nonblocking -- так что при выходе из connect()
'а
соединение уже установлено.
15.05.2005: вставил аналогичную мульку и в
cx-porter.c::AcceptConnection()
.
22.06.2005: собственно -- а
почему надо было бы включать SO_KEEPALIVE
только ПОСЛЕ
установления соединения?!
Поскольку уже достал warning о моем хаке (сравнение
serv_addr==&idst
), то перенес уставку флажка в сразу
после создания сокета.
25.01.2006: сегодня окончательно разобрался со всеми тонкостями (см. раздел по cxlib'у), то -- case closed, "done".
auxsidslist
.
06.10.2004: все оказалось просто -- оно, найдя
запрошенный сервер в servers[]
, проверяло, не добавлен ли
он уже в auxsidslist
. И если нет -- то добавляло. А
проверки на тему, что он ==defsid -- не делалось!!! Вставил проверку.
30.10.2004: ну дык -- понятно, почему они множатся!
Имеем ДВА main-соединения (сделанных из клиента явным
cx_new_server()
), оба смотрящих на ОДИН сервер. И когда
ВТОРОМУ делаем {GETP,SETP}_BYNAME/ADDSERVER, то поиск по имени находит sid
ПЕРВОГО. А этот sid и не равен базовому sid'у, и, естественно, отсутствует
в списке auxsid'ов. Вот она его к этому списку и добавляет.
(Похоже на предыдущую проблему, не правда ли? :-)
Вывод -- надо вначале сравнивать с srvrspec
'ем базового
соединения. Так и сделал.
30.10.2004: кстати, "злобная" мысль в отношении cda: надо бы сделать, чтобы aux-серверы были локальны для КАЖДОГО "основного" соединения. Тогда автоматом решается проблема "а что, если мы грохнем соединение, которое в качестве дополнительного подхвачено другим соединением?". Т.е., мы поставим барьеры между разными соединениями, и они перестанут мешать друг дружке. (И все станет предсказуемее.)
И, BTW, тогда эти aux-серверы станут РЕАЛЬНО локальными -- т.е., вообще никак недоступны для клиентской программы -- поскольку на них никак нельзя будет легально сослаться. Следовательно, мы в cda сможем делать с ними все, что захотим.
01.11.2004: сделал это изменение -- теперь поиск сервера с таким-то именем ведется только среди auxsid'ов данного соединения (плюс -- проверяется сам defsid). Надо протестировать. /*14.05.2005: да протестировано уж полгода как -- все нормально. Собственно, там глючить особо-то нечему, все просто и корректно.*/
В принципе, стоит подпеределать и нотификацию -- вместо
установки своего нотификатора, например, ввести поле parent_sid, и из
NewDataProc
или NotifyClients()
вызывать
InternalAuxsrvNotifier()
. Но это не срочно -- подождет.
04.01.2005: окончательно удалил ранее
за-#if
'ленный старый код, с нелокальными auxsid'ами.
30.01.2005: хватит думать о том, "как
по-правильному" надо бы было это сделать, и для начала изготовить хоть
как-нибудь. В таком простом варианте какие, собственно, потребуются
примитивы? Стало очевидно, что все очень просто:
cda_add_bigc (sid, bigchan, numparams, datasize, ???);
cda_getbigcdata (chanh, *buf, size, ofs); // "ofs" with "size" allows reading parts of the data
cda_getbigcparams(chanh, *list, count);
cda_setbigcparams(chanh, *list, count);
Краткое пояснение: мы исходим из того, что большие каналы используются не возможно-общим образом (как "трубы"), а именно так, как они реально работают с имеющимися драйверами и программами -- т.е.:
Символы "???" означают некоторые доп. параметры -- типа флагов cachectl и immediate.
Да, и еще: поскольку это "временная" реализация, то назовем все эти
вызовы не cda_NNN()
, а cda_TMP_NNN()
.
30.01.2005: Параллельно мысля насчет того, как в
этой схеме можно было бы реализовать режим zero-copy (см. bigfile.html,
31.05.2003) -- очень просто! Достаточно функции
(Ну и подобным же образом можно получать (если, за каким-то бодуном,
приспичит) доступ к параметрам.)
cda_getbigcdataptr(sid, chanh)
Естественно, есть и ограничения -- что тогда нельзя будет делать GrowBuf() с тем буфером, где содержатся данные большого канала. Или -- ввести правило, что эти данные валидны ТОЛЬКО до следующего вызова cda.
Парой часов позже: а-а-а!!! Понятно, почему надо будет ввести такое правило -- ведь "начальным пунктом" цепочки zero-copy может быть совсем даже не cda, а cxlib. А уж какие у него могут быть гарантии на тему фиксированности буфера в памяти -- никаких!
07.02.2005: продолжаем деятельность.
Кстати, мысль:
Надо иметь возможность УДАЛЯТЬ большие каналы из cda'шного списка -- ведь, в отличие от обычных каналов, большие -- весьма ресурсоемки, и надо иметь возможность от них избавляться. Или еще и приостанавливать их вычитывание?
Да, сделал прототип --
Следствие: массив
int cda_del_bigc(cda_bigchandle_t bigch);
bigcinfo
будет разреженным -- значение
номера "-1" будет означать "свободное место". Это будет влиять
1) на поиск незанятой позиции; 2) на процесс заказа.
Вечером: что ж, учитывая вышеуказанный принцип, сделал
первоначальный вариант cda_add_bigc()
.
Кстати -- пока что реализация имеет ОДИН буфер
данных, так что то, куда cxlib кладет возвращаемые данные и
параметры -- это тот же, откуда производится вычитывание клиентом через
cda_getbigc{data,params}()
. Что, в случае асинхронного
использования (как это есть сейчас) дает некую ненадежность. И вызов
cx_{enter,leave}_critical()
внутри читающих функций
ситуацию не спасет -- ведь клиент может вычитывать данные в несколько
приемов, плюс, он же читает данные и параметры, а это уже ДВЕ точки.
Так что -- пока что пусть КЛИЕНТ заботится о критических секциях.
16.02.2005: йоу!!! А сейчас-то ведь
вышеописанной проблемы реально не существует, если клиент будет
производить все махинации ИЗНУТРИ нотификатора -- ведь
NewDataProc()
вызывает нотификатор ДО отсылки следующего
запроса.
16.02.2005: баран! В вызовах {set,get}bigcparams() наличествовал параметр count, но отсутствовал параметр start! В общем, переделал все три вызова на порядок (...,from,count,ptr).
17.02.2005: интенсивно размышлял на тему о том, как бы корректнее отрабатывать ситуацию, когда пользователь уставляет какой-то параметр из серединки списка. Проблема тут в том, что когда придет ответ на "текущий" запрос, то все те параметры, которые юзер НЕ уставил, надо будет скопировать из ответа, а не портить.
Очевидное решение: надо иметь на каждый параметр по флагу, указывающему, был ли этот параметр изменен с момента отправки последнего запроса; и в "RequestData()" измененные не трогаем, а остальные -- копируем из принятых в "для отправки".
18.02.2005: да -- и надо ведь мочь вытащить
текущие tag и rflags большого канала. Ввел для этого вызов
cda_getbigcstats()
с тривиальнейшими внутренностями.
Кроме того, доизготовил реализацию cda_del_bigc()
. Там
есть одно ограничение: удалять большие каналы можно только в момент
"паузы" -- т.е., когда запрос НЕ отправлен. В противном случае cxlib
клал бы ответ в буфера, которые уже free()'d.
ОБЩЕЕ: надоело, что в начале каждой функции cda_XXXbigcYYY() идет
один и тот же подготовительный код, раскодирующий handle, проверяющий
sid и bigcofs. Посему сделал
#define CDA_BIGC_INTRO()
, выполняющий эти действия.
ИТОГО: вроде бы всю "обвязку" сделал.
18.02.2005: пришло время переделывать собственно
RequestData()
(а DecodeData()
-- как, есть в
ней что?).
Тем же вечером: сделал. Вроде бы все сделал...
Кстати -- поскольку всякие tag, rflags и прочие retbufused передаются cxlib'у по указателям, а там они запоминаются на будущее, то их нельзя держать в "растущем" буфере, а пришлось поместить в тот же общий буфер, куда и данные и параметры.
20.02.2005: кстати, смешно: ведь тот вариант,
который разработан и реализован в cda, поддерживает множественные bigc
в одном запросе -- основные "сложности" в реализации пришлись именно на
множественность. И даже более того: реально инфраструктура cda
поддерживает СМЕШИВАНИЕ обычных и больших каналов, достаточно убрать
проверки на тему ST_BIGC
-- все уже готово.
Вот так-то -- и ведь в cxlib'е похожая ситуация, там также есть
поддержка множественных bigc и весьма искусственное разделение на
CT_DATA
/CT_BIGC
, которое убирается в пользу
унифицированной инфраструктуры просто "на раз".
А вот cx-server этого всего не поддерживает!!! И не будет поддерживать -- пока не превратится в cxsd+cxsd_fe_cx...
21.02.2005: начинаем испытания при помощи istc/xmclients/impacis10.c. Первоначальная проверка (на sim_drv) показала, что данные приходят -- так что минимально оно работает :-). Теперь надо проводить полномасштабный тест каналов с параметрами.
28.02.2005: испытания на "кошечке" impacis10 завершились успешно -- все работает прекрасно, так что теперь надо действительно испытывать по-настоящему -- на adc333.
01.03.2005: мысль:
А как насчет ввести понятие "persistent parameter" -- чтобы, если некую программу интересует все же именно измерение с КОНКРЕТНЫМИ параметрами (с конкретным разрешением, с конкретным числом точек...), то чтобы cda не пыталась приспосабливаться к тому, что ставят другие клиенты, а ВСЕГДА проставляла бы в запросе то, что попрошено.
Т.е., вводим вызов cda_setbigcparam_p()
, которому
указывается "статус устойчивости" данного параметра -- 0/1.
Получасом позже: сделал такую вещь. Для этого добавил в "общий буфер" еще массив -- "prsargs[ninfo]". "Упертость" каждого параметра устанавливается вышеупомянутой функцией, а при отправке проверяется, что хотя бы один из флагов {mod,prs} выставлен. А сбрасывается, естественно, только mod, так что "устойчивый" параметр ВСЕГДА будет выставляться.
В принципе, разумно было бы объединить флаги mod и prs в одном int'е как битовые флаги, но -- это будет больше возни.
Кстати, нашел ляп, не успевший проявиться -- место в буфере под
массив modargs резервировалось, но поле bi->modargs_ofs
оно заполнить забывало. Исправлено.
27.04.2005: поскольку уже некоторое время отработали (вполне успешно) и impacis10.c, и adc333.c, и даже самый на данный момент навороченный -- phm-tsyline.c, можно считать, что цель достигнута. Помечаем раздел как "done".
14.05.2005: тьфу ты, лопухнулся при реализации
адресной арифметики в cda_setbigcparam_p()
-- оно там
забывало к началу буфера прибавлять еще номер параметра, так что всегда
махинировало с 0-м...
Пофиксил.
25.05.2005: да-а-а, рабо-о-отает! Губин аж жаловаться стал -- что это phm-tsyline, будучи запущенной, не дает ничего менять :-).
12.09.2006: пиз-сец!!! В
cda_getbigcdata()
было забыто использовать параметр
ofs
! Просто доселе он нигде и не применялся, а вот в
adc200 для индивидуального вычитывания нанных каналов он понадоблися.
И вот -- "программа глючит"!
Исправил -- всего-то прибавить его надо было...
И еще, заодно, обратил внимание -- там при проверке "не слишком ли
большой ofs" стояло сравнение ofs>rr->retbufused
,
вместо надлежащего ofs>=rr->retbufused
, что
могло приводить к вызову memcpy()
с нулевым размером.
Также пофиксил.
25.06.2013: тогда всё было заточено исключительно под ЧИТАЮЩИЕ большие каналы, типа осциллографов. Но хочется иметь поддержку и ПИСАНИЯ -- для козачиных таблиц.
Просмотр кода показывает, что всё должно б быть делаемо очень просто:
RequestData()
--
cx_bigcreq()
, которому сейчас передаются data=NULL,
datasize=dataunits=0.
cx_bigcreq()
флаг бы
проверялся, и если выставлен -- то данные передаются, а флаг
сбрасывается.
Главный вопрос -- насколько подобная реализация адекватна нашей задаче, подойдёт ли оно для козачиных таблиц.
...После обеда: вроде сделано. В основном по вышеприведённому проекту, с несколькими деталями:
cda_setbigcdata()
есть параметр
ofs
; т.е. -- всё симметрично чтению.
cda_add_bigc()
(иначе -- E2BIG).
08.09.2013: проверено,
cda_setbigcdata()
работает, причём совместно с
cda_setbigcparams()
вроде бы даже удовлетворяет
требованиям weldclient'а.
15.03.2014: в cda_getbigcparams()
и
cda_setbigcparams()
НЕ проверялось на count!=0, и всегда
вслепую делалось memcpy(); а формально не определено, как она должна
действовать в данном случае. Так что добавлена проверка.
Кстати, в cda_getbigcdata()
и
cda_setbigcdata()
с параметром size
аналогично.
Это нужно, например, в phm-tsyline.c, чтобы там не посылались зря запросы, результаты которых все равно не устроят. А главное -- чтобы запросы не посылались "раньше времени" (т.е., чтобы не слался запрос на измерение осциллограммы раньше момента, когда будет подведен нужный фильтр).
27.04.2005: с одной стороны, некая подготовка к
этому уже имеется -- там предусмотрено (зачем? не ради этого ли
самого?) поле bigcinfo_t.active
.
С другой стороны, даже "деактивация" канала не дает полной гарантии -- ведь может быть такой сценарий: (1) отсылка запроса; (2) деактивация; (3) активация; (4) приход ответа на запрос шага 1. И тогда в момент 4 НЕЛЬЗЯ будет понять, что это "не тот" запрос.
Так что, может быть, реальное решение проблемы "гарантированной очередности" лежит в timestamp'ах запросов?
26.02.2007: кстати, пока что потребность в "приостановке действия" больших каналов реализуется просто через приостанов их серверов -- в ndbp сейчас сделано именно так. ПОКА хватает, но вот в будущей версии надо будет реализовать так, как предложено в разделе "Отключаемость каналов в cda".
timeval
от
gettimeofday()
), когда был отправлен запрос.
Это позволит иметь полную гарантию, что мы получаем ответ на запрос, отправленный ПОСЛЕ некоторого момента, а не фиг-знает-как-давно.
27.04.2005: делов-то -- ввел поле
serverinfo_t.req_timestamp
, заполняемое перед вызовом
cx_run()
. И функцию cda_get_reqtimestamp()
,
позволяющую этот timestamp узнать.
lapprox_table_ldr()
. Раз уж
cda_do_linear_approximation()
предоставляем -- то пусть
тогда cda экспортирует "законченный" интерфейс для интерполяции. Нефиг
в других программах (типа той же phm-tsyline.c) дублировать
отточенный и обезжученный код.
И заодно -- вылезла потребность уметь запрашивать не только Y из
указанной колонки, но и X тоже не только из 0-й. Есть два варианта:
(1) упаковывать в один int
ДВА параметра, либо (2) сделать
в cxdata.h::excmd_lapprox_info_rec
ДВА
int
'а.
27.04.2005: пожалуй, сделаю по 2-му варианту -- это хоть и кривее, но два параметра нужны крайне редко.
Так что введен публичный вызов --
cda_load_lapprox_table()
, являющийся просто переходником к
lapprox_table_ldr()
, и принимающий ДВА номера колонок. Он
их пакует в старший (X, умолчание -- 0) и младший (Y) полу-int'ы
intparam
'а.
А уж модификация самой lapprox_table_ldr()
под два
параметра ее только украсивила -- там теперь один цикл по колонкам в
строке, БЕЗ отдельного выцепления x.
04.05.2005: поскольку давно сделано и работает -- "done".
06.05.2005: хрю -- там был ляп: в
lapprox_table_ldr()
стояло
if(ycol<2)ycol=2;
... Это описание default-позиции для
"Y", что и вообще есть не вполне корректно -- она default'ом быть не
должна в принципе. Наиболее "корректным" в свете этого выглядит
if(ycol<1)ycol=2;
-- чтобы по умолчанию просто бралась
2-я колонка.
Исправил, все работает.
14.05.2005: в cda-то вставить проще простого --
хотя проще дополнительным интерфейсом, принимающим те же параметры, что
и cda_srcof_formula()
, плюс номер интересующего нас
"исходного"; и чтоб оно кроме имени возвращало и текущие значения -- и
double, и сырое. Естественно, нужнО и "сколько исходников у формулы?".
Короче -- получается что-то очень похожее на интерфейс
"cda_status_NNN()
".
А самое сложное будет в Cdr (там надо сей интерфейс дублировать...) и в Chl_knobprops.c -- там-то придется организовывать отображение заранее-неизвестного-количества...
И стоило бы, для удобства использования, несколько обобщить интерфейс, а именно (список почти копируем из раздела "Групповой инкремент каналов" за сегодня):
physval
, но и
"userval
" -- то, что уставлено пользователем, пусть еще не
вернуто обратно сервером.
20.05.2005: идеи по ходу дела:
userval
".
Но это НЕПРАВИЛЬНО -- подобное может вылезти еще в куче мест. Так
что -- лучше уж добавить еще API, типа "cda_getphyschanuserval()",
который при вышеуказанной ситуации возвращает юзерское значение, а
обычно -- просто вызывает cda_getphyschanval()
.
cda_execfurmula()
некоторые флаги-опции. Вообще-то уже есть параметр
is_write
, который можно превратить в набор битовых флагов
(как два года назад flags
(нынешнее
CDA_FLAG_OTHEROP
) превратилось в rflags
).
Есть также пара "глобальных" замечаний:
и дополнить троицуdouble *args
,int nargs
{result,tag,rflags}_p
параметром
nret
.
А вообще, все, что тут понаписано -- это переход от ВЫЧИСЛИТЕЛЯ ФОРМУЛ к ПРОГРАММЕ, с входными и выходными параметрами.
Вопрос только -- а реально ли мы сего хотим? Или стоит остаться при простом и элегантном -- как есть сейчас?
Если хотим, то надо разрабатывать ДРУГОЙ API.
06.06.2005: да, а еще -- если мы все-таки как-нибудь разрешим "команды записи в read-формулах", то тем самым получим работающий механизм ОБРАТНОЙ СВЯЗИ.
12.06.2005: угу, мало того, уже СЕЙЧАС формулы используются (будут использоваться :-) для неслабого программирования -- camsel.
13.03.2006: ага, а по сравнению с тем, что уже есть, плюс делается (reset) и планируется (demag) в linmag'е, camsel -- просто детский лепет :-).
connect()
МНОГОКРАТНО, и, в результате, стартуют ОЧЕНЬ долго.
13.09.2005: выяснил, почему. Сначала делается первый connect, который обламывается. Он регистрирует таймаут на 1с. А потом делается второй, который тоже обламывается, но тоже после некоторого висения, в течение нескольких секунд. А когда дело доходит до "основного цикла", то Xt обнаруживает истекшие таймауты и дергает их, и так они по очереди истекают в течение висения друг друга.
Попахивает мисдизайном, не правда ли? Или, как минимум, недодизайном...
P.S. А все, кстати, оттого, что Xt пытается "слишком правильно" реализовывать таймауты. Вот cxscheduler -- тот бы просто прошелся по имеющимся на момент НАЧАЛА цикла, а потом в безусловном порядке прошелся бы по дескрипторам, выполнив их требования, и лишь затем устроил бы следующую проверку таймаутов. А Xt -- циклит, собака...
14.09.2005: соп-пственно -- проблема-то исчезнет
при переходе на неблокирующийся connect()
, но то ж когда
будет!
01.06.2010: эта фича начинает уже ОЧЕНЬ сильно мешать: на ЛИУ, если какой-то из cPCI-крейтов отключен, то cx-starter подвисает, считай, почти до второго пришествия (а если там, не дай бог, нажать кнопку мыши -- то еще и мышь грабит (а если правую -- то и клавиатуру...)).
Идея проекта временного решения:
- Пусть cda пытается подключиться к серверу не через 1с=1000000мкс, а, например, через 5с.
- А cxlib пусть вместо блокирующегося
connect()
пользуется функциейtimed_connect()
(она есть в архиве), с таймаутом 2с=2000000мкс.
Обсуждение:
01.06.2010: да, сделано по приведенному проекту -- и в cxlib, и в cda.
Только -- имевшаяся в архиве timed_connect()
была
не-рабочей: во-первых, имелась ошибка-опечатка (под строчкой "No
response?" проверялась r
вместо ret
), а
во-вторых -- оно неправильно определяло успешность/обломность
готового-на-запись сокета, так что оно было поправлено по образу
fdiolib'а.
14.06.2010: попробовал -- неа, бесполезно, cx-starter'у не помогает; видимо, в нем слишком много соединений.
Да еще и какие-то наведенные ошибки лезли: сам cxlib вякал (хбз почему)
CheckR: connection closed, readstate=0, readsize=0(Была мысль: из-за того, что оно местами слишком долго подвисало, превышая 60-секундный интервал MAX_UNCONNECTED_TIME в cx-porter'е других, доступных серверов; но нет -- у тех ничего такого в логах не было...)
И это в дополнение к приколу с необрабатывавшимся ETIMEDOUT -- когда в cda первоначальная ошибка "таймаут" считалась фатальной.
06.07.2010: вторая попытка:
cx_setconnecttimeout()
(который бы при <0 ставил default-значение).
(Если из-за тормозов на удаленной стороне (что возможно) она и не успеет уложиться в этот таймаут -- то через 5 секунд уже будет готова. Хотя и так тормоза бывают на уровне userspace, а уж ядро-то 3-way-handshake выполнит резво.)
Сделано по этому проекту. Но общее впечатление -- уродство, конечно, поскольку Chl_app и cx-starter вынуждены напрямую обращаться к cxlib'у, минуя стек библиотек, что есть однозначно неправильно.
18.08.2010: и еще одно неприятное последствие вылезло, из-за того, что теперь оно пытается реконнектиться не через 1 секунду, а через 5 секунд (определяется значением DEFAULT_RECONNECT_TIME_USECS=5000000 -- кстати, это-то я нигде не записал), с cx-starter'ом.
А именно: cx-starter определяет "запущенность" сервера через cda, и возможна ситуация, что по нажатию на кнопку "Старт (программы|сервера)" он вызовет запуск, и, хотя сервер уже запустится, но еще в течение 5 секунд это не будет известно, и если в течение этого времени опять нажать "Старт", то он попробует запустить сервер ПОВТОРНО. Реально от этого спасает то, что:
И, кстати, помечаем раздел как "done".
17.11.2010: увы, не так всё безоблачно. Реально
100мс таймаута при коннекте вовсе не обеспечивают именно 100мс
"задержки" -- при хоть какой-то загрузке процессора из-за
дополнительных действий (gethostbyname() etc.) исполнение
cx_connect()
выливается в болшее время -- иногда до 500мс
(?!).
В результате опять повторяется та ситуация -- программа почти всё время проводит в тех ожиданиях, почти не откликаясь на GUI.
Что делать -- хбз. Вводить возможность указывать кроме
connect_timeout также и cda'шный интервал реконнекта, который сейчас --
DEFAULT_RECONNECT_TIME_USECS=5000000
?
17.11.2010: да, ввёл
cda_setreconnecttime()
и возможность указывать в
subsysdescr.options параметр reconnect_time. После
добавлении liucc reconnect_time:10000000 его отзывчивость
резко возросла.
За компанию в cx-starter добавил config-директиву директиву ".reconnect_time USECS".
17.01.2013: вся основа проблемы исчезла еще осенью, при переходе на cxscheduler-based cxlib.
11.06.2014: а сейчас и все те доработки тоже выкидываем, ибо более не нужны.
14.01.2006: реализация может выглядеть так:
time()
при каждом
getphyschan*()
-- шибко уж накладно). Халтурновато,
конечно, но пойдет.
cda_getphyschanvnr()
к значению phystag
(только аккуратно, избегая переполнения, с максимумом в 255!).
Конечно, несколько некорректно суммировать возраста, подсчитанные (для серверов с cycle_size!=1000000) "в разных временных потоках", но пока что вполне подойдет. Ведь цикл БОЛЬШЕ 1с у нас вроде не используется.
А в идеале, конечно, надо уметь спрашивать у сервера период его цикла.
14.06.2007: да, именно так!!! Надо переходить от нынешнего понятия tag_t/"возраст" к нормальным временнЫм интервалам, примерно следующим образом:
ВозрастКанала(мс)=Возраст(цикл)*Период(мкс/цикл)/1000(мкс/мс)
И еще -- надо каждому физическому каналу дать (в дополнение к r,d и q) свойство "период свежести". Детали:
((unsigned)-1)
миллисекунд), либо размер цикла (неа,
лучше бесконечность).
В общем -- при проходе по формуле будут считаться ДВА временнЫх числа вместо нынешнего одного (tag) -- возраст и период-свежести,
возраст = max(возрасты-всех-каналов-формулы), def=0 период-свежести = min(возрасты-всех-каналов-формулы), def=infinity
19.06.2007: хм, а понятие "период свежести формулы" -- несколько некорректно: ведь период-свежести -- нужен только, чтобы с ним сравнивать возраст того же канала. А сравнивать возраст одного канала (максимальный) с периодом-свежести другого канала (минимальным) -- малоосмыслица.
По-хорошему, надо аккумулировать (OR'ом) именно флаг "DEFUNCT" от всех задействованных каналов, а "период-свежести" клиента вообще мало колышет.
Следствие-вывод: флаг DEFUNCT должен считаться именно в самой cda, а отдавать "наверх" ни возраст, ни период-свежести -- реально нафиг не надо.
08.12.2009: в продолжение истории: хочется всё-таки отвязать посинение от размера цикла сервера.
Для этого cx-server теперь с пакетом CXT_READY
возвращает в Res2
значение BaseCycleSize
. А
cxlib его складирует у себя, и отдаёт через
cx_getcyclesize()
.
24.12.2009: добавил и в cda: оно теперь в
момент установления соединения -- в ConnectProc()
--
добывает размер цикла сервера, а потом в DecodeData()
пересчитывает полученный tag_t
как tag*cyclesize/1000000.
Делается это через scale32via64()
, и с проверкой на
переполнение. И, поскольку диапазон ограничен 8 битами, то, чтобы не
казались свежими заведомо несвежие каналы при частоте выше 255/5=51Hz,
этот пересчет делается только при исходном !=MAX_TAG_T.
И, для полноты картины, такой же пересчет делается и для больших каналов -- для чего пришлось вбухивать отдельную ветку кода. (За компанию, кстати, исправил давний по-факту-безобидный ляп, что большим каналам при создании НЕ ставился бесконечный возраст.)
11.03.2010: добавил также
cda_cyclesize()
-- понадобилось для
Chl_histplot.c, чтоб писать ПРАВИЛЬНЫЕ подписи к оси времени.
Заодно вставил default-инициализацию поля
serverinfo_t.cyclesize
при создании соединения (раньше-то
оно никогда не требовалось до прихода данных, а теперь -- будет).
01.04.2011@Снежинск-каземат-11: опять вылезла эта потребность. Когда Мелкостенко пытался калибровать УБСы, не замечая, что сервер их погашен. А если б оно синело -- то вопросов бы не возникло.
Далее (пока топтались около каземата в ожидании автобуса -- это был последний день, уже собрали все манатки) воспоследовала длинная дискуссия с Пановым на тему "а вот сделай чтоб оно просто синело -- вообще в обход всех механизмов, просто синело б и всё".
Естественно -- просто взять да поусинеть нельзя, это делается в слишком глубоком месте кода при вполне определённых условиях. Но родилась такая идея:
А ведь можно завести в cda еще один "heartbeat", с периодом ~10 секунд,
который будет проверять -- что если от сервера ничего не приходило уже
>5 секунд (или дольше 2 периодов сервера -- что больше), то
ИМИТИРОВАТЬ приход данных: просто дёрнуть
NotifyClients(,CDA_R_MAINDATA)
, предварительно обновив то
поле "давность последнего прихода данных от сервера", которое будет
прибавляться ко всем возрастам.
Соображения по поводу:
Другое дело, что в такой ситуации "фальшивость" данных вряд ли является каким-то злом -- после такого длительного посинения уже всё пофиг.
12.04.2011: кстати, а ведь для посинения достаточно вызвать этот фальшивый-NotifyClients() лишь ЕДИНОЖДЫ -- при первом обнаружении проблемы.
BTW, это можно реализовывать флажком -- "не требуется вызывать fake-notify", который сделать общим для обоих случаев: и для свеже-созданных соединений, и для посиневших.
А лучше -- флажок "был приход данных", и сбрасывать его -- как раз после "фальшивого" вызова (лучше тем, что начальное состояние флага -- 0).
12.04.2011: вопрос -- а с CDA_BIGC/ST_BIGC-соединениями стоит это делать, или нет? Ведь у больших каналов своя логика, и свои мозги на тему "когда последний раз приходили данные".
Очевидно, нет.
22.04.2011@Снежинск-каземат-11: кстати, для CXv4, с per-channel defunct-временами, этот фокус, скорее всего, не подойдёт. Или подойдёт?
22.04.2011@Снежинск-каземат-11: делаем:
serverinfo_t.was_data_reply
DEFAULT_DEFUNCTHB_USECS=10*1000000
.
DefunctHbProc()
,
который и реализует жульничество:
was_data_reply
, тип --
ST_DATA, и с момента last_data_time
прошло больше
FROZEN_LIMIT
, то:
phystag=MAX_TAG_T
(т.е.,
безо всякого хитрого сложения -- просто сразу "бесконечно старый").
was_data_reply
.
NotifyClients(,CDA_R_MAINDATA)
.
Но это -- потом.
27.04.2011@Снежинск-каземат-11: да -- собственно "усинивание"
вытащено в отдельную MarkAllAsDefunct()
(со своей проверкой на
was_data_reply и ST_DATA), теперь вызываемую и из
FailureProc()
.
Вообще, хоть делается не совсем то, что заявлено в заголовке, но смысловая цель -- чтоб синело при давно-не-приходе -- достигнута, так что считаем за "done".
22.08.2012: почему-то при тестах с nkshd485 оно так НЕ работает...
...разобрался -- поведение сие было следствием изменений от
23-07-2012, когда CDR_FLAG_DEFUNCT превратился в CXCF_FLAG_DEFUNCT и
переехал в епархию cda. Если раньше этот флаг уставлялся при обходе
дерева Cdr'ом, то теперь он менеджится в DecodeData()
--
т.е. только по РЕАЛЬНОМУ приходу данных.
Решение тривиально -- в MarkAllAsDefunct()
к уставке
phystag=MAX_TAG_T добавлено и
ci->rflags|=CXCF_FLAG_DEFUNCT
(формально его одного
было бы и достаточно). Посиневание вернулось.
16.05.2013: забавный эффект: в программе linmagx,
запущенной на ring1 и берущей данные с linac1, время от времени
происходит всеобщее посинение. Разборки показали, что это результат
деятельности DefunctHbProc()
в условиях высокой загрузки
компа: время от времени программа не получает данных дольше
FROZEN_LIMIT секунд (приколы scheduling'а), и тогда оно всё усиневает.
Это -- по результатам беседы с Гусевым, рассказавшим, что от него на сварке (Медведко, Купер, Логачев, и прочие заказчики) надо иметь дело именно со СРЕДНИМ значением. И именно среднее значение должно потом участвовать во всяких вычислениях.
Подобные слова уже проскакивали и у Лебедева -- см. секцию Cdr за 26-08-2004, там идея делать сие в Cdr'е. Но усреднение в Cdr дает лишь "конечное", отображаемое значение, а в дальнейших вычислениях оно уже никак не использовабельно.
Кстати, насчет "что считать шагом" -- а если бы в cda (Cdr? Chl?) как-нибудь можно было регистрировать "формулу-callback", каковая вызывалась бы именно по приходу данных от базового сервера (отдельные -- для aux-сервером), то таковая формула просто выполняла бы надлежащие вычисления и складировала бы результат в некий локальный регистр.
Да-а-а!!! А отсюда рукой подать до еще одной формулы -- "инициализация", вызываемая одноразово при создании группировки.
И, кстати -- а за компанию с AVG будет желание мочь в cda получать и
MINMAX, и DEVN (да-да, еще в oldcx был опкод OP_DEVN
,
просуществовавший (хотя никогда не поддерживавшийся) аж до 28-03-2003
(см. bigfile.html)).
22.01.2006: кстати, чтоб особо не напрягаться -- это все НА БУДУЩЕЕ, если зачем-то понадобится, то легко можно будет сделать по приведенному выше проекту. А так -- не стоит брать в голову, пока это реально нафиг не нужно, и нефиг тратить время.
23.01.2006: BTW, ведь AVG, MINMAX, DEVN -- это и есть "временнЫе" операции, про которые есть в NOTES.html заметка от 11-10-2005@Geneva/ICALEPCS'2005 --
(TU2_2-2O, F. Carbognani, "Automation of the Lock Acquisition of the 3 km Arm Virgo Interferometer"): Их "макросы" <-> наши "формулы". Но у них есть еще "временнЫе" операции -- min/max/avg, и "пространственные" -- fft.
02.07.2007: насчет "формулы-callback'а", вызываемой по приходу данных: ведь мы хотим избавиться от единственного, "стержневого" события "приход данных", перейдя к более индивидуальным "callback'ам", так? Тогда и формула "по приходу данных" (и "отдельные для aux-серверов") становится малоосмысленной.
Зато -- поскольку callback'и скорее будут тяготеть к контейнерам, то правильнее будет ввести каждому контейнеру поле "формула, исполняемая по приходу данных".
(А "инициализация" -- она так и останется формулой для-группировки.)
Кстати, "формулы" в этом контексте -- это, естественно, "cda-источники данных" (CxDataRef_t), которые могут быть и tcl-формулами, и вообще много чем.
Так что -- надо, по образу и подобию cxlib, использовать
program_invocation_short_name
.
24.01.2006: сделал. Реально, чтобы не
выпендриваться в десяти разных местах, а также по опыту вчерашнего
вставления-всюду-strcurtime()
-- просто перевел всю cda на
скопированную из cxlib'а reporterror()
.
Инициализация/заполнение же progname[]
-- в самом начале
cda_new_server()
.
30.07.2010: привел оба варианта -- cda'шный и cxlib'овский -- в соответствие с современным подходом: '\n' печатается самим report()'ом, а не передаётся во всех вызовах.
10.10.2013: из 4cx/ скопирована
cda_report()
, которой также передаётся sid
, и
многое (кроме обработки формул) переведено на неё.
20.02.2006: проект:
cda_register_formula()
строка переводится в
"индекс" -- условный номер функции, являющийся именно индексом во
внутренней таблице.
if
'ом
или switch
'ем внутри cda_exec_formula()
, а
вызываются ею по указателям, которые и записаны в той самой таблице.
const char *name; fmlfunc_t func; int nargs; int nresults;(последние два поля позволяют иметь централизованный контроль за stack underflow/overflow).
dblstk[]
и на dblidx
.
cda_exec_formula()
. Намного лучше иметь
нечто типа опкода "print", дабы его вставлять непосредственно в
формулы.
02.03.2006: ввел
OP_DEBUGP_I='D'|OP_imm
, и для него альтернативу
char *str
в excmd_content_t
.
В cda_register_formula()
строка
strdup()
'ится, чтоб можно было при желании делать free().
При исполнении учитывается FLAG_SKIP_COMMAND
.
13.03.2006: угу, а для комплекту вообще-то следует также иметь и "OP_DEBUGPDBL" -- чтоб уметь печатать числа, получающиеся в результате калькуляций.
03.12.2008: да, сделана такая фича -- для
краткости названа OP_DEBUGPV='V'
, есть и
_I
-форма. Самым сложным было именно выбрать опкод. Плюс,
там сейчас ЗАШИТ формат %.7f (а можно было б его указывать
как раз в обязательном _I
-параметре.). В остальном --
практически копия DEBUGP
.
13.12.2008: переделал по уму -- число для печати
теперь ВСЕГДА должно быть в стеке, а параметром команды
CMD_DEBUGPV_I()
указывается формат вывода.
03.12.2012: некоторое "неудобство" -- что в форматной строке для DEBUGPV_I нельзя указывать ничего, кроме собственно формата; а возникает желание и какой-нить комментарий туда прилепливать (ну да, его можно и обычным DEBUGP_I выдать, но будет на отдельной строке).
...это уже скорее вопрос к общей системе форматов (misc_printffmt, dpyfmt) -- в cda лишь используется та инфраструктура.
11.04.2006: сделано, практически по образу и
подобию cm5307_drv.c, с использованием переменной
was_suffering
.
Можно бы, конечно, и покрасивше -- чтобы при ПЕРВОМ установлении соединения оно писало "successfully connected to SRVRSPEC", а при ВОССТАНОВЛЕНИЯХ -- "restored connection to SRVRSPEC"; для этого лишь пришлось бы завести еще один флажок -- "was_connected", который уставлять в 0 изначально, и в 1 при соединении (и никогда не сбрасывать)... Но сие уже слабоважно -- так что "done".
16.05.2006: первоначальное происхождение проблемы -- карповский блок задержек, который не позволяет читать записанные значения. "А вот если бы можно было в cda-формуле посмотреть на флаги, то, увидев CXRF_NO_Q, можно б было делать инициализацию."
Сама-то потребность linbpm'а бредовая -- нефиг программе без спросу трогать то, что стоит в устройстве; и нечего таким способом пытаться решить проблему, заложенную дятлом Карповым в устройство. Но -- а в других приложениях подобное не потребуется ли? И как тогда это реализовывать?
Ежели что -- понадобилось бы две вещи. Во-первых, возможность
считать флаги канала; это просто операция, аналогичная
OP_GETP_I
/OP_GETP_BYNAME_I
и реализуемая
"рядышком" и просто. Во-вторых -- желательны бы операции для
манипуляций с битовыми масками; хотя, в принципе, в случае карпиных
блоков все достигается при помощи остатка от деления и вычитания.
А вообще, по здравому размышлению -- не должно это использоваться НИГДЕ: прикладная программа должна просто работать с данными, а их флаги -- это уж для cda, Cdr и им подобных. И нечего нарушать это разделение; и даже более того -- попытки/желание подобных нарушений являются признаком, что что-то не слава богу.
Так что -- "withdrawn".
27.01.2007: так, для полноты сведений о вопросе: а вот ДОБАВЛЯТЬ флаги при отправке их "наверх" -- Cdr/Chl'ю -- формулы очень даже могут, для этого есть CALCERR.
18.05.2006: вылезло это ради программы диагностики блока frolov_D16, но реально задача достаточно общая.
Смысл таков: у нас есть энное количество неких одинаковых устройств, которые многоканальны, и имеют хитрую структуру (как те же пирометр и КШД485 -- но тех просто было по штучке). Т.е., реально это -- именно ОДНО устройство (угу, тут-то EPICS'ная модель с полями рекорда подошла бы хорошо...). И хочеться иметь ОДНУ программу диагностики, которую можно ткнуть в ЛЮБОЙ из этих блоков.
Получаем -- что вроде как имеем элемент, являющийся отражением
блока, но в группировке-то указываются АБСОЛЮТНЫЕ номера (да хоть
имена!) каналов, и сдвинуть их на сколько-то нету никакой разумной
возможности (ага, разве что бедной программе пробегать по всей
группировке, ныряя во все elemnet_t
'ы и
logchannet_t
'ы, и еще лазя в формулы).
А самым наипростейшим решением проблемы было бы иметь в cda
per-serverinfo_t
-параметр "база", который прибавлять к
physchan
в cda_add_physchan()
. И прибавлять
-- уже при записи в si->physlist[]
, чтоб с phprops
все автоматом работало.
Хотя, реально эта задача высвечивает проблему некоторой неадекватности в принятой у нас схеме адресации (хоть по номерам, хоть по именам). Адресацию надо как-то импрувить.
(Или -- верный ответ именно в EPICS'ной схеме адресации, с полями одного "большого" канала? Точнее -- некоей "одной сущности"?)
21.01.2007: вообще-то ответ -- в использовании БД и службы имен!
А именно:
12.07.2009: потребность по-прежнему есть, службы имен -- по-прежнему нет.
Идея: расширить "указание сервера" для cda до формата "host:N{,OPT}", где возможные на сейчас OPT -- chanofs=X и bigcofs=Y.
Сие позволит указывать такие смещения максимально простым и удобным образом, прямо в командной строке программы. (Да, решение ограниченное -- не проканает, если нужно в одной программе обращаться к НЕСКОЛЬКИМ подобным блокам (да и в формулах нельзя будет указывать ссылки на сервера с параметрами (и нехрен!)); но для >90% случаев (диагностические программы для конкретных блоков) -- полностью решит проблему.)
Итак -- делаем:
serverinfo_t
добавлены поля chan_base
и bigc_base
. (2-е вряд ли нужно -- только для
полноты.)
si->physlist[]
и в
si->bigcinfo[].bigc_n
(ну и при поиске по ним -- не
аллокирован ли уже этот канал). При поиске же по physinfo -- оно ищет
по "несмещённым" номерам.
srvrspec
'е
',', и при наличии -- туда пишется '\0' (чтоб
освободить ссылку на сервер от мишуры) и делается парсинг
(chan_base, bigc_base; [0...2^30]).
Разделители -- также запятые, а символом присваивания взято
':' -- дабы не конфликтовать с парсером
SimpleClients.lst, в точности как опции в
ChlRunApp()
.
cda_new_server()
формирует _cda_lasterr_str
, так что:
ChlRunApp()
/ChlSetServer()
потребуется это
учесть.
Пока же он для совместимости возвращает errno=EINVAL.
Проверено -- работает, так что раздел наконец-то "done".
14.06.2006: нужно это будет для работы через файрволл: если в "БД" указана ссылка на некий сервер, то ссылка-то эта наверняка будет локальной, предназначенной для пультовой сети, а из-за файрволла надо будет, например, транслировать "linac1:59" в "localhost:-8159".
Как бы это можно было делать?
В принципе, таковая таблица может вычитываться из сервера по первому соединению -- это проходит квалификацию как "информация БД". Вопрос лишь, а КАК сервер должен определить, что вот этому клиенту -- дать такую-то таблицу трансляции, а локальным -- не надо.
Другое дело, что эти макрорасширения -- дело дале-е-екого будущего, а вот таблицу трансляции легко изготовить хоть сейчас.
Так что, этот пункт вкупе с предыдущим -- "базовым сдвигом" -- складывается в некую общую картину: этакая "автоматическая трансформация" ссылок в cda...
21.01.2007: да-да-да, здесь тоже, как и в предыдущем пункте, ответ ТОЧНО в clientside/runtime-макрорасширений вкупе с БД!
04.01.2010: тут, как и в предыдущем пункте -- потребность по-прежнему есть (и даже усилилась!), а макрорасширений и службы имён -- по-прежнему нет.
Так что вводим трансляцию по идее за 21-11-2009 (пункт про "defserver" из Cdr): при создании нового сервера ищется переменная окружения с именем CX_TRANSLATE_nnn, где nnn -- указанный spec. Пара замечаний:
cda_add_auxsid()
'е как можно раньше
-- чтобы сравнивать со списком уже приконнекченных серверов. С другой
же, physinfo-то в программе имеется с оригинальными, НЕтранслированными
именами, так что надо в cda_new_server()
иметь и
оригинальное имя. Так что там введена уродливая схема -- транслируется
во 2-ю переменную, tr_auxsid
, и сверка со
списком-приконнекченных ведется по ней (если же трансляций нет, то в
делается tr_auxsid=auxsid
), а собственно
cda_new_server()
'у передаётся обычный auxsid
.
!isalnum()
заменяются на '_'. А всему остальному
делается toupper()
.
За саму трансляцию отвечает функциечка
find_srvrspec_tr()
.
Еще раз замечание: фичи "базовый сдвиг" и "трансляция имён серверов" не очень хорошо стыкуются, но это и не требуется. В основном потому, что обе они -- временный хак (и дико уродливый при том!).
Поскольку проверено и работает (пусть и с ограничением -- в
трансляции нельзя указывать base_*, по
global_physinfo_db[]
не найдёт), то наконец-то "done".
CMD_SETLCLREG
, без _I()
. Резоном было,
видимо, то, что переменные (локальные регистры), мол, должны
адресоваться "статически", а динамическому выбору номера там делать
нечего.
Это неправильно -- так мы лишаемся возможности делать "массивы", индексируемые вычисляемым формулой значением (это понадобилось в ringcamsel'е для уставки значений регистров концевиков, в зависимости от выбранной в данный момент камеры).
Надо сделать!
03.01.2007: а вот и хрен-то сделаешь!!!
По одной простой причине (и никаких "видимо было", "мол, должны" и
т.д.): флажок "локальный регистр" (OP_ARG_REG_TYPE_LCL
)
указывается вместе с номером регистра. Упс...
Так что единственный вариант -- это прямо в формуле прибавлять этот
флажок к номеру отдельной CMD_ADD_I(OP_ARG_REG_TYPE_LCL)
.
Так в ringcamsel'е и сделал.
Выводы на будущее (в CXv4):
Кстати, реально-то у нас опкод занимает 4 байта, а не 1 char. Вот и сделать там оставшиеся 3 байта "модификаторами"/"дополнительными параметрами".
Резон -- TMP-регистры адресовать динамически просто нет смысла, ибо они именно временные; переменные же более долгоживущи, так что имеет смысл пользоваться ими как массивами.
(А то, как есть -- исторически сложилось: регистры-то вводились именно как врЕменные переменные, и лишь потом (в июне-июле 2003г.) были добавлены локальные регистры/переменные. И там даже есть комментарий -- "Такой подход удобен, но имеет недостаток: становится затруднительно обращение с регистрами как с массивом -- поскольку придется прибавлять к индексу еще и селектор".)
И, кстати, поскольку в самой формуле объявлять нельзя -- ибо локальные переменные делятся между всеми формулами -- стОит объявлять все переменные сразу для всей группировки. Причем и массивы, и просто переменные.
NewDataProc()
такова:
Т.е., очередной запрос серверу отправляется ПОСЛЕ выполнения всех действий клиентами. А они-то могут быть очень долгими!DecodeData(si); NotifyClients(si, CDA_R_MAINDATA); RequestData(si);
Следствие -- надо дать клиентам возможность
сообщать cda "ты давай посылай следующий запрос, а я пока своими
делами займусь". Например, cda_continue()
.
02.03.2007: да, функцию изготовил -- она стоИт
после cda_{new,run,hlt,del}_server(), и просто вызывает
RequestData(si)
; поскольку оная проверяет, не послан ли
уже запрос, то повторный ее вызов абсолютно безопасен.
Клиентам надлежит вызывать эту функцию сразу ПОСЛЕ вычитывания данных -- cda_get_bigc*() -- и ПЕРЕД всякой своей длительной обработкой/отображением. В ndbp_{image,adc200}_elemplugin.c так и сделано.
31.12.2007: поскольку это уже давным-давно успешно работает, то -- "done".
CMD_{GET,SET}P_BYNAME_I()
номер канала
указывается вместе с вервером СТРОКОЙ. В исходниках-то он частенько
вычисляется, типа "DEV_BASE+chan", а такие формулы в строку не
загонишь...
Так что надо бы уметь указывать ДВУМЯ параметрами --
(char *server, int chan)
. Вопрос лишь, как водится, в
наполнении excmd_content_t
(введем еще структурку? :) и в
символе под опкод.
Потребность совсем не столь бредовая, как к rflags за 16-05-2006, зато по технологии реализации -- намного фиговее, ибо требуемое значение уже не тут же, в cda, а уровнем ВЫШЕ...
19.04.2007: свои-то диапазоны еще можно было бы
как-то заимевать -- например, в том же стиле, как это делается в
EPICS'ных calc expressions, через несколько "дополнительных" переменных
(хоть и лишнее расширение API, но реализация тривиальна -- в
OP_ARG_REG_TYPE_MASK
свободны еще 254 типа переменных). А
вот ЧУЖИЕ... Х.з...
Фиг-знает-каким образом сделать все атрибуты всех ручек адресуемыми? Т.е. -- чтобы Cdr предоставлял cda некий accessor к данным (в стиле плагинов)? И адресация -- по имени, да? (Иначе, если заводить handle'ы, возникнет проблема "курицы и яйца" при обращении формулой некоей ручки к атрибутам позже-расположенной ручки.)
27.06.2007: в CXv4 для обращения к "своим" параметрам сделан более общий механизм -- параметры, и диапазоны также отнесены к параметрам, доступным из формул.
А вот обращение к ЧУЖИМ параметрам -- это уже в корне неправильно, и может быть только следствием misdesign'а.
Так что тут -- "obsolete".
27.06.2007: да, видимо, надо завести некий опкод, который бы складывал в стек значение флажка OTHEROP -- 0.0/1.0.
Только вопрос: а с КАКИХ флагов она должна брать этот бит -- с
кумулятивных, или с флагов от последнего канала? Лучше, конечно, от
последнего -- они и так уже имеются в phys_rflags
, только
надобно ее инициализировать нулем.
Чуть позже: сделал, было бы о чем говорить.
18.04.2008: так с тех пор нигде и не воспользовался, но все равно давно пора -- "done".
cda_setlclreg()
и cda_getlclreg()
, чтобы
не-chlclients могли бы прямо из своего кода иметь доступ к значениям
регистров, НЕ выпендриваясь с имением/ведением для этого "формул".
Конкретные потребности:
Это явно надо делать локальными регистрами -- чтобы можно было отображать их на панели программы (и/или для модификации "на лету").
19.04.2008: собственно cda_-функции изготовил -- там делов-то. Они возвращают 0 при успехе, -1 при ошибке (например, плохой номер регистра), и +1 при чтении неинициализированного регистра.
Другой вопрос, что информация о локальных регистрах скрыта внутри
Chl'я, так что программам нужен API-"переходник", по аналогии с
ChlGetChanVal()
/ChlSetChanVal()
. Так что --
сделаны, ChlSetLclReg()
и ChlGetLclReg()
.
Теперь осталось эту шоблу только проверить -- сие будет скоро.
06.05.2008: да, проверил на тантальной программе -- работает. "done".
CMD_UPDATE
).
@пляж, прогулка А лучше -- чтоб программа могла зарегистрировать в cda свою функцию-callback, которая бы вызывалась при записи в локальные регистры.
Недостатки/ограниченность этого решения --
cda_execformula()
, так что кабы не возникло
где проблем с реентрантностью.
cda_execformula()
нет понятий "окно",
"приложение", или прочий контекст, а только localreginfo (которое лишь
в будущем планируется сделать закрытым типом/"контекстом" -- см.
рассуждения в разделе по OP_COMPILE_I за 01-02-2007), то -- никакой
возможности сосуществования нескольких независимых иерархий не будет.
Но оно совсем и не надо -- ибо сосуществование осмысленно только в
мегаклиентах типа cx-starter'а.
P.S. Да, в CXv4 такое не потребуется -- поскольку там предусмотрены разные типы каналов, в том числе и "локальные" (cda_d_local.c?), которые как раз и будут использоваться (вместо локальных регистров) для связи не-тривиальных программ с интерфейсными ручками.
01.05.2008: да, сделал -- такой "callback"
уставляется функцией cda_set_lclregchg_cb()
, а вызывается
он только при НОРМАЛЬНОЙ записи (инициализация игнорируется) в
ЛОКАЛЬНЫЕ регистры (врЕменные также игнорируются). Естественно,
программная запись через cda_setlclreg()
к вызову не
приводит.
Теперь осталось также заиспользовать :-).
06.05.2008: использовать пока не стал -- в тантальной программе схалтурил и сделал по варианту 2, но проверил. Работает, "done".
cda_strserverstatus_short()
, возвращающая по коду статуса
сервера краткое описание этого статуса. Реально -- нафиг не нужно,
сделал исключительно для Роговского.
(Кстати, это продолжается ОЧЕНЬ давно, практически с самого начала -- и я ни разу не обращал на это внимания, пока сейчас Роговский не спросил?!)
12.02.2009: полез разбираться. Утилиты на обычном cxlib -- из programs/utils/ -- пишут именно "Invalid channel number".
Заглянул в cda.c -- первое же впечатление, что это явно как-то связано с нашей замутной архитектурой "suffering". Как минимум -- она преизряднейше всё запутывает.
Но вот порывшись основательнее, обнаружил, что реальные виновники И cxlib, И cda. И проблема эта явно существовала вообще с самого появления cda и нотификаторов; хотя непосредственным виновником является cxlib.
Суть дела: cxlib вызывает нотификатор, НЕ позаботившись уставить ему
errno=cp->errcode
. А cda'шная
NotificationProc()
-- точнее, её подручная
TakeCareOfSuffering()
нагло лезет в errno
, не
думая, что вообще-то результат попадет туда только после СИНХРОННОГО
завершения вызова -- т.е., после вызова cx_result()
. А
так -- в errno
откуда-то лежали числа то -24, то -12 (это
как раз и есть "Request was successfully sent").
(В данном случае самое "забавное" -- что ошибка CEINVCHAN
вообще была как бы виртуальной, она существовала мимо errno,
передаваясь по цепочке внутренних функций cxlib'а напрямую в
cp->errcode.)
Выводы расследования:
И явно наипростейшим на настоящий момент является сделать
errno=cp->errcode
в MarkAsClosed()
. Что и
было проделано, после чего диагностика стала осмысленной.
phys_r
и phys_d
канала. Нужно это
для weldclient'а, а точнее -- для работы с таблицами в козачиных ЦАПах
(ведь там-то cda нам не помощник!). 25.06.2013: а
почему, собственно, "не помощник"?! Надо делать, чтоб и козачиности
тоже работали через cda! Тем более, что
weldclient_process_knobplugin.c, появившийся 23-11-2011 (из
fastadc*?), изначально на это и рассчитывался.
21.06.2009: что ж поделаешь -- сделал
cda_getphyschan_rd()
, практически копированием с
cda_getphyschan_q()
.
Уродство это, конечно -- ладно еще, что мы тем самым лезем в компетенцию cda, главное-то, что со стороны клиента махинации с доставанием параметров обычных каналов для использования в табличных выглядят кривыми шаманскими плясками!
В принципе-то они должны бы делаться "функциями", но той инфраструктуры пока и близко нет, а экспонента и логарифм -- скорее, даже не функции, а математические операции.
25.06.2009: так что -- введены пара опкодов,
рядышком с PWR'ом, OP_EXP='e'
и OP_LOG='E'
('l' и 'g' заняты под LABEL и GOTO, а 'n' --
под POLY). Ну и обработка сделана.
Видимо, надо перед переводом в int32 ставить round()
,
но где это еще может вылезти?
22.12.2009: да, вставил. Имевшаяся проблема с 255./100 исчезла.
18.08.2010: кстати -- давным-давно пора было сказать "done".
03.12.2012: да, "вылезло" -- в ringcamsel. Там при вычислении значений битиков делалось просто деление и MOD_I(2), полагаясь на неявный trunc(). Сейчас добавлено CMD_TRUNC и проблема ушла. (Неясно только, почему она в subharmonic не вылезла.)
11.05.2010: задача состояла из 2 частей:
Для этого былой параметр-флаг is_write
переделан в
options
(аналогично Cdr'ову synthetic), и введены флажки
CDA_OPT_IS_WRITE
=1 (реализующий функции былого параметра)
и CDA_OPT_READONLY
=2.
(Да, комбинация флагов IS_WRITE плюс READONLY будет выглядеть диковато.)
Сие решилось просто -- перед проверкой "а находимся ли мы в пишущей
формуле, или был префикс разрешить-запись" в случае
CDA_OPT_READONLY
делается break
.
Теперь надо проверить.
03.06.2010: проверено, работает -- "done".
27.07.2010: идея проста:
CMD_SVFLAGS='['-64
(Ctrl+[) складывает
текущие флаги на верхушку стека.
CMD_LDFLAGS=']'-64
(Ctrl+]) считывает флаги
из стека, и ДОБАВЛЯЕТ (or'ом) их к текущим.
Естественно, _I
-версии отсутствуют по определению.
В общем -- сделано, хотя пока не используется.
Т.е., чтобы при выходе из программы число бы сохранялось, а при повторном запуске считывалось бы (у Гусева такое есть в программах тренировки клистронов).
Напрашивается такое решение: сделать опкоды "записать число с верхушки стека в указанный файл" и "прочитать число из указанного файла на верхушку стека". Соответственно --
29.07.2010: делаем... Конкретика:
OP_SAVEVAL_I='{'|OP_imm
,
OP_LOADVAL_I='}'|OP_imm
.
Вообще, конечно, уже реально кончаются символы...
Если будем делать такую фичу для настоящего формульного языка, парсимого-из-строк, то надо будет добавить параметр "формат", и тогда проблема исчезнет.
mkstemp()
создаётся временный файл;
rename()
временного файла в запрошенное
имя; при ошибке -- unlink()
.
close()
.
Следствия такого подхода:
mkstemp()
'а просто
"00savevalXXXXXX". По-хорошему, конечно, надо б было брать
dirname(str)
, но -- это неоправданно сложно для такой
экзотической функции. Тем более, что вообще-то chlclients всё равно
должны бы держать свои рабочие файлы в текущей директории.
31.07.2010: да, проверил -- работает. И ошибки в нестандартных ситуациях выдаёт правильно.
Единственная "проблема" -- что mkstemp()
создаёт файл с
правами "-rw-------", а не с обычными "-rw-r--r--", но на это можно
забить. (Поскольку вызов getumask()
не стандартизован,
а является Linux-specific (или Hurd-specific?) и реализован с
непонятной версии libc (>2.2.5?), то делать
fchmod(fd,0444&getumask())
как-то неохота.)
31.07.2010: ёлки-палки -- а ведь в cx-starter'е-то оно НИЧЕГО НИКУДА НЕ ДОЛЖНО ПИСАТЬ!!!
Так что -- просто сразу после вычитывания параметров из стека
вставлена проверка "в случае CDA_OPT_READONLY
делается
break
".
А ВЫЧИТЫВАНИЕ из файла -- на здоровье, хуже от этого не будет. Причём, учитывая, что вся фича вводилась в первую очередь ради счётчиков, в правильно написанных группировках/формулах вообще отсутствие файла НИКАК не должно влиять на "цветность" программы.
06.08.2010: да, фича успешно используется в liucc/elem_dl200me для подсчёта количества выстрелов.
11.08.2010: а теперь надо делать вторую часть -- подсчет времени работы блоков запуска. Считается, что блок запуска работает, если канал PANOV_UBS_CHAN_STAT_STn_HEATIN находится в состоянии 1.
16.08.2010: да, сделал. Та еще "радость" была, конечно, но сделал -- так что и для подобных вещей эта фича тоже годится.
cda_new_server()
ВСЕГДА делается увеличение servers[]
на 1 элемент -- и
НИКОГДА не делается поиск свободной ячейки. Это следствие
недееспособности cda_del_server()
-- реально никакая
программа никогда не тасует набор серверов, а только добавляет их.
09.06.2014: уже давно не так -- со времени перехода на v4-alike-схему со SLOTARRAY в 2012-м.
20.12.2010@Снежинск-каземат-11: Так что ввёл:
OP_ROUND='1'
(мнемоника -- до ближайшего кратного
1).
OP_TRUNC='0'
(мнемоника -- округление в сторону 0).
_I
и CMD_
.
OP_ABS_I
и
CMD_ABS_I()
.
И соответствующую обработку.
Теперь бы еще проверить.
03.12.2012: TRUNC проверена (на ringcamsel'е) -- работает.
CMD_GETP_BYNAME_I()
.
03.02.2011@Снежинск-каземат-11: сначала озадачило то, что оно так на ДОПОЛНИТЕЛЬНЫЕ сервера вякает, хотя на неналичие ОСНОВНОГО -- диагностика нормальная.
Оказалось, что диагностику на облом основного -- от ChlSetServer()
-- генерит сам ChlRunApp()
(сам беря
cx_strerror(errno)
), а вовсе не cda.
Посему аналогичная генерация диагностики по результату
cda_add_auxsid()
'а вставлена в оба места его использования:
cda_register_formula()
, блок BYNAME_I/ADDSERVER_I (это
решило конкретную вызвавшую проблему).
CdrCvtElemnet2Eleminfo()
, блок, отвечающий за
"defserver=".
Вывод: тут у нас явнейший случай криво-разделения
ответственности, вызывающий необходимость "протыкания" уровней.
По-хорошему, ВСЕ точки рождения новых соединений должны делаться на одном
уровне библиотеки (или, на худой конец, чётко координированно между
сотрудничающими уровнями), а не как сейчас. (Да, например, в
CdrRealizeSubsystem()
, безо всяких там Chl'ей.)
cda_physinfodb_rec_t
относится к компетенции cda, хотя её место
-- явно в cxdata.h, рядом с physprops_t
?
15.02.2011@Снежинск-каземат-11: это явно просто ошибка, сделанная в своё время (2003?) при разделении первоначально единой oldChl на cxdata, cda, Chl, etc. конкретно данная структура (внедрённая 23-07-2003) была помещена не в тот файл.
Так что -- перетащил определение в cxdata.h под названием
physinfodb_rec_t
, подправил все файлы, которых это касалось
(включая mkphysdb.sh, бывший аж от 26-09-2004).
cda_sidof_physchan()
, отдающая sid указанного physhandle
(для нужд liu/plot/fastadc_knobplugin.c -- чтоб можно было
делать еще ссылки на каналы, соседние с указанным в ki "базовым").
CENOHOST
также во ВРЕМЕННУЮ проблему, НЕ препятствующую
созданию соединения.
Резон: оно, конечно, действительно есть серьёзная и, скорее всего, НЕвременная ошибка, но -- ведь иногда надо запускать программы для одного места (Снежинска) в другом (ИЯФ), где нету того набора хостов. И каждый раз ваять длинный список трансляции -- дюже муторно.
02.08.2011: делаем:
cda_new_server()
оно добавлено к списку
"исключений", при которых просто запланировывается повтор.
Также добавлено в
4cx/.../cda_d_cx.c::IsATemporaryCxError()
.
ScheduleReconnect()
сделано, что при
(ec=ERRNO)==CENOHOST оно увеличивает время до повтора в 10 раз. Причём
-- "творчески", так, чтоб оно не превысило 2 миллиарда микросекунд.
И в 4cx/.../cda_d_cx.c::FailureProc()
.
Работает, "done".
По-хорошему -- надо б уметь такое поведение включать/выключать per-server. Например, при помощи параметров, что идут в srvrspec после ',': persistent_data -- нынешнее поведение, shy_data -- НЕнастойчивость (т.е., забытие по соединению).
26.10.2011@Снежинск-каземат-11: делаем:
serverinfo_t.shy_data
, ...
ConnectProc()
при включенном shy_data для всех каналов
делается .physmodified=MODIFIED_NOT
.
Проверить бы...
27.10.2011@Снежинск-каземат-11: проверил (на медленном dl200me@rack0:44) -- работает.
10.02.2012@Снежинск-каземат-11: желание такое возникло при организации подсчёта "времени наработки" накала катода на ЛИУ. В момент реализации оного как-то получилось, что канал состояния горел "Вкл", но был посиневшим. Т.е., по-хорошему, программа должна иметь возможность в такой ситуации решать по своему усмотрению -- например, что НЕ засчитывать это во время наработки.
Конкретный побудительный мотив -- чтоб liucc могла протоколировать моменты срабатывания/запусков/выстрелов, и лучше б они попадали в обычный лог, куда и всякие загрузки режимов.
10.02.2012@Снежинск-каземат-11: поскольку файлом логгинга
заведует Chl -- ChlRecordEvent()
, а формулами -- cda, то,
очевидно, понадобится ввести еще какое-то "вакантное место"/callback, типа
lclregchg_cb, дабы Chl мог туда регистрироваться.
А сама-то реализация тривиальна -- сложнее будет название выбрать (LOG уже занято логарифмом; DO_LOG? LOGLINE? LOG_STRING?) да очередной опкод подобрать :-).
17.02.2012: а ведь реальная проблема есть --
ChlRecordEvent()
'у нужен КОНТЕКСТ --
XhWindow
, а cda этой информацией ну никак не владеет. Так
что придётся халтурить -- вводить СТАТИЧЕСКУЮ переменную. Другое дело,
что по факту логгинг -- работа Chl_app.c, и все
заинтересованные в оном программы в конечном итоге используют
ChlRunApp()
-- вот туда этот функционал и забабахаем.
Делаем.
OP_LOGSTR_I
='v'|OP_imm ('v' --
потому, что 'V' занято под DEBIGPV).
cda_set_strlogger()
.
chl_hack_strlogger()
, уставляется в
ChlRunApp()
, вместе с сохранением ссылки на окно в
chl_global_hack_win
, сразу после
ChlSetSysname()
.
Кстати, а в v4 ЯВНЫМ образом никакого контекста не предусмотрено, только privptr для sid'ова evproc'а. А надо бы! 18.07.2014: уже предусмотрены, еще с весны.
24.02.2012: насчёт v2: сейчас там действительно никакого контекста нет, но в нынешнем виде оно не шибко работоспособно. В конечном же варианте там будет как минимум контекст ради регистров (наследник нынешнего localreginfo -- см. от 01-02-2007); вот тут можно как-нибудь подмешивать и "ссылки наверх".
24.02.2012:
10.02.2012@Снежинск-каземат-11: угу, посмотрел -- оно прямо в самом начале, в проверке на OP_RET, напрямую проверяет, что если в стеке что-то есть -- вычитывает, а иначе молча берет NAN. Но ведь в read-формуле надо еще и ругнуться!
24.02.2012: да, поменял тот код с a?b:c (идущий, видимо, еще со времён ucam, и потом дополнительно напичканный условиями на BREAK и RETSEP) на нормальный if(), в котором при пустом стеке в не-write-формуле делается ругательство на stderr; "done".
18.07.2014: вылез неприятный ляп из-за криво организованного условия:
FLAG_SKIP_COMMAND
, хотя надо б в любом случае выборку из
стека делать, а уж потом проверять "исполнять ли команду".
cda_status_of()
, волюнтаристски заложенный
22-07-2003 (см. bigfile.html): закомментировано приравнивание
!is_running к DISCONNECTED, так что теперь она по нему стандартно
смотрит время последнего прихода данных и возвращает NORMAL/FROZEN.
31.08.2012@Снежинск-каземат-11: причина оказалась простой: в какой-то момент юзером нажималась кнопка ([Сброс] блокировки ВИПа), вызывавшая REFRESH -- а при этом производится принудительная обработка всего дерева, с игнорированием conns_u. Вот оно и делало процессинг со всеми нулями (значение, флаги, возраст), что приводило к красноте.
cda_add_physchan()
сделано
ci->rflags=CXCF_FLAG_DEFUNCT
вместо =0.
ci->phystag
(=si->phystags[chanofs], =MAX_TAG_T), а то в knobprops показывался age=0.
physraw
и physrflags
из
si->{physcodes,physrflags}[chanofs]
.
cda_new_server()
от параметра rcn_on_ifail
,
полагая его всегда ==1 (как реально везде и есть). Сделано быстро и легко.
19.10.2012@Снежинск-каземат-11: все изменения идут в "#if
USE_NCXLIB
". Собственно:
serverinfo_t
добавлена троица trec'ов.
servers[]
--
поскольку trec'и должны располагаться по фиксированным (в момент
enqueue'нья) адресам, и никуда уже не бегать.
(Попробовал сначала запустить со старым вариантом, ага... Знатные получились глюки -- фиг поймёшь, почему segfault'ится или зависает, никакой gdb не помощник.)
Вроде работает.
08.11.2012: да, уж точно работает, "done".
cda_localreginfo_t
.
Единственная гипотеза -- глюк замечен в момент падения сервера по SIGSEGV в драйвере (а не просто по рестарту), т.е., возможно, при этом cxlib-соединение еще не было доустановлено: отработал лишь connect()/accept(), а handshake еще не завершился (при этом и в access-log ничего не успеет попасть).
Т.е., возможно, что cda при обрыве таких недо-завершившихся
соединений забывает сделать ScheduleReconnect()
(хотя
почему бы... cxlib/fdiolib забывают уведомить?).
15.09.2013: при простой пробе делать close() сразу после accept() никакого эффекта не наблюдено, всё отрабатывается корректно.
А тот косяк 12-09-2013 в ringmag@linac3 был весьма странным: судя по cx-starter.log, 2013-09-12-08:30:34 программа получила обрыв ("socket is closed by server"), но обратно подконнектилась почему-то только 2013-09-12-10:58:15, хотя куча других программ это делали и раньше, вплоть до 2013-09-12-09:15:25. Но ringmag такой был не один, еще ringcor23 плюс ringmag@viper. А аж в 10:58 -- потому, что с 09:15 сервер был незапущен.
Т.е., почему-то в нескольких случаях (одновременно!!!) cda сделала о-о-очень долгую паузу; сколько именно неясно, но заведомо больше получаса. Это не 7200 ли секунд?
17.09.2013: возможно, это всё же какой-то глюк ядра. На xcandac16@viper->ring1 была наблюдена странная картина: софт увидел облом прошлого соединения, запустил новое, и -- ничего. А вот netstat, запущенный на обоих хостах, показал интересную картину: viper считает, что xcandac16 имеет ESTABLISHED-соединение, а на ring1 такового нету (с этим номером порта -- 192.168.171.171:46521; фильтрованная выдача netstat'а тут в тексте ниже).
И, что интересно:
2013-09-17-16:00:46 xcandac16: cda: NotificationProc("ring1:32"): Connection was closed on error: ... 2013-09-17-16:04:57 xcanadc40: cda: connected to "ring1:32" 2013-09-17-18:02:36 xcandac16: cda: connected to "ring1:32"
Т.е., как раз примерно через 2часа=7200секунд (тут вопрос, с какого момента начинать отсчёт -- по средней строчке видно, что сервер запущен был в 04минуты, но восстановилось соединение в 02...).
Чем такое поведение определяется -- хбз, как на него можно повлиять -- тоже. Возможно, просто особенность/баг 2.4.18@RH-7.3.
Единственное что -- можно с КЛИЕНТСКОЙ стороны вводить таймаут (минута? пять?) на недо-установленные соединения, и, например, рвать их. А то ведь application-ping на них не пытается работать, а тут даже его бы хватило -- система б попыталась послать пакет и обнаружила б закрытость сокета. Кстати, идея касается ВСЕХ ветвей -- в т.ч. контроллерной.
18.09.2013: да, внедряем таймауты на клиентской стороне, длиной в 1 минуту; подробности см. в разделе по cxlib. (И, за компанию, в remdrv.)Часом позже: нифига, в remdrv не надо.
"Та" ситуация более повториться не должна (а просто проверить на поведение "в ней" затруднительно -- фиг ведь теперь заметишь, с минутным-то таймаутом), так что считаем за "done".
01.10.2013: всё-таки какие-то странности есть, причём со свежей версией всего (на пульту только-только обновлённое). Почему-то после рестарта сервера linac1:1 несколько клиентов так и не пере-приконнектились. Причём как с linac3, так и с самого linac1 -- 2 подсистемы v5adc200_* в cx-starter'е, притом, что соседние трое аналогичных прекрасно отработали; и через 2 часа ничего не изменилось...
Куда рыть -- неясно. Но глюки, похоже, происходят достаточно часто, так что с нынешним пультом рано или поздно отловятся.
02.10.2013: чудеса продолжаются: вчера запущенный v5adc200_grp2@viper->linac1:1 НЕ переконнектилась; из 2 соединений (bigc,chan) живо лишь одно: сообщения "will reconnect" есть от обоих, а "connected" -- только от одного.
Кстати, весьма неудобно, что оно НЕ указывает в ругательствах sid -- надо б добавить. И cxlib'а тоже касается (частично -- там -1 вместо cd), хоть там менее актуально.
Что странно:
Вывод по разблюдовке: [0] принципиально не занимается, [3] появился из-за ALLOC_INC=2 (при затребовании второго соединения аллокировалась вторая пара); а [1] и [2] реально рабочие, но [1] достался chan-соединению и потом ожил, а [2] -- bigc и почему-то после облома более не задействовалось. Косяк, видимо, у cda?
Не в SLOTARRAY ли типа GROWFIXELEM проблема (тут -- ЕДИНСТВЕННАЯ
точка его использования, даже в 4cx не оно)? Или -- верно ли определён
тип для servers_list
? А то как-то странно gdb показывает
по print'у -- сразу содержимое, а указатели почему-то нет.
24.11.2013: безотносительно возможных странностей
с cda и показом в gdb: в cxlib'е имелся глючок, внесённый 18-09-2013 --
сюрприз!!! -- как раз во время борьбы с титульной проблемой, при
добавлении WipeUnconnectedTimed()
. Суть -- там НЕ
сбрасывалось clp_tid=-1
при снятии таймаута по ненужности.
В результате мог бы быть такой сценарий:
clp_tid
он оставался.
sq_deq_tout()
еще раз.
Диаграмма исполнения такое вроде бы не подтверждает -- sl_deq_tout() вызывается ДО CallNotifier()'а. НО: такое поведение наблюдалось практически всегда в программах с несколькими соединениями (пример -- fastadcclient-based), типа того v5adc200_grp2. А там как раз глюк cxlib'а с одним соединением мог подгадить второму.
...а не такая же ли причина у аналогичной проблемы с не-реконнектами у remdrv?
А в сервере-то (cxsd_fe_cxv2.c) аналогичный ляп был!
11.03.2014: сделано.
OP_BOOLEANIZE
='b',
OP_BOOL_OR
='O',
OP_BOOL_AND
='&'.
12.03.2014: проверено, работает.
17.03.2014: типа протокола:
Но при этом становятся недоступными некоторые служебные внутренности cda.c. Поэтому:
config_debug_*
переименованы в
_cda_debug_*
и опубликованы в cda_internals.h,
...
reporterror()
, превратившейся в
_cda_reporterror()
.
CheckSid()
используется в начале
cda_register_formula()
, то он переименован в
_CdaCheckSid()
и аналогично опубликован.
Вроде всё работает, так что "done".
connect()
в cxlib'е
так, чтобы можно было в режиме SELECT перевести сокет в неблокирующийся
режим, и пускай оно там в background'е коннектится.
Это требуется, чтобы а) обычные chlclients не подвисали на минуту,
пока connect()
не соизволит обломиться, и б) чтобы
chaincx_drv при коннекте/восстановлении не вешал бы весь сервер.
11.04.2006: примерно даже понятно, как именно сие
будет реализовываться: введением состояния CS_OPEN_CONNECT
между CS_OPEN_FRESH
и CS_OPEN_ACCESS
, с
соответствующим обработчиком async_CS_OPEN_CONNECT()
. И
-- с точки зрения диаграммы соединения -- этот шаг будет отдельно
происходить всегда, но при уставленном modeisasync
(т.е.
-- libmode!=CXLIB_MODE_SELECT
) он будет производиться
прямо изнутри ConnectTo()
(оно дождется окончания
connect()
'а и само вызовет
async_CS_OPEN_CONNECT()
).
Так что -- в принципе все очень просто.
21.05.2007: поскольку выбран путь разделения cxlib на собственно "мозги" и "транспорт", то данный раздел становится "obsolete", ибо подход будет зело иным.
Но состояние CS_OPEN_CONNECT
-- понадобится, т.к. оно
отлично ложится на fdiolib'овскую модель. В [возможно когда нибудь
потребующемся] режиме завешивающего connect'а этот шаг будет, как и
предсказано выше, просто пропускаться.
В разных программах по-разному: cx-starter продолжил показывать зеленую лампочку, nipp/ipp -- две синих. Самое забавное, netstat показывает, что соединение вроде как есть (ESTABLISHED), но на том конце -- ничего нету.
Т.е., поведение ядра-то мы не поменяем (разве что включать
SO_KEEPALIVE
, что не есть особо хорошая мысль), но надо
постараться понять -- почему, например, cx-starter продолжал показывать
зеленую лампочку, в то время как никаких данных давным-давно не
приходило.
(Хотя, может быть, как раз и использовать SO_KEEPALIVE
-- ведь cda нормально восстановит соединение при первой же возможности,
так что проблем типа "просто временно отпала связь" быть не должно.)
04.01.2005: идея, как это можно надежно продиагностировать: оставить клиентскую машину без связи с серверной путем вытыкания кабеля. (Навеяно поведением istc/xmclients/vibro после новогодних праздников, когда связь с hedgehog'ом пропала без предупреждения, и показывалась просто синяя лампочка.)
20.01.2006: очередная причина поразбираться в вопросе -- при парковке linac1 все повторилось: почему-то продолжали висеть синие лампочки, а не красные.
Краткое резюме: через сколько-то минут почти повсюду лампочки
покраснели-таки -- помог введенный (в мае-2005, кажется?)
SO_KEEPALIVE
; остались же синими лишь те чугуевские,
которые были откомпилированы и собраны ДО того введения -- на них как
раз по-прежнему netstat показывал ESTABLISHED.
25.01.2006: стало почти СОВСЕМ все понятно -- нашел в init(8) строки:
И чуть ниже.... Note that init assumes that all these processes (and their descendants) remain in the same process group which init originally created for them. If any process changes its process group affiliation it will not receive these signals. Such processes need to be terminated separately.
WARNINGS Init assumes that processes and descendants of processes remain in the same process group which was originally cre- ated for them. If the processes change their group, init can't kill them and you may end up with two processes reading from one terminal line.
Так что все ясно -- ведь cxd-то уходит не то что "в другую группу", а в другую сессию, и уж shutdown-то никогда ему никаких сигналов не пошлет, и процесс просто прекратит свое существование в момент отключения/перезагрузки системы, прямо с "живыми" соединениями. Так что -- клиенты НИКОГДА не получат FIN, и будут думать, что соединение просто молчит.
А вот SO_KEEPALIVE
-то нас и выручает!
Так что, case closed, "done".
P.S. А вот почему, как написано выше, "cx-starter продолжал показывать
зеленую лампочку, в то время как никаких данных давным-давно не
приходило" -- загадка: казалось бы, KeepaliveProc()
должна была все осинить (и она там уже БЫЛА!).
11.04.2006: еще небольшая серия разбирательств:
Если все окей -- то получает ответ и успокаевается еще на tcp_keepalive_time секунд.
Если та машина как раз была перезагружена -- то она отвечает пакетом
RST ("не знаю я такого соединения"), что отдает наверх "ready for
read" и ECONNRESET
, и, в случае cda, соединение
устанавливается заново.
ETIMEDOUT
).
24.01.2007: но все-таки, эти два часа (7200 секунд) -- это ДО ТОГО ДОЛГО!!! Мдя...
18.08.2010: кстати -- а ведь проблему "хост паркуют/перегружают при живом cx-server'е, и клиенты остаются в состоянии SERVERSTATUS_FROZEN" можно решить очень просто: добавить в /etc/init.d/ файлик, который бы при переходе из runlevel'ов 345 делал бы
kill `cat /var/tmp/cxd-*.pid`
Тогда даже и в кнопках [shut-КРЕЙТ] не надо будет этот kill исполнять -- он вообще ВСЕГДА станет отрабатываться, даже по Alt+Ctrl+Del.
(По результатам того, что Вадим зачем-то перегружал комп, а в контроллере остались висеть драйвлеты, занимая CAN-адреса.)
04.10.2010: сделан cкрипт, помещаемый в
/etc/rc.d/init.d/cx-servers, выполняющий ровно то, что надо.
Кроме stop)
, он реализует также start)
-- ради
команды
touch /var/lock/subsys/cx-servers
-- иначе rc не считает нужным вызывать stop, поскольку считает подсистему
незапущенной.
В дереве скрипт хранится в src/doc/etc_init.d_cx-servers.
23.04.2011@Снежинск-каземат-11: на крейтах оказалось удобно
добавить в конец start)
'а строчку
TERM=linux /usr/bin/setterm -blank 0 </dev/tty1 >/dev/tty1 2>&1
Это чтобы экран консоли не гас, и при взглюках и прочих kernel panic'ах вываливуемую туда информацию можно б было прочитать. (Собственно, это просто надо было выполнить при старте крейта, и сей файл просто удачно попался под руку. Никакого глубокого смысла тут нет.)
MAXCONN
)... Увеличиваем до 50? (BTW,
sizeof(CxConnection)=204
.)
(Эх, не рассчитывал я при разработке ее внутренностей (в 1996/1997г.?) на столь большой разброс в количестве потребных соединений. А вот в cda -- уже рассчитывал, потому там не статический массив, а растущий в динамической памяти.)
19.08.2010: угу, стало не хватать сейчас, на ЛИУ: при 4-стоечном режиме нужно 24 соединения на ADC200ME, 5 на liucc, и 2 на kuznitsa и kshd485 -- итого как раз 31. При 8-стоечном режиме прибавится еще 24 АЦП и 4 сервера в основную программу (хотя, надеюсь, к тому времени АЦП переедут из отдельных утилит в liucc).
Ладно -- увеличиваем MAXCONN до 100, и ставим "done".
reporterror()
'ах.
29.11.2004: сделал -- завел статический буфер
progname[40]
, в который в ConnectTo()
копируется
известное там имя -- будь то указанное пользователем argv0, либо из
program_invocation_short_name
. А reporterror()
,
соответственно, если в буфере непусто, префиксирует сообщения строкой
"program: ".
29.11.2004: кстати, наконец-то заметил, что в конце сообщения о "connection closed" отсутствовал '\n' -- вставил.
08.04.2005: (реально -- в каком-то из аэропортов, то ли в Толмачево 12.03.05, то ли в Шереметьево-1 01.04.05)
А может, ввести возможность использовать двойное двоеточие вместо восклицательного знака? Как в VMS'е -- где можно опционально указывать "node name".
Конечно, это будут некоторые проблемы с парсингом -- придется умничать, да и с адресами IPv6 тоже, но сие преодолимо.
28.04.2005: да, заменил strchr()
на
две последовательных попытки strstr()
-- "!" и
"::", с соответствующим уставлением "длины сепаратора".
Работает, проблем с "двойным смыслом двоеточия" не возникло.
Помечаем как "done".
10.07.2007: вот спрашивается -- а почему через "." не сделал, а? Вроде и все логично, и всему удовлетворяет, и нигде никак не мешается...
Понять бы еще, когда и почему была введена нотация через '!'? Ни в одном из bigfile*html это не отражено.
14.05.2008: собственно -- взял да добавил в
cx_parse_chanref()
поддержку '.' как разделителя.
В будущем надо потихоньку переходить на него повсеместно.
04.08.2008: ага, "добавил" -- как же! Оно бы глючило при попытке использовать FQDN, и пыталось бы считать все после первой '.' за канал.
Так что -- теперь проверка двухуровневая, сначала пытается искать ':', а уж если нашло, то ПОСЛЕ него -- '.'.
24.12.2009: за компанию -- переделал, что теперь в Chl_knobprops.c в поле "Source" в ссылке на канал также используется '.' вместо былого '!'.
Надо разделить cxlib на две части -- собственно "мозги", занимающиеся наполнением и расшифровкой пакетов, и транспорт, обеспечивающий connect и доставку пакетов в обоих направлениях.
Делать это можно по примеру связки fdiolib+cxscheduler, где "используемая" библиотека (там -- cxscheduler, тут -- cx-transport) является подменяемой, и конкретный ее вариант указывается при линковке.
Тогда можно для консольных утилит использовать вариант, основанный на SIGIO, а в остальных случаях -- на fdiolib'е, так что сразу после создания cxscheduler'а под Win32 и cxlib также автоматом станет Win32-compatible.
(И -- обязательно, обязательно, ОБЯЗАТЕЛЬНО надо перейти с
фиксированного connections[]
на растущий массив. И,
получше обдумать взаимодействие между "мозгами" и транспортом -- где
хранить чьи данные, не завести ли ОДИН массив вместо двух (в каждой
полубиблиотеке свой).)
Естественно, ЭТО -- уже под CXv4 :-).
28.05.2007: кстати, описанное выше решение является одним из предпоследних краеугольных камней, необходимых для "светлого будущего" и правильной, максимально портабельной и гибкой реализации, и (за счет возможности перехода на fdiolib) облегчает реализацию многопротокольности в cda. Остающиеся же камни/проблемы (здесь just for record):
Плюс -- отражение этих вещей в Drivers' API.
15.01.2009: еще вариант реализации "SIGIO mode" в CXv4'ном cxlib'е -- а что если сделать его не РЕЖИМОМ работы, а опциональным доп.модулем, который бы сам следил за SIGIO, и дергал бы callback'и (либо cxlib'овские, либо fdiolib'овские)?
Т.е., не "полнофункциональная" реализация транспорта, а именно "способ узнавания и готовности дескриптора", а собственно транспорт -- на некоем общем модуле.
11.05.2013: сделано по развитию этого проекта больше полугода назад, так что тут "done".
Причина очевидна -- это сокеты соединений с серверами, фиг знает почему остающиеся незакрытыми.
05.08.2010: гугление (да и чтение info libc) показало, что
When a file descriptor is allocated (as with `open' or `dup'), this bit is initially cleared on the new file descriptor, meaning that descriptor will survive into the new program after `exec'.
Немудрено, что у многосокетного cx-starter'а потомки имели столько открытых дескрипторов. Мудрено -- почему я этого багажа раньше не замечал.
Так что -- вставил fcntl(s,F_SETFD,1)
в
ConnectTo()
сразу после создания сокета. И в 4cx/'ный
cxlib/cx_client.c аналогично.
host
.
Причина -- чтоб диагностические сообщения об ошибках содержали кроме номера соединения также и человеко-читабельную ссылку на сервер (а то фиг разберешься).
19.04.2011@Снежинск-каземат-11: детали:
CxConnection.srvrspec[64]
.
ConnectTo()
.
reporterror()
, относящиеся к соединениям,
добавлена и выдача этого имени.
Формат теперь почти везде -- "%s[%d/\"%s\"]".
CopyReceivedBigcs()
при dataunits==0 радостно делила на 0
(в count=datasize/dataunits) -- позор! Исправлено, в стиле проверок в
ReturnBigc()
:
02.10.2012: заодно проверяем прочие места -- где б еще могли быть такие косяки.
remdrv_fd_p()
, REMDRVP_BIGC.
Туда скопирована проверка из cxlib'а.
inserver_req_bigc()
: там
аж комментарий был, что надо проверять параметры.
ServeBigcRequest()
--
это вообще тихий ужас какой-то...
connect()
'ом).
09.10.2012: "начато" сие было еще где-то в мае-июне, работы идут в lib/ncxlib/.
09.10.2012: сейчас туда скопированы все компоненты, кроме собственно "мяса" -- cxlib_client.c. Так что -- будем наполнять его.
16.10.2012@Снежинск-каземат-11: да, начинаем наполнять.
17.10.2012@Снежинск-каземат-11: немного анализа на тему "что используется из API cxlib и кем":
cx_fd_of()
-- никем. Как и вся прочая шобла касательно
cx_setlibmode (slave, select).
_n
-версии cx_connect()
,
cx_openbigc()
, cx_consolelogin()
: кроме
вышеупомянутого tsycam.c, еще утилиты командной строки -- которые и
так будут линковаться с libcx_sync.
cx_nonblock()
:
18.10.2012@Снежинск-каземат-11: убрано.
Вывод: сделать пустым.
cx_isready()
-- только самим старым cxlib'ом.
cx_result()
-- помимо самого cxlib'а, еще
Вывод: возвращать в errno код ошибки и делать
return errno == 0? 0 : -1
cx_entercritical()
/cx_leavecritical()
--
cda, и более нигде. Точнее, в QSGN'е, но с libcx_async это всё станет
неактуально.
cx_setnotifier()
:
17.10.2012@Снежинск-каземат-11: продолжаем наполнение.
do_open()
-- копии 4'шной cx_open()
, а
cx_{connect,openbigc,consolelogin}_n()
просто вызывают её.
async_CS_OPEN_LOGIN()
.
reporterror()
заменен на v4'шный
cxlib_report()
09.10.2013: в который
сейчас добавлена выдача cd
.
18.10.2012@Снежинск-каземат-11: продолжаем.
v2conn_t.retval
, так что в консольных соединениях команда
exit не работает (ибо AEXIT не передаётся).
09.02.2015: сделано (подробнее см. ниже за сегодня), так что и exit заработал.
Вроде всё. Начинаем проверять:
cx-setchan
работает.
cx-rdt
работает.
cx-console
: принятие сообщений (wall) почему-то НЕ
работает. Точнее, оно печатается ПОТОМ, при исполнении следующей команды.
Тьфу ты!!! Понятно, почему -- ведь при чтении команды с терминала оно НЕ находится в cxscheduler'е, и заходит туда только при отправке синхронной команды. А раньше -- оно ловило сигнал откуда попало. Так что -- с нынешней архитектурой фиг; кстати, и cx-sub тоже.
19.10.2012@Снежинск-каземат-11: в cx-sub сделано -- оно вместо
ручного sleep()'а вызывает sl_main_loop()
. Правда, по close не
выдаёт ругательство "killed", но то всё из-за отсутствия передачи
retval
. А её, похоже, надо бы хорошенько
обдумать -- может, даже и в 4cx'ном сделать.
07.05.2013@вечер-дома-засыпая: есть идейка, как в
cx-console решить проблему асинхронного прихода данных (и kill'ов):
надо прямо в самом cx-console.c ловить SIGIO
(как это делалось в старом cxlib'е), и из егойного обработчика вызывать
"одноразовый поллинг" cxscheduler'а. Вероятно, надо разрешать SIGIO
"скобками" вокруг fgets()
, а чтоб всё остальное время он
был блокирован (и да, ТУТ race-condition'ов можно не бояться, т.к.
if()'ов нет, и в каждый момент есть кому среагировать на приходящий
пакет -- либо сигналу, либо через доли секунды select()'у).
06.02.2015@лыжи-5-ка:
неа, не нужно ловить SIGIO. Надо использовать
FDIO_STRING
. Подробности см. в другой записи за
сегодня.
chlclient
работает... 8-0
RequestResult()
, которым
завершаются все потенциально блокирующиеся вызовы -- для "корректной" работы
с errcode; иначе пёрли ругательства
"cda: RequestData(): cx_run()=-1: Success"
(потому, что из неблокирующегося cx_run()'а возвращалось errno=0).
Делаем возможность использования нового варианта библиотеки:
LIBCX_ASYNC
и LIBCX_SYNC
в
TopRules.mk.
USE_NCXLIB=YES
Кстати, отдельный вопрос, мучающий меня уже несколько дней: как конкретно надо будет переделать внутренности cda, чтобы избавиться от сигналов? Или пока забить -- раз работает (а таймауты оттуда всё равно нужны)?
@Автобус-с-полигона ёлки, ну понятно ж как -- вместо "сигналов" вызывать их "обработку" сразу, а таймауты -- перевести на прямое использование cxscheduler'а.
19.10.2012@Снежинск-каземат-11: некоторая странность есть: программа liu к rack2:43 (он несколько раз перегружался) почему-то не переподконнектилась...
Несколькими часами позже, после обеда: при попытке воспроизвести -- не получается. Хотя ТОТ экземпляр всё так и висит и не переконнекчивается...
20.10.2012@Снежинск-каземат-11: "не воспроизводилось" скорее всего тогда, когда я просто в cx-console делал close. А в этом случае следующий же connect() из клиента отлично сработает (порт-то слушается).
20.10.2012@Снежинск-каземат-11: есть неприятная особенность: на без-сигнальном cxlib'е по крайней мере мега-клиент liu жрет неприлично много процессора. Вот кусок из top'а на table (C2D E6750@2.66GHz):
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 30371 oper 20 0 505m 184m 4024 R 81 9.1 174:11.89 liu 28168 oper 20 0 504m 182m 4024 R 73 9.0 1420:48 liu 19777 oper 20 0 630m 185m 4032 R 16 9.1 472:38.68 liu 28263 oper 20 0 628m 188m 4028 R 16 9.3 847:07.42 liu
Два верхних -- на новом cxlib, два нижних -- на классическом (да, они заняли больше ОЗУ, т.к. там сразу аллокируется CX_MAX_PKTSIZE на буфера). И еще непонятно, сколько бы новый занял CPU, если б ему не мешали -- при STOP'еньи одного коллеги второй радостно поднимает утилизацию до 94%, и до 99%.
-pg
". (А как вообще
называются profile-capable версии библиотек, и из каких .rpm они
берутся?)
strace
выдаёт вот такую картинку:
poll([{fd=3, events=POLLIN}, {fd=4, events=POLLOUT, revents=POLLERR|POLLHUP}], 2, 975) = 1 poll([{fd=4, events=POLLOUT, revents=POLLERR|POLLHUP}], 1, 0) = 1
select()
'а)...
...попытка сбагрить XtInputWriteMask|0x08
-- программа сразу
вылетела с диагностикой
Error: invalid condition passed to XtAppAddInput
XtInputWriteMask
еще
XtInputExceptMask
-- непонятно, с чего бы; но можно
попробовать.
21.20.2012@Снежинск-каземат-11: продолжаем:
#define XPOLL_EXCEPT 0
Это уже преконкретнейший баг, после этого нет никакого смысла что-то рыть -- всё равно такой Xt нормально работать не будет.
Разве только прошариться по багрепортам и проверить вариант в RHEL6...
14.01.2015: реальная проблема из-за неподдержки exception-fd в Xt (предсказанная еще тогда): НЕ работают USPCI'ные драйверы в 4cx'ных GUI+server-сборках (stand).
XtInputExceptMask
указывать XtInputReadMask
.
Бинго!!! Всё заработало -- оно ЛОВИТ ошибку!
SL_CE
("Connect Error"), который актуален только для Xh_cxscheduler'а, и тот
станет при наличии этого бита жаждать слегка другую маску.
Сделано -- вроде пашет.
31.10.2012: поскольку лень переделывать staromakh.c и descr2html.c для работающести под новой схемой, то при USE_NCXLIB=YES их сборка просто отключается. 27.11.2012: они еще 12-11-2012 удалены вовсе.
27.11.2012: проверено также и на x86_64 (SL-6.3) -- на ncxlib также работает.
Так что -- от старого варианта избавляемся вовсе:
ifeq "$(USE_NCXLIB)" "YES"
" из всех
makefile'ов.
#if USE_NCXLIB
" из всех исходников (в
основном это был cda :-D).
cda_set_environment()
,
cda_signal_handler()
, cda_timeout_handler()
,
CDA_DECLARE_XT_ENVIRONMENT()
,
CDA_REGISTER_XT_ENVIRONMENT()
.
28.11.2012: подпиленная вчера версия проверена на x86 и на x86_64 -- работает, так что считаем за "done".
25.01.2013: присутствовал баг, унаследованный от 4cx/: при отведении слота НЕ делалось fd=-1, что приводило к фееричным глюкам при не-резолвинге сервер-хостов. Детали в bigfile-0002.html за сегодня.
09.04.2014: обнаружился дурацкий ляп: в
do_open()
присутствовало сравнение
spechost!=loclhost
ДО получения spechost
.
11.06.2014: удаляем толпу старого "функционала",
требовавшегося (по крайней мере, теоретически) при SIGIO-режиме работы:
cx_setlibmode(),
cx_setlibslave(),
cx_entry_slave_sigio(),
cx_entry_select(),
cx_fd_of(),
cx_setconnecttimeout()
(с последней, вместе с cda_setreconnecttime()
, было также
энное количество функционала в Chl_app.c и
cx-starter.c).
Также и cx_entercritical()
с
cx_leavecritical()
.
09.02.2015: управильниваем работу с
retval
(непосредственная причина -- перевод cx-console на
FDIO_STRING):
MarkAsClosed()
оно делается =-1.
async_CS_OPEN_LOGIN()
=0 (почему-то было забыто).
cx_result()
теперь возвращается именно значение
cp->retval
.
Вроде работает...
11.05.2013: разборки показали причину -- различие в схеме работы старого и нового cxlib'ов:
(Был проведён тест: в cx-rdt добавлен sleep(10) перед
cx_run()
, во время которого сделано Ctrl+C -- результат
вышел ровно тем же.)
Т.е., схема работы В/В изменилась, а схема реакции (построенная на соответствующих допущениях, верных для SIGIO) осталась старой (хотя допущения уже не верны).
Сейчас видится две точки, где надо адресовать проблему:
SendRequest()
может вернуть -1
ТОЛЬКО при ошибке отправки, так что не только возвращать клиенту -1, но
и сразу MarkAsClosed()
.
cx_execcmd()<0
лишь вякает ошибку, но НЕ отрубается.
Видимо, изначально был расчёт на то, что кроме "транспортных" могут возвращаться и "логические" ошибки (результат выполнения команд -- типа "неверный идентификатор для kill"). Но по факту СЕЙЧАС этого нет, и ВСЕ ошибки -- транспортные.
Сделано -- в обеих точках. Проблема решается в обеих местах (проверено поотдельности). Единственная неприятность -- kill'нутые клиенты вместо "killed" тоже ловят "Broken pipe", но это исправится при реализации следующего пункта (асинхронная ловля по SIGIO).
23.03.2015: конкретно ТЕПЕРЬ, при чтении stdin'а через FDIO_STRING, всё уже работает прекрасно -- при обрыве соединения (в т.ч. по Ctrl+C) ругается и отваливает.
Так что закрываем случай.
Т.е. -- некоторый "откат назад", для одного конкретного клиента (хотя, если в v4 появится интерактивный вариант cxclient'а -- то в нём придётся делать то же самое).
Весьма вероятно, для реализации потребуется какое-то сотрудничество от cxlib'а.
06.02.2015@лыжи-5-ка:
есть вариант проще: учитывая существование
FDIO_STRING
, можно перевести cx-console с
fgets()
на fdiolib.
07.02.2015: только одна мелкая неприятность: отправку следующих команд надо блокировать вплоть до получения ответа на текущую (иначе будет CEBUSY).
Как? Постоянно дерегистрировать/регистрировать дескриптор? Гадость-то какая...
(И надо тогда уж полностью переводить утилиту на асинхронность.)
07.02.2015: а если в fdiolib ввести fdio_lock_recv() и fdio_unlock_recv()? Чтоб у дескриптора временно убиралось SL_RD?
09.02.2015: сделано пока без той "блокировки ввода на время исполнения команды" -- в таком варианте работает. Только еще Makefile пришлось подправить, чтоб LIBCX_ASYNC брался, вместо _SYNC-варианта.
23.03.2015: работать-то работает, но с косяками -- ведь при переходе на асинхронную архитектуру модификации требовались более серьёзные.
CAR_CONNFAIL
-- ошибок
соединения. Так что оно дальше висело до бесконечности на stdin'е.
Это исправлено тривиально.
Как подобное лечить "правильно" (инфраструктуро-индифферентно) -- вопрос.
Но при внедрении вышеупомянутого fdio_[un]lock_recv() проблема решится автоматом. Только надо будет ВНАЧАЛЕ лочить, а по CAR_CONNECT'у разлочивать.
Отдельно -- конкретно при запуске из-под стокового RH-7.3 (viper) есть какой-то косяк с выдачей через stdio больших объёмов информации: почему-то обрезается по границе чуть больше 4 килобайт. А тот же самый бинарник, но запущенный на linac1 (та же система, но glibc (оттуда используются libc.so.6 и /lib/ld-linux.so.2) проапгрейжена со стоковой glibc-2.2.5-34 до glibc-2.2.5-44.legacy.8) -- всё окей.
01.04.2015: однако "какой-то косяк с выдачей" наблюдается и при небольших объёмах: после простой команды "sato 1 3" (на linac1:3 -- lin485s) на экране вместо prompt'а стал появляться какой-то мусор -- будто xterm получил ESC-коды, переключившие codepage.
Так что разбираться всё же надо -- дабы этот косяк в v4 не пролез.
09.04.2015: "мусор" оказался по другой причине --
древний косяк в cxlib'овском ExtractPrintable()
, он
пофиксен. Но проблема "обрезания" осталась.
connect()
вдруг далее ничего не происходит
-- то ли сервер молча сдох (без FIN'а), то ли глюки какие-то (как на
днях было).
18.09.2013: реализация практически скопирована с
cxsd_fe_cxv2.c -- тот же cleanup_tid
, и
подчищатор называется WipeUnconnectedTimed()
. Часом позже: cleanup_tid
переименован в
clp_tid
-- для краткости, так удобнее, чтоб код не
раздувался; в сервере тоже.
async_CS_OPEN_CONNECT()
, т.е.,
сразу после того, как ответственность за ожидание снимет с себя ядро
(180с?); длительность -- те же 60 секунд.
async_CS_OPEN_LOGIN()
, т.е., перед
уведомлением клиента, когда ответственность за keepalive'ы возьмёт на
себя cda.
Вроде работает -- на тесте с натравливанием на "nc -lp 8012" честно отвалило через минуту. Хотя в cxscheduler пришлось внести дополнение, чтоб таймауты тоже могли б прерывать цикл.
Считаем готовым, но надо б присматривать, что не вылезет каких-то побочных эффектов.
24.11.2013: вылез побочный эффект (хотя и совсем иного рода) -- видимо, из-за ошибки с несбрасыванием clp_tid=-1 (которая сейчас исправлена). Детали см. в разделе по cda за сегодня.
ExtractPrintable()
: там значилось
const char empty[] = "";
и указатель на это empty
передавался наружу -- в
заказанный программой в cx_execcmd()
reply
.
Косяк очень древний (он есть в ucamlib.c от 28-05-1998), но
успешно скрывался, а вылез только при переводе cx-console на
FDIO_STRING (наверное, раньше так совпадало, что это место в стеке
прописывалось нулями, а сейчас практически сразу после
ExtractPrintable()
вызывается нотификатор, и место
оказывается уже в его собственном стековом кадре).
Поправлено тривиально добавлением static
.
16.02.2004: ввел флажок
CXRF_REM_C_PROBL
/"Remote controller problem", следующий за
CXRF_IO_TIMEOUT
. В
cxlib.c::_cx_rflagslist[]
изменения также
отражены.
23.05.2004: ввел флаг
CXRF_OVERLOAD
/"Input channel overload", следующий за
CXRF_REM_C_PROBL
. И в
cxlib.c::_cx_rflagslist[]
это не забыто.
CXC_*
и
CXB_BIGCREQ
.
CXC_*
и CXB_*
в
что-то унифицированное -- типа CXF{C,B,D}_*
, а то появится
префикс CXD_*
.
Или -- ввести соглашение, что в такой ситуации
отдается
CXRF_OFFLINE
плюс еще какой-то флаг? Тогда --
какой именно? Пока подходящих кандидатов не видно...
У-ф-ф-ф -- похоже, надо вводить
CXRF_WRONG_DEV=1<<13
(примыкает к
CXRF_OFFLINE
). При желании можно будет эту пару флагов
использовать вместе для какой-нибудь еще комбинации. (17.04.2005:
может, как раз для "syntax error в auxinfo?)
25.05.2005: да, ввел
CXRF_WRONG_DEV=1<<12
(а не 13, поскольку за это
время флаги успели чуть съехать. И в
cxlib.c::_cx_rflagslist[]
оно также
зарегистрировано.
И -- обнаружилось, что 30.11.2003 забыл переименовать короткое "описание" из "NO_DEV" в "OFFLINE". Пофиксено.
Кроме того, поскольку флаг "OFFLINE" теперь автоматически
уставляется "блокам в отключке", то использовать его "на пару с
другим" не удастся. <S>
.
В остальном же -- "done".
CXRF_DRV_PROBL
.
15.06.2005: ввел этот флаг, и надлежащее описание
для него в _cx_rflagslist[]
. Глядя на этот список
вылезают две мысли:
И OFFLINE, как "очень общий", и ныне уставляемый автоматически самим сервером, вообще должен стоять "с краю" -- быть 15-м битом.
05.12.2005: сделал -- в CXv4
(4cx/src/include/cx.h) флаги подпереупорядочены: снизу идут,
как и раньше, {CAMAC_NO_{X,Q},IO_TIMEOUT,REM_C_PROBL,OVERLOAD}, а
сверху -- OFFLINE=1<<15, NO_DRV,DRV_PROBL,CFG_PROBL,WRONG_DEV. И
определение CXRF_SERVER_HWERR_MASK
корректное -- не просто
младшие 8 бит, а именно список флагов.
И эти, "что снизу" -- ОТОБРАЖАТЬ ДРУГИМ ЦВЕТОМ!!! 30.08.2005: да отображаются, отображаются они болотным, давно уже :-)
Кстати, тут отдельный вопрос -- к какой категории относить
CXRF_OFFLINE
? Наверное, ко второй, где оно сейчас и
пребывает.
Акромя же того -- прошелся по имеющимся драйверам, повставляв сей флаг где надо. "Жертвы" -- new_cm5307_drv.c, canbus/canadc40_src.c с ныне пишущимся canbus/marcankoz_pre_pyr.c.
Дальше будет вопрос о том, как это передавать клиентам. Ясно одно -- передавать надо будет строку плюс диапазон номеров каналов, к которым она относится. А в cxlib'е будет "репозитарий"/"база данных", по которым можно будет делать lookup -- типа
char *cx_geterrdescr(int cd, chanaddr_t addr)
KNOB_VALUE_LIT_MASK
и
KNOB_VALUE_DISABLED_MASK
определяются сейчас в
Knobs_typesP.h:
Надо в CXv4 их унести в "будущий" cx.h -- который сейчас cx_types.h.
Тогда драйверы и клиенты будут пользоваться ОДНИМИ И ТЕМИ ЖЕ константами.
07.07.2005: а собственно -- чего тянуть?! Перенес константы в cx_types.h, сменив префикс с "KNOBS_" на "CX_".
18.07.2005: отразил сии флаги в src/doc/drv_wr_manual.html.
Напрашивается название CXRF_INVAL
. А вот по смыслу --
как-нибудь бы его отнести к SFERR (=1<<10, прямо перед
WRONG_DEV/CFG_PROBL (в CXv2/CXv4 порядок отличается)?).
28.02.2006: ввел
CXRF_INVAL=1<<10
. Также отразил сие в
_cx_rflagslist[]
.
В 4cx/.../cx.h тоже не забыто.
(Сейчас пришлось выкручиваться сочетанием CAMAC_NO_X+CAMAC_NO_Q, но это некрасиво.)
Напрашивающееся название -- именно CXRF_UNSUPPORTED
, и
относится он к группе HWERR, рядышком с "OVERLOAD".
28.02.2006: ввел
CXRF_UNSUPPORTED=1<<5
. Также отразил сие в
_cx_rflagslist[]
. И начал использовать в
cgvi8_src.c
.
И в 4cx/.../cx.h также добавил.
Кстати: а ведь у нас теперь осталось всего лишь ЧЕТЫРЕ незанятых serverside-флага!
28.02.2006: введено в cx_types.h --
CX_VALUE_COMMAND=1
(плюс в 4cx/.../cx.h).
Также на него переведены все затронутые места -- Knobs_button_widget.c, cgvi8_src.c.
Плюс kshd485_drv.c -- там уже и раньше почему-то была
проверка "values[n]&1)!=0
" (унификация с LOGD_ONOFF?).
А иных драйверов с командными каналами у нас и нету! :-)
16.05.2006: но с самого начала в
cm5307_karpov_monster был избран совсем простой подход: об underflow
сигнализируется тем же самым флагом CXRF_OVERLOAD
, но само
значение при этом -- 0, а не "большое число".
Вот этим подходом и будем везде пользоваться.
(В этом смысле флаг OVERLOAD/"Input channel overload" скорее стоило бы переименовать в NONREPRESENT/"Value not representable", но это как-то криво, так что пусть уж будет OVERLOAD.)
07.08.2006: мда, с одной стороны -- решение
простое; с другой -- получается, что для точного определения
"состояния" кроме флагов нужно также и значение... С другой стороны,
для проверки диапазонов (yellow/red) в ChooseStateRflags()
и так передается текущее значение.
CX_MAX_BIGC_PARAMS
из cx_proto.h в
cx_types.h. Объяснение -- в разделе по CANGW за вчера и
сегодня.
Это стало необходимым вследствие использования cPCI-крейтов в качестве field-контроллеров, при котором они имеют шанс либо зависнуть (вследствие высоковольтных/высокочастотных помех/пульсаций), либо быть выключены (сбой питания либо дурость персонала).
09.08.2010: технически-то ничего сложного нет -- достаточно ввести соответствующие CXT_-коды, тривиальную поддержку в cxlib и cx-server, плюс застваить cda дергать соответствующую cx_-функцию раз 300 секунд (полностью аналогично реализации в cm5307/cangw).
Проблема же в том, что таковых CXT_-кодов сейчас нет, и добавление поддержки в клиентскую часть организует несовместимость с работающими серверами.
Найти бы какой-нибудь код, на который сервер либо не реагировал бы, либо отдавал бы пустой безобидный пакет...
19.08.2010: во -- а номера версий-то у нас на что? Их же можно использовать для определения, понимает ли сервер некие фичи!!!
Первой мыслью было продвинуть CX_PROTO_VERSION_MINOR
с
0 до 1, но так поступать нельзя: тогда необновленные клиенты станут
считать обновлённые сервера несовместимыми -- в такую сторону устроена
проверка по CX_VERSION_IS_COMPATIBLE()
.
Вторая мысль: а ведь у нас есть пока неиспользуемое поле PATCHLEVEL -- вот его и заиспользуем!
Поехали:
CX_PROTO_VERSION_PATCH
=1 (и за компанию
CX_PROTO_VERSION_SNAP
=0), плюс, для возможности
сравнивания --
CX_PROTO_VERSION_PINGABLE
=CX_ENCODE_VERSION_MmPs(3,0,1,0).
CXT_PING
и CXT_PONG
(в
0x01010101-область).
DispatchPacket()
вставлено распознавание CXT_PING
(вызывает async_CXT_PING()
, пока пустую) и CXT_PONG
(вообще без реакции).
async_CS_OPEN_ACCESS()
добавлено заполнение
свежевведённого поля pingable
по версии протокола,
присланной сервером.
cx_ping()
.
В ней есть проверка и на pingable, и на то, что она вызвана НЕ в
одном из состояний CS_nnnn_PREP (т.к. она пользуется стандартным
механизмом
StartNewPacket()
+SendRequest()
, т.е.,
разрушила бы подготовленный буфер).
InteractWithClient()
в cx-server.c и
cxd_manager.c вставлены "case CXT_PING" -- с пустой реакцией
(поскольку нам ответы не требуются, важен лишь сам факт пересылки
пакета по сокету).
HeartbeatProc()
дергается раз в 5 минут, постоянно и
unconditionally, полностью аналогично cm5307'ной
HeartBeat()
, а уж собственно cx_ping()
вызывает только при полной функционирующести соединения.
Проверено -- работает. Конечно, можно бы заморочиться еще с некоторыми фичами:
Но просто остановимся на сделанном -- "done".
30.09.2010: маленькое замечание: номер версии-то отдаёт не cx-server, а cx-porter. Я же накололся, скопировав на l5sX именно только cx-server, и потом долго удивлялся -- а чё это ping не пашет и 5-минутного восстановления после зависа l5s2 не происходит?
20.10.2010: еще вылезла неприятность: большие каналы время от времени ругались
2010-10-20-15:48:23 liucc: cxlib: async_CS_BIGC_SENT(4): reply Seq/75/ != syncSeq/74/
Происходило это раз в 5 минут -- оттого, что ping посылался в
момент, когда был отправлен запрос на большой канал, и сбивал текущее
значение .
cp->Seq
Но странно другое -- что с обычными соединениями этого не происходило, а ТОЛЬКО С БОЛЬШИМИ.
Решение-то просто и уродливо -- в cx_ping()
вставлено
--(cp->Seq)
...
А вот фиг -- не помогло...
20.10.2010: Ну так еще бы помогло -- проблема-то не тут, а на другой стороне: это СЕРВЕР присылает ответ с ПОСЛЕДНИМ ПРИШЕДШИМ К НЕМУ Seq, вместо того, с которым был прислан запрос.
Проверено было очень просто -- после удаления того
cp->Seq
при больших интервалах между выстрелами стали
появляться сообщения типа
-- т.е., расхождение по 1 на каждый PING, успевший проскочить между запросом и ответом.2010-10-27-14:01:55 liucc: cxlib: async_CS_BIGC_SENT(17): reply Seq/87/ != syncSeq/84/
Стал рыться -- и нашел!!! Причём зацепкой послужило как раз то, что проблема ТОЛЬКО с большими каналами -- именно в cx-server_bigc.c и нашлось. Итак:
InitReplyPacket()
(каковой
и выполняет копирование
cp->replybuf->Seq=cp->reqbuf->Seq
) делается не
при получении запроса -- как у обычных каналов -- а непосредственно уже
в FillBigcReplyPacket()
, вызываемом при получении ответа
от драйвера.
reqbuf
, а надо туда лезть
lastreqbuf
, в который
и копируется запрос.
reqbuf
уже не
будет отражать состояние ЗАПРОСА.
Возможные варианты решений:
InitReplyPacket()
в момент получения пакета.
InitReplyPacket()
делать
cp->replybuf->Seq=cp->lastreqbuf->Seq
.
Впрочем, учитывая, что разница между вариантами -- теоретическая/потенциальная возможность обработки разных seq-пакетов вперемежку (т.е., другого seq-пакета в течение ожидания ответа на большой канал), каковая в любом случае никогда не реализуется (в т.ч. потому, что остальная инфраструктура там под это не приспособлена), какой выбрать -- почти пофигу.
Так что делаем по второму.
03.06.2014: делаем. Увеличено с
2<21
(4М) до 1<24
(16M).
В сервере ограничений нет (более того -- там в конфиге можно указать отрицательный размер... :().
В драйверах 200/812 имевшееся ограничение также убрано. Но проверить пока не на чем :)
d_NNN
(Device, вместо
нынешнего b_ -- Block), а больших каналов -- b_NNN
(Bigc,
вместо нынешнего артефакта d_ -- Dirc).
20.01.2004: переименовал. Пока не проверял, но,
учитывая, что "d_" там был только d_data[]
, превратившийся
в b_data[]
, и все откомпилировалось, проблем быть не
должно.
cycle_t
, которым и объявлять все вещи,
связанные с "внутренним временем" -- все "*time*
",
"*cycle*
" и параметры соответствующих функций.
04.06.2005: да, ввел
cx-server_data.h::typedef int32 cycle_t
. И
попереводил на него все переменные и параметры функций.
Единственное место, где "торчит" просто int
, это
Drivers-API-функция GetCurrentCycle()
.
tag_t
из булевского флага в "возраст").
04.06.2005: Она возникла во времена транспьютера, когда, кажется, кабы даже не внутри-цикловое стробирование между транспьютером и сервером существовало, и вся алхимия с "step" была придумана, чтобы ВСЕГДА отдавались значения с tag=0.
Потом же, когда "мир расширился", кроме транспьютера, с его четко ограниченным кругом обязанностей и железа, пришлось иметь дело с Одренком и Мотороллером, и с тормозным железом, далеко не всегда успевающими обработать все вовремя, попытки всегда иметь tag=0 окончательно провалились, уступив место попыткам tag<5. И при этом концепция "sub-cycle precision" стала окончательно ненужной.
(И, кстати, еще давным-давно использование
cycle_step
было исключено (путем "0*()
") из
CycleToScheduleRequestTo()
, каковой и сам после этого стал
нафиг не нужен, вместе с полем reqcycle
.)
Так что вывод -- в CXv4 оно не попадет, и из "ядра" (которое сейчас в cxsd_mainloop.c) его надо будет исключить.
23.06.2005: а собственно -- чего ждать?! Так что -- сценарий-хроника изведения:
CycleToScheduleRequestTo()
выкинута.
cyclestep_t
и переменная cycle_step
удалены.
ServerClient.reqcycle
удалено.
ServeDataRequest()
убрано его заполнение.
reqcycle
в EndOfCycle()
.
{FULLCYCLE,DATACOLLECTION,MEASUREMENT}_TIME
и переменные
step_{start,end}
.
CycleCallback()
радикально упростилось --
просто линейное выполнение действий -- EndOfCycle()
, сдвиг
cycle_start
на BaseCycleSize
и заказ
следующего таймаута, BeginOfCycle()
.
InitCycles()
также стало крайне простым.
BaseCycleSize
на части исчезла, и используется само это
значение.
Вот и все!
Трогать содержимое programs/daemon/ я не стал -- все равно этот вариант в дело не пойдет, а останется тестовым прототипом; при переделке же его в "CX server library" понятие "cycle step" просто будет опущено.
Так что -- считаем за "done".
logline()
сделать enum'ы -- чтобы не было зашито всяких
магических чисел.
А можно даже и круче -- чтобы это было не enum'ами, а переменными -- и тогда сие можно будет, как драйверные категории, менять на лету.
Актуальность свою (даже потенциальную) она давно потеряла, а поддерживать её в собирабельном состоянии стало окончательно лень в момент обновления API cxscheduler'а.
21.05.2013: детали:
За следующую неделю оно было допилено до запускабельного состояния, потестировано и обезжучено, еще через неделю втихушку запущен сервер linac1:31 (linmagx), а еще через неделю сделан симлинк pult/sbin/cxd->cxsd, и при рестартах серверов идёт миграция.
21.05.2013: да, имелись препозорнейшие ляпы:
stop_dev()
при де-регистрации файловых дескрипторов
(а там схема отличается, из-за cxscheduler'а) не имел
"break
"ов, и вякалось о попытке повторной дерегистрации.
close()
этим дескрипторам,
так что они оставались висеть и, как следствие, "sato" remdrv'шным
приводило к "device (L,K) already in use" -- на той стороне оно было
не-освобождённым.
RequestReadOfWriteChannels()
-- с вытекающими
последствиями.
15.09.2013: добавлена
cxsd_uniq_checker()
-- аналогично remcxsd'шной. Ради чего
пришлось
16.09.2013: оный убран в связи с публикацией
cxsd_uniq_checker()
в cxsd_driver.h.
CHECK_SANITY_OF_DEVID_WO_STATE()
и
CHECK_SANITY_OF_DEVID()
мясо в
DO_CHECK_SANITY_OF_DEVID()
и
DO_CHECK_SANITY_OF_DEVID_WO_STATE()
-- чтоб вместо
__FUNCTION__ подсовывать другое имя, переданное в
func_name
.
24.11.2013: был ляп в тутошней per-connection
реализации WipeUnconnectedTimed()
: оно при снятии таймаута
по завершению handshake'а забывало сбросить clp_tid=-1
. В
точности, как это обнаружилось в cxlib'е (в скопированной отсюда
реализации).
Возможно, этим объяснялись не-реконнекченья remdrv'ы.
06.02.2014: обнаружился имевшийся с незапамятных времён ляп: при ошибке открытия log-файла (например, при переполненном /var) оно падало по SIGSEGV.
Ларчик открывался просто: в коде
cxsd_config.c::SectionLogging()
все нужные
проверки имелись, но ReportError()
забывал выставить
normal_exit=1
перед exit(), и оно пыталось логгировать,
проверяло при этом FD_ISSET(logfds[logfile]), а logfds[] содержал
отрицательное значение, ну и...
Кстати, в 4cx/ проблемы с открытием логов также никак не решаются!
main()
вкралась глупость: если в старом cxd.c делалось
то сейчас (сокращённо)ForkPart(CX_PORTER); CreatePidFile(argv[0]); ForkPart(CX_SERVER);
ReadHWConfig (argv[0]); Daemonize(); ActivateFrontends(); CreatePidFile(argv[0]);
Т.е., оно СНАЧАЛА пытается инициализировать железо, даже не проверив (по доступности занятия слушающего сокета), что "можно" и не запущено другого экземпляра. В результате некоторые драйвера могут испортить жизнь уже работающим: например, UDP'шные -- просто послав пакет с другого порта, и устройство "забудет" реального хозяина.
И, что неприятно -- оно же и в 4cx/ пролезло по образу и подобию.
13.12.2013: пытаемся исправить.
Вообще-то "сценарий действий при старте" -- штука нетривиальная, с выбором порядка уже набито много шишек.
Причина такого "телега впереди лошади" -- видимо, необходимость проверки существования blklist-файла.
Так что
TestHWConfig()
, в которую скопирован из
SimulateDatabase()
код открытия с руганью при ошибке.
ReadHWConfig()
перетащен пониже,
В 4cx/ правки тоже внесены (там оказалось проще и правильнее, вследствие разделённых чтения и активации). Мелкие различия между сценариями в v2 и v4 остались, касательно мест обработки dontdaemonize и norun, но на это можно (пока?) забить.
...считаем за "done", но ухо надо держать востро -- вдруг где изменение последовательности аукнется.
ServeConsoleRequest()
в парсинге параметров: оно
умудрялось вылазить за терминирующий '\0' у команд без
параметров, потому, что безусловно делало *p++='\0'
после
пропуска whitespace после команды (которого там не было) -- в
результате и вылазило за пределы строки.
Проявлялось в nargs>1 ВСЕГДА -- в т.ч. в команде "scan" (на чём и обнаружилось -- лезло всегда как от "scan w").
Теперь запись-с-инкрементом условная, если !='\0'.
size_t privdatasize
.
privptr
к
CxsrvBlkInitFunc
.
driverrec.privdatasize!=0
,
то перед инициализацией блока он сам делает malloc()
и
RegisterDriverPtr()
, а после term_blk сам делает
free()
.
Если же драйвер хочет сам возиться с динамической памятью (например,
если у него нефиксированный размер данных), то он указывает
privdatasize=0
, и drvmgr туда не суется (и даже free не
пытается делать).
Стандартное замечание: не забыть
продвинуть CXSRV_DRIVERREC_VERSION
.
20.01.2004: угу, сделал все, что прописал выше. Место под privdatasize выбрал между описанием фич драйвера и таблицей методов, так что при промахе компилятор сразу даст warning.
Заодно три вещи, которые не предусмотрел ранее:
AddBlock()::stub
.
malloc()
'нутую область надо перед вызовом init_blk
забить нулями.
DeregisterDriverFD()
отсутствовала проверка, что мы
удаляем "основной" дескриптор драйвера -- т.е., что
d_fd[magicid]==fd
, и, как следствие, d_fd[]
не подчищался, и в будущем драйверу мог бы передаваться "протухший"
номер дескриптора.
Обнаружил это при разбирательстве, "когда же очищается default'ный дескриптор при reset'е драйвера" (никогда это поле там не очищалось ;-).
Теперь надо все это протестировать. Мда, хорошенько-прехорошенько подумать над тестами :-).
21.01.2004: в процессе переделки самих драйверов,
чтоб не занимались malloc()'ом, обнаружил еще недоделку (хоть и не
фатальную): забыл всем функциям _init_b
подобавлять
параметр privinfo.
А собственно переделка драйвера включает такие действия:
privrec_t *info
" дополняется до
"privrec_t *info=(privrec_t *) privptr
".
RegisterDriverPtr()
.
Естественно, в драйверах, не использующих privptr, переделка ограничивается добавлением параметра "void *privptr __attribute__((unused))".
28.06.2005: эх! Ведь уже почти полтора года, как
это работает! Баги были только в самих драйверах -- типа sim_drv, где
забывал указывать sizeof()
в driverrec'е. А так -- все
окей.
Помечаем как "done".
26.01.2004: стукнула мысль: а ведь решение-то очевидно (вот что значит сформулировать цель!)!
Итак:
magicnumber
ifacename
version
drv.iface_major==lyr.iface_major && drv.iface_minor<=lyr.iface_minor
Дальше идет таблица указателей на функции/методы интерфейса, которые драйвер-клиент может вызывать. Они будут определяться в некоей структуре, которую #include'ат и драйвер, и layer. (Очень напоминает VMT, не правда ли?)
Похоже, один из методов также стоит стандартизовать --
"DisconnectDriverFromLayer()
", чтобы его мог принудительно
вызывать TermBlock()
.
17.11.2014: о чём я тогда думал, какое "стандартизовать" -- ведь метод "disconnect" должен быть не в VMT, а в описателе самого layer'а. Где он по определению будет стандартизован. Так что тут и обсуждать нечего.
do_rw()
добывать таковой указатель.
"Самым удобным" было б передавать его всем методам драйвера, но, во-первых, уже достало постоянно менять интерфейс, а во-вторых -- далеко не все драйверы будут layer'ными, так что нефиг этот интерфейс засорять.
Второй вариант -- иметь функцию наподобие "GetLayerVmt(magicid)", которая будет просто возвращать что-то типа "d_layervmt[magicid]". Хоть это и малек неоптимально (вызов функции, в то время как можно было б просто прочитать слово в памяти), видимо, так и надо сделать. А захотим оптимизировать -- всегда можно поменять функцию на макрос.
А драйвер может сам проделать некоторую оптимизацию: если он не использует privptr (что вряд ли), то может держать vmtlink там. Если же у него есть privrec, то можно отвести в нем место под vmtlink.
Возможно, выглядит это все и "страшновато" (на мой взгляд -- не
очень), но реализовать можно примерно за день; больше времени уйдет на
отслеживание тонких моментов типа интерференции механизма layer'ов с
защитой драйверов друг от друга ({ENTER,LEAVE}_DRIVER()
).
Вот при написании замены для олеговых CAN-драйверов и реализуем -- там без layer'а все равно не обойдется.
02.04.2004: ма-а-а-хонькое
замечание насчет стандартизации
"DisconnectDriverFromLayer()
": а что именно мы будем
ему передавать? Т.е., каким (одинаковым среди всех
драйверов и layer'ов) образом мы будем указывать, какой именно
драйвер-клиент мы "отсоединяем"?
20.04.2004: Для layer'ов нужен свой отдельный config, где бы указывались их опции -- как драйверам в auxinfo. Например, baud_rate для can_layer (оно же именно для layer'а, а давать драйверу, как пришлось бы сейчас -- криво).
01.07.2004: (реально доперло давно, но записываю
сегодня) Насчет "что передавать
DisconnectDriverFromLayer()
" -- ясно что,
magicid
, что ж еще. Собственно, в canadc40_drv.c --
прототипе будущего marcankoz_lyr.c, под это уже все заточено.
30.08.2008: в свете будущих пачковых CAN-устройств для Снежинска -- а не реализовать ли действительно поддержку layer'ов по тому проекту 2004г., раз это так просто?
03.02.2009: кроме layer'ов надо бы иметь "просто модули". Например, все CAN-драйверы должны быть отдельными файлами, но в них слишком много общего (конкретно у всех DAC и всех ADC, и у комбинированных). И вот это общее вроде в layer пихать неохота, но и в драйверах дублировать -- изврат.
Так что надо ввести понятие "просто модуль", который:
Вопрос только -- а как именно проводить связывание и давать ли возможность иметь несколько разных модулей с одинаковой функциональностью -- как layer'ы, или же связывание будет лишь статическим? Хбз... И вообще -- отложим пока это, а? :-)
(Надо было это всё ввести отдельной секцией, но оно столь сильно переплетено с layer'ами, что пусть уж живет здесь.)
init_blk()
, что если он <0,
то помечать блок как "обломившийся"?
21.01.2004: и, заодно уж надо не забыть, что при отведении priv-области сервером и обломе blk_init надо не только деактивировать блок, но и освободить его privptr.
09.02.2004: полез разбираться -- ну и навернуто же там...
16.02.2004: доп. мысль: чтобы иметь возможность передавать из init_blk суть проблемы, введем такое усовершенствование: если вернутое значение отрицательное, то это уже проблема; а если оно !=-1, то представляет собой "neg" от маски-ошибок.
30.06.2004: достало -- сделал!
Пока довольно халтурно -- изменения коснулись
cxsd_drvmgr.c, где вставлена проверка на init_blk<0 и если так --
то делается то же, что при TermBlock(), плюс
cx-server_dbase.c -- там при проблеме с загрузкой
драйвера флаги всех принадлежащих блоку-жертве каналов забиваются
CXRF_NO_DRV
. 08.09.2014: дополнение:
теперь при включенной option_simulate
НЕ забиваются --
поскольку в режиме симуляции загруженность драйверов по барабану.
Там понапихано меток "!!!" -- что это все просится на украсивливание.
Поддерживается и протокол "Если результат !=-1, то это neg от маски ошибок".
Похоже, требуется еще один флаг -- наподобие "проблема инициализации блока", чтобы обозначать так всякие синтаксические ошибки в auxinfo и т.д.
Как бы то ни было, помечаем раздел как "done".
01.07.2004: ввел код
CXRF_DRV_PROBL
/"Driver internal problem", впихнув
его между CXRF_NO_DRV
и CXRF_OFFLINE
(поскольку это все еще в стадии обкатки, соблюдать совместимость не
требуется, и флаг можно было подвинуть).
В cm5307_drv.c оно уже используется.
Вообще-то стоит получше подумать об именовании и об описании -- что-то мне современный вариант кажется неадекватным...
getopt()
/Xrm*()
. Проект такой:
Строка auxinfo имеет вид
а для API может быть два варианта:param=value {param=value...}
- Функции типа
GetParamInt()
иGetParamStr()
, которым указывается "ключ" -- имя интересующего параметра, и если он присутствует, то они возвращают его, иначе -- указанное умолчательное значение.- Создается таблица с указанием "ключей", типов (int/string) и умолчательных значений, а также мест, куда складировать результаты, а потом ОДИН раз вызывается функция типа "
ParseAuxinfo()
", которая и разбирает строку.Плюс первого варианта -- простота, плюс второго -- возможность отлова ошибок -- опечаток в именах: оно может указать, что "параметр такой-то неизвестен". Второй вариант также требует либо раздельного указания свойств и адресов результатов, либо указание адреса "структуры для результатов" и неких оффсетов адресов внутри нее (уже в таблице).
Кстати, в любом случае, возможные "свойства": у целочисленных параметров -- умолчательное, минимальное и максимальное значения, у строковых -- только умолчательное.
19.04.2004: видимо, решение будет в рамках библиотечного модуля paramstr_parser.[ch].
15.06.2004: поскольку уже проверил, что canadc40_drv прекрасно живет на paramstr_parser'е, помечаем раздел как "done".
А хочется иметь стандартное, унифицированное решение, причем позволяющее "играть" такими деталями логгинга без лазанья в исходники и перекомпиляции. Проект таков:
- Вводится некоторое количество "стандартных категорий", первоначальная прикидка:
- точки входа/выхода в функции,
- получение/отправка пакетов (прямо с побайтными дампами),
- отдельной категорией -- результаты "разбора" входных пакетов (получен пакет код такой-то, канал номер такой-то...)
- ...
- Все эти категории получают короткие имена, чтобы можно было на них ссылаться из драйверов и в config'ах (например -- entrypoint, pktdump, pktinfo).
- В вызове
DoDriverLog()
ответственно подходящий к делу драйверописатель сможет вlevel
передавать и "категорию" (по умолчанию -- 0). Например, это будут константыDRIVERLOG_C_NNN
. (Замечание: категория здесь -- это номер, а не бит маски, как в серверномlogline()
!)- Такие
DoDriverLog()
'и будут оставаться в коде драйверов навсегда -- если они хоть сколько-то полезны, то выкидывать их по мере отладки не надо.- В blklist'е можно будет при каждом драйвере указать, какие именно категории сообщений от него стоит разрешить. Поскольку заново менять формат blklist.lst не хочется, то можно расширить поле
file
сдоfile[@layer]
file[@layer][;log=category,category...]
(В более широком варианте можно будет указывать после спецификации файла/layer'а еще список опций через точку запятой, вида "
option=value
". Это идеологически сильно смахивает на поле options в /etc/fstab и ключ mount'а "-o", а также на параметры командной строки ядра Linux; разве что разделители из-за ограничений синтаксиса другие. Какие могут еще понадобиться опции -- пока неясно, но возможность для их введения есть.)Для удобства вводится макрокатегория "
all
".- По умолчанию логгинг всех категорий, кроме "обычной" 0 отключен. Его можно включить либо для отдельного драйвера (
log=...
), либо для всех сразу -- например, каким-нибудь ключиком командной строки cxsd, в котором так же через запятую перечисляются интересующие категории (парсер формата "category{,category}" надо будет сделать общим для обоих случаев).Можно синтаксис и чуть расширить -- чтобы в конфиге можно было для "особо болтливых" драйверов отключать некоторые категории, когда они включаются для всех. Например,
log=-category1,+category2
, а по умолчанию считается "+". (Унифицированный парсер будет возвращать две маски -- того, что "+", и того, что "-".)- Поскольку при всех входах в код драйверов/layer'ов будет использоваться
ENTER_DRIVER()
, то в каждый конкретный момент времениDoDriverLog()
сможет определить, какой именно драйвер его дернул, и проверить по маске категорий -- надо ли исполнять этот запрос.
07.05.2004: кстати, а ведь захочется менять
драйверный логгинг "на лету" -- так что придется изготовить консольную
(cx-console->setdrvlog MODE MAGICID...
20.05.2004: начаты работы по воплощению фичи в
жизнь. Введены в cxsd_driver.h константы
DRIVERLOG_C*
, а парсер --
ParseDrvlogCategories()
пока растет в
cxsd_drvmgr.[ch].
21.05.2004: все сделано. И парсер с двумя
масками (+/-), и возможность указать ";log=list", и консольную команду
"setdrvlog", и собственно поддержка в vDoDriverLog()
.
Заодно, кстати, был упорядочен набор констант
DRVLOG_XXX
и введены формальные маски для собственно LEVEL
и категорий.
Помечаем раздел как "done".
21.12.2004: имелась недоработка: в случае
"NOT-IN-DRIVER" никаких проверок не делалось. Исправил -- в такой
ситуации идет сравнение с DefaultDrvlogMask
.
17.04.2005: уже довольно давно возникло желание:
Уметь так же на лету менять и ту маску, которая используется для
"NOT-IN-DRIVER" -- т.е., DefaultDrvlogMask
. Например,
указывая magicid=-1.
Ну делов-то -- сделал, что при указании -1
меняется
DefaultDrvlogMask
.
18.04.2005: проверил (на canadc40@hedgehog/istc) -- "setdrvlog SPEC -1" работает.
19.09.2005: оказалось, в
ParseDrvlogCategories()
была забыта DATACONV. Вставил.
16.10.2009@ТНК: давно было неудобно, что невозможно включить PKTDUMP/PKTINFO только в удалённых драйверах -- сами cm5307_drv/cangw_drv тоже начинают сыпать СВОЮ информацию. А ведь идея-то очевидна -- сделать отдельные категории REMDRV_PKTDUMP и REMDRV_PKTINFO.
17.11.2009: так и делаем --
catlist[]
.
08.12.2009: а вот еще было б очень удобно, чтобы в debug.log драйверовы категорированные сообщения попадали б с префиксом категории -- во-первых, так проще будет ориентироваться, а во-вторых, можно будет grep'ить.
Часом позже: вроде сделал. Для этого добавлена
cxsd_drvmgr.c::GetDrvlogCatName()
, возвращающая
по указанной категории её название. А название берется прямо из
catlist[]
, который ради этого был синхронизован с
константами DRIVERLOG_CN_
: так что они могут
использоваться в качестве индекса в нём; плюс, названия в нём были
переделаны на заглавные буквы.
Осталось проверить и посмотреть, насколько оно мерзко или нет.
11.12.2009: Роговский утверждает, что это работает.
13.12.2009: а еще очень полезно было б вести еще отдельную категорию DRIVERLOG_C_ERROR (и DRIVERLOG_C_REMDRV_ERROR) -- для удобства фильтрации сообщений об ошибках. Естественно, по умолчанию эти категории должны быть разрешены.
13.12.2009: P.S. А еще Роговский интересуется --
можно ль и для параметра level
ввести формальные константы
(типа тех LOG_*
, что есть у syslog()
'а).
15.04.2010: угу, backport из 4cx/: для сервера --
LOGL_nnn
, а для драйверов -- DRIVERLOG_nnn
.
Где nnn -- EMERG=0, ALERT=1, CRIT=2, ERR=3, WARNING=4, NOTICE=5,
INFO=6, DEBUG=7. Т.е. -- они унифицированы с
syslog()
'овыми level'ами. Соответственно, умолчательным
значением verbosity
теперь поставлено не 5, а
LOGL_NOTICE
.
P.S. И в ppc_canserver/cxsd_driver.h оно также добавлено.
P.P.S. Теперь чего не хватает -- только возможности в -v указывать сразу название категории, а не цифру. Впрочем, это уже совсем на будущее 4cx/.
07.04.2004: сделал. В собственно
vDoDriverLog()
изменения тривиальны. А немного повозиться
пришлось с cxdlib.c, переименовывая там vlogline в vloglineX,
и добавляя поддержку subaddress
-- оная практически
скопирована из cxlogger.c.
Милая вещь обнаружилась -- в инфраструктуре cx-server вообще НИКАК невозможно узнать по magic'у имя драйвера -- оно просто нигде не сохраняется. Более того, имя нельзя узнать и по driverrec'у -- в оном такого поля нет, а некая общая "таблица драйверов/модулей" у нас пока не ведется.
Сейчас пришлось в качестве имени использовать "-unknown-", а в будущем явно стоит устранить причину такой несуразности.
07.04.2004: (записано 18.04.2004) еще прикол -- поскольку инфраструктура с layer'ами пока не готова, то весь CAN-bus'ный логгинг идет как "NOT-IN-DRIVER". Вот зад...
18.04.2004: кстати, а может, совершить очевидный шаг да исправить первоначальный
design-недостаток интерфейса DoDriverLog -- отсутствие параметра
magicid
?
Ведь в любом случае, даже если сделаем всю layer'ную инфраструктуру, со специальными layer-magicid'ами, определить реальный id драйвера сервер не сможет -- он будет становиться известен уже только в самом layer'е.
20.04.2004: эх, была не была -- решился, надо:
DoDriverLog()
-- добавить параметр
magicid
.
CXSRV_DRIVERREC_VERSION_MAJOR
еще на единичку.
08.05.2004: свершилось. Сделал все по
вышеописанному проекту, CXSRV_DRIVERREC_VERSION_MAJOR
продвинут.
19.05.2004: проверил новый API на istcc'шном сервере. Работает!
Одно "но": некрасиво выглядит запись вида "m!drvname" -- плохо читается. Памятуя про давнюю идею -- посмотреть, как такие подсистемы помечаются в syslog/printk, допер -- надо ж писать "drvname[m]".
20.05.2004: переделал -- теперь "drvname[m]".
01.02.2006: сегодня при попытке собрать CX под
gcc-3.2.2 by Рома Кусков -- вылезло, что в cxdlib.h
используются varargs (va_list
), но
#include<stdarg.h>
отсутствовало. В gcc-2.96@RH73
и gcc-4.0.0@FC4, похоже, подобный файл (или определения?) включались
автоматом -- фиг знает почему, просто конфигурация отличается.
Короче -- #include
вставил. А вот
какие из этого надо общие выводы делать? Варианты: 1) Во ВСЕХ
файлах, где есть va_list
, вначале должно быть
#include<stdarg.h>
; 2) Надо в КАЖДОМ
многофайловом проекте иметь файлик PROJ_includes.h, у
которого, наряду с stdio.h, будет и stdarg.h. Мне
больше нравится первый. (Похоже -- пора все эти "общие" мысли
вытаскивать в отдельный файл -- сборник правил/умных
мыслей.)
06.08.2013: учитывая, что теперь у каждого устройства может быть своё имя (а вскорости оно станет обязательным!), удобно б было и в логи выдавать его, а не только drvname[DEVID].
После обеда: добавлено конкретно в zzz/cxsd_driver.c, выдаётся после квадратной скобки, так что общий результат выглядит как
drvname[DEVID]instname/CATEGORY
Не мега-красиво, конечно, но полезно.
Это понадобилось, чтобы в каждый конкретный момент нахождения в драйверах было известно, в ком из них -- т.е., его magicid. Потребовалось конкретно для логгера.
07.04.2004: исходная идея такая: надо найти все
точки вызова драйверов, и окружить их скобками {ENTER,LEAVE}_DRIVER().
Точки входа определяются методами из CxsrvDriverRec
,
поэтому ищем по их названиям (*_drv не в счет, поскольку они "не
принадлежат конкретным блокам"). Плюс, по именам тех массивов
методов, куда эти поля копируются при инициализации блока. В итоге
получили строку для поиска egrep'ом:
init_blk|term_blk|fd_io|do_rw|do_big|drivercallbacks|d_doio|d_dobigПоследние три -- это как раз имена "вторичных" массивов.
Нашел все такие места, и расставил вокруг них "скобки".
Кстати, скобки ставил не впритык к вызовам, а вокруг области, когда
сервер начинает делать некоторые вещи "для/от-имени драйвера".
Например, это касается фрагментов
"if(MustSimulateHardware)
...".
07.04.2004: ЗАМЕЧАНИЕ 1:
использование "скобок" без суффикса "_S
" выглядит
ненадежным: ведь в некоторых случаях возможен повторный вход в тело
драйвера, да еще и другого! Пример:
AddBigcRequest(->magic1) =>d_dobig[magic1]() =>ReturnBigc() =>callback() =>AddBigcRequest(->magic2) =>d_dobig[magic2]()И корректно отделить те 90% мест, где такая реентерабельность невозможна, от "подверженных" 10% -- весьма сложно. Плюс, а что будет дальше -- мало ли что поменяется.
Так что вывод: надо использовать ТОЛЬКО _S
-версии, а
не-_S вообще лучше искоренить.
07.04.2004: ЗАМЕЧАНИЕ 2: а
ведь действительно -- когда исполняются init_drv()
и
term_drv()
, то по всем признакам имеем "как бы
пространство сервера", и логгер ничего не сможет сообщить. И
аналогично с init_lyr()
и term_lyr()
.
Проект решения: в дополнение к {ENTER,LEAVE}_DRIVER() надо
изготовить еще и некую пару "скобок", которые будут манипулировать
дополнительной переменной -- хоть номером, хоть прямо строкой-именем
драйвера, и логгер, если обнаруживает, что
active_magic==MAGIC_NOT_IN_DRIVER
, то пытается определить
"префикс подсистемы" из той переменной.
28.04.2004: при поиске точек вызова драйверов
лопухнулся-таки -- при вызове драйверного таймаута в
DriverTimeoutCallback()
"скобки" отсутствуют. Причина
промаха -- забыл, что кроме методов, указанных в driverrec'е, есть еще
этот таймаут-callback, устанавливаемый совершенно отдельно.
Обнаружил -- когда глючил kshd485_drv, то логи от его "ping'ов" помечались как "NOT-IN-DRIVER".
В общем -- исправлено, "скобки" поставил.
magicid
, но еще и active_magic
.
Это хотя бы, пока не введена в действие "реальная" междрайверная защита.
24.05.2005: хмм... А ведь еще когда-то давно
(между BACKUP/cx.20030416_pre-cxdata_use и
STABLE/cx.20030603 -- видимо, судя по bigfile.html,
24.04.2003)) был изготовлен макрос CHECK_MAGIC
, который
сие умел. Но -- не используется! Понятно почему -- там проверяется
именно magicid!=active_magic
, а numblocks
не
при делах.
24.05.2005: короче --
CHECK_SANITY_OF_MAGICID()
и почти везде перешел на него --
кроме RegisterDriverFD()
, где оно пока может быть -1; там
собственный логгинг;
magicid
, выдается также и active_magic
.
Так что -- считаем что "done".
24.05.2005: дополненьице: подобавлял выдачу
active_magic
также в cxsd_bigc.c -- в
ReturnBigc()
.
DisconnectBlock()
параметра "какие ошибки выставить в
c_rflags[]".
02.07.2004: все это началось с cm5307_drv -- там стало очевидно, что драйвер должен иметь возможность объяснить, что же произошло.
Итого:
rflags_to_set
.
CXSRV_DRIVERREC_VERSION_MAJOR
от греха
подальше опять продвинут -- теперь он уже =7...
TermBlock()
.
CXRF_REM_C_PROBL
в виновника
торжества -- cm5307_drv.c, во все егойные DisconnectBlock().
03.07.2004: проверил -- работает. Так что -- помечаем как "done".
DefaultDrvlogMask
из командной строки при запуске сервера.
26.11.2004: ввел дополнительный ключик -- "-l".
Плюс, естественно, передачу указанного значения через environment -- шифровку и дешифровку. Еще раз убедился, насколько муторна сия процедура...
Можно указывать как "+category", так и "-category" -- оно будет корректно убираться из умолчательного списка.
28.02.2005: олух царя небесного! В парсинге
ключиков после "case 'l':
" забыл поставить
break
. А следующей альтернативой шло 'n'
--
norun. Вот оно и отваливало сразу же :-)
В остальном же -- оно работает, так что помечаем раздел как "done".
ReturnBigc()
, что там слепо
вызывается fast_memcpy()
, не глядя, что запрашиваемый
объем, возможно, 0.
25.02.2005: повставлял там во всех трех местах проверки. И "красоты для" посдвигал там все на 4 пробела вправо.
А правильно бы провести аудит ВСЕХ мест, где встречается
fast_memcpy()
.
В cxsd_driver.c::ReturnChanGroup()
предварительный облом при count<1
имеется.
В cx-server_bigc.c::FillBigcReplyPacket()
также в обоих случаях стоят предохранители на !=0.
В connlib.c проверка size!=0
есть.
Вот же ж елки -- в cxsd_bigc.c было единственное халтурное место! :-)
Осталось провести аудит в
connlib.c::GenericSendToClient()
-- там
используется fast_memmove()
, также "вроде бы" без
проверки, но там какие-то хитрые условия, так что скорее всего !=0
откуда-то следует, но это НАДО ПРОВЕРИТЬ!
А по-хорошему надо б провести аудит ВСЕХ использований
memcpy()
и memmove()
.
16.06.2005: да, а еще надо
провести аудит и всех использований memcmp()
-- как
показало сегодняшнее исправление в CheckBlockInfo()
.
16.04.2005: В первую очередь -- для будущего
"умного" cm5307_drv, когда он будет в инициализации делать только
connect()
, и может к первому вызову _rw_p() быть еще не
готов -- т.е., связь будет еще не установлена. А ведь этот
драйвер-мультиплексор даже и не знает, что реально за набор каналов он
обслуживает, так что и собственный кэш-массив запросов завести не
может... А так -- когда будет становиться готовым то вызовет
"ReRequestChannels()", а в неготовом состоянии _rw_p() можно просто
игнорировать.
Для самого же сервера это -- пара пустяков: пройтись по списку
каналов драйвера (так же интеллектуально, как
MarkRangeForIO()
), вызывая _rw_p() либо для чтения, либо
для записи, передавая значения, запомненные в некоем векторе (коего
пока нет, но нужен -- "c_next_wr_val[]
"? См. мысли за
13.06.2004).
29.06.2005: протокол реализации проекта -- ныне
"ReRequestData()
" (проводившейся в группе
"SetBlockStatus"):
24.05.2005: Приступаем.
- Создана Driver-API функция
ReRequestData()
. Она делает крайне немного -- выполняет проверку корректностиmagicid
'а, а затем вызываетReRequest{Chans,Bigcs}()
.- В cxsd_bigc.c+cxsd_module.h добавлена (пока пустая) функция
ReRequestBigcs()
.- Создан отдельный файл cxsd_channels.c -- как раз тот самый "внутрисерверный channel API". Объявления для него будут жить в cxsd_module.h.
- Первое, что туда попало --
ReRequestChans()
, также пока пустая.25.05.2005: Продолжаем.
ReRequestBigcs()
заполнена -- оно оказалось конкретно простым, и заключается в тупом проходе по списку больших каналов указанного magicid'а.Естественно, пока не проверена -- а стоило бы, махинации с большими каналами у нас какие-то навороченные, со списками, etc.
- Дальше перетащил из cx-server_channels.c в cxsd_channels.c то, что составляет собой "channel API":
RequestReadOfWriteChannels()
,HandleSimulatedHardware()
.- И, "главный" интерфейс --
MarkRangeForIO()
, с его helper'омShouldProcessChannel()
.- И --
FreshFlag()
также пришлось, инкапсуляции для, унести в cxsd_channels.c. И стал он там теперь уже НЕinline
.27.05.2005: Ага...
- Итак, после вчерашнего стало окончательно очевидно, как же именно должна будет выглядеть
ReRequestChans()
-- чрезвычайно похоже наNewMarkRangeForIO()
, только попроще:
- тоже с helper'ом -- назовем его, например,
ShouldReRequestChan()
,- но БЕЗ прописывания
c_wr_time[]
(зачем -- оно УЖЕ прописано),- и, естественно, БЕЗ сравнений на тему magicid -- оно и так работает в рамках одного блока.
30.05.2005: Угу...
- Заполнил
ReRequestChans()
.- И
ShouldReRequestChan()
оказалась совсем простенькой.21.06.2005: Тестирование.
Хмырк -- и в
ReRequestChans()
, по аналогии сMarkRangeForIO()
, "g++
" в цикле упаковки было забыто.
Проверка показала, что все работает, причем в обоих вариантах -- и
"косвенно", при переходе NOTREADY->OPERATING, и напрямую -- как
cm5307_drv в момент, когда connect()
успешно завершился.
Посему -- "done".
DisconnectBlock()
. "Not ready" -- устройство вроде
бы живо, но с запросами к нему лучше не приставать.
17.04.2005: в данный момент, фактически, есть
только "operating" и "offline", в который можно уйти только
безвозвратно. А хочется -- чтобы вполне "живой" драйвер мог бы
сигнализировать (для ВСЕХ своих каналов, а не только для тех, которые
спрашивают), что что-то не слава богу (например -- потеряна связь с
контроллером -- CXRF_REM_C_PROBL
); и для этого можно будет
вызвать
SetBlockStatus(magicid, CDRVS_NOTREADY, CXRF_REM_C_PROBL);
А когда связь восстановится -- то, соответственно, перейти в состояние
"OPERATING".
Highlights проекта:
d_status[]
переименовать в "d_loadok[]", а нынешний
d_active[]
-- в собственно "d_status[]". И формализовать
состояния константами: OPERATING, NOTREADY, OFFLINE. И, естественно,
ВЕЗДЕ считать "работающим" только при ==OPERATING, а не !=0.
c_rflags[]
, а
c_crflags[]
-- трогать незачем, ибо аккумулировать
флаги типа "offline" и "rem_c_probl" -- малоинтересно. Так что надо
соответствующим образом модифицировать и stop_block()
(ох, помню, где-то тут в bigfile-0001.html должны водиться
причитания/ругань на тему, что вынести прописывание *_*rflags[] в
отдельную функцию -- весь cx-server_dbase.c начинен
"!!!" об этом...).
ExecScan
к имеющимся вариантам символа состояния
драйвера '-':offline, '+':operating добавить еще
'.':notready.
Кстати: только что случайно обнаружил, что в bigfile.html за 30.11.2003 подобные мысли уже имеются -- разговоры на тему "SetBlock{Offline,Online}()".
29.06.2005: протокол, как концепция "SetBlockStatus()" реализовывалась:
23.05.2005: приступаем к реализации проекта.
- Переименовал
d_status
вd_loadok
, аd_active
-- вd_status
.- Введены константы
DRVS_{OFFLINE,NOTREADY,OPERATING}
- Все присвоения и сравнения делаются с этими константами.
ExecScan()
теперь сигнализирует о ТРЕХ возможных состояниях.- Методы
d_doio[]()
иd_dobig[]()
вызываются только в состоянииDRVS_OPERATING
.24.05.2005: Продолжаем:
- В дополнение к
TermBlock()
(OFFLINE) в cxsd_drvmgr.c введены функции "перехода в состояния"FreezeBlock()
(NOTREADY) иReviveBlock()
(OPERATING).- Собственно содержимое этих функций тривиально: первая вообще ничего не делает (ну, кроме собственно отмечания факта отключки в
d_status[]
и уставки флагов), а вторая -- пере-посылает запросы при помощиReRequest{Chans,Bigcs}()
.- Уставление битов в
c_rflags[]
вытащено в отдельную функцию --SetBlockRflags()
. И решил, чтоc_crflags[]
как раз НАДО ТРОГАТЬ.- Перевел также cx-server_dbase.c::
AddBlock()
на нее.В общем-то, на этом с "проектом
SetBlockStatus()
" и все -- дальше уже область ответственности "субконтракторов".
06.06.2005: м-м-м... А можно ли блоку
"рождаться" в состоянии NOTREADY? Т.е. -- он только вызвал
connect()
, который еще фиг знает когда завершится, и
хорошо бы СРАЗУ попросить сервера НЕ присылать запросы, а только
аккумулировать их...
Хотя -- проще самому иметь внутренний флажок "мы пока еще не", при котором молча игнорировать все запросы.
16.06.2005: О! Кстати -- а ведь в
ReviveBlock()
ТАКЖЕ надо вызывать
RequestReadOfWrChsOf()
! Т.е., реально оно должно быть
вызываемо при ЛЮБОМ переходе в состояние OPERATING.
Вставил, ПОСЛЕ ReRequest'ов.
20.06.2005: за-#if 0
'ил старый
вариант DisconnectBlock()
'а, заменив его на вызов
SetBlockStatus(,DRVS_OFFLINE,)
20.06.2005: сделал поле
d_stattime[]
, которое содержит время последнего изменения
статуса. Сейчас оно сделано не очень красиво -- обновляется не в одной
точке с d_status[]
, но, вроде, скоординированно.
В CXv4 надо будет "украсивить", "усимметричить" смену состояний.
Впрочем, пока-то время никак посмотреть нельзя :-)
22.06.2005: первичная проверка показывает, что все работает. Дальше -- надо провести "жесткие" тесты (типа запрещенных переключений).
Судя по проведенным тестам, концепция работает, и, пока что, оставшихся багов не заметно.
"done".
22.08.2005: насчет "где уставляется
d_stattime[]
" -- подправил, теперь везде, кроме
TermBlock()
оно ставится сразу за d_status[]
.
22.08.2005: хрю -- ReviveBlock()
НЕ
сбрасывал rflags!!! 30.11.2005: нифига! Как было
проверено еще с пару месяцев назад, ReviveBlock()
абсолютно чист -- проблема была в другом, как сегодня и выяснилось (см.
ниже). 13.09.2012: а чего б он их должен
был сбрасывать?!
Заодно обнаружил, что вообще махинации со всеми флагами -- rflags[],
rd_req[], wr_req[] -- делаются ГДЕ ПОПАЛО!!! А должны быть -- в
InitBlock()
(даже комментарий есть на эту тему в
FillChanProps()
, что "не перенести ли").
КОРОЧЕ: в CXv4 это все ДОЛЖНО быть именно в InitBlock()
(причем с правильной стороны от "if(!MustSimulateHardware)").
30.11.2005: сегодня решена давняя проблема, относившаяся, правда, скорее не к архитектуре "SetBlockStatus()", а являвшаяся багом в marcankoz_pre_lyr.c.
CXRF_OFFLINE
продолжал гореть.
Чего я только ни думал и ни делал: и полный дамп пакетов после reset'а просматривал, и чуть ли не "даиграммы хождения данных" в мозгу прокручивал -- все без толку.
marcankoz_fd_p()
по
получению от блока значений регистров
(пакет CANKOZ_DESC_READREGS
) делалось
ReturnChanGroup(..., rflags:=NULL)
. А при
rflags==NULL
там флаги каналов вообще не трогаются -- это
было введено для упрощения жизни О.Токареву (запись о сим имеется в
bigfile.html за ??.10.2003).
Т.е. -- при переходе в DRVS_NOTREADY
всем каналам
уставлялось CXRF_OFFLINE
, которое на регистровых каналах
никогда не сбрасывалось.
ReturnChan*()
обнуления соответствующих
флагов в случае, если rflags==NULL
, но не хочется, и вот
почему...
01.12.2005: прошелся по всем драйверам на тему "где ReturnChan*(...,rflags:=NULL)". Результат:
Но -- настоящие самураи так не поступают :-)
В них (код _rw_p()
у них, по причине простого
копирования, идентичный) все равно имелся цикл на случай
!=DRVA_WRITE, так что достаточно было сделать его всегдашним и ввести
пустую флаговую переменную.
init_b()
! -- был NULL
при
"уставке/выдаче" искусственного канала
KSHD485_C_NUM_STEPS
. Вставил указание нулевого значения.
Все остальные драйверы на вид в порядке. Засим считаем инцидент расследованным, проверенным, исправленным и исчерпанным.
01.12.2005: Ха, а "scan z"-то
показывает время смены состояния -- "01.01.1970,07:00:00"! Причина
очевидна --
d_stattime[]
никогда не инициализируется, а
только bzero()
'ится.
Чуть позже: странно! Посмотрел -- в InitBlock()
как раз
очень даже делается gettimeofday()
!
Блин, дятел -- так было только в режиме "simulate" -- с ключом -s! Ну так и правильно -- пусть так и будет.
RegisterDriverFD()
почему-то отсутствовала проверка на
fd<0
-- просто скопировал оную из
DeregisterDriverFD()
.
25.05.2005: эх, по-хорошему все-таки надо бы
подобные проверки делать #define
'ами, чтобы не было
множественных, потенциально отличающихся экземпляров.
С каким сценарием?
CXRF_OFFLINE
НИКТО и НИКОГДА не проставлял. Хотя
очевидно, что это должно делаться АВТОМАТИЧЕСКИ при самоубийстве блока.
25.05.2005: да -- просто вставил в
stop_block()
и FreezeBlock()
принудительное
|CXRF_OFFLINE
.
ResetBlock()
которое) необходимо делать то же самое, что
и RequestReadOfWriteChannels()
-- но только среди каналов
этого блока.
Можно сделать аналогично новому варианту
MarkRangeForIO()
, точнее -- ReRequestChans()
.
А правильнее -- просто изготовить "мясо", ReqRofWC()
,
которая будет принимать параметры "from" и "until", и уж ее услугами
будут пользоваться обе API-функции.
27.05.2005: угу -- создана внутренняя
ReqRofWrChs()
, в которую НЕ попали артефакты, так что она
маленькая и элегантная. И новый вызов --
RequestReadOfWrChsOf()
, заказывающий чтение только для
wr-каналов указанного блока.
24.05.2006: поскольку почти год как работает -- "done".
27.05.2005: угу -- создал
RemitChanRequestsOf()
.и
RemitBigcRequestsOf()
. С первой все просто: туда ушло
соответствующее содержимое из ResetBlock()
-- сброс всяких
флажков, включая rflags обоих сортов. 13.09.2012:
а с какой, собственно, стати СЮДА попал сброс флагов?!?!?! В
результате поимели дикие проблемы -- детали см. ниже за сегодня.
13.09.2012: "с какой стати" -- видимо, сделано
22-05-2008 по ошибке, которая осознана 30-11-2005.
А вот с "отказом" от bigc-запросов... Хреновенько... Там ведь по интерфейсу сами записи для постановки в список запросов предоставляют "клиенты", так что простым сбросом каких-нибудь флагов и не обойдешься. Так что, видимо, надо "правильным образом" чистить очередь -- возвращая "пустые" ответы. Фиг с ним -- пока это для реальных задач не требуется, так что оставим функцию пустой. Хотя в результате -- имеем гарантированный баг, что невыполненные запросы будут оставаться висеть...
24.02.2007: и даже хуже: если блок находится НЕ в состоянии Operating, то запросы будут "уходить в пустоту" и висеть навечно. Такое будет происходить с запросами к застрелившимся устройствам либо к незапущенным.
В CXv4 надо придумывать какой-то более стабильный/корректный/полный/straightforward способ работы с большими каналами.
25.02.2008: собственно -- а ведь реализовать
RemitBigcRequestsOf()
можно элементарно: сделав
ReturnBigc()
"от имени" драйвера, с
datasize=0
и ninfo=0
; и -- уже в состоянии
OFFLINE, чтоб не пыталось послать следующий запрос; в
SendBigcForExecution()
как раз стоИт нужная проверка.
21.02.2012: ах если б всё было так просто!
Ведь этот "ReturnBigc() от имени драйвера" уберёт из очереди ТОЛЬКО первый и совместимые запросы. Все остальные запросы с cachectl=FORCE останутся. Тут есть несколько аспектов:
Ну так и:
on_stop
, чтоб cxsd_driver.c мог "забывать"
свои запросы (они-то, в отличие от клиентских, никому не нужны).
req_sent=0
, и
тогда по ResetBlock()
отправлять первый запрос из очереди?
ResetBlock()
рядышком с
RequestReadOfWrChsOf()
поставить также и
ReRequestBigcs()
?
Вариант (а) более схож с функционированием обычных каналов, зато (b) проще в исполнении.
Пожалуй, пока сделаем (b), а при внедрении полного CX_CACHECTL_* постараемся перейти к (a).
Действия:
bigc_listitem_t.on_stop
.
inserver_req_bigc()
регистрирует в нём ссылку на
свою inserver_bigcreq_on_stop()
-- хотя и несколько
кривовато, "задним числом".
ClnBigcRequestChain()
, вызываемая
из stop_block()
.
RemitXXXXRequestsOf()
переехал из
ResetBlock()
в тот же stop_block()
, после
cleanup'а. Так выглядит корректнее -- по-хорошему, отзывать запросы
надо именно при остановке блока.
RemitBigcRequestsOf()
оставляем пустым.
RequestReadOfWrChsOf()
добавлен вызов
ReRequestBigcs()
.
13.09.2012: после того переезда Remit*() в
stop_block()
21-02-2012 у нас сдохшие устройства перестали
подсвечиваться болотным: у них флаги только выставляются -- и тут же
сбрасываются! Посему -- сейчас те сбросы просто закомментированы, а
вообще надо подумать, в какой точке НАДО этот сброс делать; четко
осознать диаграмму состояний; насчёт унификации некоторые слова есть в
cx-server_dbase.c::FillChanProps()
.
Реализовать -- в RstDevRflags()
, парной к
SetDevRflags()
.
14.09.2012: сделана, но пока не используется.
12.02.2014: вчера обнаружилось, что сервер -- ИПП'шный linac1:57 -- иногда падает по SIGSEGV в cxsd_bigc.c:
SendBigcForExecution()
, которому передаётся
item=NULL
от ReRequestDevBigcs()
.
req_sent=1
при curitem=NULL
.
ReturnBigc()
был просто цикл
"возврата клиентам", и потом уже ПОСЛЕ него сбрасывалось req_sent=0
(там на эту тему туча комментариев), и всё работало.
Потом же добавились новые обстоятельства, на которые та модель не была рассчитана.
В v4 аналогичный функционал (параметризованные запросы, их очередь) надо будет делать в парадигме "сервер отводит структуры", с реализацией "fast-SLOTARRAY" от cxscheduler'а.
values
в типе CxsrvBlkRWProc
почему-то был
int*
, вместо int32*
.
27.05.2005: мда... В 99% случаев это, конечно, несмертельно, но...
Короче -- в самой декларации исправил.
А потом пошел наводить аудит везде, где оные функции определяются...
Оказалось немного -- в server/drivers/*_drv.c и в
istc/drivers/*_drv.c. Все оказалось просто и несложно --
реально ВЕЗДЕ и так предполагалось int32*
, а ляпсус был
ТОЛЬКО в декларации.
Конечно, остались еще отокаревские драйвера, но о них задумываться не стоит -- вымирающий вид, до не-32-битных машин они явно не доживут (да и работать там наверняка не станут еще по куче причин).
RegisterDriverWrFD()
! (И, кстати, не забыть с
write-дескрипторами повторить те же махинации по cleanup'у, что с
обычными).
06.06.2005: приступаем. Highlights:
RegisterDriverWrFD()
и
DeregisterDriverWrFD()
.
driverwrcallbks[]
.
d_fd[magicid]=-1
уставляется только если в "другом" наборе
этот дескриптор отсутствует. (Главная кривизна!!!)
CHECK_IF_FDS_MAGICID_IS_SANE()
-- отличается от
CHECK_SANITY_OF_MAGICID()
тем, что допускает отрицательный
magicid
; а также CHECK_FD_SANITY()
и
CHECK_FD_AFFILIATION()
.
TermBlock()
Wr-дескрипторы прикрываются так же, как
и обычные.
Общее впечатление -- как-то кривовато это все выглядит, поскольку
одни и те же дескрипторы на чтение и на запись рассматриваются как
разные объекты (а "принадлежности"-то --
driverfd2magics[]
и, главное, d_fd[]
--
одни!), вместо элегантной архитектуры, имеющейся (уже полтора
года!!!) в cxscheduler'е. Впрочем -- эта реализация ведь является
"временным хаком", до перехода на cxsd, основанный на cxscheduler'е.
А так -- вроде все, надо переводить cm5307_drv.c на авто-reconnect'ную архитектуру и все тестировать.
10.05.2005: кстати, на будущее: в CXv4 надо
ОБЯЗАТЕЛЬНО при регистрации FD записывать его принадлежность, а при
дерегистрации -- снимать ее. А то сейчас разрегистрация уже
разрегистрированного отрабатывает прилично не из-за имеющейся защиты, а
оттого, что проверка имеется в GENERIC_FD_REMOVE()
.
21.05.2005: начал проверять.
Блин -- была малю-у-у-сенькая опечатка -- в проверке для вызова
callback'а проверял rfds
вместо wfds
(следствие копирования), а разбираться -- задолбался!!!
Вусмерть уже надоела эта старая архитектура -- с cxscheduler'ом все настолько удобнее и error-free!
29.05.2005: поскольку все проверено на cm5307_drv.c, и оно работает -- помечаем как "done".
15.11.2009: понадобится (для uspci-based
устройств) также и возможность заказать дескриптор на exceptions --
т.е., RegisterDriverExFD()
.
17.11.2009: да, вроде сделал, практически
копированием всей кухни с *wrfd. Единственное дополнение -- в
DeregisterDriver*FD()
в guard'е перед
d_fd[magicid]=-1
надо ВЕЗДЕ проверять все "остальные".
В canserver_drvmgr.c же добавлять не стал -- там и WrFD-то не реализован, за ненадобностью.
27.02.2010: ага, "вроде сделал" -- как же!
Тогда сделал только сами функции регистрации/дерегистрации, а в
cx-server.c::new_Run()
вставить их использование
забыл.
Сегодня понадобилось уже реально (dl200me@pci4624/uspci) -- так что доделал.
CxsdFrontendRec.begin_cycle()
, и даже
.end_cycle()
имеется.
12.06.2005: да, в CXv4 сделаем.
Кстати, затрат-то процессора на это не будет никаких -- один экземпляр сервера поддерживает у нас обычно ну, максимум, пару десятков блоков.
-1
". Это неправильно -- нужна символьная
константа!
10.06.2005: ну есть
cx-server_data.h::MAGIC_NOT_IN_DRIVER=-1
, и чо?
11.06.2005: ну, перенес константу в cxsd_driver.h.
Дальше воспоследовали поиски, где же требуется заменить на нее
"-1
". Оказалось -- в canadc40_drv.c пара
DoDriverLog()
'ов и единственный
RegisterDriverFD()
, все помеченные "/*!!!*/
",
и все в OpenAndSetupDevice()
. Вообще-то там еще море
вроде как незаконных ссылок на magicid
в
fd_p()
, но, поскольку дескриптор регистрируется с
-1
, то реально эти ссылки также бездрайверные. А впрочем -- в будущей
"переходной" версии marcankoz_drv.c повставляем законопослушное
MAGIC_NOT_IN_DRIVER
.
В dummy_layer.c менять не стал (мир его праху, в "новой" концепции layer'ов его вообще не будет по определению).
Короче -- "done".
ЗЫ: а вообще-то надо б назвать константу как-нибудь поадекватней -- типа "MAGICID_UNSPECIFIED". (Впрочем, это все только до появления layer'ов -- там "-1" станет незаконным.)
28.06.2005: в marcankoz_pre_lyr.c
использование MAGIC_NOT_IN_DRIVER
уж давно вставлено.
11.06.2005: детали идеи: в DriverRec'е указывается (опционально; ессно -- можно и NULL) некая NULL-terminated таблица, со строками вида
{"имя", proc, <some-param-spec>}Из консоли такие команды вызываются в стиле
call MAGICID COMMAND [PARAMS...]
Обработку параметров можно делать двумя способами:
char *params[], int num_params
Это проще, но менее удобно для драйверов, ибо тогда ОНИ должны
проверять корректность.
{int type; union {char *s; int i; double d;} v;}
Пара замечаний под конец:
13.01.2007: тогда-то эта мысля возникла, помнится, то ли на какой-то предзащите, то ли на каком-то семинаре (надо было записывать!!!).
И -- подобное есть в EPICS'е (Гусев пользуется); и в TINE также можно зарегистрировать "команду", которая исполняется из консоли сервера (там команды НЕ привязываются к устройствам, а просто расширение shell'а; может, такое также позволять для всяких frontend'ов и иных модулей?).
d_stattime
иметь и нечто, где записывается "текущее
состояние устройства" -- то, что он передал при смене статуса. А при
возврате в Online совать туда принудительный 0. И, соответственно, по
"scan" показывать и этот статус тоже.
11.02.2009: да, введено
d_statflags[]
, прописывающееся одновременно с
d_stattime
. Кстати, всё-таки устройство всех этих
*block() -- особенно TermBlock()
! -- довольно бардачно, в
частности, из-за MustSimulateHardware
, так что пришлось
подвыпендриться.
И в cx-server_execcmd.c::ExecScan()
также
добавлен вывод "%4x" младшего слова флагов. (Выглядит
результат такого вывода теперь, конечно, жутковато, но для разборок при
отладке -- пойдет.)
SetBlockStatus()
, ReRequestChans()
и
RequestReadOfWrChsOf()
:
Вылезла презабавная вещь с одним из CANDAC16 в системе магнитной коррекции: при загрузке режима его каналы иногда становились болотными с CXRF_OFFLINE. Полез разбираться -- оказалось, у блока связь глючит и он частенько присылает 0xFF,Reason=5"BusoffRecovery". По идее, marcankoz_pre_lyr.c на это реагирует дерганьем статуса (каковое и уставляет ), и должно бы вызваться чтение всех каналов записи, но -- почему-то не вызывалось.
Рыл, рыл -- нарыл: в ConsiderRequest()
стоит проверка,
что если c_time[chan]==current_cycle
, то и нефиг заново
пытаться читать. Т.е., поскольку команды записи проходили и давали
ответы в цикле N, а потом все в том же цикле происходил BusoffRecovery,
вызывающий пересчитывание, то запросы на эти каналы игнорировались.
24.05.2006: ну фто, стал врубаться, что да как да почему. Результаты изучения:
c_time[]
-- понятно, это еще с ucam'а идет.
c_time[chan]==current_cycle
была вставлена 06.03.2003 по
просьбе Олега (см. bigfile.html за указанную дату), ибо у него что-то
глючило.
Но она в любом случае разумна.
c_time[]
затронутых каналов сбрасывать в 0 говорится в том
же bigfile.html за 15.06.2003. Но сделано этого так и не было!!!
c_time[]
вообще
НИКОГДА не занулялось/не инициализировалось, даже при инициализации
блока!
ReqRofWrChs()
, НО:
в ReviveBlock()
вначале вызывается
ReRequestChans()
(каковой может сразу привести к возврату
данных и заполнению c_time[]
), а уж затем --
RequestReadOfWrChsOf()
. Так что такое "решение в лоб"
было б неудобно/нерационально.
По-хорошему, надо нулить в начале "транзакции"/"действия", каковым и являются "Reset" и "Revive"...
Итак, что же было сделано:
FillChanProps()
"приличия ради" добавлено зануление
c_time[]
.
c_time[]
всех каналов блока "в начале транзакции", но с комментариями "/*!!!
Temporary measure -- should be somewhere else */"
26.05.2006: наконец-то представилась возможность проверить (пока было тепло, блок почему-то не глючил, а как похолодало (!) -- стал глючить опять).
В общем -- да, ляп исправился, теперь все работает корректно.
В собственно регистрации-то все окей -- там стоят хитрые проверки, так что НИКОГДА -1 не используется как индекс в массиве.
А вот при вызове -- в cx-server.c::new_Run()
-- внахаловку стоИт передача d_privptr[magic]
(с d_busid[]
аналогично), что приводит к взятию -1-го элемента массива. Нехорошо!!!
16.02.2009: вставил guard'ы. Странно, что не нашел на это времени раньше -- там работы-то было на пару минут...
08.02.2009: вначале хотел вообще полностью поменять API таймаутов -- чтобы они теперь ВСЕГДА были множественными. Но, посмотрев на количество мест, где они используются, понял, что "лучше не стоит". Главная причина -- ведь старый-то вызов работает и как register, и как deregister одновременно, да еще и автоматом делается replace старого таймаута при указании нового, так что -- просто черт ногу сломит разбираться в драйверах, где что имелось в виду (например, в tsycam_drv.c -- ик!).
Еще более предыдущая мысль была -- сделать новый множественный механизм, а старые таймауты эмулировать им. Но -- это ж будет такая мутотень, как минимум придется всегда то ли резервировать 0-й номер для них, или помнить, какой именно номер занят таким "фиксированным" таймаутом...
Так что -- делаем именно ПОЛНОСТЬЮ НОВЫЙ интерфейс, никак не перекрывающийся со старым.
09.02.2009: разбирательство ясно показало, что
почти нигде не используются параметры bid
и
fd
, предоставляемые нынешним API. Единственное исключение
-- cm5307_drv.c&cangw_drv.c (shame on me!), где
используется bid
, но и там его несложно унести в privrec.
Так что -- надо сразу делать "по новому стандарту", уже присутствующему в cm5307_dbody.c:
typedef void (*CxsdToutProc) (int devid, void *privptr, int tid);
Замечание: а ведь эту хрень надо будет реализовать не только в cxsd_driver.c, но и в ppc_canserver/canserver_drvmgr.c. Впрочем, там-то уже всё на fdiolib'е, так что будет несложно.
16.02.2009: вроде сделал. Highlights:
d_devtouts[]
на
CX_MAX_TOUTS_PER_DEV=10
элементов, причем поиск свободного
начинается с 1, так что tid=0 невозможен.
d_devtinfo[]
-- поскольку даже cb-указатели иначе хранить
было б негде.
magicid
и tid
.
RegisterDevTout()
и
DeregisterDevTout()
.
stop_block()
гашение этих таймаутов также
вставлено.
Проверил -- похоже, пашут.
17.02.2009: и в canserver_drvmgr.c тоже портировал, почти что копированием. Но пока не проверял.
05.05.2009: заиспользовал еще в одном драйвере -- senkov_vip_src.c: там надо обязательно опрашивать канал "выходное напряжение ВИП" не раже раза в секунду, так что завел heartbeat-функцию, дергаемую с периодом 500мс.
25.08.2010: тьфу ты, давным-давно надо было ставить "done"!
25.08.2010: о-хо-хо -- а ведь инициализацию ни
d_devtouts[]
, ни d_devtinfo[]
никто нигде не
делает. Оно живёт и не глючит исключительно за счет того,
а) вначале там всё занулено; б) при гроханьи драйвера все
таймауты подчищаются, приводясь в начальное состояние. А вот в
canserver_drvmgr.c -- делается bzero() всего devrec'а (который
содержит и таймауты) при аллокировании devid'а.
09.08.2011: ладно, ладно -- вставил в
InitBlock()
парочку bzero()
.
09.08.2011: добавлен
RegisterDevToutAt()
-- аналогично драйвлетному API, в
основном -- в интересах cpci_fastadc_common.h.
09.02.2012@Снежинск-каземат-11: а почему
d_devtouts[]
отдельно, а не просто поле tout
внутри cxsd_devtinfo_t
? Наверное, потому, что
d_devtinfo[]
заводился позже.
Фиксим -- превращением в поле cxsd_devtinfo_t.tout
.
И в lib/rem/remsrv_drvmgr.c аналогично (ppc_canserver/canserver_drvmgr.c не трогаем -- он уже практически deprecated).
29.12.2012: кстати, ведь тогда, чуть меньше четырёх лет назад, всё было сделано для ДРАЙВЕРОВ; добавлять же таймауты NOT_IN_DRIVER'ам возможности нету. Касается как сервера, так и remsrv. А надо -- для sendqlib'а с портами.
(Что странно, осознано сие было, а записано -- нет, только в bigfile-0002.html за 18-02-2009, но НЕ здесь.)
Приступаем (параллельно в cxsd_driver.c и remsrv_drvmgr.c):
RegisterDevTout()
и RegisterDevToutAt()
:
"мясо" переехало в DoRegisterDevToutAt()
, а те двое лишь
проверяют параметры usecs/when.
CHECK_IF_TOUTS_DEVID_IS_SANE()
(копия FD'шной).
l_devtinfo[CX_MAX_TOUTS_PER_DEV]
(сервер),
lyr_tinfo[CX_MAX_TOUTS_PER_DEV]
(remsrv),
В результате, увы, на ВСЕ layer'ы таймаутов столько же, сколько на ОДИН драйвер.
DevToutCallback()
вместо d_devptr[devid] передаёт
NULL (в 4cx devptr заделан сразу в cxsd_hw_toutinfo_t).
А вообще -- пора просто переходить на унифицированную libcxsd/libremcxsd.
30.12.2012: проверено -- NOT_IN_DRIVER-таймауты работают, в обеих средах.
10.02.2009: понятно, в чем дело.
c_time[]
были далеко
в прошлом.
ResetBlock()
вызывает RemitChanRequestsOf()
,
сбрасывающий c_wr_req[]
. Поэтому
FreshFlag()
начинал возвращать 0 -- до следующей попытки
записи.
Но ведь по логике-то при инициализации блока -- когда делается
RequestReadOfWrChsOf()
-- и каналы записи тоже должны бы
начинать рассматриваться как "прочитанные бесконечно давно", вплоть до
РЕАЛЬНОГО получения данных.
Кстати, это уже было замечено еще при введении всей инфраструктуры
c_wr_time[]/c_next_*[] -- см. замечание за 10-02-2006 (РОВНО 3 года
назад :-)). И там уже предложено очевидное решение --
"Возвращать не-ноль-а-возраст при выставленном не только
c_wr_req[]
, но и если c_rd_req[]
".
Так что -- вставил в FreshFlag()
проверку
c_wr_req[chan] | c_rd_req[chan]
вместо былого просто c_wr_req[chan]
. Теперь надо
проверить и убедиться, что это работает, а других глюков не появилось.
10.02.2009: кстати, надо ведь при reset'е блока
обнулять c_wr_time[]
-- а то возраст считается от него, и
будет бредовым. Вставил -- в FillChanProps()
и в
ReviveBlock()
.
25.08.2010: да и тут тоже давно "done"!
Конкретно нужно для стенда у Ращенко:
И даже чуть хитрее: triggered должен уметь ПЕРВОЕ измерение "после" выкидывать, а отдавать ВТОРОЕ -- поскольку реально то первое могло быть
23.08.2010: работа состоит из 3 частей:
23.08.2010: пункт (1) реализован. В начале строки можно указывать имя в формате
:ИМЯ:
При неуказании ставится имя "", при указании -- проверяется
на дубли. Максимальная длина имени -- как и в v4, 31 символ (сделано
typedef'ом cxsd_devname[32]
), хранятся имена в
d_instname[]
.
27.08.2010: а ведь будет требоваться еще похожая функциональность -- не только ЧТЕНИЕ (которое тоже надо включить в API), но и ЗАПИСЬ в каналы из драйверов.
Понадобится, похоже, на сварке у Семенова.
Делать надо всё вместе -- функции типа "GetChanHandle(devname,chan_n)", "GetChanVal()", "SetChanVal()".
30.08.2010: угу -- такой набор функций реализован:
inserver_getref()
-- резолвинг пары
(имя_устройства,номер_канала) в глобальный номер канала ("ссылку на
канал"), именуемый ref.
inserver_req_cval()
-- запрос чтения канала (именно
-- ЗАПРОС, он ничего не возвращает).
inserver_snd_cval()
-- отправка значения в канал.
inserver_get_cval()
-- отдаёт ТЕКУЩЕЕ значение.
Запроса же -- НЕ делает. Предполагается, что оно будет использоваться
в evproc'ах.
01.09.2010: (начато 31.08.2010) добиваем:
inserver_add_evproc()
и
inserver_del_evproc()
предоставляют драйверам API
callback'ов. Термин "evproc" взят из v4'шной cda.
Evproc'у передаётся туча информации -- и ref, и magicid, и devptr (то, что в прочих вызовах -- privptr), и "privptr" (уже то, что указано при установке -- в стиле v4).
cxsd_timeout_t
и tout'ам:
chan_cbitem_t
:
next
и prev
для помещения в список;
callback
заполняется также "юзером", и
для него же есть поле privptr
, только им и используемое (а
в вызове никакого private-pointer'а нету).
(1)
Соответственно, реализация скопирована с
RegisterDevTout()
/DeregisterDevTout()
.
d_ievents[]
в 10
структур inserver_evprocinfo_t
, хранящих всю
"высокоуровневую" информацию (т.е. -- о вызове уже драйверова
callback'а). И в ней же, отдельным полем -- "низкоуровневая" структура
chan_cbitem_t
.
c_cblist[]
/c_cblist_end[]
.
AddChanCallback()
и
DelChanCallback()
, живущие в cxsd_channels.c и
описанные в cxsd_module.h (первое обновление с
31-05-2005).
(2) Они
практически скопированы с
AddBigcRequest()
/DelBigcRequest()
, включая
helper'ы AddToList()
и DelFromList()
.
11.10.2010: аналогично и для больших каналов
скопированы уже эти реализации
(3) inserver_add_bigcref_evproc()
/inserver_del_bigcref_evproc()
и
(4) AddBigcOnretCallback()
/DelBigcOnretCallback()
.
(5) inserver_req_bigc()
прямо в себе содержит аллокирование ячеек для отправки bigc-requests
(парной функции для удаления там нет).
ReturnChanGroup()
и ReturnChanSet()
делается вызов оных по приходу данных, ПОСЛЕ сохранения, но ДО сброса
пометки запрошенности и возможного вызова TryWrNext()
stop_block()
добавлено принудительное открепление
всех зарегистрированных callback'ов.
ЗАМЕЧАНИЕ 1: нынешняя инфраструктура НЕ рассчитана на то, что channel-callback'и будут добавляться (а главное -- удаляться!) в процессе вызова callback'ов для этого же канала. Чтобы сделать такую возможность -- пришлось бы вводить дополнительный список, маркировку cbrec'ов "пока в доп.списке" (для корректного удаления еще не переехавших в основной) и т.д.
ЗАМЕЧАНИЕ 2: учитывая количество скопированностей, надо бы при реализации внутренностей в v4 позаботиться об унификации:
06.11.2012: решение сделано и обкатано -- SLOTARRAY; в части случаев внедрено, а в другой части ждёт внедрения при переходе на будущие версии. Так что везде переводим на "was_attn".
02.09.2010: triggered_drv.c сделан.
Теперь надо всё это проверять.
03.09.2010: ага, начал проверять -- и первым же
делом столкнулся с тем, что в SimulateDatabase()
значение
numchans
сохраняется уже ПОСЛЕ чтения всей БД, в течение
же чтения -- там 0. А используется значение в данном случае именно В
ПРОЦЕССЕ.
Что интересно -- аналогичный прикол с numblocks
был
решен еще 07-03-2003 (см. bigfile.html), путём передачи
AddBlock()
'у указателя прямо на numblocks
(через #define cur_magic
).
Короче -- просто извёл всю ту фигню с якобы "парсеньем в другое
место, не трогая текущую БД", и теперь оно напрямую работает с
numblocks
, numchans
, numbigcs
.
Результат -- всё работает.
Теперь надо проверять уже на Ращенко и делать второй драйвер-юзер -- "мультиплексор записи" для использования на сварке у Семёнова.
03.09.2010: да, у Ращенко проверил -- пашет.
03.09.2010: и второй драйвер сделан -- multwrite_drv.c. Ему в auxinfo указывается набор каналов (до 10 штук) со значениями, которые надо записать по приходу 1 в его собственный канал записи. Синтаксис указания --
УСТРОЙСТВО.КАНАЛ=ЗНАЧЕНИЕ ...
Теперь и его надо проверить. 12.08.2013: ага, а
вот проверено-то оно тогда и не было, и работать не могло бы в принципе
-- уже на этапе загрузки ругалось на неправильный формат из-за
отсутствия строки p=endp;
. Сейчас же протестировано --
действительно работает.
P.S. Ох, чревато это -- на механизме inserver_*()
можно
наворотить очень многое, включая практически всю функциональность
хитрых EPICS'ных рекордов (всякие там fanout etc.). Например, уже даже
тут напрашивается возможность мочь динамически менять значения для
записи в управляемые каналы.
06.09.2010: по некоторым размышлениям -- да, можно наворотить очень многое. И, возможно, для БЫСТРЫХ операций -- НУЖНО.
Первое, что приходит в голову -- что неплохо бы сделать набор драйверов, которые бы позволяли реализовывать некую базовую логику, в т.ч. -- даже и условные конструкции (и вообще -- аналог EPICS'ного набора в сокращенном варианте). По крайней мере, для начала, для наших текущих потребностей (хотя можно обойтись для сварки и одним специализированным драйвером).
Но по здравому размышлению понятно, что нам-то надо идти своим путём, решать те же задачи более элегантно.
Так что реализовать надо будет примерно следующее:
ТАКАЯ штука позволит унифицированно исполнять всякие хитрые вещи, безо всяких спец.драйверов "условие".
Хотя -- понятно, что всё это мечты, мечты...
11.10.2010: на всякий случай, если этого ранее не было отмечено -- битым текстом: этого механизма НЕ БУДЕТ в прочих реализациях API cxsd_driver -- т.е., в cm5307_dbody и canserver_drvmgr. Потому как:
Но в v4 эта реализация всё-таки будет максимально всеобъемлющей, надеюсь. Просто в силу того, что там всё это делается из общих кирпичиков, и подселить оные в canserver не должно стать великой проблемой.
11.10.2010: добавляем аналогичный механизм и для больших каналов.
Ради этого сам API с простыми каналами немного попереименован -- чтобы ОСНОВНЫЕ (резолвинг, evproc'ы) варианты API с обычными (data, dataref) и большими (bigcref) каналами отличались только этими 4 буквами -- data/bigc.
Заодно, чтоб убрать конфликт с уже имеющимся внутренним API больших
каналов (давно уже есть bigc_callback_t
), во все имена
понадобавлено словцо onret
/Onret
. Так что
AddChanCallback()
/DelChanCallback()
превратились в
AddChanOnretCallback()
/DelChanOnretCallback()
.
По теме: поскольку ничего, аналогичного cda, в сервере нет, то получается гибридное решение:
inserver_req_bigc()
указываются args[nargs] плюс
data[datasize/dataunits].
inserver_get_bigc_data()
,
inserver_get_bigc_stats()
,
inserver_get_bigc_params()
.
Поскольку для AddBigcRequest()
надо предоставить
структурку bigc_listitem_t
, то на каждый драйвер
выделяется по CX_MAX_BIGCRQ_HOLDERS_PER_DEV
=10 оных. А
bigc-callback там тривиален, он просто "освобождает" ячейку, делая
is_used=0
.
Единственный недостаток -- там нет никаких проверок/ограничений, имеющихся в cxlib, cda и cx-server_bigc.c, так что драйвера могут понабесчинствовать...
Теперь пора проверять...
17.02.2012: во-первых, inserver*bigc давно проверено Роговским, что работает, так что "done".
Во-вторых, в интересах ist_xcdac20_drv.c увеличиваем количество CX_MAX_DATAREF_EVENTS_PER_DEV до 100.
20.02.2012: переименовываем
inserver*dataref*
в inserver*chanref*
(НО: в
4cx/ остаётся именно cda_dataref).
27.02.2012: triggered_drv.c переделан под обновлённую модель -- он теперь следит за статусами обоих подчинённых, и при их сдыхании пытается восстановить связь с ними.
Так что теперь этот драйвер будет способен работать даже под управлением remsrv.
28.02.2012: и multwrite_drv.c переделан аналогично.
13.03.2012: введены
INSERVER_DEVREF_ERROR
,
INSERVER_CHANREF_ERROR
,
INSERVER_BIGCREF_ERROR
.
Так что --
22.03.2012: нынешний механизм страдает неполнотой, он НЕ адекватен cda в отношении rw-каналов: ведь запрос inserver_req_cval() НЕ вызывает никакого реального чтения, так что и evproc в ответ на неё не вызовется, и драйвер при такой модели никогда не получит желаемого значения (разве только если кто запросит запись).
(Проблема вылезла при реализации tube-каналов в ist_xcdac20_drv.c.)
Корректный выход в данной ситуации -- драйверам-клиентам для rw-subord-каналов вместо запроса сразу делать чтение. Но как определить, что тот канал -- rw?
Так что вводим вызов inserver_chan_is_rw()
,
возвращающий значение c_type[ref]
.
22.03.2012: кстати, "более общее" соображение, на тему как в идеале должна быть организована работа с каналами -- хоть в cda, хоть внутрисерверно:
Когда "клиент" заказывает "серверу" канал, то его надо поставить в список читаемых (*), но также надо ОДНО значение прислать сразу -- чтоб у клиента появилось текущее значение.
- "Сразу" может быть и разным:
- Для rw-каналов -- именно сразу (но с оговоркой про свежесть -- если значение несвежее (с точки зрения
FreshFlag()
), то слать надо не сейчас, а дождавшись прихода ответа; т.е., как в пункте (b)).- Для ro-каналов -- сделать запрос, и по приходу данных слать. Но если данные уже свежие -- то как бы уже всё и готово.
...короче --
- Если этот канал свеж, то слать сразу, иначе -- сделать запрос, и по приходу ответа слать, на ПЕРВЫЙ раз плюя на "условия чтения" (типа частоты/отклонения).
- Отдельный вопрос -- что делать с "неживыми" каналами (от не-DRVS_OPERATING-устройств)? Видимо, тоже слать сразу, ибо ждать там нечего; а уж клиент сам по возрасту увидит "неадекватность", и сам примет решение, что делать.
- (*) "Список читаемых" -- это либо периодически, либо по изменению, либо по изменению более чем на сколько-то.
И вот нынешние ритуальные танцы с бубном "заказать evproc, но
также и сделать чтение (для ro -- req_cval(); для rw -- сразу
get_cval(), с проверкой возраста)"
(ist_xcdac20_bind_hbt()
, +req_chan()
) как раз
и есть реализация того поведения.
28.03.2012: некоторые уроки по результатам реализации драйвера ist_xcdac20.
Поскольку большинство ist-каналов связаны с cdac20-каналами, то напросилось использовать для них ЕДИНУЮ нумерацию. Вопрос, что считать "первичнее" и на чём основывать таблицу связей/маппирования.
SndCVal()
. Но это оказалось ИСКЛЮЧИТЕЛЬНО
неудобно, поскольку драйверу "командовать" надо в терминах CDAC20, и
получаемое оттуда воспринимать в них же. А и соответствие имеем не
1-к-1 -- в обоих наборах есть каналы, не имеющие "отражений".
ourc2sodc[]
).
В общем, это естественно: основная ВНУТРЕННЯЯ работа драйвера -- по
CDAC20, _rw_p()
воспринимает адреса по IST_XCDAC20, а там,
где эти вещи взаимодействуют/пересекаются -- производится
преобразование.
Но сколько же было диких неудобств из-за первоначальной неправильной схемы!
Переменные, касающиеся каналов самого драйвера (в его нумерации)
нехай имеют имена/префиксы ourc
(OUR Channels); касающиеся
каналов "ведомого" устройства -- sodc
(SubOrdinate Device
Channels).
Сейчас очевидно, что общая модель функционирования там должна быть похожей на seqexecauto -- просто последовательное исполнение (так что оно могло бы перебрать весь набор цепочки состояний за один вызов).
И отдельно -- лёгкая неясность, а как же всё-таки лучше делать "проверку разрешения перехода":
04.06.2014: в связи с необходимостью изготовить multiplexer_drv.c делаем переименование, для большей адекватности имён:
Хоть эта пара и не совсем симметрична (как их имена), но новые имена отражают суть получше.
06.06.2014: ну и сам multiplexer сделан, только проверить надо.
20.12.2011@Снежинск-каземат-11: делаем.
inserver_devref_t
-- тип ссылки (по использованию/смыслу
это просто magicid "жертвы").
inserver_get_devref()
-- получения ссылки по имени
(<0 -- нет такого девайса).
inserver_get_devstat()
-- вычитывание статуса. Причём
она возвращает статус ВЫПОЛНЕНИЯ ЗАПРОСА (0/-1), а запрошенный статус --
складируется по указателю.
inserver_add_devstat_evproc()
и
inserver_del_devstat_evproc()
-- уставка и ликвидация
"callback"'а на смену статуса.
Evproc'у сразу передаётся новый статус.
Если новый статус -- DRVS_OFFLINE, то все devstat-evproc'ы, целью которых является этот блок, автоматически снимаются; и постулируется, что ref на него становится недействительным. (Это правило введено в основном ради remsrv-драйвлетов, где после сдыхания блока его devid может получить кто-то совсем другой.) А вот data- и bigc-evproc'ы, увы, нет -- хотя стоило бы (это остаётся на совести драйверов-юзеров). 20.02.2012: всё-таки будем делать.
09.02.2012@Снежинск-каземат-11: тогда была сделана простейшая часть, НЕ касающаяся callback'ов -- резолвинг по имени и получение текущего статуса.
Продолжаем.
AddDevsOnchgCallback()
и
DelDevsOnchgCallback()
, объявленными традиционно в
cxsd_module.h, а живущими в cxsd_drvmgr.c.
Они скопированы с минимальными изменениями с *ChanOnret*();
по-устройственные списки зарегистрированных "низкоуровневых" callback'ов
хранятся в Причина -- оно могло сбрендить, если бы callback сам себя снимал (что и
делает devs_onchg): при этом Del*Callback() сделает bzero() структурке, и
А в cxsd_bigc.c УЖЕ было примерно так!
Причина -- что d_cblist[]
/d_cblist_end[]
.
CallDevsOnchgCallbacks()
, дёргаемая из
stop_block()
, FreezeBlock()
,
ReviveBlock()
.
на
cbp->callback(...);
cbp=cbp->next;
next_cbp=cbp->next;
cbp->callback(...);
cbp=next_cbp;
next
пропадёт.
CallDevsOnchgCallbacks()
, как изначально хотелось,
а в inserver_devs_callback()
.
DelDevsOnchgCallback()
делает bzero()
переданной ему структуре, а она еще нужна самому
inserver_devs_callback()
'у. Посему -- именно он и выполняет
авто-диссоциацию.
stop_block()
в секцию подчистки добавлено.
Традиционно -- надо проверять.
P.S. И не перенести ли реализацию *devstat* вверх -- ПЕРЕД dataref_evproc? Ведь оно как бы "основнее".
Парой часов позже: да, перенёс. Итого, план дальнейших действий по этим фичам:
20.02.2012: а ведь всё-таки НАДО для скопытившегося девайса снимать все поставленные на него chan/bigc-evproc'ы. Ровно по той же причине, по которой автоматом снимается и devstat-evproc -- в основном ради remsrv-драйвлетов.
Подчищать надо сразу ПОСЛЕ уведомления юзеров, чтобы затронутые могли спокойно подчистить свои запросы сами. Т.е., реально подчищаться будет только за "неряхами".
Есть технологическая проблема, аналогичная devstat'овой:
stop_block()
.
inserver_del_chanref_evproc()
'ем.
*_cbitem_t
поля "деструктор" -- какую функцию
вызвать для разрегистрации (а оно уж там пусть само разбирается).
21.02.2012: да, так и делаем.
{chan,bigc}_cbitem_t
добавлены поля
on_stop
(к devs_cbitem_t
-- нет, ибо
незачем).
"Юзеры" в cxsd_driver.c заполняют их.
ClnChanOnRetCbList()
и
ClnBigcOnRetCbList()
. Они проходятся по списку
callback'ов для указанного канала и вызывают у всех on_stop(), а если
оный ==NULL или всё равно ничего не сделал, то делают
DelXXXXOnretCallback()
сами.
stop_block()
же просто вызывает их для каждого
канала.
Остаётся же лишь проблема с "bigcrequest-holder"'ами, но она решится
автоматически при запинывании RemitBigcs()
. 21.02.2012: не совсем так, но именно в том разделе за
сегодня описано, как решено.
21.02.2012: надо запрещать какие-либо действия (отправка запросов и уставка devstat'а) в отношениии драйверов, которые имеют статус DRVS_OFFLINE.
Делаем. Запрещаются именно "активирующие" действия, а снятие callback'ов и просто чтение данных (get) -- нет.
21.02.2012: очевидно, что все эти сложности и танцы с бубном -- результат выбранной модели "callbacks", когда модули-клиенты слишком глубоко лезут в интимные детали реализации.
Вариант же на cda этих проблем будет лишён -- там всё на дескрипторах (вместо указателей и списков), так что не должно быть этих дурацких пересечений, странных условий и сложностей. Всё -- из-за более простого и узкого API, который также позволит иметь очень малое количество проверок (только на своей границе).
22.02.2012: ой ли -- а типа-cda'шный протокол inserver? Там не прямые ли вызовы получатся, так что в результате почти те же колёса на тему "что когда можно менять/освобождать, а что нельзя, воизбежание проблем". Но можно избежать таких вызовов, максимально приблизив "локальную" cda к обычной -- вместо вызовов завести pipe(), и уведомлять "клиентскую" часть о поступлении новых данных записью 1 байта. А чтоб там ничего не переполнилось -- помнить о записанности, и не писать больше 1; никаких race conditions на эту тему там всё равно не будет. 07.05.2014: насчёт сообщения через pipe см. также bigfile-0002.html по cda_d_local за сегодня.
21.02.2012: а "автоматическое" привязывание (bigfile-0002.html, "О междрайверных связях" за 06-05-2011) -- что мы на эту тему вынесли из опыта вышеописанной реализации?
22.03.2012: поскольку внедрено и проверено уже на всех inserver-драйверах, то "done".
10.06.2013: за прошедшие субботу-воскресенье (8-9) был добавлен небольшой convenience-API поверх inserver:
inserver_parse_d_c_ref()
-- парсинг ссылок вида
ИМЯ_УСТРОЙСТВА.НОМЕР_КАНАЛА.
11.03.2014: сделан также "адаптер"
inserver_d_c_ref_plugin_parser()
-- для возможности такие
ссылки указывать в auxinfo.
inserver_new_watch_list()
-- слежение за соседскими
каналами, с автоматическим привязыванием (см. предыдущую запись за
прошлый март).
Некоторые дополнительные подробности:
inserver_refmetric_t
. Для оптимизации под слежение может
указываться не один канал, а массив (потому и "_list
") --
количество определяется полем m_count
в 0-м элементе.
(s2v -- v2hw'шный драйвер, унифицированный с остальными fastadc по набору параметров.)
Для последних (там должны добавиться как минимум v3h_xa40d16 для ВЧ300 и v1k_xcdac20 для В1000) будем делать свой, отдельный модуль, включающий и машину состояний.
chan_is_bigc
!=0 (и callback тогда
будет считаться bigc_evproc'ом -- из-за их одинаковости даже warning'а
не будет).
12.03.2014: ага, а в собственно ДОБЫЧЕ ссылки оно не проверялось. Результат должен был получаться совершенно бредовый (спасло лишь то, что до сегодняшнего дня не использовалось). Пофиксено.
05.08.2013: кстати, это всё ("inserver2") сделано только в zzz/cxsd_driver.c, но НЕ в server/cxsd_driver.c (который скоро полностью уйдёт на покой).
06.06.2014: неудобно, что в этом API не предусмотрено
уведомление драйвера-клиента о смене состояния целевого устройства.
Это идёт от triggered/multwrite, не требовавших такого, а в multiplexer
оно занадобилось -- для "настаивания" на записанном значении. Так что
подготовлено, но закрыто #if'ом INSERVER2_STAT_CB_PRESENT
.
15.06.2014: ага, а еще ведь никто опрос каналов не
вызывает (по тем же причинам -- не надо было; а vdev делает), а для
bpmd_cfg_drv.c понадобилось, конкретно большой канал. Так что
пока вставлено в #if !INSERVER2_STAT_CB_PRESENT: оно сразу по
прибиндивании делает запрос на чтение (большого канала -- с
CX_CACHECTL_SHARABLE
).
17.06.2014: и еще страшенный ляп: в вызове
inserver2_try_to_bind()
были перепутаны
devptr
и m_p
. Драйвер triggered как-то
работал лишь из-за расположения rms[] в начале privrec'а.
01.07.2013: приступаем к связанной теме -- поддержке для "навороченных" драйверов, типа ist_xcdac20. Там потребуется как-то понаперемешанные НЕСКОЛЬКО разных сущностей:
...а вот ПАРСИНГ там не требуется, поскольку просто имя устройства и так несложно добывается.
Работы ведём в qult/drivers/, модуль -- vdev.[ch] (Virtual DEVice).
02.07.2013: слегка меняем имена в "простом" API, для совместимости с "навороченном", где они правильнее (в основном дабы не перекрывались со "ссылками" на девайс-владелец (devid)):
devname
->targ_name
;
devref
->targ_ref
;
chanref
->chan_ref
;
hbt_*
добавляем префикс bind_
.
02.07.2013: продолжаем пилить vdev. Делать реализацию всего желаемого функционала общим блоком кода (как в самом ist_xcdac20) -- повеситься, поэтому разделение следующее:
...или несколькими -- технология та же с "m_count".
04.07.2013: vdev.[ch] более-менее сварганен, и вариант драйвера ИСТа на нём тоже сделан.
Учитывая навороченность даже observer'а, унифицировать его с inserver2 вряд ли бы удалось, слишком уж разнятся.
12.07.2013: теперь допилена уже вся троица драйверов, включая хитрый v3h_xa40d16, пользующийся 2 базовыми устройствами, да еще и вычисляющий набор целевых каналов в зависимости от номера ВЧ300 (0-7); вышло монструозненько (и наверняка косячно).
Проверять будем в августе, при остановке ВЭПП-5 -- тогда и переведём всё на x-CAN-драйверы и на v1k,v3h вместо прямого управления.
13.09.2013: проверялось только сейчас (поскольку без дистиллята ИСТы не работают), на wst_xcdac20.
Был багчок -- в vdev_trgc_cb()
при тесте "можно ли
переходить к следующему состоянию" проверялись "интересные каналы" из
описания не СЛЕДУЮЩЕГО состояния, а текущего.
17.09.2013: вылезло некоторое неудобство:
18.09.2013: да, сделано "is_suffering";
проверено, ругается в лог. 12.03.2014: а в
обычном inserver2 не было. Хотя и нужно с точно теми же аргументами.
Поскольку реализация внутри самого сервера, то добавлять поле в
inserver_refmetric_t
нельзя (изменится ABI); поэтому пока
сделано хитрО -- признаком "NOT_SUFFERING" служит значение 0 в поле
chan_ref
. Потом, при очередном большом апгрейде, можно
будет переделать.
25.09.2013: поскольку новая схема проверена на работающесть, то старый "самостоятельный" драйвер ist_xcdac20_drv увольняем, и переименовываем в него vdev-based wst_xcdac20.
25.09.2013: надо б и большие каналы сюда приспосабливать -- для возможности табличного перехода источников (и чтоб знание об этих таблицах шло через сами vdev-based драйверы, а не мимо них в CAN-блоки).
30.09.2013: кстати, вот нефиг было экономить на v3h_xa40d16, выделяя ему всего 20 каналов.
Надо делать унифицированную штуку "vdev-based источник", чтоб у всех источников были одинаковые каналы (просто не все у всех рабочие), по аналогии с fastadc и kozdev.
01.10.2013: уже не раз замечены странности с не-переподключением vdev-драйверов к базовым при ожитии тех.
Конкретно сегодня: восьмёрка v3h_xa40d16 (ВЧ400) на ring1:32, после оживления базового xcandac16 (а он самоубивался в DEVSTATE_OFFLINE вследствие проблемы с inpktsize) НЕ восстановилась. А вот это (в отличие от двух других сегодняшних странностей) уже можно попробовать воспроизвести.
24.11.2013: возможно, и эта проблема была (как и в remdrv) из-за ляпа с несбросом clp_tid=-1 в cxsd_fe_cxv2.c.
11.12.2013: (как бы не сюда, но раз уж тут всё про эту троицу драйверов) сделано, что ist,v1k,v3h воспринимают команду SWITCH_ON не только из состояния IS_OFF, но и из INTERLOCK (при этом теперь во ВСЕХ накладывается дополнительное условие, что блокировки должны НЕ гореть).
Смысл -- удобство юзеров: зачастую блокировки сбрасываются вручную на источнике (а на v3h ТОЛЬКО так), и драйвер об этом не знает, но юзеру жать [R] в под-окне [...] очень уж муторно. Вот и сделано, что из "безопасного" состояния (а INTERLOCK без блокировок -- для железки тот же IS_OFF) драйвер позволяет включаться сразу.
14.02.2014: vdev переехал в cx/, lib/vdev/, для возможности быть использованным не только в qult/, но и прочими (bpms/).
25.02.2014: близкая тема: Федя время от времени возникает, что надо бы мочь ограничивать СУММАРНЫЙ ток, уставляемый в несколько разных каналов ЦАПа, мотивируя желание тем, что управляемые этими каналами источники питаются от единого БП, имеющего ограничение по току.
На уровне САМОГО драйвера (да и сервера) такое не реализовать никак -- не то место. Но не есть проблема сделать драйвер-прокладку, чтоб всё шло через него, а он уж может считать сумму и ограничивать запись, приводящую к её превышению. Проблем только две:
Но это решается просто: надо запросы на запись запоминать, вместе со значениями, и при обработке следующих к сумме прибавлять не "последнее известное записанное", а с "запрошенное на запись". И флаг "запрошено" сбрасывать по возврату актуального значения.
Максимально общо, но громоздко и неудобно в случае, когда все каналы в ОДНОМ ЦАПе (что чаще всего).
Какой из вариантов выбрать -- надо смотреть по конкретной задаче. Сделать любой из них несложно.
Вечером: как утверждает Федя, может понадобиться случай померзее -- "в одном блоке две группы устройств и у каждой группы может быть свое ограничение (причем разное)"...
12.05.2014: отдельное замечание: а что, если понадобится так ограничивать сумму уставок источников, являющихся разными vdev-драйверами, работающими через разные устройства (пример -- ИСТы)? Ответ: видимо, ограничение надо будет ставить "выше" -- чтоб клиенты писали в каналы драйвера-сумматора, а тот уж переадресовывал бы в _CHAN_OUT'ы тех vdev-драйверов. Да, тогда получится уже 3-уровневый бутерброд.
07.05.2014: битым текстом: устройство
vdev_set_state()
таково, что его МОЖНО вызывать из
swch_to-методов, и всё отработается корректно (только
вычитывание state-related channels будет сделано несколько
раз).
08.05.2014: и даже более того:
SwchToON_UP()
(перед началом поднятия тока; и также в
ist_xcdac20_rw_p()
при записи уставки) оно проверяет, что
если полярность надо менять, то отправляется в состояние
IST_STATE_RV_ZERO, начинающее цепочку смену полярности, которая
заканчивается переходом обратно в IST_STATE_SW_ON_UP
(так что всё корректно отработается даже если посреди этой
цепочки юзер передумает и опять сменит полярность -- при повторном
входе в ON_UP оно еще раз проверит и опять уйдёт на
переполяризацию).
SwchToDETERMINE()
, и прекрасно там работает, переключаясь
в определяемые там состояния (вместо былого хака внутри
ist_xcdac20_sodc_cb()
, из-за якобы невозможности
переключения в другое состояние из swch_to).
15.06.2014: в vdev'е надо бы кое-что добавить:
state_important_channels[]
и
ourc2sodc[]
-- каждый драйвер делает сам, а оно длинное.
find_sdp()
-- почему у каждого драйвера свой
экземпляр?
vdev_set_state()
корректно
отрабатывал состояния с delay_us==0 и sw_alwd==NULL, и сам бы
перещелкивался в следующее состояние (и так пока можно). Аналогично
sea.
23.06.2014: ясно, в чём проблема с inserver2,
когда нет возможности удовлетворять постоянно растущие требования по
наполнению inserver_refmetric_t
: в самом подходе с
использованием структуры вместо ID.
Т.е., правильным вариантом было бы запрашивать у сервера аллокацию "ref id", который потом заполнять как надо отдельными вызовами. И vdev тоже касается.
24.04.2012: смысл -- ибо достало. Еще что у разных сред исполнения свои cxsd_driver.h, и приходилось химичить и надеяться на включение нужного.
#ifdef TRUE_CXSD_DRIVER_H
делается #include TRUE_CXSD_DRIVER_H
, а собственное
содержимое пропускается.
28.04.2012: э-э-э -- а почему cangw_cxsd_driver.h?!?!?! Ведь это фактически remsrv'шный вариант!
Так что -- он переименован в remsrv_cxsd_driver.h и переселен в cx/src/include/.
28.04.2012: несколькими часами позже: учитывая, что он -- просто ОБРЕЗАННЫЙ вариант обычного cxsd_driver.h (с совпадающими числовыми кодами), вопрос: а нафига он вообще нужен? Только чтоб лишнего не могло компилироваться?
13.09.2012: реализовано модификацией
CHECK_SANITY_OF_DEVID()
, которая дополнена проверкой на
d_state[devid]==DEVSTATE_OFFLINE
. Обычная же проверка на
нахождение devid в диапазоне [0,numdevs) вытащена в отдельную
CHECK_SANITY_OF_DEVID_WO_STATE
-- исключительно для
SetDevState()
.
Эта модификация сделана не только в cxsd_driver.c, но также
скопирована в cxsd_channels.c. А
cxsd_bigc.c::ReturnBigc()
традиционно имеет свой
набор проверок, в т.ч. и devid, который был дополнен тестом на OFFLINE.
16.09.2012: тогда был пропущен один аспект:
CHECK_IF_FDS_DEVID_IS_SANE()
-- он не проверял живость
устройства. Теперь проверяет.
16.09.2012: на аналогичную тему проапдейчен и remsrv_drvmgr.c:
dr->in_use
)
там была изначально. Но:
CHECK_IF_FDS_DEVID_IS_SANE()
и CHECK_FD_SANITY()
-- их использование было
закомментировано. Добавлены, раскомментировано.
А вот CHECK_FD_AFFILIATION()
и будет отсутствовать, по
причине иной модели работы с дескрипторами.
RegisterDevTout*()
.
SetDevState()
.
ReRequestDevData()
.
Добавлены.
do{...}while(0)
.
Надлежит проверить эту модификацию (а то предыдущие исправления в данной обласьт 13-09 показали много неожиданных чудес).
sato
перестало работать, и
приводило к SIGSEGV'у.
13.09.2012: симптомы оказались знакомыми по предшествующей разборке с падением по таймауту -- вызов метода с devptr==NULL.
Обусловлено такое изменение оказалось как раз предыдущим пунктом --
что теперь мёртвые устройства не могут и RegisterDevPtr() делать, а в
InitDev()
ОТСУТСТВОВАЛО заполнение
d_state[devid]
ПЕРЕД вызовом init_dev()
.
Исправлено.
02.10.2012: это в двух местах -- в сервере и в remsrv.
AddBigcRequest()
.
ProcessPacket()
, прямо в самом вызове.
29.12.2012: да вроде всё нормально, так что "done".
ReturnChanGroup()
'у count
=0 (см. идею за
21-11-2012).
23.11.2012: собственно:
RETURNCHAN_COUNT_PONG
. Это и наглядность/искабельность
повышает, и позволит в будущем при надобности добавлять еще
специфичности (выходит этакий "обратный ioctl").
Из-за до сих пор недоунифицированного API добавлять пришлось аж в 3 места: cxsd_driver.h, remdrvlet_c.h, cm5307_dbody.h.
cankoz_add()
и
nkshd485_drv_common.c::piv485_open()
.
03.12.2012: да, в canserver'е это работает, REMDRVP_PING отсылается. Так что "done".
Такое впечатление, что "предыдущий" присылается не устройством, а софтом (сервер? драйвер?).
02.06.2016: протокол:
03.06.2016: Хотя конкретно этот эффект, возможно, как-то и
вызван тем, что pzframe_gui::RgParKCB()
уставляла параметр
большого канала.
magicid
к
DoDriverLog()
.
08.05.2004: во-первых, там делались попытки
максимально подходящим образом имитировать нынешнее поведение
DoDriverLog()'а -- печатались magicid
и
__FILE__
. Это убрано, код упростился и украсивился.
Во-вторых, поскольку уже давно реализован вызов
DisconnectBlock()
, а в сим драйвере было несколько мест,
требующих его в комментариях (собственно, ради этого драйвера вызов и
родился), то вставил его.
11.06.2004: исправил -- перенес.
20.06.2004: для начала, имеется несколько мыслей на тему "как это надо сделать":
(Угу -- структура директорий: drivers/cm5307/, в которой есть common, содержащая "общие" вещи, и поддиректории uclinux_drvlets/ и ppc_drvlets/.)
20.06.2004: начинаем изготавливать cx/tools/c5307-ppc/. Сценарий:
viper:/tmp/cm5307-ppc/tools/lib% ls -l total 1464 -rw-r--r-- 1 bolkhov lab5 6741 Feb 17 06:26 crt1.o -rw-r--r-- 1 bolkhov lab5 1544 Feb 17 06:26 crti.o -rw-r--r-- 1 bolkhov lab5 1213 Feb 17 06:26 crtn.o drwxr-xr-x 3 bolkhov lab5 4096 Feb 17 03:32 gcc-lib/ -rwxr-xr-x 1 bolkhov lab5 90060 Feb 17 06:37 ld-2.3.1.so* lrwxrwxrwx 1 bolkhov lab5 11 Jun 20 20:08 ld.so.1 -> ld-2.3.1.so* -rwxr-xr-x 1 bolkhov lab5 1365696 Feb 17 06:37 libc-2.3.1.so* -rw-r--r-- 1 bolkhov lab5 6880 Feb 17 06:37 libc_nonshared.a -rw-r--r-- 1 bolkhov lab5 193 Feb 17 06:26 libc.so lrwxrwxrwx 1 bolkhov lab5 13 Jun 20 20:08 libc.so.6 -> libc-2.3.1.so*
CROSS_COMPILE=ppc-linux \ ./cm5307-ppc/tools/bin/ppc-linux-gcc -o FILE.EXE FILE.c \ ./cm5307-ppc/tools/lib/ld.so.1
25.06.2004: что сделано за прошедшее время:
Из общих вещей: вместо суффикса ".driver" теперь используется ".drvlet".
27.06.2004: полностью запинал "прототип" компилируемости ppc-drvlets.
Пришлось еще повозиться с include-директориями, плюс доделать правила для перевода %_drv.o->%.drvlet.
Пока что include берутся из /tmp/mamkin -- надо будет корректненько поразбираться, чего ж подобавлять в cx/tools/cm5307-ppc/include/, а то все копировать -- жаба душит, там 9М.
И -- сделан симлинк
cm5307-ppc/include/camac-> Заодно, кстати, устранил warning gcc-3 "deprecated use of label
at end of compound statement" в rrund.c.
Обнаружился еще один глюк этого gcc-for-ppc860: по "-MM" этот кретин
пишет в dep-файлы ВСЕ include-файлы -- не только
#include"...", но и #include<...>. Видимо, для
него "системность" определяется не "типом кавычек", а тем, что файл
найден не в стандартных путях, а по "-I". Расследование в документации
(info) показало, что этот идиотизм введен в gcc-3.
Что еще надо будет сделать:
28.06.2004: Разобрался с проблемой
"-MM
": в доках (info) есть упоминание, что директории,
добавляемые к "second include path" -- при помощи
"-idirafter
", считаются системными. Поменял
соответствующие "-MM
" на "-idirafter
" --
помогло. (Расследование показало, что то же самое сделал бы и ключ
"-isystem
".)
29.06.2004: разобрался с проблемой include'ов.
"Не было бы счастья...". Дело в том, что идея прогнать make под
"strace -f", а потом оттуда grep'нуть все на тему /tmp/mamkin,
провалилась. Хрен -- тот gcc вместо обычного fork()
пользует vfork()
, трассировка которого даже под
"strace -F" ненадежна.
В результате -- вернул "-I" вместо "-idirafter", и оно мне само сгенерило список интересующих файлов. (Пришлось, правда, сделать один прогон "руками", поскольку для драйверов .d-файлы не создаются.) А потом --
cat *.d /tmp/file | \ perl -pe 's/\s/\n/g' | \ grep mamkin | \ sed -e 's|/tmp/mamkin/ppc860/linux/initrd_big/usr/include/||' | \ sort -u
Из получившегося списка и последующего разбирательства следовало, что можно скопировать ЦЕЛИКОМ директории {bits,gnu,netinet,rpc,sys}. А вот {asm,linux} являются симлинками в ../src/kernel/include, и объемчики у них -- около 1M и 5M соответственно. Посему оттуда было взято ровно то, что требовалось -- asm/{errno,ptrace,sigcontext/socket}.h и linux/{errno,limits}.h.
Плюс после здравого размышления решил использовать ключик
"-isystem
" вместо "-idirafter
" -- так
мнемоничнее.
29.06.2004: итого -- откомпилировал, провел испытания.
Во-первых, вылезла пара вполне предсказуемых багов -- их исправил. После чего -- система работает.
А во-вторых, пришло понимание, что надо сделать несколько дополнений в систему cm5307_drv<->rrund+drvlets:
CM5307DLP_CONFIG
?
01.07.2004: кстати, замечания для, что надо добавлять в /etc/rc.sh на cm5307-ppc:
/bin/mkdir -p /mnt/host echo "/etc/rc.sh: mounting /mnt/host" mount -t nfs -o nolock,ro 192.168.2.254:/export/linac1 /mnt/host echo "/etc/rc.sh: starting rrund" /mnt/host/oduser/pult/lib/server/drivers/cm5307_ppc_drvlets/rrund -d >/dev/null 2>/dev/null &
Плюс, естественно, в том же файле прописывается и IP контроллера -- в строке
(все ppc идут с адресами вида 192.168.{2,8}.1NN -- адреса с сотни, подсеть 2 на linac1 и 8 на ring1).ifconfig eth0 192.168.2.100 netmask 255.255.255.0
02.07.2004: сделал, что rrund возвращает ошибку при ненахождении файла для exec()'а. Для этого:
CM5307DLP_RRUNDP/*'RrdP'*/
.
#include"cm5307_drvlet_proto.h"
(так что теперь rrund НЕ
независимая/самостоятельная программа), а во-вторых -- сделал там
ReportProblem()
по образу и подобию
cm5307_drvletbody.c::vDoDriverDebug()
.
Заодно обнаружил, что rrund почему-то при проблемах при чтении имени
программы для запуска сразу делает exit()
-- тем самым
отваливаясь целиком. Поправил.
15.07.2004: еще махинации с *_drvlets/LocalRules.mk (необходимость в них вылезла при написании CAMAC-benchmark'а на основе cm5307-camac, во внешней директории):
Решение: перенес сей include в ПОСЛЕ Rules.mk -- и вообще в раздел "Out-of-tree usage support", в ветку if'а "$(OUTOFTREE)"=="" и он теперь выглядит как
include $(D_CM5307_PATH)/common/CommonRules.mk
DRVSRCDIR
, а вместо того оно было в
LocalRules.mk, где заключалось в if (это -- еще наследство
uclinux_slaves), то НИКАК нельзя было делать директории БЕЗ абстрактных
драйверов, только с утилитами.
Решение: DRVSRCDIR определяется теперь в Makefile, а if из LocalRules.mk убран. Т.о., поскольку в AbstractCamacRules.mk есть соответствующая проверка, можно просто присваивать DRVSRCDIR'у пустоту.
LOCAL_INCLUDES
стояло "-I$(DRVSRCDIR)" (что после
предыдущего пункта стало приводить к просто "-I" в командной строке,
поглощающей следующий ключик), то пришлось вынести в отдельный
ifneq "$(DRVSRCDIR)" ""
строчку
LOCAL_INCLUDES+=-I$(DRVSRCDIR)
.
Решение: сделал "выходной" параметр
CM5307_CAMAC_O=$(MY_PATH)/cm5307_camac.o
, теперь даже
*_drvlets/Makefile ссылаются для cm5307_alfo{,.elf}
на него.
Все эти приколы присутствовали в той версии, которая попала в BACKUP/cx.20040714.
19.08.2004: старая директория uclinux_slaves/ удалена. Если вдруг возникнет необходимость -- она есть в STABLE/cx.20040802. 13.02.2007: ага, а в src/EXPORTSTREE-то она оставалась! Также удалил.
Поскольку и ppc_drvlets, и uclinux_drvlets уже готовы, помечаем раздел как "done".
23.08.2004: совсем забыл -- надо было грохнуть и server/drivers/m5200_{drv.c,drvproto.h}. Сделано.
18.10.2004: обнаружилось, что в cm5307-ppc-drvlets нельзя использовать математические функции, там нет ни math.h
Пробуем решить: копируем ppc860/linux/initrd_big/lib/libm{.so.6,-2.3.1.so} в tools/cm5307-ppc/tools/lib/. Плюс, делаем симлинк libm.so->libm.so.6. И не забываем про include/math.h. Тесты показали, что все работает.
21.10.2004: и еще обнаружилось, что "внедеревные" uclinux- и ppc-драйверы не компилировались -- не находили std_abstract_iface.h. Пофиксено -- в оба LocalRules.mk добавлено -I$(ABSTRACTCAMACRULES_PATH).
11.05.2006: обнаружилось отсутствие файла ctype.h. Он добавлен.
04.06.2006: также обнаружилась (при добавлении в
cm5307_camac.h #include<sys/ioctl.h>
)
отсутствие asm/ioctls.h, тащащего за собой
asm/iotcl.h (скопированы из
initrd_big/usr/src/linux-2.4.22/include/asm-ppc/), плюс
termios.h.
14.05.2010: странная штука: в rrund.c
при неуспехе exec()'а стоял ReportProblem()
в
fd
-- при том, что к этому моменту уже было сделано
close(fd)
. Самое непонятное -- что до хоста сообщение
доходило!!! Как, спрашивается?!?!?!
Сменил там fd на 1
.
(Обнаружил сие при создании v4'шного rrund.c и аналогичного кода в новом remdrv_drv.c.)
Например, если мы жмем кнопку [Reset] у козачиного CANDAC16, то -- в сервере (при -wY) будут помниться уставленные (или -- считанные при запуске) значения, а в блоке -- будут стоять нули.
Если же по получению пакета DESC_GETDEVATTRS=0xFF
выполнять пересчитывание, с дальнейшим ReturnChan*(), то все
автоматически скогерентится.
01.02.2006: хо, так именно эта технология и реализована еще летом 2005-го в новом can-драйвере! Так что -- "done".
Вот понадобилось отладить ipp в отсутствие живой установки -- а хрен. А так бы, раз -- и готово.
И еще -- пора перевести sim на psp.
02.02.2008: поскольку давным-давно (уже два года)
вместо "собственных мозгов" там используется
StdSimulated_rw_p()
, то -- withdrawn. Но это "пока" -- в
будущем какая-то "умная" симуляция может понадобиться.
P.S. А sim-то на psp перевел, да.
DEFINE_DRIVER_REC()
'е размер оного указать забыл, там
стояло 0, и cx-server великолепно segfault'ился. Пофиксил :-).
Причина -- драйвер-то не знает, какой канал у него что, и всегда
возвращает этот номер, в т.ч. -- и при "инициализационном чтении" от
RequestReadOfWriteChannels()
. (В смысле -- там
была заложена логика, что DRVA_WRITE -- это всегда для каналов записи,
а DRVA_READ -- только для каналов чтения.)
(Особо весело было это наблюдать на программе vacuum -- где в
четных строках все было OK, а в нечетных -- горели alarm'ы, поскольку
по протоколу KNOB_B_IS_ALARM проверяется именно младший бит --
CX_VALUE_LIT_MASK
.)
27.01.2006: вообще-то правильный подход тут -- это сделать В СЕРВЕРЕ специальный API для симуляционных драйверов, типа "исполни-ка симуляционный запрос на r/w такого-то канала". Ведь симуляционные драйверы-то ничего про раскладку своих каналов не знают, а сервер -- знает. Плюс -- в нем и так есть симуляция, так вот можно все это и унифицировать -- тогда и будет полная идентичность симулированных данных, что при ключе -s, что при sim/noop.
28.01.2006: ох-хо! А не ввести ли просто
"стандартный метод для симулянтов" -- StdSimulated_rw_p()
,
который можно просто сразу указывать в
DEFINE_DRIVER_REC()
?
01.02.2006: сделал. Именно по такому проекту. В
StdSimulated_rw_p()
для каждого канала сначала вычисляется
(на основе его глобального номера и соответствующего этому номеру
типа (r/w)) надлежащее значение -- по формуле из
HandleSimulatedHardware()
и в зависимости от
DRVA_READ/DRVA_WRITE, а потом делается ReturnChanGroup()
.
В noop_drv.c и sim_drv.c собственные
_rw_p()
выкинуты, и вставлены ссылки на
StdSimulated_rw_p()
.
"done".
Выяснилось -- дело было в том, как указывались параметры для линковки: в конце командной строки была прямая ссылка на $(CROSS_TOOLS)/lib/ld.so.1. А надо было иметь примерно следующее:
-L$(CROSS_TOOLS)/lib -Wl,-rpath-link $(CROSS_TOOLS)/lib
(Почему именно
-rpath-link
-- изначально было непонятно, потому как при сборке вроде как должно хватать-L
; но вот -- не хватает... Потом, после внимательного чтения man'а по ld, стало ясно: оказывается,-L
влияет только на поиск "archive libraries", т.е., статических -- .a-файлов; для .so же файлов этот search path уставляет именно-rpath-link
.Мутности в вопрос добавляло наличие ключика
-rpath
, подвидом которого, казалось бы, должен быть-rpath-link
; ан нет -- это изрядно разные вещи:-rpath
просто добавляет директории к "runtime library search path", а вот-rpath-link
влияет на процесс сборки. (Хотя под SunOS, как там написано,-rpath
интерферирует и с-L
-- сие писание также добавило к запутанности вопроса.))
Собственно, формально-то результат должен был бы быть одинаковым, ибо и набор файлов/библиотек получался тот же, и ошибок вроде не было -- но вот что-то все же глючило, причем и размер бинарников в этих вариантах выходит разный. В общем, как водится -- экзотический cross-SDK несколько дурковат.
20.01.2007: так что -- в ppc_drvlets/LocalRules.mk также подправил.
Кроме того, несколько дней назад, в начале разбирательства с realloc()'ом, выяснилось, что в том SDK, полученном от Мамкина летом 2004г., был немножко неправильный libgcc.a -- от какого-то другого процессора (причем даже неясно, от какого именно -- MD5 ни с кем не совпадает). Так что подправил, скопировав в tools/lib/gcc-lib/ppc-linux/3.2.2/ и в .../3.2.2/pic/ файл из поддиректории m860/ (в pic/'е раньше был, судя по MD5, из m750/).
А то надоело уже править в обоих местах одинаково.
13.02.2007: да, сделано, все вытащено в drivers/ppc860_rules.mk.
02.02.2008: надоела неадекватность старого симулятора больших каналов, так что за день накатал новый вариант. Детали:
Собственно, это и послужило главной причиной -- иначе тяжко было отлаживать нувую программу adc333.
rand()
'ом выкинута за ненадобностью.
malloc()
'ом -- так что теперь снято дурное ограничение в
10000 единиц, а поставлено (от фонаря) 1000000.
Новый вариант сделан практически с нуля, а не на основе старого. Старый, если что, сохранился в BACKUP и STABLE (его дата была 01-02-2006).
А что -- если сделать так: вытащить монтирование и запуск в отдельный процесс в background'е, который при обломе монтирования засыпает на 60 секунд и повторяет попытку снова, а при успехе -- запускает rrund и отваливает?
20.05.2008: да, сделал такое -- вытащил соответствующие команды в отдельный файл /etc/mounthome.sh, имеющий такой вид (на CANGW):
а в /etc/rc.sh теперь осталась строка#!/bin/sh echo "/etc/rc.sh: mounting /mnt/host" while ! mount -t nfs -o nolock,ro $1 /mnt/host do sleep 60 done echo "/etc.rc.sh: starting canserver" /mnt/host/oduser/pult/lib/server/drivers/cangw_ppc_drvlets/canserver -d >/dev/null 2>/dev/null &
/etc/mounthome.sh $MY_HOST:/export/ring1 &
Проверил на cangw-ringcams -- работает, ура-ура-ура.
Осталось только распространить это на второй CANGW :-), а главное -- на все CM5307/PPC.
11.08.2008: да, на cangw-istc это тоже сделал,
когда ставил эту "вторую" коробочку в губятник. Вначале забыл сделать
chmod +x /etc/mounthome.sh
-- оно, естественно, просто не
запускалось. Помнить надо!!!
В общем, все работает -- считаем за "done".
15.10.2008: елы-палы -- надо реально эту технологию распространить на все CM5307/PPC и даже на старые CM5307! А то вот были проблемы с сетью, так половина контроллеров остались нефункциональны -- поскольку грузились с неработающей сетью...
20.05.2009: just for reference: сегодня эта технология использована и на первом CM5307/PPC (видимо, это первый новый CAMAC-контроллер со времени изобретения mounthome.sh). Итого: в качестве образца для CANGW может использоваться ring1:cangw-ringcams, а для CM5307/PPC -- linac1:ppc-ippspectre.
11.08.2008: Еще из "неприятностей" -- в продолжение бурчания за 18-10-2004 на тему "порядком задрало, что на каждом конкретном контроллере надо...": там надо указывать не только имя хоста для монтирования (linac1, ring1, bahus, ...), но и имя юзера -- раньше это был oduser, а теперь -- oper.
С одной стороны, в будущем и всегда будет юзер oper, и, скорее
всего, контроллеры одной пультовой сети будут всегда монтироваться с
одного хоста (так что варьироваться реально будет только
$MY_IP
, а $MY_NET
и
"$MY_HOST_UNAME_S
" -- лишь между разными установками).
Но, тем не менее: нынешняя схема, пусть и с mounthome.sh, выглядит некрасиво -- надо как-то ее улучшить.
Т.е., чтобы все было автоматически и не требовало бы такой ручной настройки. Чтобы все "прошивки" всех однотипных контроллеров были бы одинаковы (либо прямо прошивка, либо на каждый новый контроллер просто раз-tar'ивать стандартный набор файлов).
Видимо, ответ лежит все-таки в DHCP; но это потребует ведения
конфигурации на хосте. Тогда -- можно предусмотреть механизм
fallback'а, что при обломе с DHCP оно бы конфигурилось статически,
пытаясь использовать предопределенные MY_NET=192.168.1
,
MY_HOST_UNAME_S=localhost
, а в качестве последней цифры IP
брать последнее число из MAC-адреса.
P.S. А вообще -- ну не халтура ли, что у нас вообще монтируется /export/HOST/USER/? Может, пора уже перейти на какую-нибудь более нейтральную директорию -- типа /usr/local/cx/? Хотя бы в CXv4 (Снежинск, карботрон-HITS).
31.08.2009: с проблемой указания имени хоста справился очень просто: при помощи bind mount'а. Т.е., в /etc/fstab имеется строчка вида
в /etc/exports -- соответственно,/export/swan /export/HOST none bind 0 0
а в контроллере в конце /etc/rc.sh указываем/export/HOST 192.168.8.*(ro)
/etc/mounthome.sh $MY_HOST:/export/HOST &
Ключевые моменты в /etc/fstab: тип файловой системы -- none (хотя, по слухам, и bind тоже катит), fs_freq и fs_passno -- нули (не дампить и не проверять).
setuid()
подо что-нибудь другое, например,
daemon или лучше вообще nobody.
Эта фича должна быть опциональной -- например, по ключику
"-uUSER". Замечание: на CM5307, под uClinux, файл
/etc/passwd ОТСУТСТВУЕТ, так что там проканает только
"-uUID". Да и с getpwnam()
& Co. не
удивлюсь, если будут "неудобства".
Вопрос в том, какие могут возникнуть сложности -- например, по доступу к устройствам (/dev/camac и /dev/can*).
16.03.2017: чтоб не гадать:
# ls -l /dev/can* crw-rw-rw- 1 root root 91, 0 Dec 31 1969 /dev/can0 crw-rw-rw- 1 root root 91, 1 Dec 31 1969 /dev/can1 crw-rw-rw- 1 root root 91, 2 Dec 31 1969 /dev/can2 crw-rw-rw- 1 root root 91, 3 Dec 31 1969 /dev/can3 crw-rw-rw- 1 root root 91, 4 Dec 31 1969 /dev/can4 crw-rw-rw- 1 root root 91, 5 Dec 31 1969 /dev/can5 crw-rw-rw- 1 root root 91, 6 Dec 31 1969 /dev/can6 crw-rw-rw- 1 root root 91, 7 Dec 31 1969 /dev/can7
# ls -l /dev/camac crw-rw-r-- 1 root 1000 21, 0 Mar 11 2004 /dev/camac
Т.е., с CAN -- нет проблем, с CAMAC -- будут (там открывается с
O_RDWR
).
А по-хорошему и dataunits тоже надо бы там указывать.
13.03.2009: да, сделал "по максимуму".
NADCxxx_MAXNUMPTS
-- максимально допустимое
значение параметра NUMPTS.
NADCxxx_NUM_LINES
-- "канальность" блока -- обычно
2 или 4.
NADCxxx_DATAUNITS
=sizeof(NADCxxx_DATUM_T) -- 1, 2
или 4.
NADCxxx_DATUM_T
, уставляемый в
uint8, int16 или int32.
Соответственно, во-первых, все "магические числа" в драйверах заменены на эти константы (включая циклы по каналам/линиям при вычитывании данных), а во-вторых, аналогично заменены и типы данных.
(И cm5307_nadc333.c несколько необычен -- тем, что он,
вследствие общей на все каналы памяти, не делает при вычислении объемов
домолнительного умножения NADC333_MAX_NUMPTS
на
NADC333_NUM_LINES
.)
Замечание: эта параметризация -- несомненно, серьезный шаг в сторону унификации, позволяющей сделать "общий fastadc_common".
16.03.2009: да, и в старые a_adc200.h и a_adc333.h тоже это портировал. Проверить бы еще...
04.06.2009: проверено Роговским чуть ли еще не тогда же -- всё работает. Это, конечно, хорошо, а вот еще бы в клиентах тоже заиспользовать!
27.02.2010: да, заиспользовал. Хотя пока не проверял.
12.03.2010: еще дополнение: добавлены константы
*ADCxxx_MIN_VALUE
и *ADCxxx_MAX_VALUE
-- в
интересах fastadc_common.*
, которому надо указывать
диапазон, в котором могут лежать данные.
22.07.2011: давным-давно уже пора было ставить "done".
11.08.2009: да, теперь Makefile'ы могут указывать
UTILSSOURCES
(список .c-файлов, из которых будут
сделаны бинарники) плюс LIBSSOURCES
(список
.c-файлов, для которых надо генерить зависимости).
(Да-да-да, с человеческой системой сборки эта проблема даже и не
встала бы :-))
Кстати, предыдущая дата на файле была 14-06-2005.
27.01.2012: юзеров её практически изначально было всего 3:
Так что -- аминь!
10.08.2013: первый шаг с собственно копированием и выкашиванием из DirRules.mk всего старого (SUBDIRS, LAYER*, ABSTRACT*) очень прост -- всё легко и сразу заработало. И Makefile получился весьма кратким.
Заодно triggered_drv.c и multwrite_drv.c переведены на "inserver2".
Теперь же надо надо прогуляться по всем прочим drivers-директориям,
чтоб избавиться от каких-то мега-особенностей, и чтоб
drivers/DirRules.mk определял лишь
DIR_CFLAGS=-fPIC
и
EXPORTSDIR=lib/server/drivers
.
12.08.2013: продолжаем:
12.08.2013: добиваем:
MONO_C_FILES
или SOLIBS
, а где лениво --
заиспользовано свежевведённое LOCAL_DEPENDS
.
А за ним и старые драйверные директории из v2hw/ -- old_camac, cpci, can/c4l-cangw, vme/bivme2, serial/moxa (их место заняли новые версии).
(14.08.2013) и еще из v2hw/ убрана поддержка старых драйверов adc333 и adc200 (как раз удалённых) -- _drv_i.h-файлы и вся шобла xmclients/*adc*.c. Плюс старое (зависевшее от adc200_drv_i.h) из ndbp/.
19.08.2013: ага, заодно по ошибке и
RegisterDevPtr()
попала тогда под горячую руку. Вёрнута.
15.09.2013: ...но в cxsd_driver.h всё отсутствовала. Тоже вёрнута.
Если что, то старое, "до" удаления есть в w20130813-pre-p_s-rem.tar.gz, а "после" -- в w20130813-rem-old-p_s.tar.gz.
Из временно отрицательного: кросс-директории в v2hw/drivers/ -- camac/cm5307_ppc_drvlets/ и vme/bivme2/ покамест не поддерживают "стороннюю" сборку при помощи DirRules.mk.
22.08.2013: и стороннести сделаны.
15.03.2014: зрело оно давно, сейчас просто записываем. Предпосылки и обсуждение:
cda_del_server()
, а то сейчас он просто пустышка.
15.03.2014: (после обеда) делаем! Готово за пару часов -- оно не так уж сложно. Проверять будем завтра.
16.03.2014: проверено. Почти (пучка нет, так что стопроцентно сравнить не удаётся).
Из неприятностей -- нынешняя реализация cda тянет за собой еще и libmisc+libuseful (ради printffmt* и findfilein() соответственно). Точнее, их куски, отсутствующие в сервере. Но фишка в том, что и драйверам-то они не нужны -- оно тянется от "формул". Косяк из-за того, что в едином cda.c сразу и "ключевое" -- поддержка соединений, и вторичный сервис -- формулы.
P.S. Еще какие-то колёса с PSP_P_MSTRING -- SIGSEGV при psp_free().
17.03.2014: проверено с пучком -- всё окей (различие формы "бахромы" шума было обусловлено разным вертикальным размером графиков, там какие-то колёса со скейлингом).
Единственная тонкость -- некоторые косячки с регулировкой параметров (например, NUMPTS): поскольку pzframe_gw лишь туннелирует запросы, и, похоже, не совсем точно соответствует функционалу pzframe_drv, то наблюдаются "проскоки" -- двойное быстрое нажатие "вверх" меняет параметр единожды, т.к. ко второму значение в ручке успевает "отскочить" назад, и лишь через цикл принимает заданное первым нажатием значение.
19.03.2014: колёса с SIGSEGV исправлены -- это был ляп paramstr_parser'а.
Косячок с параметрами -- неприятен, но не фатален (хотя разобраться бы стоило).
Так что -- пока считаем за "done".
17.10.2014: всё дальнейшее обсуждение pzframe_gw -- в его собственном разделе в v2hw/drivers/.
А чё б не сделать СОФТОВУЮ реализацию обычной табличной waveform -- специальный драйвер, часто-часто сбагривающий данные из записанной в него таблицы в ЦАП?
28.05.2014@Снежинск-каземат-11: приступаем, драйвер назовём softwform.
...кстати, потом можно расширить функционал double-buffer'ингом: иметь второй буфер, в который писать во время исполнения первого, чтоб по окончанию первого драйвер бы "менял их местами", переходя к исполнению второго. Так можно реализовывать длинные непрерывные процессы.
После обеда: вроде первоначальный вариант доделан.
sl_enq_tout_at()
. Исходя из сути задачи.
Единственное что -- цепочка HeartBeat()'ов начинается при старте драйвера, и НЕ привязывается к сигналу START. А надо бы -- тогда куча процессов может запускаться скопом через multwrite.
Чуть позже: да ну нафиг -- сделано. Оно ре-стартует цепочку прямо в момент START.
Теперь -- проверять.
17.06.2014: проверено, после исправления мелкого глюка (не было bzero(opts), и считалось target.chan_is_bigc) работает.
Теперь надо будет еще добавить конфигурябельность периода.
23.06.2014: конфигурябельность тоже добавлена -- параметр period. Работает.
Т.е., такому драйверу указывается целевой УСТРОЙСТВО.КАНАЛ, а он имеет 3 канала -- OUT, OUT_RATE, OUT_CUR (уставка, скорость, текущее), и при записи в OUT сбагривает значения в "жертву" со скоростью не выше OUT_RATE.
Потребность -- на сварке, там канал "ток" в weld02 надо б мочь изменять плавно.
07.06.2005: Приступаем. Пока новая версия именуется new_cm5307_drv.c.
Основная идея -- она основана на privrec_t
, а не на
массивах размером в MAX_ALLOWED_FD+1
.
08.06.2005: перевел бОльшую часть имевшегося кода, кроме собственно момента коннекченья.
Обнаружилось, что по-прежнему использовался термин "slaveinfo". Естественно, заменен на "drvletinfo".
Там остается "кривость", что отправные буфера лимитированы неким размером. Пока забиваем -- в версии, основанной на fdiolib'е, эта проблема отпадет сама собой.
Короче -- осталось реализовать только сам блок
connect/ScheduleReconnect и использование
RegisterDriverWrFD()
.
10.06.2005: вроде все сделано -- часть, отвечающая за реконнекченье, создана. Теперь надо испытывать. Что касается чисел -- старый файл 591 строка, новый -- 715.
20.06.2005: сделал "перекидку имен": cm5307_drv.c->old_cm5307_drv.c, new_cm5307_drv.c->cm5307_drv.c.
21.06.2005: начинаем тестирование.
Дятел -- имена перекинул, а "new_" убрать в
DefineDriverRec()
'е забыл! :-)
Б-л-л-л-и-н-н-н!!! Забыл аллокировать inbuf
... Да,
fdiolib'а сильно не хватает...
21.06.2005: продолжаем тестирование.
listen(,5)
-- вот оно просто молча и игнорирует
дальнейшие запросы. А ядро, на котором запущен сервер, через 3 секунды
запрос повторит.
(Странно, конечно, что НЕ connection refused, как, вроде бы, утверждалось в tutorial'е, но так оно и к лучшему -- иначе бы раньше вообще никак не работало.)
В общем, увеличил число до 20, хотя, как показывает man-страница, это реально какая-то алхимия.
DRVS_OPERATING
вызовет перезапрос, а так -- смены состояния-то не происходит...
Вот оно -- надо уметь стартовать драйвер сразу в NOTREADY!
Кстати -- а сейчас-то НЕЛЬЗЯ прямо из метода init_blk()
вызывать смену статуса -- ибо
d_status[magicid]=DRVS_OPERATING
прописывается ПОСЛЕ этого
вызова...
На будущее -- CXv4 -- надо из init_blk()
возвращать именно СТАТУС, а дескриптор -- драйвер и сам зарегистрирует.
Соответственно метод fd_io()
устраняется вовсе.
Пока же ввел в InitializeDrivelet()
дополнительный
параметр -- re_request
, который из
FdWrReady_p()
уставляется, если предыдущее значение
is_suffering
было 0, т.е., это ПЕРВИЧНАЯ инициализация.
Да, криво, но пока иначе никак...
Итого -- сей проблем исправлен.
Короче -- похоже, все работает.
28.06.2005: да, проверил аж три "жестких" случая:
Все испытания прошли успешно.
"0-й" тест -- убитие драйвлета -- был проделан еще неделю назад, и все отлажено. Ну а "0.5-й тест" -- reboot контроллеру, был также проделан еще неделю назад -- при этом ядро контроллера закрывает все соединения, так что все сводится к 0-му случаю.
Так что, на вид все работает, и помечаем как "done".
05.09.2005: поддостало, что при восстановлении
связи оно ничего не пишет -- вставил, в
InitializeDrivelet()
, при условии !re_request
(которое, в свою очередь, передается как !was_suffering
;
да, криво, некрасиво, увы...).
23.01.2009: old_cm5307_drv.c уже давным-давно не используется, на нем даже mtime=07-06-2005, а теперь, при модификации связки misclib+misc_macros.h, он стал еще и слегка out-of-sync. Смысла его править нету никакого, так что -- удаляем из дерева!
SO_KEEPALIVE
.
28.06.2005: да, вставил:
FdWrReady_p()
.
DriverBody()
-- на
всякий случай в ДВУХ экземплярах: для дескрипторов 0 и 1.
25.01.2006: "done" -- это надо было сделать еще в прошлом июне :-).
Так что -- вставил при получении пакетов и при их отправке в _fd_p()
и _big_p() диагностические сообщения с пометкой
DRIVERLOG_C_PKTINFO
-- чтобы можно было включать при
надобности.
auxinfo
в cm5307_init_b
халтурновата -- оно
просто ищет пробел, и все с него считает за drvletinfo. И -- ни
'\t' не поддерживается, ни пробелы в начале drvletinfo не
пропускаются...
12.05.2006: полностью переделал тот кусочек --
теперь оно ищет первый isspace()
, засчитывая его за конец
drvname, потом пропускает все isspace()
, и уж результат
сих действий считает за начало drvletinfo
.
char privrec_t.drvname[20]
. В
результате фразы типа "cm5307_karpov_delayunit" и
"cm5307_karpov_monster" туда просто не помещались (еще бы!), обрезаясб
до "cm5307_karpov_delay" и "cm5307_karpov_monst". Странно, что не
вылезли за этот лимит раньше.
15.05.2006: увеличил с 20 до 100.
02.01.2007: реализация может состоять из двух частей, по частям задачи:
cm5307_pkt_header_t.var.debugp
поле
"level", в котором просто передавать параметр
DoDriverLog().level
-- это будет сразу и level, и mask.
(По умолчанию оно будет 0 (как сейчас, и как от более старых
драйвлетов (поэтому можно и версию протокола не двигать)), и размер
заголовка не изменится, поскольку сейчас var.debugp
вообще
пустая.)
Когда его передавать: в точке хост-драйвера, реагирующей на пакет _DEBUGP, проверять -- если текущее значение отличается от предыдущего, то передать. (Безопасным начальным значением для "предыдущего" будет 0.) Так оно будет гонять по сети изменившееся значение лишь по мере надобности -- "адаптивно".
Следствие: нужен API-вызов "GetCurrentLogSetting(magicid)", который бы отдавал level+mask.
03.01.2007: приступаем к реализации.
Да, поддержка отсылки подчиненными параметра level и его отработка в cm5307_drv.c и cangw_drv.c сделана по вышеприведенному проекту.
(Старый cm5307_drvletbody.c понятно, трогать нет смысла -- он сейчас используется только для абстрактных драйверов, и никакого параметра "level" там нету.)
04.01.2007: реализовываем вторую половину -- отправку текущего уровня+маски и их использование.
Пришлось ради GetCurrentLogSettings()
добавить в
cxdpartlib.[ch] экспортируемую переменную
logverbosity_level
-- в CXv4 надо будет ее не забыть (с
самим же cxlogger'ом все проще -- он и так принимает целочисленный, а
не строковый параметр).
12.01.2007: кстати, а есть ведь еще одна мелкая
толстость: DRIVERLOG_ERRNO
. Его надо, во-первых,
отсеивать из флагов при передаче хосту, а во-вторых -- отрабатывать на
стороне драйвлета. 25.05.2012: воистину надо отрабатывать -- а то иначе НИКАК не
понять, что же именно не так у драйвлета. Вот сейчас сходу неясно
было, чего не так canserver'у -- а там /dev/can0 был занят
canmon'ом...
Отсеивание-то и в cm5307_dbody, и в canserver_drvmgr вставил (в
заполнение поля debugp.level
-- самое правильное место).
15.01.2007: проверки на стороне драйвлетов -- в cm5307_dbody и в canserver_drvmgr -- также вставил.
18.01.2007: а вот и фигушки -- значение 0 вовсе не является совсем уж безопасным начальным значением:
(Уровень же 0 как раз отлично катит -- ибо он и есть максимум.)
И, кстати, уставлять эти "максимальные" значения в last_* надо в момент инициализации ДРАЙВЛЕТА, а не самого драйвера/privrec'а!
В общем -- так и сделал.
19.01.2007: ХРЕН! Не работает такая адаптация, точнее -- работает только в одном направлении, по простейшей причине: когда мы ВЗВОДИМ биты в маске, то у драйвлета-то они по-прежнему СБРОШЕНЫ, и он сообщений не присылает. И только когда ПРИШЛЕТ что-нибудь, тогда и получит обновленные значения. Ну я и олух!
В общем, в CXv4 надо вводить какой-то способ уведомлять драйвер о смене подобных параметров немедленно -- наверное, что-то аналогичное knob'овому PropsChg().
А пока -- фиг с ним, оставим как есть.
13.02.2007: ну раз уж пока "фиг с ним", то считаем за "done".
09.01.2012: насчёт "Старый cm5307_drvletbody.c понятно, трогать нет смысла" -- неправда, ЕСТЬ смысл -- просто упоминание этого кода, без каких либо действий; чтоб он не вякал
DriverBody: unknown command 0x53646267
Так и сделано.
(Да-да, мы давным-давно хотим его "уволить", и потому туда вообще почти ничего из модификаций не идёт, но он, собака, очень уж живуч...)
04.01.2007: на стороне cm5307_drv/cangw_drv -- сделал. Осталось еще слабать простенькие функции, исполняющие собственно посылку, на подчиненной стороне, и проверить.
01.02.2007: в canserver_drvmgr.c SetBlockStatus()->_CHSTAT также уже почти месяц работает.
А вот в cm5307_dbody.[ch] значения DRVS_nnn
ведь другие, так что потребуется трансляция. Но, поскольку пока что
эта функция в CAMAC-драйверах не используется вовсе, то и фиг с ним --
как понадобится, так и сделаем.
И реализовывать на подчиненной стороне ReRequestData() также пока не потребовалось -- *cankoz_pre_lyr.c вызывают перезапрос косвенно, дерграя состояния ->NOTREADY->OPERATING.
В общем -- помечаем как "done".
09.02.2007: сделал-таки
SetDevStatus()
и в cm5307_dbody.[ch] -- да, там
делается трансляция {-1,0,+1}->{0,1,2}. И по результату вызова
init_b автоматом дергается при надобности DRVS_NOTREADY или
DRVS_OFFLINE.
И при DRVS_OFFLINE также уставляется TCP_NODELAY:=1, и после
отправки делается exit(1)
.
21.02.2012: а вот реализации ReRequestData() нигде на стороне драйвлетов не было -- только реакция на него в (сейчас) remdrv_drv.c. Не то чтобы прям щас сильно требовались, но -- пусть будут. Делаем:
Различаться же должны именно и только конечные серверы/исполнители: если сейчас бедный cm5307 начинен кодом работы с разными архитектурами, то вообще-то надо просто перед запуском rrund делать cd в соответствующую директорию, а он уж пусть запускает "./DRIVER.drvlet".
11.01.2007: и еще --
01.02.2007: ну так понятно, почему этот ляп больше пяти лет не проявлялся -- в драйвлетах всегда были статические буферы (изначально, естественно, забитые нулями), а пакет CONFIG всегда приходил первым. Ну а slaveinfo/drvletinfo использовалось крайне редко, поэтому отбрасывание последних не-нулевых байтов и не замечалось.
25.08.2008: вообще, че выпендриваться -- надо
сделать эти параметры настраиваемыми из конфига, беря их из
INIT_FUNC::infostr
.
Или так: в принципе-то в модели abstract_drivers/drvlet_proto была предусмотрена передача параметров измерения -- set1,set2 -- в каждом запросе, но оно покамест вообще никак не используется (всегда передаются -1,-1). Но вся инфраструктура есть, и даже самим драйвером a_c0609.h оно будет использоваться. Так что -- если добавить это в cm5307_drv.c, то оно сразу заработает, причем сразу во ВСЕХ драйверах (естественно, если в тех в принципе это не забыто).
Да, так и поступим! Синтаксис, видимо, расширим так:
Arch/host:driver[,options][ drvletinfo]
где "options" -- это psp-строка, в которой пока что будут только
параметры set1 и set2.
Часом позже: да, сделал. Проверил на с0609 -- работает!
Так что -- помечаем этот раздел с великим достижением (а то! эпохальный шаг -- абстрактным драйверам стало можно указывать параметры измерения!) как "done", и переносим его из раздела "cm5307 drivers" в раздел "cm5307_drv".
P.S. В cangw_drv.c это портировать смысла никакого --
во-первых, у CAN-драйверов свой парсинг auxinfo/drvletinfo, а
во-вторых, в canserver_drvmgr.c поля
set1
,set2
не используются.
21.07.2013: и, кстати, в новой унифицированной реализации (remcxsd) set1/set2 тоже не будут использоваться. Ибо нехрен -- все драйвера, требующие подобной настройки, надо делать НЕабстрактными, а настройки -- настроечными каналами (да, с какой-нибудь возможностью указания умоолчаний в auxinfo).
Just for info: использовалась фича один-единственный раз, тогда летом 2008-го, в blklist-linac1-48.lst/korepanov8hrs, для c0609 set2=4.
inpktsize=0, <sizeof(cm5307_pkt_header_t)=32с последующим "cm5307_fd_p: FATAL -- terminating".
Интересно -- что бы это могло быть?
25.08.2010: это ВСЕГДА бывает ТОЛЬКО от драйвлета dds300. А сам девайс замечался за странными сбросами. Скорее всего, что-то не так с драйвлетом; а возможно, он умудряется дергать какой-то баг в cm5307_dbody.c...
31.08.2010: вставляем в cm5307_drv.c (и заодно в cangw_drv.c) еще отладочную печать -- теперь оно при неправильном inpktsize выдаёт также и код команды (раньше оно было доступно только при включенной категории DRIVERLOG_C_REMDRV_PKTDUMP).
31.08.2010: нашел проблему -- она была совсем в
другом: опечатка при реализации ответа на CM5307DLP_PING
в
cm5307_dbody.c. Из-за того, что при отсылке обратного пакета
в качестве длины вместо должного rcvpktsize
использовалось
rcvpktsize
, оно и фигачило ВСЁ содержимое входного буфера
-- мусор от предыдущего пакета. И вот следующее за заголовком
int32-поле и "прикидывалось" длиной следующего пакета -- обычно 0, а
иногда и 5.
Выявление проблемы осножнилось тем, что
Еще задача усложнялась тем, что в cm5307_dbody.[ch], в отличие от cm5307_drvletbody.[ch] и canserver*, напрочь отсутствует какая-либо печать на stderr, так что даже ручной запуск rrund из консоли ситуацию не прояснил.
Поэтому добавлена выдача на stderr при запуске и при останове драйвлета, а также при получении неизвестной команды.
15.01.2013: собственно:
REMDRVP_PING
и
REMDRVP_READ
/REMDRVP_WRITE
-- сбрасывает
O_NONBLOCK:=0, и лишь после отправки ставит обратно O_NONBLOCK:=0.
Вот именно по этой причине на прошлой неделе были "чудеса", когда сервер linac1:57 (ipp) время от времени "подвисал", так что его лампочка посиневала -- это просто отправной буфер в сторону контроллера забивался.
remdrv_rw_p()
код команды вместо
"0x%08x" выдавался простым "%d".
remdrv_fd_p()
код команды выдавался бездумно
через ntohl()
(хотя /*!!!*/
там и было).
Добавлено условие.
ScheduleReconnect()
DeregisterDevRdFD того, что еще не было
зарегистрировано (fdh=-1) -- на стадии "connecting" (когда
зарегистрирован только ). Всё результат кривоватой архитектуры -- в
4cx'ном варианте уже "прямее" (и вследствие лучшего API, и изначально
устроено поправильней).
04.03.2013: разборки:
write()
в
remdrv_rw_p()
. Оно после развисания выдавало в лог
ругательство, но БЕЗ кода результата и errno.
Посему -- теперь после ошибок перед почти всеми
ScheduleReconnect()
'ами в диагностику добавлены
r
и errno
.
n_write()
вместо
fdiolib'а!),
05.03.2013: да, делаем возможность менять размеры буферов, и перед этим вначале читаются текущие размеры.
11.03.2013: да, попробовал. Результаты:
Только по kill драйвлета -- там заявляет errno=104 (Connection reset by peer).
Так что остальные эксперименты шли уже с модификацией буферов.
На 12.5Hz почти не виснет (ПОЧТИ!).
А вот на 50Hz -- продолжает. Не очень понятно, почему -- ведь 100кб на отправку должно хватить (и даже если драйвлет зависнет, то сервер синеть не должен; а синеет!). Или что-то с окном?
Кстати, иногда синеет на секунду, но потом восстанавливается (врЕменное подтормаживание?).
Общее впечатление -- надо всё же ускоренными темпами переходить на унифицированное ядро сервера/драйвлетов, на fdiolib+cxscheduler, тогда проблема сама исчезнет.
12.06.2013@вечер-дома-в-ванне: а ведь скорее всего всё намного проще и СЕРВЕРНАЯ сторона вообще почти ни при чём! Поскольку cm5307_u0632 работает на dbody (и, что обидно, sl_dbody не поддерживает), то затык именно в ней -- это ТАМ оно успевает накидать в сокет тучу данных, так что забивает ядровый буфер отправки. И именно ТАМ надо увеличивать SNDBUF!
13.06.2013: типа пробуем. Причиной (и вчерашнего размышления) послужило то, что при смене уставок ИПП (чувствительность) они почему-то отрабатываются секунд через 10, а до того просто синеют. Т.е. -- на вид какие-то приколы с буферами. Итак:
Результат: без толку. Ничего не изменилось.
После некоторой маеты удалось запустить top (hint: положить рядышком libproc*so (и указать туда LD_LIBRARY_PATH), TERM=linux и сделать симлинк /.terminfo->etc/terminfo), и оказалось, что ~75% idle.
14.06.2013: идея:
HandleDriveletInput()
, вычитывающий ОДИН пакет; и всё это
с таймаутом 100ms.
Нет, по идее -- не должно: оно ж на следующем же цикле тут же увидит готовность сокета и вычитает еще.
Да и непонятно, почему такая радикальная разница между поведением на двух контроллерах.
HandleDriveletInput()
на цикл по
while (check_fd_state(DRIVELET_INPUT_FD, O_RDONLY) > 0)
проблема ушла -- всё стало реактивно!
А до того наблюдалась дичайшая картина: после гроханья сервера драйвлет еще секунд 10 продолжал жить своей жизнью, черпая из stdin'а команды и ловя запуски (видно по отладочной печати).
Т.е. -- надо в HandleDriveletInput()
вставлять цикленье
"пока есть что (до 30 раз)".
Но всё равно неясно: во-первых, почему такое странное поведение (чего сразу несколькими подряд select()'ами не вытащит всё? Планировщик дурит? Процессор-то ~80% idle...), а во-вторых, почему на втором контроллере проблем нет...
Но почему вариант с "полным" select() тормозит, а с
"подсматривающим" -- нет? Или scheduler ядра ведёт себя по разному при
нулевом и не-нулевом таймауте? Несколькими часами
позже: попробовал посмотреть в 2.4.18-5'шном
fs/select.c::do_select()
-- ничего не понял;
вроде, на вид, какое-то отличие при __timeout==0 есть, но даст ли оно
такой радикальный эффект -- неясно.
18.09.2013: а вот и нет, не надо! Ведь тут сразу после успешного connect()'а делается отправка конфига драйверу (т.е., безо всякого многоуровневого договаривания) и включается в работу HeartBeat() с периодом 5 минут.
16.10.2013: подсмотр за его privrec'ом с помощью gdb показал, что:
Т.е., дескриптор в cxscheduler'е зарегистрирован за другим devid. Возможно, ДРУГОЙ экземпляр remdrv сделал close(), но НЕ sl_del_fd(); хотя как бы такое возможно -- из кода неясно (что-то вызванное кривизной "ScheduleReconnect()'у передаётся fd"?).
А еще --
Как такое возможно -- тоже загадка загадочная.
Подсмотр -- само по себе нетривиальное занятие, поскольку:
d_devptr[4]
к
типу remdrv_drv.c::privrec_t
не удаётся ("No symbol
"remdrv_drv" in current context"),
remdrv_rw_p::privrec_t
("Cannot look
up value of a typedef").
((int*)(d_devptr[4]))[1]
и
т.д., перебирая циферки -- благо, большинство полей int'ы.
В общем, скорее всего сейчас проще на проблему вообще забить, в связи с предстоящим созданием fdiolib-based remote_drv.c (где возможностей для таких косяков будет меньше, вследствие более прямой архтектуры и отсутствия древнего наследия с раздельными rd/wr-регистрациями).
24.11.2013: возможно, проблема была из-за ляпа с несбросом clp_tid=-1 в cxsd_fe_cxv2.c. И даже скорее всего -- все признаки сходятся, и по времени появления проблемы примерно соответствует.
Вот он и отрицательный эффект от всеобщего прямого доступа к cxscheduler'у. Хотя, возможно, ситуацию спасут те самые дополнительные hook'и с проверками (или, как минимум, отладочной руганью).
Жить ему осталось недолго, но всё же...
22.10.2013: сделано просто: добавлена
remdrv_term_d()
с safe_free().
08.06.2005: директория создана.
14.06.2005: структура также создана. Драйвер сейчас состоит из:
15.06.2005: сделал (точнее -- перевел из
canadc40.c) и работу с очередью, включая таймауты. В т.ч.
теперь ВСЕ "заинтересованные", включая do_send()
функции
возвращают коды QFE_nnn
(но НЕ sendframe()
--
та, как и положено, 0/-1).
Кроме того, в marcankoz_fd_p()
вставлена
"интеллектуальная" обработка пакета 0xFF:
DRVS_OFFLINE
с кодом CXRF_WRONG_DEV
.
16.06.2005: О! Так надо сделать, чтобы прямо
NNNcankoz_add()
посылал бы unicast-запрос 0xFF. Тогда,
даже если блок будет добавляться уже потом, через большой интервал
времени ПОСЛЕ инициализации порта, от него все равно будет приходить
идентификационный пакет, по которому драйвер и станет запрашивать
периодические измерения. А из init_b()
этого делать
совсем не надо.
Вставил такую посылку, а RequestPeriodic()
из
canadc40_init_b()
убрал.
16.06.2005: интересный идеологический вопрос: как реализовывать команды, имеющие вид "1) Команда записи, без ответа; 2) Команда чтения (контрольная)". Просматриваются 2 варианта:
sendframe(WRITE); enqueue(READ, QFE_IF_NONEORFIRST);
.
if (enqueue_or_replace_ons(WRITE) = ENQUEUED) enqueue(READ, ALWAYS);
.
(Есть еще 0-й подход -- "дебильный":
enqueue_ons(WRITE,ALWAYS); enqueue_ons(READ,ALWAYS);
, но
его за кандидата считать не стоит :-)
- Первый подход используется в старом istc/drivers/canadc40_drv.c -- для чтения каналов ЦАПа в
candac16_rw_p()
(УР там не поддерживается;canadc40_rw_p()
же использует для регистров "дебильный" вариант).- Второй -- в impacis10_drv.c для всех каналов записи (правда, без "_ons", за ненужностью оного -- протокол IS10 предполагает подтверждения ВСЕГДА).
- А kshd485_drv.c вообще полагается на то, что сервер защитит его от повторных запросов, и там мозги на сию тему вообще отсутствуют.
Анализ плюсов и минусов обоих подходов: ... лень писать :-).
Как бы то ни было, для "писуемых" CAN-драйверов выбираем первый подход. BTW, при этом "oneshot", похоже, становится вообще нафиг не нужен.
16.06.2005: помимо всей вышеописанной "идеологии" -- наполнил candac16_src.c
17.06.2005: после вчерашних размышлений решил привнести следующие изменения в CAN-KOZ-интерфейс и реализацию:
_fd_p()
проверку сразу после обработки
пакета 0xFF -- if(dp->magicid<0)return;
.
add()/disconnect()
, параметр
magicid
убирается -- нефиг, он и так определим по
handle
.
->regs_rw()
-- в самом
layer'е.
cankoz_ioregsinfo_t
--
переезжает (в сокращенном виде) в kozdevinfo_t
.
regs_base>=0
.
Таким образом, мы унесем ВЕСЬ "стандартный" интеллект (0xFF и ioregs) в layer, но сами пакеты БУДУТ отдаваться _in()-callback'ам, так что желающие смогут производить нестандартные действия.
Часом позже: перетряска произведена. Заодно отделил собственно
отправку фрейма (ныне writeframe()
) от API-вызова. Общее
впечатление: все стало красивее и правильнее.
07.07.2005: еще вчера начал наконец-то реально делать поддержку ioregs. Тезисы (ажно в блокнотике их сначала написал :-) таковы:
- Чтение inreg/wrreg: if(absent(READREGS)) enqueue(READREGS).
- По команде записи:
- reqd_val,mask=...
- if (!were_read) enq(READREGS,IF_ABSENT);
else {
if (enq_or_repl(WRITEREGS,ons)) enq(READREGS);
}- Собственно "отправка запроса на запись" -- отдельной функцией.
- При "реализации" запроса на запись (хоть через очередь -- ons, хоть прямой отсылкой) -- reqd_mask:=0.
Да, "отправка" сделана по "проекту 1" -- через
writeframe()
.
Сегодня доделал, начал тестировать. Предварительные результаты таковы: canadc40 -- работает, и меряет, и УР егошний; candac16 -- не работает ничего вообще -- сплошная синева.
Вечером: разобрался-таки, блин!!! Проблема была в том, что
при получении пакета я забыл после foreach(,,,ERASE_FIRST) вставить
sendnext(). Собственно --
проблема-то идеологическая, что реально это ЕДИНАЯ, АТОМАРНАЯ операция
-- "перейти к следующему пакету", а ее приходится делать ДВУМЯ
вызовами, вот и накалываюсь... 27.02.2006:
доделано -- теперь и есть операция
q_erase_and_send_next()
, и все что можно переведено на
нее.
Так что -- с phm-tsyline, на вид, оба драйвера работают. Надо еще протестировать с Лебедевым с пристрастием -- завтра.
08.07.2005: да, проверил -- работает, и не только на вид, а полностью.
Так что -- "done" (хо, ровно месяц!).
20.09.2005: хре-нуш-ки!!! Не работает толком поддержка регистров!!!
Во-первых, там сейчас устроено так, что если подряд (например, в одном пакете запросов) приходят ДВЕ команды на запись в разные биты регистров, то одна из них теряется (почему-то вторая).
Во-вторых, почему-то при reset'е блока все болотовеет -- что-то недоотрабатывается.
28.09.2005: выяснилось, что из-за того, что в
_marcankoz_q_enqueue_s()
в ветке
"QFE_IF_NONEORFIRST
" проверялось не на ==AFE_COUND, а на
==AFE_ISFIRST, оно глючило -- реально NONEORFIRST не работал (был
эквивалентен ABSENT). Как следствие -- запись в регистры глючила,
посколько отрабатывалось READREGS только после первой записи, а не
после остальных. (Скопировано сюда из транслитной записи
31-03-2006.)
09.09.2015: первое сделано повсеместно еще
давным-давно (в основном me
). Второе -- сейчас в v4'шных
во всех "iface" заменено на lvmt
.
25.01.2007: just for record -- вся вакуумная система переведена на CAN -- сейчас там 6 штук CANIVA. Как и предполагалось, переход произошел безболезненно и почти незаметно для пультовых программ -- понадобилось только повторно загрузить режим, поскольку уставки-то пределов теперь хранятся не в устройстве, а в драйвере.
29.01.2007: ага, щас!!! Эта долбанутая CANIVA благодаря стукнутому на всю голову и ни за что не отвечающему Гудкову сделана как попало -- у нее период интегрирования не 200мс, как у обычной ИВЫ, а 300мс, так что все измерения оказываются в 1.5 раза больше.
Первоначально-то Тарарышкин согласился перепрограммировать на 200мс, но теперь оказывается, что, во-первых, такую частоту эта долбанутая Java не тянет, а во-вторых, Тарарышкин не хочет тянуть две версии блока (а на ВЭПП-2000 уже используется 300мс-вариант).
Пришлось в драйвер засовывать деление на полтора (*2/3).
04.09.2013: есть с CANIVA некоторая проблема. С нынешним периодом 300мс получается, что от каждого блока прилетает по ~3*16=48 пакетов в секунду, каждый из которых транслируется в 4 ReturnChanGroup()'а, что при 6 блоках даёт непосильную нагрузку на дохлый 50-мегагерцовый проц.
Надо что-то придумывать на более глобальном уровне, а если никак -- то можно б и ввести возможность увеличивать период измерения (благо, он указывается 3-м байтом в команде 0xFD, множителем для 300мс).
04.09.2013: после обеда: проведено испытание
новой версии, с ReturnChanSet()
вместо 4 штук старых
вызовов.
Выводы:
30.10.2013: давно разобрано -- множественные fdio_send()'ы тормозили, с lock'ингом проблема исчезла.
04.08.2005 (13:09) прошло меньше получаса -- а уже написал. Точнее, практически скопировал с candac16, удалив работу с регистрами.
Пока не проверено.
31.01.2007: уже полтора года прошло, а сами-то блоки так в В1000 и не пошли -- ломает наших боймельштейнов там что-то переделывать. Так что -- отдал я все четыре блока Володе Реве, им понадобятся.
Явно надо ставить ограничение, чтобы не происходило таких уродских неприятностей.
06.09.2005: вроде сделал -- число вгоняется в безопасный диапазон [-10000305,9999999].
24.05.2006: давно проверено и работает -- "done".
_marcankoz_q_enqueue_s()
v vetke
"QFE_IF_NONEORFIRST
" proveryalos' ne na ==QFE_FOUND, a na
==QFE_ISFIRST, ono glyuchilo -- real'no NONEORFIRST ne rabotal (byl
ekvivalenten ABSENT). Kak sledstvie -- zapis' v registry glyuchila,
poskol'ku otrabatyvalos' READREGS tol'ko posle pervoj zapisi, a ne
posle ostal'nyh.
30.10.2005: вроде как изготовил -- cac208_src.c, взяв за основу canadc40_src.c, и дополнив его кусочками из candac16_src.c. Доступны все 24 канала АЦП, включая 4 диагностических, а не только "реальные" 20. Каналы ЦАПа идут следом - т.е., с позиции 24. А 18 каналов регистров -- за ними, с позиции 32.
Попробовал -- не работает. :-)
01.11.2005: разобрался, почему не работало. Оно там что-то все пыталось отправить пакет с кодом 0xA0, на который, естественно, не приходило ответа, так что очередь не двигалась.
Причина была проста: при подселении кода из candac16_src.c
совсем забыл, что каналы ЦАПа идут теперь не с 0, а с
WRCN_BASE=24
, которую надо прибавлять/вычитать при
операциях ввода/вывода. Теперь вроде заработало.
10.12.2005: так, на будущее: Надо в драйверах ВСЕГДА, даже если некий массив каналов идет с 0, иметь некую константу "что-то_BASE" и прибавлять/вычитать ее.
В остальном же -- поскольку на "последнем прогоне" эксперимента драйвер отработал нормально -- то помечаем как "done".
01.04.2006: БЛИН!!! Не работали каналы записи -- при уставке 0 на выходе было -10V!
(Только-только вчера стали использовать каналы записи 208-го -- для Абдульманова в 3-м здании, а раньше их не использовали, вот проблема и не вылезала.)
Разобрались. Дело в том, что Козак, при переходе от команд w=0x00-0x0F/r=0x10-0x1F к w=0x80-0x8F/r=0x90-0x9F, заодно поменял и кодировку значений -- если раньше было little-endian, то теперь -- big endian. А я этого не заметил, и просто скопировал код из candac16_src.c.
Поправил -- теперь все окей...
marcankoz_fd_p()
НЕ проверялось, какой именно ТИП пакета
нам пришел, т.е. -- поле "приоритет".
Даже если забыть об "общекорректности", можно считать, что нам просто везло, что никаких иных, помимо "reply" (7) не приходило. Не говоря уж о том, что в multi-host environment (например, при отладке) оно б точно сдурело.
30.01.2006: вставил проверку, сразу после
log_frame()
-- т.е., прямо перед разбирательством, что за
пакет.
03.02.2006: посмотрел систему команд -- ну, блин! Есть как плюсы (удобства), так и минусы (неприятности).
Имеем ту же проблему, что с IS10, где команда "pa" скопом вычитывала море разных параметров, писимых (пишимых?) разными командами, из-за чего мог выходить "сбой синхронизации выдачи подтверждений" (обсуждение см. за 27-02-2005).
09.02.2006: насчет дилеммы "16-битные коды или сотни наносекунд" -- и то, и другое! Т.е., каналы 0-7 -- коды, а 8-15 -- сотни наносекунд. Просто драйвер перед отправкой в устройство в случае прихода через каналы 8-15 будет пересчитывать наносекунды в код с учетом прескалера.
10.02.2006: итак, карта каналов:
14.02.2006: изготовил начальную версию cgvi8_drv_i.h, где эта карта и определяется.
15.02.2006: дополнил карту определениями регистровых каналов, на основе свежевведенного cankoz_common_drv_i.h.
31.03.2006: первое тестовое включение с использованием этого драйвера. Ну ладно, что было несколько совершенно дебильных багов и опечаток -- их-то я пофиксил.
Хуже оказалось другое: эта "концепция" с "aliasing'ом" управляющих каналов-кодов на "100ns-каналы" -- это просто полная задница! Это СПЛОШНЫЕ НЕУДОБСТВА!!!
Во всех этих случаях будет выполняться некорректный перевод.
07.05.2010: сегодня оказалось, что драйвер вроде как-то не совсем корректно отрабатывает дерганье питания у блока: после выключения и потом включения почему-то каналы на экране остались болотными, как будто драйвер просто не стал их считывать. Надо бы повторить этот тест еще разок вручную...
И еще: работа с маской-то ВООБЩЕ ОТСУТСТВУЕТ!!! (Так что при старте блока маска считывалась лишь потому, что читаются каналы BASEBYTE и PRESCALER, а это вся эта троица вычитывается по GETDEVSTAT=0xFE.) (делалось на стенде у Ращенко).
24.05.2010: работа с маской сделана, полностью, и
проверено -- работает. Так что старый код в cgvi8_rst()
,
посылавший mask:=0xFF,prescaler:=0 -- выкинут.
Но в общем-то код получился диковатым -- спасибо Козаку за упихивание в одну команду WRITE_MODE=0xF0 записи и маски, и прескалера (да и общее чтение всего через GETDEVSTAT -- немногим лучше). Нынешний вид кода меня слабо устраивает, надеюсь, в будущем cgvi8_drv.c будет лучше.
01.06.2010: вообще-то конкретно прескалер там не
пахал -- из-за глупости: там было ДВА экземпляра
cur_prescaler
, просто и mode.cur_prescaler
, и
писАла она в mode., а использовала в расчетах -- обычный. Решил просто
-- оставив только в mode
.
26.07.2010: да, проверено на ЛИУ и у Ращенко -- новая схема отлично работает.
НО: поскольку на ЛИУ (kuznitsa) не было на экране управления маской ГВИ, то с новым драйвером у них просто нифига не работало (маска после включения питания была нулевой).
Напрашиваются две вещи:
Поскольку маска может быть любой, включая 0, то можно использовать такой приёмчик: значением-по-умолчанию сделать -1, а разрешенный диапазон -- 0...255, и "понимать" указанность маски по обнуленности битов вне 0-7.
Учитывая подлянскость системы команд -- придётся "следить" И за маской, И за прескалером (и указывать в auxinfo их также ОБОИХ).
27.07.2010: пока выкрутились, просто введя в kuznitsa управление битиками маски, но вообще-то проблему решать придётся...
02.12.2010: кстати, поскольку драйвер уже проверен в реальных условиях -- и у Ращенко/astand, и у Токарева/liu, то давным-давно пора "done".
01.11.2011@Снежинск-каземат-11: еще несколько дней назад заметил, что при загрузке режима в программе beam значения в ГВИ почему-то не прописываются. Они появляются на экране на мгновение, а потом -- обратно сбрасываются в 0. Квант стоит 51.2us.
20.12.2011@Снежинск-каземат-11: кроме ЗНАЧЕНИЙ еще тогда же заметил, что не прописываются и биты разрешения (в маске) -- конкретно самый нижний (он младший).
Сейчас стал разбираться, полез в debug.log -- и увидел там
Потом припомнил -- что, по лености, многое делается прямымиSendFrame(0:k61/6, dlc=3): No buffer space available
send_frame()
вместо
q_enq_ons()
. И тут всё стало ясно:
send_frame()
.
Исправляем на q_enq_ons()
:
send_wrmode_cmd()
.
Использование очереди в любом из сих двух мест сразу убирает проблемы "незаписи" в этом месте (причём 2-е -- в том числе и проблемы в 1-м; загадка... -- видимо, чисто по вероятности).
Резюме:
_term_b()
.
И, кстати, чтения-после-записи во ВСЕХ каналах надо делать так же -- не как сейчас
что отлично приведёт к таким же прикольчикам, а без-лишней-экономии, корректноif(is_write) { WRITE } read(IFNONEORFIRST)
if(is_write) { if(write(REPLACEFIRST) == NOTFOUND) read(ALWAYS) } else { read(IFNONEORFIRST) }
21.12.2011@Снежинск-каземат-11: а сегодня после вчерашнего исправления вылезла другая проблема: при загрузке режима значение PRESCALER не меняется (точнее, оно меняется в клиенте на мгновение, а потом возвращается к старому).
Стал разбираться:
send_wrmode_cmd()
: оно при
заполнении пакета WRITE_MODE (мать его!!!) имеет такой код:
item.data[2] = info->mode.wrt_prescaler? info->mode.req_prescaler : info->mode.cur_prescaler; . . . info->mode.wrt_prescaler = 0;
В результате -- если сначала прилетает команда на запись в PRESCALER, а потом -- в любой из битов разрешения, то в первом пакете оно отправляет правильное значение, а в следующих -- "старое".
Видимо, потому, что оно всегда слало напрямую, а затем ставило чтение в очередь с условием IFNONEORFIRST -- а если в очереди уже были другие пакеты (например, чтение записанных времён), то первый же GETDEVSTAT оказывался НЕ в голове очереди, и более их туда не лезло -- хотя последующие команды записи пхали уже обратно старое значение.
В результате, очень вероятно, реально в устройстве оставалось СТАРОЕ значение prescaler'а, но просто на экране мы этого не видели. 22.12.2011@Снежинск-каземат-11: проверил на старом, неисправленном драйвере, после загрузки режима монитором послав 0xFE -- нет, НЕ оставалось. Да и не могло (если подумать :-)) -- ведь если GETDEVSTAT был НЕ в голове очереди, то все отправки через send_frame() лезли в обход, оказываясь ПЕРЕД ним, так что он и считывал последнее значение.
wrt_prescaler
.
req_m_msk
только взводятся, но никогда
не сбрасываются (кроме как в _rst()
).
req_o_mask=0
после отправки пакета, и оно так же
делается через прямую отправку с последующим чтением по IFNONEORFIRST, и при
переводе на q_enq_ons()
там вылезет такое же "залипание старого
значения". Чё, тоже убирать обнуление req_o_mask после отправки?
Резюме:
17.04.2012: к вопросу о "работе с ioregs" -- да, проблема вылезла, безо всякого "q_enq_ons()". См. в разделе о баге в выходном регистре.
11.12.2012: краткая история добавления поддержки быковского варианта CGVI8 (код 32):
cgvi8_rst()
проверяется "либо/либо" (см. за 02-10-2012).
->set_regs_base()
.
А сегодня Федя Еманов заявил, что такое же нужно и у нас, в тех же самых целях. Вопрос -- а как?
08.02.2006: результаты собственно размышлений (в т.ч. при разговоре с Федей-jr.) и общения с Козаком:
- Первая мысль -- что это будет возможно только в следующей версии CX, через "хитрый", ioctl-подобный механизм "динамических методов драйверов" (call MAGICID COMMAND [PARAMS...]).
- Вторая мысль -- а ведь можно сие сделать прямо сейчас, при помощи больших каналов (и никакой "call" нахрен не нужен). Атрибутами будут номер канала, число точек, код-времени-измерения.
- В любом случае, внутренняя реализация будет такой: на некоторое время обычная работа с каналами блокируется (впрочем, блокировать-то там нечего, драйвер в ответ на запросы сервера всегда ничего не делает :-), блок перепрограммируется на одноканальные измерения, и вперед; по окончании же блок заново программируется на многоканальные измерения.
(Подразумевается, что работа идет в 2 стадии: сначала запускается измерение, а потом, когда оно завершено, данные скопом вычитываются.)
- А с клиентской стороны будет отдельная (от всяких linmag'ов) программа, a-la осциллограф, которая и будет отсылать bigc-request и рисовать результаты графиком.
- Проблема 0: а как, собственно, глядя на канал в linmag'е, понять, что именно надо спрашивать (какой номер большого канала и какой параметр "канал в блоке") у этой клиентской программы?
- Проблема 1: в отличие от CAMAC'овских быстрых АЦП, у CANADC40/CAC208 -- НЕТУ возможности автоматически остановиться по заполнению кольцевого буфера, и сколько измерений провести -- тоже указать нельзя, меряется от старта и до бесконечности. Соответственно, никакого сигнала о том, что N измерений произведено, не поступает.
(Как говорит Козак, такая куцесть была сделана оттого, что в CANADC40 дюже слабенький процессор, а работы на нем много.)
- Проблема 2: (следствие): понадобится иметь на один драйвер БОЛЕЕ ОДНОГО таймаута (как это предусмотрено для следующей версии CX), поскольку стандартный/0-й -- для функционирования очереди, а еще нужно ждать какое-то время, пока измерения закончатся.
(В принципе, можно-то и как-бы-без-таймаутов, просто заставить прибор все измерения сразу гнать в линию, но при времени 1мс мало того что линию забъют сверх меры, так еще и компьютер может либо опухнуть от получения сих пакетов, либо начать их терять.)
- А что, если блок в середине операции подскопытится, или связь порвется? Надо чтоб драйвер корректно подобную ситуацию диагностировал (причем ВСЕГДА -- хоть в стадии измерения (а вот как прилетит вдруг пакет FF!), хоть в стадии чтения) и, то ли рестартовал бы измерение (а стоит ли?), то ли обламывал бы, возвращал наверх результат "ошибка!" с соответствующими rflags, и возвращался бы к обычному многоканальному состоянию.
- Поскольку программа указывает номер канала и прочие параметры, то потребуется возможность как-то ответить "Ошибаетесь, хозяин!" в случае некорректного параметра. Т.е. -- нужен соответствующий CXRF_-флаг (CXRF_INVAL?), который скорее всего будет иметь смысл только для больших каналов (хотя... в будущем... когда можно будет уставлять параметры измерения обычных каналов...). 13.01.2007: флаг
CXRF_UNSUPPORTED
введен еще 28-02-2006.
У-ф-ф-ф... А иы реально этим собираемся маяться? :-)
13.01.2007: частично-то эта потребность может быть удовлетворена поддержкой драйверами одноканального режима (ключик "fast" (см. за 02-01-2007), но лишь частично.
(Тогда и проблема CAC208 никогда бы не возникла -- ибо там везде бы стояли оффсеты каналов.)
15.02.2006: поскольку в определениях каналов ПОЧТИ ВСЕГДА участвуют смещения регистров (18-канальный блок), то нужны ОБЩИЕ определения для них.
Вот и создал include/drv_i/cankoz_common_drv_i.h, включабельный прочими c*_drv_i.h-файлами. Содержимое таково:
т.е. то, что раньше было "*8_BASE", теперь стало "*8_BOFS" -- чтобы не возникало путаницы, поскольку это все оффсеты. Плюс -- добавлено стандартное определение количества каналов.CANKOZ_REGS_WR8_BOFS = 0, // Individual (channel-per-bit) outreg's bits CANKOZ_REGS_RD8_BOFS = 8, // Individual (channel-per-bit) inreg's bits CANKOZ_REGS_WR1_OFS = 16, // Whole, 8-bit outreg channel CANKOZ_REGS_RD1_OFS = 17, // Whole, 8-bit inreg channel CANKOZ_REGS_NUMCHANS = 18 // # of regs-block channels
Далее (завершалось сие уже 16.02.2006):
REGS_*
, и все переведено на новые.
А для "размеров" таких сегментов -- суффикс "_N_COUNT" (хоть он по большей части и не используется).
ReturnChanGroup()
к
номеру, полученному от устройства, добавляется
CANADC40_CHAN_READ_N_BASE
.)
CANDAC16_CHAN_WRITE_N_BASE
при отправке/получении пакета.)
Елы -- мало того, что самый простой и короткий драйвер, так еще и, пожалуй, самый короткий и простой из возможных! (А, не -- может быть еще драйвер на ОДИН-единственный канал :-)
*_main_info[]
переведены на константы *_N_COUNT
.
#define CANKOZ_REGS_MAIN_INFO {8, 1}, {8, 0}, {1, 1}, {1, 0}
и везде старая цепочка цифр заменена на него.
Короче -- считаем, что "done".
(Конечно, драйвер может и сам реагировать на такие пакеты и запоминать, но -- зачем?)
Так что -- потребно, 1) чтобы layer сохранял эти числа из пакета 0xFF; 2) иметь API для доступа к ним.
22.02.2006: сделал, и сохранение, и метод для
доступа к ним -- он именуется ->get_dev_ver()
, и
возвращает два числа по указателям, причем любой из указателей может
быть NULL'ом.
Этот метод вставлен в cankoz_liface_t
сразу за методом
add()
-- поскольку пока что это все равно не совсем layer,
да и драйверы все "местные", то тем самым никакая совместимость не
нарушается.
27.02.2006: собственно -- в cgvi8_src.c использование вставил, скорее всего там проблем не будет (а пока что фиг проверишь), так что -- "done".
02.10.2012: понадобилось кроме версии уметь получать также и КОД (тип) устройства. Что забавно, понадобилось для того же CGVI8 -- чтоб драйвер умел работать как с настоящим CGVI8, так и с быковским ГВИ8М, имеющим код 30 (?) (Драйвер при этом заказывает devcode=-1, а дальше сам следит за корректностью кода.).
Для этого добавляем еще параметр -- hw_code_p
, плюс,
естественно, сохранение полученного по 0xFF кода.
int ndata, ...
).
Это ЗНАЧИТЕЛЬНО упростит драйверы, да и объем их уменьшится.
22.02.2006: добавил такой метод в конец списка,
назвав его q_erase_and_send_next()
. Он принимает
параметры через varargs.
В принципе все довольно несложно, есть лишь одна тонкость:
Вva_arg()
надо указывать не тот аргумент, который "нам требуется", а уже promoted -- например, вместоuint8
надлежит указыватьint
. Иначе оно лается следующим образом:marcankoz_pre_lyr.c:832: `int8' is promoted to `int' when passed through `...' marcankoz_pre_lyr.c:832: (so you should pass `int' not `int8' to `va_arg')(и это НЕ warning!).
И еще две вещи:
prio=CANKOZ_PRIO_UNICAST
. С другой стороны -- никакие
иные вроде бы и не надо поддерживать.
НО: на будущее, возможно, стоит расширить семантику
q_erase_and_send_next()
-- чтобы он удалял пакеты,
НАЧИНАЮЩИЕСЯ с указанных данных. Тогда для пакетов записи достаточно
будет указывать либо только дескриптор пакета (код команды), либо еще,
вторым байтом, номер канала. А значение -- участвовать не будет.
Сейчас осталось только перевести на сей метод все имеющиеся драйверы.
27.02.2006: перевел. Что занятно -- ВЕЗДЕ метод
вызывается с одними и теми же параметрами: (,1,desc)
, где
desc
-- дескриптор из только что полученного пакета. И
даже в самом marcankoz_pre_lyr.c -- практически то же самое
(с отличием лишь в способе формирования параметров).
Стандартная присказка -- "теперь надо проверить" :-)
27.02.2006: кстати, а ведь сия функция могла бы заодно проверять, ПЕРВЫЙ ли пакет был удален -- ведь если не первый, то тут что-то не так.
Вставил детектирование такой ситуации, но там не делается ничего --
просто {}
.
05.05.2009: во-первых, сегодня первый раз понадобилось datasize!=1, а 2 -- для senkov_vip_src.c, где номер канала читаемого ЦАПа и АЦП указывается не кодом команды, а следующим байтом пакета.
Во-вторых, проблему с "чтобы он удалял пакеты,
НАЧИНАЮЩИЕСЯ с указанных данных" можно будет решить просто -- при
model->datasize<0
считать, что передан минус-размер,
и сравнивать по-другому -- что объем проверяемого пакета не меньше
указанного, и проверять именно первые N байт.
А в-третьих, этот механизм надежно работает уже больше 3 лет, так что помечаем раздел как "done".
15.05.2009: да, а вот сегодня уже начало требоваться "...НАЧИНАЮЩИЕСЯ с указанных данных" -- для пановского блока: у него там:
Так что -- сделал. Состояло из 2 пунктов: использование в
cankoz_q_erase_and_send_next()
значения
abs(datasize)
для проверок и циклов; в
elem_is_equal()
добавлена отдельная ветка для
datasize<0
(в ней используется не abs()
, а
сразу -datasize
).
Но осталось еще проверить.
21.08.2009: попробовал -- что-то не пашет...
09.02.2010: еще бы оно пахало!!! Там в
elem_is_equal()
стояло сравнение
elem->datasize <= -model->datasize
вместо
elem->datasize >= -model->datasize
Надобилось, как и раньше, для драйвера пановского УБСа (на этот раз -- уже новая версия, с человеческой системой команд, драйвер panov_ubs). Оно там начинало глючить и бесконечно слать один и тот же пакет -- поскольку не удаляло его из очереди вследствие неработавшего сравнения. Поправил -- всё заработало.
P.S. А ведь эта же проверка была скопирована один-в-один и в v4'шный
cankoz_lyr_common.c::cankoz_eq_cmp_func()
-- там
тоже поправил.
21.12.2011@Снежинск-каземат-11: в v4'шном оно так сделано, а тут уже едва ли будет. Так что -- "obsolete".
27.03.2006: А все оттого, как делается по is_a_reset "перезапрос" данных (точнее, как сервер заставляется перезапросить): это "лерганье" статуса ->NOTREADY->OPERATING. Дело в том, что сброс своих флагов оно делает уже определив проблему, но ДО дерганья статуса, а вот _rst() вызывает ПОСЛЕ.
Так что _rst() не может спокойно брать да сбрасывать у себя какие-нибудь флаги -- просто потому, что запросы, потенциально приводящие к чтению каналов, к которым относятся эти флаги, уже могли быть отданы сервером. А надо дергаться ДО...
31.03.2006: ну и что делать будем? Из "простых" просматриваются 2 варианта:
Пока склоняюсь ко 2-му, но надо отследить все "пути данных"...
03.04.2006: вообще -- какое, нафиг, "отследить"?! По самой очевиднейшей логике -- надо именно просто вставить ПЕРЕД дерганьем, да и все.
Короче -- переставил, а дальше "будем посмотреть", когда допишем поддержку регистра маски у cgvi8.
05.05.2009: поскольку сделано еще три года назад (!!!), и вроде должно было заработать (хотя хбз, вроде маску у cgvi8 так и не доделали), то помечаем как "done".
25.06.2010: дя-дя-дя!!! Вот только сегодня вылез другой прикол:
send_frame()
'ом, а enq_ons()
'ом.
Переставил очистку очереди при is_a_reset
ДО вызова _rst(), так что у нас теперь такая цепочка:
И в cankoz_lyr_common.c::cankoz_fd_p()
сделал
такое же изменение.
Вопрос -- и что делать?
(BTW, затронутые устройства -- CANADC40, CAC208, CDAC20, CAC168.)
08.06.2006: фиг знает -- некоего общего решения, которое сработало бы в 99% случаев, в голову не приходит.
Но есть некий паллиативный вариант -- "по аналогии с КШД485": для таких драйверов, которые сделали заказ, и более не вякают, надо уставлять таймаут -- а точнее, "метроном" -- который тикает раз в минуту, и там проверяется, когда же последний раз приходили данные. Если более, чем "цикл метронома" -- минуту -- назад, то шлется пакет "ping" -- 0xFF.
"НО" -- для этого понадобится дополнительный таймаут, поскольку default'ный драйверный -- уже занят очередью.
В качестве оптимизации: вообще-то, совсем необязательно работать с
"настоящим временем", постоянно дергая gettimeofday()
-- в
принципе, можно обходиться и номером текущего цикла. Правда, от
надобности в дополнительном таймауте это нас не избавит :-)
10.02.2007: а можно обойтись и без таймаута: сейчас ведь команды на чтение просто тупо игнорируются, а можно в них проверять дваность прихода последнего пакета -- и достигнется примерно тот же результат.
22.05.2009: ага, и забыл еще одну "деталь": ведь когда блок пинганётся после паузы, его НАДО ЗАНОВО ЗАПРОГРАММИРОВАТЬ. Что может быть отдельным приколом -- драйвер должен помнить ВСЁ, что слалось блоку, и повторять это.
А с таблицами что делать? Объём-то их небольшой, можно и в драйвере хранить...
25.08.2010: еще прикол: почему-то на сварке иногда устройства, ведомые драйверами senkov_vip и weld02 посиневают, но достаточно сделать sato -- и устройство "оживает". Т.е., явно в какой-то момент устройство "отпадает" и потом возвращается к жизни молча, и драйвер этого не замечает.
Кстати -- конкретно от ВИП'а последний 0xFF с кодом 0:PowerOn был аж 09-03-2010; прямое показание к нынешней ситуации. Впрочем, от WELD01:Repkov PowerOn'ы приходят, но вот конкретно сегодня почему-то как-то драйвер, похоже, перевключения не заметил.
Еще раз, битым текстом:
Да, с правильно работающими блоками такая проблема НЕ ДОЛЖНА проявляться.Но могут возникать всякие дурные ситуации, типа того передергивания CAN-кабеля (а какие еще возможны сценарии?), при которых будет теряться соответствие между состоянием, знаемым драйвером и реальным состоянием блока.
Посему -- НЕОБХОДИМО реализовывать это "слежение за живостью блока", чтобы драйвер мог приводить всё в чувство АВТОМАТИЧЕСКИ, безо всяких sato. Это будет важным шагом в правильном направлении -- наряду с reconnect'ами в cda и cm5307/cangw/remdrv.
Ох...
24.09.2010: а ведь всё ОЧЕНЬ просто, с учётом
13.02.2011: почему-то тогда не записал, пишу сейчас: и
"метроном" в xcac208/xceac124 также сделан -- поле
privrec_t.rd_rcvd
плюс функции _alv()
@60секунд,
при rd_rcvd==0 посылающие 0xFF; а уж _rst()
по ЛЮБОМУ 0xFF
вызывает SendMULTICHAN()
.
Итого: просто заводим метроном с периодом 60 секунд, и при определении зависания -- ставим в очередь пакет 0xFF с how=QFE_IF_ABSENT.
И еще, насчёт определения зависания: нафиг не нужны ни gettimeofday(), ни GetCurrentCycle(). Всё намного проще -- достаточно иметь булевский флажок, который по приходу данных от АЦП взводить в 1, а в конце обработчика метронома сбрасывать в 0. Вот автоматом и получится ровно то, что нужно.
Сделано по вышеприведённому проекту в xcac208_drv.c и xceac124_drv.c. Традиционно -- надо проверять.
07.03.2013: традиционно -- не первый год работает, "done".
CANDRIVERSSOURCES
.
Смысл -- чтобы при сборке canserver'а (для CANGW) список драйверов брался сразу из этого файла.
Так вот: ведь в таком случае можно включать не многоканальные измерения (0x01), а ОДНОканальные (0x02) -- тогда он сможет шпарить единственный канал в 14 раз быстрее (4 -- на канал, 10 -- калибровка перед циклом).
Детали реализации могут быть такими: чтобы драйвер "не умничал", то
надо включать такой режим не просто при ch_beg==ch_end
, а
ввести еще дополнительный параметр "singlechan", и им уж
указывать на необходимость такого режима.
Потенциально такое можно вставить (и очень легко!) в драйверы всех козачиных АЦП -- CANADC40, -CDAC20, CAC208, -CAC168 ("-" помечены пока не поддерживаемые устройства).
02.01.2007: вставил в canadc40_src.c и cac208_src.c. Все довольно просто. Только вместо "singlechan" пользуемся словцом "fast" -- так короче.
Теперь надо проверять.
20.01.2007: проверил -- работает.
Заодно вставил в оба драйвера проверку на ошибочное указание beg>end.
В общем -- "done".
15.04.2013: а в новых, x-драйверах (xcac208 &
Co.) этот функционал не реализован. Видимо, потому, что там beg/end
могут меняться на лету. Видимо, нужно в SendMULTICHAN()
вносить соответствующие мозги.
25.04.2013: сделано, но надо проверить.
14.06.2013: проверено на xcac208 -- работает. А для корректности изменён логгинг -- теперь оно сообщает, что именно отправило (MULTICHAN/OSCILL).
q_enq_ons()
.
Причина -- например, если из устройства ненадолго выдергивается кабель, то большинство команд прекрасно сохранятся в очереди, команды же записи в регистры уйдут в пустоту и будут проигнорированы. А уж какие разнообразные сценарии могут быть, если связь исчезает в некий хитрый момент между получением одного пакета и отправкой следующего...
Единственный возможный контррезон -- гусиный: что при восстановлении связи драйвер выплюнет сразу много данных на запись, возможно, сильно отличающихся от текущих, что с точки зрения аппаратуры не есть хорошо.
marcankoz_add()
отсутствовала проверка на занятость
запрашиваемого устройства.
18.01.2007: вставил, точную копию из
c4lcankoz_add()
.
09.02.2007: сделано, детали таковы:
#include CANKOZ_HAL_H
, а уж указать
CPPFLAGS+=-DCANKOZ_HAL_H='"???cankoz_hal.h"'
-- задача
Makefile'а.
canhal_open_and_setup_device()
-- теперь она
именно только открывает устройство и уставляет baud rate, а уж
регистрацией файлового дескриптора и отправкой бродкастного 0xFF
занимается cankoz_add()
.
canhal_writeframe()
-- почти не отличается от
того, что было раньше.
canhal_readframe()
-- сюда из
cankoz_fd_p()
вынесен read()
с проверкой.
В общем -- стало очень красиво: работа с адаптером -- отдельно от собственно мозгов, заведующих очередью.
10.02.2007: и c4lcankoz_hal.h также сделал. Проверил -- работает.
16.02.2011: давно очевидно, что этот проект давным-давно прочно и основательно накрылся -- из-за ухода Вадима. Так что 'obsolete".
21.04.2008: приступаем. Файл cdac20_drv_i.h изготовлен.
23.04.2008: да и сам драйвер уже дописан (точнее, в основном доСписан с cac208'шного). Теперь надо проверять.
29.04.2008: начал проыерять -- мда, побольше бы надо внимательности к деталям!!! У сего девайса характЕрным является число 0x800000 (восемь с ПЯТЬЮ ноликами) -- это середина диапазона кодов, ее надо вычитать/прибавлять и использовать как делитель/множитель при переводе из кода в микровольты и обратно. А я, лабух, написал в коде 0x800000 -- восемь с ЧЕТЫРЬМЯ ноликами. Результат был потряснейший -- программа показывала, что после включения питания из блока считывается уставка в 150В!
Поправил, естественно. Завтра доиспытываю.
04.05.2008: проверили -- работает, так что "done".
P.S. Отдельный вопрос -- какой ставить квант DAC-каналу? Поскольку там некратное маппирование (да и вообще формула мерзковатая -- с игнорированием младших битов), то самое разумное -- 10 единиц/микровольт.
11.03.2011: понадобилось уметь ПЛАВНО менять уставку -- для чернякинских ИСТов (которых надо окучить море).
Так что -- делаем xcdac20_src.c, копированием из xcac208_src.c. Для простоты даже карту каналов оставляем совпадающую -- с местом под 8 каналов ЦАПа.
14.03.2011: на совместимость забили, теперь там по 1 каналу на OUT/OUT_RATE/OUT_CUR.
Надлежащие изменения в пересчёт кодов внесены, плюс оно пользуется
CDAC20-specific-командами для ЦАПа, вместо "стандартных" 0x80/0x90.
В начало _big_p() просто
вставлен return
. 23.05.2013:
вообще всё содержимое убрано.
Проверено -- драйвер работает, всё плавненько крутит.
08.09.2011: а вот поддержка таблиц там гарантированно нерабочая. 23.05.2013: и вообще выкошена нафиг, чтоб не маячила.
this_targ
используется kozdac_val_to_c16()
,
вместо вроде бы положенной cdac20_val_to_daccode()
.
08.09.2011: тэк-с, а поддержку калибровки (CALIBRATE) и цифровой коррекции (NUMCORR), присутствующих в этом девайсе, мы делать будем?
Там, кстати, будет иметь место некоторая сложность: канал NUMCORR_V расположен в config-блоке, объявленном как w20 -- так что СЕРВЕР потребует его чтения только один раз. А надо б как-то вычитывать АКТУАЛЬНОЕ значение.
28.02.2012: да, делаем.
Для начала -- везде (глобально!) меняем фиг знает как родившийся термин "NUMCORR" на "DIGCORR".
XCDAC20_CHAN_AUTOCALB_PERIOD
(на место RESERVED19). В него указывается период, если он 0 -- то
"отключено" (отдельного канала на вкл/выкл нету...). Реализовано оно
прямо в _hbt().
Стандартно -- осталось проверить :-)
28.02.2012: проверяем.
Всё из-за дурной модели с необходимостью заполнять item
вместо нормального набора отправляющих функций (которые, кстати, ВСЕГДА
шлют UNICAST -- в очереди остальное бессмысленно).
if()
'ом в начале. Пришлось переставить эти 4
вперёд.
Опять же -- ведь это уже вовсе НЕ config-каналы в том смысле, что остальные (adc_mode, do_reset, do_tab*), а вполне нормальные. Но на них иного места нету. Всё-таки надо расширять с 20:30 до 50:50 (см. за 06-09-2011) -- тогда эта проблема уйдёт.
02.03.2012: переделываем по схеме "60:40".
XCDAC20_CHAN_AUTOCALB_ONOFF
,
"период" переименован в XCDAC20_CHAN_AUTOCALB_SECS
, а
команда калибровки -- в XCDAC20_CHAN_DO_CALB_DAC
.
XCDAC20_CHAN_DIGCORR_FACTOR
переехал в
RD-область и теперь читается сам по себе.
05.03.2012: проверяем.
Так что вставлена отдельная альтернатива с "пустой" реакцией.
xcdac20_in()
была недопеределана реакция на
DESC_DIGCORR_STAT
-- оно пыталось возвращать при помощи
ReturnCtlCh()
; пофиксено.
Итого -- тот функционал доделан.
28.03.2012: имелся недочёт, обычно незаметный, но вылезший на ist_xcdac20: в ответ на запрос от сервера на чтение драйвер преспокойно прямо в _rw_p() возвращал значения CHAN_OUT_CUR, прямо сразу, даже когда они еще ни разу реально не были прочитаны.
Обычно это никак ни на что не влияло -- ну моргнёт разок при старте 0 (и то не факт, что успеет).
А вот ist_xcdac20 старался получить значение ОДИН раз, чтобы рассматривать его как текущую уставку. И получал он всегда 0.
Вставлена защита -- ch_rcv[]
, уставляемые в 1 при
получении, сбрасываемые в 0 по _rst(), и если оно еще не было
выставлено, то _rw_p() ничего не станет возвращать. 23.05.2013: это распространено и на
xcac208/xceac124.
07.03.2013: в xcdac20_src.c добавлена поддержка CEAC51 -- он практически идентичен по набору команд (отличия лишь в таблицах, которая одна).
Для этого проверка на тип устройства переведена в
_rst()
-- аналогично ГВИ8.
cankoz_fd_p()
при получении
пакета GETDEVATTRS не упоминает magicid устройства, к которому этот
пакет относится, а всегда делает логгинг как
MAGIC_NOT_IN_DRIVER
.
15.10.2008: есть два варианта решения:
dp->magicid
в выдаче.
dp->magicid>0
использовать его для
передачи DoDriverLog()
.
И -- это надо делать с учетом предстоящего перехода на layer'ы.
16.10.2008: сделал по 2-му варианту.
08.04.2009: задача состояла из двух частей:
Сделано:
А сам драйвер-пустышка -- соответственно, sktcankoz_drv.c.
09.04.2009: попробовали на foxy.inp.nsk.su с Tews 1498:032a -- работает!
09.04.2009: поскольку функционально CEAC124 -- это практически ровно половинка CAC208, то изготовление произведено практически копированием с контекстной заменой и уменьшением числа каналов чтения на 8 и каналов записи на 4.
Плюс: в _drv_i.h-файлах для cac208, ceac124 и cdac20
добавлены описания специализированных "полускрытых" каналов, вида
Cxxx_CHAN_READ_nnn
, где nnn -- P10V
,
0V
, T
, PWR
и DAC
(этот только у CDAC20) для +10V, 0V, термодатчика, источника питания и
канала ЦАПа соответственно. Прикол в том, что у разных блоков эти
каналы в разном порядке.
Проверил -- работает!
28.05.2009: да, реализовано -- пока скорее в виде хака:
bid
,
чтобы оно бралось от первого драйвера, который сошлется на данную
линию.
iface->add()
/cancoz_add()
передавались
лишь пара параметров line
,kid
, явным образом
извлекавшиеся из bid
каждым драйвером.
bid
.
cankoz_add()
он раскодируется, и 3-й байт
передаётся в canhal_open_and_setup_device()
2-м
параметром, который под это и был в своё время зарезервирован.
canhal_open_and_setup_device()
'ы
(кроме sktcan'овского) используют этот 2-й параметр -- теперь он
называется speed_n
-- для уставки скорости.
01.06.2009: несколько замечаний по опыту работы/использования:
По теме же -- да, работает, так что "done".
_rst()
выполняют программирование устройства, а
reason=3 -- после реализации "консольного интерфейса в коробочках" --
может быть и просто из-за команды "canlist".
Да и из общих соображений: ведь "who are here" -- идеологически совсем не то же самое, что все остальные коды, это именно скан шины, а не результат запроса от драйвера или оживания блока, так что драйверу этот пакет видеть совершенно незачем.
02.06.2009: да, сделал -- при
CANKOZ_IAMR_WHOAREHERE
метод _rst()
НЕ
вызывается.
Теперь надо смотреть на последствия -- не вылезет ли где.
02.12.2010: да вроде за полтора года нигде не аукнулось. Так что -- "done".
12.08.2009: за эти два дня сделал.
ID:b0,b1,b2,...bNлибо
kKID/PRIO[.RESV]:b0,b1,b2,...bNт.е. --
_IOLBF
-буферизация stdout'а
там делается).
canhal_writeraw()
, принимающую на вход готовый ID -- а то
canhal_writeframe()
берет отдельно kid,prio.
Некоторые выводы по опыту создания этих утилит:
Но это слишком муторно, так что пока будем обходиться парой утилит -- поскольку у каждой карточки по 2 линии, то можно вешать candump на 0-ю, а cansend на 1-ю. Собственно, сейчас так и делал. (А уж если ну совсем-совсем припрёт -- ну тогда можно и подумать об универсальной утилите "canmon_HAL".)
В принципе -- это очень похоже на то, что требовал Тарарышкин&Co. для ТНК.
И как сие можно сделать? Нечто похожее на механизм для получения доступа к логгингу всех пакетов -- специальным как-бы-драйвером (без аппаратуры). И -- 1) а не понадобится ли в сервере иметь директиву "загрузи такой-то просто-модуль"; 2) а как это будет работать с CAN-сервером в КОРОБОЧКЕ?
19.10.2009@ТНК: в дополнение к cansend+candump создан также canmon -- необходимый для cangw, где только один процесс может держать CAN-линию открытой.
Команда ":[msecs]" работает как "ждать [некоторое время] принимаемые пакеты". Когда пакеты-для-отправки в командной строке указаны, оно работает как cansend; когда нет -- неявно добавляет команду ":", эффективно работая как candump.
Фактически, canmon обладает ВСЕМИ фичами candump+cansend, и может заменить обоих.
20.10.2009@ТНК:
devtypes[]
и
GETDEVATTRS_reasons[]
перемещены из
cankoz_pre_lyr.c в свежесозданный kozak_strdb.c.
len
и data
в
cankoz_pre_lyr.c переименованы в can_len
и
can_data
-- для унификации.
21.10.2009@ТНК:
CANKOZ_
-enum'ы из
cankoz_pre_lyr.h в новый cankoz_numbers.h, доступный
для can*_common.
22.10.2009@ТНК: надо
чтобы canutil_common.c::CanutilOpenDev()
КОРРЕКТНО ПРОВЕРЯЛ бы правильность указания CAN-линии, а то сейчас оно
просто берется за 0. Короче -- надо atoi()
заменить на
strtol()
с проверкой.
04.11.2009: да, сделал.
16.02.2011@Снежинск-каземат-11: оказывается, canmon НЕ воспринимал с stdin'а команду :МИЛЛИСЕКУНДЫ -- просто не стояла проверка (а оно потребовалось Панову для игрищ с выводом на восьмисегментниках УБСов всяких надписей).
Исправил.
(Селивановский can4lgw это делает, и нам тоже стоит -- для возможности сосуществования и работы по очереди.)
02.11.2009: во-первых, это вопрос даже не canserver'а, а cankoz_pre_lyr.c'а.
А во-вторых, при этом коробочка будет себя вести более похоже на marcan и sktcan -- поскольку в односерверном случае (которых пока 100%) при каждом запуске сервера линии будут открываться заново, и заново будет получаться "карта" по 0xFF.
04.11.2009: да, вроде сделал.
Во-первых, пришлось во все *cankoz_hal.h ввести функцию
cankoz_close_device()
, которая просто делает
close(fd)
.
Во-вторых, собственно требуемое свелось к маленькому кусочку кода в
cankoz_disconnect()
, пробегающему по списку драйверов
линии, и если что-то есть, то делающему return
, а далее,
после цикла -- закрывающему линию.
В-третьих, ради приличия в canutil_common.[ch] добавлена
CanutilCloseDev()
и во все 3 утилиты вставлен её вызов.
Теперь осталось только проверить.
18.11.2009: проверил на коробочке -- линию он отпускает, "done".
24.03.2016: вылезло неприятное последствие (уже в v4, но это неважно): на CANGW команда "СТОП" устройству от последнего (на линии) из закрываемых драйверов обычно до устройства не доходит, и устройство продолжает молотить в линию пакетами "очередное периодическое измерение".
Причина в том, что (как случайно замечено вчера на играх с c4lcanmon'ом) пакет, "отправленный" непосредственно перед закрытием линии, реально отправиться не успевает -- потому, что драйвер SJA1000 делает тому reset (то ли еще до начала отправки пакета, то ли в процессе).
Единственным решением могло бы быть "отложенное" закрытие:
fdio_deregister_flush()
,
Вместо этого придётся помнить о "почти-закрытости" линии, и в случае прихода запроса на её открытие отменять закрытие.
Очень уж это всё хлопотно -- игра не стоит свеч, выигрыш (сейчас?) скорее академического характера. Так что ничего предпринимать не будем -- фиг с ним, пусть один-два девайса продолжают сыпать в линию.
Так что, свободно можно везде ставить по умолчанию квант 4 (20мс) вместо нынешнего 5 (40мс). Что и сделано в имеющихся драйверах.
07.12.2009: highlights:
canhal_readframe()
возвращают некое
отрицательное значение. А уж конкретным отрицательным числом и будет
кодироваться причина ошибки. Например, -2
-- bus-off.
(Естественно, -1
не трогаем.)
Следствие: очевидно, что протокольчик "cankoz_hal" (точнее -- даже просто "can_hal") надо формализовать в каком-нибудь .h-файле. (А в v4 так и вовсе подмывает взаимодействие cankoz_lyr<->HAL организовать не прямым связыванием, а через VMT.)
21.11.2011: пункты 2 (частично), 3 и "Следствие"
уже давно реализованы; формализовано всё в виде констант
CANHAL_OK
, CANHAL_ZERO
, CANHAL_ERR
,
CANHAL_BUSOFF
.
НЕ сделан же пункт 1 и частично 2 -- ничегошеньки-то с "error frames" мы не умеем, да и BUSOFF никто из троицы mar/c4l/skt ловить не умеет.
21.11.2011: в продолжение: сегодня наблюдена ситуация, что marcanhal при "пустой" линии (все устройства отключены) приводил к тому, что cx-server начинал якобы жрать 99% CPU. Реально это явно был кривой марафонский candrv -- у него что-то переклинивает, и выглядит так, будто процесс-юзер потребляет проц, а реально это заскоки драйвера. Тут, правда, летит всего по несколько сотен IRQ в секунду, а не тысячи, как с TPMC-810, но всё же.
Собственно, идея: а что, если ПЕРЕД попыткой отправки пакета проверять готовность дескриптора на запись? Тогда при переполненном выходном FIFO оно бы и не пыталось заниматься лишней активностью.
Тут не всё так гладко, есть сложности:
last_wr_r
) есть только у "юзера", у *canhal.h же её нет,
и тому остаётся только ВСЕГДА перед write() делать и check_fd_state().
check_fd_state()
во все canhal_send_frame()'ы, чьи
подстилающие драйверы это поддерживают, да и всё?
13.05.2010: итак:
do_sendnext()
таймаут на 100ms
уставляется только при УСПЕШНОЙ отправке, а при r!=0 она возвращает
QFE_ERROR и до уставления не доходит.
Интересно, когда и ПОЧЕМУ было сделано именно так?
send_frame()
, в обход очереди. Это
должно вызвать постоянный поток измеряемых данных, НО: данные от АЦП
идут в обход очереди, и НЕ вызовут посылку очередного пакета.
Одно ясно -- если реализуем keepalive-pings, то ситуацию "блок казался мертвым в течение некоторого времени; на какой-то ping вдруг ответил" надо приравнивать к "0xFF,is_a_reset".
cankoz_sender()
'е результат
SendFrame()
и не проверяется -- всегда возвращается 0.
Так что подобного поведения там не будет.
last_wr_r
.
28.05.2010: вот и наткнулись на ситуацию, когда такое поведение -- с прекращением повторов при -1/"No buffer space available" -- доставляет реальную проблему:
Короче -- надо переходить на более свежую схему с
last_wr_r
, при которой:
Приступаем. Анализ:
canhal_writeframe()
дергается в
нескольких местах:
send_wrreg_cmd()
;
cankoz_sendframe()
-- метод
send_frame()
для драйверов;
do_sendnext()
-- собственно, наш пациент;
CanKozSend0xFF()
.
Результат вызова используется только в (c) и (d), причём ВСЕ
драйверы игнорируют результат send_frame()
.
send_frame()
0.
(И вообще -- во-первых, надо отходить от прямой посылки, а всё делать через очередь (оставляя для прямых посылок только "режимные" команды, типа MULTICHAN, STOP, etc. (ой ли?! А если таблица не успела загрузиться, как пришло TAB_STOP?)); во-вторых -- в будущем это превратится в ОДИН вызов (отправить/в-очередь).)
canhal_send_frame()
на wrapper (как это и сделано в
cankoz_lyr_common.c), заботящийся о last_wr_r
.
Поскольку внесение модификаций в нынешний код всё равно оставило бы местами уродливость, принято решение не маяться, и не тащить старый код, а провести маленькую революцию: перейти на v4'шные HAL-модули из drivers/can/common_sources/. Протокол:
Замечание: мастер-копии -- живут именно в drivers/can/common_sources/.
Соответственно, имя макроса сменено с CANKOZ_HAL_H
на
CANHAL_FILE_H
.
lineinfo_t
добавлены поля last_wr_r
и
(за компанию) last_rd_r
.
SendFrame()
.
НО: она не void
, а int
-- т.е.,
вызывальщик всё-таки МОЖЕТ понять успешность/обломность. Причина --
чтоб oneshot-пакеты могли оставаться в очереди при обломе и пытались бы
посылаться дальше.
SendFrame()
.
26.05.2012: тогда при "революции" код отправки
благополучно лишился вызова log_frame()
(жившего в
???cankoz_hal.h::canhal_writeframe()
), что на
CANGW (не позволяющем подслушивать тот же порт) сильно усложнило
отладку. Посему оно вёрнуто в
cankoz_pre_lyr.c::SendFrame()
.
06.08.2013: тогда почему-то в
SendFrame()
был вставлен вызов
log_frame(DEVID_NOT_IN_DRIVER,...
вместо
log_frame(devid,...
. В результате оно пыталось логгировать
пакеты чёрт-те-куда (особо пикантно было при отстреливании одного
устройства по "duplicate" -- оно оказывалось "последним", и весь
логгинг пытался идти на него -- т.е., ->stderr->/dev/null).
Исправлено.
01.06.2010: проверил на sktcankoz'е -- вроде, работает.
Так что теперь надо реализовывать НЕ-прекращение пере-посылок и НЕ-выкидывание из очереди oneshot-пакетов в случае ошибки отправки. Плюс -- собственно отсылку MULTICHAN делать не прямой посылкой, а _ons()'ом.
10.06.2010: завершаем -- изготавливаем НЕ-прекращение:
cankoz_sendframe()
еще тогда вставлен безусловный
return 0
.
perform_sendnext()
, пытающаяся
отправлять пакеты "по-умному":
cankoz_q_sendnext()
удалена вообще, вместе с
do_sendnext()
.
Вроде всё -- теперь надо проверять.
25.06.2010: да, проверил. Там по-прежнему стояла ПРЯМАЯ отсылка DESC_MULTICHAN -- перевел на _ons. Плюс, была сложность с чисткой очереди pre_lyr'ом, но это поправил -- после чего всё заработало, теперь драйвер xceac124 подхватывает оживший блок стабильно. В xcac208 тоже портировал.
Засим наконец-то считаем раздел за "done".
22.09.2010: неприятность: в какой-то момент CAN-линия на l5s0 стала "дребезжать": она то была готова, то случался busoff. И в результате в лог пачками посыпались сообщения об ошибках -- поскольку каждое сообщение шло после УСПЕШНОЙ посылки.
Что с таким делать -- непонятно. Можно завести лишний интеллект -- кроме last_wr_r помнить время и тип последней ОШИБКИ, и НЕ логгировать при повторе, например, менее чем через минуту. Но это также фигово -- вполне легитимные сообщения об ошибках будут подавляться (или -- вводить еще более лишний интеллект, который бы подсчитывал количество таких повторов и "flush'ил" бы сообщение при появлении ошибки другого типа.
20.12.2011@Снежинск-каземат-11: в SendFrame()
в
диагностике по ошибке записи полезно было бы видеть и КОД пакета (0-й байт
данных). Добавил (для невозможных у нас случаев с dlc==0 будет выдавать
0xFFFFFFFF).
В drivers/can/common_sources/cankoz_lyr_common.c тоже добавил, и
в canmon_common.c::CanutilSendFrame()
тоже.
canhal_writeframe()
, а через очередь --
cankoz_q_enqueue()
.
13.05.2010: причиной перехода на такую модель стало поведение CEAC124, который в условиях своей не-единственности на линии (CEAC124:id=61, CGVI8:id=60, BIIP:id=2) умудрялся терять ответ unicast-FF. (Да, ситуация идиотская -- тот же CGVI8 вообще отвечал на оба FF'а.)
Разговор с Козаком дал один ответ: Козак точно не готов сказать, нормально ли это, и не "баг" ли это, но -- на пакеты, предполагающие ответ, надо давать блоку возможность ответить. Поскольку тут как раз такой случай -- значит, надо давать.
Посему -- произведена переделка. Плюс, естественно, в _fd_p() вставлено и удаление такого пакета из очереди (бесхитростно -- просто по приходу ЛЮБОГО FF делается erase_and_send_next).
После этой модификации ситуация резко изменилась -- 124-й прекрасно работает. Единственный drawback -- теперь отсутствующим блокам по 10 раз в секунду шлются FF'ы.
Надо ввести аналогичное изменение и в common_sources/cankoz_lyr_common.c. Только "таймаут" на такой пакет ставить не стандартные 100ms, а, например, 10 секунд.
Немного деталей о причинах того поведения блока: дело в том, что почти сразу же после unicast-FF блоку отправлялся и 0xF8 (чтение регистров). Но -- в то же время линию занимали другие блоки, с бОльшим приоритетом, и 124-й никак не мог отправить ответ на FF. А тут приходил еще пакет, на который также надо было отправить ответ -- и, похоже, он перетирал в отправном буфере предыдущий пакет; поскольку, как говорит Козак, на ВСЕ такие пакеты используется один и тот же "message object" (а вот на всякие busoff-recovery и периодические измерения -- другие). А теперь, с отправкой unicast-FF через очередь, пакет 0xF8 придерживается до получения ответа на FF.
26.05.2013: вроде всё работает хорошо, но вот сегодня на yamkshd8 вылезла странность: изначально, при запуске сервера+драйвера устройств на линии не было -- единственный KSHD8 был обесточен. При включении же в лог вывалилось 402 входящих пакета 0xFF -- первые два PowerOn и WhoAreHere, а за ними 400 GetAttrs. Учитывая, что размер очереди ставится YAMKSHD8_NUMCHANS*2=200*2=400 -- выглядит очень подозрительно. Как они могли туда попасть?
04.09.2013: причину понял еще несколько дней
назад: во-первых, какое-то своё внутреннее FIFO (непонятного по
исходникам размера
(can_defs.h::MAX_BUFSIZE
400)) есть в селивановско-мамкинском драйвере; во-вторых,
камилевский модуль умудряется отвечать на ВСЕ получаемые пакеты (а не
только на первый/последний).
01.06.2010: проблема была в том, что
send_wrreg_cmd()
НЕ делало
dp->ioregs.req_o_mask=0
после отправки пакета
(причём -- сама эта строчка там имелась, но почему-то была
закомментирована).
В результате после перезагрузки блока в нём было OutReg=0, но драйвер при записи в некий битик пытался другие, КОГДА-ТО записанные битики ставить в те, старые значения.
Так что -- строчка раскомментирована, и проблема исчезла.
11.10.2011: опять какая-то странность с выходным регистром: на ceac124, что стоят в МРН в вакуумной стойке с ВИП-27, почему-то, когда начинаешь быстро включать битики УРа один за другим, некоторые почти сразу "отжимаются".
20.12.2011@Снежинск-каземат-11: а не в том же ли проблема, что оказалась сегодня и в cgvi8 -- т.е., в прямой посылке (в обход очереди) в условиях забитого FIFO?
send_wrreg_cmd()
вызывает SendFrame()
(что давно уже является моим стыдом).
17.04.2012: в продолжение темы -- баг вылез посерьёзнее.
ioregs.req_o_mask=0
проблема ушла.
Теперь вопросы:
Когда и какое изменение привело к этим глюкам?
req_o_mask=0
-- где и как
это может аукнуться?
Расследование быстро нашло ответ на 1-й вопрос:
cankoz_fd_p()
::0xFF::is_a_reset
что-то не вполне понятное, в т.ч. комментарий "Do we need to send a
write request?", после которого ничего нет.
18.04.2012: попробовал разобраться. Результаты:
Это всё уходит корнями еще в старую версию, жившую в istc/drivers/canadc40_drv.c (доступна в BACKUP/istc.20060228). Обсуждение имеется в разделе "CAN:", начиная с 16-06-2005, особенно за 07-07-2005.
ПОЧЕМУ было закомментировано -- стопроцентно неясно, но есть подозрение, что это произошло при разборках с неработающестью регистров 20-09-2005--28-09-2005. Причина-то оказалась в другом, но, возможно, как закомментировали в процессе отладки -- так и забыли, а оно относительно работало.
Но: ведь самое правильное -- делать req_o_mask=0 по
is_a_reset
, вместе с занулением were_read и pending!
Да -- вставил сброс req_o_mask=0
по is_a_reset
(cankoz_lyr_common.c too). Надо бы везде попроверять (panov_ubs, curvv,
...).
15.05.2012@Снежинск-каземат-11: проверяем:
mode.req_m_msk
и так НИГДЕ не сбрасывалось,
кроме is_a_reset; у mode.wrt_prescaler
(выполняющий аналогичные
функции) -- также обнуление было закомментировано, т.е., уже "нынешний"
подход.
ptregs
, а were_read и
pending.
Исправлено -- но еще не проверено.
send_wrmsks_cmd()
ничего не нулилось; с другой стороны -- по is_a_reset также нулилось не всё
msks
, а were_read и pending.
Также исправлено, но не проверено.
Кстати, нынешняя модель ХОРОША, но с одним ИСКЛЮЧЕНИЕМ:
(Вопрос тут -- в какой момент можно НАДЁЖНО "сложить с себя ответственность", обнулив req_*_mask, и начать считать уже блок авторитетом?)
24.05.2012@дорога-на-работу;около-ИХБФМ: ага, опять "передача ответственности" -- это и есть КЛЮЧЕВОЙ термин.
Когда -- наверное,
if (pending==0) req_o_mask=0
Там как раз везде есть
if(pending!=0){send_wr();SEND_RD()}
-- вот к нему
else
и приделать.
С таким "правильным" изменением начинает
представляться подозрительным нынешний подход к использованию
"were_read" в regs_rw()
и подобных: там тупо стоит
if(were_read){send_wr();SEND_RD()}
. А как это будет себя
вести при НЕСКОЛЬКИХ последовательных записях, когда чтение после одной
еще не успеет придти перед следующей?
26.05.2012: в продолжение:
send_wr*()
до сих пор пользуются
send_frame()
.
QFE_IF_NONEORFIRST
(в обоих местах -- и по получению
CANKOZ_DESC_READREGS при наличии pending
, и по команде
записи), а...
!were_read
-- с
QFE_IF_ABSENT
.
Насколько это всё нормально?
16.08.2013: в ту же степь -- делаем унификацию по именам полей везде, где используем эту концепцию:
were_read
превратилось в
rcvd
.
pending
сократилось до pend
.
cur_i
стало
cur_inp
, чтоб проще, однозначнее и красивее сделать
следующий пункт -- ...
cur_o
, req_o_val
,
req_o_mask
переименована в более однородную
cur_val
, req_val
,
req_msk
(оно так было названо в новеньком (вчера
начатом) smc8_drv.c, и это красивше, ибо речь о ЗАПИСЫВАЕМОСТИ,
а заодно-читаемость (столь бесящая у Козака) -- побочна).
cankoz_fd_p()
:
сейчас он считывает по ОДНОМУ пакету, а их может накопиться несколько,
и будет лишний раз дергаться серверов основной цикл.
01.06.2010: да, надо бы пытаться читать пакеты,
пока они есть (т.е. -- пока не вернут CANHAL_ZERO
). Но,
на всякий случай, например, не более 10 пакетов за раз.
История: на прошлой неделе Федя Еманов поинтересовался, как у меня с поддержкой CURVV -- потому как им оно, возможно, потребуется для системы контроля открытости дверей/защиты на комплексе (сейчас там -- всё чисто железно, а с компа узнать нельзя). Ответ был, что пока нету, но сделать -- не проблема.
01.12.2010: ну так -- сделал. Поскольку поведение дополнительных регистров идентично обычным F8/F9-регистрам, то код скопирован из cankoz_pre_lyr.c, с адаптацией под драйверный (а не внутри-layer'ный) API.
Пока не проверено.
05.02.2011@Снежинск-каземат-11: тьфу ты, а поддержку ОБЫЧНЫХ
регистров в curvv_rw_p()
тогда вставить забыл. Маладес!!!
Чуть позже: и вместо TTL-битов возвращал "мусор", поскольку вместо ttl_vals отдавал pwr_vals. И db_curvv.h вместо CURVV_CHAN_TTL_24B показывал CURVV_CHAN_REGS_RD1 -- всё результаты копирования, блин!!!
P.S. Хохма ситуации в том, что испытания проводились не на настоящем устройстве, а на пановском блочке, который ПРИКИДЫВАЕТСЯ, что он -- CURVV.
Причина: для локальной задачи нужен был девайс с УР и СДС по 2 бита, и, чтоб не делать/регистрировать лишнее устройство, выбрали "прикрытием" стандартный блок с самой простой системой команд -- это оказался CURVV.
Девайс просто эмулирует CURVV, отдавая HWver=SWver=0; PWR-регистр пишется, но ничего не делает; TTL-регистр всегда отдаёт 0.
01.03.2019: есть подозрение, что реализация записи в
регистр не вполне корректна: там сначала вызывается
send_wrpwr_cmd()
, а потом для обратного вычитывания --
q_enq_v(, SQ_IF_ABSENT, 1, DESC_READ_PWR_TTL)
.
Но ведь получится криво: если в очереди УЖЕ есть читающий пакет (от предыдущей записи, ещё недоотработанной), то на данную запись вычитывание произведено НЕ будет.
Похоже, спасло нас лишь то, что якобы-CURVV используется только на ЛИУ-2, в виде пары пановских девайсиков, и в очень ограниченном применении:
PANOV_FROLOV_BASE
) -- там 2 бита, но они всегда используются
только раздельно, а чтоб 2 сразу ехали (и, соответственно, записывались бы 2
бита быстро друг за дружкой) -- такого нету.
PANOV_CAMSEL_BASE
)
-- там всего 1 бит.
Так что если и присутствует ошибка, то у неё не было шанса проявиться.
Но вроде бы аналогичный код должен присутствовать и в других драйверах,
потенциально требующих использования SQ_REPLACE_NOTFIRST
.
03.03.2019: тут-то, в v2hw/, ничего исправлять уже не будем. Продолжение разбирательств см. в bigfile-0002.html за сегодня.
16.08.2013: ага, начат -- как же! Только cir8_drv_i.h тогда и был сделан, и с тех пор всё.
send_wr*_cmd()
и т.п.), может -- пора уже сделать готовую
инфраструктурку (типа template), на которую перевести весь этот
карнавал вариантов? Ведь механизм не самый тривиальный, и легко
допустить ошибку.
14.01.2011: да, железно надо -- надоело уже для каждого случая такого функционала тратить час-два времени для копирования и адаптации, да еще и трястись, не забыл ли чего при копировании.
Что успокаивает -- общая схема действий пока вроде бы остаётся единой (то, что базируется на were_read+pending).
BTW, а ведь эта фигня -- потенциально не только CAN-specific, но sendq-specific. Ведь у RS232/485-девайсов могут быть аналогичные заскоки.
cankoz_add()
при ошибке
открытия линии по canhal_open_and_setup_line()
делалось
return CXRF_DRV_PROBL;
вместо надлежащего
return -CXRF_DRV_PROBL;
В результате потом в лог пёрли сообщения типа
_cankoz_q_enqueue_s: invalid handle 16384 (=>0:0)
24.01.2011: исправил, конечно. Но самое
печальное -- что этот ляп успел пролезть и в
drivers/can/common/cankoz_lyr_common.c::cankoz_add()
.
cankoz_q_erase_and_send_next()
-- теперь и отправка очередного
пакета, и даже удаление указанного делаются только в случае, если указанный
найден в голове очереди.
20.04.2011@Снежинск-каземат-11: ситуация:
Т.е., сервер/клиенты получат в качестве актуального некое "старое" значение, затем выполнится запись, но её результата никто не узнает.
cankoz_q_erase_and_send_next()
добавлена проверка
«если очередь не-пуста, и пакет в голове -- "не наш", то
return».
21.04.2011@Снежинск-каземат-11: проверено, вроде работает нормально, так что "done".
22.04.2011@Снежинск-каземат-11: даже более того -- пановским УБСам резко похорошело (раньше они захлёбывались в этих лишних пакетах, и на стойке с 6 УБСами при загрузке режима или перезагрузке "высокие номера" стабильно посиневали).
06.07.2015: вот только само условие там сделано халтурно:
(условно-сокращённое описание), что в sendqlib-вариантах в ottcam_drv.c и cankoz_lyr_common.c превратилось вif (ring_used!=0 && elem_is_equal(ring+ring_start)) return NOTFOUND;
вместо надлежащегоfe = sq_access_e(q, 0); if (fe != NULL && _eq_cmp_func(fe, &item) == 0) return SQ_NOTFOUND;
-- т.е., "если ОЧЕРЕДЬ_ПУСТА или В_ГОЛОВЕ_НЕ_ТО то отвалить".if (fe == NULL || _eq_cmp_func(fe, &item) == 0) return SQ_NOTFOUND;
Нынешний вариант безопасный -- оно потом сделает ERASE_FIRST и send_next пустой очереди -- но просто дурной.
Поскольку в случае изменения проверять надо, а сейчас не на чем, то пока оставлено как есть, с пометками в комментариях.
_in()
НЕ проверяют размер пакета (хотя
даже комментарии /*!!! Check datasize? */
частенько есть):
Панов в эмулируемом CANADC40 присылает "ACK"'и длиной 1 байт (просто код
команды, в подтверждение, что эта команда им получена); а драйвер, не глядя
на размер, тупо смотрел следующие байты и брал оттуда мусор.
Вывод: надо это исправлять.
Заодно: везде проверять номера каналов, присылаемые в пакетах.
canadc40_in()
: вставлено сравнение как на длину пакета,
так и на номер канала (оное стоит ПОСЛЕ логгинга PKTINFO, но перед
возвращением данных, тем самым обеспечивая возможность разборок без
дополнительных логгингов и приседаний).
candac16_in()
: проверка просто на размер. Заодно ввёл
там chan
.
cac208_in()
, ceac124_in()
: комбинация
предыдущих двух.
xcac208_in()
, xceac124_in()
,
xcdac20_in()
: аналогично не-x-версиям.
tvac320_in()
шибко мутно -- так что временно на него
забито.
vsdc2_in()
-- тоже, от него просто тошнит.
panov_ubs_in()
, естественно, ничего не потребовалось --
там давно уже везде стояли проверки.
cankoz_fd_p()
доп.условие, чтобы
оно НЕ проверяло код устройства, если пакет содержит меньше 2 байт (тогда
считается devcode=-1).
Но передумал: а какого черта -- если уж такие пакеты летают, то пусть драйвер побыстрее отключится, привлеча внимание к проблеме.
30.10.2011@Снежинск-каземат-11: приступаем.
30.10.2011@Снежинск-каземат-11: доделываем:
01.11.2011@Снежинск-каземат-11: проверяем. По несколько багцов обнаружилось и у меня в драйвере (неудивительно -- копировалось нахрапом с 208-го, у которого всё-таки другая идеология), и в пановском коде.
Драйвер вроде причесали, так что "done".
CanKozSetLogTo()
, для управления включенностью
log_frame()
(чтоб разгрузить слабенький процессор CANGW).
19.11.2012: и в canserver.c добавлен
консольный интерфейс для её вызова -- функции
disable_log_frame()
и enable_log_frame()
.
send_frame()
.
17.05.2013: xcanadc40 сделан, просто и халтурно -- карта каналов на основе kozdev_common_drv_i.h, а исходник драйвера старый, просто с заменой "CANADC40" на "XCANADC40" (24.05.2013: названия каналов переделаны на новый стиль -- типа "ADC_n_base" вместо "READ_N_BASE".). Переключение TIMECODE, GAIN, BEG/END на лету пока не поддерживается.
И db_xcanadc40.h нарисован.
21.05.2013: за компанию сделан db_kozdev_common.h, чтоб можно было натравливать на любое из совместимых устройств (не особо задумываясь о конкретном типе).
Монструозненький экранчик получился, и бОльшая часть каналов бордовенькая.
23.05.2013: приступаем к xcandac16...
ch_spd[0]
, оно в init_d() размножает на
все остальные. Это добавлено и в xcac208/xceac124 (раньше в них вообще
нельзя было указывать скорость).
24.05.2013: и db_xcandac16.h изготовлен.
Теперь -- проверять надо обоих.
19.09.2013: да, проверено (причём ещё и в связке с v3h_xa40d16) -- оба работают.
23.05.2013: оно начато было давно, как минимум 11-04-2012. Зачем -- пёс его знает, следов использования нигде не видно (как и записей о нём), "db_xcead20.h" отсутствует. Сделано явно на основе canadc40 (это его евромеханический аналог), но на карте kozdev_common.
14.10.2013: вылезло это на gid25@ВЭПП4, где Карнаев что-то напортачил с джамперами, так что по адресу 63 в дополнение к CEAC124 водился также CGVI8M. Проблема в том, что после вроде бы исправления проблемы (в железе) на экране это никак не отражается.
По-хорошему, надо б таковое считать не фатальной проблемой, а рассматривать как частный/хитрый случай того, что требует "reconnect'а". Т.е., например:
SetDevState(, DEVSTATE_NOTREADY, CXRF_WRONG_DEV, )
Или требовать, чтоб "этому" reset нажали? Тоже не айс...
Или всё-таки продолжать раз в N секунд слать пакеты 0xFF?
Но тут может быть прикол, что начнутся "метания", вследствие попеременного получения пакетов от "того" и "не того".
А это может быть решено "режимом недоверия" с повторами:
Одна неприятность: мозни получаются мега-навороченными, и надо б их
тогда как-то сделать доступными для "множественных" устройств, с
разными device type code. ...или лучше вместо
единственного kozdevinfo_t.devcode
завести массив
элементов на 10: чтоб сравнивалось со всеми возможностями, по
add().devcode
заполнялся бы [0], а прочие б можно было
добавить отдельным вызовом.
30.10.2013: множественные devcodes[] сделаны в
будущей версии, создаваемой в drivers/can/nsrc/. Регистрация
дополнительных делается методом add_devcode()
.
А прочее (с доверием/недоверием) пока не начато.
21.12.2016: уже на v4 -- проблема вылезла опять: в магнитной системе линака (cangw-magsys) с адреса 63 вместо положенного пакета вроде
ID=0,63, DevCode=1:CANDAC16, HWver=1, SWver=7, Reason=0:PowerOn
было получено
ID=0,63, DevCode=85:???, HWver=105, SWver=17, Reason=1:Reset
что привело к "cankoz_fd_p: DevCode=85 differs from registered 1.
Terminating device".
Так что надо делать поддержку "нефатальности" получения чужого 0xFF.
22.12.2016: просто "ждать у моря погоды" (т.е., следующего, уже "правильного") 0xFF нельзя -- надо активно "пинговать" устройство. 23.12.2016: ну да -- это же было придумано еще 3 года назад (просто сейчас не помнилось).
23.12.2016@утро-по-пути-на-работу: но просто сразу слать пинг-пакет -- нельзя. Надо бы через какую-то задержку. А как сделать просто пустую задержку ПЕРЕД пакетом?
На уровне sendqlib -- никак: нету там API для просто добавления таймаута (и чтобы сделалось tout_set=1).
А можно так: использовать пакеты с dlc=0 для
задержек; т.е., физически такой пакет НЕ отправлять (это в компетенции
cankoz_pre_lyr.c::cankoz_sender()
) -- тогда как бы
ничего не произойдёт, а таймаут будет уставлен как надо.
26.12.2016: не очень-то это красиво -- отпадёт возможность слать "пустые" пакеты. Хоть в CAN-BINP/CAN-Kozak они и не используются, но само по себе не комильфо.
24.12.2016@МДМ-банк-платил-за-квартиру: надо при счётчике!=0 запрещать постановку в очередь всего, что не-0xFF. Чтобы драйверы не пытались работать с не своим устройством.
Резон: если устройство сначала было OK, а потом появилось "чужое", то, хоть и будет уйдено в NOTREADY (в котором сервер запросы драйверам не шлет), но "в полёте" сервер->CANGW могут быть уже запросы -- которые драйвером будут приняты к исполнению и приведут к отправке пакетов.
И еще, насчёт того как/когда:
26.12.2016: ... неа, нифига не отпадает!
26.12.2016: тот (последний) 0xFF, что вызвал обнуление счётчика и "переход в доверие", надо для драйвера переделывать в is_a_reset.
26.12.2016: пытаемся делать.
devcode_chk_ctr
.
cankoz_q_enqueue()
не пропускаются
никакие пакеты, кроме 0xFF.
@по-пути-обратно-после-проходной-ОК: совсем по-хорошему -- надо бы вообще иметь флаг "was_good_ff_rcvd", и пропускать пакеты в любую сторону только при нём !=0. И изначально делать его =0, сбрасывать по чужим пакетам, и ставить =1 по приходу хороших (точнее -- в точке перед передачей 0xFF'а драйверу: чтобы и в режиме недоверия оно б срабатывало как надо). Но, учитывая, что у нас устройства сейчас рождаются НЕ в NOTREADY (коему бы "was_good_ff_rcvd=0" отлично соответствовало), с этим будут проблемы.
"Красиво" -- было бы просто делать определение, и результат помещать в
булевскую devcode_is_correct
, с которой потом уже что-то
делать.
Так было бы легче модифицировать код "решения" -- а там нужно что-то менять в логике.
Проблем 2:
devcode_is_correct
, то всё
переделывается тривиально.
...но:
sq_pause()
.
Некоторые замечания:
sq_clear()
аргумент "сколько выдержать паузу после очистки"». Но это
а) менее гибко; б) сломает совместимость.
sq_pause()
можно
использовать:
sq_clear()
;
27.12.2016: теоретически, можно было бы задействовать очередевы таймауты -- которые при работе через sq_port не используются. Но это именно если припрёт, ибо получится нехилое усложнение (там и так приподзапутано).
27.12.2016, получасом позже: что интересно, в работе с
портами очередевы tout_set
тоже проверяются. Видимо, наследие
от до-портовых времён; в любом случае, "если припрёт" -- код к такому
дополнению (заюзывание очередевых таймаутов) уже готов.
27.12.2016: делаем.
sq_pause()
сделана.
devcode_is_correct
.
devcode_is_correct
-- также отдельным
if(){}else{}.
В нём и делается вся работа по "восстановлению доверия":
devcode_chk_ctr=5
.
is_a_reset=1
и проваливание ниже в
обычную обработку 0xFF.
Замечание: пакеты типа CANKOZ_IAMR_WHOAREHERE
игнорируются,
т.к. они ниже зафильтрованы от передачи драйверовым ffproc()
, а
нам НЕОБХОДИМО, чтобы после прихода последнего пакета -- когда происходит
"восстановление доверия" -- драйвер был дёрнут. Ну фильтрация нестрашна --
дождёмся ответа на очередной unicast.
is_a_reset=1
легло очень в струю: как раз по
нему ниже делается дрыганье ->NOTREADY,->OPERATING -- т.е., всё
получилось автоматом, дополнительно переходить в OPERATING и не
потребовалось.
Собственно -- теперь уже проверять (и исправлять косяки ;)). Видимо, уже после НГ.
05.01.2017: попробовал -- есть какой-то косяк при уходе в NOTREADY, remdrv пишет (с подачи fdiolib'а)
ProcessIO: FATAL: received packet is too big
Видимо, remcxsd'шный SetDevState() при наличии description
придуривается; хотя сходу в нём косяков не видно. Надо сниффером смотреть.
06.01.2017: да, проверено сниффером и найдена ошибка в remcxsd'шном SetDevState() (и как только она раньше не вылезла?!).
Затем проведено тестирование -- ручным присыланием пакета якобы от имени блока (kNN/r:0xFF,...) -- которое показало работу в полном соответствии с задумкой.
Теперь просто пособирать статистику, не глюканёт ли еще где да как.
03.04.2017: статистика за 3 месяца показывает, что всё окей. Итого -- цель достигнута, раздел закрываем.
DriverBody()
. Это
нужно как минимум для предстоящего драйвера репковских ИПП, да и для
осциллографов тоже будет нелишним...
16.05.2006: за последнюю неделю, когда понадобилось изготовить не-абстрактный драйвер cm5307_karpov_monster, заодно изготовил и "расшитый вариант driverbody". Оно живет в cm5307_dbody.[ch], пока что -- в work/karpov/drivers/.
Реализован -- по проекту раздела "Об интерфейсе удаленных драйверов", простейший вариант за 01-07-2005. Отличия/детали:
HandleDriveletInput()
не "возвращает код действия и
заполняет union", а ей передаются указатели на функции-методы cfig_p,
rdwr_p, bigc_p.
Замечу: там именно ОДНА функция rdwr_p (а не read_p+feed_p) -- для унификации с интерфейсом обычных драйверов.
ReturnChanGroup()
и ReturnBigc()
по параметрам полностью унифицированы с серверовыми, даже лишний
параметр magicid
имеется.
А вот ReturnChanSet()
отсутствует -- во-первых, она
практически не используется, а во-вторых, протокол cm5307-drvlet такой
операции не поддерживает. 04.09.2013: плохая была
идея, ох плохая...
InitDrivelet()
'у main()
'ом драйвера.
DoDriveletDebug()
/vDoDriveletDebug()
пока
недоунифицированы -- но на то у них и названия пока отличаются от
серверовых. Отправляют через тот же sndbuf.
select(1,[0],NULL,NULL,{1s,0us})
, причем
SIGUSR1
разблокируется только на время этого
select()
'а, чтобы когда-попало не дернулся обработчик LAM,
вызывающий отправку данных.
Теперь в принципе можно переводить i_cm5307.c с cm5307_drvletbody.[ch] на cm5307_dbody.[ch].
03.06.2006: перетащил cm5307_dbody.[ch] уже в cx/src/programs/server/drivers/cm5307/common/.
Некоторые замечания:
Cxsd*
.
magicid
" теперь сменено на "devid
".
init_b
вместо bid
(который
раскодируй-как-хочешь) теперь принимает int *businfo,int
businfocount
; в "описании" же драйвера указывается
(min_busid_n,max_busid_n).
DRVS_OFFLINE=-1
, DRVS_NOTREADY=0
,
DRVS_OPERATING=+1
, либо -rflags. Не 0!
bid
, ни fd
не передаются -- пусть складируют в privrec'е, именуемом
"me
". Только devid
и privptr
.
paramtable
, в которую "хозяин" сможет pre-parse'ить
содержимое auxinfo. Если NULL -- ничего не делается.
DEFINE_DRIVER()
, и
должно стоять в конце файла (резоны -- см. ниже).
char *layer,int layerver
(пока -- под хвост).
{,v}DoDriveletDebug()
заменены на целиком
унифицированные {,v}DoDriverLog()
.
(Естественно, "полностью" -- с точностью до предполагающейся в CXv4, но пока нереализованной унификации номеров обычных и больших каналов.)
DEFINE_DRIVER()
определяет в себе
main()
, в котором вызывает InitDrivelet()
, а
потом select(,[stdin],...) с вызовом по надобности
HandleDriveletInput()
.
Разве что "вытягивание" из драйвера надлежащего объема буфера пока не сделано -- оставлено 2000, доделаем вместе с новым i_cm5307.c.
И, поскольку API таймаутов и файловых дескрипторов пока нету, то
"нетривиальные" драйверы -- типа karpov_monster и u0632 -- пока имеют
собственный main()
со специфичными трюками.
DEFINE_DRIVER()
будет просто определять DriverRec -- вот и унификация.
Для этого (надеюсь, экзотического, возможно раз-два
потребующегося случая) просто делаем специальный драйвер, который
создает собственную "среду исполнения", при помощи
#define
'ов подставляя свои методы взамен стандартных, а
"подчиненные" драйверы включая при помощи #include
(воизбежание конфликтов -- включая из раздельных
.c
-файлов).
Единственный проблем такого подхода: этот хак одноразов, поскольку сварганить больше уровней из такой конструкции будет затруднительно -- начнутся перекрытия с #define'ами, непредвиденные разопределения, и т.д. Впрочем -- более чем один уровень едва ли понадобится.
А если уж вдруг возникнет реальная, регулярная осмысленная потребность -- то тогда надо делать внутрисерверный channel-API, который бы позволил одному драйверу обращаться к каналам другого.
11.01.2009: пришло время под-доделать
cm5307_dbody.h::DEFINE_DRIVER()
в отношении
размеров буферов. Теперь вместо былого фиксированного
CM5307_DBODY_IOBUFSIZE
, равного 2000, введены
#define-параметры CM5307_DBODY_RCVBUFSIZE
и
CM5307_DBODY_SNDBUFSIZE
, которые если
программой-пользователем не уставлены, принимаются за те же 2000.
Кроме того, #include "misc_macros.h"
переставлен
из cm5307_dbody.c прямо в cm5307_dbody.h (он все
равно включается практически всеми затрагиваемыми файлами). Смысл --
заменить имевшуюся в DEFINE_DRIVER()
халтурную проверку
результата select()
'а "errno!=EINTR
" на
надлежащую "!SHOULD_RESTART_SYSCALL()
". Заодно
управильнил эту проверку и в cm5307_fastadc_common.h,
cm5307_u0632.c и cm5307_karpov_monster.c
(А вот в staromakh.c трогать не стал -- вообще давно
пора вынести check_fd_state()
в отдельный модуль, как и
планировалось в bigfile.html еще 11-05-2003!)
12.01.2009: Кстати -- вся эта шобла разнообразных пользователей
cm5307_dbody (включая новый i_cm5307!) явно напрашивается на
унификацию, чтоб вместо их собственных main()
'ов с
"основными циклами" всегда использовался бы
DEFINE_DRIVER()
12.02.2014: эта идея доведена до логического
конца полгода назад, когда ВСЕ драйвлеты переведены на
DEFINE_DRIVER()). По идее, в API для этого все есть --
надо лишь:
init_b()
и
term_b()
.
privrec
.
29.07.2011: да, "dbody-API интерфейс CAMAC" наконец-то сделан, а вот с LAM'ами ничего не получится -- из-за реализации в CM5307 через сигналы работа с ними так и останется сильно специфичной.
29.07.2011: вообще-то ПЕРВОНАЧАЛЬНАЯ цель раздела давно выполнена, сейчас уже 2-е поколение делается, так что -- "done".
14.01.2012: позорище: вместо SIGUSR1 везде реально разрешался/запрещался SIGIO.
Затронутые файлы -- cm5307_karpov_monster.c (да-да, видимо, с него тогда всё и началось! В остальные уже копировалось...), i_cm5307n.c (этот вообще никому нафиг не сдался -- он не используется), cm5307_fastadc_common.h (а вот это -- да-а-а... Как оно вообще работало и не падало постоянно...).
Исправлено...
Ну и че -- обе проблемы решит DHCP?
cm5307[16]: cm5307_fd_p:ERREXIT: Success
Самое забавное, что так померли сразу ТРИ чугуевских драйвера, причем в
04:02-04:03 утра 09.12.2004. Занятно, не правда ли? Именно в это время
запускается /etc/cron.daily. Переполнение каких-то буферов
из-за большой загрузки процессора?
20.12.2004: расследование показало, что в
принципе в cm5307_drvletbody.c::DriverBody()
имеется 3 штуки break
из основного цикла, приводящие в
конечном итоге к завершению драйвера.
А никаких сообщений об ошибках там и рядом не валялось... С другой стороны, при нарушениях протокола пытаться делать отладочную печать -- несколько сомнительная идея. Хотя -- почему бы и нет?
В общем -- вставил DoDriverDebug()
во все три точки,
возможно, после пересборки драйверов в будущем что-нибудь и вылезет.
31.01.2005: Виталий Мамкин посоветовал -- что у него эта хрень любит так падать при re-#define, и что стоит окружать все такие места условиями. Угу, вставил на каждый из htonl()/htons() по #ifndef'у.
Надо это свести в одно место и сделать enum
'ом.
07.04.2005: ввел
cm5307_drvlet_proto.h::CM5307DLP_MAXDATASIZE
,
сейчас равное 20100*4. "20100" -- это до 20000 int'ов данных плюс
CX_MAX_BIGC_PARAMS
=100. Плюс
CM5307DLP_MAXPKTSIZE
, включающее дополнительно
размер заголовка.
Далее -- позаменял все 32000 и 8000 в cm5307_drv.c (заодно
корректно разделив, где размер данных, а где -- всего пакета, и
позаменяв просто int
на более корректный
int32
). И -- в common/cm5307_drvletbody.c
аналогично.
Итого -- потенциальная чугуевская проблема вроде бы решена.
07.04.2005: но остается другая проблема: ведь в
adc333 УЖЕ возможен больший объем -- до 65535 short'ов. Увеличивать
CM5307DLP_MAXDATASIZE
? Но это тоже плохо -- тем самым мы
будем тратить в каждом драйвере море памяти впустую, ведь в большинстве
драйверов большие каналы не используются вовсе. И передавать нужный
объем буфера DriverBody()
для последующего
malloc()
'а -- тоже нельзя, поскольку в uClinux с
динамической памятью плохо.
А что, если отводить буфер прямо в "основной программе"
драйвера, и указатель на него передавать вместе с объемом в
DriverBody()
?
16.05.2006: да, cm5307_dbody.[ch] создавался как раз под такую архитектуру. Так что теперь открывается возможность именно вышеобозначенным методом и решить проблему. С двумя оговорками/дополнениями: 1. Буферов-то ДВА -- входной и для отправки/логгинга. 2. Объем надо определять не только из соображений БОЛЬШИХ каналов, но и ОБЫЧНЫХ.
17.12.2007: да, реально возникла необходимость
уметь выгребать ВСЮ память adc333 и adc200 -- на стенде для ЛИУ. Так
что -- забил на "будем тратить в каждом драйвере море памяти
впустую" (да, абстрактные драйверы все еще живут на
cm5307_drvletbody.[ch], а не на cm5307_dbody.[ch],
shame on me...), и увеличил CM5307DLP_MAXDATASIZE
с
20100*4 до (65535+100)*4. 06.05.2013: до
(131071+100)*4 -- для nadc4, nadc502.
Первый же результат -- схлопотал ошибку линковки uclinux-драйвлетов "region flatmem is full". Погугля, нашел намек на ответ -- надо было поменять число в файле tools/cm5307/arch/user.ld; стояло 0x40000 (256K), я добавил еще нолик (стало 4M).
31.12.2007: замечание на будущее -- вообще-то кривость исчезнет при переводе драйверов adc200 и adc333 с абстрактных на cm5307_dbody.[ch]. И еще -- надо все-таки, черт подери, переводить и абстрактные тоже с _drvletbody на _dbody!
10.08.2011: а ведь у этой проблемы имеется еще
один аспект: максимальный размер пакета, который готов
принять СЕРВЕРНЫЙ драйвер (cm5307/remdrv). Ведь он сейчас равен
REMDRVP_MAXPKTSIZE
, но для некоторых fastadc (nadc502,
например) может быть и больше...
11.08.2011: и что -- вводить в драйверный API дополнительный вызов "а скажи-ка, какой объём моего n-го большого канала"?
04.09.2013: основная проблема исчезла как класс с переходом на fdiolib'ный remcxsd месяц назад, так что ставим "obsolete".
А проблема с максимальным размером принимаемого на стороне сервера пакета исчезнет при переходе на аналогичный remote_drv.
RRUND_DEFAULT_PORT=8000
, избавив rrund.c и
cm5307_drv.c от раздельных констант.
::Run()
: сейчас там порядок действий
следующий:
Имеем следствие: rrund DDoS'ится тривиально -- первым же
connect()
'ом. Это вроде как нехорошо, и, по логике, надо
СРАЗУ уфорковываться и уже там читать имя программы (хотя в случае с
vfork()
это без разницы).
С другой стороны, нынешняя последовательность обеспечивает сериализацию -- что rrund НИЧЕГО не вякнет (типа "can't fork()"), пока ему не отправят имя программы целиком.
Я влез в эти дебри сегодня, при написании новой,
не-блокирующейся-на-connect()
'е версии
cm5307_drv.c. И там нынешнее поведение представляется явным
благом.
30.04.2010: в can/gw/ppc_canserver/ и в 4cx'ной версии rrund (точнее -- в remdrvlet_listener'е) используется такая же последовательность, только чтение делается неблокирующимся и отваливающимся по таймауту, что и решает все проблемы. Более того -- именно такой подход и является максимально адекватным (всё централизованно и управляемо).
Так что -- считаем вопрос закрытым как "obsolete".
12.05.2006: еще деталь -- такое поведение ТОЛЬКО у ppc_drvlets/, а в uclinux_drvlets/ -- нормально (как-то сказываются ELF'ы).
И -- кстати, главная проблема: НЕ собираются в ppc_drvlets/ DIRECT_-драйверы. Т.е., оно просто говорит "Nothing to be done for...".
15.05.2006: разобрался, чего не хватало в ppc_drvlets/ из имевшегося в uclinux_drvlets/: в дополнение к шаблону "%.drvlet: %_drv.o" (это -- для абстрактных) требовался еще "%.drvlet: %.o" (для DIRECT_-).
Но вопрос-то остался -- почему ж это make просто отваливает, ничего не делая, вместо того, чтобы ругнуться? Эта проблема есть и с абстрактными, и с DIRECT_...
А вообще -- надо избавляться от этой "косвенной компиляции" (gcc -o hrundel.o another_name.c), и пусть уж лучше оно просто для каждого .o-файла создает .c-исходник -- либо копируя, либо симлинком. Как это сейчас и делается с chlclients/ -- что позволяет заодно иметь нормальную генерацию зависимостей в .d-файлы (а не единственную предопределенную в i_cm5307_rules.mk, как сейчас с абстрактными драйверами).
03.05.2010: несколько дней назад начал работы по переходу от косвенной компиляции к прямой. Писец это просто полнейший!!!
Покамест по результатам получил 2 куска знаний:
Автопеременная $*
принимает своё
значение ОТ ШАБЛОНА ТОГО ПРАВИЛА, КОТОРОЕ ОПРЕДЕЛЯЕТ СОЗДАНИЕ target'а,
в чьём контексте $* используется. Точка. А вовсе не по тому pattern'у,
который определяет pattern-specific variable.
Я же пытался выпендриться, написав правило (генеримое)
i_cm5307_%_drv.d: CPPFLAGS+=-DABSTRACT_NAME=$*
полагая, что в качестве $* оно подставит "имя устройства" (то, чему
соответствует '%' в шаблоне "i_cm5307_%_drv.d" -- для
cm5307_sukh_comm_drv.d это должно было бы быть "sukh_comm").
Ага -- хрен-то там! Поскольку $*
использовалась в
контексте правила "%.d: %.dep
", то в качестве
значения бралось имя файла за вычетом расширения.
(Конечно, там еще надо было это всё делать в отношении файла .dep, а не .d, но относительно описываемого принципа сие уже неважно.)
Если немножко подумать и вспомнить принципы функционирования GNU Make, то всё кристально понятно и правильно, но сходу, глядя на pattern-specific variable definition -- это неочевидно.
Надо быть аккуратнее с автогенерацией: make должен иметь возможность В ПЕРВЫЙ ЖЕ ПРОХОД узнать способ создания всех своих include'имых частей, иначе он сразу же и обламывается, ругаясь в стиле "make: *** No rule to make target `cm5307_sukh_comm_drv.d'. Stop.".
Проблема заключалась в том, что автогенерились И cm5307_rules.mk, И cm5307_*_drv.d, причём описание способа получения последних было в первом. Но make-то, не имея этого описания на первом проходе, полагал, что ничего нельзя сделать.
Решить проблему удалось -- вставив уродливую строчку
$(ABSTRACTDRIVERSOURCES:.c=.d): $(ABSTRACT_INTERFACES_RULES)
т.е. -- ВСЕ абстрактные драйверы зависят от определений ВСЕХ
интерфейсов.
Правильным-то было бы изготовить per-interface-зависимости типа "%_*_drv.d: i_%_rules.mk", но таких двойных паттернов make не позволяет, и реализовать правильный шаблон удалось бы лишь в генеримом же файле -- а от этого мы и хотели уйти. Но тут -- особенно учитывая, что других интерфейсов и не бывает -- можно на эту "неидеальность" забить, лишь бы работало.
Из всей этой маеты 2 вывода:
Но и автогенерация "частей makefile" -- тоже не сахар.
Первоначальная же проблема за 05-2006 решена, на отсутствующие a_ИМЯ.h оно ругается, да и от косвенной компиляции наконец-то вроде бы как ушли (аллилуя!!!), так что -- "done".
P.S. Да, и .d-зависимости оно теперь для абстрактных драйверов генерит.
05.05.2010: насчет абстрактных драйверов в v4: совсем от них избавляться не надо -- концепция-то удобная. Просто надо упростить ситуацию: в каждой директории поддерживать ТОЛЬКО ОДИН ИНТЕРФЕЙС. При этом *Rules.mk значительно упростятся, и не понадобится вообще никакое генерение i_IFACE_rules.mk -- достаточно будет иметь прямо в AbstractCamacRules.mk соответствующие строчки с $(ABSTRACT_IFACE) в именах target'ов.
cm5307_pkt_header_t.var.debugp.length
, которое не
используется и никогда не использовалось -- для строк отладочной печати
используется терминатор '\0'.
12.05.2006: удалил это поле.
а в новых -- введена функция(status & CAMAC_Q? 0 : CXRF_CAMAC_NO_Q) | (status & CAMAC_X? 0 : CXRF_CAMAC_NO_X)
status2rflags()
, делающая
ровно это же.
Может, пора ее в спецификацию стандартного абстрактного
CAMAC-интерфейса засунуть? Вместе с ее подвидом --
x2rflags()
, который обращает внимание только на X.
03.06.2006: сделано -- подселил оные в cm5307_camac.h. Так они доступны и абстрактным, и нормальным драйверам.
ABSTRACT_INIT_FUNC()
возвращает int
(сейчас
не использующийся), то почему бы не использовать то же соглашение, что
и для обычных драйверов -- что когда функция инициализации возвращает
>=0 -- это нормально, <0 -- проблема, блок надлежит пристрелить,
причем !=-1 -- значит, это -rflags_to_set.
Для сего понадобится:
cm5307d_cfig_p
также стала int
.
cm5307_fd_p()
.
DisconnectBlock()
.
03.06.2006: в ходе махинаций последних нескольких
дней, с переводом cm5307_dbody.* на
унифицированную-с-серверной-v4 архитектуру, нынешний тип
CxsdBlkInitFunc
стал int
, и пользуется как
раз соглашением "оно возвращает состояние драйвера либо
-rflags".
Теперь осталось только в протокол добавить и реально начать поддерживать.
13.02.2007: да, это поддерживается (кроме абстрактных драйверов, ибо оные до сих пор пользуются _drvletbody.[ch]) -- в январе в протокол добавлен пакет CHSTAT, а 09-02-2007 и cm5307_dbody.c на отрицательное значение от init_b() стал реагировать уходом в OFFLINE.
01.08.2011: давно пора было этот раздел "done".
04.06.2006: повставлял свежепоявившуюся
status2rflags()
в a_ind.h, a_c0642.h,
a_r0601.h, a_v06rr.h.
Трогать a_sukh_comm24.h не стал -- если понадобится, то тогда и потрогаем.
08.06.2006: позаменял старую отдачу rflags через
условия "(status & CAMAC_Q? 0 : CXRF_CAMAC_NO_Q) | ..." на
status2rflags()
в a_c0602.h, a_c0609.h,
a_c0612.h, a_g0603.h.
Покамест считаем, что "done". Если где-то вылезет, что надо было
смотреть токмо X, а не XQ -- заменим на x2rflags()
.
18.09.2006: потрогал и a_sukh_comm24.h -- перетащив его в qult/.
08.06.2006: во-первых, не хватало возможности
указывать данные в восьмеричном или шестнадцатиричном виде. Потому
заменил dw=atoi(argv[4])
на
dw=strtol(argv[4],NULL,0)
.
Во-вторых, при печати прочитанных данных имелась аналогичная потребность -- потому там сейчас выдается в формате "dr=%d/0x%x/%c%o" ("%c" выдает префикс "0", если dr!=0).
В-третьих, статус теперь печатается не числом, а буквами -- "XQ", "X-", "--" ("-Q", по идее, быть не должно :-)
16.07.2006: исправил. Ввел функцию
NAF_UNTIL_Q()
, которая исполняет указанный NAF, пока не
получит на него Q (но не более 100 раз). А WriteOne()
просто последовательно исполняет те 4 команды, проверяя статус каждой и
переходя к следующей только при наличии Q (получился этакий
"последовательный исполнитель NAF'ов"). Затем на эту же технику
перевел и ReadOne()
. "done".
Вообще, драйвер был изрядно странен. Ну ладно, понятно, что он
реально должен был бы быть НЕ-абстрактным, и потому несколько
сложноват. НО: похоже, я его два года назад делал в изрядных попыхах
-- там в HandleRW()
имелось ДВЕ почти одинаковых ветки
обработки IsW
; видимо, первоначальную (она была ниже) при
изготовлении более корректного варианта я просто забыл удалить. Сейчас
-- удалил.
BTW, итак, у нас теперь есть уже ДВЕ CAMAC-"макрофункции":
WAIT_FOR_L()
(в a_c0612.h) и
NAF_UNTIL_Q()
.
30.08.2006: "последней каплей" послужила потребность программы adc200 -- потому сделал, включая определения таймингов и диапазонов.
30.08.2006: ну что, вводим еще один параметр -- "timing"? Да, и ради совместимости со старыми программами придется тянуть такую дурацкую последовательность параметров -- тут уж ничего не попишешь...
Да, добавил -- ADC200_PARAM_TIMING
, пока
неиспользуемый.
31.08.2006: и добавил также списочек возможных
значений -- ADC200_T_nnn
.
11.09.2006: и даже реализовал в драйвере
поддержку ADC200_PARAM_TIMING
.
Теперь надо делать поддержку количества измерений (RETDATASIZE) больше 4096.
12.09.2006: драйвер a_adc200.h обращался с параметрами непристойно -- у него не было понятия "текущие значения", а при неуказании параметра юзером он просто брал некий свой default. Это полностью противоречило нынешней идеологии "клиент указывает лишь то, что меняет".
Исправил -- взяв код из a_adc333.h.
13.02.2007: кстати, драйвер-то со всеми этими нововведениями проверен еще в октябре -- тогда понадобилось перед RUPAC-2006 срочно восстановить/обновить систему фазовых измерений.
Так что -- "done".
30.08.2006: ввел проверки, по образу и подобию caniva.
07.10.2006: в самих-то программах (linvac и ringvac) каналы подсвечиваются желтым/красным при приближении к пределу. А при неуставленных пределах это совершенно бессмысленно. Так что вставил туда в колоризующую формулу проверку вида "if (limit<=0) return 0":
CMD_DUP_I(1.0), CMD_PUSH_I(0.0), \ CMD_GETP_I(C_N(line,2)), CMD_CASE, \ CMD_TEST, CMD_BREAK_I(0), \
18.01.2007: да, вставил, в:
main()
.
InitDrivelet()
-- чтоб во
все программы не пихать отдельно.
main()
-- для старых,
cm5307_drvletbody-based драйверов (так ведь их на dbody и не
перевели!!!).
Идея: а что, если при загрузке драйвера вычитывать эти параметры из регистров?
20.02.2008: конечно, ВСЕ вычитать не удастся -- например, число точек у ADC333 не узнать (ибо оно не существует в виде регистра), не говоря уж о PTSOFS. Но уж что есть -- диапазоны и времена, например.
Но: при ПЕРВОЙ-то загрузке драйвера надо бы все-таки инициализировать некоторыми разумными значениями. Вопрос: а КАК проверять "первость"? Теоретически, можно бы, например, сдвиги нулей у ADC200 проверять на 0 -- но это значение все-таки возможно.
А можно -- писать СИГНАТУРКУ в последнее слово ОЗУ устройства. Это слово практически никогда не занимается, так что -- если там будет некое хитрое число (в идеале -- НЕ могущее быть результатом измерения, для 12-битного ADC333 это делается элементарно), то драйвер пусть "верит" данным из устройства, а иначе -- пусть инициализирует "первичными" значениями.
22.07.2011: идея "писать СИГНАТУРКУ в последнее слово ОЗУ" используется в adc200me_drv.c уже хбз сколько. Только там пишется не одно слово (которое может и совпасть), а целых 4 -- 0x00FFFFFF,0x00000000,0x00123456,0x00654321 (вероятность совпадения практически нулевая). И, вместо "верить данным из устройства" -- сразу после сигнатуры записываются сами значения параметров. Работает успешно.
А вот в CAMAC'овских драйверах это еще предстоит сделать...
31.08.2011: еще с начала августа пытался это сделать в CAMAC'jвских драйверах -- пока всё печально...
Главная потребность -- у драйверов быстрых АЦП (nadc*), поскольку на их объёмах накладные расходы на syscall на каждый NAF становились уже астрономическими (??? тут статистику от Роговского надо !!!???).
18.02.2009: ввел в cm5307_camac.[ch]
функцию do_nafb()
, принимающую дополнительный параметр
count. Отличия её от обычной do_naf()
минимальны
(благодаря устройству API драйвера в ядре).
Затем на использование блочных NAF'ов был переведен
cm5307_nadc4.c. Изменения очень несложные, и идеологически это
похоже на технологию группировки точек/линий, используемую при
отрисовке в *adc*.c -- FlushXLines()
: весь блок
бъется на слайсы по N элементов (тут по COUNT_MAX=100
).
Разница лишь в том, что там -- "выталкивание" подготовленных точек, а
тут -- "вытягивание" слайса с последующим разбором.
Драйвер отдан на испытания Роговскому, если покатит -- вставим использование и в остальные драйверы быстрых АЦП.
P.S. Кстати, вообще-то первоначальные исследования на эту тему -- как устроена поддержка блочности в мамкинском CAMAC-драйвере/модуле -- были сделаны еще с год назад, где-то в феврале 2008г., но что-то тогда заленился и нифига не сделал.
19.02.2009: Роговский проверил -- работает отлично. Так что теперь можно и прочие драйверы переводить.
16.05.2009: да, перевел. С nadc200 и nadc502 никаких проблем, а вот с nadc333, как водится, были сложности из-за нетривиальной организации памяти и переменного числа каналов; пришлось даже поменять COUNT_MAX на 108 -- чтобы без остатка делился на 2, 3 и 4.
Опять же -- осталось проверить.
18.05.2009: ага, в nadc333 вылез-таки ляп: из-за
чередующегося (а не последовательного) вычитывания каналов там какая-то
диковатая арифметика -- связь numpts
и count
через множитель/делитель nchans, и стояло просто
count=numpts
(вместо count=numpts*NCHANS
).
Причем, оно не просто вычитывало бредовые данные, а еще и вылетало.
22.07.2011: уже два года как работает, так что "done".
22.07.2011: за последние несколько дней вроде сделал, но еще не проверял. Ключевое слово -- cm5307_sl_dbody.[ch].
#if CM5307_SL_DBODY #include "cm5307_sl_dbody.c" // или .h #else ...тут старое содержимое файла #endif
CM5307_SL_DBODY= 0 DEFINES+= -DCM5307_SL_DBODY=$(CM5307_SL_DBODY)
DEFINE_CXSD_DRIVER()
, со списком параметров из
4cx/src/include/cxsd_driver.h (а DEFINE_DRIVER()
просто вызывает его).
cx_module*
) и
CxsdDriverModRec
.
select()
и жаждет иметь
отсутствующую тут HandleDriveletInput()
.
Посему -- в него также добавляются #if CM5307_SL_DBODY
.
А технология работы через Tout'ы -- давно уже отлажена в cpci_fastadc_common.h, где она и делалась ровно по образу тутошнего, но под полноценный driver-API.
09.08.2011: как тогда всё казалось "просто"! Реально
оно заняло почти три недели (впрочем -- в основном НЕделания),
поскольку делать как-то очень уж не пёрло, и в конце концов даже в
cpci_fastadc_common.h были внесены изрядные изменения --
SetDeadline()
, работающая через
RegisterDevToutAt()
. Наконец-то вроде сделано, надо
проверять -- а потом аналогично перепахивать u0632 и karpov_monster.
Некоторые размышления "на будущее" о потенциальной унификации, по результатам изготовления:
Видимо, надо все "стандартные" вещи унести в общие remdrvlet.[ch], а специфические оставить в {cm5307,bivme2,moxa}_dbody.[ch].
26.07.2011: да, "мясо" вынесено в парочку drivers/remdrvlet_{h,c}.h; _sl_dbody.h теперь вообще почти девственно пуст, за _sl_dbody.c же остались открытие camac_fd и DEFINE_*_DRIVER().
Но понятно, что для MOXA, где нужны МУЛЬТИдрайверы (в стиле CANGW) конкретно это решение не прокатит -- там понадобится дальнейшая сегментация, в стиле 4cx/src/lib/rem/.
Пока что пункт "done".
10.12.2012: почему-то тогда
CHECK_SANITY_OF_MODID()
и
SELECT_EVENT_TARGET()
. Очевидно, и с API каналов будет
аналогично -- с отличием в том, что делать с полученными данными (в
сервере -- отдать в БД, в драйвлетах -- отправить "наверх").
Напрашивается -- как-то попробовать и это вынести в некий общий файл.
10.12.2012: поскольку в "унифицированном" интерфейсе v2/v4 для v2 был вариант файлового API {Rd,Wr,Ex}FD, а не просто FD (этот -- для v4), и реально оно не использовалось нигде, то оно выкинуто.
27.07.2011: расширил Tout-API: добавлена
RegisterDevToutAt()
.
RegisterDevTout()
с
тривиальной модификацией.
Вообще-то это дюже некрасиво (там ОГРОМНОЕ количество кода скопировано), но пока пойдёт и так.
27.07.2011: Роговский успел попроверять -- на "sl_dbody" всё работает, и оба варианта Tout'ов в том числе.
29.07.2011: приподупорядочен API работы с CAMAC: теперь не каждый драйвер содержит кусочек вида
а это вынесено в c5307_{,sl_}dbody.h.#include "cm5307_camac.h" #define DO_NAF do_naf #define DO_NAFB do_nafb
Также, для изоляции специфики, туда добавлена строка
и во всех исходниках direct-drivelet'ов позаменены "ref" и "camac_fd" на#define CAMAC_REF camac_fd
CAMAC_REF
. (Еще бы в API абстрактных
драйверов это поменять... Но ломает.)
02.08.2011: еще добавление в API --
DevIOAccept()
. Без него слушающие сокеты в принципе не
могли работать -- т.к. IO-API изолирует драйверы от fdiolib'овских
handle'ов (хотя у Роговского непонятным образом работало... 03.08.2011: разобрались, КАК: в старом fdiolib'е
handle'ы начинались с 0, в API -- с 1; НО: 0-й fdiolib'овский был занят
соединением с хостом -- вот и получалось, что handle'ы совпадали. С
новым fdiolib, нумерующим уже с 1, появился сдвиг на единицу.).
Сделан тупым копированием DevIOSend()
, с заменой
собственно конкретного fdiolib'овского вызова. Это, конечно, нехорошо
-- надо б ввести какую-то общую #define-()
-преамбулу.
И вот ЭТО в 4cx/.../cxsd_driver.c уже перетащено.
03.08.2011: изрядно путало, что, вследствие смешения в remdrvlet_h.h API из разных версий, там для обозначения "ссылки на privrec" в разных местах использовались разные имена:
privptr
-- это в "Driver's methods definition,
унаследованное от "продвинутого" API нынешней версии (т.е., уже не 2.0,
как в сервере, а то ли 3, то ли 2.5);
devptr
-- во всех остальных, взятых уже из 4cx/; а
privptr
там уже "для callback'ов".
Правильное решение -- везде
devptr
.
09.08.2011: имела место забавно-постыдная история:
CxsdDriverModRec
там остались и поля chan_nsegs
, chan_info
,
chan_namespace
, cmd_table
, а их типы были, за
ненадобностью, тихо с-typedef
'лены на void
.
DEFINE_CXSD_DRIVER()
значения для них в инициализаторе
остались.
Почему избыточные элементы в инициализаторе ("excess elements in initializer") вместо ошибки генерили просто warning'и -- большой вопрос, уж так GCC к этому относится.
10.08.2011: и еще дополнение -- в оба варианта
API (sl_- и не-sl) добавлены константы "уровней" логгинга --
DRIVERLOG_nnn
, изначально появившиеся в 4cx/, а
15-04-2010 введённые и в v2.
09.09.2011: обнаружился позорноватый ляп, касающийся bid/businfo, состоящий из двух аспектов:
ProcessPacket()
делается
приведение busid'а к массиву businfo -- оно бъётся на байты. А для VME
это вредно -- поскольку адрес на шине указывается 12/16-битным числом.
Можно обходиться полумерами -- типа восстановления 16-битного числа из пары 8-битных конкретно в VME-драйверах, плюс устранения проверки на [1,24] вообще для всех.
Но раз уж всё равно remdrvlet_c.h
#include
'ится в конкретные .c-файлы, то лучше
сделать РАЗНЫЕ случаи для разных видов драйверов, и переключать их
#if
'ами. Так что теперь:
REMDRVLET_C_H_BUSINFO_MODE
, могущим принимать значения
REMDRVLET_C_H_BUSINFO_CAMAC
-- раскладка по 4
байтам, с проверкой на [1,24].
REMDRVLET_C_H_BUSINFO_VME
-- байты 0,1 идут в [0],
байт 2 -- в [1], байт 3 -- в [2] (т.е., это триплет
{address,irq,vector}).
30.07.2013: всё вышеописанное не только было сделано и использовалось, но уже и де-факто устарело (заменено новым libremdrvlet'ом, сделанным на основе полученного тут опыта) и отправлено в отставку. Так что даже не "done", а сразу "obsolete".
И, кстати, многое из описанного и обсуждённого РЕАЛЬНО устарело -- и махинации с businfo, и "API дескрипторов и таймаутов" -- в связи с существенной переработкой драйверного API.
Пора переходить на ЕДИНОЕ определение -- очевидно, с префиксом remdrv/REMDRV.
(Раздельные cm5307_drv.c и cangw_drv.c, так и быть, пока оставим -- там имеются некоторые отличия в формировании имён драйверов для запуска.)
22.07.2011: да, сделан src/include/remdrv_proto.h.
После обеда: и cm5307_drv.c и cangw_drv.c перевёл на него. (Кстати, теперь, когда все протокольные названия в них уницифированы, diff'ом отлично видно, что различия заключаются в присутствии в cm5307 дополнительных концепциях а) "архитектуры", б) opts.set1/set2.)
25.07.2011: да, и все файлы со стороны контроллеров тоже перевёл, старые *_proto.h удалены; всё собирается, "done".
04.08.2011: Делаем:
Возможные архитектуры -- ppc, uclinux, cangw, bivme2, moxa.
arch_info_t
, собранных в [].name==NULL-terminated массив
architectures
. При парсинге в privrec складывается
указатель на описатель данной архитектуры.
Изготовлен, надо проверять.
09.09.2011: да, проверил на cangw и ppc -- работает.
Так что теперь уже удаляем cm5307_drv.c и cangw_drv.c, и -- "done".
08.12.2011: в связи с началом поддержки MOXA, которая на ARM (armv4tl), и little-endian -- надо поддерживать и little-endian (вся предыдущая шобла -- cm5307/mcf5200, cm5307-ppc, cangw, bivme2 -- была BIG-endian, в связи с чем в серверном драйвере явно стояло hton*()/ntoh*()).
is_be
-- сразу после имени.
В будущем, конечно, надо будет понаделать функций конверсирования -- для int16 и int32, плюс для массивов int16 и int32 -- и прописывать указатели на нужные функции. Тогда код станет выглядеть вменяемее, плюс сможет работать и на не-le-процессорах.
17.02.2012: пауза до реконнекта -- 10*1000*1000
-- перетащена в константу DEFAULT_RECONNECT_TIME_USECS
.
11.09.2012: давно напрашивалось, что надо бы в
_fd_p()
читать не по одному пакету, а стараться вычитать
весь входной буфер -- как сделано в {tsycam,ottcam}{,v}.
Конкретно сейчас проблема проявилась на linac1:31 (linmagx), где сервер обслуживает как удалённые устройства, так и напрямую воткнутую CAN-линию через марафонскую карточку.
При отсутствии устройств на локальной линии в ней стоит "звон" (около 1000 IRQ/s), от которых недеогловый драйвер дуреет и выходит, что сервер занимает 99.9% CPU -- хотя реально он всё время проводит в ядрёном драйвере.
И в такой ситуации он очень редко обращает внимание на дескриптор от CANGW, а обращая -- вычитывает всего по одному пакетику, оставляя там еще десятки.
Реализация множественного вычитывания сделана слегка халтурно -- вместо цикла
for (repcount=NNN; repcount>0; repcount--)
в начале стоит repcount=NNN (сейчас NNN=30), перед обработкой --
repcount--, а в конце (перед return
) -- проверка, что если
repcount>0 и дескриптор готов на чтение -- то goto
наверх на обработку.
Это помогло -- даже в той больной ситуации с linac1:31 драйвер ist_xcdac20 (он локальный, но завязан на cangw-istr13:xcdac20) стал почти полностью функционален (при NNN=10 было похуже).
В принципе, можно еще и NNN сделать конфигурябельным (через remdrvopts), но глубокого смысла не видно. А главное --
В будущем надо бы эту схему распространить и на
13.09.2012: в результате того усовершенствования
появился феерический глюк: remdrv стал регистрировать таймаут уже ПОСЛЕ
ухода в OFFLINE. В результате сервер вызывал
TryToReconnect_p()
с devptr==NULL и всё благополучно
SIGSEGV'илось.
Технически причина проста -- по сдыханию драйвера дескриптор закрывался, дальнейшая проверка check_fd_state() давала -1/EBADF, и оно ставило RECONNECT.
Выводы:
return
-- это
в remdrv реализовано.
fdio_ntfr_t
возвращала int (==0/!=0). 16.09.2012: неа, решено делать по-другому -- с
отложенным освобождением дескрипторов.
20.09.2012: в CAN сделал (по той же схеме с
goto
) -- на CANGW толку мало. Например, в магнитной
системе (linac1:31, контроллер cangw-magsys) что без REPEAT, что с ним
canserver занимает 75-77% CPU. Вероятно, на 125кбод он успевает
обработать пакет до того, как прилетит следующий (надо проверить!).
22.09.2013: оно понятно, почему загрузка от этого не зависела -- поскольку в основном потребляют ReturnChan*().
Но на будущее, с учётом "дороговизны" syscall'ов, даже мысля возникала (несколько дней назад): раз CAN-пакеты маленькие и фиксированного объёма, то можно ль сэкономить, СРАЗУ прося вычитать пачку (30) пакетов? Проблем с этим будет 2:
Так что по возможности надо стараться всё же воздерживаться от внедрения данной "оптимизации".
13.09.2011: да, вставил
close(lsock)
.
(В принципе, можно было бы уставлять lsock'у close-on-exec:=1 --
fcntl(lsock,F_SETFD,1)
-- но close() кажется симпатичней.)
16.04.2013: концептуальности:
31.05.2013: проверено; в реализации
DoIO()
были критичные ляпы -- очень кривой парсинг.
Исправлено, NAF'ы исполняются (так что старый cm5307_alfo можно уже и
не использовать).
30.07.2013: поддержка LAM'ов тоже сделана (уже в новом месте -- v2hw/driver/camac/cm5307_ppc_drvlets/, но опишем за компанию тут). Интересности:
WATCH_FOR_LAM()
. Стыд и позор, плюс
непортабельность (оно живёт именно в cm5307_ppc_drvlets/
вместо src/).
DoWait()
взята с
uspci_test.c, в т.ч. ":[ТАЙМАУТ]" означает "ждать первого
LAM", а ".ТАЙМАУТ" -- "выждать всё время".
Для будущего vme_test это тоже может быть полезно.
В результате сделано "всё" -- можно указывать число и суффикс единиц -- us/ms/s/m/h, по умолчанию ms.
Код с одной стороны громоздковатый, а с другой -- напрашивается на внедрение и в прочие утилиты mon/_test. Из чего вытекает идея о попытке унификации оных -- но это скорее маниловщина, поскольку точно потребует перевода их всех на cxscheduler.
camac_setlam()
переставлен уже в "после синхронной реакции
на LAM" (в для-драйверной реализации тоже).
Вообще вроде бы пашет.
Основой послужило то, как реализована последняя (на данный момент :-) инфраструктура "cm5307 drivers": там просто среда исполнения, поддерживающая/реализующая API драйверов.
Ну так и для CANGW можно сделать то же самое -- изготовить среду исполнения, имитирующую для драйверов "сервер", и результат -- "как бы сервер", управляемый с хоста, запускать на "коробочке".
07.06.2006: детали:
Стоит сделать ДВА процесса: собственно драйвер, и запускальщик-сторож, имитирующий SysV-init. Т.е., если драйвер копытится -- то "init" его перепускает, но не чаще чем раз-в-сколько-то.
25.12.2006: приступаем к изготовлению поддержки CANGW по вышеприведенному проекту (понадобилось наконец-то для управления лебедиными камерами).
Делаем в директории ~/work/cangw/.
Большое и толстое замечание: ведь сам-то обмен между драйвером в сервере и исполнителем в контроллере -- ПОЛНОСТЬЮ идентичен оному у cm5307 (используем, простоты для, один-сокет-на-стройство). Так что на сейчас мы просто скопируем аф-фигительную часть cm5307_drv.c в cangw_drv.c (и аналогично с описанием протокола), а в будущем можно бы и подумать о введении "унифицированного" протокола подчиненных драйверов. 08.02.2012@Снежинск-каземат-11: сделано во второй половине 2011-го, remdrv (и протокол, и драйвер) заменил cm5307 и cangw.
(Но!: поскольку в CANGW, как и в cm5307, могут быть "левые"
драйверы для всяких RS232, то надо б думать то ли о разводке портов
rrund и canserver'а, то ли о хитром формате "имени
программы/драйвера". Например, если начинается с '/' -- то
это файл, если нет -- то имя внутреннего драйвера.)
09.02.2009: нифига -- по идее, вообще надо у
cm5307_drv/remote_drv грохать "arch" к черту, вместе с
def_drvlets_paths[]
, drvlets_path_envs[]
! А
вместо этого -- указывать директорию самому rrund, или просто перед его
запуском в mounthome.sh делать cd
в нужную
директорию. А хост пусть передаёт перед собственно именем драйвера
просто ./, так что rrund (ну или "удаленный исполнитель")
поймет, по наличию '/', что надо делать exec()
, а
не брать внутренний драйвер.
(И еще -- надо бы в подпротоколе логгинга передавать и уровень/категорию.)
10.02.2007: протокол реализации:
26.12.2006: некоторая часть сделана, а именно:
- Сделана "подсистема сборки" в виде набора Makefile'ов.
- cangw_drv.c практически изготовлен, из cm5307_drv.c, с усечением -- ибо не нужна ни поддержка разных архитектур, ни возня с передачей контроллеру полного имени того, что запускать.
- То, что пойдет в контроллер, собирается в поддиректории drivers/ppc_canserver/. Там Makefile содержит:
- и надлежащие определения для кросс-компиляции;
- туда symlink'уются для кросс-компиляции из cx/src/lib/useful/: {fdiolib,cxscheduler,timeval_utils,paramstr_parser,memcasecmp}.c;
- туда же symlink'уются _src.c-файлы из cx/src/programs/server/drivers/canbus/.
- Собственно "сервер-исполнитель" назван "canserver". К нему скопирован (с урезанием всего лишнего) cxsd_driver.h, а также marcankoz_pre_lyr.h (по большому счету отличается от оригинала лишь alias'ингом функции получения интерфейса).
16.01.2007: пытаемся запускать. Результаты:
- В
Daemonize()
имелось унаследованное от rrund vfork(), которое, естественно, нифига не работало (поскольку никакой exec() далее не делается). Заменено на fork(). (Впрочем, в /etc.rc.sh оно все равно запускается с ключом -d и с &.)- Чтобы у контроллера дата была не 1970 год, при загрузке делается
rdate -s $MY_HOST
, а на ring1 и linac1 включен сервисtime
в xinetd.После этого стало осмысленно в отладочной печати, выполняемой функцией
DebugPrint()
на stderr, сообщения префиксировать значениемstrcurtime()
. При этом можно сопоставлять почти до миллисекунд времена на стороне контроллера, и на стороне сервера в log-файлах -- поскольку времена синхронизованы.17.01.2007: напрашивается мысль:
- а не унифицировать ли {mar,c4l}cankoz -- сделав "adaptor-access-layer"? А то уж больно эти файлы похожи, отличаются ТОЛЬКО функциями работы с адаптером.
18.01.2007: странно, что не пришло в голову раньше, еще на марафонской карточке:
- вставил проверку на занятость затребываемого line:kid. Если занято -- то _add() возвращает -CXRF_CFG_PROBL, что приводит к уходу в DRVS_OFFLINE. Правда, почему-то вылезла проблема -- первый пакет ("device (line,kid) is already in use") до хоста доходит, а вот второй -- CANGWSRVP_CHSTAT(DRVS_OFFLINE) -- нет.
Стал разбираться -- оказывается, это устройство TCP такое, что
close()
сокета, в котором имеются непрочитанные данные, приводит к высылке RST, еще ДО отправки только-что-write()'нных данных. Это даже некоторым образом требуется в RFC2525.Другой вопрос -- а что мешает ядру просто выбрасывать пришедшие и приходящие дальше данные, продолжая ОТПРАВЛЯТЬ запрошенное, как обычно? Явная дурость и попросту misdesign в TCP...
19.01.2007: давно вылез вопрос --
- а как делать логгинг с MAGIC_NOT_IN_DRIVER? Т.е., к примеру, дампы пакетов ДО даскодировки и маппирования на magicid?
Использовано паллиативное решение: в
vDoDriverLog()
есть статическая переменнаяlast_good_magicid
(=-1), в которую складывается последний из известных "живых" magicid'ов. И при вызове с magicid==MAGIC_NOT_IN_DRIVER оно отправляет сообщение именно тому, "последнему из известно-настоящих" magicid'ов.Единственная проблема в большинстве случаев -- что в лог сервера на хосте такие сообщения будут попадать под разными magicid'ами.
В случае же "совместного" использования одного CANGW несколькими серверами эти сообщения будут довольно случайно распределяться между серверами.
24.01.2007: шаманские заклинания с соединениями:
- Было забыто уставить SO_KEEPALIVE на стороне canserver'а. Добавлено.
- Пытаемся решить проблему отправки-RST-до-отсылки-всех-пакетов.
Народ в сети почему-то советует делать shutdown(,SHUT_WR) и вычитывать все пришедшее до опупения, и только потом делать close(). Возможно, прокатит, хотя и без гарантий (есть race condition). И зачем делать shutdown() -- тоже неясно.
Казалось бы разумная идея сделать shutdown(,SHUT_RD) не прокатывает -- ядро на это просто не обращает внимания.
Работает же отключение алгоритма Нагла (TCP_NODELAY:=1) непосредственно перед отправкой DRVS_OFFLINE.
29.01.2007: наблюдены какие-то странности:
- Во-первых, иногда драйвер сразу при запуске валится с диагностикой
-- странно, поскольку везде auxinfo сохраняется и передается корректно, не забывая о +1 на trailing NUL.DEBUGP: psp_parse(auxinfo): The '0ЪЪЪЪ' is an invalid value for 'magn=' (integer expected)- Во-вторых, после добавления забивки всего пакета FF'ами перед его заполнением -- пару раз с-segfault'ится сам сервер. Но -- тоже невоспроизводимо, так что и gdb фиг натравишь...
Концов так и не нашел, но на всякий случай (если это НЕ битая память у ring1) вставил перед инициализацией драйвера побайтовый дамп пакета на stderr.
01.02.2007: с первой странностью (0ЪЪЪЪ) разобрался -- это был ляп в
InitializeDrivelet()
, пришедший еще из cm5307_drv.c (а туда оно попало из old_cm5307_drv.c, куда оно перекочевало из m5200_drv.c -- итого, ошибка больше пяти лет). Там размер пакета считался так:Либо надо было "+1+3)&~3U" -- добивка до кратного 4, либо вообще безо всякой кратности -- тут она незачем (ибо строка, в конце пакета, и размер пакета -- в байтах).sizeof(pkt.hdr) + ((strlen(rec->drvletinfo) + 1) &~ 3U)
В результате же там просто ОТБРАСЫВАЛИСЬ последние 1-3 байта, в т.ч. и trailing NUL. А уж в контроллере-то обычно на этом месте просто случайно оказывался нолик -- но не всегда.
Пофиксил, просто убрав "&~3U".
16.02.2010: замечание: написанное выше «Либо надо было "+1+3)&~3U"» -- явная чушь, добивкой "до кратного 4" является просто "+3", а не "+1+3".
В общем, уже давно работает, так что -- "done".
Самое простое -- слушать еще один порт (8081?), и с него принимать просто текстовые строки-команды, и обратно отправлять тоже текст. Так что можно будет "заходить" при помощи "telnet ppc-... 8001". Реализация -- прямо на cxscheduler'е, читать хоть по-байтно.
02.06.2009: да, сделал.
fdprintf()
"
(нестандартизованная glibc'шная dprintf()
не в счет), так
что пришлось слабать халтурненькую Limited_fdprintf()
, с
буфером на 1000 символов.
strcmp()
'ится с возможными командами в одном
if()/elseif()/else. Так что ни "?" не поддерживается, ни
параметры у команд.
16.06.2009: слегка поменял парсинг -- оно
пропускает isspace()
в начале строки, и ничего не делает с
пустыми строками и строками, начинающимися с '#' (это чтобы
можно было рисовать целые "сценарии" для скармливания консоли, с
комментариями внутри).
Команда list стала чуток умнее: она для каждого драйвера
выдает также информацию от cankoz_pre_lyr'а -- line,kid,devcode, для
чего в дополнение к CanKozSend0xFF()
создан еще один
публичный вызов CanKozGetDevInfo()
. И, кстати, она
проверяет код возврата, так что для не-CAN-драйверов печатает только
"magicid busid drvname".
Также добавлена команда date, отдающая
strcurtime()
.
16.06.2009: еще мыслишка: а не сделать ли аналогичный интерфейс в обычном rrund?
Нет, не сделать, ибо:
SIGCHLD_handler()
'е искать по ней, чтобы грохнуть
нужное...
"ps"
в консоли контроллера.
P.S. Но идея интересная, если припрет -- сделать не проблема.
18.06.2009: добавил красивостей-удобств: теперь по "who" и "list" после consid'а/magicid'а выдаётся ":IP.АДРЕС.ХОСТА.КЛИЕНТА", а в prompt'е выдаётся IP-адрес canserver'а.
Ради inet_ntoa()
пришлось добавить в
tools/cm5307-ppc/include/ файл arpa/inet.h.
Кстати, читаемость-то от этого всего не супер -- для полноты еще можно время-запуска-драйвера добавить :-), но для отладки самое то.
А былая маловнятная команда "canlist" переименована в просто "ff".
Засим можно считать как "done".
07.09.2011: а ведь совсем необязательно было
слушать еще один порт: можно считать консольным
ОБЫЧНОЕ соединение, если на него подано пустое имя драйвера -- т.е.,
просто сразу нажат Enter. (для этого пришлось бы в
canserver_drvmgr.c::ReadDriverName()
вставить в
качестве символа-терминатора и '\n' в дополнение к
'\0'.)
Идея, конечно, красивая, но использовать её скорее не стоит:
А в CXv4 -- на этот канал может навешиваться и прочая диагностика, типа сообщений о каких-то ошибках.
Реализовать это можно довольно просто: специальный "псевдодрайвер" (реально -- пустышка!), который не привязывается ни к какому адресу на CAN-линии, а просто при его наличии менеджер коробочки гонит ему все сообщения, которые драйвером на стороне сервера просто отдаются -- стандартным образом! -- системе протоколирования. И таких "протоколяторов" одновременно может быть активировано НЕСКОЛЬКО -- чтобы каждый из серверов, пользующихся услугами данного CANGW, получал бы всю инфу.
Это автоматом решит и проблему "кому отдаётся NOT_IN_DRIVER-сообщение в-сервере" -- там можно активировать этот же драйвер-пустышку, и все такие сообщения будут идти ему.
02.06.2009: замечание -- ежу понятно, что вместо
DoDriverLog()
надо будет в cankoz_pre_lyr.c
вызывать какую-нибудь свою функцию, которая при
magicid>0
вызовет обычный логгинг, а при NOT_IN_DRIVER
-- "отдаст" всем псевдодрайверам, если таковые имеются.
В canserver_drvmgr.c уже и так имеется "интеллектуальный" выбор файлового дескриптора -- вот будет нечто как бы похожее. Плюс -- при таких отдачах НЕ НАДО проверять по logmask.
ReturnBigc()
в canserver_drvmgr.c.
16.06.2009: да, сделал -- скопировав код из
cm5307_dbody.c, и адаптировав его по образу
ReturnChanGroup()
.
Заодно начинил оба файла проверками на корректность параметров:
pktsize
'ом sizeof(pkt)
.
ninfo
в диапазон
[0...100] (остальные проверки пусть уже на стороне сервера
делаются), и в cm5307_dbody.c тоже это скопировал.
Используется та же технология "CHECK_LOG_CORRECT()
", что и
в основном сервере.
ReturnChanGroup()
добавлена проверка на
count<1
.
magicid
'а в canserver'е теперь тоже
проверяется -- макросом с таким же названием
CHECK_SANITY_OF_MAGICID()
.
Дополнительный вывод из введения проверки на ninfo
:
имеющееся сейчас в
cx_proto.h определение CX_MAX_BIGC_PARAMS=100
надо выносить в общедоступный файл -- cx_types.h. А то
оно уже в куче мест дублируется. (А в
cx-server_dbase.c::GetArgsSizeUnits()
при этом --
не проверяется!
17.06.2009: да, определение
CX_MAX_BIGC_PARAMS=100
перенес. И все места, где были
либо свои определения, либо просто число 100
в этих целях,
подправил. И в
cx-server_dbase.c::SimulateDatabase()
проверку
тоже вставил.
08.09.2011: проверено еще тогда, при реализации табличной работы в xcac208, так что "done".
GetCurrentCycle()
.
Её при надобности сделать будет несложно, но слегка муторно: при
входе в ЛЮБОЙ метод драйвера надо будет запоминать его magicid в некоей
глобальной переменной active_magic
, и в функции брать
devinfo[active_magic].curcycle
-- благо, получение номеров
от сервера и сохранение их per-device уже есть.
Кроме того, поскольку драйвер cangw давно изведён в пользу remdrv, да и вообще это всё скоро перебазируется в v2hw/, то в этот раздел более ничего писаться не будет.
(Возможно, единственный пункт, который продолжит существование -- про «"обратный" канал» от 01-06-2009.)
29.04.2012: да, и вся cx/src/programs/server/drivers/canbus/gw/ также грохнута вместе с gw/.
Технологически это должно быть похоже на драйверы cm5307_ppc, и содержать оно будет следующее:
Тут мы никаких абстрактных драйверов поддерживать (пока?) не будем, так что всё сравнительно просто.
Ситуация облегчается тем, что прерывания видны по
select()
, а не сигналами.
19.12.2011@Снежинск-каземат-11: кстати, в drivers/bivme2/Makefile отсутствовала проверка на тему CPU_X86_COMPAT. Добавил.
02.09.2011:
Помимо использования тут, на него уже переведены cm5307/common/CommonRules.mk и can/gw/ppc_canserver/Makefile.
02.09.2011: замечание насчёт bivme2_io: базовая идея -- сделать портабельный API доступа к VME, который бы можно было реализовать и для других контроллеров; в идеале -- чтоб могло работать по технологии layer'ов, с несколькими контроллерами сразу. Пока же даём этому API имя/префикс bivme2_io чисто для простоты, чтобы не заморачиваться.
06.09.2011: приступаем к начинению bivme2_io.
Выбрана общая концепция -- работа аналогично pci4624, т.е.:
bivme2_io_open()
,
получая в ответ handle, который потом указывается всем операциям В/В.
Эта возможность понадобится, если придётся вместо нынешней cm5307-подобной архитектуры "каждый драйвер живёт в отдельном процессе" использовать cangw/ppc_canserver-подобную "все драйверы живут в одном процессе-сервере". А понадобится сие в случае необходимости вешать на одно IRQ несколько устройств (с разделением по векторам) -- поскольку мамкинский интерфейс /dev/vmeiN поддерживает не более одного процесса на каждую линию IRQ.
bivme2_io_close()
.
am
).
В целях упрощения обработчику передаётся ТОЛЬКО devptr, безо всякого privptr'а. Тут этого будет вполне достаточно (драйвер, работающий с несколькими IRQ -- просто зарегистрирует несколько разных обработчиков).
Отличие -- что для упрощения делается покамест БЕЗ layer-alike технологии, на прямых вызовах.
Пока что там будет очень ограниченный функционал:
bivme2_io_a16wr16()
и
bivme2_io_a16rd16()
(поскольку для козачиных ничего
другого не требуется).
13.09.2011: попробовал/проверил на vadc16: всё и хорошо, и плохо:
vme_poll()
СБРАСЫВАЕТ "признак irq"
-- .vector=-2
. Таким образом, select() для некоего
дескриптора можно вызвать лишь единожды -- второй подряд вызов ВСЕГДА
вернёт "таймаут".
P.S. Проверить удалось далеко не сразу -- блок никак не запихивался, имелся дребезг контактов, происходили чудеса.
14.09.2011: сделал ВСЕ функции В/В. Для
краткости и простоты они определяются макросом
DEFINE_IO()
.
15.09.2011: в bivme2_io_open()
добавлен параметр space_size
-- размер занимаемого
адресного пространства. Это на будущее -- когда будет layer, чтоб он
контролировал неперекрытие.
20.10.2011: после поднятой мною бучи (послал письмо с результатами Козаку и Мамкину, Козак передал Куперу, тот скомандовал Мамкину разобраться) история с IRQ счастливо разрешилась. Мамкин провёл расследование с осциллографом, и оказалось, что были 2 проблемы:
Итого:
KCMD_START_FLAG_EACH_IRQ
) всё работает как надо.
Заодно надо будет и в CAMAC-драйвер ввести поддержку poll/select.
20.10.2011: дополнительная "радость": поскольку
libvmedirect работает через mutex в ОЗУ, то при
асинхронном гроханьи драйвера (хоть по kill, хоть по Ctrl+C -- если
rrund запущен руками) mutex оказывается заблокирован
навечно, и другие программы лишь до второго пришествия вызывают
sched_yield()
.
Т.к. сделать с этим ничего нельзя -- ни atexit(), ни даже ловля SIGINT нас не спасут -- то остаётся только НЕ пользоваться kill/Ctrl+C, а грохать соединение.
07.09.2011: первая версия vadc16_src.c вроде сделана -- после нескольких дней мучительных разборок и консультаций с Козаком.
08.09.2011: аналогично и первая версия vadc20_src.c сделана. Некоторое количество кода было взято из xcdac20_src.c -- перекодировка, пределы. Реализованы также калибровка (DO_CALIBRATE) и цифровая коррекция (NUMCORR_MODE, NUMCORR_V).
Что интересно -- драйвер vadc20 получился совпадающим с xcdac20 по каналам (с учётом отсутствия регистров, таблиц и нереализованности плавного изменения). Ну оно и понятно -- ведь это фактически тот же самый девайс, только с другим интерфейсом и упрощённый.
13.09.2011: проверил в варианте БЕЗ IRQ -- vadc16_src.c работает вполне прилично.
15.09.2011: сделал еще "корректное" чтение измерений АЦП, с защитой от чтения в момент записи: двойное, и при несовпадении -- третье чтение (поскольку период измерения велик по сравнению с временем чтения, то НЕ пытаемся циклить, считая, что вероятность перепланировки на столь долгий срок с точным попаданием в ту же точку практически 0).
13.10.2011: попробовал ДРУГОЙ экземпляр vadc16 (взятый у Козака, а не тот калечный от батраковцев). Один фиг -- IRQ не работают, а после попытки уставить ЛЮБОЕ, кроме 5 -- начинает страшно тормозить.
20.10.2011: IRQ в VADC16 заработали, подробности см. выше за сегодняшнюю дату. Засим vadc16_src.c можно считать рабочим.
08.12.2011: да, ТУТ всё сделано, на Star@CentOS-5.7 оно собирается, проверено также на самой Moxa -- работает, драйвера и правильно подхватываются, и функционируют (вопрос уже в самой работе с RS485/КШД485).
Кстати -- такой же ключик понадобится и в cxsd! 15.06.2014: да, давно сделано.
24.06.2005: окончательно достало -- вводим такой ключ. Хроника:
option_donttrapsignals
, environment --
$DONTTRAP
.
ParseCommandLine()
в начало.
InterceptSignals()
-- теперь условные.
Короче -- "done".
07.07.2005: Закомментированы -- шестью слэшами.
31.01.2006: все оказалось довольно просто -- оно
делало в vloglineX()
проверку
!FD_ISSET(logfds[logfile], &used)
не глядя, что logfds[logfile]
-- вне разумного диапазона.
(А уж как в нее попадает -3
-- совсем просто: позиция в
logging_fds
выглядит как "0-", а это дает
0*10+-3, поскольку '-'-'0'==-3
; первоначальные же корни
числа -3 -- в том, что
cxd_config.cSectionLogging()
::fd_failed=-3
.)
Дурь заключалась в том, что, якобы, при ошибке открытия файла для категории "default" должна выдаваться ошибка, а не warning, но реально там проверялось не НАЛИЧИЕ этой категории в списке (а указывать можно НЕСКОЛЬКО сразу!), а то, что ПОСЛЕДНЯЯ категория была default'ом.
Ввел именно "накопительный" флаг default_mentioned
--
проблема ушла. "Done".
Так, некоторые комментарии:
O_RDWR
файл cxd_config.c менялся аж 17-09-2002 -- при создании CXv2
(нынешнего cx/), куда он переполз из curcx с минимальными
изменениями (была сделана реально работающая секция
Environment); куда, в свою очередь, попал из oldcx
(добавлением секции Drivers), где дата была 14-06-1998; куда
попал -- с косметическими/именнЫми модификациями из ucam, наираньшайшая
дата где -- 13-04-1997. Короче -- сей баг существует от самого
рождения много-много лет назад.
FD_ISSET()
сейчас есть и в
cxlogger.c::ll_vlogline()
(ключевое слово --
СЕЙЧАС, были планы от таких проверок избавиться...)...
07.02.2014: да, этой фигни там уже нету, и вообще от концепции "МАСКА" избавлено 06-11-2013.
PrepareClean()
) стоит ПОСЛЕ
StartPart()
, приводящей к падению, а вот cxd -- улетает в
бесконечный цикл, когда он, например, получает SIGINT, пытается о нем
сообщить, в результате имеет SIGSEGV, о котором также пытается
сообщить, и т.д.
Вопрос: а почему появление SIGSEGV не блокирует дальнейшие его
проявления? Что -- надо SA_ONESHOT
указывать?
25.11.2013: в связи с переходом на однопроцессность (да и вообще сильной модификацией потрохов за эти почти 8 лет) ставим "obsolete", хотя де-юре проблема так и не была исследована.
16.02.2012: если это и делать -- то только после перехода на GeneralRules.mk, CX_CACHECTL* и SLOTARRAY сотоварищи; просто чтоб копировать в новый сервер уже полностью отлаженные куски, а не дублировать сырость.
И, строго говоря -- не так это много работы, несколько дней (при отключенных телефонах :-D).
25.11.2013: ну уже полгода как на нём всё и пашет.
05.04.2004: дело в том, что там формат ответа на consolelogin-запрос -- "issue\0prompt". А когда я вставлял приколы с отдачей в issue версии, то "по привычке" оставил все добавления текста как "p+=sprintf(...)+1". Вот оно и вставляло лишние '\0'. Правильная же реализация (как я и сделал) -- "+1" только в ПОСЛЕДНЕМ sprintf'е, ПЕРЕД prompt'ом.
01.11.2004: ага, щас... /var/run/ -- root.root,rwx-rx-rx, так что -- фиг...
Ну и что же -- защищаться от logrotate'а периодическим чтением pid-файла
прямо самим демоном? В принципе, не есть проблема -- готовое имя-то лежит в
pid_file
...
Часом позже: да, так и сделал. Пришлось 0) переименовать (для
общности) pidfile_created
в pid_file_created
;
1) перенести его в cxd_data.h; 2) ввести в основной
select()
таймаут (1 час), чтобы даже при отсутствии активности
иногда дело доходило до чтения.
И еще -- надо будет такую же функциональность добавить в cxsd! 15.06.2014: да, давно сделано.
-n
-- norun -- в syslog падает строчка на тему
"cx-master: exit(0) errno=...", что, вообще-то, не к месту.
28.02.2005: делов-то было -- уставить
normal_exit=1
.
(BTW, tmpwatch, а не logrotate! :-)
25.01.2006: что ж -- пробуем ввести countermeasures.
Проекты решения:
read(,,1)
.
lseek(,0,)
+read(,,1)
.
dup*()
, и
извращаться уже над клоном.
Проводим "простейший тест":
rm -f /tmp/tmp; cat </dev/null >/tmp/tmp; stat /tmp/tmp; touch -at199007311234 /tmp/tmp; stat /tmp/tmp; cat </tmp/tmp; stat /tmp/tmp
В Linux-2.4/2.6, OSF1-4.0, IRIX-6.5 -- в них во всех просто сам факт исполнения команды чтения (или открытия файла :-?), даже при том, что читать-то и нечего, вполне достаточен для обновления access-time.
В идеале это надо делать "по таймеру, раз в n секунд", но -- пока что достаточно просто тихо-мирно поселить сие деяние рядышком с "прочитыванием" pid-файла.
Так что -- в cxdlib.[ch] добавлена функция
accesslogs()
, а в
cxdmanager.c::Run()
-- ее вызов, сразу за чтением
pid-файла.
И -- voila, работает!!! Проблема решена -- "done".
Вылезла только одна "маленькая тонкость": открывались-то
log-дескрипторы как O_WRONLY
, и попытки чтения приводили к
"загадочной" ошибке EBADF (мдя, явно не хватает в POSIX'е кода
наподобие "EINAPPROPR"). Всего-то потребовалось заменить в
cxd_config.c::LOGFILE_flags
режим доступа
на O_RDWR
. (Заодно, кстати, узнал, как и при каких
условиях работает restart syscall'ов -- ключевым словом является
ERESTARTNOHAND
, а это -- один из "внутренних" кодов ошибок
(есть и такие!), описанных в linux*/include/linux/errno.h.)
А на будущее, в CXv4:
07.11.2013: сделано, ll_access()
.
Вопрос только -- по какому сигналу это делать? Явно напрашивается
SIGHUP
: во-первых, он практически общепринят для этих
целей, а во-вторых, и kill (по умолчанию), и
shutdown/init (в качестве сигнала завершения), и
/etc/init.d/functions::killproc (по "subsys stop")
шлют SIGTERM
.
07.11.2013: сделано, ll_reopen()
.
Используется SIGHUP
.
26.01.2006: вообще-то RD у логов может иметь отрицательные последствия -- теперь нельзя ставить ни "write-only" устройства типа лент, ни char-devices (типа /dev/tty).
Ну и -- нужон либо ключ, отключающий сию функциональность, либо --
перед read()
делать что-то "обламывающееся на
не-disk-files", типа lseek()
...
О! fstat()
+S_ISREG(st_mode)
.
Часом позже: сделал. И проверил -- работает, при указании /dev/pts/NNN ничего читать не пытается.
30.08.2012@Снежинск-каземат-11: вылезла странность -- логи всё-таки стёрлись, после включения rack0, хотя .pid-файлы остались.
Расследование:
За этот час tmpwatch успел сделать свою грязную работу:
Решение простО -- чтение перетащено из конца цикла в его начало, так что теперь atime будет обновляться прямо сразу при запуске.
07.11.2013: а в v4 вместо регистрации таймаута просто
сразу вызывается PerformAccess()
(который сам себя
зарегистрирует).
08.04.2019: в продолжение вопроса: ведь сейчас в Linux повсеместно применяется опция монтирования "relatime", при которой atime обновляется лишь в случае, если предыдущее значение atime было давнЕе mtime/ctime -- это сделано в интересах почтовиков (чтобы видеть, что запись в mailbox произошла позже последнего чтения -- значит, новые письма). И как при этом работает наш механизм "периодически читать, чтобы atime обновлялось"?
Ответы нашлись в документации:
...since Linux 2.6.30, the file's last access time is always updated if it is more than 1 day old.
...Наконец, с помощью параметра загрузки relatime_interval= можно изменить время, по истечении которого система обновит данные atime файлов. Значение по умолчанию -- 86400.
Смысл ясен, и он совпадает с нашими потребностями: сделать так, чтобы tmpwatch имел смысл (при "простом" варианте relatime оно работать не будет -- после первого чтения-после-изменения atime так бы и оставалось на месте, и через 10 суток файлы бы становились "старыми" и стирались).
sigaction()
, но НИКОГДА --
signal()
.
16.12.2007: откуда это вылезло -- почему-то иногда сервер пучкового датчика скопычивался; явно это был какой-то сигнал -- в лог попадало «signal 11 ("Segmentation fault") arrived». Я стал разбираться -- ключ -D "почему-то" не помогал, никакого core-файла не образовывалось. В конце концов просто приаттачив gdb к живому процессу, и тогда увидел прилетающий SIGPIPE. Видимо, клиента грохали ("крестиком") в середине пересылки картинки с CCD-камеры, которая 660000 байт, и сервер в результате ловил SIGPIPE.
Но: почему-то в результате "signal(SIGPIPE,
SIG_IGN)
" игнорируется только ПЕРВЫЙ сигнал, а дальше все
сбрасывается в SIG_DFL (во бред-то, а...), и следующий SIGPIPE
уже просто убивает процесс. (В частности, попалась в google-groups
(ссылка)
цитата про Solaris: if you use signal(), the default signal
handler is re-enabled after you get the first signal. Thus with
Solaris 5.x if you use signal(SIGPIPE, SIG_IGN) the second time your
process gets the SIGPIPE signal it will exit.)
(А вот откуда брался SIGSEGV -- вопрос. Какое-то дальнее следствие. Как бы это выяснить-то, а?)
Стало ясно и то, почему ключ -D был слабополезен: судя по man-странице signal(7) умолчательное действие SIGPIPE -- просто завершение процесса ("A"), БЕЗ сваливания в core ("C").
Решение: сделал функцию set_signal()
:
которая теперь и вызывается вместо
static void set_signal(int signum, void *handler)
{
struct sigaction act;
bzero(&act, sizeof(act));
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(signum, &act, NULL);
}
signal()
(результат
выполнения которого все равно никогда не проверялся).
Сервер запущен, опять к нему прикреплен gdb -- посмотрим.
И в 4cx/src/programs/server/cxsd.c это решение так же
перетащил (прошлая версия cxsd.c была за 27-11-2005...). А
более нигде signal()
не использовался -- везде было
sigaction()
(какой я умный! :-).
BTW: вообще-то если вместо
write()
/uintr_write()
/n_write()
использовать send()
(uintr_send()
/n_send()
?), то можно указывать
ему флажок MSG_NOSIGNAL
. Надо подумать над этой
возможностью применительно к fdiolib'у и cxlib'у, плюс узнать,
насколько это портабельно. Было бы огромным плюсом -- дабы не
напрягать программу-юзера этих библиотек перехватыванием SIGPIPE'а.
24.01.2013: тут есть крупная проблема-неясность: а
как отреагируют на send()/recv() НЕ-сокеты, типа pipe()? Похоже,
плохо:
"Potentially
useful Linux kernel changes" ("Allow send() on pipes"),
"O_NONBLOCK is
broken"
(там инфа за 2007 год, но не видно, чтоб что-то изменилось; плюс, то
ведь всё диктуется POSIX'ом...).
18.01.2008: вообще-то с этим SIGPIPE дело темное:
сейчас сготовил тестовую программу tests/sigpipe.c, и
оказалось, что по получению сигнала мало того, что результат
signal(SIGPIPE,SIG_IGN)
остается в силе, но даже и при
sigaction()
с явным указанием SA_RESETHAND
.
22.02.2009: вообще-то оно уже больше года
проработало в варианте с set_signal()
, а сегодня так и
вовсе эта фцнкция переехала в misclib; так что -- "done".
Пожаловался на это Роговский -- у них почему-то из-за отключения электричества что-то глюкануло.
Понятно, что с однопроцессным сервером такого не произойдёт, а пока
можно в cxd ловить SIGCHLD'ы, и при издыхании кого-либо из
children[]
делать DoClean(); exit(SIGCHLD);
.
08.06.2010: поскольку вся инфраструктура --
InterceptSignals()
-- уже была, то сделал быстро.
В обработчике SIGCHLD onchild()
проверяется --
стандартным циклом по waitpid(-1,,WNOHANG)
-- не сдох ли
кто из потомков, и если "да", то всё подчищается и завершаемся.
Только тонкость -- т.к. SIGCHLD может придти и во время обычного
процесса старта (а там своя, синхронная обработка), и даже во время
завершения, то введен статический флаг use_sigchld
,
который вначале 0, взводится в 1 прямо перед Run()
'ом, и
сбрасывается в начале DoClean()
'а.
Простые тесты показали, что вроде работает, так что "done".
10.09.2012: наблюдены какие-то странности: когда после натравливания strace/ltrace на cx-server ему было сделано kill -CONT, то cxd вякнул "SIGCHLD: cx-server died, terminating".
То ли проблема в onchild()
(хбз, как по
status
понять, сдох процесс или просто сменил состояние),
то ли SIGCONT в cxdlib'е как-то некорректно ловится...
11.09.2012: воспроизвести проблему не удалось -- хотя гонял в разных последовательностях strace+ltrace, на kill -CONT ничего не происходит.
Кстати, в состоянии T (stopped) остаётся ТОЛЬКО после
strace
, ltrace
же оставляет в R. Это всё под
RedHat-7.3.
#include "timeval_utils.h"
.
Через один BACKUP/STABLE надо будет usefullib.[ch] вообще удалить.
23.08.2004: удалил, ежели что -- STABLE/cx.20040802.
c_wr_req[chan]!=0
, дальнейшие запросы просто игнорируются
(cx-server_channels.c,
MarkRangeForIO()
+ShouldProcessChannel()
).
Это НЕПРАВИЛЬНО -- при последующих запросах оно должно складировать
приходящие значения в специальные "свойства" (типа
"c_next_wr_val[]
") и взводить при этом флажок (типа
c_next_wr_val_pending[]
). А по приходу ответа от драйвера
(т.е., по сбросу c_wr_req[chan]
) надо эти "следующие"
значения отправлять драйверу.
Текущая же реализация принципиально некорректна.
29.06.2005: проект реализован вместе с проектом "c_wr_time". Детали/протокол реализации см. там же.
Помечаем как "done".
CheckFork()
была халтурная
проверка на номера каналов в CXC_{GET,SET}VSET
-- реально
проверялся только первый канал в наборе, поскольку отстутвовало
"p++
".
09.07.2004: пофиксено -- засунул и p
тоже внутрь цикла.
03.08.2004: для начала сбросил умолчательное значение verbosity с 9 до 5. Затем добавил в cx-server_channels.c вызовы logline() с уровнем 9.
Вообще-то это все -- лишь часть общей картины. Надо будет уметь селективно включать логгинг разных действий (аналогично тому, как это уже сделано для драйверов).
Видимо, самое для начала простое -- это ввести ключик типа
"-Vcategory=N", где "category" -- это некое название
предопределенной категории (аналогично драйверным), а N -- значение для
нее. И соответствующие вызовы logline*()
будут указывать
что-то типа "loglevel_category" вместо предопределенного числа.
NumData=0
.
30.10.2004: как выяснилось, в
cx-server_bigc.c::FillBigcReplyPacket()
после
инициализации ответного пакета -- InitReplyPacket()
-- НЕ
уставлялось в нем поле NumData
, как это делается для обычных
каналов.
Сделал -- теперь оно уставляется в то, что лежит в
cp->lastreqbuf->NumData
.
Вообще-то достало -- надо переходить на унифицированную, СОВМЕСТНУЮ обработку нормальных и bigc-fork'ов. Вот только -- хлопотно это в современной архитектуре, нужна fdiolib, следовательно -- cxsd...
И еще, возникли некоторые вопросы по ходу разбирательства:
Zdata
"? Видимо, когда-то оно было
предназначено под снос, но в bigfile*.html никаких упоминаний о том нет.
CxHeader.Status
-- оно где-нибудь для чего-нибудь
применяется? Вроде в bigfile.html упоминается про некое
CXS_ENDIANID
, но -- корректно ли делать ТАК? Или лучше
использовать Res1/Res2?
25.05.2005: комментарии насчет вышеупомянутых вопросов, по тем же пунктам:
Zdata
" -- оно
явно было предназначено для того, чтобы легко доступаться к точке, с
которой начинаются данные. Что, собственно, и делается в
cxlib.c и cx-server_bigc.c.
Похоже, это было зачем-то сделано при унификации DataFork и BigcFork -- видимо, чтобы были отличающиеся имена. Ну и фиг с ним :-).
"lastreqbuf" используется cx-server_bigc.c для хранения последнего полученного запроса. Чтобы
А "copy_src" -- всего лишь используется для более удобной адресации внутрь этого сохраненного пакета.
CxHeader.Status
-- все очевидно, кроме
вышеупомянутого CXS_ENDIANID
оно используется еще и в
console-протоколе, флажки CXS_PRINT
и
CXS_PROMPT
. Всего-то надо было заглянуть в
cx_proto.h :-) И -- ответ "да", это поле применяется, и оно
применяется абсолютно правильно, не надо ничего никуда менять. (Ну
-- согласен, что для endian-id несколько кривовато, но зато
просто.)
Короче -- поскольку и заявленная в заголовке проблема разрешена, и на вопросы отвечено, то -- "done".
FreshFlag()
). Но если в
канал была произведена запись, а значение еще не вернулось -- то надо
отдавать Tcurrent-Twrreq.
В противном случае у cda слегка поедет крыша -- оно и подоранжевывлять значение будет, да и просто поведение будет некорректным.
07.11.2004: вообще-то не вполне понятно, как именно надо делать корректнее. Некоторые соображения:
c_time[]
.
Замечание 1: всю эту алхимию надо будет решать одновременно с проблемой, обозначенной 13.06.2004 -- "дальнейшие запросы на запись игнорируются".
Замечание 2: и, соответственно, в будущие cxsd_fe_cx.c и cxsd_channels.c решение должно будет также перейти "по наследству".
08.11.2004: да, самым осмысленным выглядит вариант "отдавать возраст с момента команды-записи минус 1". Таким образом, значение словно замрет на "предыдущем" моменте, который всеми заинтересованными клиентами уже обработан. И притом 1 -- это совсем маленькое расстояние, так что не будет резкого "устаревания".
17.04.2005: ох-ой-ей... А как этот алгоритм с "минус 1" будет работать в паре с аккумуляцией последующих значений на запись и немедленной отправкой их после получения результатов предыдущих запросов?
20.05.2005: еще: посинение каналов-записи мертвых блоков.
Вчера на C13 обнаружилось -- CANDAC16 сдурел и связь с ним пропала, а управляющие поля НЕ посинели -- потому, что в них отдавалось age=0. Это плохо!
Есть две альтернативы:
(Чем-то мне это напоминает игрища с SO_KEEPALIVE
.
Но способ "периодического опроса" не годится -- будут сложности с
нечитабельными каналами, типа PKS8.)
25.05.2005: да, и ладно еще временно
отключенных (NOTREADY) блоков, а СОВСЕМ МЕРТВЫХ (OFFLINE)? Там-то уж
стопроцентно ту-ту...
А! Понял! Во -- ведь теперь такие хоть "временно", хоть "навсегда"
неработающие блоки будут ОБЯЗАТЕЛЬНО иметь пометку
CXRF_OFFLINE
, что приведет к их побордовению.
Так что -- проблема "хоть какой-то сигнализации" для не-OPERATING-блоков решена.
А вот проблема не-посинения УПРАВЛЯЮЩИХ каналов -- есть ли она?
Нет, НЕТУ!!! Они посинеют, если в них будет попробовано что-то писать -- тогда возраст станет отдаваться с "запись-1", и будет постепенно расти.
29.06.2005: протокол реализации этой фичи (проводившейся в группе "SetBlockStatus"):
24.05.2005: Начинаем реализовывать проекты "c_next_wr_val[]" и "c_wr_time[]". Они тесно связаны с
SetBlockStatus()
иReRequestChans()
.
- Созданы массивы
c_next_wr_val[]
,c_next_wr_val_p[]
иc_wr_time[]
. Они нужны для корректной работыReRequest{Chans,Bigcs}()
25.05.2005: Продолжаем. Теперь большинство махинаций уже с cxsd_channels.c.
- Вставлен сброс флажков
c_next_wr_val_p[]
вResetBlock()
иFillChanProps()
.- Изготовлен новый вариант --
FreshFlag_n()
, отдающий возраст "интеллектуально", с использованием при надобностиc_wr_time[]
, как и записано в проекте.А вот теперь надо переделывать
MarkRangeForIO()
, причем капитально. Практически -- переписывать с нуля. А ведь это и есть самое-самое ядро CX, практически -- его сердце+мозг. Ой, болезненно и кроваво это будет!26.05.2005: Дальше...
- Создана будущая функция -- сейчас
NewMarkRangeForIO()
.- А анализируя текст
ShouldProcessChannel()
обнаружил, что при попытке записи в канал чтения НЕ ПОСЫЛАЕТСЯ даже запрос на чтение. Спорно, конечно, но -- корректнее бы посылать.- Вечером: О! Yes! Доперло! Я теперь знаю, как элегантно реализовать
NewMarkRangeForIO()
! Во-первых, делаем helper-функцию("рассмотреть запрос") -- аналог былойint ConsiderRequest(int chan, int32 val, int action)
ShouldProcessChannel()
, но "поумнее":
- Она будет возвращать код -- ЧТО делать (ничего/read/write); тем самым мы решим проблему "что делать при попытке писать в канал чтения".
- И именно ОНА будет прописывать
c_next_wr_val{,_p}
.А сама
NewMarkRangeForIO()
будет иметь примерно такой вид:void NewMarkRangeForIO(chanaddr_t start, int count, int action, int32 *values) { int barrier = start + count; for (g = start; g < barrier; /*NO-OP*/) { /* Get "model" parameters */ first = g; magic = c_magic[first]; f_act = ConsiderRequest(first, values[first - start], action); g++; /* Find out how many channels can be packed */ while (g < barrier && c_magic[g] == magic && ConsiderRequest(g, values[g - start], action) == f_act) /*DO-NOTHING*/;21.06.2005 -- "g++", а не "do-nothing"!!! length = g - first; switch (f_act) { case DRVA_IGNORE: break; case DRVA_READ: .../*c_rd_req[]; Send();*/; break; case DRVA_WRITE: .../*c_wr_req[];c_wr_time[];Send();*/; break; } } }
Это даже красивей и элегантней, чем прежний вариант!
Единственное сомнение -- а корректно ли лезть в
values
при DRVA_READ?27.05.2005: Ти-ра-ра-р-и-и-и-и! (тирольский вопль)
- "Философские мысли": насчет "сердце+мозг" -- скорее спинной мозг, отвечающий за рефлексы. И -- болезненность и кровавость, похоже, отменяются :-).
- Насчет "корректно ли лезть в
values
" -- некорректно, однозначно. Да и незачем -- надо просто передавать указатель, а уж разыменовываться он будет только при реальной надобности.- Наполняем
NewMarkRangeForIO()
содержимым, по вышеозначенному проекту.- Ясно, что надлежит иметь функцию "SendChanRequestToDriver()" -- во-первых, из-за того, что теперь посылка в двух местах, а во-вторых, ради
ReRequestChans()
. Сделана.30.05.2005: Возвращаемся к делам.
- Собственно, нафиг всякие "_n" и "New"? Препроцессор рулит! Перетащил оное в
#if
, теперь уже под стандартными именами.- Начинил содержимым
ConsiderRequest()
. Ну и монстяра получилась!Теперь надо лезть в
ReturnChan{Group,Set}()
, дабы они знали оc_next_wr_*[]
. Как это делать? А очень просто! Изготовить в cxsd_channels.c еще один вызов, которому уведомляется, что, только что было вернуто некоторое количество результатов и можно попробовать послать следующие запросы.Прототип вызова изготовил --
TryWrNext()
, и начинил имReturnChan{Group,Set}()
. Хм...Кстати, очевиднейший вывод -- этим
ReturnChan{Group,Set}()
место именно в cxsd_channels.c. С большими каналами такое ужо сделано, с рождения.31.05.2005: М-м-м...
- Да, надо, надо переносить
ReturnChan{Group,Set}()
в cxsd_channels.c. Так что -- перенес. За компанию скопировал и макросCHECK_SANITY_OF_MAGICID()
.- После чего
TryWrNext()
перешла в локальный статус, что позволило НЕ делать в ней хитрые проверки наmagicid
-- ВСЕ делается в рамках одного блока.А на совсем будущее -- CXv4 -- кстати, вывод: то, что сейчас в cx-server_{channels,bigc}.c обязательно переползет в cxsd_fe_cx.c, правильно? А то, что находится в cxsd_{channels,bigc}.c -- надлежит также свести в ОДИН файл, вопрос только, как оный назвать -- напрашивающееся "cxsd_data" уже занято.
03.06.2005: Завершающие действия. (Хотя так хотелось написать -- "немного почесался"...)
- Проблема с именованием решается крайне просто -- то, что сейчас имеет суффикс "_data", переименовываем в "cxsd_vars.[ch]".
- Кстати, на будущее: явно стоит свести нынешние поля
c_simnew[]
иc_next_wr_val[]
в одно -- они по сути являют собой одно и то же, и никак не интерферируют.- Собственно -- вроде бы окончательно все доделал, теперь даже и реально содержимое
TryWrNext()
изготовил. Далее -- надо проверять. 10.02.2012@Снежинск-каземат-11: да давно уж проверено и работает :-).- Насчет "как алгоритм «минус 1» будет работать вместе с c_next_wr_val*[]" -- да, на первый взгляд, не очень хорошо будет работать: не начнется ли подоранжевление?
Хотя -- в cda есть некая логика (ключевое слово --
mdctr
), предохраняющая от подобных фокусов. Достаточно ввести еще одно поле --c_next_wr_time[]
, куда складировать цикл, в котором пришла команда записи (в смысле -- одновременно с уставлениемc_next_wr_val_p[]
), и в момент реальной отсылки запроса (вTryWrNext()
) копировать вc_wr_time[]
именно его, а неcurrent_cycle
.04.06.2005: Продолжаем завершать :-)
- Во-первых, изготовлено поле
c_next_wr_time[]
и введено его использование по вчерашнему проекту -- от него ничего не убудет, только, мож, польза какая получится.- Во-вторых, насчет "подоранжевления": так оно же и ДОЛЖНО происходить!!! Почти всегда, при "почти-одновременной" записи. Надо только потоньше разобраться -- когда должно, а когда нет. Но с нынешней схемой -- однозначно будет в случае ТРЕХ команд подряд, будет подоранжевляться по крайней мере у части участников.
06.06.2005: Э...
- Кстати, есть такой полуфилософский вопрос -- когда и во что уставлять
c_next_wr_time[]
. Имеется некое "дерево вариантов":
21.06.2005: начинаем тестировать.
Перво-наперво вылезло, что в
MarkRangeForIO()
цикле упаковки было забыто "g++
".22.06.2005: а при повторном запросе (пока не успел придти ответ на первый) почему-то не сбрасывался
c_wr_req[]
...24.06.2005: угу, а он вообще НИГДЕ не сбрасывался. Хотя должен был бы -- при отсылке. Это пофиксил.
Похоже, все работает. Так что -- считаем за "done".
16.01.2006: имелся ляп -- в
TryWrNext()
стояло
memcpy(c_wr_time + first, c_next_wr_time,...
вместо
memcpy(c_wr_time + first, c_next_wr_time + first,...
Исправлено.
16.01.2006: А вообще-то, надо бы корректности для переименовать
c_next_wr_time[]
, например, в
c_next_wr_val_time[]
.
10.02.2009: да, переименовал.
10.02.2006: еще неприятность: каналы записи блоков, с которыми связь пока так и не была установлена, светятся обычным белым -- хотя данные из них так пока и не были прочитаны.
Естественно -- по-хорошему, такие каналы должны подсвечиваться гусиным. Для этого надо:
c_wr_req[]
, но и если c_rd_req[]
.
10.02.2009: да, сделано -- см. замечание за
сегодня.
c_wr_time[]
(или вообще занулять его?).
c_wr_time[]
в
какое-нибудь "c_io_time[]"?
24.05.2006: удалил из cxsd_channels.c
старые, за-#if !NEW_MARK
'нутые варианты
FreshFlag()
, MarkRangeForIO()
и
RequestReadOfWriteChannels()
-- просто надоело, что
мешаются перед глазами и забардачивают результаты grep'а. Если что --
предыдущий, нечищенный вариант есть в
BACKUP/cx_src.20060310_regular/.
13.05.2014: встретилась "забавная" особенность
при использовании SetDevState()
->DEVSTATE_OFFLINE->DEVSTATE_OPERATING:
SetDevState()
, ReviveDev()
,
ReRequestDevChans()
, SendChanRequest()
,
nkshd485_rw_p()
-- и далее по кольцу.
Где бы и как такие заскоки разводить?
#if 0
'енные куски кода:
Run()
-- древний вариант, не
поддерживавший драфверных таймаутов.
SimulateDatabase()
--
первый, не-psp'шный вариант парсинга ";log=", плюс старую
локальную переменную cur_magic
(ныне это имя напрямую
смотрит на numblocks
).
TimedConnect()
-- оно
никогда не использовалось, и полагалось на ныне удаленную функцию
timed_connect()
. (В будущем-то эта работа вообще
будет выполняться при помощи cxscheduler'а.)
RequestReadOfWriteChannels()
, выполняющая предвыборку, но
при этом начинают сходить с ума глючные отокаревские драйвера (детали
см. в bigfile.html за {13,14,16}.06.2003). Так что ее пришлось
отключить.
А что, если ввести еще один ключик, указывающий наличие (по умолчанию -- отсутствие) олеговых багливых драйверов? Например, -T{y|n}?
06.02.2005: да, ввел парсинг ключика "-T"
(скопировав из парсинга "-w") в option_otokarevs
, с
последующей передачей ее через $OTOKAREVS
в cx-server'ову
OtokarevsBuggyDrivers
.
И теперь "return" в начале функции предвыборки выполняется не безусловно, а только если указан флаг "-T1".
Что теперь надлежит сделать:
RequestReadOfWriteChannels()
-- там какие-то хитромудрые "&&0
" имеются.
21.02.2005: угу, начал проверять. Итак: конфиги прошерстил, где надо "-Ty" поставил (таковых мест оказалось 5 штук), и "-wN" поубирал (а таковых -- 4 штуки).
А дальше стал смотреть содержимое
RequestReadOfWriteChannels()
. Ох и жуть же там!
&&0
" нафиг
все отключал. Я его заменил на "1".
if(c_type[x++]!=0)
-- из-за "++" первый канал в каждой
группе (например, 0-й) всегда упускался.
Поскольку там дальше идет цикл, продвигающий x
по
каналам, то он и так сдвинется на сколько нужно, и я убрал "++" из if'а
и поставил его в "else" -- т.е., если канал -- не записи, то
x
сдвигается на 1. Вроде теперь все заработало.
Итого -- стандартное замечание: надо протестировать.
28.06.2005: собственно -- давно все
протестировано, флаг "-T" работает,
RequestReadOfWriteChannels()
тоже работало, а теперь оно и
вовсе переведено на другую архитектуру.
Так что -- давно "done".
CheckBlockInfo()
:
*_nsegs!=-1
на
*_nsegs>=0
.
memcmp()
"ограду"
*_nsegs!=0
-- а то раньше было опасно в драйвере указывать
"нет таких каналов" как просто *_nsegs=0.
16.06.2005: это где это там была ограда?! А вот и фиг -- забыл я тогда ее там изготовить!
Вставил.
AddBlock()
'ов
логгинг "добавляю то-то с такими-то параметрами" еще и
blk_name
.
-1
, и фиг что поймешь -- каналы болотные, и все.
Надо иметь хоть какой-то логгинг этого события.
22.08.2005: сделал. Валит короткое сообщение.
А вообще -- в будущем, когда драйверы будут возвращать состояние
(offline/notready/operating), такое сообщение надо будет валить ВСЕГДА.
Причем, будущий-в-CXv4 аналог
InitBlock()
'а должен будет этот код возвращать
"вверх".
22.08.2005: да -- теперь TermBlock()
печатает сообщение о себе. (Хотя у can-драйверов subharmonic'а
проблема была в другом. :-)
22.08.2005: вставил -- теперь, если
"scan" вызывается с параметрами (без разницы -- просто
nargs>1
), то оно сразу за символом статуса пишет еще и
время в формате "@DD.MM.YYYY,HH:MM:SS".
30.01.2006: собственно -- теперь, когда есть
strcurtime()
, сам Б-г велел перевести и "scan" на
нее.
Э-э-э... Только не strcurtime()
-- та-то отдает ТЕКУЩЕЕ
время... Так что -- в первую очередь в этих целях ввел
stroftime()
. Вот на нее и перевел -- с разделителем
"_".
31.03.2006: проблем-то особых нет -- ну увеличил
до MAX_MAIN_SEGS=20
. Но на будущее надо б это запомнить
(а еще когда обычные и большие каналы будут перемешаны...).
d_simulate[]
, работающий
через "||
" с MustSimulateHardware
.
d_simulate[]
в
SendChanRequest()
и SendBigcForExecution()
.
d_simulate[]
не нужен).
Хранить, видимо, в privrec'е, в котором первые bigc_count int'ов будут содержать смещения, а далее -- просто последовательно массивы параметров для каждого bigc.
Конкретно -- чтобы осциллограммы не присылались чаще 5 раз в секунду. Реже, если частота запусков меньше -- пожалуйста, а чаще -- не надо: человеку и 5Гц хватит, а вот лишнее мерцание и напряг процессора -- ни к чему.
И делать это ограничение надо именно в сервере, поскольку клиенту такие времена считать -- очень не с руки.
07.05.2007: сходу приходит в голову одна идея:
переделать флаг immediate
в
параметр, и в нем передавать желаемые герцы. Возникающая
проблема -- что 1 должно означать, как и сейчас, "столь быстро, сколь
возможно", а не 1Гц.
И еще вопрос, похитрее -- а КАК именно серверу делать этот лимит? Ведь клиенты-то не подписываются, а именно запросы присылают. И если ответ некоторое время не отправляется, то и очередной запрос не придет, и измерение проделано не будет. Так что -- при freq!=fastest просто ИМИТИРОВАТЬ холостой запрос от клиента?
10.05.2007: подумал-подумал -- неа, как-то хреновато вышла бы такая реализация, поскольку серверу не очень-то понятно, пропустивши отсылку результата клиенту -- КОГДА все-таки данные отправить? А что, если один раз пропустили -- а потом-то откуда возьмется "импульс", который бы вызвал проверку слать/не слать?
И опять же -- КАКИЕ данные надо отправлять? И с КАКИМИ параметрами померянные?
Неа, ну его нафиг -- в следующей версии протокола сделаем возможность нормального указания частоты, а следующая версия сервера будет нормально обрабатывать запросы с указанной частотой.
Пока же -- пусть этими махинациями с ограничением частоты занимается клиент, а эту секцию -- делаем "withdrawn".
FillChanProps()
, но там неудобно.
15.10.2008: фиксим проблемы...
FillChanProps()
и
FillBigcProps()
убрано.
И, кстати, строчка logline()
в AddBlock()
была с половинным отступом -- типа временный логгинг. Теперь она
переставлена в нормальное положение.
CXT_EINVCHAN
.
19.08.2009: исходная проблема -- при CXT_EINVCHAN
обычно просто выдаются ругательства "reply is 1006..." (cxlib) и
"Invalid channel number" (cda). Но вот как понять, КАКОЙ канал
её смутил -- хбз (и в данном конкретном случае эту -1
долго бы искал).
Поэтому введено усовершенствование: сервер при ошибке также передаёт и НОМЕР плохого канала.
CxHeader.Res2
TellClientWParam()
("WParam" -- "with
parameter").
С этой инфраструктурой ошибка была найдена очень быстро.
А на будущее -- надо предусматривать (и в протоколе, и в библиотеках), чтобы кроме кода ошибки могло также передаваться и некое "описание" -- либо текстовое (как сделано в psp-плагинах), либо числовое (как тут). И это должно быть НА ВСЕХ УРОВНЯХ -- дабы именно сама программа могла получать эту инфу (а не как сейчас, когда cxlib печатает ругань).
02.09.2009: причина была тривиальной: в
cx-server_dbase.c::AddBlock()
в цикле
for()
, вызывающем FillBigcProps()
, был криво
организован "инкремент" -- стояло
x++, cur_b += bigc_info[x].count
-- естественно, оно увеличивало cur_b
на количество
каналов СЛЕДУЮЩЕЙ группы. Поправил на
cur_b += bigc_info[x].count, x++
А проявлялась ошибка феерически -- запросы на канал 2-й группы
просто никуда не уходили (правильно -- b_data[8]
не было
заполнено). Это вылезло на xcac208, а раньше множественные большие
каналы использовались редко, и либо одной группой (ipp), либо группами
по одному (tsycamv).
Хорошо, что "карта" каналов печатается в system.log -- там и заметил, что во второй группе стоит "@1" вместо "@8".
13.02.2011@Снежинск-каземат-11: вылезло, когда переделывал ручку в CALCED, phys=-1 уже сделал, а DIRECT по невнимательности оставил. И -- хрен чё поймёшь...
Конечно, реализовать сие будет не так тривиально, поскольку придётся
вводить лишние условия/сложности в MarkRangeForIO()
и
FillDataPacket()
(зато CheckFork()
радикально
упростится -- останутся только проверки на OpCode и Count).
14.06.2014: да, надо, ибо реально поддостало -- малейшее добавление на стороне клиента, еще не отраженное в сервере, делает клиента нерабочим...
Муторно, но оно того стоит.
И -- надо постараться возвращать на такие каналы флаг
CXCF_FLAG_NOTFOUND
. Он хоть и cda'шный, и будет
срезаться, но можно прям в cda его отдельно забирать в
ci->rflags
. Бонус -- такие каналы будут правильно
раскрашиваться, поскольку уже давно вся поддержка сделана.
Как выяснилось, потому, что считывание их -- в GetInt()
-- идёт вручную, по циферке, безо всяких strtol()
. Это --
тяжкое наследие давних времён, было уже во времена oldcx (дата на самом
старом cx-server_dbase_new.c -- 10-12-1999).
14.09.2011: перевёл на strtol()
.
"Хитрость" в том, что там везде адресация по позиции --
linepos
в буфере line
, вот и сделал
тривиальный переход к указателям и обратно. "done".
GetBlockLabel()
, в интересах remdrv, чтоб он мог передавать
имя/метку экземпляра устройства подчинённому контроллеру (для реализации в
нём интерфейса inserver).
16.02.2012: оно потребовалось Роговскому, чтоб некая программа имела возможность только ПОДСЛУШИВАТЬ, вообще никак не влияя на функционирование блока и не интерферируя с другими программами даже самими фактами запроса. (Речь об этом зашла еще в январе, тогда и код посмотрел.)
Что интересно, само название флага -- cachectl -- предполагает что-то о кэше, а реально ничего такого пока нет. Просто первоначально предполагался еще вариант "дай сразу прямо из кэша, безо всяких измерений" -- аналогично тому, как могут функционировать скалярные каналы.
Можно было бы ввести такие константы:
CX_CACHECTL_SHARABLE
-- нынешнее ==0.
CX_CACHECTL_FORCE
-- нынешнее !=0.
CX_CACHECTL_SNIFF
-- считать, что такой запрос
совместим с любым другим, но на исполнение его не отправлять.
CX_CACHECTL_FROMCACHE
-- вообще всегда отдавать
ответ, даже если ничего не приходило. Имеет смысл только с
immediate=0, тогда такой большой канал поведёт себя в точности как
скалярный.
Кстати, СЕЙЧАС во всех программах используется cachectl=0, так что конкретные значения CX_CACHECTL_* можно выбирать любыми, не опасаясь за совместимость.
Ничего особо сложного в реализации этого функционала нет, но кое-что
придётся подпеределать -- в частности, код отправки "следующего"
запроса по приходу ответа. Сейчас он просто берёт очередной первый из
списка, а надо будет циклом искать первый не-SNIFF. Ну и с возвратом
FROMCACHE придётся подвыпендриться -- непосредственно перед возвратом
(по "EndOfCycle"?) делать им FillBigcReplyPacket()
.
BTW, логично будет при наличии ключа -readonly
делать force режима SNIFF. Вопрос только, кто и как будет доставлять
указанность readonly и до какой части cda.
16.02.2012: предварительно-оформительские действия:
CX_CACHECTL_*
-- пока просто 0,1,2,3. Жаль, конечно, что
так -- напрашивается-то "по возрастанию активности", FROMCACHE=0,
SNIFF=1, SHARABLE=2, FORCE=3; но не получится из-за требования
совместимости.
CX_BIGC_IMMED_NO
=0
и CX_BIGC_IMMED_YES
=1.
cx_bigcmsg()
вместо cx_bigcreq()
),
переведено с констант 0/1 на свежевведённые.
cda_setbigc_cachectl()
-- для
возможности менять cachectl по ходу работы программы.
В КОДЕ/поведении пока ничего не менялось.
23.10.2012@Снежинск-каземат-11: делаем.
ServeBigcRequest()
:
CX_CACHECTL_FROMCACHE
влечёт:
AddBigcRequest()
-- сразу делается
подготовка ответа, сводящаяся к FillBigcReplyPacket()
, только
для "простоты" она впрямую вызывает bigc_callback()
.
Впрочем, это уже тонкости, имеющие исчезающе малое значение.
AddBigcRequest()
в проверке "а не отправить ли
свежедобавленный запрос" также НЕ отправляет SNIFF и FROMCACHE.
ReturnBigc()
в конце вместо былой отправки первого в
списке теперь ищет в нём первый не-SNIFF и не-FROMCACHE, и если найдёт -- то
отправляет.
may_return_data()
считает "да" при SNIFF и FROMCACHE.
cx-bigc
добавлен флажок -c
, принимающий
параметром значение флага cachectl -- 0...3.
По первоначальной проверке -- вроде работает.
03.12.2012: Роговским широко используется, жалоб не было -- следовательно, считаем, что всё окей, "done".
d_status[]
по умолчанию 0, т.е. DRVS_OFFLINE.
21.03.2012: решение -- прямо перед вызовом
init_blk()
вставлено
d_status[magicid]=DRVS_NOTREADY
.
Вылезут ли побочные эффекты?
21.03.2012: да, вылезли -- почему-то конкретно на самом ist_xcdac20 (и ТОЛЬКО на нём!), перестало производиться вычитывание каналов записи. СОВСЕМ.
Хбз почему. Так что пока ту строчку отключаем.
CacheReadsFromWriteChannels=0
.
Раньше это было безопасно (хоть и некрасиво). Сейчас же, с
введением inserver, вышло, что на время вычитывания каналов некоего
драйвера MASTER (ist_xcdac20) сброшенный флаг влияет также и на
возрасты, возвращаемые inserver_get_cval()
'ом на каналы
другого драйвера (TARGET -- xcdac20): всё потому, что этот же флаг
используется и в FreshFlag()
.
В результате драйвер MASTER считает, что получил старые данные, и не учитывает их, надеясь на приход в будущем более свежих, чего никогда не случится.
22.03.2012: исправлено, по очевидному "правильному" пути:
IsReqRofWrChsIng
, по умолчанию он =0
.
ConsiderRequest()
он добавлен в пару к
CacheReadsFromWriteChannels
.
ReqRofWrChs()
теперь махинирует именно с ним, не
трогая флаг кэширования.
Естественно, проблема решилась :-)
BaseCycleSize
-- на лету через консольный интерфейс.
11.12.2012: сделать-то несложно, но только клиенты не будут знать об изменении, и продолжат использовать (при рисовании графиков) старое значение.
Вот если б было уведомление...
29.05.2013: заметно этого не было, поскольку буфер сравнительно небольшой (обычно набор номеров каналов для чтения), а входные данные большим каналам почти никогда не передаются. Ну и перезапуски GUI-клиентов на пульту не так часты. Существенно вылезло бы при частом скармливании данных консольными утилитами (особенно В большие каналы).
Подозрение на наличие утечки возникло при реализации однопроцессного
cxsd в начале месяца, когда не обнаружилось освобождение lastreqbuf'а
(в сам cxsd_fe_cxv2.c::RlsV2connSlot()
оно
вставлено). А сейчас проверено экспериментально указанием cx-bigc
-Di "123,"x10000" (perl'ом) -- память действительно утекала.
Исправлено добавлением safe_free()
в
cx-server_fdio.c::DisconnectClient()
. С парочкой
subscrbuf
+subscrsetbuf
было аналогично (но
оно не влияло из-за полной неиспользуемости).
08.01.2004: сделал. Работы-то...
И аналогично в cx-bigc и cx-setchan. Свести эту алхимию в отдельный .c/.h?
09.01.2004: первоначальный вариант есть. Пока -- внутри cx-rdt_wrt.h, и без "-ddT".
08.06.2006: добавил описание ключей -dST в "help".
BTW, враки, что «без "-ddT"» -- "-ddh" там работает, вместо пробелов начинают печататься '\t'.
И, кстати, а cx-bigc и cx-setchan до сих пор на сию тему неграмотны, гы-гы-гы!
09.06.2006: добавил также поддержку ключей, определяющих систему счисления для данных -- -ddd, -ddo, -ddx.
$(LOCALLIBDEPS)
поддерживалось.
04.05.2005: перевел. И
$(LOCALLIBDEPS)
поддерживается.
20.06.2005: вообще-то подобное я наблюдал еще два
года назад, на staromakh'е, когда пытался пользоваться им в связке с
tee
, но тогда в причину так и не въехал.
А вообще-то все просто -- ведь stdio по умолчанию делает
line-buffered только те потоки, что ассоциированы с tty, а остальные
(за исключением unconditionally-unbuffered stderr) -- block-buffered.
Вот оно и копит по BUFSIZ
байт.
Так что решение крайне простое:
Во всех консольных утилитах, результат печати которых, хотя бы потенциально, может pipe'иться другим программам, надо:
- Либо делать
fflush()
после печати каждого блока данных (как printable в cx-console);- либо в самом начале программы делать
setvbuf(stdout,NULL,_IOLBF,0);
.
Возвращаясь к cx-console: это программа НЕ высокопроизводительная,
посему сделал там принудительный перевод stdout
'а в
состояние line-buffered. Усе и заработало.
Троица cx-{rdt,wrt,sub}.c хоть и "потенциально
высокопроизводительная", но у них за раз печатается именно строка.
Потому в cx-rdt_wrt.h::main()
также попал
setvbuf(,,_IOLBF,)
.
А вот в cx-bigc.c правильнее оказалось вставить в конце
печати данных fflush()
.
Странная же cx-setchan сейчас вообще одноразовая (не
циклическая), но на всякий пожарный также получила
setvbuf(,,_IOLBF,)
.
10.07.2005: за компанию вставил такую же строчку и в многострадальный staromakh.c.
30.07.2005: сделал -- в самом-то
cx-console.c для этого нужно было всего лишь добавить
обработку CAR_ERRCLOSE
в нотификатор.
Но хуже оказалось другое: обрыв соединения --- это
CAR_ERRCLOSE
, который в
cxlib.c::CallNotifier()
рассматривается как
не-требующий-вызова-нотификатора при синхронном режиме.
Так что пришлось добавить CAR_ERRCLOSE
в список
дергаемых-всегда.
21.01.2007: в cx-bigc сделал. Результаты тестирования: локально (viper-viper) -- 150-170мкс, по сети (viper-linac1) -- ~500мкс.
В остальных утилитах пока что особого смысла делать этот ключик нету (раз каналы возвращаются только в конце цикла). Понадобится -- не проблема.
22.03.2010: ключик переименован в -R -- для возможности задействовать -T под "print ISO8601 timestamps".
Это чтобы из командной строки можно было, без применения специализированных программ, добывать данные с осциллографов (не говоря уж об отладке новых устройств, для которых пока графическая программа не написана).
24.12.2008: для этого надо:
Во -- если указано число >0, то это высота колонки, а если <0 -- то номер параметра, из которого брать высоту! При этом оно будет автоматом отлично адаптироваться к изменениям параметров по ходу работы.
И, в любом случае, при указании этого параметра оно автоматом переключается в режим "выдача для текстового файла".
P.S. И, кстати, давно пора научить cx-bigc УСТАВЛЯТЬ параметры. Причем в ДВУХ вариантах -- уставлять каждый раз, или же только первый раз.
P.P.S. А вообще -- сильно тянет поменять интерфейс этой утилиты, поскольку указание в стиле "-f КАНАЛ" является извращением.
27.12.2008: сделано, именно по вышеприведенному проекту. Формат вывода "для gnuplot'а" включается ключом -g, а высота колонки -- -H. Уставка единичного параметра также реализована -- теперь за это отвечает ключ -aARGN=VALUE; неработающие же пока уставки параметров и данных списками -- повешены на -A и -D.
Вообще-то явно напрашивается некоторая переделка утилитки, а именно:
04.06.2008: поскольку давно работает и используется Роговским -- помечаем как "done". А "переделка" -- на потом, если РЕАЛЬНО и СИЛЬНО потребуется.
cx-bigc
ключик -q
(quiet)
гасил ЛЮБОЙ вывод.
Для этого в "главный if() выбора формата" вставлено отдельной альтернативой -- на первое место --
if (flag_quiet){}
14.12.2013: добавлен ключик -Q
(quiet_data), гасящий лишь вывод данных. Потребно для разборок с
видеокамерами -- иначе печать их многосоткилобайтных данных занимает
море места и времени. При нём вместо данных печатается троеточие
"...".
cx-bigc
, что получаемые данные -- UNSIGNED, а не знаковые,
как он всегда полагает. Это очень актуально, например, для adc200, где
данные -- 8-битовые, но они там БЕЗзнаковые.
04.06.2009: да, добавил флажок -U, при котором, кроме основного действия, еще пере названием единиц (ints/shorts/bytes) печатается "u-".
Ох и нетривиально это оказалось -- там полезла куча условий. Мерзковато выглядит -- вообще, явно надо бы отделить ДОБЫЧУ значения от ВЫВОДА.
Но по теме -- помечаем "done".
Кстати: а может, знаковость должна указываться прямо в протоколе, вместе с dataunits? В CXv4-то всё равно будет общая инфраструктура поддержки РАЗНЫХ типов, в т.ч. вещественных -- так может, в байте описания типа предусмотреть и битик "знаковость"?
05.06.2009: мда, работало оно -- как же!
Во-первых, забыл даже 'U' добавить в список
getopt()
'а, а главное -- формат с "%d" на
"%u" менялся, а вот значение -- по-прежнему бралось
int8
с расширением в int32
-- естественно,
знаково!
Так что теперь переделано кардинально -- вместо внутренней алхимии с
условной добычей, нынче значение предоставляется функцией
GetDatum(int idx)
, которая сама разбирается и в размере
значения, и в его знаковости.
Стало существенно симпатичнее -- хотя можно еще так же попробовать унифицировать подготовку форматной строки.
cx-setchan
странновато реагирует на ключик -h:
вместо того, чтобы показать help/usage, оно пытается разрезолвить его
как имя хоста.
04.06.2009: да, сделал, что оно показывает usage
не только при argc<2
, но и при
argv[1][0]=='-'
-- так что теперь достаточно попробовать
указать ЛЮБОЙ ключик.
01.09.2009: итак:
optarg
(сам парсинг сделан по образу парсера данных
CAN-пакета в cansend_common.c).
Print*()
.
Понадобилось в основном для расшифровки rflags.
Вообще, конечно, интересно -- во что вся эта шобла (cx-rdt, cx-wrt, cx-bigc, cx-setchan) превратится в CXv4. Явно это будет ОДНА утилитка, могущая и обычные скалярные запросы исполнять, и векторные, и агрегированные. И эта утилитка потребуется ОЧЕНЬ СКОРО -- ведь она и есть главный инструмент при начальной отладке!
19.03.2010: смысл -- чтобы мочь соотносить отладочную печать сервера -- DoDriverLog()'и -- с выводом тестовых утилит (для чего, естественно, их надо пускать на одном и том же хосте).
22.03.2010: и в cx-bigc.c тоже добавлено (в ОБА режима печати).
Вообще, конечно, вызывает легкое прискорбие нынешняя дикая мешанина ключиков в разных утилитах (главное -- смысл ключа -t в древних utils/). Но в новых утилитах -- canmon, uspci_test, v4'шных cxclient и cdaclient -- всё уже унифицировано и без шизы.
14.12.2013: в cx-bigc.c тогда было добавлено всё, кроме разрешения getopt()'у принимать этот флаг. Исправлено, проверено, работает.
Заодно все утилиты переведены на stroftime_msc()
(вместо имевшейся странной самодеятельности).
getopt()
. Смысл -- чтоб поддерживать ключик
'-q'.
Ну и сам ключик также сделан.
14.03.2013: вроде сделано.
DoPrintDatum()
.
15.03.2013: вроде работает, закрываем.
flag_binary
. Оно делает write() сначала "свойств" (rflags
и tag (оный продвинут до 4 байт)), потом счётчиков (rninfo, retbufused,
retbufunits), затем info и retbuf.
29.03.2007: причиной послужилос ледующее: Малютин со Старостенко что-то хитрое делали с люминофором на пучковом датчике, и им приходилось производить кучу щелчков хвостом: загрузить некий режим, подождать несколько секунд, сохранить картинку в файл, потом загрузить другой режим, да еще и одну уставку поменять.
А ведь, по идее, в CX это все делается прямо из скрипта -- уставки при помощи cx-setchan'а, снятие картинки -- cx-bigc. Единственная проблема -- немного неудобно: ведь cx-setchan работает с ФИЗИЧЕСКИМИ каналами, а нам надо имитировать манипуляции оператора с экранными (ЛОГИЧЕСКИМИ).
Ну так -- можно сделать просто утилиты "load_mode" и "save_mode", а лучше -- единую программу, которая бы и давала ту же функциональность, что chlclient, но для командной строки.
Планируемый синтаксис ее вызова --
cdrclient ПОДСИСТЕМА [SRVREF] {КОМАНДА...}
Где командами могут быть "load FILE", "save FILE", и т.п.
По умолчанию программа исполняет указанные команды и отваливает. А можно бы дать возможность указывать и ПЕРИОДИЧЕСКИЕ команды -- типа "включи логгинг с таким-то периодом". Тогда можно и функциональность descr2html пристроить сюда же, сделав команду log4web и/или "pog4web_on_data_arrival".
Некоторые тонкости:
Итак, планируемые команды:
load
FILESPEC
save
FILESPEC // FILESPEC=="." => autoselect name
log
FILESPEC // "."=>auto
html
FILESPEC // "."=>auto
set
KNOBREF=VALUE
print
KNOBREF
pause
SECONDS
quit
P.S. Кстати, с появлением "cdrclient" переименование "chlclient" в "pult" кажется намного менее осмысленным.
06.04.2007: программа cdrclient.c была начата позавчера, интенсивно делалась вчера, и доведена до первоначально работающего состояния сегодня. Highlights интересностей:
Поскольку периодичность делается Xt-таймаутами и
DataProc()
'ем, то будущая команда pause
на
периодические команды никак не влияет -- они являются как бы "фоновыми
задачами", а все не периодические -- "консольными" и
последовательно-исполняемыми.
cda_status_of()
не побывает хоть раз CDA_SERVERSTATUS_NORMAL. Ожидание длится 10
секунд.
Пока есть только инфраструктура/планировщик, а собственно ТЕЛА
команд пока не сделаны. Также пока не реализованы "интересности" --
интерактивный режим и команда pause
.
08.04.2007: сделаны тела базовых команд -- load, save, log, set, print. Пара замечаний:
XhFindFilename()
, слегка ее изменив и
обозвав ChooseFilename()
. Халтурноватенько!
Вопрос -- как? Видимо, эти команды должны уставлять некий флажок
"modified", и по окончанию обработки очереди надо (при отсутствии
периодических команд и интерактивности) запускать процесс, аналогичный
первоначальному ожиданию -- чтоб она дожидалась прихода данных ото всех
серверов; плюс, поскольку отправка запросов сейчас делается ПОСЛЕ
уведомления клиентов, то надо перед exit()
сделать
cda_continue(mainsid)
.
11.09.2014: изменена кривость с реализацией команды
set
, имевшаяся изначально -- что строка-параметр
моодифицировалась (заменой '=' на '\0') для простоты.
Теперь строка остаётся как есть, а имя ручки копируется в отдельный буфер.
Бонус -- команды при исполнении печатаются целиком, без обрезания по бывшему '='.
17.03.2009: понятно куда переносить -- в runner/.
Перенес, избавив заодно utils/LocalRules.mk от "CDRCLIENTS", каковая существовала единственно для этой утилитки.
А секцию эту оставим уж здесь.
23.06.2014: проверено, работает. И -- да, надо.
pause
(команда была
изначально, но без наполнения). Для техпроцесса на сварке
("3D-принтер", для изготовления цилиндра).
23.06.2014: делаем:
StartOperation()
в
ExecCommandsInQueue()
. Поскольку теперь она будет не
"начинать исполнение", а исполнять очередной последовательный кусок
очереди.
pause_ms
(перед вызовами proc'ев ей делается =0).
ExecCommandsInQueue()
при обнаружении после
исполнения наличия pause_ms>0
прерывает цикл и перед
return'ом заказывает таймаут -- через указанный промежуток вызвать
PauseEnd()
.
PauseEnd()
, в свою очередь, вызывает
ExecCommandsInQueue()
, и всё продолжается.
cmd_queue_ex_idx
.
Судя по тестам -- пашет. Реализация, конечно, кривенькая, ну да фиг с ней, на сейчас сойдёт.
26.06.2014: да, проверено на "сварке" -- работает.
Дальнейшие усовершенствования:
Причём -- с префиксом времени.
timestamp
, параметр -- буква-"метка". Сохраняет
текущее время в указанную ячейку (A-Z).
waituntil
, параметр имеет вид L+MILLISECONDS, где
L -- метка, к которой привязываться. Делает паузу до момента "того"
времени плюс указанный интервал.
pause_tv
, куда должно
складываться точное время окончания.
Еще энное время назад понадобилось уметь показывать текущее состояние комплекса по WWW -- т.е., уметь генерить html-файлы с текущим состоянием. Федя-jr. выдвигал какие-то диковатые идеи и желания (детали сейчас не помню).
А очевидным было, что надо слабать утилитку на базе
CdrOpenDescription()
, которая будет по приходу данных
сваливать их в html-файл с "красивым" форматированием.
Собственно -- как раз в тот понедельник 25-го апреля изготовил "скелет" -- открытие описания, запуск основного цикла, и т.д., но без собственно сваливания в файл. И забросил -- все силы пошли на C13/МНТЦ#2257.
22.05.2005: ура! Наконец-то смог вернуться к этой работе, и -- оно практически готово. В "первом" приближении -- без расцвечивания, без "правильного" отображения селекторов, да и всякие "кнопки" пока также показываются числами.
Еще из пока отсутствующего: НЕ пишутся заголовки элементов, и НЕ реализовано "многострочное расположение элементов" -- они просто валятся в таблицы.
Получасом позже: заголовки элементов уже ПИШУТСЯ :-).
21.02.2006: мда, по прошествии 9 месяцев оно так толком и не понадобилось... Жаль...
По делу: во-первых, хотя свежевведенная фича "n-подъездности"
("перенос потока колонок") и не поддерживается, но теперь
WriteElem()
хотя бы честно использует лишь 16 младших бит
поля ei->ncols
.
Во-вторых, отразил переименование KNOB_VALUE_LIT_MASK
в
CX_VALUE_LIT_MASK
.
03.03.2006: проапдейтил
get_label_and_tip()
.
accept()
параметру len
(который
value-result!) не уставлялось =sizeof(isrc)
. Ма-ла-дец!!!
05.09.2005: Обнаружилось -- по жалобе от Малютина, что иногда старостенкина программа не может подцепиться к staromakh'у, и анализом staromakh'овской диагностики в таких случаях -- какой-то непонятный "EINVAL".
Я-то долго разбирался, искал, когда ж бывает EINVAL: судя по man-странице, "хрен-знает-когда" -- "Various Linux kernels can return other errors such as EMFILE, EINVAL...". В BSD сказано, что тогда, когде не-был-сделан-listen(). Ну а я честно пытался рыться в исходниках -- net/ipv4/tcp.c, ища какую-то специфичную-для-TCP-проблему, и греша на IP-стек Форточек. И упустил bugreport от FreeBSD, где говорилось про EINVAL в случае, если len is negative...
Исправил, делов-то... Предыдущая версия была за 10.07.2005.
OpenDescription()
,
подгружая описания, а не линковаться с ними. Список обслуживаемых
подсистем будет указываться в командной строке.
Эти модули могут как влинковываться сразу, так и грузиться в момент старта программы (тогда их список также надо будет указывать в командной строке).
CdrFindKnob()
'ом.
08.11.2008: во-первых, этот alien2cx -- отличный кандидат для использования давно планируемого компонента "Module Loader". Вот на нем, как на кошечке, этот модуль и отладим.
Во-вторых, а КОГДА клиенту отдавать значения каналов, и КАК? Варианты:
Тут есть еще вопрос -- а в каком виде отправлять ответные пакеты?
double
'ов.
Впрочем, это вопросы уже, пожалуй, не к ядру программы, а к реализации конкретного протокола -- в данном случае, "staromakh v.2". Хотя потребности "CA server library" также надо изучить, поскольку ядро должно удовлетворять им.
26.11.2008: кстати, одним из модулей протокола доступа может стать оный для DataSocket.
21.01.2015: higglights:
С некоторым закосом под стандартные интернетовские протоколы -- все ответы начинаются с 3-значного числового кода, характеризующего успешность результата.
Поддерживаются 2 команды:
monitor ИМЯ.КАНАЛА
Помимо ответа "200" затем будут приходить строчки вида
100 value ИМЯ.КАНАЛА ЗНАЧЕНИЕ
(код 100 выбран от балды).
write ИМЯ.КАНАЛА ЗНАЧЕНИЕ
Определения лежат в starogate_proto.h.
FindMinMax()
теперь вызывается в любом случае, отдельно от
NormalizeCurdata()
, перед оным.
GetGray2ImageConverter()
возвращает ошибку, а она ее не
проверяет.
27.06.2005: вставил проверку и печать сообщения о проблеме, с перечислением параметров.
10.04.2007: добавил -- просто еще парочка чекбоксов, совершенно аналогичных SYNC'у.
А надо -- сделать simple-Knob'ами, уложив их поразумнее. А "Loop" и "Get picture" переделать на кнопочки [>] и [|>].
11.04.2007: да, переделал внешний вид. Стало суш-чественно симпатичнее!
А возможно, завести также и третий компонент библиотеки -- собственно реализацию протокола (включая описание его форматов и констант). Ибо нефиг в tsycamlib.c и tsycamv_drv.c иметь дублирующийся код.
И, естественно, перейти с cxlib на cda!
- Писались какие-то странные числа в окне ИПП-2-10 (гистограмма).
- Был странный масштаб в том же окне.
- Надо КОРРЕКТНО показывать перегрузку входа -- а то она незаметна, а из-за вычитания фона еще и программа ее не замечает.
- Достало, что ipp приходится перепускать.
18.05.2004: первые две проблемы решились просто -- мы с Федей посидели и поняли, что 1) в какой-то момент Логачев заставил поправить коэффициенты, на неправильные; 2) в вычислении масштаба гистограммного окна использовался совсем другой принцип -- не в единицах ИПП, а так, чтобы шкала была некие 100 единиц (видимо, тот же Логачев, потребовал).
Вывод: ох, зря я не протоколировал изменения в ipp.c!
Насчет отображения перегрузки -- надо, видимо, подсвечивать жирными фиолетовыми bar'ами во всю высоту столбца.
И вообще -- я же давно собирался переделать ipp.c -- перевести его на cda (необходимость в перепуске исчезнет) и перевести его на Knobs.
23.05.2004: еще одна претензия от Феди-jr.: его достало каждый раз при перезапуске программы ИПП перераскладывать его окошки.
Мой вариант: еще давным-давно, на ICALEPCS'2001, высказывалась мысль указывать позиции в X-ресурсах (через xrdb).
Федин вариант: чтобы программа запоминала позиции и могла их восстанавливать, но и позволяла менять.
24.05.2004: еще на тему "раскладки":
Во-первых, пришла в голову мысль: можно ж иметь свой простенький geometry-manager -- чтобы он запоминал список "смаппированных" окон, и когда впервые маппирует очередное -- то пытался бы ставить его снизу/сбоку от последнего; "пытался" -- чтобы оно максимально лезло в экран. Если обламывается -- то чтобы не делал ничего.
Вопрос: а можно ли просто поправить Motif'овский баг и резать флаг PositionHint? (Посмотрел -- хрен его знает, замутно там -- надо разбираться...)
Во-вторых, поговорено со Старостенкой, и результат примерно таков:
Он жаждет иметь утилитку, которая бы сохраняла расположение окон, и потом позволяла бы "одной кнопкой" восстановить его. Резон -- у разных операторов разные предпочтения (у самого Старостенки и у Логачева, например). BTW, можно вспомнить кнопочки настройки под разных водителей у Celsior'а и Brevis'а.
Ну что, в дополнение к cx-starter нужен еще и некий "Desktop manager"? А может, существуют готовые?
04.06.2004: сделал, что мотифовский баг фиксится. Учитывая, что ipp теперь будет однооконным, никакой надобности во внутреннем geometry-manager'е нету -- нехай все делает window manager.
06.10.2004: поскольку новый клиент уже давно готов и работает на пульту, и все означенные в заголовке проблемы исправлены, помечаем как "done".
08.07.2004: итого, основные отличия этого нового варианта:
- Поскольку реально окошек-ИПП оказалось немного, делать многооконный интерфейс (по окну на каждый ИПП плюс окно "панель управления") представляется совсем неразумным. Укладываем все в одно большое окно.
- Доступ к данным делаем через cda (которая выросла из кусочка прошлого ipp :-).
- Управляющие селекторы делаем при помощи Knobs.
- Само окно делается теперь не руками, а при помощи Xh, и содержит на тулбаре кнопки "Набрать фон", "Сбросить фон", "Замерзнуть" и "Выход".
- Ради экономии места каждый "элемент" -- "бывшее окно" делается так: всю правую часть занимает картинка, а слева от нее -- номер ИПП (tooltip'ом к нему вылазит "длинное имя"), и под ним плотно упиханы управляющие ручки -- Sensitivity, Time, Magnification.
Создание этого нового варианта пока приостановлено, до перевода пульта на Cx/ProtoV3. Потом будет продолжено параллельно с новым vacclient, cx-starter и заменителями файловых диалогов.
24.08.2004: новый ИПП-клиент (nipp/ipp.c) в первом приближении готов. Он уже сейчас обладает той же функциональностью, что и старый, но основан на cda+Knobs и монооконен.
Нынешний вариант содержит, ради экономии места, управляющие селекторы не слева от графиков, а сверху (поскольку селекторы "горизонтАлисты").
Введена рудиментарная поддержка цветовой сигнализации селекторами -- поддерживаются DEFUNCT, HWERR и (куце) OTHEROP. И уже почти сделано отображение "перегрузки" -- отсутствует только собственно детектирование оной. (Часом позже: вставил уже и детектирование, но пока не проверял.)
06.10.2004: все было проверено еще с месяц назад, и все работает (включая отображение перегрузки), так что помечаем как "done".
Это похоже на 1) sukhphase, где надлежало отображать данные с нескольких точек графика в числовом виде; 2) отображение (в будущем множественное) "крупногабаритных" значений каналов. Так что, наверное, стоит и подумать об унификации интерфейса этих трех случаев.
Во-первых, отображать сии текстовые данные следует, видимо, на дополнительной панельке в том же окне (в случае с ipp -- справа от графиков; пусть появляется только когда отображается хоть что-то, а в остальное время -- пусть будет unmanaged).
Во-вторых, вопрос, каким именно способом юзеру надлежит эти параметры "вызывать" и "удалять". Закладываться на Ctrl+Btn3, как с bigval'ом, не стоит -- там ситуация отдельная. А вот унифицировать ipp с sukhphase и возможными будущими клиентами -- стоит.
И, кстати, недурственно бы те "палки", для которых выводятся данные, как-нибудь помечать.
Сейчас в sukhphase принята такая схема: Btn1 ставит репер, Btn2 позволяет его двигать, Btn3 -- убирает.
k_sens[]
и k_time[]
и выполняется, но
неполностью -- в самом начале не ставится
COLALARM_JUSTCREATED
?
12.11.2005: вставил -- там делов-то было. Это наметка на будущее -- когда у "сложных" plugin-knob'ов будут внутренние "под-knob"'ы, надо будет исполнять аналогичные телодвижения.
26.06.2006: чуть более детально:
widdepinfo
.
31.07.2006: да, проект создан некоторое время назад. Highlights его таковы:
Элемент-"коробочка" же создает только собственно график и ручку "коэффициент отображения".
И он использует опцию panel, которая два года назад и создавалась для ipp, хотя и в несколько ином варианте :-).
01.08.2006: добавлена поддержка "методов" -- с
использованием свежеопубликованных Chl_E_Nnn_m()
;
естественно, alarm'ы не поддерживаются.
16.04.2007: поскольку программа работает уже чуть ли не полгода (хоть и не до конца, в смысле мелочей, доделана -- но это больше по милости Феди-jr, да и не принципиально), то считаем за "done".
Мало того, что тогда программу можно будет использовать для разных приложений (хоть для ВЭПП-4 :-), но главное -- описание-то будет впрямую читабельно cx-starter'ом, безо всяких chaninfo=...!
К тому же, в самом исходнике все описание уже лежит особнячком.
16.04.2007: готово -- клиент переименован в ippclient.c, описание вынуто в chlclients/DB/db_linipp.h, с clientprog=ippclient, а определения номеров элемент-плагинов унесены в include/ippclient.h.
Учитывая, что так уже сделано в ndbp, надо аналогично разделить linbpm, и именно так же поступать со ВСЕМИ остальными не-тривиальными клиентами.
17.04.2007: ну что ж -- и linbpm так же разделил, на xmclients/bpmclient*.[ch] и chlclients/DB/db_linbpm.h (clientprog=bpmclient), а в include/bpmclient.h перемещены определения LOGD_BPM_* и былое содержимое _hwmap-файла -- "описания" карпиных каналов.
Т.е., каждая "коробочка" делается knobplugin'ом, вместо elemplugin'а, а раскладкой "оформления" занимается обычный элемент, в котором оно всё содержится.
02.07.2012: детали:
Это сделано быстро и просто.
Данной части и посвящено всё дальнейшее изложение.
SetValue_m()
, безо всяких
synthetic.
IPP_ESPECTR_Draw()
убраны прямые обращения к
me->e->content[]
(которые б просто не проканали).
Для этого введены поля i_ki
и r_ki
, для
старой версии заполняемые как ссылки на content[], а для новой
получаемые через datatree_FindNodeFrom()
по именам,
указываемым в widdepinfo.
04.07.2012: добиваем:
ident
(заполняемое
создавателями), для отвязки кода сохранения данных от элементов. А
ipp_elems_array[]
переименован в
ipp_recs_array[]
.
Заодно possible_scales[]
переименовано в
magn_factors[]
.
В общем, оно работает, так что "done".
10.12.2012: угу, тогда zlinipp сделал, а переименовать (и, соответственно, выложить на пульт) забыл.
В результате вылезло, что "старая" версия, не ловя ExposeEvent, также не перерисовывалась в режиме [пауза]. Сделано.
Ну и сама группировка переименована и проапдейчена.
13.01.2013: и старый код наконец-то выкинут и из ippclient.c, и из db_linipp.h.
XCopyArea()
(шрифт,
по-хорошему, надо добывать ресурсом -- через
Xrm-чего-нибудь()
)
OpenDescription()
общедоступным).
25.07.2004: еще насчет транспонированного текста: роясь в архивах fvwm'а, наткнулся на упоминание программки "xvertext". Найдя ее исходники (компилируются хреново, а потом падает по SIGSEGV) узнал, что она делает "дело" путем загрузки шрифта и выкладывания его на ximage'и. А потом еще наткнулся на упоминание, что нечто подобное имеется в XFig'е -- точнее, что у XFig'а есть какая-то связь с xvertext.
06.07.2005: "транспонированный текст" мы делать уже давным-давно умеем, так что эта проблема снята. А насчет того, как компоновать собственно гистограмму с метками подписями -- да в общем-то довольно просто. Итак:
28.07.2005: сделал "заготовку" -- создание grid'а
и расстановка вертикальных меток. Добыча текста меток и tooltip'ов
сделана такой же функцией get_label_and_tip()
, как в
Chl_gui.c и descr2html.c.
Кстати, стало ясно, что эту функцию надо вытаскивать в, видимо, Cdr.
31.07.2006: проект реализован, так что "done". Блин, ну писец какой-то -- начал это все ДВА ГОДА назад (08-07-2004), сделал заготовку -- ГОД назад (28-07-2005), а добил только сейчас. ДВА ГОДА!!! Ну что за тормоза, а!!! (Хотя -- ты ведь знаешь, почему тормоза были. Сейчас причина устранена, так что должно пойти быстрее. А 2004-й был очень хорошим годом -- и приятным, и радостным, и продуктивным! (Катюша?))
01.03.2006: отразил в vacclient'овском
PopulateWindow
недавно введенную
n-этажность/многоподъездность -- теперь от поля ncols
учитываются только младшие 16 бит.
03.03.2006: а также обновил
get_label_and_tip()
(м-мать!!!).
Вопрос же -- КАКОЙ интерфейс делать.
26.06.2006: замечания по теме:
И по правой кнопке мыши просто переключаться на сетку с числами и фокусировать нужный knob.
Вот этот 3-й вариант и реализовываем.
И еще -- надо кнопку "linear/logarithmic" также иметь на тулбаре.
Но реально возникает контрвопрос -- а как? Видимо, каждый такой
элемент делать именно "элементом" -- подчиняясь флагам
"notitle"/"noshadow" и fromnewline
.
CdrDup{Groupunits,Eleminfo,Knobs}()
.
ChlPopulateWorkspace()
, что занимается раскладыванием
готовой группировки, вытащить в отдельную функцию -- например,
"ChlLayOutGrouping()
".
Так, немного подумав (после обеда): а нафига иметь "двойное
отображение"? Просто нехай существует "application-level callback"
(который ставится через ChlSetDataArrivedCallback()
), и уж
в нем пусть пробегает по НОРМАЛЬНОЙ группировке и на основе ее данных
отрисовывает те каналы.
А чтоб особо не маяться -- в каждом "столбике гистограммы" заимеваем указатель на "base knob" (первый из тех 6).
Зато плюс -- что иерархия ОДНА, и модификация желтого диапазона в сетке чисел отразится и на гистограмме.
Так что -- пока что
"CdrDup{Groupunits,Eleminfo,Knobs}()
" в баню. А вот
"ChlLayOutGrouping()
" -- по-прежнему нужен, хотя бы для
ipp.
07.07.2006: еще неделю назад (не записывал, потому как ваял презентацию на предзащиту) сделал более-менее рабочий вариант. Highlights:
И -- "правильная" формула вгоняния значения в шкалу выглядит как
(COLHEIGHT-1) - (sv*(COLHEIGHT-1)) / sm
где COLHEIGHT
-- высота графика в пикселах,
sv=v-minv
и sm=maxv-minv
-- значение и
максимум, из которых вычтен "неотображаемый" минимум. И больше -- НИГДЕ
никаких "- 1
" быть не должно (в частности -- в
sm
)!
27.02.2007: в продолжение темы "правильной"
формулы -- см. раздел о RESCALE_VALUE()
, в терминах
которой это вгоняние будет выглядеть как
(COLHEIGHT-1) - RESCALE_VALUE(v, minv, maxv, 0, COLHEIGHT-1)
-- или, с отзеркаливанием сразу --
RESCALE_VALUE(v, minv, maxv, COLHEIGHT-1, 0)
XhMakeTempMessage()
; в соответствии с идеей после
обсуждения с Гусевым за 30-06-2004: при входе курсора в столбец в
statusline отображаются числами текущие ток и напряжение. И даже
обновляются по приходу данных.
Возможно, стоит туда же сваливать и флаги, при !=0.
ChlLayOutGrouping()
-- очень даже понадобился, именно
он выкладывает все в числовой панели.
Что еще осталось сделать -- до полной готовности:
DisplayAlarm()
'а & Co.).
13.07.2006: пункт 1 исполнен -- через ныне
опубликованный интерфейс ChlHandleStdCommand()
.
14.07.2006: пункт 2 также исполнен -- через
CdrChooseColorState()
. Замечания:
16.07.2006: и пункт 3 также реализован.
Очень просто -- в NewDataArrived()
узнаем новое состояние
alarm-или-нет, при надобности щелкаем переменной
alarm_flipflop
либо скидываем ее в 0, а при отображении --
выбираем один из двух GC, используя alarm_flipflop
как
индекс в массиве.
16.07.2006: похоже, надо б отображать столбцы в нормальном состоянии не зеленым, а каким-то другим цветом. Причин две:
Использовать более бледно-зеленый? Или -- менее яркий, типа VIC'а? Собственно сам VIC не подходит -- он какой-то серо-зеленый, а просто зеленым выглядит только на стандартном сером фоне.
17.07.2006: да, перешел на темно-зеленый #00a000. Стало чуть лучше, и даже вопрос -- а не перейти ли на подобный цвет вообще на всех гистограммах? Вот только на КАКОЙ именно цвет -- ведь он все же должен быть красив... Или ввести ДВА набора цветов для гистограмм -- нынешние "яркие" плюс более "спокойные"?
И еще: сделал, что RELAX по напряжению также отображается. Никакого разумного цвета для сего найти не удалось -- так что пришлось воспользоваться GRAPH_PREV ("тень"). Фиговенько -- похоже, надо как-то продумать мысль об отображении не унифицированных теней, а "bound-to-color" -- т.е., чтобы для красного столбца "тень" была одна, для синего -- другая.
О! А может -- тот штриховкой? При том же цвете, а?
22.07.2006: насчет темно-зеленого #00a000 -- нет, не перейти!!! Он слишком слабо отличается от цвета SFERR #8b8b00...
22.07.2006: мелкая модификация: при наличии
"системной" ошибки (CDR_FLAG_SYSERR_MASK
-- HWERR, SFERR,
DEFUNCT, ...) столбец всегда рисуется на всю высоту (как в ipp при
перешкале).
23.07.2006: уф, подобрал-таки "удобный" цвет для гистограммы в нормальном состоянии -- «спокойный светлый салатный» #00c080. Он вроде более-менее нормально сочетается и различается со всеми остальными.
"Процесс":
Но вообще-то это уже конец/задница/тупик -- промаялся я с подбором долго, и подобрал с неслабым трудом. Иных цветов зеленой группы уже не найти -- только синие, там резерв еще есть.
30.07.2006: реализовал "подтверждение алармов"
(alarm acknowledgement) по образу и подобию alarmonoffled+Chl'ного. По
щелчку на столбце, в котором стоит CDR_FLAG_ALARM_ALARM
либо у Ialm'а, либо у Ualm'а, вызывается
fki->uplink->emlink->AckAlarm()
. А чтобы и сама
гистограмма перестала мигать -- введена переменная (sic!)
alarm_acked
, работающая в паре с
alarm_flipflop
, и по тому же алгоритму, что и в Chl'е.
31.07.2006: вчера поставил программу на пульт. И linvac, и ringvac. Вроде все работает, посему -- считаем проект законченным, "done".
(По рассказу Знобищева, в первый же вечер Клющев, придя и не увидев знакомых чисел, перепугался и стал тыкать во все кнопочки. Нашел, как вернуть числа, и в численном виде оставил. :-)
28.08.2006: немного дебильновато выглядит, если для обезбибикивания щелкнуть в числовом окне, а потом переключиться на гистонрамму -- оно не бибикает, но продолжает мерцать.
По-хорошему, надо в гистограмме пользоваться
флагами alarm_*
от самого элемента.
28.08.2006: заметил еще какую-то странность: при отключении питания ДИВов (когда переключались) почему-то оно и бибикает, и столбцы синие (defunct). Хотя вообще-то в коде написано наоборот -- что у ALARM'а приоритет наивысший... Странно...
02.08.2006: сделал.
03.08.2006: хрю-хрю!!! А по вертикали -- не влезет! С 12-м шрифтом нынешнее окно linvac с 48 столбцами имеет ширину 861 пиксел; даже при небольшом скоращении (за счет "уплощения" шкалы) вместе с тулбаром и заголовком оно все равно будет вылезать за нынешнее пультовое ограничение в 864 пиксела. Вот только после перехода на TFT с 1280*1024 -- станет влазить...
И еще минус: при повороте наверняка возникнет желание удлинить столбцы. И это съест экономию, которая вышла бы за счет сокращения пустой площади тулбара. (Реально-то НАВЕРНЯКА придется удлинить -- иначе, даже с отображаторами текущих значений тока, тулбар будет шире.)
alarm_*
используются от "настоящих" элементов.
10.11.2006: сделал -- там особо и говорить-то не о чем. И -- придуривается при повторном вызове так же, как и из сетки.
06.02.2007: приравнял в смысле мыши метки к столбцам -- теперь и Ctrl+MB3, и курсоренье поверх меток имеют тот же эффект, что и на столбцах.
06.11.2004: Несколько замечаний:
info[]
возвращало бы тип
прибора. Тогда можно будет при старте выполнить одих "холостой"
запрос, с retbufsize=0
и immediate=1
. Плюс,
естественно, надо будет завести и "реестр кодов-типов осциллографов".
07.07.2008: неа, не надо этого делать -- это задача
другого "уровня", информация о типах должна браться из "БД
конфигурации". Gо-хорошему, и "описание параметров" также должно
предоставляться БД. Тут, в CXv2, мы этого делать уже точно не будем,
так что см. раздел "Насчет БД конфигурации" в
bigfile-0002.html.
07.04.2005: ага! Мыслишки: в частности, "описание на лету" должно отдавать параметры: минимальное возможное значение, максимальное, макс. количество точек.
"Задвигов" только два:
Видимо -- надо будет иметь этакое "описание параметров", и селекторы создавать уже из него, а не просто строкой в коде, и по этому же описанию делать трансляцию строка->значение.
(А в CXv4, когда получится отдельное поле knobinfo'ы "список для селектора", сия проблема исчезнет.)
23.01.2008: списочек на сегодня:
03.04.2008: продолжение:
08.05.2009: "показывать"-то несложно, вопрос -- КАК СЧИТАТЬ. Т.к., возможные варианты:
Для больших чисел просто -- раз в секунду показывать некую переменную, инкрементируемую при каждом кадре, и сбрасывать её потом в 0. Но около 1 это будет казать просто "1", а меньше 1fps -- вообще 0; не катит!
Старостенко предложел считать раз в 5 секунд -- тогда доступно 0.2fps; если раз в 10 -- то 0.1fps, а всё, что меньше, считать за 0. Но это тормозной и визуально неприятный способ...
А как делают Mozilla и wget?
14.05.2009: во -- а что, если использовать
одновременно ОБА способа, и при onesec_fps>4 показывать его (раз в
секунду, как ("%dfps",onesec_fps)
), а иначе -- fivesec_fps
(раз в 5 секунд, как
("%d.%d",fivesec_fps/10,fivesec_fps%10)
).
08.04.2010: оно реализовано примерно по приведенному проекту (только 1-секундный результат используется уже начиная с границы 1fps -- из-за отдачи 1/10-долей), живёт в misclib'е -- fps_counter.[ch]. Да -- и отдаются ДЕСЯТЫЕ доли FPS'ов, чтоб упростить жизнь программе-юзеру.
07.04.2005: Губин потребовал иметь возможность менять пределы отображения. В идеале, наверное, именно указывать пределы, а для начала хватит и селектора "увеличение".
Сделал -- с коэффициентами (как у ИПП) 1, 2, 4, 10, 20, 50, 100.
07.04.2005: и еще он возжелал сеточку.
Что ж -- тоже сделано. Выглядит, признаюсь, препоганенько, но уж пусть так.
06.08.2006: какого черта?! Давным-давно готова программа -- так что "done".
22.02.2008: поскольку уже есть более свежая и продвинутая nadc333, которая имеет данную фичу от рождения, то -- "obsolete".
21.02.2008: сделано в первом приближении еще чуть ли не месяц назад, протестировано на симуляторе. Сегодня -- проверено на реальном устройстве.
Из ляпов -- в text2show[]
-то не было параметров
SHOT/ISTART/WAITTIME/STOP, так что значения оставались 0, и,
следовательно, отправлялись в параметрах при первом запросе -- так что
программа при запуске гасила ISTART. Теперь после bzero()
и перед psp_parse_as()
все параметры циклом уставляются в
-1
.
Что еще осталось -- запись/чтение файла и рюшечки типа включателя "IStart".
13.05.2012@Снежинск-каземат-11: всё проверено, еще энное время назад; и даже уже исчезло в пользу v2hw/fastadc/. Так что с чистой совестью "done".
28.08.2006: чего хочется иметь (типа план работ):
Горизонтальный же scrollbar должен быть ПОД горизонтальными подписями, но МЕЖДУ вертикальными -- те должны быть "по бокам" от него.
31.08.2006: Ну -- некий первоначальный "скелет" сделан.
01.09.2006: пообщался с Гусевым на тему, как у него сделано отображение в adc200 (выбор пределов для графика, etc.). Ой, хи-хи-хи. ы-гы-гы!
Он всегда отображает просто 8-битовые числа -- [0..255], не задумываясь о смысле, а ПОДПИСИ рисует уже с учетом и диапазонов, и сдвигов нулей.
Итого, вариантов получается два:
Хотя в общем-то случае правильнее бы делать с пересчетом, здесь вполне адекватным будет отображать в 8-битовой шкале (особенно учитывая, как иначе пришлось бы извращаться с учетом сдвигов нулей).
01.09.2006: еще результат: я-то вчера пытался управляющую панель оформить в стиле "a-la Goussef" -- с XmFrame, с метками-подписями. Вышло -- хреновастенько, ибо рамки эти никак не уравниваются по вертикали с графиком, и вставленные в сетку они сдуревают при filling:=1... Короче -- надо продолжать идти СВОИМ путем, ценить СВОЮ самобытность, и пользоваться ЭЛЕМЕНТАМИ.. Вот с элементами, являющимися формами, всех вышеперечисленных проблем не возникает.
12.09.2006: за прошедшие пару дней практически реализовал внутренности adc200. Highlights таковы:
Что осталось сделать:
SetKnobValue()
, дважды. При отпускании же
паузы оно автоматом пересчитает то, что есть реально.) 22.09.2006: сделано.
psp_parse_as()
, безо всякого собирания -- прямо поштучно
из argv[]
.
И, как минимум, отображение в горизонтальном масштабе не только номера отсчета, но и времени.
21.09.2006: реализовал сохранение данных в файл. Детали:
CdrStatGrouplistMode()
.
Единственное что -- подсистема указывается как "adc200,ПОДСИСТЕМА" (по аналогии со всякими text/plain, text/html etc.). "ПОДСИСТЕМА" -- на будущее, когда можно будет app_name из командной строки указывать.
Заодно уж сделал, что и на горизонтальной оси отображаются не номера отсчетов, а время в наносекундах (для timing=TIMER считается, что 1ns/отсчет).
22.09.2006: реализовал отображение "того, что под мышью" в statusline. Отображается, только если в этой точке есть данные -- иначе не пишет ничего. По приходу новых данных обновляется. Экспериментально получил знание:
Перехватывать надо не толькоТак что во всяких istcc.c/phm-tsyline.c/lebed_meat.c используется недоделанный подход.LeaveNotify
(для убирания) иMotionNotify
(для отображения), но иEnterNotify
тоже! Дело в том, что при входе курсора в окно присылается толькоEnterNotify
, так что корректно бы и на него тоже правильно реагировать, а не дожидаться последующих шевелений мыши.
Доделано наконец-то и чтение из файла, примерно по означенному в за
12-09-2006 проекту. Унификация -- код "условного обновления" шкал
вытащен в отдельную функцию DisplayNewData()
, и вызывается
как по приходу данных, так и при загрузке.
И уж, наконец-то, реализован "умный" разбор argv[]
, так
что имя файла можно указать и из командной строки.
Замечание: похоже, adc200 становится этаким reference-приложением для осциллографоподобных программ (за вычетом раздельных шкал, вместо будущих двух вложенных DrawingArea). И формат файла вполне можно брать за стандартный -- хоть для того же sukhphase.
28.03.2007: поскольку программа уже давно вполне функциональна, то помечаем раздел как "done". Дальнейшие же усовершенствования пойдут отдельными разделами.
10.11.2006: сделал, это достигается ключом командной строки "black".
Единственное что -- никакого массива [2][n] (как думалось) делать не
стал -- в определениях XmNbackground
везде ставится
заранее определенный bg
, а в отведении GC применяется
хитрый приемчик: в XhGetColor()
передаются параметры вида
XH_COLOR_GRAPH_NNN+cofs
, где
cofs=show.black?XH_COLOR_BGRAPH_BG-XH_COLOR_GRAPH_BG:0
(пользуемся тем, что наборы обычных и BLACK-цветов синхронизованы).
ADC200_PARAM_ISTART
(одноразово-срабатывающий программный
запуск).
28.02.2007: поскольку одноразово-срабатывающесть в драйвере была переделана на нормальный булевский флаг, то и тут оно сменилось на onoff-panel "Int. Start", в нажатом состоянии зеленую.
26.04.2007: ага, а упоминание
show.params[ADC200_PARAM_ISTART] в text2show[]
добавить
забыл, и в результате ладно, что этот параметр нельзя было уставить из
командной строки, но оно оставалось равным 0, из-за этого уставлялось в
первом же запросе, и, как следствие, уставлялись в 0 и все остальные
параметры. Исправил.
28.03.2007: смысл -- как усовершенствование вообще, так и приведение к годности для дополнения/замены гусиных adc200 на linac1. Общие пожелания следующие:
physprops_t.r
, делитель?) (А по максимуму Старостенко согласен на некий "формульный язык" -- чтобы можно было организовывать зависимость от некоего "внешнего" параметра (частота комплекса, ток в некоем магните, etc.).
Что уже сделано:
Чтоб размеры у тянутых окон при
сворачивании/разворачивании не прыгали, пришлось
AdjustPreferredSizeInForm()
делать всей шобле: графику,
scale_bot'у, а главное -- parent'у parent'а (frame) -- т.е., первому
"сверху" child'у формы workspace. И оказалось, что последнего
достаточно, а остальное вообще не нужно! Т.е., делать
AdjustPreferredSizeInForm()
надо только самому верхнему
уровню, "непосредственно" управляемому бордюром окна. Почему так --
хрен его знает, в иной ситуации может оказаться и по-другому...
17.03.2010: ага -- как раз НЕ так!!! И совсем НЕ так
оно стало явно после введения Xh_hilite: размер прыгает обратно либо
при его появлении, либо при исчезновении (в зависимости от того, какой
части виджетов БЫЛО сделано
XhAdjustPreferredSizeInForm()
). Более детально -- см. в
разделе по Xh_utils.c за сегодня.
XhWindowCompactMask
, плюс у всех элементов (Controls,
frame графика) убирается тень.
26.04.2007:
29.04.2007: с реперами -- все маялся и маялся, куда же их помещать, как их ставить/удалять, и т.д. и т.п. И справа от графика -- фигово и будут проблемы с растягиванием графика. И слева от графика -- тоже хреново, много места занимают. И общий менеджмент -- тоже маета: держать их отсортированными, "не более чем n", всякое таскание мышью -- бе-е-е... Промаялся целый день.
А потом, уже по пути на ужин -- снизошло:
Дальнейшие действия:
03.05.2007: далее:
XhProcessPendingXEvents()
, ()
и
()
.
SwitchContentFoldedness()
.
PointerHandler()
, меняет координату
этого репера на координату события. (Раньше же оно смотрело на само
событие -- есть ли ButtonNMask в поле state, не является ли событие
ButtonPress'ом с button==N, etc.)
Кстати, еще на прошлой неделе общался с Пироговым: говорит, что просто коэффициент, на который бы домножались вольты перед отображением -- его не устроит, у него там некая кривая. Так что -- надо уметь читать файл и делать аппроксимацию.
19.12.2007: реализовал возможность указывать "коэффициент"-множитель для каждого канала -- параметры coeff1 и coeff2. Это (пока) влияет только на шкалы слева/справа, числа в statusline и реперах, и сохранение в файл. Плюс, введены строковые параметры units1 и units2, призванные заменить умолчательные единицы "V" (но это пока сделано не до конца).
Доп. результаты/выводы по опыту изготовления сего:
А основополагающая проблема -- неизвестно, какой ФОРМАТ использовать. Откуда его брать? Десятичным логарифмом от минимума и максимума (а если кроме множителя еще и базу указывать можно будет -- т.е., сдвиг нуля...)?
27.12.2007: был ляп -- у параметра PTSOFS стояло minnorm=1, вместо надлежащего 0. Исправил.
29.12.2007: да, плагины такие будут требоваться в нескольких разных программах -- на данный момент видны управление пучковым датчиком и стендом для Снежинска. И -- также будет требоваться плагин для adc333. Так что -- впору изготавливать "общую инфраструктуру" для графиковых плагинов; Xh_graph?
Чтобы оно само создавало виджеты graph и axis, само бы все отрисовывало, реагировало бы на resize, etc. Для этого, видимо, надо будет указывать N штук (N<=5) буферов, пар пределов, и т.д. Короче -- некий "graphrec". И еще -- чтобы некие "user"-supplied hooks вызывались при прорисовке (для рисования реперов, средних, ...) и при обработке событий от мыши (для установки реперов и "резиночки"). Общая "техника" -- программа указывает "маску событий" (до-тени; до-сетки; до-графика; после-графика), а hook'у передается "момент" -- для какого из этих событий он вызван. В общем -- "объект"/"класс" для рисования графиков.
XDrawLines()
.
10.05.2007: готово.
Вначале-то хотел это повесить на сервер, указывая частоту в
параметре immediate
, но, поразмыслив, пришел к выводу, что
проще это делать в клиенте.
10.05.2007: сама идея очень проста -- указывать "максимальные герцы" (0 -- фича отключена, и обновлять со скоростью отдачи данных сервером), и при получении данных проверять, прошло ли с момента предыдущего обновления времени более, чем 1000000us/maxfrq.
Так что -- ввели еще один параметр, maxfrq (умолчание=0).
Guard же для сравнения времен имеет такую логику -- "если oneshot==0 и show.maxfrq!=0", т.е., по нажатию кнопки [|>] она результат показывает сразу.
Точность отработки заданной частоты, конечно, далеко не выдающаяся: во-первых, потому, что частота прихода данных и заданная частота не обязательно кратны, а во-вторых (и это главное!) время-то отсчитывается от ПРОШЛОГО отображения, что приводит к охренительной погрешности. Впрочем -- это уже неважно.
Вообще-то, конечно, стоит подумать о более корректной реализации ("привязывающейся" к "абсолютному" времени), но пока что и так пойдет. Так что -- "done".
19.12.2007: Старостенко это подтвердил, что оно им на пульту нужно (плюс что ему также хотелось бы мочь видеть и разность текущего с сохраненным, и воопче иметь бы какую-нить матобработку -- по минимуму среднее и какой-то тангенс/касательная).
Сделать сие совсем несложно, так что быстренько реализовал:
Но, подумав, сделал проще: есть кнопка "скопировать текущий в
хороший", копирующая
Так что реализация ИСКЛЮЧИТЕЛЬНО простая.
mes_info[]
+mes_data[]
в
svd_info[]
+svd_data[]
и выставляющая флажок
"отображать хороший" (use_svd
), плюс кнопка "забыть
хороший", просто сбрасывающая use_svd
.
Про всякие усреднения -- надо думать (как минимум, ГДЕ это рисовать; прямо на графике?). А диапазон можно задавать 1-м и 3-м реперами.
29.12.2007: с пиктограммой возникла идея: ведь "хороший" режим это что? -- правильно, это "good", "fine", "wonderful", в общем -- положительные эмоции. А оные обозначаются рукой с поднятым большим пальцем. Так что -- надо как-то творчески переработать пиктограммы рук из progman_icons.bmp (изобразить с другой стороны и поднять палец).
03.04.2008: да, сделал такие пиктограммы -- по большому счету, с нуля, на progman'овские только ориентируясь. Они названы setgood и rstgood, и уже задействованы в обеих (на настоящий момент) осциллографных программах -- adc200 и nadc333.
13.05.2012@Снежинск-каземат-11: да, фича давно реализована.
20.12.2007: собственно -- практически сразу же стал ваять этот fastadc_common.[ch], складывая в него стандартную для быстрых АЦП начинку, так, чтобы программа конкретного АЦП указывала только некие специфические вещи (плюс -- населяла бы окно (в т.ч. ctl_form), делала бы отрисовку графиков и чтение/запись данных.
Первый прицел -- и adc200 потом перевести на этот модуль.
А потом -- чем черт не шутит, может, еще более стандартизируем все это как-нибудь, так что можно будет обходиться просто неким текстовым описанием (будет тогда та самая программа "осциллограф вообще").
26.12.2007: промаявшись неделю с тягомотной реализацией, и доведя ее до появления просто пустого окошка, решил пока забить. Причина -- все-таки слишком затратно, хоть и много общего у разных быстрых АЦП, но унификация выходит (на нынешнем уровне развития) дороговато.
Вот когда потребуется ТРЕТЬЯ программа -- тогда реализую, на новом уровне понимания. Опять же, к тому времени будет "правильно" сделанная adc333, с человеческим представлением данных, а не только узкоспециализированная под 256 значений adc200.
03.03.2010: в связи с потребностью в программе для ADC200ME возвращаемся к этой теме. Итак:
А именно:
fastadc_show_t
, уже включающий массивы
data[], coeff[], zero[], units[], descr[] размером в MAX_LINES
.
RunFastADC()
на лету:
fastadc_show_t
.
fastadc_show_t
к
psp_paramdescr_t.offset
.
fastadc_show_t
окончательно
оприватится из .h-файла в .c.
(В идеале -- и это бы как-нибудь упростить, например, сделав текстовым описанием. Видимо, в v4 так и будет, там есть все ингредиенты: 1) параметры адресуются именами; 2) имеется парсер из текста в группировки/элементы; 3) ??? )
С учетом наличия psp-таблицы с описанием параметров, можно дальше --
через "#" -- писать осмысленное вещи: имя параметра плюс, ориентируясь
на psp_paramdescr_t.t
, текстовое значение; в т.ч. --
lookup-имена.
Замечание: имена-то она станет брать первые найденные, так что надо
будет более человекочитаемые пускать раньше (в случае с adc333 -- в
range_lkp[]
должно сначала идти "4096", а уж
потом -- "4".
P.S. А вообще, какого лешего это всё здесь, а не в разделе oscilloscope? Видимо, потому, что это пока не совсем "общий" осциллограф...
12.03.2010: продолжаем:
RunFastADC()
добавлен параметр
line_num_base
-- указывающий, откуда нумеровать каналы (с
0 или с 1).
min_val
и max_val
,
описывающие возможный "разумный" диапазон входных данных.
17.03.2010: в основном доведено до юзабельного состояния:
line_def_name
-- как
именовать канал (у ADC200ME -- никак, просто номер).
info2on
передаётся функция, по текущему
mes_info[] определяющая, какие из каналов "активны", т.е.,
а) присутствуют в данных, и, следовательно б) могут быть
отображены.
mkstdctl
-- создаёт "стандартный" элемент
управления, такой, как метка, управление масштабом, атрибуты реперов, и
т.д. Тип элемента указывается параметром kind
(один из
FASTADC_CTL_nnn
), а параметры a
и
b
дают "уточняющую" информацию (к какой линии относится
элемент, номер репера).
mkparknob
-- создаёт по указанной
simple-Knob-спецификации ручку, регистрируя её у себя в
k_params[]
и ссылая её на ParamKCB()
.
mkstdctl(,FASTADC_CTL_COMMONS,,)
.
Дальнейшие действия:
XhAdjustPreferredSizeInForm()
.
Видимо, надо переходить от всегда-определенных min_val/max_val к per-line-диапазонам, высчитываемым программой-юзером по текущему mes_info[].
У нас же сейчас -- xN полезен только для слабых сигналов...
23.03.2010: расширил возможности
line_num_base
: теперь при указании
FASTADC_LINE_BASE_LETTERS
(=-1) используются не цифры, а
буквы A, B, ... И имён по-линейных параметров командной строки также
касается. Причина -- у ADC200ME входы называются A и B. Ну и обычные
0 и 1 также осимволичил -- FASTADC_LINE_BASE_ZERO
и
FASTADC_LINE_BASE_ONE
За компанию "управильнил" параметр magnN: теперь это lookup, и
содержимое его таблицы magn_lkp[]
генерится at-run-time.
23.03.2010: публика также требует возможности сжимать экран и по горизонтали -- т.е., X-аналог magn'а, но в ОБРАТНУЮ сторону.
23.03.2010: А еще публика требует весьма диких вещей -- возможности помнить 1000 последних измерений, и мочь показывать любое из запомненных. И вообще -- чтоб экранный осциллограф умел максимально столько же, сколько обычный Tektronix.
Вряд ли имеет смысл все кнопки для махинации с этими вещами помещать прямо в основное окно. Лучше сделать вызываемое по экранной кнопке не-модальное диалоговое окошко "Менеджер сохранённых осциллограмм".
24.03.2010: продолжение:
mes_mins[]
и
mes_maxs[]
, передаваемые ей параметрами, по текущему
mes_info[].
Параметры min_val/max_val оставлены -- они используются для расчета полей под подписи, плюс, если info2ranges==NULL (как будет для ADC4 и нынешнего ADC200/NADC200), то и для самостоятельного заполнения mes_mins[]/mes_maxs[].
datum2dsp
-- она
пересчитывает указанное значение (обычно микровольты) для указанной
линии и при указанном info[] в отображаемое на экране число. В
adc200me оно просто делит на 1000000., а в nadc200 -- требуется уже
реально хитрый пересчет с учетом диапазона и сдвига нуля.
dpyfmt
, интеллектуально модифицируемый для
каждой линии с учетом её zero и coeff.
25.03.2010: nadc200.c доделан!
25.03.2010: а-а-а!!! Идиот!!! Фтопку все FASTADC_LINE_BASE_xxx -- надо просто передавать "базовый символ"!
Сделал. Кстати, это даёт вообще полную свободу -- при надобности можно будет использовать и заглавные буквы, и маленькие.
29.03.2010: насчет изменения масштаба: эта публика вообще хочет мочь просто нарисовать мышью на экране рамку-прямоугольник, и чтоб именно этот кусок и стал во всё окно.
С одной стороны -- да, классика, именно так вообще-то обычно и делается, поскольку это самое удобное. С другой же -- ч-черт, а как это реализовывать? Совсем ведь другая схема отрисовки/скроллинга будет..
Несколько замечаний по теме:
30.03.2010: дополнение во все нынешние
fastadc_common-клиенты: при отображении времени надо учитывать PTSOFS,
прибавляя его к номеру отсчета. Что и сделано -- добавление
производится в *adc*_n2xs()
.
31.03.2010: очень разумная мысль от Роговского:
сделай фон клиентов чуть отличающимся друг от друга, ну или в уголке клиентика логотипчик или название АЦП, то вчера чуток попутал их после ночной смены в особенности :)
Поскольку понаподбирать для ВСЕЙ полудюжины/десятка разные цвета -- совсем опухнешь, то надо действительно где-то помещать название АЦП большим шрифтом...
01.04.2010: реализовано -- на тулбар добавлена
кнопка-метка с именем типа (из параметра type_name
).
01.04.2010: за последние несколько дней сделаны и/или доделаны ВСЕ клиенты: adc200me, nadc200, nadc502, nadc333, nadc4, adc812me. "Базовыми" являются adc200me (для 2-канальных АЦП) и nadc333 (для 4-/8-канальных).
08.04.2010: за прошедшую неделю сделано много-много -- в основном с подачи Роговского:
RunFastADC()
введён параметр
param_phys_rs
-- указатель на int-массив штучек,
аналогичных phys_r
у обычных каналов. Если N-й элемент
!=0, то для N-го параметра получаемое от сервера значение перед
отображением делится на это число.
Понадобилось для того, чтоб параметры WAITTIME отображать не в МИКРОсекундах (как их понимают драйверы), а в МИЛЛИсекундах.
info2exttim
,
возвращаущая, не включено ли внешнее таймирование.
При внешнем таймировании единицы времени нигде не подписываются.
Зато теперь у ADC8-12 значатся каналы 0A, 0B, 1A, ...
31.05.2010: давно мешает то, что из GUI-клиентов (вследствие API больших каналов) проблемы с "залипанием" измерений:
И вот, в беседах с Роговским, родилась мысль: а что, если дополнительным параметром указывать программе еще и ссылки на обычные каналы SHOT и STOP? А потом -- мысль еще получше: указывать этим дополнительным параметром номер базового ОБЫЧНОГО канала для данного устройства. Тогда там можно будет любую функциональность добавить.
Конкретно функциональность такая:
Суммируя вышесказанное -- можно высылать STOP=1 при любом касании кнопки ISTART.
31.05.2010: и еще: ведь надо бы как-то и rflags осциллограммы отображать. Например -- по образу строчки State из "Свойств ручки". Или, с учетом наиболее частых ситуаций -- при state==COLALARM_HWERR проверять, что если по CDR_FLAG_HWERR_MASK выставлен только CXRF_IO_TIMEOUT, то ставить строчку "Timeout", а по CXRF_OVERLOAD -- "Overflow".
08.09.2010: по факту -- а ведь
реализованный API "fastadc_common" УЖЕ почти позволяет делать
"объекты-АЦП": фактически достаточно в конкретной программе иметь СВОЮ
реализацию API mkstdctl и mkparknob, и можно вызывать
*adc*_mkctls()
.
Идея возникла при добавлении поддержки adc200me в liuclient, и для того, чтобы это работало, нужно лишь пара косметических изменений:
#include
'ить
соответствующие *adc*.c по несколько сразу, не опасаясь
коллизий имён.
main()
'ами, да и чтоб на отсутствующий
RunFastADC()
не ругалось, все adc'шные main()'ы окружаются
условиями типа
#ifndef EMBEDDED_FASTADC int main... #endif /* EMBEDDED_FASTADC */
Переделал так все liu/xmclients/*adc*.c, стоявшие нетронутыми аж с 07-04-2010.
08.09.2010: собственно, напрашивается дальнейшее действие: поскольку в liuclient.c приходится вставлять копию бОльшей части кода из fastadc_common.c, переводя его с глобальных переменных на поля структуры, то -- а не сделать ли ОДНУ копию такого кода, чтобы ВСЕГДА работала со структурой, и была бы годна для использования как в *adc*, так и в embedded-варианте?
Т.е., сейчас делаем кусок кода для embedded-использования, а потом переходим на него же и в adc-случае.
P.S. Задачу сильно упрощает то, что всё это находится в одной директории liu/xmclients/ :-)
08.09.2010: Замечание: при таком переходе есть тонкость: теперь callback'ам нужен не 1 параметр (например, номер канала, или номер параметра), а ДВА -- добавляется еще "указатель на структуру". Упаковать pointer+int в один pointer -- нереально в принципе. Поэтому приходится переходить от прямой передачи нужного значения к передаче указателя на структуру такого вида:
typedef struct { void *p; int n; } paramrec_t;
Естественно -- структуры эти отводятся в "private structure".
10.09.2010: движемся по пути унификации. Все структуры объявляем в fastadc_common.h, а реализуем -- в liuclient.c. Довольно муторненько, но терпимо.
По результатом этой маеты очевидно, что надо ВСЕГДА даже одиночные/тестовые программы делать не на глобальных переменных, а на структурах, как бы "объектами" -- чтобы результат был встраивабелен в другие программы, а эти одиночные программы являлись бы лишь вариантом использования тех "объектов".
10.09.2010: да, произведена глубокая переделка API "fastadc_common". Главное изменение -- теперь вместо ОДНОЙ функции RunFastADC() с диким списком параметров есть ДВЕ:
RunFastADC()
, принимающая лишь минимально
необходимые параметры (argc/argv, def_app_name/def_app_class) плюс
указатель на "функцию-описыватель".
А вот последней передаётся указатель на структуру
fastadc_dscr_t
, которую надлежит начинить информацией.
Начинение же делается...
FillFastADCDscr()
-- принимающей ту тучу
параметров.
Это позволило заиспользовать в liuclient'е тот же самый код.
27.12.2007: вообще-то понятно, "каким" нажатием это делать -- надо перед номером репера (1, 2, 3) иметь еще и чекбокс. Автоматом включаемый при установке репера.
26.03.2010: поскольку в fastadc_common-версии оно уже сделано, то "done".
13.05.2012@Снежинск-каземат-11: вся активность отсюда переехала в lib/fastadc/, да и сами файлы xmclients/fastadc_common.[ch] удалены неделю-две назад, так что помечаем раздел как "obsolete".
P.S. некоторые вещи отсюда еще актуальны -- в основном, width<700.
Сделано... ("В де-етском саду-у-у скоро бу-у-удет Новый го-о-од..." :-)).
20.04.2009: Старостенко-то захотел иметь возможность просто как-то щелкнуть на канале -- и чтоб он попал на график, как-то еще щелкнуть на другом -- и чтоб он попал на другую ось.
Понятно, что упихивать такую функциональность в обычные программы -- будет уже жирновато, так что надо заводить отдельную утилитку. Которая и умела бы рисовать что угодно от чего угодно -- включая каналы от РАЗНЫХ подсистем.
Как это можно/нужно реализовать -- в принципе понятно:
12.06.2004:
Оказалось, что там в mkclient.sh оставался просто закомментированный старый код (который main(){...ChlRunSimpleApp(...);}), и, соответственно, незакомментированные старые #include.
Выкинул все, оставив только misc_macros.h и добавив
cxdata.h. Заодно пришлось в mkphysdb.sh вставить
#include "cda.h"
-- там-то оно актуально, из-за
cda_physinfodb_rec_t
.
groupings_database[]
.
Выкинуто нафиг.
Так что -- файл стерт.
Это понадобится для не-Chl-программ, которым, тем не менее, требуется "описание группировки" в стандартном формате (например -- будущий vacuum, с гистограммами).
Или, вынести генерацию SYSTEM_db.so-файлов в отдельный
.mk-файл, чтобы оно было пригодно для include
'нья
в другие Makefile'ы, типа оного для vacuum'а?
09.07.2004: даааа...
А может, ввести в SimpleClients.lst дополнительный опциональный параметр -- "на что делать симлинк"?
И -- тогда уж перейти на формат "app_name[;option=value{;option=value}]"? Т.е., нынешний префикс "*" превратится в опцию "global", суффикс "@server" -- в "srv=SERVER", а "куда делать симлинк" -- станет обозначаться как "prog=NNN". Обработка подобного в mkclient.sh проблемы не составит -- "awk -F';'" либо "sed -e's/;/ /g" и for по результату (в mkphysdb.sh -- не сложнее).
Только проблема будет -- все эти вещи передать в LocalRules.mk.
26.09.2004: сделал, хотя пока (на shell'е вместо perl'а) и в дегенеративном варианте -- по-прежнему с "*" и "@". Подробности см, в разделе "clientprog".
Итого, исходная цель -- "no binary" достигается при помощи ";clientprog=-".
#include "cx_types.h"
. И-за этого formula-программы
не могли пользоваться константами
CX_VALUE_{LIT,DISABLED}_MASK
.
10.12.2005: делов-то -- ну добавил :-).
PARAMS=`echo $DEFSERVER | awk -F';' '{print $2}'`
24.06.2009: поправил -- теперь оно не берет "2-е слово", а берет "всё после первой ';'":
PARAMS=` echo $DEFSERVER | sed -e 's/;/ /' | awk -F' ' '{print $2}'`
Но что самое смешное -- там УЖЕ присутствовал прототип правильного
выкусывания, в виде прогонки $PARAMS перед сплиттингом через хитроумную
конструкцию
sed -e 's/^[^;]*;//'
,
которая и должна была выкусить всё вплоть до первой ';'
включительно... А теперь она стала выкусывать первый из параметров --
так что пришлось её убрать.
12.07.2009: вылезла другая задница -- что при спецификации вида "cac208@;options=vertical" (т.е. -- при ПУСТОМ srvref) оно полностью проглатывает всё остальное.
Расследование показало, что дело как раз в появившейся полмесяца
назад конструкции, заканчивающейся на
| awk -F' ' '{print $2}'
-- проблема в том, что
"awk -F' '"
строки вида
" SOMETHING"
(т.е. -- с пустой строкой перед сепаратором)
воспринимает как состоящие из на-один-элемент-меньше. Это, похоже,
особый случай с пробелом.
Решил проблему, сменив сепаратор с пробела на '|'.
А вообще -- по-хорошему, поскольку парсинг-то у нас последовательный, надо бы стараться максимально отказываться от awk'а в пользу sed'a. И, возможно, лучше и нагляднее было бы вернуть ту былую конструкцию, что "...должна была выкусить всё вплоть до первой ';'". включительно
${SYSTEM}_physinfo_database
. При этом, в отличие
от '*', по-прежнему #include
'ится файл
DB/db_{SYSTEM}.h.
02.01.2010: понадобилось для floor/liucc -- где
разные элементы могут общаться с разными серверами (через "defserver="
в options
), а там ведь надо им и physinfo предоставлять.
И, кстати, -- этот вариант намного предпочтительнее, чем '*'; по-хорошему от неё вообще надо избавляться, а надлежало с самого начала делать именно по нынешнему сценарию.
02.02.2011@Снежинск-каземат-11: поскольку gcc-4.1.2@CentOS-5.2
на приведение (physprops_t *)
вякал
warning: dereferencing type-punned pointer will break strict-aliasing rules
то оно было заменено на тупое (void *)
.
07.04.2008: она будет состоять из нескольких закладок:
И можно бы стараться делать там reference-примеры использования ручек, для копирования при надобности в рабочие программы.
check_snprintf()
отсутствовал
__attribute__((format))
. Добавил.
С одной стороны -- хочется, поскольку "*_memcasecmp()" по статусу лезет именно в libmisc, а нужна она -- в libuseful, для psp. С другой стороны -- душа не лежит, уж шибко эти библиотеки разные по "классу"...
12.05.2007: нет, НЕ СТОИТ, и даже НЕЛЬЗЯ объединять эти две библиотеки -- они должны быть раздельными. Это стало ясно сегодня, по результатам вытаскивания cxscheduler.o из libuseful.a в отдельную библиотеку.
Повторю здесь резоны и критерии:
Посему -- "withdrawn".
14.05.2007: произвел обоснованное выше (необходимое для Xh_cxscheduler'а) переселение memcasecmp.c и timeval_utils.c из useful/ в misc/.
Но тем не менее -- оно сделало "что надо", просто выделив на текстовый виджет соответствующее дефолтное количество колонок -- в данном случае 5+strlen(units); и печатает -- "23C". А после тестов выяснилось, что и "-" не нужен -- даже и просто "%.0f" отлично работает.
Вот так правильно я тогда (пару лет назад) разработал
идеологию/модель работы dpyfmt
+misc_printffmt.c!
:-)
20.04.2005: не-е-ет!!! Так НЕправильно -- надо ж все-таки уметь УКАЗЫВАТЬ количество колонок, не полагаясь на его default. Сегодня это вылезло в phm-tsyline.c, где (как и в impacis10.c, бывшем причиной начала этого раздела) надо иметь в строке ДВА текстовых поля, с "красивым" выравниванием и с правильным прилеганием поля/полей "units".
29.04.2005: мда -- ну и как бы это делать? Ведь
в результате ParseDoubleFormat()
должен образоваться
вполне нормальный, годный для *printf()
формат. И, хотя
GetTextColumns()
ее и вызывает, но -- ничего такого
"специфического" там быть, увы, не может...
29.04.2005: О! А собственно -- РЕАЛЬНО ли нам нужна
такая хитрая фича? Или, может, достаточно в
Knobs_internals.c::SetTextString()
"хитро"
дописывать units -- предварительно убрав КОНЕЧНЫЕ пробелы
(предполагается формат с "-" -- типа "%-5.2f")?
04.05.2005: сделал по вышеприведенной идее --
вставил в SetTextString()
отсечение конечных пробелов
перед добавлением units. Все прекрасно работает.
Так что теперь можно преспокойно использовать форматы типа "%-5.2f", не опасаясь, что между числом и единицами появится пробел. И теперь НЕ надо извращаться с недосказанностями типа "%-.0f".
05.05.2005: неа, неа, неа!!! Все было не
так просто -- модифицировать лишь SetTextString()
совсем
НЕдостаточно!
Ведь dpyfmt используется еще в нескольких местах, и в части из них вылазят просто некрасивости, а в сохранении/восстановлении режимов -- так и просто все сломалось бы, поскольку там полагается на то, что сразу после числа будет идти сепаратор (","). Да и везде, где идет ВВОД числа -- код считывания также предполагает, что число заканчивается на '\0'.
Так что -- надо обрезать конечные пробелы ВЕЗДЕ.
Вроде исправил. Что сделал:
SetTextString()
хвостовые пробелы теперь обрезаются
ВСЕГДА, а не только перед дописыванием units.
fprintf_f_rtrim()
, пишущая
во внутренний буфер, обрезающая хвостовые пробелы, а потом буфер -- в
указанный fp
. По параметрам совместима с просто
fprintf()
'ом одного числа.
Ltsv()
,
делающая left-trim. Вот в нее вставил за компанию и right-trim. А в
единственном оставшемся месте -- "ручное" отсечение.
Вообще-то кое-где там стоИт
p=buf+snprintf()...
, что может вылезти боком.
29.04.2005: думаю -- не стоит. Ведь в случае,
если такой формат пройдет мимо Cdr, то он останется целочисленным, а
применен будет -- к double
-значению, что сходу будет
ошибкой. Так что -- неа, и раздел помечаем как "done" и <S>.
OUT_OF_RANGE()
, который
использовался только в
{oldcx,curcx}/server/cx-server_link_new.c.
09.06.2005: Поскольку оно уж три года как вышло из употребления, да и корректность (с точки зрения возможных side-effects -- см. "info gcc 'c extensions' 'naming types'") сомнительна, то я этот макрос грохнул.
SHOULD_RESTART_SYSCALL()
, иметь и макрос, определяющий
фатальность/временность ошибки от connect()
'а.
09.06.2005: сделал --
IS_A_TEMPORARY_CONNECT_ERROR()
, и перевел на него
cda.c::cda_new_server()
и
cm5307_drv.c::InitiateStart()
.
Стоит подчеркнуть: этот макрос проверяет на временные ошибки
именно только от connect()
'а, он НЕ рассматривает
общесистемные "временные" проблемы типа EAGAIN
.
22.06.2005: для комплекту добавил в число
временных ошибок ENETDOWN
.
14.06.2010: ага -- а собственно
ETIMEDOUT
в списке отсутствовала! Из-за чего она
считалась "фатальной".
Сегодня наткнулся на это при тестировании
timed_connect()
-based cxlib'а. А раньше оно не вылазило
потому, что отсутствующий хост -- он через некоторое время (3 ARP'а?)
даёт EHOSTUNREACH.
Добавлена.
14.06.2010: отдельный вопрос -- а не добавить ли
к этому списку еще и EHOSTDOWN
? (И что это вообще за
код?)
10.03.2020: в список проверяемых ошибок первой
добавлены также EAGAIN
/EWOULDBLOCK
-- в виде
ERRNO_WOULDBLOCK()()
.
Смысл:
bridge_init()
-- забывало делать
++
при разборе @-префикса).
cx_open()
.
Всё потому, что в проверке -- IsATemporaryCxError()
--
используется IS_A_TEMPORARY_CONNECT_ERROR()
, НЕ считавшая
EAGAIN/EWOULDBLOCK временной ошибкой.
После добавления всё исправилось -- теперь оно говорит "will reconnect".
fd_set
'ов, с
точки зрения производительности, security и портабельности на Win32:
похоже, надо будет слегка под-усложнить эту подсистему и, возможно,
даже частично вынести в отдельный файл, и кабы не унести из libmisc в
libuseful. Детали -- в комментарии.
10.06.2005: по порядку:
maxSET
иметь еще и minSET
-- чтобы не тратить в циклах переборки/поиска время на точно не
относящиеся к делу дескрипторы. (Это особо актуально для
Win32.)
fd_set
устроен совсем по-другому. Так что,
видимо, под Win32 наши GENERIC_FD_NNN
должны выглядеть
весьма иным образом (в частности, поиск максимального/минимального).
fd_set
'у"
должны выглядеть совсем другим образом -- это ж просто тупые циклы
переборки массива, а то итераторы в POSIX'ном стиле будут занимать
вместо n аж n^2 времени!
(При введении таких итераторов, впрочем, надобность в
maxSET
(а особенно -- в minSET
) становится
весьма невеликой.)
fd_set
устроен совсем
по-другому", то:
fd_set
" НЕ делается при помощи
"fd<FD_SETSIZE
", и должна выполняться ПРИНЦИПИАЛЬНО
ПО-ДРУГОМУ -- причем, в задачу начинает входить еще и сам "целевой"
fd_set
...
(А ведь в задачу может входить и не один fd_set
--
тогда вообще зад... Или не может?)
В общем, сию проверку надо, как минимум, вынести в отдельный макрос.
fd_set
'ов -- FD_ADD(src,dst)
, делающая просто
побитовый OR.
С другой стороны -- оно сейчас используется скорее оттого, что нынешние cxd+cx-server+cx-porter НЕ используют cxscheduler+fdiolib, а в будущем cxsd такая функция станет ненужна. Так что на эту проблему можно бы и забить.
11.05.2007: да, надо забить, и НИЧЕГО с этой "подсистемой" делать не надо -- вот почему:
CheckFD()
, но она по смыслу может быть перенесена в
cxscheduler, где и будет специфична для платформы.
Так что -- "забиваем", помечая как "withdrawn".
Реально оная функциональность есть в cx-starter'е --
curtime()
, но хотца иметь такое же и в других программах.
23.01.2006: что ж -- она перенесена в
misc_macros.h под названием strcurtime()
, для
чего туда добавлено также #include<time.h>
.
И начал начинять этим: cda.c -- море мест, cxlib.c
-- reporterror()
и cx_perror()
,
cx-console(), adc333.c, ...
({istc,pult}/xmclients/).
Но -- абсолютной полноты покрытия пока нет: в частности, сообщения
для XhMakeMessage()
никак не помечаются -- хотя,
вообще-то, они все равно падают в event-log с пометкой о времени.
А вообще-то надо разработать единые правила -- как печатать диагностические сообщения на stderr -- "FILE::FUNC: TIME: " либо "FILE::FUNC (TIME): ", как сейчас, или как?
Чуть позже: стало очевидно, что наиболее разумно -- выдавать пометку времени ПРЕФИКСОМ, через ": ". Перевел все на такой вариант.
Кстати -- при этом населении возникала мысль "что нехило бы это все свалить на библиотечную функцию", НО: реально-то бОльшая часть таких мест -- это инициализация соединения с сервером, которая в CXv4 останется в одной-единственной точке (в "knobber'е"). А остальные сообщения -- можно будет поразмыслить :-).
24.01.2006: да, совсем забыл -- функция-то
static
, но не inline
-- так что ей необходим
__attribute__((unused))
, чтоб компилятор не ругался
"defined but not used".
А так -- "done".
30.01.2006: продолжение в ту же степь:
Так вот -- надо бы все-таки переходить на такой формат, тогда и о совместимости со всякими MathCAD/Matlab можно не волноваться.
(Вопрос только в human-readability строк с символом "T".)
В общем -- сделано:
static char *stroftime(time_t when, char *dts)
где параметром "dts
" передается строка-разделитель для
даты и времени (Date-Time Separator).
И теперь strcurtime()
сводится к вызову
stroftime
.
Но -- для читабельности -- strcurtime()
использует
"-" вместо "T".
XhLoadDlgShow()
.
На будущее:
"...%s.%03d",...tv_usec/1000
, а саму
stroftime()
сие соглашение никак не затрагивает.
03.01.2013: теперь, с введением
_msc
-вариантов, "не затрагивает" уже как раз клиентов --
они могут расслабиться и просто пользоваться готовым API модуля
misc_iso8601.
26.02.2006: оставалось некоторое количество мест,
все еще использовавших ctime()
; все переведены на
strcurtime()
либо stroftime()
:
ChlRecordEvent()
CmdWho()
-- там была выдача "LOGIN-TIME" в формате
команды who -- "Wkd DD HH:MM".
CmdClients()
-- аналогично.
CmdWall()
-- выдача была слизана с команды wall.
12.04.2006: наткнулся на еще один нормативный документ -- RFC-3339, "Date and Time on the Internet: Timestamps", предписывающий использовать подмножество ISO-8601, и явным образом позволяющий для читабельности использовать вместо "T", например, пробел.
05.09.2009: в продолжение унификации:
cx_perror()
создана совместимой с обычной
perror()
, то она НЕ получает имя программы, и программы
печатают свои названия сами, ДО того. В результате выходит уродство
вида
cx-rdt: 2009-09-05-17:32:13: cx_connect(): Connection refused
Так что введен cx_perror2()
, получающий также и
argv[0] (а обычный cx_perror()
-- вызывает его с
argv0:=NULL), и все utils/ переведены на него. И, кстати, сие
и были единственные юзеры данной функциональности.
А cx_perror()
вёрнута к своему старому виду, из
cx.20051231/, БЕЗ префикса времени -- для 100%-совместимости с
обычным perror()
.
И в 4cx/.../cx_util.c этот код также скопирован.
RunCommand()
забывал печатать двоеточие, а во-вторых, при старте печатал всё в
другом формате. Пофиксено.
16.09.2009: всё-таки перевел ВСЁ на печать времени БЕЗ ":" -- просто через пробел. Во-первых, оно там просто незачем, во-вторых, отнимает один символ, в-третьих, в /var/log/* после дат/времен всегда просто пробел. 4cx/ too.
03.01.2013: добавлена пара вызовов, выдающих еще
и миллисекунды -- stroftime_msc()
(принимает struct
timeval*
вместо time_t) и strcurtime_msc()
.
04.01.2013: все серверные/логгерные места
(включая cxlogger.c) переведены на
stroftime_msc()
, использование ctime()
из них
выкинуто и осталось теперь лишь в клиентах.
ParseDoubleFormat()
НЕ поддерживает флаг
апостроф -- "'" -- "группирователь тысяч".
Почему -- фиг его знает: что мешало сделать это весной 2003-го ()? Казалось бы, ничего сложного нет, ибо он и не интерферирует с другими флагами (как "+", и никак не влияет на длину... Просто лень было, что ли?!
А сейчас вот -- захотелось...
10.04.2006: вроде сделал.
FMT_GROUPTHNDS=1<<5
.
ParseDoubleFormat()
.
CdrCvtLogchannets2Knobs()
.
Так, а что у нас с размером knobinfo_t.dpyfmt
?
"Максимальный" формат -- "%#0+'20.10f", 11 символов плюс NUL,
так что в 16 все влезает.
А вот дальше... Вроде как все сделано, не ругается, но толку-то -- не нашлось НИ ОДНОЙ локали с символом-группирователем :-)
И, теоретически, имеется вопрос -- а поддерживается ли обратное
преобразование с символом-группирователем функцией
strtod()
? Судя по найденным исходникам -- вроде как да,
хотя в man-странице ничего не сказано.
Ну да ладно -- "done". (Да, в 4cx/ скопировано.)
countof()
иметь в misc_macros.h и
offsetof()
, а также макрос, дающий sizeof указанного поля
в указанном типе структуры -- sizeof_field()
.
20.01.2007: скопировал из
PSP_OFFSET_OF()
и PSP_SIZEOF()
. (У
paramstr_parser'а и fdiolib'а останутся свои определения -- чтобы они
были хоть формально независимы от cx/.)
(Хотя, конечно, и остается вопрос -- раз во всех библиотеках (включая Xt :) есть свои определения, то нафига нужны эти?)
11.02.2007: ...и обнаружилось, что макрос с точно таким же именем определяется еще в море мест, в частности -- в stddef.h. Оказалось даже, что оно стандартизовано в ISO и ANSI C (и поддерживается даже Мелкософтом). Так что кросс-компилятор gcc-3.2.2 под PowerPC вякает "warning: "offsetof" redefined".
В принципе -- несмертельно, поскольку все варианты этого макроса
будут делать одно и то же. Но от греха подальше этот #define теперь
окружен скобками #ifndef offsetof
/#endif
.
06.03.2007: "от греха" не спасает -- поскольку misc_macros.h обычно включается ДО прочих наших .h-файлов, то он и оказывается первым. А под OpenBSD-2.5 прут warning'и, что
Впрочем -- фиг с ними, делать/usr/include/stddef.h:63: warning: `offsetof' redefined
#include<stddef.h>
меня как-то не тянет.
SHOULD_RESTART_SYSCALL()
и
IS_A_TEMPORARY_CONNECT_ERROR()
в misc_macros.h
еще и ERRNO_WOULDBLOCK()
-- проверку на
EWOULDBLOCK/EAGAIN. Взял из fdiolib, а используется аналогичная
проверка также в tsycamlib.c и в connlib.c.
RESCALE_VALUE()
.
27.02.2007: она предназначена для отображения значения из одного диапазона в другой -- в первую очередь для пересчетов координат при отображении разнообразных графиков, но также и для нормализации картинки с tsycam и прочих манипуляций с пикселами (например, при переводе из grayscale в RGB через HSV).
Она определяется следующим образом:
#define RESCALE_VALUE(v, from_min, from_max, to_min, to_max) \ ( (v - (from_min)) * ((to_max) - (to_min)) / ((from_max) - (from_min)) + (to_min) )
Она является результатом тех "глубоких дум" о том, "как отображать", при разработке vacclient'а, а ранее -- и adc333 и istc/xmclients/Chl_histplot'а.
Ею можно маппировать значения даже на "обратные диапазоны" (т.е., когда min>max) -- например, для отображения на экране, когда ноль внизу.
Имеется только одна проблема/непонятность: почему-то присутствует некоторая нелинейность при отображении -- тики на осях стоят неравномерно, а с существенным разбросом (БОЛЬШЕ 1 пиксела), на графиках при увеличении по вертикали рельеф не монотонно проявляется, а "то есть, то нет"...
Возможно, дело в каких-то погрешностях округления -- например, где-то надо приводить что-то к double (хотя, вроде бы сначала умножение, а потом деление -- потерь быть не должно); но -- надо разбираться!
27.01.2008: вылезла некая очевидная "тонкость" с
использованием RESCALE_VALUE()
: при шибко крупных
from_*/to_* может произойти арифметическое переполнение, и будет
задница. В этих случаях надо делать принудительный type promotion --
например, nadc333 спасло изменение с
RESCALE_VALUE(mes_data[n][i] * magn[n], -8192000, +8192000, grf_h - 1, 0)
на
RESCALE_VALUE((int64)(mes_data[n][i]) * magn[n], -8192000, +8192000, grf_h - 1, 0)
fprintf(stderr,...)
-- непорядок!!! Надо переводить оное
на архитектуру "_lasterr"!
04.12.2008: да, сделал. Публичный API на эту
тему -- printffmt_lasterr()
.
05.12.2008: и в 4cx/ тоже портировал -- misc_printffmt.c просто скопировал, а в misclib.h, поскольку он в 4cx чуток отличается, просто добавил строчку.
ParseDoubleFormat()
, делается прямо в
Cdr.c::CdrCvtLogchannets2Knobs()
, и только там --
это ж неправильно!
13.12.2008: да, сделал новую функцию --
CreateDoubleFormat()
, и перетащил сборку туда. Теперь эту
функцию можно использовать и в других местах.
Как водится, и в 4cx/ также портировал; заодно адаптировал
и единственного пока пользователя -- DPYFMT_fparser()
.
Так что -- населяем misclib.a мелкими модулечками, содержащими эти функции, перенесенные из misc_macros.h.
22.01.2009: история растянулась на неделю, так что вот протокол:
16.01.2009: 1-й шаг, проделанный вчера и сегодня, включил создание модулей misc_fd_state.c и misc_fd_flags.c.
Собственно создание модулей и переделка исходников на их использование было несложной работой -- "тупые" поиск, удаление определений функций и, в нескольких случаях, замена ("CheckFD" на "check_fd_state"); плюс, добавление
#include "misclib.h"
; и еще в паре случаев -- добавление зависимости$(LIBMISC)
.Но главная-то задница была с кросс-компиляцией -- ведь cm5307/ppc_drvlets/ и (особенно!) cm5307/uclinux_drvlets/ были совершенно неприспособлены для использования каких-то CX'ных БИБЛИОТЕК -- они раньше брали из дерева только include-файлы. Вот с canbus/gw/ppc_canserver/ оказалось проще -- там уже использовались несколько модулечков из misc/ и useful/.
Так что -- в cm5307/* эта поддержка была скопирована, и вместе с ней был проведен изрядный рефакторинг связки Makefile, LocalRules.mk, ../common/CommonRules.mk. Основная часть этого рефакторинга свелась к "управильниванию" структуры, так что теперь Makefile'ы (в т.ч. внедеревные) выглядят проще.
Изменения (в т.ч.) Makefile'ов проделаны -- и проверены -- во всех современных директориях (work/): cx/, tmp/, qult/, ndbp/, staromakh/.
Замечание: "старый" код, из директорий pult/, cangw/, karpov/ (не говоря уж об ncx/) трогать не стал.
17.01.2009: также перенес в misclib.h и:
- Функции
n_read()
иn_write()
-- в misc_n_read.c и misc_n_write.c.- Функции
uintr_read()
иuintr_write()
-- поскольку они совсем мелкие, то пока оставил их какstatic inline
.22.01.2009: за прошедшие несколько дней внедрил в uclinux_drvlets/, ppc_drvlets/ и ppc_canserver/ еще более правильную структуру -- они теперь просто локально собирают у себя libmisc.a и (только ppc_canserver) libuseful.a+libcxscheduler.a, и линкуются с ними (кстати, в ppc_canserver'е cxscheduler.o по-прежнему считался Makefile'ом за часть libuseful). Для этого:
- Определения списков компонентов библиотек вынесены из их Makefile'ов в отдельные .mk-файлы, которые могут include'иться и кросс-директориями.
- Добавлены строчки
AR=
в .mk-файлы определений для кросс-компиляции (всё оказалось очень просто).- В tools/cm5307-ppc/include/ были добавлены syslog.h и sys/syslog.h -- чтобы cxlogger.c компилировался.
Так что теперь любые добавления в эти библиотеки будут автоматически становиться доступными и для содержимого кросс-директорий.
А в будущем, конечно, надо будет сделать отдельные модули-директории, в которых бы кросс-собирались стандартные библиотеки, чтоб они были доступны для разных директорий (в т.ч. внедереыных). И, видимо, эти кросс-библиотеки должны помещаться в exports/.
22.01.2009: также давно было пора стандартизовать штуку, с давних пор существовавшую под именами "CheckR()" и "IsReadError()". Что интересно, вариант "IsReadError()" намного элегантнее -- при той же функциональности.
Поскольку функциональность этой штуки близка ко всяким макросам-проверкам
errno
, то она помещена рядышком с ними в misc_macros.h как "static inline int CheckReadR()
" (поскольку она хоть вроде и "check r", но реально семантика -- с проверкой на r==0 -- касалась исключительноread()
и сородичей; и даже более того: эти функции всегда используются в одном сценарии, в чтении из сокета, в 2 экземплярах -- @header и @data; так что в CXv4, похоже, надобность в ней отпадет -- поскольку всё чтение из сокетов будет повешено на fdiolib).BTW, в 2 точках остались свои определения: cxlib.c::
CheckR()
и fdiolib.c::IsReadError()
-- там прямо внутри этих проверяльщиков имеется своя доп.обработка.22.01.2009: также перетащены
stroftime()
иstrcurtime()
-- в misc_iso8601.c, иGrowBuf()
-- в misc_growbuf.c.04.02.2009: еще тогда обнаружилась некоторая странность -- некоторые файлы компилировались при КАЖДОМ проходе make, независимо от собранности ранее.
Разборка показала, что это были несколько компонентов libmisc.a -- misc_fd_state.o, misc_fd_flags.o, timeval_utils.o. Как выяснилось дальше, проблема была в глючном ppc-linux-ar -- он конкретно эти файлы почему-то заносил в директорию .a-библиотеки неправильно, без расширения (но с точкой!). Соответственно, при следующем прогоне make обнаруживал их отсутствие в библиотеке, компилировал и вставлял в библиотеку заново.
Решил проблему просто -- закомментировал нафиг строчку
AR=...
в ppc860_rules.mk, так что оно теперь использует ar из хост-системы. Поскольку ar, вообще-то, сродни всяким zip'ам с rar'ами, то его платформозависимость очень небольшая -- она сводится лишь к манипуляции с индексом символов. А учитывая, что система та же самая, отличается только процессором -- можно было (наверное :-)) сразу такой ar и использовать.Кстати, всё равно ведь забыл в дополнение к
$(AR)
переопределять еще и$(RANLIB)
-- а оно работало.BTW, что интересно -- будучи запущенными без параметров, ar и ranlib пишут, что "ranlib: supported targets: elf32-i386 a.out-i386-linux efi-app-ia32 elf32-little elf32-big srec symbolsrec tekhex binary ihex trad-core", но всё равно с файлами для ppc860 работают нормально.
02.07.2009: стандартизована также
SleepBySelect()
, модуль misc_sleepbyselect.c. 4cx/ too.15.08.2020: прототип для misc_sleepbyselect.c вытащен из misclib.h в отдельный misc_sleepbyselect.h (и из .c'шника именно он теперь и #include'ится) -- чтоб модуль стал самодостаточным и годным для опубликования на GitHub'е и подобных.
...правда, зависимость от timeval_utils у него есть -- ради
timeval_add_usecs()
.
Засим -- вроде всё, "done"!
30.01.2009: и в 4cx/ тоже портировал.
13.02.2009: еще толстость -- ведь драйверы
(конкретно cm5307 и cangw) пользуются некоторыми функциями из
misclib'а, ранее бывшими макросами в misc_macros.h, а
cx-server -- нет, так что в бинарник они не попадали. И в результате
при подгрузке драйверов мы получали от dlopen()
'а ошибку
"undefined symbol: set_fd_flags".
Решилась проблема очень просто -- завел, прямо в
cxsd_driver.c (поскольку именно он и поставляет прочий ABI),
массивчик указателей следующего вида:
Поскольку массив НЕ-static, то компилятор с линкером его оставляют, а он тянет с собою и функции, на которые ссылается.void *public_funcs_list [] = { set_fd_flags, };
Замечание: по-хорошему надо бы как-то уметь объяснять ЛИНКЕРУ, что нам надо иметь для экспорта такой-то набор символов (составляющий ABI), а остальные -- не экспортировать вовсе. Но, почитав на тему "dlopen() & Co." по ссылкам из bigfile.html за 21-06-2003, понял, что никакого стандартного и портабельного способа для этого нету. Т.е., gcc+GNU_ld это умеют, но на уровне __attribute__ и спецключиков линкера, а даже в стандарте C99 ничего внятного на эту тему нету... (И, кстати -- другие пользуются тем же приемчиком, таблица с указателями на нужные функции.)
13.02.2009: кстати, еще при первом прогоне под
OpenBSD-4.2 пару недель назад оказалось, что ТАМ dlopen()
никакой ошибки не даёт, он возвращает какой-то handle, а на stderr
пишет
sbin/cx-server:lib/server/drivers/cm5307_drv.so: undefined symbol 'set_fd_flags'
Это, мягко говоря, странновато.
Сегодня же, погуглив, обнаружил, что это известный баг в OpenBSD-4.2, вроде как поправленный еще 27-11-2007 -- 5579/system "dlopen and RTLD_NOW strange behaviour ".
18.02.2009: проблем как бы 2:
if (ParseDoubleFormat(GetTextFormat(...
а ведь GetTextFormat()
вполне может при ошибке вернуть
NULL, и будет SIGSEGV (проверено!).
GetTextColumns()
стоял непроверяемый вызов
ParseDoubleFormat()
, вполне могущий и обломиться.
Вторая проблема скорее умозрительна, поскольку
GetTextColumns()
ВСЕГДА применяется к строке, прошедшей
через
ParseDoubleFormat()
+CreateDoubleFormat()
.
Первая же посущественней.
Посему было проделано следующее:
ParseDoubleFormat()
вставлена проверка на
fmt==NULL
. При этом _printffmt_lasterr_str
НЕ трогается, т.к. предполагается, что там уже имеется сообщение от
GetTextFormat()
.
return -1
" по ошибке оно
уставляет ret=-1
и делает goto
в конец, так
что результаты ВСЕГДА возвращаются.
Плюс, добавлена также и инициализация conv_c='f'
.
GetTextColumns()
же теперь ничего не надо делать,
поскольку ParseDoubleFormat()
нынче ГАРАНТИРОВАННО вернет
разумные результаты.
19.02.2009: так что -- заменил на '='.
Конечно, "стандартные форматы" за прошедшее с 2003г. время так и не получили вообще никакого распространения, но мало ли -- вдруг понадобятся. И если вдруг '=' чем-то не устроит, всегда можно будет поменять.
22.02.2009: смысл его появления -- предоставлять
функцию set_signal()
, являющуюся аналогом
signal()
, но функционирующим одинаково на всех платформах,
BSD и SysV, без этих дурацких отличий по самосбросу обработчика и т.п.
Сделано оно, конечно, на основе sigaction()
, а точнее
-- взято почти один-в-один из cxdlib.c. Отличия от обычного
signal()
:
sighandler_t
.
sa_flags
-- оно само
разбирается с помощью OPTION_SA_FLAGS
.
Оно сразу же скопировано и в 4cx/, и на него переведены все пользователи -- cxlib.c, cxdlib.c, daemon/cxsd.c, canserver.c, 4cx/.../cxsd.c, cx-starter.c. Драйверы для CM5307* пока не трогаем.
08.09.2009: нужен он скорее для 4cx/, но поскольку библиотека у нас в обеих ветках синхронизована, то пишем сюда.
Итак: назначение -- парсинг/разбивка URL'ов вида [СХЕМА:]ПУТЬ на
компоненты. Реализуется сие действие функцией
split_url()
, позволяющей:
Оно пропускает начальные isalnum()
'ы, а потом смотрит,
идет ли сразу в этой позиции строка-разделитель.
11.09.2009: проверил -- вроде работает, так что "done".
18.04.2013: отсутствовала проверка на def_scheme==NULL. Добавлена.
05.09.2019: исправляем идеологический косяк, выражающийся в том, что возвращалось имя схемы с оставлением того регистра букв, что был в url'е.
Для этого memcpy()
заменено на цикл с по-символьным
копированием через tolower()
.
Кстати, в старом варианте был косяк: оно копировало количество прямо
"p - url
"
(т.е., сколько в url'е), а не
"scheme_buf_size - 1
",
как положено.
08.09.2009: нужно это, например, для менеджмента массивов соединений/серверов и event-proc'ев в cda и cxlib (речь скорее про 4cx/). В cda.c эти махинации делаются вручную, что неудобно.
Так что добавлена функция GrowUnitsBuf()
, которой
передаются размеры не в байтах, а в "единицах", плюс
unit_size
.
07.02.2012@Снежинск-каземат-11: ну сделана она, а толку -- пока что не используется.
25.06.2013: а скорее всего и не будет
использоваться -- по причине тотального перехода на SLOTARRAY
(опубликованного как раз в тот же день 07-02-2012, когда
сделана предыдущая запись), который сам решает эти вопросы,
а пользуется safe_realloc()
'ом напрямую.
08.04.2010: сделано по проекту от 14.05.2009.
Данные держатся в объектах типа fps_ctr_t
, и
предоставляется следующий API:
fps_init()
fps_beat()
fps_frme()
fps_dfps()
07.02.2012@Снежинск-каземат-11: оно было сделано в
fastadc_data.c еще 25-04-2011 под именем
DEFINE_SLOT_FUNCS()
, откуда и забираем, переименовав в
GENERIC_SLOTARRAY_DEFINE()
и добавив мелкие детали. Прочие
детали:
GENERIC_FD_*
.
GENERIC_SLOTARRAY_DECLARE()
-- для возможности объявлять
функции в .h-файлах.
FindFreeZZZSlot()
и
GetZZZSlot()
; а вот RlsZZZSlot()
-- уже забота
конкретного использователя.
Причина -- в Rls-функциях обычно бывает куча всякой подчистки ресурсов, что в стандартные макро-генеримости никак не впихнуть.
bzero()
выдаваемого элемента и УСТАВЛЯЕТ
in_use_field=in_use_val_used. А вот уставление остальных "начальных"
значений -- забота клиента.
И fastadc_data.c вместе с fastadc_gui.c уже переведены на него.
08.02.2012@Снежинск-каземат-11: делаем дальше -- в соответствии с конкретикой из раздела "О работе с памятью" за сегодня.
GENERIC_SLOTARRAY_DECLARE_GROWING()
.
Параметр "obj_arg" идёт не в начале, а в конце -- чтобы избежать сложностей с запятой в списке параметров (которая для статичиских должна отсутствовать, а в объектах с полями -- присутствовать), и воспользоваться gcc/C99-фичей "swallow the comma preceding ##".
GENERIC_SLOTARRAY_DEFINE_FIXED()
--
копированием и "удалением лишнего".
GENERIC_SLOTARRAY_DEFINE_GROWFIXELEM()
-- также
изначально копированием, а затем
FindFreeZZZSlot()
.
29.01.2025: был ещё один косяк, оставшийся при
копировании: неверное приведение типа --
(slot_type *)safe_realloc(...)
вместо
(slot_type **)safe_realloc(...)
; некритично, ошибочного кода не
генерило (ибо в C++ не использовалось), но warning при компиляции выдавался.
14.05.2012@Снежинск-каземат-11: ЗАМЕЧАНИЕ: а
MAX_COUNT
пока никак не влияет на рост!
13.08.2012: при использовании вне объектов
(static) вылезла проблема: пустой obj_arg
приводил к
warning'у "function declaration isn't a prototype". Угу -- ему
там требуется указывать не ()
, а (void)
(клятое наследие 1970-х).
Но при указании последним параметром void
вылезла уже
ошибка, в Access*Slot() -- "`void' in parameter list must be the
entire list".
Короче -- пришлось один obj_arg
разбить на пару
single_obj_arg
и last_obj_arg
. Для
в-объектовых приходится писать дважды одно и то же, для вне-объектовых
-- "void,
".
09.10.2012: замечание: для вне-объектовых надо в
конце указывать просто "void
" -- БЕЗ ",
".
Иначе только gcc-2.96@RH-7.3 это переваривает и удаляет запятую (а
странно -- не должен бы, "This does *not* happen if you pass an empty
argument" -- info cpp), а gcc-4.1.2@SL-5.8 ругается
error: expected declaration specifiers or '...' before ')' token
14.08.2012: еще несколько доработок:
ForeachZZZSlot()
.
Ему передаётся функция-сравнивальщик checker и privptr, который надо ей
давать.
Вызывается checker для каждого занятого элемента, и когда он вернёт !=0, то перебор заканчивается. Индекс "найденного" элемента не возвращается, но checker может это сделать через privptr.
Насколько сей функционал будет полезен -- хбз, попробуем его использовать в будущих применениях.
22.10.2012@Снежинск-каземат-11: по опыту использования -- 4cx/'ные cda_api.c и cxsd_driver.c -- всё-таки переделано: теперь Foreach...() при НЕнайденности возвращает -1 (вместо былого 0), а при найденности -- индекс "найденного". Значение, вёрнутое checker'ом, теперь игнорируется (но его-то при надобности можно вернуть через privptr).
GENERIC_SLOTARRAY_DECLARE_NNN_FIELDS()
(в
соответствии с идеей от 27-02-2012 на тему "template").
Правда, инициализации там нету (_list=NULL, _list_allocd=0) -- это слишком use-dependent, так что полагаемся на нуление юзером.
GENERIC_SLOTARRAY_DECLARE()
добавлено
__attribute__((unused))
для потенциально неиспользуемых
"методов", чтоб warning'ов поуменьшить. Впрочем, оно пока тоже нигде
не используется.
22.11.2015: LATINITSA pervyj raz zaispol'zoval
GENERIC_SLOTARRAY_DECLARE()
, v 4cx'nom
programs/runner/cx-starter_Cdr.h, i pervyj raz storage!=static. I
srazy vylez kosyak: v zagolovke makrosa stotalo "lastobj_arg" vmesto
"last_obj_arg". Zaodno dobavlena deklaratsiya RlsZZZSlot()
.
15.08.2012: кстати -- а ведь некорректно делать DestroyZZZSlotArray(), предварительно не RlsZZZSlot() каждому неюзанному элементу. Посему, если всё-таки где-то начнём использовать Destroy, то надо будет сделать следующее:
19.10.2012@Снежинск-каземат-11: впервые заиспользован
GENERIC_SLOTARRAY_DEFINE_GROWFIXELEM
-- в cda.c.
Определения были жестко недоделаны -- несколько ошибок (в т.ч. просто лишний
if() перед другим if() -- при копировании не был стёрт). Исправлено, и
вроде работает.
Считаем раздел "done", а мелочи (типа MAX_COUNT) дочешем по мере надобности.
19.12.2012: замечание: надо ввести правило «MAX_COUNT==0 означает "не лимитировать"» (касается только GROW*-вариантов, конечно).
19.12.2012: пока локальное мелкое добавление -- в GROWING'ов Destroy##cname##SlotArray() добавлено обнуление _list=NULL и _list_allocd=0.
01.08.2014: крупная халтура: при до-аллокировании
свежесозданным НЕ делается in_use_field=in_use_val_unused
.
Работало всё лишь потому, что делалось bzero() и unused-значения всегда
были ==0.
06.08.2014: добавлено забитие in_use_val_unused,
помогло (да, проверялось на GetNthSidSlot()
-- до
исправления глючило).
Замечание: это касается только варианта GROWING, в GROWFIXELEM'е оно неактуально, поскольку там при до-аллокировании предварительным признаком первичной неиспользованности является NULL в качестве указателя на ячейку.
Зато обнаружился другой ляп: оно размером ячейки для аллокации
считало sizeof(slot_type)
вместо положенного
sizeof(slot_type*)
. Работало без проблем -- аллокировало
всегда с избытком (проблемы б были при
sizeof(slot_type)<sizeof(void*)
, но единственный юзер у
нас cda). Но кривизна. Исправлено, и теперь надо
последить, что ничего не сломается.
22.10.2014: хотел было "управильнить", чтобы Destroy*() вызывали Rls*() для каждого элемента (15-08-2012). Фиг, не так всё просто: там же нужно уметь вызывть в двух вариантах -- Rls*(id) и Rls*(id,obj_ref); а КАК это сделать -- неясно, поскольку obj_ref НЕпоследний параметр макроса, и "swallow" перед ним запятой так просто не получится.
28.01.2015: опять долго думал на эту тему, так пока ничего не надумалось. Единственное -- что можно б разнести макросы для отдельных массивов (БЕЗ obj_ref'ов) и вложенных в другие объекты (С obj_ref'ами). Но это мерзковато -- еще вдвое увеличится дублирование кода.
04.02.2015: ну или еще паллиативный вариант: ввести раздельные только деклараторы спец.функций "RlsAll*Slots()", которые бы просто вызывали Rls для всех элементов (хотя ничто не запрещает также и Destroy потом вызывать).
26.07.2019: мелкое добавление, понадобившееся для
работоспособности SLOTARRAY'ев в C++: результат safe_realloc()
в Get##cname##Slot()
явно кастится к slot_type*
--
в противном случае была ошибка "invalid cast".
snprintf_dbl_trim()
-- по результатам вчерашнего исправления бага в Xh_plot.c с
отображением вещественных чисел через буфер.
15.05.2014: показание -- что во всех таких местах делалось примерно одно и то же:
Так что создана такая функция.
#if
с check_snprintf()
.
ltrim
" -- надо ли
делать "3-й шаг".
21.05.2014@Снежинск-каземат-11: на неё, помимо Xh_plot.c тогда, сейчас переведены еще:
Ltsv()
("LeftTrimmedSnprintfV", теперь уволенной).
fprintf_f_rtrim()
-- почти совсем
опустела.
put_trimmed_val()
-- больше на
будущее, для следующего поколения vacclient'а.
*SetTextString()
пока НЕ тронуты. 22.05.2014: тронуты.
vsprintf()
превращены в vsnprintf()
.
Замечание: старый код в тех местах зачастую
за-#if
'лен, потом его надо будет совсем удалить.
22.05.2014@Снежинск-каземат-11: продолжение/окончание:
SetTextString()
и
MotifKnobs_internals.c::MotifKnobs_SetTextString
. Но
БЕЗ snprintf_dbl_trim()
-- в них свой код, в целях оптимизации
(функции очень часто вызываемые): избегаем лишнего strlen()'а, обходясь
результатом snprintf()'а/укорачивания.
DisplayCurVals()
/ShowCurVals()
, формирование
строки для val_dpy: там хоть уже был snprintf(), но ", COLV"
дописывалось некорректно.
ВОПРОС: а почему переменная, отвечающая за оставшееся в буфере место
(после числа) была тогда (она появилась в
Xh_plot.c::ShowMouseStats()
15-05-2014)
названа frsize
? Ну, "r" -- rest; а "f"?
02.06.2014: чуть управильнено одно из сравнений -- теперь вместо
len < 0 || len > bufsize-1
сделано
len < 0 || (size_t)len > bufsize-1
для избавления от warning'а "comparison between signed and unsigned".
03.06.2014: всё работает, засим "case closed".
GetIntFormat()
.
Сделано в интересах CXv4, чтоб тамошний cdaclient более нативно обращался с целочисленными данными.
НЕ сделана поддержка флага "ll" для int64 -- это софтина-юзер должна организовывать сама.
26.04.2015: был якобы "ляпчик": умолчательным ставился формат "%5.0d" (по написанному в man 3 printf, раздел "The precision"), а, как выяснилось, precision=0 ставить не стоит -- при этом число 0 не печатается вовсе (по ощущениям -- баг в glibc, а то и в C99).
Заменено на просто "%5d" -- всё исправилось (хвала устройству
CreateDoubleFormat(), при precision<0 опускающему его полностью,
и умолчанию в ParseIntFormat()
-- precision=-1).
Работает на вид как надо, так что "done".
13.07.2017: а получится ли? Ведь, судя по man 3
printf
, оно выводит в формате "[-]0xh.hhhhp?", и до десятичной точки
всегда только ОДИН знак.
Сейчас вот проверил -- да, так и есть.
Причина-то понятна: это сделано для вывода данных без потерь при конверсии в десятичку (полезно при сериализации/десериализации). (И цифра перед ".", скорее всего, почти всегда будет "1": по определению, это нехранимая единица при нормализации.)
Но от понимания не легче -- очень уж была бы шестнадцатиричка полезна. Но это НЕ ТА шестнадцатиричка, и единственное, что остаётся -- "withdrawn"...
Потребовалось для Д16 -- там отличие даже в дальнем знаке значения в наносекундах (канал Vn) может приводить к отличию на единицу в значении канала аналоговой задержки (Bn).
04.02.2019: делаем. Собственно изменений всего 2, крохотных:
ParseDoubleFormat()
символы 'a' и 'A'
добавлены к списку валидных.
GetTextColumns()
добавлена эвристика угадывания ширины
при неуказанности.
Вся "эвристика" сводится к числу 24
-- это длина максимально
возможного числа -0x1.fffffffffffffp+1023.
Значение precision
тут в задачу не входит (да и угадать его
всё равно нетривиально).
05.02.2019: проверил на cdaclient -- вроде работает.
Но использовать этот формат в скринах вряд ли захочется -- для человека-то он абсолютно бессмыслен, а нужен именно для маршаллинга вещественных чисел юез потери точности.
Кстати, в
datatree.c::get_knob_items_count()
УЖЕ есть поддержка
формата %a; точнее, "понимание" его -- эта буква в спецификаторе
#FXXf классифицируется как LIST_FMT_FLOAT
.
Короче -- считаем за "done"; если потом что-то вылезет, будем разбираться.
logline()
: в строчках, включающих клиента, передавать "объект-клиент", чтобы в log падала запись "IP:username", а в случае драйверов -- ID девайса ("magicid:driverid"), причем сделать это все совместимым:
- промеж себя;
- с syslog'ом, чтоб была возможность исп. стандартные log analyser'ы.
- Насчет "[IP:user]" -- поискать подобные sub-addressing в других системах -- типа kernel.log, и постараться сделать так же.
- Если
logline()
=> в отдельную lib, то как делать там стандартизованный logging user'а и device'а? "void *auxlogfunc, void *privptr
"? При этомзаменяется наlogline(type, level, format, ...)
а обычные вызовыloglineX(type, level, auxlogger, auxptr, format, ...)
logline()
меняются на специфические типамаппирующиеся какloglineClient(subsys, level, int s, .....)
=>logline(subsys, level, ClientAuxLogger, (void *)s, .....)
Замечание: Насчет ID клиента: заложить туда возможность писать не только cx-, но и CORBA-, и иных клиентов.
06.11.2003: подумавши, решил, что "двоеточие" в "IP:username" и "magicid:driverid" стоит заменить, например, на восклицательный знак -- поскольку двоеточие по большей части имеет смысл "подсистема:".
10.11.2003: еще подумал -- да за каким же лешим
нужны эти "auxlogfunc", "privptr", etc?! Осел!!! Ведь все намного
проще -- просто loglineClient()
etc. будут сами
формировать эту доп. инфу, а у "реальной" функции (loglineX()
?)
будет доп. параметр -- const char *subaddress
, который
она, если он non-empty, будет печатать после "cxsd#N" и перед
собственно текстом.
10.11.2003: как, собственно, надо делать этот
"универсальный логгер": при инициализации -- initlog()
--
ему передается таблица структур вида
{char *name; int mask}
плюс туда же идет имя "cxsd#n"
(отдаваемое также
openlog()
'у), verbosity level
(option_verbosity
), "консольность"
(option_dontdaemonize
).
Плюс есть функция типа "addlogfile()
", которой
передается список категорий и имя файла.
11.11.2003: кстати, насчет "консольности", точнее
вываливания сообщений на консоль: что-то гложут меня сомнения, что это
(указание на необходимость дублировать в stderr) стоит делать в конфиге
-- скорее надо завести флаг LOGF_CONS
?
14.11.2003: породил cxlogger.[ch].
Префикс всех имен -- ll_
(LogLib).
18.11.2003: довел до некоторого состояния
"наполненности", но пока она еще неюзабельна (поэтому в cxsd сейчас
делается просто vfprintf(stderr,format,ap)
).
Почему пока неюзабельно -- какие проблемы:
ll_addfile()
пока пуста. А точнее -- я пока не вполне
уверен, как же именно ее реализовать. И частично это зависит от
следующей проблемы.
ll_vlogline()
помещает уже тронутые текущим сообщением
дескрипторы в fd_set
, и затем проверяет их наличие.
НО! Если мы захотим-таки в будущем реализовать по образу и подобию Apache "HIGH_SLACK_LINE", то логгерные дескрипторы-то как раз лучше выносить ЗА пределы, поддерживаемые fd_set'ом/select()'ом, так что наш способ проверки перестанет работать.
Появились у меня кой-какие мысли, как сделать проверку безо всяких fd_set'ов (идея связана не то с выставлением флагов в маске, не то с упорядочиванием флагов файлов...), но реализовать их пока не успел -- отвлекло другое.
А что уже сделано хорошо: защита в ll_vlogline()
от
переполнения буфера строки -- там используются
snprintf()
/snprintf()
, и постоянно
"поддерживается" знание текущей длины. Это важно, поскольку библиотека
теперь годна не только для cx-сервера, а другие программы не смогут так
"железно" обеспечить непереполнение (да и он-то ничего не гарантировал
:-).
Тогда это было сделано из каких-то умозрительных соображений (уж точно не вспомнить), но оно и разумным сейчас не выглядит, и в реализации весьма неудобно.
06.11.2013: так что переделываем.
int
category
(да, с ней МОЖЕТ or'иться LOGF_ERRNO).
ll_vlogline()
,
касающуюся распределения сообщения по файлАМ.
ll_init()
указываются пара массивов:
categories[]
((.name==NULL)-terminated) -- список
категорий, каждая из которых ссылается полем fref
на
строчку из массива...
files[]
((.path==NULL)-terminated) -- список
файлов для логгинга.
Такая структура позволяет привязывать несколько категорий к одному файлу.
ll_reopen()
делает открытие -- и любое, и
ПЕРЕоткрытие (для возможности logrotate). Она получает доп.параметр
"initial", чтоб ругнуться при проблеме во время первого открытия.
ll_access()
производит "доступ" с чтением.
Скопировано из старосерверовой accesslogs(), с дополнительной проверкой
на можность чтения.
LOG_LOCAL7
. Правда,
пока вместе с собственным префиксом "DATE TIME HOST". И в RH-7.3
почему-то опция LOG_PID не отрабатывается...
cxsd_log_setf()
теперь вместо
почившей-в-бозе-никогда-не-наполнившись "ll_addfile()". Она получает
маску категорий (по 1 биту на категорию -- благо, их сейчас всего 5) и
имя файла.
И создаёт правильные ссылки (при изменении целевого файла правильно выполняет освобождение неиспользуемых).
cats_info[]
и file_info[]
(которыми и
жонглируется).
cxsd_log_init()
является добрым феем.
Немаппированные категории он сам маппирует на первую из указанных. В
случае отсутствия оной -- ничего, и тогда это отдаётся на откуп
cxlogger'у, который сбагрит syslog'у.
Кстати, бестолковое logmask_t
переименовано в
logtype_t
.
08.11.2013: потестировано, всё работает -- "done".
07.02.2014: а по-хорошему надо бы из
ll_reopen()
возвращать ошибку, и чтоб при старте -- т.е.,
из ll_init()
оно б вертало её выше, так что при ошибке
первоначального открытия не запускалось бы.
...прикол в том, что в ll_reopen()
даже параметр
initial
предусмотрен.
10.02.2014: сделано.
ll_reopen()
->ll_init()
при помощи
минус-номера категории -- -x-1
("-1" -- чтоб даже 0-я
давала отрицательное значение).
cxsd_log_init()
, возвращая
-1, а exit() делается самим кодом сервера (имеющим доступ к
normal_exit
).
initial
так и не понадобилось.
unconfigured
сообщение
сбагривается syslog()
'у только при
!is_on_console
.
Смысл -- чтобы простые программы, совчетающие GUI и libcxsd, не заморачивались с конфигурированием категорий логгинга, и тогда всё просто пойдёт на stderr.
Когда /var переполняется, то логи сервера начинают сыпаться на ВСЕ терминалы этой машины (включая ssh'ные и xterm'ы).
В результате и сделать-то что-то невозможно -- экраны зафлуживаются. Только вслепую набрать
echo -n >/var/tmp/4access.log
Проблема вылезала за последние месяцы пару раз на vepp4-pult6, а сегодня и на ring1.
10.02.2017: механизм, когда начинает использоваться syslog -- понятен:
syslog()
-- который и валится
всем невинным.
Технический же вопрос состоит в том, ПОЧЕМУ это валится на все терминалы, а не только в /var/log/messages?
LOG_LOCAL0
. С
чего вдруг оно валится всем -- неясно.
Анализ /etc/rsyslog.conf сходу ответа не даёт. Там "всех залогиненных" касается лишь строчка
*.emerg *
...которая к LOCAL0 вроде бы относиться не должна.
openlog()
указано
LOG_PID
в параметре facility
, а не в
option
-- это оно так с давних пор, еще с пре-v2'шного сервера.
И вот вопрос: не OR'ится ли это значение с указываемым
syslog()
'ом priority
? А ведь LOG_PID=0x01, что
равно LOG_ALERT
.
select()
, чтобы не страдать от проблемы дескрипторов
выше FD_SETSIZE
?") а нет ли проблемы, что в
ll_reopen()
при переоткрытии новый дескриптор будет другим (и
не факт, что новый не окажется за пределами FD_SETSIZE
)?
а?newfd=open(); if (oldfd<0) oldfd=newfd; else {dup2(newfd,oldfd); close(newfd);}
select()
не попадают, так что могут быть хоть выше 1000000.
Так что "withdrawn".
- А все-таки, как насчет strong authorization в CX? Сертификаты, SSL, etc., или просто забить на это, оставив для будущих access modules типа CORBA -- если потребуется, то пользоваться их access control'ом.
- Подумать-таки о .cxhosts -- надо:
- Все же начать использовать хоть текущую версию;
- Разработать более пристойный формат -- типа
allow|deny PROTO:addresses
- Вынести это вообще в отдельную lib -- типа
returns -1 if no file, 0 if denied, +1 if allowed.int IsAllowed(const char *file, ???)
- // Итого: libuseful+=cxlogger.o,cxnetacl.o
23.11.2003: сделаны файлы include/cxnetacl.h и libs/useful/cxnetacl.c. Интерфейс именно простейший --
int IsAllowed(const char *fref, unsigned int from)
Пока это заглушка -- она всегда возвращает 1, но оно уже вызывается из
cxsd_fe_cx.c::AcceptConnection()
.
30.01.2005: видимо -- как в socket API: т.е., указывать addrlen::addrbuf. А в текстовом файле -- "family/address" (unix/*, tcp/host, tcp6/host),
sl_XXX()
.
(У меня имеется устойчивая ассоциация: "sl" -- телефон Siemens SL55, уж дюже он необычный, посему прочно занял в мозгу это буквосочетание. Что ж -- сделаем его "талисманом" scheduler-библиотеки, с комментарием типа "Посвящается телефону Siemens SL55, по необычности дизайна достойному находиться рядом с картинами Сальвадора Дали" (или что-нибудь на тему "по духу близкое С.Д.").)
Так что -- берем cxsd_events.c плюс
cx-server.c::Run()
(вынув из последнего вещи,
специфичные для сервера), и делаем из них
cxscheduler.[ch].
Еще замечание: вообще-то в этом cxscheduler.[ch] не должно быть ничего CX-specific (а по возможности -- и на include/misc_*.h лучше б не ссылаться). В идеале надо было назвать не "cxscheduler", а типа "schedlib", но это может перекрываться с "библиотекой-интерфейсом к планировщику ядра". Как бы то ни было, эта lib'а должна быть полностью автономной.
24.11.2003: некоторое время назад родил эту библиотеку, и уже даже довел до вполне юзабельного состояния -- cxsd_{mainloop,fe_cx}.c функционируют на ее основе.
Хотя fdiolib пока никакая, заточки под нее уже есть. В частности, сильное отличие от имевшегося в server/cx-server.c планировщика -- можно попросить слушать любой дескриптор на ready-to-write (что полезно и для драйверов -- "timed connect"), а еще и на "exceptions" (это просто для полноты -- нам сие пока как-то не требуется).
30.01.2007: на cxscheduler'е уже не первый год работает прототип однопроцессного сервера, а теперь и canserver. Так что, наконец-то "done".
13.06.2005: да, поскольку cxscheduler именно "центральная" библиотека (ядро программы), то реально от нее и зависит производительность. И именно ТОЛЬКО на нее завязаны тонкости/особенности планировки.
Посему -- под Win32 содержимое cxscheduler'а должно быть просто ПОЛНОСТЬЮ другим, оптимизированным именно под эту платформу. Что, впрочем, несложно -- библиотечка-то совсем маленькая.
В связи с этим надлежит посмотреть, что такое "completion ports".
14.06.2005: угу -- Google дал первую же ссылку на MSDN: I/O Completion Ports; а еще чуть выше там описание вообще концепций ввода/вывода под Win32 -- Input and Output (I/O).
Кстати, API-функция GetQueuedCompletionStatus()
является весьма идеологически близким аналогом select()
'а,
отличаясь от него, впрочем, как японский язык от английского.
А есть еще "Alertable I/O", которое, вроде как, проще.
Последнее замечание -- оно работает ТОЛЬКО под NT>=3.5, а под Win9x -- похоже, нет.
11.05.2007: just for record, повтор в третий раз: внутреннее устройство cxscheduler'а под Форточки может быть один-в-один как у Xh_cxscheduler'а -- отличие только во взаимодействии с подстилающим OS-dependent API, а все потроха -- такие же.
P.S. Там в качестве "private pointer", похоже, можно использовать
параметр CompletionKey
.
P.P.S. Microsoft -- долбоебы, читать ихний долбаный MSDN Мозиллой почти невозможно, там все время лезут фреймы и циклические редиректы, так что приходится отыскивать во фрейме слева ссылочку "Completion ports", в ней менять "frame=true" на "frame=false", и тогда уж оно наконец-то даст посмотреть нормальную страницу.
Т.е., с точки зрения функциональности-то -- все окей, поскольку есть trec->privptr, но криво то, что fd- и timeout-callback'и выглядят сильно по-разному.
(А идет это из планировщика cx-server'а -- cxsd_events.[ch] -- там так было удобнее.)
02.05.2013 да, пофиксено еще почти полгода назад при переходе на cxscheduler2.
30.01.2007: да, сделал такие константы и перевел внутренности cxscheduler'а и fdiolib'а на них.
07.12.2007: ага, перевел fdiolib -- как же! Там оставались константы 1 и 2.
Исправил, и в 4cx/ тоже скопировал.
sl_timeout_t.when
сейчас заполняется программой. Уже
наткнулся на это -- в canserver_drvmgr'е забывал его заполнять, и оно
все время гнало предыдущий таймаут.
Надо вместо sl_enqueue_timeout()
делать
sl_enqueue_timeout_at()
-- с обязательным указанием
"когда".
21.01.2007: сделал -- теперь
sl_enqueue_timeout_at()
с параметром when
.
На примере daemon/cxsd_mainloop.c, который пришлось обновить,
четко видно, что это намного более натуральный вариант.
Так что -- надо вытаскивать его в отдельную .a, хоть и в той же директории. А по-хорошему и fdiolib тоже надо вытащить, чтобы в libuseful остались только "общеполезности", "расширения для POSIX", индифферентные к тулкиту.
12.05.2007: да, выселил cxscheduler.o в libcxscheduler.a. При этом создал в Rules.mk макрос LIBCXSCHEDULER, и добавил его в список зависимостей
ЗАМЕЧАНИЯ:
Видимо, правильным будет все-таки корректно разделить все компоненты библиотек по уровням, между которыми зависимости только в одну сторону -- в данном случае, надо унести timeval_utils в libmisc.
(BTW, модули в libmisc совершенно самодостаточны и уже ни от каких других не зависят -- таков же и timeval_utils, и, кстати, memcasecmp. Вот и обоснование для существования и libuseful, и libmisc, и критерий для определения, в какую же из библиотек какой модуль определить.)
А вот бОльшая проблема -- как будет выглядеть граф зависимостей касательно Xh_cxscheduler'а (ради которого это все и затеялось), и не будет ли там неразрешимого цикла.
Поскольку готово, то "done".
14.05.2007: угу, с Xh_cxscheduler'ом предсказанная проблема, естественно, возникла! Как точно я все предсказал! :-)
sl_set_select_behaviour()
. Она
позволяет указать функции-hook'и, должные быть вызванными ПЕРЕД и ПОСЛЕ
select()
'а.
23.04.2010: причины следующие:
select()
, перед которым делается
EnableSIGUSR1()
, а после -- DisableSIGUSR1()
.
Т.е., SIGUSR1 разрешается ровно на время select()'а.
И чтоб драйвлеты можно было перевести на cxscheduler -- необходима эта пара hook'ов.
Теперь же можно указывать этот интервал -- в микросекундах (более 2000s все равно не понадобится).
Возможно, при собственно переводе драйвлетов понадобится еще какой-нить лёгкий тюнинг -- посмотрим.
P.S. И, естественно -- этот механизм реализован только в "классическом" cxscheduler.c -- ни в Xh_cxscheduler, ни в Qcxscheduler оно никак.
P.P.S. Old cxscheduler.c -- 30.01.2007...
SL_CE=8
,
в интересах Xh_cxscheduler'а (основной же реализацией оно никак не
используется).
fdio_register_fd()
-- должны указываться только
"обязательные", "жизненно важные" параметры (hdr_size, endianness,
len_{offset,size}). А остальные (типа len_{addition,multiplier}) --
уставляться доп. вызовами.
Причина -- оно требовалось живое и рабочее для canserver'а, а попытка компилиться с базовой системой CXv3, но с fdiolib'ом из CXv4 обламывалась, поскольку include-файлы брались из "базовой" include/, и получался конфликт.
14.04.2004: ПРЕДЫСТОРИЯ: в istcc.c возникла необходимость прямо при живом графическом интерфейсе исполнять некий "последовательный алгоритм", причем длительный. Конкретно -- крутить колесо, останавливаясь каждые 1/6 оборота на очередном светофильтре и снимая картинку с камеры. Писать море callback'ов либо какие-то завернутые функции, проверяющие "текущее состояние", было напряжно. Так что пришла в голову мысль: сделать "автомат последовательного исполнения" команд.
ИДЕЯ: тот последовательный алгоритм можно рассматривать как набор шагов. Каждый шаг делится на 2 компонента: собственно действие (точнее, его начало) и "проверяльщика", которого можно вызвать в любой момент, а он чтобы отвечал -- завершено действие или нет. Если завершено -- переходим к следующему шагу. (Когда происходят события, потенциально могущие символизировать/сигнализировать завершение текущего шага, то их обработчики из клиентской программы вызывают метод "check", который и дергает "проверяльщика".)
Поскольку могут быть разные типы событий (завершение скачивания картинки с камеры, приход данных от CX-сервера, ...), то каждый проверяльщик снабжается "(тэгом типа) события", а метод "check" вызывает проверяльщика, только если переданный ему тип "этого" события совпадает с проверяльщиковым. Выделенный случай -- проверяльщикам с типом "0" передаются все события.
12.04.2004: Стало ясно, что надо эту радость вытаскивать в отдельную библиотеку. И вместо "одной программы" вводить понятие "контекста", в котором записывается текущий шаг, имеется обеспечиваемый клиентом метод "выведи сообщение", и т.д.
После тяжких размышлений решено было обозвать сам модуль
"seqexecauto.[ch]" (Sequental Execution Automaton), а префикс
-- sea_
. Место модулю, очевидным образом -- в
libs/useful/.
Кроме того, стало очевидно, что надо ввести в структуру команды дополнительное поле "ожидание между action и первой проверкой" -- в отсутствие оной фичи пришлось бить многие шаги на триплеты действие-без-проверки, шаг-задержки, шаг-без-действия-только-проверка.
14.04.2004: мысль: в некоторых случаях делать
"умный" checker нет смысла, поскольку информация о
готовности/завершенности имеется в некоем callback'е, который
гарантированно знает, что действие завершено, и в каком контексте оно
вызвано, то стоит добавить функцию sea_next_step()
,
которая бы явно переходила к следующему шагу.
Это заменит муторную комбинацию из статической переменной, которую б
проверял checker, и которая обычно =0, а при надобности в callback'е
делается =1 и вызывается sea_check_cond()
.
17.04.2004: ЗАМЕЧАНИЕ ВООБЩЕ (давно назрело):
seqexecauto, хоть и имитирует архитектуру последовательного исполнения,
является все же event-driven-архитектурой. Как следствие, "переход к
следующей команде" представляет не просто модификацию
program-counter'а, а является вызовом функции, которая, если
результатом "действия" очередной команды будет "замаскированный"
SUCCESS, может привести к рекурсии, так же как и
sea_next()
.
В реальных применениях seqexecauto размеры программ невелики, а следовательно, и рекурсия употребит не так много стека. Но на случай будущего расширения с условными конструкциями (а то и циклами), либо с "искусственными" циклами, происходящими без возврата для ожидания события-триггера, эту тонкость с рекурсией стоит иметь в виду.
18.04.2004: файл доведен до завершенного состояния. Теперь надо проверять (переводить на него istcc).
20.04.2004: решил перетряхнуть определение
sea_step_t
так, чтобы наиболее часто используемые
(указываемые) поля были длиже к началу, а "опциональные" съехали бы в
конец, с тем, чтобы их можно было не указывать. Итого:
Было | Стало |
---|---|
const char *label; sea_action_t action; void *actarg; sea_cndchk_t check; void *chkarg; int chkevtype; int chkdelay; |
const char *label; sea_action_t action; sea_cndchk_t check; void *actarg; int chkevtype; int chkdelay; void *param2; |
Старый вариант был более "логичным" -- все перечислялось по мере использования, а новый дает более оптимальную запись в программе.
21.04.2004: сменил смысл
"curcmd->check==NULL
".
Было: отсутствующий check приравнивался к SUCCESS. Вчера я пытался
думать, для чего такое может быть удобно -- не придумал (тем более, что
у нас и так есть предопределенный SEA_SUCCESS_CHECK()
).
Стало: отсутствующие check приравнивается к NONE. Это аналогично
тому, как в Си'шном for
если второй (средний) оператор --
проверка -- пустой, то цикл считается бесконечным, и завершается только
явным break
'ом; у нас аналогом является
sea_next()
.
22.04.2004: попроверял -- istcc на seqexecauto вполне работает. Так что эту секцию ("новый модуль") помечаем как "done".
sea_goto()
, которая просто
перекидывает curcmd на указанную команду.
17.04.2004:Возможная проблема: АДРЕСАЦИЯ. Ведь actions-то не знают, из какой программы они вызваны. И даже более того -- как получать адрес некоей абстрактной команды? Можно, конечно, указывать относительное знаковое смещение в командах... Но это тоже гадко.
В общем -- возможность есть, а если реально понадобится -- подумаем.
24.11.2004: Йоу!!! Придумал, как решить проблему с адресацией!
В сущности, все очень просто -- зачем получать АДРЕС
команды, когда можно указать просто текстовую МЕТКУ
(label
)?
Тут, правда, возникает еще одна "проблема" -- откуда начинать поиск?
У нас сейчас в sea_context_t
нет поля "адрес начала
программы". Ну что ж -- можно ввести. Единственное что: надо сделать
отдельную sea_goto()
, и из sea_check()
и
sea_next()
вызывать именно ее (а не
sea_run()
, как сейчас), так же как и из
sea_run()
, сразу после прописывания поля "prog_begin".
06.05.2005: поскольку сегодня ввел понятие
"начало программы", то препятствия на пути sea_goto()
исчезли. И я его сделал. Mea culpa...
Не проверял -- не на чем, но и глючить там нечему. Так что -- "done"...
06.09.2005: так, о птичках: а ведь если мы
вызываем sea_goto()
из action'а или checker'а, то этот
action/checker должен возвращать NONE, иначе то, откуда он вызван,
перейдет на следующий шаг.
По-хорошему -- надо,
чтобы sea_goto()
возвращал sea_rslt_t
, дабы
можно было писать "return sea_goto(...);
".
07.09.2005: кстати, а уж совсем по-хорошему --
надо унифицировать sea_rslt_t
со "стандартными" кодами:
0/-1. А SUCCESS -- +1.
Чуть позже: да, так и сделал -- 0/+1/-1, а sea_goto()
теперь возвращает sea_rslt_t
. А для его былого "-2" (not
running) ввел специальный код -- SEA_RSLT_USRERR=-2
.
sea_message_t
с "...
", или лучше с
va_list
?
22.04.2004: вообще-то, из простейшей логики: при "..." перейти к va_list можно, а наоборот -- нет. Так что, дабы внутри seqexecauto тоже можно было использовать "свой MakeMessage", надо делать именно на va_list.
Кстати, последней каплей стал ляп, когда istcc отображал какую-то
ахинею. Как выяснилось, из-за того, что я там в
make_message_method()
вместо XhVMakeMessage()
вызывал не-V-вариант.
(BTW, заодно избегаем проблемы "как указать __attribute__((format)) типу-указателю на функцию, имеющую "..." в списке параметров".)
В общем -- сделал.
06.05.2005: сделано, сегодня. Только "причина завершения" -- это не строка, а именно код.
А что, если в дополнение к "make_message" ввести еще
какую-нибудь функцию, которой бы передавался еще указатель на "шаг"
(для получения его названия либо номера) и некий "код причины" -- о чем
это сообщение (начало исполнения шага, некий облом, или что --
SEA_MMC_NNN
, "MakeMessage Code").
А разумнее всего -- модифицировать таким способом имеющийся интерфейс make_message.
sea_stop()
выдавалось бы какое-нибудь осмысленное
сообщение.
06.05.2005: поразмыслив и проанализировав
ситуацию пришел к такому выводу: реально в ныне существующих программах
({istcc,phm-tsyline}.c :-) используется ОДИН контекст для
несколько разных действий. Так что вставлять туда "перманентное" поле
имени -- некорректно, лучше добавить его параметром к вызову
sea_run()
.
Итого -- вносим первые исправления в seqexecauto.h чуть ли не с момента его рождения -- по крайней мере с 22.04.2004.
Помимо дополнений в "приватную" часть sea_context_t
,
в sea_run()
вторым параметром (отодвинув точку старта)
добавлено имя.
Собственно произведенные махинации внутри seqexecauto.c:
sea_run()
перенес в
ExecuteFrom()
, а былое содержимое sea_stop()
-- в StopBecause()
.
sea_run()
теперь инициализирует состояние контекста из
переданных параметров.
StopBecause()
выдает сообщение на тему "Остановилися,
по такой-то причине".
Так что -- заявленная цель достигнута, "done".
SNL-программы пишутся на некоем Си-подобном языке, которые потом компилируется компилятором SNC.
Некие бумаги по SNL: State Notation Language Overview, SNL:: Getting Started with EPICS Lecture Series: State Notation Language and the Sequencer.
А сайт EPICS'а вообще -- http://www.aps.anl.gov/epics/.
SEA_RSLT_NONE
, а по нему ExecuteFrom()
начинает проверять, не надо ль еще и задержку сделать -- и,
естественно, падает, поскольку ctx->curcmd==NULL
.
07.11.2005: первая мысль -- перед обращением
вставить проверку на !=NULL
. Но это некорректно: тот же
контекст мог быть запущен уже заново, и в данной ситуации туда лучше
вообще не соваться.
Поэтому наиболее корректно -- ввести еще один код результата, типа "отвали немедленно, ничего не делая".
Так и поступил -- ввел еще код SEA_RSLT_GOAWAY
. Вроде
оно нас спасло. Некрасиво, конечно, но -- работает.
Итак: вложенная "программа" делает "sea_check(&wheel_ctx,
EVT_LEVEL2)
" в своей cleanup-функции. А уж checker
соответствующего шага "верхней" программы по этому коду ситуации
возвращает SUCCESS, так что та программа идет дальше.
Для этого (т.к. cleanup внешней вызывает cleanup внутренней) нужно в
"правильном" месте StopBecause()
-- в самом начале, по
крайней мере, ДО вызова собственно cleanup-функции -- переводить
"программу" в не-исполняющееся состояние, чтобы
sea_check()
ничего не пытался сделать.
12.11.2005: так и сделал -- всего-то переместил одну строку. Но пока не проверял.
Вечером: проверил. Перевел lebed_sea.c на эту архитектуру -- вроде все работает. Выкидывать GOAWAY из seqexecauto.[ch] не стал -- мож где и пригодится.
В результате пришлось выпендриваться -- во-первых, делать вызов хозяйского sea_check() из подчинённого процесса, а во-вторых -- вводить глобальный флажок для передачи информации о статусе. А всё потому, что у нас куцеватая модель: ведь cleanup -- это свойство ПРОЦЕССА, и оно действительно должно быть принадлежностью контекста, а для его ХОЗЯИНА нужен отдельный механизм.
А ведь модель с "вложенными" sea -- очень похожа на модель процессов в *nix. Ну так и надо сделать аналогично:
atexit()
-- еще
ВТОРОГО уведомляемого, аналог wait*()
.
28.12.2011: меняем -- первый раз за 6 лет!
sea_on_term_t()
.
sea_context_t.on_term
(в приватной части!).
sea_run4()
, коему передаётся указатель на
функцию, которую вызвать по окончанию. В неё же перенесены все мозги,
а sea_run()
сделана wrapper'ом.
StopBecause()
сначала копирует оба указателя в
локальные переменные, а уж потом вызывает.
reason
.
sea_cleanup_t
мы модифицировали, но
обратно-совместимо -- оно безопасно.
reason
у
StopBecause()
-- теперь вместо STOP_R_* передаются обычные
SEA_RSLT_*. Замена такая: STOP_R_FINISH:SEA_RSLT_SUCCESS,
STOP_R_ABORT:SEA_RSLT_NONE, STOP_R_FATAL:SEA_RSLT_FATAL.
SEA_END()
-- он давно напрашивался.
Новый именно только модуль -- в него ушли функции манипуляции со struct timeval из cxscheduler.[ch].
Резон -- оне понадобились в seqexecauto.c, а приделывать к пользующимся оным программам еще и cxscheduler было б абсолютно бессмысленно -- например, к Chl/Xh/Xm/Xt-программам он вообще никак, ибо Xt и cxscheduler взаимоисключающи.
А в libs/useful (т.е., в libuseful.a) он будет в
самый раз -- рядышком с cxscheduler, но не обязательно вместе. Ирония
в том, что первоначально timeval_NNN()
появились в
programs/server/usefullib.[ch] :-).
Помимо создания этого модуля, провернуты следующие манипуляции:
timeval_subtract()
портит параметр y
--
вычитаемое.
Как следствие -- переменная, передаваемая в качестве вычитаемого -- ОДНОРАЗОВА.
В частности:
SleepBySelect()
.
Выводы:
y
нужно в дальнейшем -- то стоит
передавать его копию.
subtract(diff,now,prev)а
moment=start+delay; IS_AFTER(now,moment)В *adc* -- сейчас сделано плохо и ненадежно!
Так что вставил в .c и .h комментарий-предупреждение (кстати, timeval_utils.c был за 18-04-2004).
timeval_subtract()
должна делать копию и извращаться с
ней.
25.10.2010: Так и сделал -- yc=*y.
Кстати, что странно:
Теперь фиксим места, где вынужденно делались копии перед вычитанием:
08.08.2011: кстати, и из timeval_utils.h предупреждение тоже убрал.
Тогда:
19.04.2004: в настоящий момент это даже еще не модуль, а просто идея.
ИДЕЯ же заключается в том, что часто возникает потребность в разборке строки описаний вида
либоparam=value {param=value}
param=value,{param=value}
На настоящий момент в числе потенциальных потребителей выступают:
ParseAuxinfo()
.
CreateSimpleKnob()
.
Очевидно, что надо изготовить единую библиотеку, работающую по варианту 2 проекта парсера AuxInfo. Проект надо дополнить понятием "квазибулевских опций" -- которые просто при наличии некоего ключевого слова прописывают в указанное место указанное число (как mount'овы опции sync/async, etc.).
Символ-разделитель будет передаваться отдельным параметром -- это
может быть ','
, ';'
, либо пробел (в последнем
случае будет использоваться isspace()
).
Да, и еще тонкость: для возможности "кавычить" символ-разделитель
просмотр всегда будет выполняться последовательно, БЕЗ предварительного
поиска этого разделителя strchr()
'ом. А чтобы избавить
вызывающую программу от подобной привычки, стоит, видимо, предусмотреть
еще передачу параметром "символа-терминатора" (который если
'\0' -- то просматривать всю строку до конца).
20.04.2004: префикс этой библиотеки будет
"psp_
".
20.04.2004: еще мысли про функционирование либы:
PSP_OFFSET_OF(struct_type,field_name)
(слижем с
XtOffsetOf()
).
22.04.2004: кстати, а "символ" ли разделитель? А как быть с записями вида "param1=value1; param2=value2;"? Или, как и терминатор, сделать "строку-набор символов"?
22.04.2004: изготовил "макет-проект"
paramstr_parser.h (содержит и #define
PSP_OFFSET_OF()
, и определение psp_paramdescr_t
.
26.04.2004: сделал первый инициализационный
макрос -- PSP_P_INT()
. Ну и навернуто...
27.04.2004: и еще мысли:
PSP_T_MSTRING
-- вместо буфера под
строку в rec'е имеется указатель, а библиотека сама делает malloc() и
кладет туда результат.
'='
, либо ':'
(для
"param=value" либо "param:value").
PSP_T_REAL
-- на случай, если понадобятся
вещественные числа. А поле size
определит тип -- float
или double.
PspSetParameter(psp_paramdescr_t *descr, void *rec, void *val)
для выполнения "интеллектуальные" действий по прописыванию значения как
при вычитывании его из строки, так и при инициализации.
char psp_errdescr[100]
, в который
при обломах будет складываться сообщение об ошибке, и функцию
psp_error()
, возвращающую указатель на этот буфер (эта
функция должна работать как dlerror()
-- быть однократно
вызываемой).
PSP_R_OK=0
,
PSP_R_USRERR=-1
(ошибка в строке параметров),
PSP_R_APPERR=-2
(ошибка в описании параметров).
01.05.2004: кстати, кому еще будет полезен этот модуль -- sim_drv.c. Сейчас в нем свой собственный, косоватый и недостаточный парсер. А так -- готовое решение, удовлетворяющее всем требованиям.
31.05.2004: УРА-А-А-А-А-А!!! Готово! Оно работает!!! Теперь буду проверять.
08.06.2004: модуль уже работает и используется, так что помечаем секцию как "done".
offset
заносить прямо указатели на переменные, и передавать
rec=NULL
...
27.04.2004: выход 1: забивать туда указатели "руками"; выход 2: ввести ДВА комплекта макросов -- первый (как сейчас) принимает sname+field, второй -- указатель.
Вывод: НЕФИГ! Надлежит ВСЕГДА иметь конфигурацию сгруппированной в структуру. Так что помечаем секцию как "done".
30.12.2013: ага, только при sizeof(int)!=sizeof(void*) -- т.е., на 64-битных архитектурах -- это работать не будет.
02.01.2014: и даже больше: раз уж rec==NULL означает "парсинг в никуда" (выкинуть результат), то эта фича невозможна в принципе. Так что "withdrawn".
PSP_T_APPDEFINED
?)
01.06.2004: сделал в первом приближении -- оно
называется PSP_T_PLUGIN
. Пока даже не пытался проверять
-- как где-то понадобится, так и доразберусь. Сейчас там также есть
проблема с "инициализацией" -- то, что делается в
PspCountAndInit()
.
02.06.2004: ввел-таки инициализацию -- теперь
действует соглашение (очень разумное), что если str==NULL
,
то, естественно, никакой парсинг не делается, а вот инициализация --
да. Так что в PspCountAndInit()
вызывается
var.p_plugin.parser(NULL,NULL,...)
.
Заодно изготовил ранее забытый макрос PSP_P_PLUGIN()
.
Итого: пока ничего не проверено, но стоит переделать под
PSP_T_PLUGIN парсинг опций (которые сейчас только "log") в
cx-server_dbase.c::SimulateDatabase()
-- тогда
все и проверим. Заодно -- встает вопрос унифицированного возврата
"строки ошибки" из plugin-parser'а.
06.06.2004: перевел
SimulateDatabase()
на PSP_T_PLUGIN, заодно решив проблему
восзрата строки ошибки -- добавил в определение plugin-parser'а
параметр "char **errstr
".
Осталось только протестировать.
06.06.2004: протестировал -- работает. И тексты ошибок выдает корректно.
(Там, правда, отсутствовала инициализация по умолчанию, но это мелочь -- я вставил вызов psp_parse() с str=NULL.)
Кстати, именно с этого момента (точнее -- с 06.06.2004)
внутрисерверности, в т.ч. драйверы, могут пользоваться
psp_parse()
-- собственно, canadc40_drv.c уже
пользуется.
Помечаем раздел как "done".
11.03.2014: есть подозрение, что тогда было заложено некоторое неудобство: plugin-parser'ам НЕ передаётся "имя собственное" того, куда они парсят (item->name).
inserver_d_c_ref_plugin_parser()
, вызывающим
inserver_parse_d_c_ref()
, коий содержит собственный
параметр what
.
08.06.2004: что ж, вот он:
'#'
,
если не надо -- то '\0'
)? Ведь поиск таких вещей лучше
всего ложится именно на psp, ибо если его caller начнет искать такие
вещи сам, то будет иметь проблемы типа "символ комментария внутри
кавычек".
Замечание: если вводить, то, аналогично separators/terminators, не символ, а строку -- чтобы можно было передавать вложенным вызовам, дополняя их символами.
27.04.2005: а нафиг?! Ведь достаточно эти
символы-начала-комментария добавить к terminators
,
а уж вызывальщик пусть по *endptr
разбирается, что
послужило причиной завершения -- не комментарий ли.
Так что помещаем идею в <S>, одновременно помечая как "done".
02.08.2004: самый идеологически простой вариант
-- ввести в дополнение к size
еще и поле
count
.
А если бы еще можно было указывать числа не только последовательно через запятую, но и как в gcc/C99 -- произвольные номера, диапазоны, и т.д... (Угу, раскатал губу :-)
Но элегантную архитектуру с PspSetParameter()
такое
сломает напрочь. Так что вероятность, что я такую "массивовую" фичу
добавлю -- близка к нулю.
А если уж очень припрет -- ничто не мешает реализовать оное в виде плагина (userptr=count).
06.07.2005: а если все-таки вводить массивовые параметры, то тогда должны бы поддерживаться следующие варианты синтаксиса:
arr={val,val,...} arr={[5]=val,...} arr={[5,6]=val,[10...20]=val} arr[5,6,10...20]=val
fopen()
.
Это нужно, когда из ЕДИНОЙ строки параметров несколько подсистем выкусывают СВОИ опции.
Не очень корректно, конечно, но может понадобиться...
12.04.2005: да, наверняка понадобится -- в реализации "unified knobs" -- там надо будет уметь парсить knobinfo_t.placement, в принципе предназначенное для произвольного "менеджера", не обязательно того, который реально используется.
03.05.2006: да, оное будет крайне потребно. Наглядно убедился в этом, разрабатывая плагин-компоненты "закладочник" и "дерево" -- их опции слабо пересекаются с опциями обычных элементов, но сами они вполне interchangeable с теми элементами, и весьма неудобно, когда оно начинает ругаться из-за чужих опций и сходу, отдавая ошибку, "режет" даже свои...
А так -- можно будет в строке опций указывать сразу "полный набор" -- и для одного, и для другого -- так что каждый возьмет то, что понимает.
14.09.2006: сделано, в рамках
"psp_parse_as()
" -- флаг PSP_MF_SKIP_UNKNOWN
.
Оно при этом просто молча пропускает все проверки на разумность
(наличие/отсутствие символа equals_c
), парсит
shell-string, и так же молча выкидывает результат.
Считаем за "done".
10.08.2005: собственно, а нахрен?! Ведь у нас уже есть "вложенный psp" -- PSP_T_PSP -- он эффективно и является тем самым "include", причем "структурным" -- парсится в отдельную подзапись.
16.09.2005: "нахрен" -- понятно: потому, что все-таки nested НЕ эквивалентен include -- у них "разные пространства имен", плюс, отличный синтаксис. Т.е., некие параметры p1 и p2 в случае nested будут указываться как
inc=p1:v1,p2:v2а в случае include -- "нормальным" образом, как
p1=v1 p2=v2
Так что -- в CXv4 для реализации унифицированных параметров knob'ов и прочих элементов будем вводить PSP_T_INCLUDE.
Ведь это, в общем-то, совсем несложно -- при инициализации вызывать самого себя с теми жи параметрами (НЕ конкатенируя separators,terminators), а в парсинге -- просто вытащить "поиск" параметра по имени в отдельную функцию, которая так же сможет при надобности вызвать сама себя.
01.03.2009: кстати, есть другая альтернатива --
Как насчет ввести "сборный" PSP, с МНОЖЕСТВЕННЫМИ парами таблица/структура? Чтобы оно пыталось найти указанный параметр в ЛЮБОЙ из указанных таблиц?
30.12.2013: а вот сейчас понадобилось -- для хитрой связки {pzframe,fastadc}_{data,gui,main,knobplugin}...
31.12.2013: сделано (см. ниже); но этот кусочек оставим "withdrawn" чисто для истории.
03.03.2009: хорошенько подумал, и пришел к выводу, что поддержка множественных пар таблица/структура, хоть и сравнительно проста в реализации, но всё же излишня. В основном -- потому, что схема с INCLUDE все же полезнее, и её хватит для большинства применений (а в дополнение есть еще PSP_T_PSP); перегружать же модуль излишними вариантами -- не хочется. Так что ставим тот пункт как "withdrawn".
07.12.2010@Снежинск-каземат-11: делаем:
PSP_T_INCLUDE
и PSP_P_INCLUDE
,
плюс psp_paramdescr_t.var.p_include
.
PspCountAndInit()
в
PspDoInit()
, и выкидываем весь подсчёт-количества (который
и не использовался; а зачем он вообще был сделан?).
PspDoInit(descr->var.p_include.table, rec)
(в отличие
от проекта -- нету там никаких separators/terminators).
FindItem()
, по
PSP_T_INCLUDE вызывающую саму себя.
Теперь осталось только проверить :-D.
08.12.2010@Снежинск-каземат-11: реально fastadc_knobplugin,
ради которого сейчас затевалась эта реализация, НЕ требует INCLUDE --
поскольку там сейчас своя алхимия с "merged table", которая сама
сливает все под-таблицы с соответствующей настройкой полей
offset
в момент сливания.
Но: стоит перевести всех клиентов
_Chl_GetStd_text2elemopts()
с
PSP_MF_SKIP_UNKNOWN
на INCLUDE. Как раз и проверилось бы.
31.12.2010: конкретная мысль -- как можно применять INCLUDE для парсинга стандартных опций:
16.01.2011: а не добавить ли в
psp_paramdescr_t.p_include
доп.offset? Тогда это
эффективно получится «"сборный PSP"»-01-03-2009. И тогда
станет возможным не только "одиночное наследование", описанное
31-12-2010, но и множественное -- поскольку не будет ограничения
"поле наследуемого типа должно быть в начале конкретной
privrec-структуры".
Вопрос только в том, что придётся в FindItem()
'е ВСЕГДА
дополнительно возвращать доп.offset -- причём с учётом вложенности.
Мож, withdrawn, а?
18.01.2011: какой, нафиг,
p_include.доп.offset?! У нас же уже есть
ГОТОВОЕ поле psp_paramdescr_t.offset
-- его и
задействуем!
Ok -- в PSP_P_INCLUDE
добавлен параметр
aux_offset
.
И -- очевидно, что делать надо, как бы ни не хотелось -- уж больно много пользы будет от этой фичи.
18.01.2011: еще приятная мысль -- теперь можно в Chl_subwin избавиться от махинации с SUBWIN_PARAM_SEP. 01.02.2011@Снежинск-каземат-11: да, избавились! Уф...
26.01.2011: блин, а про psp_free()
забыл -- но ведь в нём также надо учитывать...
Вставил -- вроде всё straightforward.
26.01.2011: оживляем поддержку "доп.offset".
PspSetParameter()
::INCLUDE/init
рекурсивному-себе-через-PspDoInit()
передаётся ссылка на
rec
, откорректированная на offset.
FindItem()
передаётся дополнительно указатель на
"aux_offset", куда он аккумулирует offset'ы встреченных по пути/глубине
INCLUDE'ов.
psp_parse_as()
везде в качестве
"адреса начала структуры" (для PspSetParameter()
и по
PSP,PLUGIN) теперь используется не rec
, а
base_addr
=rec+aux_offset.
psp_free()
оно было вставлено сразу, еще утром.
01.02.2011@Снежинск-каземат-11: надо бы облегчить уставку
INCLUDE-таблиц, добываемых через функции-аксессоры (т.е., когда мы НЕ можем
указывать таблицу прямо в PSP_P_INCLUDE()
). Без этого
программам придётся "вручную" уставлять .var.p_include.table у заранее
определённой строки, что очень бажно.
Делаем так: поскольку INCLUDE-строка всё равно имеет имя, префиксируемое
"#" (кстати, в определении сам параметр n
был
забыт), то передаём введённой функции
psp_set_include_table()
, кроме указателя на основную таблицу,
имя под-таблицы и тот указатель, что надо прописать.
Естественно, работает это только на один уровень вложенности, но больше вряд ли потребуется.
01.02.2011@Снежинск-каземат-11: проверил на LOGD_SCENARIO -- похоже, PSP_T_INCLUDE работает; в т.ч. с доп.offset'ом -- нормально парсятся поля не в начале базовой структуры. Вложенные INCLUDE, правда, не проверял -- не на чем.
Пора считать за "done".
01.02.2011@Снежинск-каземат-11: а ведь в CXv4 НЫНЕШНЕЙ моделью INCLUDE, с таблицами, возвращаемыми аксессорами, не попользуешься -- поскольку там парсинг делается базовой инфраструктурой. Выводы-следствия:
extern psp_paramdescr_t *zzzzzopts[]
_Create_m_as()
, чтоб
наследователи могли вызывать их с уже распарсенными опциями.
Но лучше вообще от этой ситуации с "наследованием" уйти полностью.
08.02.2011@Снежинск-каземат-11: повсюду отсутствовали проверки на table==NULL -- при внедрении INCLUDE это стало возможно (когда таблица формируется из кучи под-таблиц, добываемых из разных мест, то часть из них может отсутствовать -- т.е., NULL).
Посему -- в PspDoInit()
и в FindItem()
вставлены guard'ы, заставляющие просто игнорировать table==NULL. И, за
компанию, мы перешли к соглашению, что
FindItem()
возвращает NULL при не-найденности -- вместо
былого item->name==NULL.
31.12.2013: делаем парсинг "сборного" PSP с МНОЖЕСТВЕННЫМИ парами таблица/структура, по проекту от 01-03-2009. В интересах pzframe+fastadc/vcamimg.
psp_parse_v()
-- по аналогии с
readv()
.
tables[]
и
pair_count
идут сразу после recs[]
.
FindItem()
,
реализация крайне проста. Буквально дело нескольких минут.
psp_parse_v()
, и уже
psp_parse_as()
стала wrapper'ом.
Проблема лишь в том, что оно может захотеться для ВСЕХ числовых типов -- INT, SELECTOR, LOOKUP, FLAG.
Впрочем -- все они в PspSetParameter()
отрабатываются
скопом, так что реально деятельность сведется к:
var.p_{int,selector,lookup,flag}
поля
"mask".
PSP_P_{INT,SELECTOR,LOOKUP,FLAG}_BITFIELD
.
PspSetParameter()
, который бы (также
тремя вариантами) вместо простого присвоения делал бы присвоение
битового поля, предварительно сдвинув значение на базовый сдвиг маски.
- Массивовые параметры.
- PSP_T_INCLUDE. (сделана в 12-2010)
- Множественные таблицы. (@01.03.2009)
- Битовые поля.
psp_paramdescr_t
еще и
поле "comment"? Чтобы в нем было некое описание. Макросы же
PSP_P_NNN(n,sname,field,...)
оставим старые, они будут проставлять
NULL
, а для тех случаев, когда хочется указать
комментарий, введем доп. комплект --
PSP_CP_NNN(n,comment,sname,field,...)
.
"title=\"Lebedev's line\""
оно ругается
"Unterminated <'>-string in 'title' value".
02.11.2005: все оказалось просто -- там при проверке, не является ли очередной символ открывающей кавычкой, не стояло дополнительное условие -- что мы сейчас уже внутри кавычек.
Условие вставлено -- проблема исчезла.
Заодно: там стояли уж больно дебильные комментарии не тему "open quote" и "closing quote" -- исправил на более адекватные.
LookPluginParser()
(см. замечание
за сегодня): похоже, надо б иметь интерфейс для плагинов, облегчающий
парсинг. Как вариант -- специальный подвид плагина -- "конвертер",
которому передается уже отпарсенная строка (попросту --
valbuf[]
).
27.07.2006: да, надо. Другой опыт -- парсинг параметров у декоративных knob'ов при ELEM_CANVAS'е: там несколько своих форматов -- так их приходится объявлять как PSP_*_STRING, чтоб складировались в строчки, а потом эти строчки парсить; делать же плагин-парсеры -- просто сил нету, слишком мерзко. И "доморощенные" парсеры крайне примитивны, нифига не проверяют на ошибки (ну а как бы -- опять наворачивать море кода).
Так что -- НАДО!
getopt()
'а -- popt.
27.07.2006: да, выглядит-то это соблазнительно, но -- НЕФИГ!!!
argv[]
, без необходимости
предварительно сливать все эти параметры вместе в одну строку.
14.09.2006: решение этой и иных задач
"расширенного парсинга" -- введение лишнего параметра, "как парсить".
Чтобы не ломать совместимость, вводим функцию
psp_parse_as()
, с дополнительным параметром
mode_flags
. В нее и переезжает все содержимое, а
psp_parse()
превращается просто в ее вызов.
На необходимость пропустить инициализацию указывает флажок
PSP_MF_NOINIT
.
22.09.2006: еще тогда, неделю назад, разборки показали, что КОРРЕКТНО реализовать не-инициализацию весьма нетривиально -- поскольку туда же завязана проверка таблицы.
В общем, схалявил -- сделал по первоначальному, "простому" проекту,
что при наличии PSP_MF_NOINIT
попросту пропускается вызов
PspCountAndInit()
.
А в CXv4 надо будет сделать параметр "как парсить"
ОБЯЗАТЕЛЬНЫМ, прямо в psp_parse()
. И, естественно,
расширить интерфейс плагинов -- чтобы им также передавался параметр
"только проверить, но не инициализировать".
02.01.2014: тогда было забыто для PSP_T_PSP тоже
вызывать psp_parse_as()
, и флаги во вложенные не
передавались. Исправлено.
PSP_P_STRING
нельзя было указывать def=NULL -- оно падало
по SIGSEGV. Оказалось -- стоял вызов strncpy() вслепую. Вставил
проверку -- теперь NULL равносилен "".
srcp-str
, так что
нумеровалось с 0, вместо принятого с 1. Добавил +1
. В
4cx/ скопировано.
getcap()
.
tgetent()
из curses
(специфически заточенная только под termcap), а более навороченный
интерфейс getcap(3)
.
PSP_T_NOP=0
.
25.03.2010: смысл -- чтобы программа могла at-runtime деактивировать некоторые элементы таблицы. Конкретно потребовалось в fastadc_common-based nadc200 в режиме эмуляции (oadc200).
Для полноты сделан и PSP_P_NOP()
-- помимо типа он
заполняет только поле name
, просто чтоб можно было
использовать как placeholder.
31.03.2010: ма-ла-дец!!! Вставив код 0 и сдвинув все остальные -- нарушил бинарную совместимость сервера/драйверов!!! Теперь надо везде обновлять, блин!!!
FastadcCreateMergedTable()
выполняет всё самостоятельно.
endptr
у нас везде (и в самих
psp_parse*()
, и в psp_pluginp_t
) просто
"char **
", БЕЗ const
.
В результате мы везде при возврате туда значения имеем warning "assignment discards qualifiers from pointer target type".
18.01.2011: фиг знает, почему ТОГДА, весной 2004-го, я сделал именно так. Вероятно для того, чтобы не заставлять приложения более верхнего уровня всё делать в const'е -- а они-то могут иметь и модифицировабельную строку.
И, кстати, все стандартные strto*()
также сделаны без
этого const
'а.
Так что -- делаем этот пункт "withdrawn".
24.01.2011: неа, всё-таки сделал. Всё прекрасно
и замечательно, за вычетом единственной точки, где пришлось этот же
const char **
передавать strtoul()
'у -- но
тут и фиг с ним.
Итого -- "done".
01.02.2011@Снежинск-каземат-11: пара замечаний:
FindItem()
'ом
игнорируется, но сделать парсимый-в-никуда аналог -- раз плюнуть.
02.02.2011@Снежинск-каземат-11: да, делаем, для liu/plot/*knobplugin*'ов оно точно понадобится.
Сделал за несколько минут -- там всё тривиально:
PSP_P_VOID()
заполняет только имя и тип.
PspSetParameter()
-- рядышком с NOP'ом, просто чтоб не
ругалось.
Считаем за "done" сразу, проверим позже.
PSP_T_NULL=0
, а NOP и VOID ставим сразу после
него.
rsrvd1
и
rsrvd1
. Это запас на будущее -- например, для массивов
и/или битовых полей.
Мотивировка: всё равно на пульту сейчас придётся всё обновлять (поскольку там старый API/ABI, уже не совместимый из-за отсутствия PSP_T_NOP, а надо будет ставить туда драйвер xcdac20 -- для управления чернякинским ИСТом).
16.03.2011: и чтоб уж решить проблему с добавлением новых PSP_T-типов раз и навсегда -- перетряхнуто намного сильнее (коль всё равно не успели на пульте обновить ;-)):
"done".
31.03.2011@Снежинск-каземат-11: еще до-делка в ту же степь: дополнительно сегментируем разные "подвиды" типов, заодно меняя базу со 100 на 50:
28.06.2011: проблема в том, что в макросах
PSP_P_nnn()
для инициализации union'а
psp_paramdescr_t.var
использовался gcc'шный extension
"Labeled Elements in Initializers", почему-то не реализованный для C++.
Гугление на тему "how to initialize union" оказалось весьма показательным -- вменяемого решения не существует. Есть кой-какие хаки для C++, но и только.
Шаренье в "info gcc" напомнило о еще одном расширении -- "Cast to Union", которое, вроде как, НЕ C-only.
В связи с этим содержимое psp_paramdescr_t
было изрядно
переделано: определение поля var
вытащено в отдельный
typedef psp_var_t
, содержимое которого, в свою очередь,
раздербанено на отдельные typedef'ы psp_var_p_nnn_t
.
Это-то однозначно полезный переход. А вот попытка использовать "cast to union" не заработала: не только в C++, где оно даёт вообще дичайшую диагностику, но даже и в C (gcc>2.96)...
06.09.2019: немного в продолжение вопроса:
(Сейчас и не помню, где точно нашёл с месяц назад -- что-то типа stackoverflow.)
struct
, и даже у
union
.
Вопрос лишь в том, получится ли результирующий тип, сгенерённый C++'ом, совпадающим с C'шным.
psp_paramdescr_t.offset
имеет тип int
, а
должно б быть ptrdiff_t
. На 64-битных архитектурах это не
одно и то же (32 и 64 бита соответственно).
30.12.2013: обсуждение:
02.01.2014: но в свете "rec==NULL => выкинуть результат" та фича всё равно не жилец.
Придётся отложить до очередного глобального апгрейда.
02.01.2014: вставлены проверки в 3 места:
PspDoInit()
.
PspSetParameter()
.
psp_free()
.
base_addr
(поиск среди таблиц, PSP_T_PSP, PSP_T_PLUGIN),
проверяется на ==NULL и если да, то оставляется NULL.
Проверить затруднительно (не на чем), так что верим в "done".
size_t
-- оно на x86_64 является 64-битным (т.е.,
8-байтным), а в PspSetParameter()
максимально поддерживаемая
альтернатива -- 4 байта.
10.04.2016: добавлена альтернатива "8:",
#if MAY_USE_INT64
";
MAY_USE_INT64
же вначале уставляется в 1, если не
определена.
ARCH_CPPFLAGS=-DMAY_USE_INT64=0
Несколько слов об аспектах реализации:
int
:
psp_var_p_int_t.defval
(и minval/maxval; и
defval'ы у selector, lookup и flag) являются int
'ами;
psp_parse_v()
делается в
int
,
int
.
(Хотя если где-то простой int будет 64-битным, то и парситься
будут более объёмные значения; но таких платформ вроде не видно, а если
проявятся, то там вылезут иные проблемы, в misc_types.h с
определением типа int32
.)
*((uint64 *)dst) = *((int *)vp);
но парсинг числа 2147483648 в size_t на 64-битной платформе даёт результат
0xffffffff80000000 -- проверено (код утилитки тут внутри, закомментирован).
psp_var_t
изменится. Хотя
-- ведь в psp_var_p_real_t
же как раз 3 штуки 64-битных поля?
PSP_T_NNN
-коды
(плюс соответствующие psp_var_p_NNN_t
и
PSP_P_NNN()
-макросы). Хотя выглядит сие криво -- столь же
криво, как отдельные NNN32-вызовы в Win32.
24.10.2004: еще энное время назад в
paramstr_parser'е понадобилась функция memcasecmp()
. В
стандарте POSIX оная почему-то отсутствует, так что она была добыта в
RedHat-7.3:SRPMS:fileutils-4.1/lib/memcasecmp.c и названа
psp_memcasecmp()
.
Но потом то же самое понадобилось и в cx-starter'е для сравнения кусков имен хостов (до первой '.'). Пришлось сделать psp'шную функцию публичной, но это криво.
Посему очевидное решение -- «сделать себе немножко libc».
Итого -- сделан новый модуль, содержащий функцию
cx_memcasecmp()
, и paramstr_parser с cx-starter'ом
переведены на нее.
BTW, заодно обнаружил, чего еще не хватает в Posix'е: strcasestr() и memcasemem(). Обе они имеются (аж в нескольких вариантах) в ньюсах -- groups.google.com на тему "memcasecmp" расскажет много интересного. Плюс, естественно, можно изобрести и (по аналогии с strrchr()) memr{,case}mem() и strr{,case}str() :-).
01.12.2004: обнаружил, что в
paramstr_parser.h оставалось объявление уже несуществующей
psp_memcasecmp()
-- удалил.
03.12.2004: для начала сделал
в самом Cdr.c. Идея реализации понятна :-). Она несколько халтурновата тем, что возвращает не -1/0/+1, а ==0/!=0.static int cx_strmemcasecmp(const char *str, const void *mem, size_t memlen) { return !(cx_memcasecmp(str, mem, memlen) == 0 && strlen(str) == memlen); }
Весьма вероятно, понадобится также и не-'case'-версия --
cx_strmemcmp()
Запрягши Google на эту тему, нашел "коллегу по разуму" -- одну единственную, одну давнюю-давнюю реализацию:
Автор там использует эту функцию точь-в-точь как я.static int strmemcmp(s, p, n) char *s, *p; int n; { for (; *s && n > 0; s++, p++, n--) if (*s != *p) return *s - *p; return n ? -1 : (*s ? 1 : 0); }
17.05.2005: во-первых, прошло уже почти полгода, так что помечаем как "done".
А во-вторых, реально практически ВСЕГДА нам требуется именно
strmemcasecmp()
, а не просто
memcasecmp()
-- везде, где использовался не-str-вариант,
отдельно стояло сравнение длины строки с длиной символьной цепочки.
Посему -- потроганы cx-starter.c,
paramstr_parser.c (в нем вообще была ошибка -- попытка
обратиться к строке по чужому индексу, вместо вызова
strlen()
), и Cdr.c.
cx_strmemcmp()
-- понадобилась, для
ChlRunMulticlientApp()
. Добавил ее практически
копированием из cx_strmemcasecmp()
. В 4cx/ также
скопировано.
20.04.2007: создаем модуль с именем findfilein, и функцию с таким же именем. Смысл такого именования -- просто "findfile" уже широко используется разными вариантами, водящимися в сети (чаще всего -- рекурсивный поиск), "findafile" -- как-то подозрительно (что-то связанное с malware), а вот "findfilein" и "findfileinpath" -- обычно используют примерно в тех же целях, что и у нас.
Проект таков:
typedef void * (*findfilein_check_proc)(const char *name, const char *path, void *privptr); void *findfilein(const char *name, const char *argv0, findfilein_check_proc checker, void *privptr, ...);
Параметр privptr
просто передается ей как есть -- на
случай, если клиенту понадобится передать функции какую-то информацию,
либо получить от функции что-то важное.
fopen()
. (Просто имя или
директорию вернуть не удастся, поскольку они лежат в автоматических
переменных в стеке.)
...
, пока тот
не закончится NULL'ом (тогда она вернет NULL), либо пока проверяльщик
не вернет не-NULL (тогда этот результат и будет вернут).
Естественно, нужна и va_list
-версия --
vfindfilein()
.
Замечание: с таким вариантом API его можно использовать даже не только для файлов, а для произвольных объектов -- это определяется проверяльщиками.
31.05.2007: just for record -- а СДЕЛАНО-то тогда нифига не было, токмо этот раздельчик записан (впрочем, это описание и есть ~70% работы).
08.08.2007: вроде бы сделано, включая
builtin_fopen_checker()
, используемый при checker==NULL, и
специальный код-возврата FINDFILEIN_ERR
; теперь надо
испытывать.
Несколькими часами позже: испытал (на 4cx/.../Cdr_file_ldr.c). Работает, черт возьми! И в 4cx/ скопировано.
В принципе, может понадобиться хорошенько проверить ситуации с "/..." и подобным, но уже можно считать за "done".
findfilein()
всех виновников его появления --
cx-starter.c::ReadConfig()
,
CdrOpenDescription()
и
cda.c::lapprox_table_ldr()
, отправив на пенсию
ихние замутные реализации.
05.07.2009: угу, перевел, причем в прямо противоположном порядке. Несколько часов потратил на вроде бы плёвую работу.
Там фокус в том, что во всех трех случаях РАЗНЫЕ списки директорий
для поиска, определяемые сутью задачи: cx-starter.conf может
ищется по одним правилам, _db.so-подсистемы -- по другим,
таблицы -- по третьим (и даже ЭТИ правила не совпадают с правилами для
.xpm-пиктограмм). Так что в придуманном с год назад макросе
CX_SEARCH_LIST_FOR_FINDFILEIN(subdir)
нет никакого смысла.
05.07.2009: да -- теперь если
privptr!=NULL
, то builtin_fopen_checker()
считает его за суффикс.
#include<sys/param.h>
-- ради PATH_MAX. Детали см.
в разделе про систему сборки.
29.03.2013: "общепринятый" вариант позволил бы указывать такие пути в конфигах (т.е., он более the Unix way). А сейчас, например, cxldr'у приходится обходиться ЕДИНСТВЕННЫМ вариантом пути.
Потенциальные сложности такого варианта (помимо просто перетряски API):
':'
проблемен с точки зрения Форточек --
там это буква диска, так что придётся вводить поддержку ';'.
Наверное, для определённости -- в *nix надо ':', в Win* -- ';'; БЕЗ вариантов.
09.08.2013: функция с одним параметром --
const char *search_path
-- реализована:
findfilein2()
.
FINDFILEIN_SEP
и FINDFILEIN_SEP_CHAR
, сейчас
это всегда ":" и ':'.
Короче -- надо переводить всех юзеров на эту версию, каждый раз тщательно проверяя работоспособность, а потом удалить старую.
10.08.2013: переделываем проверяя (это, кстати, список текущих применений):
lapprox_table_ldr()
CdrOpenDescription()
MaybeAssignPixmap()
cxldr_get_module()
(этого
проверять особо не на чем)
ReadConfig(()
Cdr_file_subsys_ldr()
file_opener()
Поскольку всё вроде пашет -- удаляем старые определения и
переименовываем обновлённую функцию из findfilein2()
в
просто findfilein()
.
...По-хорошему, конечно, еще б код самой функции немножко причесать, для повышения пристойности и читабельности, но это уже косметика. А так -- считаем за "done".
slen
и plen
в findfilein()
:
теперь они size_t
вместо int
.
Смысл -- избавление от warning'а; чисто по способу вычисления они не могут быть <0. Хотя и немного ссыкотно -- мало ли, какое изменение взбредёт в голову в дальнейшем, а там права на ошибку не осталось...
Отсюда возникает вопрос: а как бы этак сделать, чтобы программа могла пытаться взять одно из НЕСКОЛЬКИХ имён файлов? Т.е., не удалось взять по имени хоста -- берём по localhost; не удалось и его -- берём просто без имени хоста (т.е., cx-starter.conf или devlist-N.conf). Соответственно, чтоб и снаружи эти "множественные" имена могли бы указываться (серверам -- стартером).
Напрашивается идея, что множественные имена должны указываться в том же стиле, что и список путей для поиска -- через ':'; и чтоб уж findfilein() и пытался оное искать.
Соответственно, в findfilein() получится ДВА вложенных цикла -- по путям и по именам. Вопрос только, какой из них должен быть наружным, а какой внутренним (это определяет порядок перебора).
P.S. А вообще примерно то же самое можно б было сделать какой-нибудь shell'овской командой -- типа "взять первое из такого-то списка имён", где "список" получается как бы расширением "списка шаблонов".
25.01.2018: а вообще проблема ep1-berezin2 (на котором "потребность" и встала в реальный рост) решается проще -- пусть у него будет static IP (и hostname тоже). 29.01.2018: так и сделано.
lse_
/LSE_
, местожительство --
lib/useful/ (по применимости он чем-то близок к seqexecauto).
Коротко -- это модуль простейшего текстового скриптинга, с расширяемым набором команд. Основное предназначение -- база для Cdr_script, в свою очередь, созданного в интересах Chl_scenario.
20.01.2011: тут следует сказать, что в его нынешнем виде этот модуль скорее временный, в основном для обкатки технологии скриптинга. Уже сейчас понятно, что такой вариант технологии очень примитивен и подходит лишь для тривиальных применений. В будущем наверняка будет какое-то иное решение, объединяющее скриптинг и формулы.
Кроме того -- само название уже занято, хренью для мобильников: http://www.lightscript.net/. Так что у будущего развития технологии еще и название будет иное.
20.01.2011: по сравнению с сырым Chl_scenario'вским прототипом схема улучшена и украсивлена: базовые концепции содержимого context-структуры делаем по принципам, аналогичным sea:
21.01.2011: да, в первом приближении работает. Что еще надо сделать:
lse_init()
автоматически делалась
проверка с LSE_PARSE_ONLY
.
25.01.2011: автоматический проверочный прогон с
LSE_PARSE_ONLY
добавлен.
01.02.2011@Снежинск-каземат-11: понадобилось добавить
lse_is_running()
, чтоб можно было ЛЕГКО определять, можно ли
сейчас стартануть скрипт, или же он уже исполняется. Иначе фиг отличишь,
вернул lse_run()
-1 из-за какой-то внутренней причины, или же
это EBUSY.
За компанию повставлял в начала функций errno=0
.
02.02.2011@Снежинск-каземат-11: был ляп --
lse_init()
пыталась складировать себе scriptname
не проверяя на NULL, и в результате падало. Пофиксил.
Заявленный функционал работает, так что уже ставим "done".
29.10.2012: общности ради переименовываем
flags
в options
.
tc_stop()
, который тормозил бы прием текущего кадра, если
таковой происходит.
Понадобилось это для istcc.c, чтобы там по кнопке [STOP] можно было корректно тормозить sea-программу.
10.04.2007: ввел пару новых параметров -- "RRQ" и "MEM", по умолчанию равных 0.
И приступаем к собственно ПОДДЕРЖКЕ перезапросов. Во-первых, былое
поле is_waiting
переделано в state
, могущее
быть READY|INITIAL|REGET.
12.04.2007: поддержка перезапросов сделана.
Непосредственно перезапрос делает функция
RequestMissingLines()
, а отправка пакета вытащена в
SendReq()
.
Таймаут при перезапросе -- 0.1с, так же как и при запросе сразу из памяти -- вместо обычного 1с.
30.08.2007: добавлена поддержка новой фичи камеры -- "подтверждение получения команды". Фича заключается в том, что последний вариант прошивки камеры (кроме умения досылать строки) при получении команды отвечает пакетом с line_n=12345.
Внесенные в tsycamlib.c изменения заключаются в следующем:
Теперь осталось все эти новации внедрить и в tsycamv_drv.c (а поддерживать ли перезапросы -- указывать в auxinfo).
26.05.2008: подготовка к отражению -- введены параметры TSYCAMV_PARAM_WAITTIME (def=1000ms), TSYCAMV_PARAM_WAITTIME_ACKD (def=10*1000ms, пока не используется) и TSYCAMV_PARAM_STOP.
Так что -- в принципе, было бы полезным наличие конвертера в 3-байтный формат...
В связи с чем и сама miscXutils/ прекратила существование.
Код в настоящее время состоит из 4 компонентов:
Первый компонент складывается в библиотеку libfastadc_data.a, а
остальные три -- в libfastadc_gui.a (и Makefile её НЕ
собирает при наличии NOX
).
28.06.2012: по опыту [начала] создания 2-й аналогичной инфраструктуры (для CCD-камер -- vcamimg) стало ясно, что между ними ОЧЕНЬ много общего: vcamimg делается копированием огромных кусков fastadc, с удалением лишнего.
Явно надо выделять общую часть, и называть её parframe. А специфичности (для fastadc -- раздербанивание общего массива (и вообще многострочность), пересчёт raw/pvl/dsp, ...) выносить в "наследников"/"extension'ы".
02.07.2012: аналогичные мысли при реализации обновлённого ippclient (с knobplugin'ами вместо elemplugin'ов): там тоже была бы полезна эта инфраструктура. И для DIMEX тоже.
Итого, потенциальный список девайсов, требующих как parframe-драйверов, так и parframe-отображаторов (точнее, клиентской части):
...что бы еще -- неясно: сходу в голову ничего не приходит, Гусев ничего добавить не смог, Чеблаков+Роговский просто молчат.
Как бы то ни было: надо сделать "общий кусок" -- parframe, а поверх него "отнаследованные" классы -- fastadc, vcamimg, onedfrm, от которых генерить поддержку конкретных устройств.
DoConnect()
был ляп: там сначала
делалось cda_run_server()
, а ПОТОМ при надобности
уставлялись параметры [из командной строки]. Пофиксено.
10.02.2012@Снежинск-каземат-11: как-то там мутновато: в _data и _gui как раз СЕЙЧАС есть evproc, а не ndcb. Надо бы разобраться и устранить лишнее.
13.05.2012@Снежинск-каземат-11: разбираемся.
FastadcKnobpluginInit()
: параметр "cb" действительно
никем не использовался. Так что -- убираем:
fastadc_knobplugin_t.cb
;
fastadc_knobplugin_evproc_t
;
FastadcKnobpluginInit()
и
FastadcKnobpluginEventProc()
тоже.
fastadc_knobplugin_t.ki
, также
требовавшееся только этому механизму.
Господи, ну и brain-damaged же модель там использовалась -- каким местом я думал, вводя весь этот кошмар (в конце 2010-го?)?
Засим проблему считаем за "done".
Единственная оставшаяся не-вполне-адекватность -- что здесь у нас именно ndcb
,
а не evproc
; т.е. -- тупая, без параметра reason
,
нерасширябельная.
14.05.2012@Снежинск-каземат-11: радикально решаем проблему "нерасширяемости":
reason
будет единым/сквозным для
всех уровней. Для data -- 0-999, gui -- 1000-1999, knobplugin -- 2000-1999.
Да, это халтурновато и какой-то сверх-расширяемости не обеспечит, но на данном этапе (и вообще для целей всей этой инфраструктуры) -- более чем достаточно; а если не хватит -- значит, надо переводить на реально объектную модель с совсем другими механизмами передачи событий.
cb==NULL
ничего не делаем
и возвращаем "success".
fastadc_knobplugin_ndcb_t
в
fastadc_knobplugin_evproc_t
-- теперь вся шобла имеет
унифицированные названия вида fastadc_LEVEL_evproc_t
.
(В начале "клиентов" manyadcs и two812ch теперь стоят проверки -- if(reason!=FASTADC_REASON_BIGC_DATA)return;.)
FastadcGuiEventProc()
для передачи
"наверх" РАЗНЫХ типов событий, а не только FASTADC_REASON_BIGC_DATA. Причём
и тип события он теперь передаёт, а не тупо 0.
CallCBs()
.
privptr
использовалось также для
передачи в mkctls. Теперь же, поскольку принят принцип "предок идёт в
начале структуры", на это забито и указатель на свой privrec все берут,
просто кастя к нему gui
.
(Да, это делается в рамках подготовки к возможности уведомлять об изменениях в реперах.)
14.05.2012@Снежинск-каземат-11: вводим уведомления об изменении состояния реперов.
FASTADC_REASON_REPER_CHANGE
.
gui_set_rpr()
(сдвиг) и
RprSwchKCB()
.
Модель с "методом" set_rpr() выказалась очень удобной.
06.10.2012@Снежинск-каземат-11: в
fastadc_gui.c::set_rpr()
-- в нынешней инкарнации --
был ляп: после действия при x<0
отсутствовал "else" и оно
оставляло репер включенным, да еще и со значением -1.
И вот тут вопрос: чтоб не плодить два формально РАЗДЕЛЬНЫХ knobplugin'а (каждый со своим sid'ом) -- может, как-нибудь стандартизовать прямо на уровне fastadc_data специальный "фэйковый" способ добычи данных, сосанием их с другого knobplugin'а (как это делает twi812ch)? Ключевым словом может быть "mirror". Вопрос лишь, как бы сие реализовать.
cda_hlt_server()
.
17.02.2012: сейчас есть недоинкапсулированность:
используются running/oneshot уровнем data, а меняет их -- main,
причем напрямую; ну и FastadcBigcEventProc()
.
Так что --
FastadcDataSetRunMode()
.
Она принимает на вход и running, и oneshot, но значение <0 означает
"оставь текущее".
Оно делает либо run, либо hlt sid'у.
ЗАМЕЧАНИЕ: оно влияет ТОЛЬКО на bigc_sid, chan_sid оно НЕ трогает.
FastadcDataInit()
добавлено уставление обоих
sid'ов в CDA_SERVERID_ERROR
-- чтоб не пытаться сделать
run еще несуществующему sid'у.
FastadcBigcEventProc()
переведена с прямого
уставления oneshot=0.
DoConnect()
теперь run делается также
условно.
Кстати, а нет ли некоторой кривизны -- что при нажатой паузе ручки, отмаппированные на скалярные каналы, будут продолжать обновляться?
18.02.2012: формально, кривизна есть и как бы полезно бы замораживать обновление и скалярных ручек тоже. Но:
Так что -- фиг с ними, оставим как есть сейчас.
18.02.2012: проверено Роговским, работает как положено. Был прикол с краснеющей лампочкой соединения, но он пофиксен, так что "done".
21.03.2012: собственно, на уровне Xh_plot это уже
возможно, а вот в fastadc_gui -- пока никак, так что делается
"вручную", см. конец
manyadcs_knobplugin.c::SetLineKind()
.
Реально, ГЛАВНЫЙ вопрос -- как оформить GUI для изменения этих параметров:
15.08.2012: побудительный мотив: по ошибке nadc200/remdrv были натравлены "не на тот" cm5307/ppc (ipp вместо podval). В результате они там пытались что-то как-то читать, но, естественно, нифига не получалось. А на экране эта проблема вообще никак не была заметна. При отображении же статуса -- стало бы ясно намного раньше (хотя бы по CAMAC_NO_X).
Как показывать? Можно аналогично строчке State в knobprops. Или -- фоном, как в ndbp?
16.08.2012: да, НАДО ФОНОМ. Для этого придётся
ввести некую инфраструктуру указания "текущего bkgd_GC", а наборчик
добывается через AllocStateGCs()
-- который (как и
состояния) на уровне Xh, конечно, недоступен, поэтому трансляцией
состояний в фон будет заниматься уровень fastadc_gui.
17.08.2012: сделано, хотя и несколько отлично от проекта.
AllocStateGCs()
не используется.
XhPlotSetBG()
, являющимся просто переходником к
XhViewportSetBG()
.
bgkdGC
(аллокацией
нового и release старого) и уставляет XmNbackground всем затронутым
виджетам.
Но для синтетических (FASTADC_B_ARTIFICIAL) сразу ставится COLALARM_NONE, т.к. их обновление идёт в обход обычной цепочки EventProc'ев, да и понятия "приход данных" у них формально нет.
Реализация несколько неполная, т.к. ни расшифровка статуса, ни список флагов (строки State, Flags) никак не отображаются. Однако текущая базовая проблема решена, так что помечаем за "done" и далее будем допиливать по мере надобности.
26.10.2012@Снежинск-каземат-11: деяния (синхронно в fastadc/, pzframe/ и yzframe/):
readonly
добавлено перед maxfrq
.
Main()
"-readonly" превращается в
просто "readonly" -- причём халявно: argv[n]++
.
ParamKCB()
/RgParKCB()
просто ничего не
делают.
Прям хоть в API CreateSimpleKnob()
вводить
дополнительный параметр "flags" (позволяющий форсить READONLY).
29.10.2012: да, доп. параметр (только "options") введён, и теперь в режиме readonly передаётся флаг и ручки-параметры централизованно делаются display-only. Работает.
30.10.2012: и добыча флага от Chl'я тоже сделана
-- через ChlIsReadonly
.
06.11.2012: есть некая пикантность: в режиме readonly кнопки [Shot] и [Stop] не нужны вовсе -- даже в ro-виде.
Можно, конечно, вводить условия в каждый *adc*_gui.c, чтоб при readonly такие ручки бы не делались. Но это муторно и кривовато.
Лучше повесить этот функционал на
fastadc_paraminfo_t.param_type
и
fastadc_gui_mkparknob()
.
Так и делаем -- вводим FASTADC_PARAM_RW_ONLY_MASK
, это
ФЛАГ (в отличие от прочих), поэтому обращение со значением
param_type
сделано слегка халявновато. В описатели
*adc*_data.h также добавлено, работает -- кнопки исчезают.
13.11.2012: поскольку явно работает, то считаем за "done".
Некрасивенько сделано, но работает.
21.05.2013: протокол:
ShowRunMode()
и SetRunMode()
, плюс их
использование (всё адаптировано к параметру gui
вместо
глобальности).
SetMiniToolButtonOnOff()
,
для отражения текущего статуса "нажатости" кнопочки.
Итого -- работает! "done".
Единственная некрасивость -- при несколько-раз-подряд кликаньи по
[|>] у неё перепутываются bg/armed. Причина -- данные приходят в
момент нажатости мышью, и перепутывается инвертирование программой и
Motif'ом (кстати, на обычном тулбаре в _main.c тоже). Посему пока
попытка отражать состояние "ждём один приход данных..." убрана (в обоих
местах). 10.06.2013: ага, только в тулбаре еще
надо было и тип кнопки сменить с TOOLCHK
на
TOOLCMD
, а то её сам тулбар инвертировал.
04.10.2013: а вот как бы сие можно сделать:
Либо же принудительно требовать наличия рядом с плагином invisible-ручки-"дирижёра", которая б любым удобным способом добывала информацию, и сбагривала б её плагину уже по стандартному внешнему API. Но нет, это плохой способ -- даже standalone-утилиты *adc* должны быть способны к такому частото-учитыванию.
FastadcDataSave()
: все ли числа
там разделяются пробелами? А строка "0"?
21.05.2014@Снежинск-каземат-11: мда, там
Когда это (1) появилось -- вопрос. Возможно, код еще от старых клиентов вроде nadc200.
Исправлено уже в pzframe/fastadc_data.c, тут оставим как есть -- zastadc/ ныне на увольнение.
22.05.2014@Снежинск-каземат-11: проверено (на тестовом примере: сохранение искусственного manyadcs, с одной линией в 1024, а второй в 500) -- теперь пишет корректно.
Неужто где-то по ходу вычислений происходит переполнение в 32 битах?
22.05.2014@Снежинск-каземат-11: разобрался -- неа, проблема
оказалась в способе вычисления в UpdateRepers()
: оно там само
напрямую вызывало x2xs() от числа "x", в качестве которого для репера "d"
используется разность x'ов 1-го и 2-го реперов. Но ведь x2xs() учитывает
PTSOFS, чего в данном случае делать совсем не надо, вот и получалось из-за
того прибавления дурное число.
А правильно -- вычислять через x2xs() xs'ы от реперов 1 и 2, а для "d" брать разность прямо этих xs'ов.
Исправлено в pzframe/fastadc_gui.c, в zastadc/ забъём.
ЗАМЕЧАНИЕ: в новом fastadc_type_dscr_t
теперь вместо
УКАЗАТЕЛЯ на parframe_type_dscr_t (как было в старом
fastadc_gui_dscr_t
) лежит сама структура (ибо
"наследование").
А вот всякие *gui должны будут содержать именно УКАЗАТЕЛЬ -- поскольку там другая, ПАРАЛЛЕЛЬНАЯ иерархия.
fastadc_data <- pzframe_data v v fastadc_gui <- pzframe_gui v v fastadc_knobplugin <- pzframe_knobplugin
Спрашивается -- какая из них должна быть ПЕРВИЧНОЙ, т.е., содержать в каждом наследнике поле ДАННЫХ родителя, а какая -- ВТОРИЧНОЙ (параллельной), т.е., содержать поле УКАЗАТЕЛЯ на данные?
Например, fastadc_gui: понятно, что он содержит в себе поле fastadc_data_t (больше негде). А вот поле pzframe_data_t в ком должно быть -- в fastadc_data, или же в pzframe_gui?
И второй аспект этого же: кто должен в "цепочке наследования" методов (типа инициализации) вызывать "родительский" метод, а кто -- только делать свою работу, полагаясь на то, что предка вызовет параллельная иерархия (а тут могут быть сложности, связанные с порядком вызовов)?
03.09.2012@Снежинск-каземат-11: всё это несколько напоминает множественное наследование (C++ 2.0+).
fastadc_gui_t
будет располагаться объект
pzframe_gui_t
?
В этом смысле представляется разумнее как бы "порвать" вертикальные связи в правой колонке, держа там УКАЗАТЕЛИ.
В таком случае всё из правой колонки будет выполнять как бы "консультативные" функции, возясь лишь со СВОИМИ полями, но НЕ дрыгаясь глубже.
Короче -- сейчас выбираем ВТОРОЙ вариант, когда "использующая" (левая, fastadc) иерархия первична, а "используемая" (правая, pzframe) вторична.
04.09.2012@Снежинск-утро-в-805-на-полигон: а может, это всё часть некоей более общей модели, и проблемы -- от непонимания её (традиционно :)), а при понимании всё стало бы просто, стройно и очевидно.
04.09.2012@Снежинск-каземат-11: если хорошенько подумать, то
"второй" вариант является неправильным -- с точки зрения объектной модели
это fastadc_*
наследуются от pzframe_*
, и
инициатива должна быть у pzframe_*
(а те уж чтоб вызывали
методы из наследников).
Ошибка же (сколько месяцев назад?) была в том, что наследуется knobplugin(gui(data)))! Тут должно быть не наследование, а "ссылание".
Т.е., надо наследовать по горизонтали, а по вертикали ссылаться. Так что -- вертикальное наследование рвём. Усовершенствованная картинка будет выглядеть так (она отзеркалена по вертикали, чтоб отразить смену идеи с "наследования" (когда корень обычно рисуют сверху) на "уровни иерархии" -- более низкие снизу):
fastadc_knobplugin (pzframe_knobplugin) v v fastadc_gui (pzframe_gui) v v fastadc_data (pzframe_data)
Ссылки в левой и правой колонках -- это одно и то же, поскольку они есть ссылки из объектов разной степени наследования на "параллельные" (сопутствующие) объекты соответствующей степени наследования.
Замечания/комментарии:
fastadc_data_t
-- поле в fastadc_gui_t
, а
fastadc_gui_t
-- поле в fastadc_knobplugin_t
),
fastadc_data_t
-- в дереве "данных" (в будущем в Cdr), а прочие на неё лишь ссылаются
(причём -- могут ссылаться МНОЖЕСТВЕННЫЕ: два экранных графика на один
объект "данные графика").
pzframe_type_dscr_t
первым полем в
fastadc_type_dscr_t
-- существенно сокращается количество
указателей, поскольку ссылки на "предка" и "потомка" становятся
взаимозаменяемыми.
Делаем поля-методы прямо в структуре.
Делаем. Работы временно ведём в соведней директории yzframe/, файлы получают названия yzframe_*.[ch] и xastadc_*.[ch].
18.12.2013: давно пора доделывать новую версию pzframe, так что уже несколько дней как анализируем имеющийся результат разделения (более чем год назад).
pzframe_data_t
единого
поля "mes" нету, только отдельные mes_*
; а в новом
fastadc_data_t
-- есть и mes, и svd.
22.12.2013:
pzframe_data_t.mes_*
заново
вводим единое поле mes
и его тип
pzframe_mes_t
. Смысл -- чтоб удобно атомарно ссылаться на
измерение (и копировать в svd при надобности).
Аналогично, теперь в fastadc_mes_t
будет ссылка на
таковую (вместо сделанной 09-2012 info_p
), названная
inhrt
. 28.12.2013: ага -- только
ПРОПИСЫВАНИЕ этой ссылки никем не делалось. Добавлено в
FastadcDataInit()
.
(Вылезло при попытке сделать nadc200_info2mes()
--
данных-то для ссылания на них нема!)
Очевидно, это такое хитрое наследование fastadc_mes_t(pzframe_mes_t), только из-за хитростей расположения (наследуемый располагается в другом объекте) вместо включения сделано ссылание.
Кстати, вопрос -- а насколько оно так корректно, или же надо как раз в fastadc делать нормальное наследование, а pzframe'у отдавать ссылку?
23.12.2013:
change_important
-- битым текстом: хбз, с какими
конкретно мотивами оно ТОГДА вводилось.
А вот СЕЙЧАС анализ старого и нового кода показал, что таковые
параметры (с !=0) приводят к уставлению info_changed
,
которое в конечном итоге отражается на обновлении осей (axis) у plot'а
-- XhPlotUpdate(with_axis=1)
.
Соответственно, для fastadc'ов надо change_important=1 ставить
change_important
переставлено ПЕРЕД
param_type
-- поскольку оно просто 0/1, а не длинные
константы, то так табличка выходит читабельнее (в
pzframe_param_info_t
симметрично).
pzframe_cfg_t
--
int32'шный массив param_init[]
пришлось переделать по
образу старого, в поле param_iv
имеющее struct-тип
pzframe_params_t
.
Во-первых, иначе дюже громоздко выглядели PSP_-описания. Во-вторых
они были бы слишком интимно привязаны к pzframe_cfg_t
, а
сейчас просто массив (пусть и впихнутый в struct ради psp).
MakeStdCtl()
.
ЗАМЕЧАНИЕ: надо прототип делать общий в yzframe, а
реализации разные в xastadc и vcamimg. И передавать ВСЕМ надо
указатели на наследуемые pzframe_gui_t
, а реализации уж
пусть сами -- через вычитание OFFSETOFF()'а -- пусть приводят к своему
типу-наследнику.
24.12.2013: так и сделано, и
MakeStdCtl()
переехал обратно в
yzframe_cpanel_decor.h. И теперь мысль -- не сделать ли
следующий шаг, не обобщить ли аналогично и
*_mkctls_t
? Там будет аналогичный прикол с
"наследованием" *_gui_dscr_t
. Но главное, что пока от
этого останавливает -- неясность с самой концепцией mkctls, с учётом
возможного варианта CPANEL_AROUND.
24.12.2013:
Значит, надо делать всё исходя именно из этой парадигмы: чтобы _data были "для Cdr" (а _gui/_knobplugin -- для Chl/Knobs).
Тогда, кстати, pzframe_data_t/fastadc_data_t будут жить не внутри fastadc_gui_t, а прям в Cdr/datatree-дереве, что снимет вопрос "где физически должны жить какие поля?!" и сделает общую архитектуру стройной.
Итого, строим всю структуру исходя из двух базовых принципов:
25.12.2013:
fastadc_type_dscr_t
и
fastadc_gui_dscr_t
имелась изрядная неясность -- как это
вообще всё организовывать, в свете разделения на компоненты
pzframe<-fastadc/vcamimg. Так что в 09-2012 ничего сделано не было,
и даже параметры именовались "void *zzz".
А сейчас, поразмысливши -- да так же с ООП-наследованием!
*_type_dscr_t
в
*_data_dscr_t
Подумавши, всё взвесивши -- неа, не стоит. Переименовать было бы симметричнее, но ТАК оно уникальнее, да и сокращается до "ftd" (atd,vtd), а то что будет -- "fdd" (add,vdd)?
Вопрос только, как с ВЕРТИКАЛЬНЫМИ связями (кто когда кого будет вызывать) -- ведь dscr'ы есть как у data, так и у gui.
26.12.2013:
Поскольку у нас ООП лишь имитируется, то придётся как-то и
заполнение тоже имитировать? Макросами, "возвращающими" структуры
(точнее, списки инициализации)? Парой часов позже: нет, так
нельзя -- для доступа макросами придётся всем потрохам торчать наружу.
Скорее тогда уж методы с именами вроде
get_*_dscr()
, возвращающие указатели на статические
переменные. А как они у себя внутри проинициализируют -- их личное
дело.
pzframe2fastadc_type_dscr()
и
pzframe2fastadc_gui_dscr()
.
FastadcFillDscr()
, pzframe-часть работы
которой выделена в PzframeFillDscr()
, вызываемую точно как
"конструктор предка" -- в самом начале.
privrec_t
станут просто
fastadc_knobplugin_t
, ...
27.12.2013:
fastadc_gui_t
можно было
разместить прямо в fastadc_knobplugin_t/fastadc_main_t, то теперь --
никак. Возможные варианты решения:
По соображениям простоты выбран вариант 2.
28.12.2013:
pzframe_gui_dscr_t.update
и временно
реализован в тестовом nadc200.c.
Шоб я еще раз возился с такими вещами вручную, вместо реализации на адекватном задаче языке (уж GUI-то -- самая то задача для ООП!).
30.12.2013:
Т.е. -- некоторый винегрет.
pzframe_gui_t
были "для
Inheritance" введены callback'и child_evproc
,
child_newstate
, child_do_renew
, плюс
pzframe_data_t.child_evproc
.
Вот не факт, что это правильный путь: ведь оные callback'и определяются на уровне конкретного ЭКЗЕМПЛЯРА, а должны бы на уровне КЛАССА (пожалуй, именно "класса" в вышеупомянутом смысле, т.е., fastadc/vcamimg).
FastadcGuiInit()
. Потом, возможно,
сделаем "правильнее".
Как? Наверное, по аналогии с идейкой от 27-04-2004 о вычитывании конфигурации в глобальные переменные: указывать rec=NULL, а в качестве offset'ов указывать прямо указатели на под-структуры. Но проблема с 64-битностью... (Вот уж где б пригодился парсинг в разрозненные дуплеты таблица/структура!)
31.12.2013:
PzframeMain()
его использует -- ура!
Кстати, возможно, пора избавиться от pzframe_main_t
myrec
-- раз уж всё равно единая структура более не требуется --
и парсить main_opts сразу в переменную такого типа.
02.01.2014: сделано.
02.01.2014: возимся с парсингом:
FastadcDataCreateText2DcnvTable()
практически
скопирована из старой (с минимальными изменениями -- теперь сама
fastadc_dcnv_t
стала парсимой единицей).
FastadcGuiCreateText2LookTable()
, с некоторой
оптимизацией, и из неё...
pzframe_gui_text2look[]
.
04.01.2014: возимся с knobplugin (начато еще вчера):
PzframeKnobpluginInit()
переименована в
PzframeKnobpluginDoCreate()
, поскольку это НЕ
"init" в том смысле, как у data/gui, а именно "create" в смысле knobs.
FastadcGuiFillStdDscr()
(своего рода
"конструктор").
Так что те наследники, которым потребуется что-то менять (как manyadcs_knobplugin в отношении realize), запросят заполнение стандартной VMT и поменяют в своём экземпляре что надо (при надобности куда-нибудь сохранив указатель на inherited-метод).
06.01.2014:
В порядке проекта-идеологии:
Делаем:
25.05.2014@Снежинск-каземат-11: добавлена
PzframeGuiAddEvproc()
-- собственно
PzframeGuiCallCBs()
была и раньше, но никакой возможности
уставить callback не было. По идее, там как-то должно было вызываться
"через другую ветвь" -- то ли через _data, то ли через _knobplugin
(но у этого что-то поназапутано -- реально
pzframe_knobplugin_evproc_t
содержит pzframe_data_t
*pfr
-- идентично pzframe_data_evproc_t
; хбз как это всё
работает...), но нет -- так что фиг с ним, сделаем просто чтоб
давало результат, лень разбираться со всеми хитросплетениями этой хрени.
Заодно FastadcFillDscr()
переименована в
FastadcDataFillStdDscr()
и переведена из inline в обычную
функцию, чтоб правильно производить заполнение VMT.
07.01.2013: и порядок параметров слегка поменян --
чтоб dtype
и num_params
были вместе сразу
после behaviour
, для унификации с vcamimg.
Кстати, метод "filter" отвязан от специфики Xh_fdlg, через
функцию-адаптер PzframeDataFdlgFilter()
, и теперь получает
сразу ссылку на pfr.
07.01.2014:
adc
->vci
(VCamImg),
atd
->vtd
. 30.07.2015:
был нефатальный ляп: в vcamimg_{data,gui}.h оставалось
#include"plotdata.h"
.
kdc
(KozakDaC) и ktd
(KozakTypeDescr).
08.01.2014:
10.01.2014: доводим _knobplugin -- внешний сервис, для "соседей".
(И незачем тогда маяться с подменой gui'шного метода.)
16.01.2014: запинываем под новую архитектуру содержимое liu/y/, теперь вернувшееся (через 3 года!) в liu/xmclients/.
PzframeKnobpluginGetGui()
-- в
интересах manyadcs, который показывает микропервеанс с учётом положения
реперов в другом GUI.
PzframeKnobpluginDoCreate()
порядок
вызовов разных "realize": теперь сначала вызывается kp_realize, а потом
уж gui'шный. На такое уж был рассчитан manyadcs -- иначе давал SIGSEGV
вследствие неинициализированности ссылок на "источники".
MANYADCS_privrec_t
из прочих, типа gui -- пришлось
помучиться. Но вроде с нынешней архитектурой (раскладкой полей по
структурам) всё неплохо получается, хотя и громоздко и неочевидно.
Но вот с этого момента новую "pzframe" можно считать введённой в строй.
27.01.2014: добавлено поле
pzframe_gui_dscr_t.bhv
(behaviour) и пока что единственный
флаг PZFRAME_GUI_BHV_NORESIZE
, указывающий уставить окну
XhWindowUnresizableMask
. И в PzframeMain()
его использование, а в VcamimgGuiFillStdDscr()
--
уставление.
01.04.2014@Снежинск-каземат-11: проверено, новый клиент liu работает и показывает всё как надо (в two812ch был багчок, сути не касающийся).
03.04.2014@Снежинск-каземат-11: ага, "работает", как же! Падает непонятным образом в непонятном месте (стек портит, так что и gdb'шный bt ничего не кажет).
Так что старое удалять пока рано.
02.06.2014: причина тех падений найдена 14-05-2014 -- баг в Xh_plot'е, чьё срабатывание вызывалось багом в новом manyadcs_knobplugin.c (детали см. в 201403-SNEZHINSK-ACTIVITY.txt за ту дату).
Так что -- старое наконец-то удалено, и весь раздел наконец-то считаем за "done".
07.10.2014@Снежинск-каземат-11: модифицируем работу
PzframeKnobpluginRegisterCB()
: теперь он вешается не на
PzframeData, как раньше, а на PzframeGui.
PzframeGuiAddEvproc()
-- появившейся лишь 25-05-2014.
(В предыдущем юзере аналогичного функционала --
beamprof_knobplugin.c -- самостоятельно делалось
PzframeGuiAddEvproc()
отдельно добытому gui, что
криво.)
PzframeKnobpluginAddEvproc()
, а не "RegisterCB".
Но оставим как есть из-за следующего пункта...
Но именно pfr уже используется практически во ВСЕХ местах её вызова, так что оставим как есть.
Смысл -- чтоб драйвер сразу отдавал отконвертированные данные min/max/avg/sum. Хотя, это скорее уже для v4, тут-то вряд ли выйдет...
(Сейчас это компенсируется мозгами *adc*_data.c::x2xs(); но, например, для nadc333 всё равно отдаётся *1000.)
Смысл -- в некоторых случаях ставится внешнее таймирование, и размер такта добывабелен (снаружи через какой-нибудь канал СУ). В таком случае грех показывать просто "x".
FastadcGuiSetReper()
, в интересах liu'шного
beamprof_knobplugin.c, чтоб синхронизировать реперы двух gui, не
лазя в их внутренности.
Семантика параметров идентична оной у XhPlotSetReper()
.
15.04.2015@Снежинск-каземат-11: ...вообще-то корректнее,
наверное, конкретно в manyadcs_knobplugin.c отдельно проверять изменение
NUMPTS, и если изменилось, то ставить info_changed:=1; а можно так
проверить? Похоже, так проверять нельзя -- т.к. info_changed вычисляется в
pzframe_data.c::PzframeBigcEventProc()
, и там делается
копирование prv_info[]:=mes.info[] еще ДО вызова вышестоящих callback'ов.
...в принципе, копирование можно переставить в ПОСЛЕ вызова. Стоит ли?
Причина очевидна: в RgParKCB()
кроме
cda_setphyschanraw()
делается ещё и
cda_setbigcparams()
. Как следствие -- серверов механизм
проверки соответствия ответа запросу -- may_return_data()
--
считает, что ответ с 0 в ячейке STOP не соответствует запросу с 1 в этой
позиции.
Анализ показал, что каналы с флагом PZFRAME_PARAM_CHAN_ONLY
-- это у всех только SHOT, STOP и ELAPSED (этот вообще readonly).
Хоть v2'шному vcamimg/ottcam и осталось жить всего неделю, лучше бы всё же подправить (fastadc'ам-то существовать дольше, а там косяк тоже должен быть!): для каналов с этим флагом НЕ делать уставку параметра большого канала.
03.06.2016: да, вставлен if()
.
Проверено -- помогло.
Понять причину удалось на v4, но, т.к. там архитектура в точности такая же, а ошибка появилась в v2, то записываем здесь.
22.06.2016: по порядку:
И, собственно -- у Оттмара внутри просто нет памяти, в которой мог бы храниться предыдущий кадр.
А главное -- на [Stop]-то Оттмар как бы мог влиять?
Значит, косяк явно у меня.
VcamimgEvproc()
, а обновление экрана --
в DoRenew()
.
Но из них сначала вызывался DoRenew()
(показывая предыдущий
кадр), а уже потом VcamimgEvproc()
(как раз и "готовя" данные
для отображения на следующем цикле -- когда они и станут "предыдущими").
XhMonoimgUpdate()
, а вовсе не отдельно,
как было.
И в v4 тоже (реально -- именно там всё и было проделано, а в v2 уже бэкпорчено).
Возможно, было сделано по какой-то аналогии с fastadc (точнее, с его прошлыми версиями, т.к. сейчас fastadc_gui_std_pzframe_vmt.evproc=NULL).
VcamimgEvproc()
стал ненужен совсем, поэтому
он убран и vcamimg_gui_std_pzframe_vmt.evproc=NULL.
В качестве замечания: архитектура событий (как evproc'ев, так и прочих служебных) в нынешнем стеке {pzframe,fastadc/vcamimg}_{data,gui} шибко развесиста, разбираться в её работе -- а особенно в косяках! -- довольно сложно.
08.07.2016: сделано, практически тупым копированием.
1.07.2016: и в v4 перенесено (где оно отсутствовало из-за отсутствия в v2'шной, откуда копировалась). Только тут уже потребовались модификации -- API-то приподизменились.
15.02.2012: да, этот код весьма широкоприменябелен, для любых устройств с параметризованными кадрами -- а это не все ли устройства с большими каналами вообще?
И, напрашивается: как fastadc_data -- это уровень "a-la Cdr над cda" (201110-SNEZHINSK-ACTIVITY.txt@24-10-2011), так и fastadc_common_drv/parframe -- это аналогично де-факто стандартный код для работы с подобными сущностями на стороне сервера.
05.03.2012: а вот и не совсем так. Уже сейчас кроме "АЦП-подобного" использования больших каналов есть и другое -- табличные каналы в ЦАПах. А если еще вспомнить "запиши такое-то число на такой-то скорости изменения" -- то вообще куча вариантов получается.
Похоже, что для "больших" каналов дополнительно требуется классификация -- ПОВЕДЕНИЕ: АЦП-подобный, таблице-ЦАП-подобный, ...
15.03.2012: переименовываем "fastadc_common_drv" в "parframe_drv" -- ибо в связке с ottcam слово "fastadc" будет смотреться странновато.
Соответственно -- libparframe_drv.a, parframe_drv.c, parframe_drv.h.
Структурка -- parframe_drv_t
, префикс кодов результата
от "методов" драйверов -- PARFRAME_R_
.
21.03.2012: правильное место для этой библиотеки -- в
programs/server/drivers/, а не тут. Туда покамест повременим
(до перехода на GeneralRules.mk); пока ограничимся добавлением для
компиляции флага -I../../programs/server
(ради
cxsd_driver.h). 24.04.2012: в связи с
углобаливанием cxsd_driver.h этот хак с -I...
более не нужен и убран.
А описательный раздел пусть продолжает жить здесь, ибо тема близкая к прочим fastadc*.
10.04.2012: населяем, копируя из cpci_fastadc_common.h.
parframe_drv_t
возложена на
parframe_drv_init()
.
param_NNN
для адресации в
*_args[]
теперь защищено проверками, что соответствующий
параметр >=0 (т.к. при отсутствии возможности реализации функционала
соотв. параметр можно указать как -1
).
FASTADC_IRQ_P()
переехало в
parframe_drv_drdy_p()
-- драйвер должен будет её вызвать
при готовности данных для вычитывания, а уж реальную слежку за IRQ --
нехай сам организовывает.
Покамест скопирован только старый функционал, запланированной "машины состояний" еще нету.
30.07.2012: переименовываем "parframe_drv" в "pzframe_drv" и переносим его из lib/fastadc/ в lib/pzframe/.
29.08.2012@Снежинск-каземат-11: продолжаем.
pzframe_drv_term()
-- в основном приличия ради, пока он
просто таймаут снимает.
pzframe_set_buf_p()
-- указывает ссылку на
retdata
(в большинстве случаев должон вызываться прямо после
_init().
30.08.2012@Снежинск-каземат-11: по результатам прикидочной реализации первого не-локального драйвера -- ottcam:
void
-- без кода возврата.
ReturnMeasurements()
-- чтоб уж во всех случаях (включая
немедленную готовность и abort) подготовить данные.
Сделано:
do_return
, заполняемое сразу в
StartMeasurements()
-- дабы быть доступным для
FASTADC_IRQ_P()
.
Строго говоря, с отъёмом у ReadMeasurements() способности к прерывности модель несколько упрощается: в принципе, с некоторыми натяжками оно способно работать и так, БЕЗО ВСЯКОЙ МАШИНЫ СОСТОЯНИЙ. Теряем мы всякие тонкости: отложенное вычитывание текущих параметров и не-мгновенный abort.
07.12.2020: касательно наличия параметра
"do_return
" у _drdy_p()
-- он пригодился ещё, уже
не для калибровок, а для "искусственного таймаута". (Это уже в CXv4, но
неважно.)
CXRF_IO_TIMEOUT
, что нервировало Виталю Балакина.
И нервировало оправданно: карповское забывание -- специфика конкретной железки, и по-хорошему должна обрабатываться на уровне драйвера, а не выноситься юзеру.
pzframe_drv_do_stop()
, ...
PerformTimeoutActions()
и pzframe_drdy_p()
почти
идентичны -- в последней только отсутствует вызов
abort_measurements()
.
Так что не надо ничего добавлять, а достаточно в драйвере вызывать pzframe_drdy_p(,0,) (при надобности тормознув устройство), что и было рекомендовано Роговскому.
Так что -- мои аплодисменты самому себе, pzframe_drv сделан очень хорошо и оптимально, ибо покрывает все возникающие потребности.
09.12.2020: поправка от Роговского: "ибо ПОКА ЕЩЁ покрывает все возникающие потребности". :D
30.08.2012@Снежинск-Снежинка-307-вечер: мысли:
#include
куска реального кода, всё же кривовата и некрасива,
01.09.2012@Снежинск-Снежинка-307-вечер: строго говоря, в pzframe_drv зашита логика работы не только (или не столько?) с параметризованными измерениями, а с измерениями с внешним запуском -- поскольку ровно эта же схема отлично подходит и для u0632 и karpov_monster. (Отличия -- отсутствие программного запуска (его в самих железках нет) и другая работа с параметрами (но pzframe_drv'шная -- корректней).)
26.09.2012: Проверен pzframe_drv -- работает.
02.11.2012: добавляем еще один стандартный параметр -- "elapsed" (время с момента начала ожидания измерения). Оно явочным порядком присутствовало с давних времён ("Treat all channels above NUMCHANS as time-since-measurement-start"), а сейчас легитимизируем.
Сделано -- во всех. И в nxt_args[] значение перманентно
поддерживается =0. И во все _drv_i.h добавлено (включая
ottcam), и в *adc*_data.h::*_param_infos[]
оно
вставлено (r=1000, CHAN_ONLY). Только в GUI нигде пока не выведено --
ибо неясно куда.
19.07.2013: представляется недостаточным
возможность указывать rflags только в вызове
pzframe_drv_drdy_p()
: ведь что-то может быть определено
уже по самим данным в момент их вычитывания -- типа OVERLOAD (и nadc333
это умеет!). Так что надо б выдать ReadMeasurements()
'у
возможность возвращать флаги для дополнительного OR'енья.
20.07.2013: сделано.
30.01.2014: pzframe_drv_init()
:
убрано bzero(pdr, sizeof(*pdr)), и заменено на ЯВНУЮ инициализацию всех
полей -- кроме cur_args[]/nxt_args[]. Причина -- идеологическая:
поскольку многие драйверы могут использовать в своих paramtable'ах
возможность указывать начальные значения параметров, ссылаясь на
pz.nxt_args[n], то этот bzero() всё портил.
18.01.2013: побудительный мотив: а что, если некий fast-АЦП может после измерения отдать не только весь массив, но и просто некие характерные числа (например, среднее значение, минимум и максимум). Тогда при высоких частотах (>10-100Гц) вычитывание массива сильно бы тормозило, а вот просто пара-тройка чисел -- прекрасно дадут сколь угодно большие Гц (сам-то прибор всё успеет). Для CCD-камеры аналогичным функционалом может быть, например, отдача самой железкой координат "центра тяжести" пятна.
Идея в том, чтоб пойти по стопам tsycamv_drv (имевшего 3 больших-канала-alias'а, и после измерения возвращавшего лишь те, что были запрошены). А именно:
Потенциальные сложности:
Может, по приходу такого запроса сохранять параметры в "nxt_args", а при окончании измерения молча запускать измерение заново, но уже с флагом "запрошено большим каналом, а не стат.параметром"?
Всё равно как-то мутновато. И еще -- надо сразу рассчитывать на будущую схему работы со СВЯЗАННЫМИ каналами, чтоб и в ней тоже всё было корректно.
Только эти части никак не интегрированы -- в отличие от 4cx, где они (предположительно?) будут пользоваться одним и тем же модулем разбора пакетов и вообще взаимодействия с хостом.
Делаем в lib/gem, include-файлы -- gem*.h ('g' -- картавящее 'r'; и ни include/g* других нет, ни lib/g*).
21-10-2012@Снежинск-каземат-11: Общая идеология:
Мысли по теме:
...а может и правда поступить как с cxsd_driver.h -- иметь ОДНО определение (cxsd_*.h) и РАЗНЫЕ реализации?
22.10.2012@Снежинск-утро-в-Газели-на-полигон: О -- remcxsd?
22.10.2012@Снежинск-каземат-11: да, так и поступим. Краткое резюме проекта:
Доп. соображения:
@вечер: да, надо всё делать в одной директории. Ибо всё это -- СРЕДЫ ИСПОЛНЕНИЯ драйверов, просто чуть разные их варианты. 12.11.2012: see bigfile-0002.html, 09.02.2012 :-)
23.10.2012@Снежинск-каземат-11: так что пока gem* грохаем.
Основные работы постараемся вести в 4cx/src/lib/srv/.
01.07.2013: в связи с переходом на uniq и устранением "oslike" надо еще раз оценить объединение или разделение srv/ и rem/. Теперь между ними значительно меньше пересечений, и вопрос, насколько имеет смысл унификация.
17.07.2013: да как-то не особо видны общности...
Посему пока будем вести работы в cx/src/lib/gem/, исключительно над удалённостями, рассчитывая и на совместимость/портабельность в v4, а если вдруг стукнет, что с обычным cxsd есть что-то общее -- то поразмыслим насчёт объединения (а пока главное -- делать непротиворечаще такой перспективе).
18.07.2013: потихоньку делаем. Промежуточное действие -- устранена старая пустая lib/rem/libremdrvlet.a.
И, собственно, новая libremdrvlet (состоящая из одного специфичного
remdrvlet_main_v2.c и общих remcxsd*.c) сделана. Под
неё слегка подпилено в v2hw/drivers/camac/cm5307_ppc_drvlets/
-- собирается (хотя поддержка LAM-via-pipe пока не реализована);
файл cm5307_camac_DEFINE_DRIVER.c остался пустышкой, служащей
теперь для определения camac_fd
. Заодно пришлось
pzframe_drv.[ch] перевести на cxscheduler.
19.07.2013: ...и LAM'ы там сделаны, реализация
прям в самом cm5307_camac_DEFINE_DRIVER.c --
WATCH_FOR_LAM()
.
И в camac_fastadc_common.h его использование добавлено.
30.07.2013: работа libremdrvlet проверена на ppc-sukhanov -- все драйвера пашут.
01.08.2013: libremsrv (файл remsrv2_drvmgr.c) также предварительно "припилена":
CxsdDriverModRec
'и,
и при поиске драйвера используются прямо поля .mr.name
.
"Примерное" соответствие потому, что таблица простая, и предоставить её
должна непосредственно программа-юзер (т.е., никакого сотрудничества от
чего-нибудь в стиле "plugmgr" нету).
15.04.2015@Снежинск-каземат-11: вопрос -- а КАКИЕ концепции? Давно об этой записи помню, но вот сейчас совсем неясно, что же такого ценного было в 4cx'ной rem/, чего нету в нынешней v2-cx'ной (разве что rrund.c на основе remdrvlet_listener, а не своей реализации всего).
16.04.2015@Снежинск-каземат-11: да, поанализировал исходники -- стало ясно, что
19.06.2015@вечер-ванна: с учётом поддержки prefix+suffix, конечно, хотелось бы перейти на тамошнюю версию. Но трудоёмко, а хочется иметь в v4 работающую rem-инфраструктуру "прям щас" -- скопировав всё из v2.
Но можно ж просто тупо добавить поддержку prefix+suffix, это почти ничего не стоит!
21.06.2015: добавляем (идеологически -- максимально притягиваем к "той" v4'шной версии).
Run()
теперь использует эти параметры.
Что странно -- gcc-2.96 выдаёт warning'и
-- в отличие от v4'шного кода. Хотя на вид они аналогичны.warning: variable `prefix' might be clobbered by `longjmp' or `vfork' warning: variable `suffix' might be clobbered by `longjmp' or `vfork'
И мелкая модификация: если запрошенное имя абсолютное -- начинается с '/', то prefix+suffix игнорируются.
В общем, теперь плюсом старой v4'шной осталась та "элегантность реализации". Пока постараемся пережить...
Сейчас-то неохота, ибо даст вроде лишнее усложнение, но мало ли, что потребуется в будущем...
06.08.2013: более интенсифицирован логгинг на консоль -- и содержимое businfo теперь выдаётся, и по максимуму всё префиксируется [DEVID].
13.08.2013: сюда же переселён rrund.c -- из предназначенной под снос старой rem/.
(Его б надо еще как-нибудь в кросс-директориях собирать...)
13.08.2013: старая rem/ отправлена в ARCHIVE/, а gem/ переименована в rem/.
06.08.2013: проверяем на живой аппаратуре. Результаты:
FreeDevID()
НЕ
вызывало term_dev()
; в результате повторный (после
рестарта сервера) запрос драйвера приводил к ошибке "device (0,2)
already in use".
Это просто тупо было забыто при копировании из старого кода.
04.09.2013: выяснилось, FIFO+ответы-на-всё, см. другой комментарий за сегодня.
17.08.2013: процедурное: в ЭТОМ "общем" разделе дообсуждаем только те проблемы, что уже затронуты, всё же касательно новых фич и развития -- будет в подразделах remsrv/remdrvlet/remcxsd.
25.09.2014: было забыто сделать
LIBREMSRV
и LIBREMDRVLET
в
TopRules.mk. А понадобилось для v2sktcanserver'а. Кстати,
оный работает -- т.е., remsrv под обычной x86_64 пашет.
Поскольку всё хорошо, то "done".
30.09.2011@Снежинск-каземат-11:
Первоначально была мысль сделать аналогично v4'шной lib/rem/, но там уже слегка сильно совсем по-другому, так что обойдёмся без излишней работы -- простейшим разделением с переименованием.
07.10.2011: продолжаем.
12.10.2011: добиваем.
Ну и фиг с ним -- в ЭТОЙ версии не станем улучшать.
remsrv_conscmd_proc_t
-- обработчик конкретной
консольной команды, которому передаётся, в частности, ...
remsrv_cprintf_t
-- указатель на функцию,
исполняющую Limited_fdprintf() в нужный дескриптор. Для указания
дескриптора обработчику передаётся void *context
, который
он должен передать печатателю.
remsrv_conscmd_descr_t
; они собираются в
.name=NULL-terminated массив.
remsrv_clninfo_t
-- по указанному devid возвращает
указатель на строку, содержащую расширенную информацию по соотв.
устройству; это переходник к CanKozGetDevInfo()
.
remsrv_main()
теперь, в дополнение
к argc,argv[], передаются:
prog_prompt_name
-- что писать в prompt'е перед
@IP ("canserver").
prog_issue_name
-- что писать в issue ("CX CANGW
canserver").
prog_cmd_table
с prog_clninfo
.
SetDrvmgrEnvironment()
.
exec_ff()
плюс описатель
canserver_commands[]
, а также
canserver_clninfo()
.
FindCommand()
.
07.08.2012: в ней была допущена дикая халтура: вместо
if(strcmp(...)==0)
стояло if(strcmp(...))
. В
результате оно ЛЮБУЮ команду считало за "ff" (кроме неё самой).
drvmgr_do_printf()
. И, поскольку
Limited_fdprintf()
принимает ...
, а нужен
v-вариант с va_list, то...
Limited_vfdprintf()
.
Одиночная реализация консольного интерфейса в canserver'е была очень проста, превращение же её в открытую с API для разных программ заняло времени едва ли не больше, чем то первоначальное изготовление. Вот так...
Теперь осталось проверить.
08.11.2011: ага, "добил", как же!!! В
remsrv_drvmgr.c оставалось
#include"cankoz_pre_lyr.h"
, из-за невыкошенного
"ff"=>CanKozSend0xFF(). Это исправили.
Далее, начал испытывать, пока что на cxv2moxaserver без кросс-компиляции -- ох что там полезло!!!
C nc -- всё OK...
Вывод: надо толерантнее быть, например, воспринимать ЛЮБОЙ из этих символов (ну да, пустые строки будут -- ну и хрен с ними).
Сделал -- забавно выглядит telnet'ная сессия (prompt'ы двоятся), но зато работает.
02.04.2012: ЗАМЕЧАНИЕ: это всё уже в новой, remsrv-based версии в nppc_canserver/; в старой в ppc_canserver/ остался глючный код.
03.04.2012: от дублирования можно избавиться очень просто: помнить предыдущий символ, и если он -- '\r' (на который мы уже отреагировали), а нынешний -- '\n', то нынешний символ просто игнорировать.
07.11.2012: да, так и сделано -- LF сразу после CR'а игнорируется. Двоение в telnet'е исчезло.
Оказалось, что в ReadConsoleCommand()
cmdbufused++
делается только при складировании не-'\n', а '\0' оно
пишет в [cmdbufused-1] -- тем самым зануляя не Enter, а последний
символ. Ну ладно, пофиксил, но...
...самое странное -- что код-то скопирован из canserver_drvmgr.c, а там он вроде как работал! Причём именно такой код присутствует в BACKUP/cx_src.20091030_regular/, притом, что фича введена всего 02-06-2009 -- т.е., этот код и был с самого начала (а не поменялся позже), и именно он везде пашет.
02.04.2012: вообще-то ясно -- два вышеперечисленных глюка взаимокомпенсировались: оно принимало команду на 1 символ короче, но при последовательности КОМАНДА\r\n перетирался как раз '\r', что и давало правильную команду. Единственная реальная загадка -- почему telnet иногда шлёт \n, а иногда -- \r\n? Проверить Ethereal'ом?
07.11.2012: сходу Ethereal никакого "договаривания" не показал, а шлётся вроде ВСЕГДА \r\n. Просто \n шлётся не telnet'ом, а nc.
sl_add_fd()
стояла маска 1
-- позаменял на
SL_RD
.
GetCurrentCycle()
. Точнее, просто вызывалась, но
складируемый в privrec результат так никогда и никак не применялся
(видимо, была идея спрашивать статус не чаще раза в цикл). А в
drivelet-API оной функции нет. Ну и закомментировал оба вызова.
17.12.2011@Снежинск-Снежинка-холл: нашелся еще один прикольчик
-- тут НЕ делалась автоматическая регистрация дескриптора, возвращаемого из
init_blk()
(по fd>0). Просто этот код растёт из
ppc_canserver'а, а у CAN-драйверов сей устаревший способ неактуален (в
отличие от старющего kshd485_src.c).
Так что, ради общности -- чтоб могли работать и другие серверовы драйверы
-- в remsrv_drvmgr.c::ProcessPacket()/REMDRVP_CONFIG такая автоматическая
регистрация добавлена (скопировано из
cxsd_drvmgr.c::InitBlock()
).
В остальном -- на Moxa вроде работает.
22.12.2011@Снежинск-каземат-11: очевидно, что надо превращать эти remsrv* в отдельную библиотеку, живущую в lib/rem/. Полностью аналогично 4cx'ному, и чтоб для всех кросс-архитектур оно бы симлинковалось в тамощние директории аналогично прочим lib*.
И оно, по этой же аналогии, должно состоять из ДВУХ частей:
Конечно, ту парочку и её пользователей надо будет подпеределать:
FDIO_LEN_BIG_ENDIAN
в пользу
определения endianness.
Короче -- все работы с sl_dbody/remdrvlet_[ch].h перевести в lib/rem/.
Проблема же будет исключительно в наличии РАЗНЫХ версий driver-API, в т.ч. определяемых в разных местах.
14.05.2012@Снежинск-каземат-11: маркируем "done".
12.11.2012: для профилировабельности на CANGW
добавлена консольная команда "terminate", делающая
exit(0)
. Она заключена в
#ifdef REMSRV_DRVMGR_ALLOW_TERMINATE
Смысл -- для "быстрых" синхронных действий:
А вот не факт: оно ведь тоже использует SIGUSR1, и работать с МНОЖЕСТВЕННЫМИ позициями так просто не удастся, т.к. не различить, от кого пришёл LAM (или маску читать?).
08.11.2011: о степени "обкорнанности":
inserver_snd_cval()
.
inserver_req_bigc()
.
И это нужно даже в СЕРВЕРОВОМ inserver.
Сами дескрипторы -- inserver_dataref_t
--
можно реализовать как target_devid*65536+chan_n. Разве что проверки на
выход за допустимый диапазон [пока] не будет.
Помимо технических проблем реализации собственно API есть еще вопросы, отсутствующие в настоящем сервере и обусловленные "сборищной" моделью группировки драйверов:
Добавить команду отсылки имени в протокол remdrv?
А сами имена -- (a) брать принадлежащее самому remdrv или (b) мочь указывать в auxinfo вместе с set1/set2? Наверное, для унификации лучше (a) (хотя (b) был бы гибче).
Или повесить это на драйвер -- чтоб, в стиле remdrv/cda, пытался сбиндиться раз в секунду? Тогда автоматом получится и ре-биндинг (восстановление при умирании и последующем рестарте блока-мишени), и управление блоками от иных серверов (появляющихся существенно до или после этого).
12.05.2012@Снежинск-каземат-11: в СЕРВЕРОВОМ-то реализовано, еще энное время назад и "КОГДА" в драйверах делается именно по вышеприведённому принципу.
НО: а тут-то как делать -- ведь это означает всё же наличие неких
callback'ов, чего как раз очень не хотелось. Хотя, раз уж никакой кэш не
нужен, то особых проблем не будет -- при условии НЕиспользования
inserver_get_devstat()
(впрочем, при АККУРАТНОМ использовании
-- со свежеполученным devref'ом -- и его легко сделать).
30.12.2011: делаем, с легкими модификациями.
Модификации заключаются в добавлении к именам публичных вызовов
префикса RemSrv
и аналогичном переименовании
drivermapping_t
в remsrv_drivermapping_t
.
Оно собирается, хотя толку от этого немного (чисто для проверки кода), т.к. применение библиотеки -- только для кросс-сборки.
26.01.2012: да, теперь libremsrv используется уже и в кросс-компилируемостях. Соответственно, старая, локальная версия из nppc_canserver/ удалена.
init_drv()
(он же init_m). В результате
не может нормально работать init_lyr() в pre-lyr'ах; в cangw изначально
сделано через layer_initialized
.
16.06.2012: и вообще отсутствует поддержка layer'ов как класс -- а надо! Для начала, хоть в протокол добавить (уже в v4, видимо...).
(Первый раз эта мысля возникала еще черт-те когда -- кабы не в Зеленограде...)
10.10.2012: смысл -- что если это просто клиент (в смысле -- cx-server) молча сдох (например, у него моргнуло питание), то пройдёт аж до 7200 секунд, прежде чем будет обнаружена мёртвость соединения (ровно это произошло сегодня, когда Хамёл дёрнул питание linac1 в целях тренировки Детокоса). По PING'у же произойдёт отправка пакета, и если "тот конец" просто перегрузился, то его ядро немедленно пришлёт RST, и фиктивная занятость сбросится.
20.11.2012: вариант решения: чтоб remdrv уходил в offline после НЕСКОЛЬКИХ таких попыток -- тогда через 10 секунд remsrv уже точно обнаружит скопытившесть сокета и закроет соединение, освободив позицию, и вторая попытка станет успешной. Но тут встаёт следующий вопрос -- а КАК remdrv должен понять, что тут надо включить счетчик?
26.11.2012: как-как -- да просто по всем отлупам делать несколько попыток.
21.11.2012: есть идейка, как решить проблему, не
вводя всяких лишних сущностей (и не "протыкая" слои): считать, что при передаче ReturnChanGroup()
значения count
=0 надо сделать "понг".
26.11.2012: часть 2 вроде сделана, осталась часть 1 -- повтор попыток коннекченья.
Тут есть тонкость: поскольку всё асинхронно, то нет возможности "сразу" понять неудачу CONFIG'а, ибо ответный CHSTAT придёт очень далеко не сразу. Так что:
fail_count
-- для подсчёта ошибок.
config_timestamp
-- момент отправки пакета CONFIG.
...в идеале -- драйвлет должен ВСЕГДА при запуске присылать CHSTAT, пусть даже в OPERATING. ...Несколькими минутами позже -- сделано во все, так что по возврату данных убрано.
Надо признать, что все эти махинации -- сплошной хак. Часть проблем была бы решена при наличии отдельного ответа "потенциально врЕменная проблема" (как коды 5xx в ftp/smtp/http), но лишь часть. Основная сложность -- то, что данные фортели сильно выпадают из общей модели работы сервера/драйверов. То ли из-за не совсем адекватности модели для таких перепусков, то ли потому, что вопросы уведомления о перезагрузках должны бы решаться на совсем других уровнях.
03.12.2012: да, проверено -- remdrv при
CXRF_CFG_PROBL
повторяет попытки коннекченья еще 2 раза.
Проверялось просто запуском двух серверов с одним blklist-файлом на разных :N, с последующим прибиванием первого "захватившего" -- тогда оно переходит второму. Также проверялось, что после успешного "захвата" и последующего обрыва оно будет повторяться.
На совсем живой системе после reset'а хоста проверить пока не было возможности, но будем надеяться и ставим "done".
19.10.2012: делаем для команд логгинга
RemSrvExecConsCommand()
.
ParseCommandLine()
добавлена обработка ключика -e.
Заодно:
На пультовых коробочках теперь на всех включено -- сделан pult/lib/server/drivers/cangw_ppc_drvlets/etc/rc.d/rc.local с содержимым
/mnt/host/oper/pult/lib/server/drivers/cangw_ppc_drvlets/canserver -d \ -e disable_log_frame >/dev/null 2>/dev/null &
SetDevState(devid, DEVSTATE_OFFLINE, 0, "remsrv: Terminated from console");
15.09.2013: но для этого придётся сделать нормальный парсинг командной строки -- с разбиением на собственно команду и параметры.
21.10.2013: делаем:
cmd
передаётся пара nargs
,argp[]
(смысл как у
argc,argv[], в т.ч. argp[0] содержит былую cmd).
Плюс '#' перед любым токеном работает как комментарий.
Только надо учитывать, что, поскольку оно делает корректное "застреливание", то после этого драйвер уже не возродится, поскольку и на хосте он тоже застрелится.
22.10.2013: за компанию еще несколько усовершенствований:
remcxsd_dev_t.when
и
consrec_t.when
, тип time_t
.
RemSrvAccept{Host,Cons}Connection()
.
У list это выглядит не очень красиво, поскольку из-за давней попытки выравнивания IP-адреса добавленная инфа выдаётся отдельно через пробел.
И дальше, уже после завершения драйвера, в log попали сообщения вида
remdrv[1]/DEFAULT: DEBUGP: ReturnChanGroup(): devid=2(active=-1) is inactive
т.е., уже после того самого "непонятно чем вызванного FreeDevID()"
драйвер продолжал трепыхаться (и логгинг шел через соседний драйвер,
по хитроумному алгоритму "last_good_devid").
Видимо, "непонятно" -- это когда контроллер присылал кривой пакет, то remdrv закрывал соединение, что и приводило на стороне контроллера к закрытию по причине FDIO_R_CLOSED; странно, правда, что сообщения об ЭТОМ на консоль не выводилось.
04.09.2013: похоже, имеют место две РАЗНЫЕ проблемы:
05.09.2013: кстати, в одном из ругательств было
"inpktsize=1147495015", а это ==0x44656267, т.е. код
REMDRVP_DEBUGP
. Спрашивается -- как оно туда попало?
...или где-то ошибка с подсчётом длины пакета,
отправляется на 4 байта меньше, в качестве последнего int32 считывается
pktsize
следующего пакета, а егойный command
пытается интерпретироваться как длина? (В прочих случаях -- ошибка не
на 4, и потому значения совсем бредовы.)
Но тогда б вылеты наблюдались очень часто. А тут словно какой-то race condition: то ли пачка fdio_send()'ов одного пакета перебивается чем-то еще, то ли вообще в fdiolib'е баг. Но: 1) с арифметикой вроде всё в порядке -- проверял и перепроверял; 2) перебивок на вид быть не может вовсе -- всё скопом идёт; 3) fdiolib работает уже седьмой год, в т.ч. уже почти год в ветке клиенты-сервер, и там подобного не наблюдалось.
Вот не дай бог это глюк компилятора или библиотек -- тогда совсем ж...
14.09.2013: в процессе тестирования (while-true запуск cxsd, с нажатием/зажатием Ctrl+C) обнаружилось, что как будто драйверы в canserver'е недо-убивались, продолжая активничать после FreeDevID().
15.09.2013: а вот и да -- увидено такое. Но не только.
15.09.2013: разборки-разборки, длинные и нудные. Результаты:
Тогда в вызове первого же DoDriverLog(,ENTRYPOINT) получалась ошибка отправки и вызывалось FreeDevID() -- с не вполне ясными последствиями: оно ж делает free(devptr), в т.ч. портя содержимое.
Теперь что с этим делать:
Надо использовать ОТЛОЖЕННОЕ освобождение, как в fdiolib'е -- being_processed/being_destroyed (18-06-2013).
Сейчас, правда, проблема с тем, что очень даже далеко не всегда сейчас передача управления коду драйвера находится под контролем remcxsd: еще cxscheduler и fdiolib вовлечены. Но это вопрос на будущее.
А вопрос похуже -- как быть с layer'ами? Ведь ENTER() делается по lyrid, а потом исколняется код уже конкретного драйвера. Заставлять ещё и layer'ы все вызовы оборачивать в ENTER()/LEAVE()? И ведь мозги layer'ов тоже должны будут весьма корректно себя вести и быть готовы к тому, что в момент вызова "LEAVE()" это самое устройство будет высвобождено.
Причина проблемы в том, что вызов layer'овых disconnect() сейчас возложен на драйверовы term_d(), а не на среду исполнения, как в v4. И если вдруг чё -- то вызов сделан не будет, и "ресурс останется неподчищенным".
Остаётся только один вопрос: а что всё ж был за прикол с приходом пакетов с бредовыми inpktsize (в т.ч. ==REMDRVP_DEBUGP)? Ведь, по идее, освобождение-devid/remcxsd_dev_t-не-вовремя не должно было давать такого эффекта: там проверка на in_use==0 стоит, и оно б просто ничего не делало.
15.09.2013: реализуем отложенное освобождение. Некоторые пресуппозиции:
ENTER_DRIVER_S()
и LEAVE_DRIVER_S()
, ведь
именно они ставятся вокруг каждого вызова драйверова кода.
being_processed
должен быть не булевскм флагом, а
счётчиком, и "можно доделывать" при уменьшении его до 0.
(А being_destroyed
наоборот, должен быть кумулятивным
-- если хоть раз выставлен, то уж и стоять.)
Итак:
remcxsd_dev_t
: добавлены
being_processed
и being_destroyed
, сразу
после in_use.
FreeDevID()
: введена
проверка, что если being_processed, то лишь выставляем
being_destroyed=1.
ENTER_DRIVER_S()
: being_processed++, причём после
суровой проверки, что речь действительно о живом девайсе.
LEAVE_DRIVER_S()
: после аналогичной суровой проверки
делается being_processed-- и смотрится, что если being_destroyed, то
дёргаем FreeDevID()
.
inited=1
сделана безусловной и делается
сразу после вызова init_dev(). В противном случае не вызывался
term_dev() и, следовательно, disconnect().
Засим глюки вроде бы прекратились. Простой тест с авто-kill'ом и перезапуском каждые 0.1с показывает некоторые некрасивости, но чисто в логгинге, а так нефатально. Но надо еще тестировать и наблюдать.
(Есть некоторая "загадка": ведь изначально глюки проявились при ЗАПУСКЕ сервера, а не при быстром пере-запуске после Ctrl+C. Но, возможно, они именно ПРОЯВЛЯЛИСЬ при запуске, а обуславливались кривым завершением при предшествовавшем останове сервера.)
Итого:
В частности, какая-то странная строка от log_frame() с devid=0. Вот откуда б?
01.10.2013: всё-таки косяки продолжаются, хоть и нечасто. Вот сейчас (вчера) на ring1:32 вылезло с одним из драйвлетов, причём в момент реконнекта (который сам неясно чем вызван)
inpktsize=1633890614, >REMDRVP_MAXPKTSIZE=524716 (command=0x5f696e69)
(полный фрагмент тут в комментарии).
Всё вроде свеженькое, последненькое. Видать -- тут тоже надо ждать и ловить проявление проблемы, для накопления статистики.
01.04.2015: продолжение веселья (РОВНО через полтора года!) -- опять заскоки с inpktsize:
inpktsize=1130910580, >REMDRVP_MAXPKTSIZE=524716 (command=0x00000000)
Причём от ДВУХ девайсов одновременно, даже log-сообщения поперемешались
(полный фрагмент опять же тут в комментарии). И от обоих ОДИНАКОВЫЕ
числа.
На этот раз оно от moxa, которая little-endian, а само число
1130910580 -- это ==0x43685374, т.е. 'ChSt', т.е. код
REMDRVP_CHSTAT
-- опять команда на месте размера!
(Полуторагодовалой давности 1633890614 на вид не так --
==0x61633136, 'ac16', совсем никак не клеится.)
И, опять же, рядом ругань
DEBUGP: ReturnChanGroup(): devid=4(active=4) is inactive
на ОБА девайса, через миллисекунды от предыдущего.
Что противно -- ведь этот же код librem* пойдёт с минимальной адаптацией в v4. То ли искать проблему ДО того, то ли, наоборот, надеяться, что там, в иных обстоятельствах, вылезет быстрее?
__DATE__
__TIME__
печатаются в
RemSrvAcceptConsConnection()
, то по факту выдаётся время
сборки remsrv_drvmgr.c, а не собственно программы-сервера.
Правильно, конечно, их передавать параметрами в
remsrv_main()
, но пока проще забить.
ReturnChanSet()
-- в интересах как минимум
caniva_drv.c, чтоб обходиться ОДНИМ Return*() и отправкой
серверу, вместо 4, на каждый пакет от устройства.
04.09.2013: делаем:
REMDRVP_RCSET
,
"RCSt".
ReturnChanSet()
:
addrs[]
у нас int
, то слать
сразу нельзя. Поэтому введено волюнтаристское ограничение в
MAX_CHANS_PER_RCS
=100 каналов за присест.
Если вдруг станет мало -- то несложно сделать кусочную обработку с "выпихиванием", как в fastadc/Xh_plot.
remdrv_fd_p()
: добавлена
очевидная обработка.
Тут тоже волюнтаристское ограничение в те же 100 каналов, с теми же возможностями расширения.
Проверено, вроде работает.
Надо опять профилировать, а сначала и просто подумать не помешает...
05.09.2013: возможно, это из-за разницы в коде
отправки пакетов: раньше оно готовило буфер и делало ОДИН
n_write()
, а теперь по нескольку
fdio_send()
'ов. Если проблема в этом -- то печально.
19.09.2013: имеем: каждый вызов
fdio_send()
использует от 1 (лучший случай) до 3
syscall'ов. Если проблема в них, то надо в fdiolib'е
ввести возможность указывать "сейчас начнётся пакет, не начинай его
слать, пока не скажу". Т.е., пару скобок lock()+unlock(), которыми
окружать пачки вызовов отправки (а их в Return*() по 3-4).
После обеда: да, вставлено использование свежевведённых
Надо б еще с CANIVA проверить, для сравнения, но, скорее всего,
проблема решена.
fdio_lock_send()
/fdio_unlock_send()
.
Загрузка дохленького 50МГц процессора CANGW сократилась заметно: на
v5-sr-ist (работает на ring1:32) в режиме disable_log_frame с ~54%
упала до ~36%.
remcxsd_uniq_checker()
. Ради чего пришлось выделить из
CHECK_SANITY_OF_DEVID()
мясо в
DO_CHECK_SANITY_OF_DEVID()
-- чтоб вместо __FUNCTION__
подсовывать другое имя, переданное в func_name
.
16.09.2013: переименована в
cxsd_uniq_checker()
в связи со стандартизацией.
Делается на "архитектуре ProjectRules.mk".
30.12.2011: показания к сему:
А так -- вытащим всё в одну точку, и уж сюда пусть ссылаются конкретные проекты.
Общий принцип: если может быть смысл собирать драйверы под другую архитектуру, то сами исходники помещаются в поддиректорию src/, откуда симлинкуются в директории для конкретных архитектур. Директория для "обычных" всерверных драйверов всегда называется local/.
08.01.2012: систему сборки здесь делаем на основе
ProjectRules.mk, вариант с SECTION
(основа взята
из yweld/).
14.09.2012: в настоящий момент структура такая:
ARCH_SUBDIR
,
указывающую на корневую для данного типа контроллеров поддиректорию
внутри /mnt/host/.
Поддиректории по типам контроллеров --
02.11.2012: были баги в CM5307PPC/ -- rc.local от CANGW, rc.banner пустой. Поправлено.
14.03.2013: в arch.conf может быть
указан еще один параметр -- ARCH_MNTDIR
. Если НЕ указано,
то rc.sh уставляет его в "/export/HOST". И в любом
случае mounthost.sh'у передаётся уже оно, а не фиксированное.
Сделано ради ВЭПП-4: у них home-директории монтируются по NFS, так
что фиг сделаешь ре-экспорт, а то, что для контроллеров -- в
/opt/ppc860. Вот туда же, в pult/, поселена и ветка
для CANGW, а в ARCH_SUBDIR
убрано "oper".
Заодно в rc.sh добавлено приспособленчество к "новой" мамкинской прошивке, в которой xinetd заменен на inetd. Там теперь цепочка "xinetd || inetd".
-- обламывается, и потом ждётся ещё минуту.while ! mount -t nfs -o nolock,ro $MY_HOSTDIR $MNT_HOST do sleep 60 done
05.02.2017: обнаружена проблема была Гусевым на его контроллерах -- у него монтирование было одноразовое, без повторов, и потому всё обламывалось навечно (оно бы и раньше обламывалось в случае включения питания контроллера раньше, чем у хоста).
Гусеву был выдан вышеприведённый кусочек кода, решивший его проблему фатального облома. Но, дабы сократить время бесполезного простоя (ведь свич-то включает порт в течение секунд), он (посоветовавшись со мной) внёс в процесс монтирования небольшое дополнение:
(приведён фрагмент из ЕГОшнего rc.sh, где монтирование фиксированное). Общий принцип ясен: первая пауза -- 10 секунд, а последующие уже по минуте.SECONDS=10 while ! /bin/mount /home do sleep $SECONDS SECONDS=60 done
Переделывать нынешние контроллеры -- лень, т.к. CANGW в ближайшее время заменятся на "canserver'ы" (X11SAE-F с 2*PCI7841 + 4*CP602E), а CAMAC тоже остаётся всё меньше. Но если что -- рецепт есть.
25.07.2017: в связи с переделкой пультовой сети настройки всё равно приходится менять, так что -- делаем!
Это всё уже в hw4cx/etc/, свежесозданной.
Заодно изменения и дополнения:
MY_GW
, который в
rc.sh проверяется, и если непуст, то делается
route add default gw $MY_GW
P.S. Она изначально создаётся на новой системе сборки.
MAKEFILE_PARTS+=...
на все lib*_COMPONENTS.mk --
чтоб при модификации состава библиотек оно осознавало необходимость
пере-сборки.
18.08.2013: также слегка сменено распределение
обязанностей: теперь указание ARLIBS=
(и
свежепонадобившееся MONO_C_FILES=
) перенесено из
под-директорных Makefile'ов в общий x_libs_rules.mk --
воизбежание дублирования.
23.05.2012: переведена на x-compile/ppc860-linux/.
12.11.2012: действия:
override
" в x_rules.mk.
Сделано. Вылезла пара нюансов:
exit()
.
./c4lcanmon: select()=-1 processing ":": Interrupted system call
ProcessPacket(handle=16, dev=7): I/O error
Видимо, профилирующий таймер?
На первое забил, второе исправлено fdiolib'ом (см. за сегодня).
Профилирование работает.
23.05.2012: поскольку КОМПИЛЯЦИЯ работает даже под RH-7.3, то сама эта директория защищена только по
ifeq ":$(OS):$(CPU_X86_COMPAT):" ":LINUX:YES:"
Пользуется сразу x-compile/arm-linux_1.3/.
18.08.2013: а теперь, в связи с добавлением сюда и rrund, пришлось ставить полную защиту аналогично директории serial/moxa/.
/pzframe_gw
.
17.10.2014: скелет.
20.10.2014: вроде сделано. Детали:
Случаи "вылезания за границу" пока никак не отрабатываются (в буфере будет оставаться старый мусор).
_PARAM_NUMPTS
'ом, поэтому отдаётся info от
реального устройства, но в нём подменяются PTSOFS и NUMPTS.
Теперь испытывать.
21.10.2014: к вопросу об испытаниях: можно ведь натравливать драйвер и не на настоящий adc*me, а испытывать в отдельном сервере (хоть вообще на другом компе), через pzframe_gw, смотрящий на настоящий драйвер.
21.10.2014: да, именно так и испытано. После фиксенья нескольких багов-опечаток вроде работает.
08.01.2012: делаем.
Главная модификация пришлась на Makefile. Старый был сделан крайне странно, под "как бы внутридеревную" сборку, там ставилось SRCDIR=../../../cx/src. Сейчас переделано под стандартное ProjectRules.mk -- выглядит, конечно, страхолюдненько: этакая смесь стилей.
Работа не особо сложная, оно собирается, но результат надо будет проверить.
07.05.2012: в can/Makefile добавлен хитрый
проверочный код, так что при `uname -r`
выше либо равно
2.6.25 оно включает в сборку и socketcan/. Ключевые слова --
OS_GE_2_6_25
и MAY_BUILD_SOCKETCAN
.
Проверка сделана извратно -- без сравнений, а при помощи арифметики в awk с последующей трансформацией sed'ом.
10.05.2012@Снежинск-каземат-11: арифметика была сделана халтурно --
2006025-$$1*1000000+$$2*1000+$$3
вместо
2006024-($$1*1000000+$$2*1000+$$3)
Т.е., во-первых, оно считало неправильно (вычитался только major, из-за отсутствия скобок), а во-вторых, сравнение выходило ">2.6.25", вместо ">=".
Заодно добавил отсутствовавшую проверку для cangw/ -- ключевое
слово MAY_BUILD_CANGW
.
04.06.2012: директория cangw/ переименована в c4l-cangw/. Смысл -- чтоб можно было сделать и "локальный" layer "cangw", работающий через селивановский can4lgw.
31.07.2013: списочком:
CANDRIVERS
, а не список
исходников CANDRIVERSSOURCES
.
Смысл -- что при переводе управления магнитной системой линака с cangw-magsys на комп с PCI-CAN под SL-6.3 переносить туда сервер -- ай-яй-яй, поскольку поедет вся конфигурация. А вот если сделать этот комп просто таким же набором драйвлетов -- для клиентов всё останется как есть.
Делать это в директории socketcan/, там всё равно уже всё имеется, надо только добавить собирание еще одного бинарника из готовых кубиков (один из которых, правда, canserver.c -- надо симлинковать из ../c4l-cangw.
25.09.2014: делаем
29.09.2014: доделываем. Чтоб не было зависимости между директориями, чья собираемость может включаться независимо, основа canserver'а унесена из c4l-cangw в src/:
Для правильного "issue" введён define CANSERVER_ISSUE
,
в котором можно указать нужную строку.
Ну и собственно в socketcan/ теперь
v2sktcanserver
делается (для этого, кстати, пришлось
генерить пустой canserver.c, иначе make не хотел -- известный
прикол с ним).
09.01.2012: после простейшей адаптации Makefile всё собралось.
Главная кривизна -- присутствует явная ссылка
CPPFLAGS+=-I$(PRJDIR)/../uspci/include
...
07.05.2012: а как от кривизны избавиться? Поселить uspci сюда, в v2hw/kernel/x86_uspci/?
23.05.2012: поскольку собирается moxa не везде под LINUX-X86, и внятно определить собираемость ЗАРАНЕЕ нельзя (т.к. дело в левости -- GLIBC_2.3), то довольно от балды выбрано требование, что ядро >=2.6.18. Сама проверка в serial/Makefile идентична оной в can/Makefile.
_in()
полученные данные
передевались "как есть", вместе с байтом адреса в начале и контрольной
суммой в конце. Теперь же они отбрасываются --
piv485_fd_p()
передаёт (...,dlc-2,data+1);
make
-C
).
30.08.2012@Снежинск-каземат-11: делаем и инфраструктуру сборки, с единственной директорией camac/cm5307_ppc_drvlets/. Покамест просто для проверки компилируемости pzframe-драйверов, так что халтурновато (хоть уже и с src/ShadowRules.mk):
21.07.2013: за последние несколько дней состояние сильно продвинулось:
#define ABSTRACT_NAME ТИП #include "camac_abstract_common.h"
Так что, осталось доперетащить абстрактные драйверы (существенную часть превратив в обычные) и протестировать.
26.07.2013: произведено переименование -- хбз почему тогда (31-08-2012 в Снежинске) странно называнные cm5307_camac_DEFINE_DRIVER.[ch] переименованы в просто cm5307_DEFINE_DRIVER.[ch].
30.07.2013: первая пачка драйверов -- для сухановского крейта -- проверена, пашут. Так что инфраструктуру можно считать работающей, и дальше переводить пульт на неё.
...и пилить новый remsrv!
WATCH_FOR_LAM()
перетащить прямо в сам
cm5307_camac.[ch] -- чтоб оно было независимо от driver-API, и
доступно cm5307_test'у.
26.07.2013: для этого оно должно будет сразу САМО пользоваться cxscheduler'ом.
Заодно для независимости придётся сделать, чтоб оно возвращало NULL
при успехе и строку-описание при ошибке, вместо использования
DoDriverLog()
'а.
Получасом позже: да, теперь оно возвращает строку-описание либо NULL.
29.07.2013: ...впрочем, cm5307_test этим всё равно не пользуется, поскольку вломы было его делать на cxscheduler'е -- у него своя личная копия всего этого кода, с соответствующими модификациями (криво, да).
22.08.2013: за вчера-сегодня сделано. Пока не мега-красиво, но работает.
V2HDIR
.
PRJDIR= ../.. SECTION= $(V2HDIR)/drivers/camac/cm5307_ppc_drvlets/DirRules.mk include $(PRJDIR)/PrjRules.mk
Поэтому строка
ifeq "$(patsubst .%,,$(SECTION))" ""
должна быть заменена на
ifeq "$(filter-out .% /%,$(SECTION))" ""
Здесь поселим драйверы со стандартным разделением на src/ и "РЕАЛИЗАЦИЯ/". Покамест единственная реализация -- именно BIVME2, работы будем вести в nbivme2/.
26.07.2013: детали:
18.08.2013: оно уже превратилось в просто bivme2/ и вроде б работает, так что "done".
Пока что -- всё скопом, как _data, так и _gui/_knobplugin/main. Если cx/src/lib/fastadc/ разделится -- то и тут подумаем.
10.01.2012: сделано, оно всё собирается, в т.ч. ненужные самой директории *_knobplugin.o.
13.05.2012@Снежинск-каземат-11: а нифига -- вот как раз *_knobplugin.o там НЕ собирались.
DIR_DEPENDS
.
Но вообще-то это халтура -- проблема именно в разблюдовке имён, в компетенции то ли GeneralRules.mk, то ли xmclients/DirRules.mk. Дело в том, что НЕТ возможности локальной директории указать список файлов "для готовки", которые входили бы в список "для них генерятся зависимости".
fastadcclient
--
программа, включающая все fastadc-плагины. Определения
LOGD_*ADC*
содержатся в fastadcclient.h. Сам
.c тривиален.
Библиотеки перемещены.
Оные перемещены.
14.08.2013: остался один tsycam -- все *adc* изведены.
Собираться/генеритья они никак не будут, и сама директория given/ в списке сборки никак не фигурирует, но на её содержимое смогут ссылаться прочие директории.
18.08.2013: что сделано конкретно сейчас:
Замечания:
- Работа с памятью -- обычным образом, malloc()/realloc()/free().
- Проблем с максимальным размером при отсылке -- нету, даже два мегабайта прекрасно отправляются за один прием.
select()
вполне работает для проверки, какие из сокетов готовы для чтения/записи.- Все IP'шные танцы с бубном -- htons(), gethostbyname(), etc., также совместимы.
- Единственное кардинальное отличие -- что
read()
/write()
с сокетами НЕ работают, надо использоватьsend()
/recv()
.
Ну что -- в дополнение к
Кстати, 06.02.2005: Т.о., нынешняя архитектура с
массивом, индексируемым файловыми дескрипторами (принятая в
cxscheduler'е, будущем fdiolib'е, а ныне -- в cx-server'е (основной
цикл и connlib)) под Win32 работать не будет в принципе.
Что будем делать? В принципе-то решение само напрашивается:
Ввести дополнительный уровень косвенности, и
индексироваться/адресоваться не по файловым дескрипторам, а по
внутренним "индексам записей", которые наружу никак не видны.
Т.е., каждая из затрагиваемых библиотек (cm5307_drv.c не
считается -- он переедет на fdiolib) заводит внутри себя массив
некоторого размера, достаточно произвольного. Детали реализации:
В fdiolib'е то это просто -- можно при регистрации дескриптора в
cxscheduler'е указывать в качестве А вот в самом cxscheduler'е, особенно при модификации маски событий
-- сейчас-то там ничего не проверяется, и просто идет работа с
Естественно, таковая модификация должна быть проделана ТОЛЬКО с
cxscheduler'ом и с fdiolib'ом, а старый сервер трогать не надо -- пусть
остается как есть, ему (с его хитрой трехпроцессной архитектурой и с
02.05.2005: хе, заново (склероз -- приятнейшая
из болезней!) пришла в голову та же идея -- "добавить лишний
уровень косвенности", и ввести тип И сразу соображение: а зачем, собственно, нам иметь fdiolib (читай
-- cxsd) под Форточки? А оно нам туда надо?
19.12.2011@Снежинск-каземат-11: сейчас увидел этот вопрос
"оно нам туда надо?" -- да конечно надо! Ведь fdiolib теперь будет
вовсе не только для cxsd, а вообще одним из корневых компонентов системы,
делающим возможной модульно-кирпичиковую конструкцию, когда "кубики" с
формально-разных уровней могут собираться вместе в единую программу
(например, утилита для диагностики CAN-устройств, содержащая в себе сразу и
GUI, и невидимый снаружи сервер с драйверами конкретных устройств).
n_read()
/n_write()
и
uintr_read()
/uintr_write()
делаем еще
n_recv()
/n_send()
и
uintr_recv()
/uintr_send()
?
uintr_write()
не содержит логики на тему
OPTION_CYGWIN_BROKEN_WRITE_SIZE
-- это оно так и должно
быть, или что?
fd_set
устроен по-другому (это массив
int'ов), а сами файловые дескрипторы -- это НЕ небольшие числа, как
принято в POSIX'е, и могут иметь очень даже произвольные значения.
#ifndef ARRAY_CAPACITY
#define ARRAY_CAPACITY 1024
#endif
enum {CAPACITY = ARRAY_CAPACITY};
GENERIC_FD_*()
).
select()
'а.
privptr
именно номер
ячейки.
{r,w,e}fds
; что ж -- в будущем ради оптимизации тоже не
делать проверку, что данный дескриптор у нас зарегистрирован?
sendmsg()
) форточко-совместимость и не требуется.
fdio_handle_t
. С парой
дополнений:
fdio_handle_t
вместо fd --
типа fdio_accept()
.
Основная идея: он не компилируется с теми программами, а
подгружает их через dlopen()
, и берет из них "формальное
описание". Некоторые вопросы/тонкости:
dlopen()
прямо самой
исполняемой программы, или придется на каждого клиента генерить
дополнительный .so-файл? Если Linux такой финт позволяет, то
надо проверить и другие ОС.
13.01.2004: точно так же можно сделать "обобщенный staromakh": чтобы эта программа была одна для всех серверов/систем, а при старте, во-первых, загружала бы себе требуемые бинарники, а во-вторых, читала бы некий config-файл со списком "мониторируемых" каналов (~/cx/conf/staromakh.conf).
14.01.2004: попробовал dlopen()
программы. Хрен -- при попытке загрузить такой бинарник заявляет
(dlerror()
) "cannot dynamically load executable".
Полез в glibc-2.2.5/elf/dl-load.c, нашел там место, где она
так ругается -- _dl_map_object_from_fd()
. Оно булькает
в случае, если тип бинарника -- не ET_DYN
, и притом это
именно dlopen()
, а не загрузка программы.
В принципе, оно и понятно -- не-PIC-код действительно непонятно как грузить куда попало.
Заодно проверил на других рядомлежащих ОС. Результат занятный.
SIGILL
.
Ладно, откомпилировал, запустил -- ругается аналогично Linux'у, только сообщение выглядит как "can't process object file: Inappropriate file type or format".
Как бы то ни было, вывод: грузить при помощи
dlopen()
готовые бинарники низзя.
Что будем делать? Похоже, ДО появления реальной БД напрашивается один выход: генерить .so с необходимой информацией.
Оптимизация: не генерить отдельно исполняемый файл и отдельно
.so, а сделать директорию (типа ~/cx/lib/chlclients),
в которую сваливать эти .so, и ОДНОГО клиента (назовем его,
например, chlclient), на которого изготавливать по мере
надобности symlink'и. А он будет определять "кто он есть" по
argv[0]
и тоже делать dlopen()
.
В любом случае остается вопрос -- как обходиться с не-chlclients.
30.01.2004: - наконец-то доперекурочил programs/chlclients/ (занимался этим вчера и сегодня). Теперь LocalRules.mk не делает бинарников, а только .so и символьные линки на chlclient.
Причем, чтобы make "не умничал", описания имеют имя вида SYSTEM_db.so -- иначе начинаются приколы, когда make применяет не ту последовательность команд, делает не те файлы, etc. Возможно, это я некорректно описывал правила, но в любом случае иметь "_db" в имени удобно.
Отдельная песня -- копирование символьных линков. В конце концов добавил в Rules.mk к cp ключ "-R". Он вроде как определяется POSIX'ом и должен быть достаточно портабелен. Иных вариантов нет -- даже в доступных мне сей момент IRIX, OSF/1 и BSDI на эту тему такой несовместимый зоопарк ключиков...
02.02.2004: подпеределал cx-starter.c так, что теперь
05.02.2004: поскольку реально заявленная в заголовке темы цель (освободить cx-starter от allsystems.h) достигнута, и этот вариант работает, помечаем раздел как "done". Остальные цели -- поддержка больших каналов, staromakh и "иностранцы" -- заслуживают отдельных разделов.
19.04.2004: насчет "иностранцев" через cx-starter.conf: а вот ЭТА потребность послужила уже последней каплей в принятии решения о создании библиотечного модуля paramstr_parser.[ch]. Все потенциальные проблемы -- типа необходимости указывать команды с пробелами -- решатся сами собой.
$CHLCLIENTS_PATH
.
19.04.2004: для этого можно брать "dirname(argv[0])/../lib/chlclients/".
Так: функциональность эта у нас в
descraccess.c::OpenDescription()
. Там НЕТУ argv,
так что -- добавлен параметр argv0
сразу за
subsys
. "Клиенты" chlclient.c и
cx-starter.c соответствующим образом подправлены.
А собственно "../lib/chlclients" -- сделал. Навернуто там получилось, блин...
Клиенты запускаются, безо всяких приседаний с
$CHLCLIENTS_PATH
-- лепота-а-а-а!!!
05.07.2004: дык -- какие проблемы: вводим в
OpenDescription()
еще один шаг --
F_ATARGV0DIR
, который практически запараллелен с
F_ATDOTDOT
, только используется другой формат в
snprintf()
.
elemnet_t
.Так что -- надо как-то применять версионирование и здесь...
11.06.2004: ну точно -- вот сегодня намылился
выкинуть LOGT_WRITEM
, так что значение
LOGT_MINMAX
изменится, а проверка -- йок...
12.06.2004: вот осел!!! Ведь это
версионирование было заложено в схему (subsysdescr_t
и
OpenDescription()
) с самого начала, просто я про это забыл!!!
(Замечание: по смыслу-то эта библиотека очень близка (родственна) Cdr, так что -- рядышком ее пристроить, что ли?)
Как минимум -- надо б descraccess хоть в каком-то виде класть в exports/.
04.12.2004: да, надо, во-первых, заделать это в Cdr, а во-вторых, на будущее, сделать еще и CloseDescription(), чтобы никто, кроме самого descraccess, не знал, откуда берется информация -- тогда можно будет очень легко перевести все на БД.
20.01.2005: да, пора, пора перенести.
Посему -- descraccess.c переехал в lib/Cdr/,
функция переименовалась в CdrOpenDescription()
, ее
определение добавилось в Cdr.h, плюс сделана
CdrCloseDescription()
(содержащая пока просто
dlclose()
без ничего).
Соответственно, в programs/runner/ оно больше не живет, и тамошний Makefile зело упростился.
Все клиенты -- programs/runner/{chlclient,cx-starter}.c и work/vacuum/vacclient.c -- подправлены. Соответственно, vacclient'овский мэйкфайл также существенно упростился, ибо нет там теперь дикой ссылки черт-те-куда.
Заодно обнаружил в Cdr.h зачем-то
#include"cx.h"
-- удалил.
Итак -- обозначенная цель вроде как выполнена, так что помечаем как "done".
А насчет "вернуть chlclient.c в chlclients/" -- а это реально нужно? Надо подумать, и если да -- то сие есть действие совершенно несложное.
subsysdescr_t
дополнительное поле --
clientprog
? По умолчанию -- "", что будет означать
"chlclient".
30.08.2004: еще явно потребно иметь и поле
options
, где указывать флаги типа "notoolbar" и
"nostatusline". Нужно, в частности, для мелких программ Феди-jr, у
которых внутри всего-то пара полей, и всякие декорации (да и работа с
файлами тоже) им совсем ни к чему.
31.08.2004: поля добавил. Что реально было сделано:
DEFINE_SUBSYS_DESCR()
.
Продвигать CXSS_SUBSYS_VERSION_MAJOR
не стал --
предыдущая-то еще не успела расползтись, достаточно ручного обновления
на пульту.
DEFINE_SUBSYS_DESCR()
пара "". Парсинга пока что нет.
options
к
ChlRunSimpleApp()
, и вставил его парсинг и обработку --
практически скопировав из махинаций с eleminfo_t.options
в
Chl_gui.c. Здесь сепараторами назначены не только пробел и
табуляция, но еще и ',', для удобства указания в
SimpleClients.lst.
За компанию удалил из Chl_simple.c #include Xm/{FileSB,SelectioB}.h, остававшиеся там еще со времен, когда файловые диалоги НЕ были унифицированы.
Вообще-то, пора б с такими ОЧЕНЬ неслабыми изменениями издать очередной STABLE/cx. 31.12.2004: издал, STABLE/cx.20041231/.
25.09.2004: ввел опции в простейшем варианте -- указываемые через ";" после defserver'а.
А реально -- видимо, стОит перевести скрипт на Perl, а то парсинг при помощи awk'а становится мутноват...
26.09.2004: мдя-я-я-я... Пришлось еще
mkphysdb.sh поправить -- а то он в качестве defserver'а
дописывал все эти ";...". Вставил туда хитрую конструкцию со
"split($3,params,";")
".
26.09.2004: в остальном -- реализовал в mkclient.sh парсинг опций в формате
app_name@defserver{;option[=value]}Ох и наворочено же теперь там!!! Все, при следующем возможном изменении переходим от {mkclient,mkphysdb}.sh к procclients.pl.
Кроме того, теперь можно при помощи
mkclient.sh -p system SimpleClients.lstвыспросить clientprog указанной системы.
Что и делает chlclients/LocalRules.mk к опции "clientprog" -- теперь оно делает симлинк не unconditionally на chlclient, а на что-нибудь, причем если оно !="-".
#define
.
02.04.2007: поскольку сейчас уже давно совсем другой подход к config-файлам, с возможностью указания пути к нему, и не файла вообще а директории -- "obsolete".
12.07.2004: вот они:
argv[1]
начинается с '@'
-- это это ссылка на
конфиг.
PSP_T_FLAG
, и -- в будущем, если появится прямая поддержка
Epics'а, можно будет ввести три варианта: SUBSYS_CX=0/*default*/,
SUBSYS_EPICS=1, SUBSYS_FOREIGN=2 (а старые ключевые слова можно будет
оставить для совместимости).
start=@linac1:"cd ~/smc && ./smc podpol"
(а можно и весь аргумент засунуть в кавычки).
OpenPipe()
(она
когда-то давно была позаимствована из NEdit'а).
Самое простое, что приходит в голову -- это слева от кнопок и от leds серверов добавить еще по одной колонке, в которую класть XmLabel (или что-то подобное), которая обычно пуста, а при надобности -- содержит символ ">". (А при сильном желании можно туда и циферку таймаута писать, как у Олега.)
19.07.2004: на тему отображения статусов не-CHL-/не-CX- (или, как с rfsyn, доисторических CX-) клиентов:
05.06.2007: учитывая, что в будущем всякие не-родные клиенты все же будут поддерживаться прямо в cda, все вышеописанные танцы с бубном выглядят излишними, так что помечаем как "withdrawn". Хотя идея и хорошо продумана, практически готовый проект.
19.07.2004: учитывая, что количество и длина ключиков для не-CHL-систем могут быть весьма велики:
Стоит предусмотреть в "читчике config-файла" возможность разбиения строки на несколько при помощи бэкслэша. (Где-то ведь такая фича была -- если последний символ строки -- бэкслэш, то читаем следующую строчку и прилепляем ее в конец этой.)
19.07.2004: И, кстати: а не придется ли делать, чтобы можно было в командах/параметрах указывать ${ПЕРЕМЕННАЯ} а то и вовсе %{ПАРАМЕТР} (типа имени хоста)?
(Ага, ню-ню, еще в psp поддержку $-переменных засунуть...)
26.07.2004: за последние три дня крупно переделал внутренности cx-starter'а на тему того, откуда и как берется список систем.
Итак -- теперь вместо былых халявных проверок сделана последовательность из трех шагов:
ParseCommandLine()
-- просматриваем командную строку и
загоняем ее в reqd_count
::reqd_systems[]
.
ReadConfig()
-- подыскивается подходящий config-файл и
считывается в config_count
::config_info[]
.
PopulateWindow()
-- создается окно и набивается
подсистемами. Есть два варианта: если что-то спрошено в командной
строке, то напихиваются эти подсистемы, а иначе -- все подряд из
считанного конфига.
Есть несколько нововведений:
'@'
, то это считается именем конфиг-файла, и в таком
случае автопоиск конфига отключается.
ReadConfigFile()
вызывается
ReadConfigDir()
, которая в ближайшем будущем станет
сканировать директорию на предмет файлов "*_db.so". Таким образом,
можно будет просто безо всякого конфига сказать
"cx-starter @~/cx/bin", и он сам подхватит всех клиентов.
(Замечание на полмесяца назад: префикс '@' выбран потому, что подобный способ сослаться на "список файлов" использовался с незапамятных времен в [pk]zip'е и в arj'е, да и в rar'е.)
ДАЛЕЕ: теперь производится ПОЛНЫЙ, ЧЕСТНЫЙ парсинг конфиг-файла. Т.е., в начале строки должен располагаться идентификатор подсистемы, а далее, через пробел, могут идти опции -- foreign, app_class=, start=, stop=, statuschkcmd=, statuschktime=.
Пока что они в AddSubsystem()
никак не используются, но
этим мы займемся завтра.
27.07.2004: теперь реализована и
ReadConfigDir()
-- она работает. Есть, конечно, проблема,
что директория считывается в бредовом порядке, но что ж -- сортировать
ее, что ли?
28.07.2004: угу -- вставил пузырьковую сортировку по возрастанию по именам.
29.07.2004: окей -- теперь
AddSubsystem()
ищет запрошенную подсистему в
config_info
(если не находит -- то не булькает, а
использует умолчательную "пустую" config-запись). И -- она смотрит на
поле kind
и предпринимает разные действия для CX- и
не-CX-подсистем.
Кроме того, добавлен config-параметр comment=, позволяющий указать tooltip для кнопки.
29.07.2004: насчет LED'ов:
XmNunselectColor
вместо XmNselectColor
и поддерживать состояние
XmNset=False
). Это будет более "консистентно" с
остальными выпуклостями. 29.07.2004: все сделано в
Chl_leds.c.
А индикаторы a_ и d_ не трогаем -- поскольку они пользуются
протоколом XmNvisibleWhenOff=False
.
27.08.2004: (реально давно, с полмесяца назад, но записываю сейчас):
Надо б иметь возможность указывать НЕСКОЛЬКО
config-файлов/директорий. Как? А так: ReadConfig()
вызываем прямо из ParseCommandLine()
, помечая это
переменной типа "config_was_specified". А сортировать в
ReadConfigDir()
-- не весь список, а только тот слайс, что
был добавлен ею.
02.04.2007: поскольку cx-starter используется уже не первый и даже не второй год, то помечаем этот первоначальный рздел как "done".
26.08.2004: сделал, что по нажатию на кнопку проверяется, есть ли уже такая программа на экране. Это было хитрО! Highlights:
Сначала я, по наивности, думал, что достаточно взять кусок из
xwininfo, отвечающий за работу ключика "-name" (это в
xc/programs/xwininfo/dsimple.c,
Window_With_Name()
), основанный на
XFetchName()
. Хрена лысого -- так находится только
какое-то странное окно, непонятно к чему относящееся, и лишь имеющее
WM_NAME==<нужный-app-name>
Потом, насмотревшись на xkill.c/xwd.c, решил, что меня спасет
функция XmuClientWindow()
. Щас, как же... Ведь старый-то
метод находил совсем левое окно, не имеющее никаких child'ов, которые
бы искала XmuClientWindow()
.
XmuClientWindow()
подсказала идею: она ищет среди
под-окон указанного окна (считая его окном-контейнером/декоратором от
WM'а) такое, у которого есть property под названием WM_STATUS.
Почитавши, зачем оно нужно (это описано где-то в 4-й части спецификации
ICCCM), я пришел к идее:
Надо искать по иерархии такое окно, у которого и имеется WM_STATUS, и имя совпадает с нужным нам.
В качестве оптимизации: если при поиске наталкиваемся на окно, имеющее WM_STATUS, то глубже от него искать смысла нету -- ведь оно уже и так top-level window, просто, возможно, не устраивающее нас по имени.
По этому плану и стал писАть код, взяв общую схему от
xc/lib/Xmu/ClientWin.c, функции
XmuClientWindow()
и TryChildren()
. У меня
это SearchForWindow()
и SearchOneLevel()
.
В принципе, все получилось -- идея была верной. Только искать надо
не по WM_NAME, а по WM_CLASS.res_name (ее дает
XGetClassHint()
), а WM_NAME -- это именно имя для
отображения в заголовке окна WM'ом.
XRaiseWindow()
. Деиконизировать оказалось
просто -- XMapWindow()
(как оппонент
XIconifyWindow()
). Проблемы:
XSetInputFocus()
--
штука очень нетривиальная, и фиг его знает, как оно интерферирует с
WM'ами. Надо разбираться. 15.06.2008: Ой, ну
о-о-очень нетривиальная, прямо опухнуть можно, щаз! Сделано, легко
(см. раздельчик за сегодня)!
В общем -- по первости достаточно и деиконизировать и вытащить, это уже сделано. А чтобы привлекать внимание -- можно поверх окна класть другое окошко, и поморгать им секунду, как это делает editres по команде "flash active widgets".
XInternAtom()
. Так вот
(Making
use of the ICCCM Pt1: Requestor Side Selections,
локальная копия):
Атом -- это некая "ассоциация", когда строке присваивается уникальное число, чтобы потом не гонять эту строку по сети между X-сервером и клиентом, а передавать именно число (т.е., атом -- это словно указатель).
Так что атомы не принадлежат каким-то конкретным окнам, и их не надо (да и способа нет) убивать/освобождать.
27.08.2004: реализовал запуск программ. Уже поддерживается (хотя пока не проверял :-) и формат @host:command. Просто в локальном случае делается
execv(, {"/bin/sh", "-c", prog})
а в удаленном --
execv(, {"/bin/ssh", host, prog})
Это реализуется функцией RunCommand()
. Пока никаких
"слотов программ", требуемых для statuschk*, нету.
Программы запускаются, а есть и поддержка "убития". Аналогично
вроде как сделана и поддержка запуска/останова серверов, но пока не
проверена. Общая идеология -- "устанавливается статус" некоей команде
либо серверу: указывается ЧТО, и в какой статус привести. А уж
соответствующие функции -- ChangeClientStatus()
и
ChangeServerStatus()
-- смотрят, в каком состоянии оно
сейчас, и надо ли что-то делать.
Реально теперь надо изготовить менюшки по правой кнопке мыши, в которых можно было б выбрать команды "Start" и "Stop".
Понадобится интерфейс в Chl_leds для получения виджета LED'а -- для навешивания на него event-handler'а.
27.08.2004: некоторости:
- Поиск окна, особенно если X-сервер и cx-starter запущены на разных машинах, работает ОЧЕНЬ долго.
- Как можно подерживать современные не-chlsimple-клиенты: ввести kind "nonchl", и указывать параметр chaninfo=spec, где "spec" -- это строка вида chanref{,chanref...}, причем от первого chanref'а берется сервер и ставится в качестве mainsid. Чтоб это реализовать, cx-starter будет создавать искусственный элемент, состоящий из этих каналов в
CMD_GETP_BYNAME_I()
.- Вообще с имеющимся сейчас "version mismatch" между viper:~/work/cx/ и oduser@linac1:pult наличествует некий бардак: я что-то совсем запутался в том, что получается, а что нет, и выглядит это все кривовато.
- Надо б как-то поменять префикс конфигов с "@" на что-нибудь, после чего shell'ы корректно понимают кнопку <Tab> -- а то задолбало набирать руками.
30.08.2004: итак, что еще надлежит реализовать:
А при указании номера канала меньше 0 (типа "linac1:0!-1") чтобы этот канал НЕ использовался, а только делалось бы соединение. (А для остальных, не-первых серверов, как? Или -- вводим этот хак в cda, чтобы при n==-1 cx_{get,set} реально не исполнялись?)
03.09.2004: кстати, мысль, как запускать cx-starter:
Плюс, возможно, еще какая-нибудь иммунизация на тему SIGHUP a-la nohup (но чистый nohup использовать нельзя).(xterm -geometry +0-0 -iconic -T "cx-starter" \ cx-starter -geometry -0-0 \ ~/pult/configs/cx-starter-`hostname -s|tr A-Z a-z`.conf` &)
04.09.2004: сделал появление меню по якобы правой кнопке мыши -- и хрен: почему-то ловится только ее отпускание, а нажатие -- фиг, кто-то его перехватывает.
05.09.2004: реально наполнил меню командами Start, Stop и Restart, и даже завел на них callback.
И тут обнаружилось: так просто-то restart не сделаешь -- вылетает по
BadWindow
. Полез разбираться -- похоже, придется ставить
свой error handler, чтобы вместо отпадания просто возвращалась бы
ошибка (блин, вот маразм-то, а! Это, очевидно, ляп разработчиков, а
конкретно -- Jim Gettys, см.
XQueryTree and BadWindow errors).
А с тем, как должен выглядеть этот
error handler -- отдельные заморочки, толковой документации совершенно
нету. К счастью, нашелся пример в src/xv-3.10a/xevent.c.
06.09.2004: пришла в голову гениальная мысль, как можно избавиться от '@'.
Ведь в именах подсистем символа '/' никогда не бывает, так? А в имени config-файла он как раз наверняка должен быть, правильно? Ну так -- и считаем все параметры, в которых есть '/', за имена config'ов, а все остальное -- за имена подсистем.
(А в shell'е никакого умного символа на замену '@' не нашлось, увы :-(. Оно только в некоторых случаях типа-интеллектуально понимает ':' (по крайней мере, "PATH=...:~/bin" работает как надо, но только в определениях переменных...)
Итого -- реализуем новую схему, а махинации с '@' выкидываем (они просто излишни -- если надо указать файл из текущей директории, то можно сослаться на ./file.conf.
Часом позже: все это реализовано -- и '/' вместо '@', и
множественные config-файлы, и сортировка
ReadConfigDir()
'ом только того, что считано им самим. И
ReadConfig()
вызывается уже прямо из конца
ParseCommandLine()
'а, если никакого конфига ранее считано
не было.
07.09.2004: решил проблему с выпадением по
BadWindow -- поставил error handler. Это оказалось крайне просто --
тупая функция, которая (для меня) взводит флажок
was_X_error
и делает return 0.
А вот взведение/сброс этого handler'а сделал "скобками"
ENTER_FAILABLE()
/LEAVE_FAILABLE()
-- по
аналогии с {ENTER,LEAVE}_CRITICAL в cxlib'е. Там стоит счетчик, и при
переходе 0->1 handler устанавливается, а при 1->0 --
сбрасывается.
И, кстати, ввел при CMD_RESTART паузу в 500мс между убиением
прежнего и запуском нового клиента -- дабы start'ом гарантированно не
лапалось старое обреченное окно. Пауза делается "правильно" -- при
помощи select()
, повторяющегося (при, например,
прерывании), пока не истечет срок. Это -- функция
SleepBySelect()
.
07.09.2004: разобрался с проблемой "кто-то крадет правую кнопку мыши". Этим "кем-то" было само меню: я его цеплял на общий grid, а оно умничало и вставляло свои event handler'ы.
Порылся в Motif FAQ и нашел ответ -- в вопросе "How can I disable
the button 3 grab if I am not using popup menus?". Всего-то надо
было сделать этому меню XmNpopupEnabled:=XmPOPUP_DISABLED
.
В man'е говорится что-то на тему "keyboard input", но почему-то оно
отвечает и за третью кнопку мыши.
Вообще-то лучше б все-таки кооперироваться с
Motif'ом, а не бороться с ним. Хотя из других ответов в том же
Motif FAQ и следует, что Popup Menus так толком и не досделаны, но в
man'е я нашел функцию XmAddToPostFromList()
, позволяющую
привесить одно меню на кучу разных виджетов.
08.09.2004: да, "кооперация с Motif'ом"
интересовала в первую очередь из тех соображений, что он должен бы
уметь "интеллектуально" вести себя при нажатии правой кнопки -- если
быстро нажать и отпустить, то меню не исчезает, а остается. И, как
показала проверка, XmAddToPostFromList()
именно это и
обеспечивает.
Так что -- теперь меню "прицепляется" функцией, делающей сразу и
CatchRightClick()
(т.е., вешающей event handler) и
XmAddToPostFromList()
, и настало нам счастье. 26.05.2013: имелась проблема: при insensitive-кнопке
клиента по правой кнопке мыши она как бы начинала вызывать меню -- мышь
становилась вправо-вверх и ограничивалась площадью кнопки, но затем всё
намертво зависало (причём блокирование ButtonPress'ов не помогало!).
Видимо, следствие кривого механизма реализации меню в Motif'е. Поэтому
вызов AddProgMenu()
перенесено из точки создания кнопки
дальше, в самый конец -- когда всё точно OK.
(Да, якобы нелишним было бы еще ставить этому меню
XmNmenuPost:="<Btn3Down>"
, но работает и так.)
08.09.2004: сделал и все, касающееся serv_menu --
и создание самого меню, и привешивание event handler'ов и callback'ов,
и собственно вызовы ChangeServerStatus()
(при restart'е
также делается пауза в 500мс). И второе popup-menu на том же базовом
виджете (многострадальный grid) прекрасно работает. А никакого
дополнительного интерфейса для получения виджета LED'а не потребовалось
-- там и так все открыто, ledinfo_t.w
.
Теперь осталось только собственно проверить, плюс вставить, чтобы при нажатии на кнопку [Start] еще бы автоматически запускались все ее сервера.
09.09.2004: проверил -- запуск сервера работает. (Насчет ПЕРЕзапуска -- сомневаюсь), следовательно, и запуск программ на удаленной машине -- тоже.
Была проблема с ключиком "-wN", который сейчас необходимо указывать (наследие Олега). Пока что добавил его в умолчательную строку запуска сервера, но надо б ввести поле "serverflags=" для указывания таких весчей.
06.10.2004: а вот "serverflags=" вводить-то некуда -- у нас отсутствует понятие "БД конфигурации серверов", а есть оная только по клиентам. Насчет конкретно "-wN" -- правильно истребить его вовсе, он является лишь хаком.
Но вообще-то способ для указания флагов серверов необходим -- например, для флага "-b". 16.09.2005: Ну, блин -- ведь .srvparams уже почти год как поддерживаются!
06.10.2004: (реально стало очевидно давно): на
тему указания "-1" в качестве номера канала в "chaninfo=" -- да, нужен
именно хак в cda. Ведь именно в ней легче всего реагировать на "-1" и
заменять соответствующую команду на OP_NOOP
. (Второй
вариант -- чтобы cx-starter САМ передавал именно команду типа
"OP_ADDSERVER", но тогда придется на него навешивать парсинг. Или
именно так и сделать?)
Получасом позже: стал-таки делать по второму сценарию -- ввел опкод
OP_ADDSERVER_I='R'|OP_imm
, который обрабатывается в
cda_register_formula()
вместе с
OP_{GETP,SETP}_BYNAME_I
, с той лишь разницей, что в
результирующую формулу попадает OP_NOOP
. Символ 'R' был
выбран почти от фонаря -- типа "Refer to server". Поскольку 'r' уже
занят под OP_SQRT
, проблем возникнуть не должно. Не забыт
и макрос CMD_ADDSERVER_I()
.
Еще несколькими часами позже: сделал в первом приближении, работает.
Парсинг chaninfo
оказалось разумней вынести в отдельную
функцию -- уж шибко он громоздок.
Теперь надо бы объединить по максимуму варианты chaninfo и dlopen, чтобы не было дублирования кода.
08.10.2004: ввел в запуск серверов проверку, что
если сервер на этой же машине, то обходимся просто /bin/sh
вместо /usr/bin/ssh. Для этой проверки служит
IsThisHost()
, в паре с вызываемой при старте
ObtainHostIdentity
.
06.08.2006: "приближается к юзабельному состоянию" -- да он там уже почти два года, с осени 2004 постоянно используется! Помечаем как "done".
(Да, мерцание поверх окошка не сделано, но оно как-то особо и не требовалось.) 15.06.2008: да сделано, сделано еще 25-04-2007 -- и мерцание, и указание стрелкой.
dlclose()
.
27.08.2004: ага, попробовал -- сделать-то это можно, но много маеты с physinfo, особенно в варианте physinfo-dbase.
29.08.2004:
Вчера, 28.08.2004, наконец-то смог притащить Олега Токарева, дабы он разобрался в корнях нашей стародавней проблемы -- подсистемы rfsyn. Оно не желало работать с новой версией сервера (да реально и ни с какой не желало -- компилируешь, а оно не пашет; а имевшиеся бинарники (непонятно из чего откомпилированные, за 22.07.2003) -- работают).
Итак, притащил я его, сели разбираться... Прошло три часа, а толку -- ноль. Да еще возникла идея, что проблемап роявилась из-за смены на пультовых машинах glibc (произведенной 10.12.2003); т.е., что бинарники были собраны со старой версией, так что теперь их хрен воспроизведешь.
Закручинился я... Я на этот чертов rfsyn убил уже море времени (наверное, переписать драйверы под cm5307 заняло бы меньше времени). Да еще и сменено оно будет скоро Гусевым (впрочем, это "скоро" тянется уже больше полугода). А еще меня к 18:00 в сауну позвали...
В общем, решил я, как это ни прискорбно, признать свое на данном направлении поражение... И ушел в сауну...
Поганое было чувство -- мучался, и так пробовал, и эдак, и все не получается... Это так мерзко -- чувствовать свое поражение, свой неуспех...
А сегодня прихожу на работу -- и, оказывается, Олег-то вчера разобрался в проблеме!!! Когда я ушел в сауну, он остался, со своей флегматичной настойчивостью копаться дальше -- и нашел! Проблема была в od/odcx.c::
od_yahoo()
, каковую следовало "откатить назад до версии 1.2" -- Олег там что-то наваял с retry'ями, и не протестировал (там даже комментарий стоял).Итого -- сегодня на смену вчерашнему ощущению неуспеха пришла дикая радость -- ура-а-а-а!!! Wow, как классно!
С чего я это сюда записал -- что именно старый, несменябельный rfsyn сильно тормозил введение в строй cx-starter'а. Я в любом случае собирался завтра, после планерки устроить мини-презентацию "новых фич", которая должна была включать новый IPP, новую систему запуска, и новый интерфейс программ. А теперь план слегка подкорректирован -- завтра cx-starter менять не буду, отложу на неделю, а будет такое:
- Общее: произведена смена софта:
- Всех программ вообще, новый интерфейс: нет сетки, большой красный курсор, программы появляются не в левом верхнем углу.
- RfSyn
- ИПП
- ИПП:
- Совсем новый, однооконный интерфейс. Он и удобнее, и занимает меньше места, да и кнопки сделаны пиктограммами вверху.
- Отображается "перегрузка" -- бордовым.
- Обычные программы:
- Они стали компактнее -- убрана сетка, все "подсжато".
- При "проблемах" с железом (отключенный блок, проблемы с контроллером, дохлый блок, перегрузка по входу) каналы могут "бордоветь".
- По нажатию ПравойКнопки вместо былого окошка "Шаг" теперь появляется окно "Свойства ручки". Там есть и шаг, и еще море параметров, плюс -- списочек флагов, и если какой сработал -- то он подсвечен бордовым. Наведя туда мышь, можно увидеть комментарий, в чем дело.
- По Ctrl+ПравойКнопке появляется окно "Большое число" -- когда значение канала виднО издалека.
- Double-click на заголовках колонок позволяет эти колонки отключать. Аналогично можно отключать целые элементы.
- RfSyn:
- Во включенном состоянии переключатель "Запуск" становится зеленым, что позволяет издалека узреть включенность комплекса. В отключенном -- желтый.
- Поскольку rfsyn "починен" и запинан под новый сервер, то теперь вполне можно доьавить в крейт давно требовавшиеся два блока.
На следующей же неделе надо будет устроить презентацию по cx-starter'у и новым файловым диалогам (каковые к тому времени доделать).
До того надо попробовать "взять гусиные программы в свои руки" -- во-первых, запинать их под cx-starter, а во-вторых -- изготовить libbigredcur.so, чтобы они тоже "окрасномышивались".
Возможно, потребуется ввести возможность опознания программ по заголовкам окон, а не только по res_name. Иначе (и лучше бы) -- только договариваться с Гусевым.
Да, и не помешает написать документацию по использованию cx-starter -- для оператора, что как нажимать. Да и для писателей cx-starter-*.conf-файлов тоже не помешает.
30.08.2004: да, устроил презентацию для Логачева, Старостенко и толпы свежих аспирантов (Гусев, поц, опоздал, как и Федя-jr.). Презентация прошла удачно.
Да, на следующей неделе -- cx-starter и файловые диалоги, а еще через неделю -- новый vacclient. В очереди висит также CAN-драйвер на замену отокаревского.
06.09.2004: вот оболтус -- протормозил... Так и не довел за прошедшую неделю cx-starter до готового состояния... Ладно, план на будущее такой:
01.11.2004: мдяяяя... "Следующая неделя"... "Будущее"... В общем -- сегодня наконец-то наступило продолжение :-).
Итак: в прошедшую субботу я запинал Андрея Чугуева, чтобы он портировал
свои "спектрометр" и "bpm" на новый cx. Так что исчезли ВСЕ препятствия для
смены олегова "start" (он же tk_probe.pl) на
cx-starter
. Так что -- сменил, поправил несколько вылезших
багов (в т.ч. в cda), и оставил на денек проверяться. А сегодня (после
планерки) устроил микросеминар для Логачева, аспирантов, Гусева и Громова.
План оного был таков:
- Сделана новая запускалка.
- Она компактная, имеет интуитивно-понятный пользовательский интерфейс.
- Она реально показывает текущее состояние: индикация статуса серверов, LED'ами показывает желтое/красное/бордовое/синее, при alarm'ах кнопка подсистемы краснеет.
- Обычно хватает кнопки подсистемы, если мало -- то на подсистеме и на лампочке сервера можно нажать правую кнопку мыши, появится меню.
- Запускается она -- как и раньше, "start".
- При содействии г-на Гусева его программы также легко включаются в этот список. Аналогично г-н Громов может заполучить эту запускалку на ring1.
- (Гусев завел речь о логах, после чего было продемонстрировано log-окошко.)
В общем -- получилось довольно удачно, все возликовали, и в будущем обещали об ошибках/недостатках/неудобствах сразу жаловаться.
По прежнему остаюсь при мысли, что надо будет продолжать подобные презентации. Ближайшие темы -- файловые диалоги, camsel, nmagsys взамен magsys+v1000+magcorr+e2c+c2e, vacclient, новый CAN-драйвер.
06.12.2004: (записано 20.12.2004) сделал презентацию файловых диалогов -- все уложилось в несколько минут. Тезисы были примерно таковы (реально даже -- это все выступление и есть):
- Давно стояла проблема, что неудобно работать с режимами -- при сохранении приходится подбирать имя, при загрузке вспоминать, как же оно было названо и искать в списке; да еще можно по ошибке перепрописать имеющийся файл.
- Теперь это заменено: при сохранении спрашивается только комментарий, а при восстановлении -- показывается список, причем комментариев, а не имен файлов (и только если комментарий пуст (как у старых), то вместо него показывает имя).
- Итак -- от имен файлов пользователь избавлен, и все стало на несравненно удобнее.
Итак, what's next? Похоже -- новый CAN-драйвер, плюс, опционально, nmagsys.
18.04.2005: ой, хи-хи-хи! Ну и скорость, оперативность... Мда...
Короче: сегодня презентовал "синхронное изменение нескольких
каналов" -- LOGD_TEXTGPBL
. Презентация заняла всего
минуту и была вполне результативной.
06.09.2004: пофиксено -- просто поставлен
обработчик SIGCHLD
, делающий waitpid()
в
цикле пока можно.
24.11.2007: ага, а кто errno
будет
сохранять в обработчике сигнала?! (Пришло в голову при прочтении
книги Стивенса и Раго.)
01.12.2007: сохранение вставлено.
22.01.2013: иногда программы скопычиваются (например, вследствие обнаруженной сегодня проблемы с SIGPIPE, а чаще по SIGSEGV), и хорошо бы это в логах замечать.
Для этого нужно в обработчике SIGCHLD проверять, не скопытилось ли в результате сигнала, и если "да" -- то писать об этом.
Проблема в наглядности -- в идеале надо бы помнить все запущенные процессы, чтоб сообщать о том, КТО скопытился. Это слишком много маеты. Но можно схалтурить: печатать лишь PID несчастного, а при запуске также выдавать PID'ы -- тогда при ручном анализе логов информации достаточно.
29.01.2013: делаем.
RunCommand()
выдаёт PID свежезапущенной команды.
ChildrenCatcher()
проверяет, что если
WIFSIGNALED(status)
, то выдаёт PID и
WTERMSIG(status)
.
SIGHUP
(и,
возможно, SIGPIPE
?) в SIG_IGN
.
12.10.2004: вроде сделал. Столь же вроде -- работает :-).
23.10.2004: кстати, перехватывается пока ТОЛЬКО
SIGHUP
, и этого вполне хватает. Сейчас провел тест -- что
будет, если грохнуть xterm некоего процесса, а этот процесс потом
пытается что-то писАть в stdout/stderr. Выяснилось:
- В ответ на fprintf()/write() возвращается -1/EIO, безо всяких SIGPIPE/EPIPE.
(Где-то когда-то я про подобное читал -- на тему что будет, если процесс, находящийся в background'е, пытается читать stdin -- ему прилетит SIGTTIN, а если оный будет проигнорирован -- то будет вернуто EIO. И вроде как что-то подобное про "писать"/SIGTTOU, но это и тогда, в 1995/1996, уже проходило без проблем. Почему без проблем -- нашел в /usr/src/linux-2.4.18-5/drivers/char/tty_io.c, функция
tty_check_change()
. Там есть проверка-- т.е., дозволяется писать обладателям той же группы. Там же есть и объяснение нашей ситуации --if (current->pgrp==tty->pgrp) return 0;
)if (is_orphaned_pgrp(current->pgrp)) return -EIO;
- Заодно обнаружил, что этот процесс, бывший ранее привязанным к /dev/pts/0, и после закрытия xterm'а сохранял его, и НИКАКИМ новым xterm'ам он не давался, а вот когда наш процесс завершился -- очередной xterm захватил именно /dev/pts/0. Reference counting?
28.10.2004: сделал первоначальную версию, работает.
Вообще-то выглядит она малек некрасиво, схема такая:
Уродство -- в том, что мы имеем ДВА fgets()'а.
while (fgets(...)) // Основной цикл чтения файла
{
do
{
...проверяем, если отсутствует '\\' в онце строки -- то break;...
fgets(к-концу-уже-считанного);
} while (1);
}
Сейчас прошерстил все содержимое ~/work/ на тему, где ж еще
я подобную фичу делал, и как. Нашел -- опять в
work/ucam/mkdb.pl. Там схема такая (это часть функции
ReadLine(), детали выкинуты):
Надо бы перейти на аналогичную схему -- выглядит более
"straightforward".
$line = "";
GETLINE: while(1)
{
$buf = <SRC>; ++$lineno;
chomp($buf); ... # Trim left and right whitespace
$line .= $buf;
if ($line =~ /\\$/)
{
chop($line); $line .= " ";
redo GETLINE;
}
last GETLINE;
}
29.10.2004: сделал -- оно просто проходится по
всем серверам и делает им ChangeServerStatus(,,1)
.
30.10.2004: да, еще, для не-cda-программ -- надо было сделать, что если при сим процессе реально происходит запуск сервера, то надо немножко подождать, чтобы сервер успел стартовать. А то клиент запускался, а готового сервера еще не было. Вылезло это на чугуевском spectr'е.
Вставил -- 1сек., половинки оказалось мало :-).
30.10.2004: вставил -- теперь делается cd, в обоих вариантах -- хоть в $CX_ROOT, хоть в $HOME.
08.11.2004: да, это был именно тот глюк. Сейчас он исправлен, так что -- "done".
cstart
пускает cx-starter
'а,
настраиваемой per-host?
30.10.2004: да, есть пара идей:
(Подобный подход используется в команде chkconfig
-- она
берет информацию о подсистемах прямо из файлов
/etc/rc.d/init.d/SYSTEM -- в строчках вида
"# chkconfig:".).
31.10.2004: да, сделал, по второму варианту --
cstart
ищет в конфиге строку вида "#GEOMETRY=...", и
если находит -- то указывает cx-starter'у ключ "-geometry
...". Если не находит -- то не указывает, и оно запускается как есть.
cx-starter
в
какое-нибудь стандартное место, то, если вставлять первой строчкой в
config-файлы нечто типа
#!/usr/local/bin/cx-starter
можно будет уставлять этим config'ам атрибут "x", и пускать их как скрипты.
Не то, чтобы это реально требовалось, но -- приятно!
cstart
'ом...
01.11.2004: вставил в самое начало main()
печать коротенького сообщения -- "CX-starter, CX version M.N".
25.04.2007: сделано, наконец-то.
02.11.2004: да, вставил в
fallbacks[]
указания цветов для "*.push" --
сейчас выбран серо-голубой цвет "#d0dbff".
15.11.2004: так, между делом, обнаружил, что надо-то было еще и "*.push.armColor" указывать. Сделал, узнав цвет опять из SGI'шного Motif'а.
А вообще -- некоторое время назад закрадывалась крамольная мысль, что стоит вместо серо-голубого выбрать просто серый, как у обычных программ (ассоциация -- кнопки ведь вызывают эти программы). Попробовал... Все-таки серо-голубой удобнее -- глаза за него лучше цепляются, что подтвердил и Федя-jr.
19.11.2004: поскольку теперь в Xh_colors имеются отдельные цвета для "нажимабельностей", переделал с fallback-ресурсов на них -- CTL3D*.
19.09.2005: обнаружил, что почему-то цвет "BG_ARMED" указывался равным "BS_DEFAULT" -- "#a4ada4". Наверное, копировал, да не исправил.
Поправлено на надлежащий "#edf2ed" (thanks, IRIX!).
03.11.2004: сделал. Если присутствует "params=", то она добавляет указанное через пробельчик к генерируемой команде запуска.
SleepBySelect()
дикий ляп -- она могла влететь в бесконечный
цикл:
Переделал две внутренние строчки наgettimeofday(&deadline, NULL); timeval_add_usecs(&deadline, &deadline, usecs); while (1) { gettimeofday(&now, NULL); if (timeval_subtract(&timeout, &deadline, &now) == 0) { if (select(0, NULL, NULL, NULL, &timeout) == 0) return; } }
if (timeval_subtract(&timeout, &deadline, &now) != 0) return; if (select(0, NULL, NULL, NULL, &timeout) == 0) return;
И вообще -- впредь надо пользоваться готовой, проверенной реализацией...
06.12.2004: все просто -- /bin/sh корректно висит,
пока его child не завершится. Чтобы сего не происходило, надо перед именем
программы ставить "exec
". Сделал.
13.01.2005: сделал -- теперь есть функция
curtime()
, возвращающая указатель на статический буфер,
содержащий текущее время в формате DD/MM/YYYY-HH:MM:SS. Эта метка
времени печатается перед каждым сообщением о "RunCommand()", а также
при старте самого cx-starter'а.
11.04.2005: Исправил... Вставил проверку, что если это не-первый символ, то может быть и '-'. А в psp это уже и так возможно (хотя -- нам и неактуально :-).
а в самой программе заголовок указывается как "ИПП-32".ipp chaninfo=linac1:13!-1,linac1:12!105 \ title="ИПП-32" comment="Измерения положения пучка"
05.07.2005: Начинаем расследование. Забавно -- оно это окно ВООБЩЕ НЕ ВИДИТ!!!
Т.е. -- xprop показывает
WM_NAME(COMPOUND_TEXT) = "ИПП-32"а
XFetchName()
ничего не отдает (возвращает 0).
Причина -- очевидна: такое поведение присутствует при включенном (в
Xh, используемом в ipp) XtSetLanguageProc()
. При этом тип
property WM_NAME превращается из STRING
в
COMPOUND_TEXT
. А в исходниках XFetchName()
(по крайней мере, из XFree86-3.3.3 -- xc/lib/X11/FetchName.c)
прямо написано -- что 1 (не-0) возвращается ТОЛЬКО при
( (actual_type == XA_STRING) && (actual_format == 8) )
Собственно, параметр "title=" с самого своего появления был хаком, причем временным -- использовался из-за того, что г-н Чугуев в своих программах использовал такой же res_name.
Так что -- с КОНКРЕТНОЙ проблемой ничего сделать нельзя, а надо просто уходить от использования matching'а по имени -- переходить на "app_name=". Что и сделал -- app_name=ipp32 (как и было изначально). Все прекрасно работает.
16.07.2005: нашел причину/решение: надо
использовать XGetWMName()
вместо
XFetchName()
-- спасибо
XFree86
bug#714. (А где-то в Usenet'е и вовсе есть фраза, что "You should
use XGetWMName, because XFetchName is deprecated".)
Единственная проблема -- оно возвращает ПРОСТО PROPERTY, которую затем еще надо раскодировать, что есть не вполне тривиально. Детали можно посмотреть в патче к багу#714.
Так что -- оставим этот enhancement на будущее. Для дуростей типа гусиных -- пригодится.
terminators
. А собственно в cx-starter'е
это сделать -- забыл!!!
16.09.2005: исправлено. Самое интересное, что
там УЖЕ был #define CFG_COMMENT "#"
, он присутствует как
минимум с STABLE/cx.20040802. Его и вставил в вызов
psp_parse()
.
19.09.2005: сделано. Вызов -- широкая кнопка [?] под списком подсистем. Это первый клиент интерфейса Xh_stddlg.
Сам текст подсказки -- несколько строк, разделенных на абзацы '\n', отображаемые XmText'ом, который сам выполняет line-wrapping (для чего делаетя XmNwordWrap:=XmNresizeHeight:=True). К сожалению, XmLabel подобного не умеет, так что разношрифтовые (normal/bold/italic, разные шрифты) и разноцветные фрагменты сделать не удастся.
Также заголовок окна программы сменился с "CX starter" на "CX-starter".
20.12.2005: мдя, я тогда так квалифицированно
лопухнулся: в HelpCB()
есть параметр Widget
w
, а для виджета-текста я также ввел переменную -- Widget
w
. Gcc-2.96 просто тихо предупреждал:
а вот gcc4 под FC4 уже ругался --cx-starter.c: In function `HelpCB': cx-starter.c:1530: warning: declaration of `w' shadows a parameter
(что там ему "different" -- для меня загадка).cx-starter.c:1530: 'w' redeclared as different kind of symbol cx-starter.c:1507 previous definition of 'w' was here
Вообще-то переменная совершенно не требовалась, так что я просто выкинул ее.
А всего-то и надо -- делать бы cd куда-нибудь.
05.10.2006: делов-то -- вставил в самое начало просто cd (в $HOME).
Надо б как-нибудь отправлять stdout+stderr на tee...
07.10.2006: да, сделал. Оно теперь вместо былого
~/pult/bin/cx-starter $GEOM_OPT $GEOMETRY $CONFIG_FILE $PARAMS_FILEзапускает
/bin/sh -c \где LOG_FILE=/var/tmp/cx-starter.log.
"~/pult/bin/cx-starter $GEOM_OPT $GEOMETRY $CONFIG_FILE $PARAMS_FILE 2>&1 | tee -a $LOG_FILE"
Все работает прекрасно -- в лог валятся ВСЕ сообщения, включая ругань сервера при запуске.
Единственное отличие: теперь даже при закрытии cx-starter'а окошко "cx-starter logs" продолжает оставаться, до закрытия последней из запущенных этим cx-starter'ом программы. Причина -- поскольку вывод программ также идет к tee, то тот не отваливает, пока жива хоть одна из них, и, соответственно, продолжает жить /bin/sh, которого xterm и считает за своего "поднадзорного".
02.01.2007: поскольку все уже почти три месяца как отлично работает, то "done".
31.01.2007: just for information: Гусев захотел, чтобы логи не накапливались, а при каждом включении машины очищались бы. Ну так это просто -- убрать ключик "-a" у tee.
08.06.2007: а есть ведь и некоторая проблема -- файл-то ведь только ПИШЕТСЯ, и никто его не читает, access-time становится все дальше, и он будет грохнут tmpwatch'ем... Что делать?
(Вообще-то это проблема скорее ssh'а, унаследованная им от rsh'а
-- ведь программы-то честно печатают на stdout, если сообщения -- не
ошибки (каковые и надо б на stderr). Так что Гусеву посоветовано
вставить себе в начало
setvbuf(stdout,NULL,_IOLBF,0);
.)
05.10.2006: а вообще-то cx-starter'у эту проблему решить проще -- передавать ssh'у дополнительный ключик "-t". Сделал.
06.10.2006: подумал-подумал -- неа, ну, нафиг -- особенно если мы сделаем логгинг через tee, то терминальной семантики и вовсе реально никогда не будет, и оно постоянно будет ругаться
Pseudo-terminal will not be allocated because stdin is not a terminal.Хотя это в принципе и можно отключить двойным указанием "-t" -- "-tt", но все равно будут неотключаемые ругательства вида
tcgetattr: Invalid argument
К тому же, оно еще и вякать начинает при завершении исполнения команды всякую муть --
Connection to linac1 closed.Хотя и это отключаемо, при помощи "-q" -- так что интегрально потребовался бы набор ключиков "-qtt".
Посему -- да ну это в баню, "withdrawn".
07.10.2006: это свелось к двум вещам:
main()
'а вызова
setvbuf(stdout,NULL,_IOLBF,0);
.
Все работает корректно с tee.
tail -f
.
Единственная "тонкость" -- следить-то надо (кажется) за ДВУМЯ процессами сразу: и за cx-starter'ом, и за tail'ом.
02.01.2007: некоторые соображения по теме:
CX-STARTER .... | tail -f /var/tmp/cx-starter.logгде "CX-STARTER" -- это такой хитрый запуск cx-starter'а, который бы все-таки отправлял stdout+stderr в нужный файл. (Хитрость в том, что запускается как бы pipeline, так что shell следит за ДВУМЯ процессами, но второй реально читает не свой stdin, а указанный файл. С первым процессом вообще-то аналогичное жульничество.)
Проверялось на zsh'е -- работает.
Хотя, обеспечить пришибание tail'а все-таки надо...
08.06.2007: угу, а еще при использовании tail'а автоматом исчезла бы проблема с access-time и tmpwatch'ем...
02.01.2007: ответ-то несложен -- взять да добавить. Т.е., blklist'ы теперь должны именоваться blklist-HOST-N.lst.
Ну и в cx-starter, соответственно, надо добавить при запуске серверов указание HOST'а.
03.01.2007: сделал -- и все blklist-файлы в qult/configs/ попереименовывал, и cx-starter подправил. Осталось только испытать это на пульту.
02.02.2007: Да, и на пульт сие поставил. Только
пришлось несколько пошаманить: в ChangeServerStatus()
переменная/понятие host
использовалось в двух смыслах -- и
как то, куда делать ssh, и в имени blklist-файла. Пришлось это расшить
на at_host
и cf_host
.
21.03.2013: в ChangeServerStatus()
делается tolower() всему host[]
. Воизбежание проблем с
хостами типа "VEPP4-pult6".
03.05.2013: тьфу ты!!! Умудрился там написать
*p = tolower(p)
вместо
*p = tolower(*p)
-- глючило просто жутко!
30.01.2007: да, оставалась ссылка в
одной-единственной строчке в ChangeServerStatus()
в двух
экземплярах -- исправил на configs/.
Теперь можно на пульту с чистой совестью изводить старые директории с симлинками, заодно переходя на blklist-HOST-N.lst.
02.02.2007: да, на пульту также перешел.
31.01.2007: понадобилось это для Гусева -- он попросил на сварке поставить cx-starter, т.к. там уже четыре программы, и запускать их каждый день вручную стало утомительно.
Проблему решил очень просто -- введен еще один параметр, label=, в котором можно указать произвольную строку. При неуказанности используется, как и раньше, имя подсистемы.
02.04.2007: сделал. Логика: "starter" -- это запуск, т.е. -- должна быть стрелка вправо-вверх. Очень похожая вещь -- это символ Марса (копье-за-щитом), он же символ железа (в смысле элемента), и, как следствие последнего, символ Volvo.
В нашем варианте -- это круг (как бы пустой внутри, а не как щит) со стрелкой вправо-вверх справа-сверху. Цвет -- как у Xh'ных окон, светло-серый, с 3D-эффектом. Поскольку по пиктограмме будут щелкать мышью, то, чтобы серединка не была пустой и некликабельной, она заполнена светло-голубым цветом (steelblue вытянутый до максимальной яркости) и в ней нарисован как бы стрелочный прибор -- шкала со стрелкой, указывающей вправо-вверх.
Сам файл -- include/pixmaps/cx-starter.xpm.
В принципе, конечно, можно указывать это через параметр start=, но это криво.
(А для клиентов, поскольку нестандартные/ssh-клиенты все равно указываются через start=, то изначально можно использовать синтаксис "@USER@HOST:COMMAND".)
05.06.2007: да, ввел параметр
srvparams_t.user
(maxlen=32-1). И, если at_host!="", то
команда запуска/останова сервера принимает вид именно
"@<USER@>HOST:...".
10.07.2007: проверил -- работает. "done".
25.04.2007: за эти три дня сделал (в основном в один день -- вчера, но из-за мелкого ляпа доделывал сегодня). Highlights:
Была идея на время мерцания входить в собственный event loop,
который бы не брал бы события клавы и мыши (чтобы юзер не мог нажать
еще что-то), благо, на опыте XhProcessPendingXEvents()
мы
это умеем делать. Но потом решил забить -- во-первых, излишние
сложности, а во-вторых, не факт, что корректно бы работало.
При наведении на клиента этому окошку уставляются координаты и
размер клиентского (которое является именно рабочей областью
окна, без декораций). Для пересчета координат в "глобальные"
используется XTranslateCoordinates()
.
Convex
пришлось
поставить Nonconvex
, ибо нифига она не округлая, и
рисовалось похабно (искаженно, и если часть окна была obscured, то не
рисовалось вовсе).
Угол считается через atan2()
от разницы координат
центра клиентского окна и нашего, а поворот делается синусами и
косинусами.
Указывать такой сепаратор можно было бы символом '-' в config-файле или в командной строке. (А на будущее, если захотим не просто сепаратор, а метку -- можно будет использовать синтаксис "-МЕТКА" или "-:МЕТКА".)
05.06.2007: сделал. Основные мозги/изменения
пришлись на AddSubsystem()
:
sepr_config
(.kind=SUBSYS_SEPR
).
ReadConfigFile()
имя подсистемы теперь может и
НАЧИНАТЬСЯ с '-'
.
Сами сепараторы делаются простой черной линией
(XmSINGLE_LINE
).
Жаль, конечно, что они лишь между кнопками, но уж как есть.
26.05.2013: дополнение: если указывается не просто "-", а двойной -- "--", то вместо одиночной линии (SINGLE_LINE) используется двойная (DOUBLE_LINE).
15.06.2008: мда, там все было тривиально просто
по самое не хочу, достаточно было внимательно вчитаться в man-страницу
по XSetInputFocus()
да посмотреть примерчик использования
в xc/programs/xdm/greeter/Login.c (чтоб быть уверенным в том,
какие указывать 3-й и 4-й параметры). Собственно строка --
XSetInputFocus(dpy, win, RevertToPointerRoot, CurrentTime);
А заставил меня это сделать comment#7 в Mozilla bug#202145 "Bringing window to front should give it focus", где есть цитата из Jim Knoble, выступающего там как гуру WindowMaker'а:
Mozilla is obviously already raising the Mozilla Mail window using XRaiseWindow(3) (or a wrapper around it). Why shouldn't the Mozilla Mail window also request the input focus using XSetInputFocus(3)?Эта цитата сняла опасения, что "window manager отреагирует как-то не так" -- все он делает "так", по крайней мере FVWM2 (точнее, fvwm2-2.4.6 под RedHat-7.3).
BTW, репОртер и главный обсуждатор мозильного бага#202145 -- некто Cay Horstmann, уж не автор ли ChiWriter'а?
15.11.2008: причина -- при установке системы на "grin" с 30"-монитором (NEC3090) там был сделан юзер oper, вместо старого oduser. И понадобилось засунуть в менюшку cx-starter-grin.conf ВСЕХ клиентов с linac1 и linac2; а для запуска серверов-то оно должно ходить на linac1, причем -- именно под oduser'ом! И пришлось для каждого сервера писать ".srvparams linac1:NN user=oduser" -- мутотень!
Как можно бы сделать:
В любом случае, алгоритм использования srv_info[]
с
нынешнего простого "ищем, есть ли строчка с таким
.name
" надо будет поменять на "прочесывание". А
именно: идем по всему списку, и смотрим -- если текущая строчка
"интересует" нас (т.е. -- либо полностью искомое имя, либо подходит по
шаблону), то копируем из нее то, что не-NULL.
24.11.2008: вроде сделал, теперь надо проверять.
03.12.2008: да, проверил -- работает.
07.07.2009: и как я тогда проверял, КАК оно могло
работать?! Там стояла проверка с помощью
cx_strmemcasecmp()
вместо нужной
cx_memcasecmp()
-- и естественно оно всегда отдавало
FALSE, поскольку длины не совпадали!
Исправил -- теперь вроде пашет.
argv[]
после
Xt'шного парсинга, сбагривает в AddSubsystem()
. Так что
туда уходят и словца вида "-nnn".
А надо же все-таки как-то уметь указывать и ОПЦИИ -- например, "-n" ("не исполнять команды, а только печатать их").
25.11.2008: да, сделал по-простому -- прямо в
ParseCommandLine()
сразу после проверки на тему
'/' вставил сравнение с "-n". А добавление в массив
reqd_systems[]
осталось в замыкающем else
,
так что все "слова", не являющиеся опознанными опциями, по-прежнему
будет сваливать в массив запрошенного.
А конкретно опция "-n" отображается на переменную
option_n
. Реализация же такова: во-первых,
RunCommand()
собственно exec()
не делает;
во-вторых, при надобности прибить клиента XKillClient()
также не вызывается.
07.07.2009: а вот хреново вышло -- ключ "-n" работал, только будучи В КОНЦЕ командной строки. Иначе он (спасибо Xt, мать её!) воспринимался как сокращенная версия "-name" и съедал следующий параметр.
Так что поменял ключ на "-passive", а флаг -- на
option_passive
.
-- при том, что никакого cstart.sh никогда не существовало! Видимо, хотел сделать, да забыл.cstart: cstart.sh cp -p $< $@ && chmod +x $@
15.02.2009: ну вообще-то действительно правильнее
иметь не-executable-"исходник" для этого скрипта. Так что --
сготовлен, просто файл скопирован, плюс добавлено
INTMFILES+=cstart
(а вообще в Makefile -- муть; насколько
проще было бы с новой инфраструктурой! :-)).
17.03.2009: тьфу ты -- тогда, месяц назад (или еще что приплюсовалось...), лоханулся, и сделал, что cstart стал класться в exports/sbin/ вместо exports/bin/.
Это было из-за мелкой дури -- что EXPORTSFILES
соответствовало директории sbin/ (в которую идуть реально
вообще неиспользуемые и нерабочие runas и cx-run-agent), а вот
директории bin/ -- EXPORTSFILES2
. Вот я тогда и
попутал, куда добавить cstart.
Так что -- перетащил "cstart" в правильный список, и правильно "расставил приоритеты", поменяв местами содержимое EXPORTS{FILES,DIR}{,2}.
Причина-то очень проста -- после первого нажатия программа еще не успела открыть окно, и запускатель, не найдя этого окна, пускает программу второй (и третий...) раз.
Первая мысль -- тупая -- что надо после запуска делать паузу на секунду-другую.
Более разумная мысль -- запоминать момент последнего запуска, и НЕ запускать раньше чем через, например, 5 секунд. Это автоматом исправит и потенциальную проблему дребезжащих кнопок мыши.
06.04.2009: да, сделал -- добавил поле
subsys_t.last_start_time
, заполняемое именно в момент
исполнения RunCommand()
, и вставил проверку, что
прошло не менее 5 секунд.
27.08.2009: по идее -- надо делать аналогично .srvparams: например,
.sshparams ШАБЛОН ПАРАМЕТРЫгде обработка ШАБЛОНа полностью аналогична оной у srvparams.
10.05.2010: очевидное решение -- в получаемых от
CdrProcessGrouplist()
флагах оставлять лишь серверовы
CXRF_SERVER_MASK
да CDR_FLAG_DEFUNCT
(ну и
еще, скорее на будущее -- CDA_FLAG_NOTFOUND
).
А флажок/параметр, видимо, назовём ignorevals.
Сделано -- надо подождать результатов проверки.
03.06.2010: проверено, работает -- "done".
03.06.2010: в принципе, можно сделать так: пусть ПОЛНЫЕ спецификации разделяются ';' (вместо нынешней ','), а через ',' можно будет указывать дополнительные каналы для того же сервера. Для диапазонов напрашивается '-', надо только делать это аккуратно, чтоб не было синтаксического конфликта с "-1" (хотя, похоже, просто автоматически всё выйдет нормально).
Первый вариант он вообще игнорирует -- даже старт делать не пытается, а второй будет рассматривать как просто-какой-то-хост.
(Вылезло при настройке локальности на ноуте l6-dovzhenko-nt, чтоб Довженко мог оное использовать как возимый стенд, и при этом там всё -- копия bike.)
31.07.2010: фиксим.
SplitSrvspec()
имелась проверка на
!isalnum(*spec)
. Дополнил её исключением на ':'.
memcpy()
проверки на тему len==0. Добавил её.
А всё потому, что в ChangeServerStatus()
с незапамятных
времён была поддержка этого случая, просто она никогда не
использовалась в силу вышеозвученных обстоятельств.
Следствие -- для таких "переносимых локальностей" можно делать симлинки типа blklist-46.lst->blklist-bike-46.lst.
Что интересно -- в IsThisHost()
никакие исправления
вносить не понадобилось, хотя она и возвращает 0 для
hostname[0]=='\0'
: просто и так имя было "", и
именно этот факт является в дальнейшем коде признаком локальности.
Но вообще-то с IsThisHost()
стоит поразбираться
попристальнее -- какой-то там странноватый код имеется:
is_fqdn
, который
присваивается (весьма странным образом!), но потом не используется.
02.08.2010: проверено -- то указание ":46" на l6-dovzhenko-nt работает, "done".
XKillClient()
-- чтобы по
аналогии с убитием серверов можно было видеть в логах и убитие
программ.
05.08.2010: сделал.
Правда, вылезла мелкая неприятность: печатать ID окна -- та еще
радость: непонятно какой %-формат выбрать, ибо даже на %lx и
конверсию (unsigned long)win
gcc ругается (хотя в
/usr/include/X11/... есть определения "typedef unsigned long XID" и
"typedef XID Window").
05.08.2010: делаем -- просто скопировав
cxlib'овскую reporterror()
, с переименованием её в
reportinfo()
и заменой stderr
'а на
stdout
.
MAXSYSTEMS=100
.
Сейчас делается просто -- программа грохается и запускается заново. А было б очень удобно что-нибудь типа FVWM/AnotherLevel'овского "Restart"...
06.02.2011@Снежинск, в "Соболе" утром по пути в каземат: даже если оставить в стороне вопрос "а как это делать из пользовательского интерфейса" (ну там -- менюшка, вызываемая по правой кнопке при нажатых Alt+Ctrl), то главная проблема -- а как делать собственно рестарт? В голову приходят такие идеи (в порядке появления):
execv(argv[0], argv)
. Но:
Предварительно делать копию argv[]?
Хотя это был бы единственный 100%-"корректный" способ, позволяющий на ходу обновлять и сам бинарник cx-starter.
ParseCommandLine()
.
К сожалению, малореально -- cda и Cdr не очень-то поддерживают гроханье...
Жульнический вариант: прямо в самом начале main(),
непосредственно перед XhInitApplication()
, сделать
fork()
, и собственно работу пусть выполняет 2-й, child-процесс,
а 1-й пусть висит и ничего не делает. По команде restart 2-й передаёт её
1-му (сам затем завершаясь), и всё повторяется заново.
Для общения между процессами можно использовать pipe()
: EOF
в читающем конце -- завершение, любой другой байт -- рестарт. Саму
pipe-пару достаточно создать один раз еще ДО первого fork()'а.
(Можно было бы держать в резерве 2-й процесс, и так потихоньку передавать эстафету, но тогда нарушится семантика в отношении wait()'а -- когда 1-й процесс после первого рестарта завершится.)
В общем, не срочно, но если припрёт -- можно будет легко сделать.
27.09.2012: разобрался -- из-за принятого 30-08-2004 принципа, что при указанности chaninfo=... "чтобы OpenDescription() вообще не делалось". Раз description отсутствует, то и ихние app_name'ы из-за этого неизвестны, и cx-starter их окон просто не видит.
Соображения:
Сейчас сделано следующее:
FastadcMain()
,
что там по умолчанию вообще отсутствовали app_name/app_class. Теперь в
качестве оных ставятся указанные в def_app_name
и
def_app_class
-- они прописываются прямо в
text2fastadc_main_opts[]
. И в делаемом
yzframe_main.c тоже.
А насчёт возможности указания Chl_app-клиентам -- надо думать...
27.09.2012@по-пути-домой-около-НИПСа: насчёт последнего: а может -- пытаться делать psp_parse(argv[n]), и если оно ==PSP_R_OK, то НЕ делать запись?
04.10.2012: да, может. Только там создание окна еще ДО всего этого парсинга. Что, впрочем, теперь идеологически неправильно -- всё равно надо переносить в конец 0-й фазы.
05.10.2012: ...ну перенесём. И гарантированно наткнёмся на проблему "курицы и яйца" -- ведь тулкит инициализируется в любом случае ДО парсинга параметров.
Как бы то ни было -- сделано. Слегка халтурновато (используется MSTRING, безо всякого free'нья), да и код теперь выглядит совсем диковато, но -- работает.
На этом можно ставить "done".
P.S. Да, на проблему яйцатых куриц натыкаемся -- что тут, что
в fastadc при указании app_name=... перестаёт работать ключ
-geometry
. Увы. Но несмертельно (кстати, при
меганеобходимости можно указывать "-xrm app_name.geometry:...".
14.04.2013: частично можно
попробовать решить проблему куриц при помощи ключа
-name
.
...правда, КАК? Куда и когда его подсовывать -- клиенту в командной строке, что ли? Была еще бредовая мысль: передавать не настоящие argc,argv[], а свой дубль, где между argv[0] и остальным подсовывать это "-name NNN".
30.05.2013: кстати, попробовал -- ключ -name, хоть и описан в man-странице X(7x), но реально просто не отрабатывается. Фиг знает, почему (т.к. и man-страница по XtDisplayInitialize(3Xt) утверждает, что должно)...
11.09.2012: более детальное рассмотрение:
.srvparams
.
Напрашивается -- что надо бы извлекать это знание в какую-то библиотеку, вторым юзером которой (после cx-starter'а) сделать утилитку "run-cx-server", которая и будет использоваться вышеупомянутым скриптцом; он же после этого сведётся к чему-то типа
#!/bin/sh cd LIST_FILE=~/pult/configs/cx-servers-`hostname -s|tr A-Z a-z`.conf for N in `cat $LIST_FILE` do ~/pult/bin/run-cx-server :N done
(вопрос будет только в том, что СЕЙЧАС uspci грузится в /etc/rc.d/rc.local -- вероятно, ПОСЛЕ всех из /etc/init.d/.)
14.09.2012: подготавливаем простейший вариант:
-b200000
.
Загвоздка -- в серверах, которые б надо тоже запустить, но изначально их состояние неизвестно (должна пройти секунда-другая).
И что -- сначала делать паузу 5 секунд, в течение которой ничего не позволять? ...неа, лучше ПЕРЕД КАЖДОЙ подсистемой делать паузу 5 секунд (поскольку разные подсистемы могут использовать один сервер, и если тупо стартовать все подряд -- то он попробуется запуститься несколько раз).
А вот КАК?
26.05.2013: да, МНОГОКОЛОНОЧНОСТЬ. Вопрос "как?", варианты: 1) указывать число строк (или брать от высоты экрана/окна?); 2) в конфиге указывать "переносы". Выбираем второй вариант; указанием "переноса" будет хитрый вариант сеператора -- 3 дефиса (т.е., 1 -- сепаратор, 2 -- двойной, 3 -- перенос).
Приступаем...
27.05.2013: доделано, на пульт поставлено.
Декорирование и уставление размеров сеткам вынесено в отдельные
функции InitCol()
/FiniCol()
. Было еще
желание сделать, чтоб при отсутствии индикаторов состояния/серверов во
2-й/3-й колонках сами бы эти колонки убирались (для гусиностей), но там
не так тривиально, так что пока забито.
27.05.2013: а вообще явно надо программу переписать "from scratch" -- ибо концепции сильно поменялись.
А для этого надо уставлять ДРУГИЕ app_name/app_class. И опять вылазит проблема курицы/яйца...
01.06.2013: можно сделать с симлинками -- чтоб "разные" экземпляры имели разные имена, а app_name/app_class уставлять в basename(argv[0]).
Так и сделано, причём app_class[0] делается toupper()
.
29.10.2015: да, похоже на библиотеки: свежезапущенный процесс сначала брал (VIRT/RES) 226m/13m, а через минуту стало те же 228m/14m. Так что считаем проблему отсутствующей, а раздел "withdrawn".
26.01.2004: начал изготавливать это чудо в programs/chlclients/.
27.01.2004: пришла в голову мысля: ведь некоторый кусок кода у chlclient (загрузка описаний) будет одинаковой с cx-starter и staromakh. А делать на это лишнюю библиотеку в lib/ неохота. Поэтому проще перенести его в programs/runner, там обойдемся просто общим .[cho]-файлом.
В общем -- перенес.
28.01.2004: изготовил функцию
OpenDescription()
, которая пытается нарыть этот
.so-файл в нескольких директориях. Вроде работает.
Обнаружилась одна тонкость: чтобы dlopen()
искал в
текущей директории, надо ЯВНО указывать "./", иначе он прется
во всякие /usr/lib/ etc.
30.01.2004: доделал chlclient до рабочего состояния. Черт возьми, оно действительно работает!
В целях отладки добавил в OpenDescription()
еще одно
место, где оно ищет описания -- в пути, указываемом переменной
окружения CHLCLIENTS_PATH. Это делается ДО остальных попыток.
02.02.2004: перевел
OpenDescription()
в отдельную единицу --
descraccess.[ch].
Кроме того, теперь еще проверяется переменная окружения CX_ROOT, которая если есть, то ее значение используется вместо $HOME/cx.
Короче -- chlclient готов.
А потом, поразмыслив, вытащил их все в поддиректорию doc/lib/. Кстати, пожалуй, имеет смысл аналогичным образом "дублировать" в doc/ и остальное дерево директорий.
make install
" работало бы и для внедеревного софта. Дабы
для всяких федино-маленьких программ тоже можно было б "унифицированно"
говорить "make install".
10.10.2003: вроде как сделал, на одну только пару
$(EXPORTSDIR)/$(EXPORTSFILES)
, а EXPORTSDIR{2,3} не
поддерживаются.
Ну и код же там получился -- просто полный кошмар! Там из-за потенциально разрешенной разнесенности поддиректорий инсталляции (т.е., не из под одного корня) пришлось делать трансляцию путей, так эта, в принципе несложная на каком-нибудь Perl'е манипуляция, вылилась в совершенно нечитабельный и плохомасштабируемый код.
Надо будет проверить на Олеге -- при отдаче очередной версии CX, как раз когда протокол допоменяю.
10.11.2003: проверял (хотя и не очень интенсивно), вроде работает.
Кроме того, намудрячил так, что теперь работают и
EXPORTS{DIR,FILES}{2,3}
.
24.05.2004: нашел презабавный баг: в определении внедеревных install'ов была лишняя круглая скобочка, так что Bash весьма непотребно ругался на некую лишнюю ";". Да еще и "-Rp" там отсутствовало. Поправлено.
make install
" можно было указывать
при помощи "PREFIX=...
", а не
"INSTALLROOTDIR=...
".
10.10.2003: вставил простенькую проверку -- типа
ifneq "$(PREFIX)" ""; INSTALLROOTDIR=$(PREFIX); endif
AUXWARNS=all
-- gcc-3 отлавливает бОльшее число ситуаций.
03.12.2003: насчет "missing initializer" -- хрен вам, так и не дошли те патчи до stable версий.
По причине задолбавшести сделан bug report -- #13282.
04.12.2003: вода камень точит -- на этот багрепорт откликнулся автор того патча, и пообещал довести его к 3.5. Правда, 3.5 обещают, увы, нескоро, но и то хлеб.
03.12.2003: проба компиляции gcc-3.2.2.
Проявились warning'и:
cxlib.c: In function `handle_input':
cxlib.c:1179: warning: deprecated use of label at end of compound statement
cxlib.c: In function `cx_entry_slave_sigio':
cxlib.c:1331: warning: control reaches end of non-void function
cxlib.c: In function `cx_entry_select':
cxlib.c:1342: warning: control reaches end of non-void function
Во-первых, gcc-2.96 почему-то не отлавливал проблемы в
cx_entry_*()
, с реально отсутствующим
return
в конце. Исправлено.
Во-вторых, как выяснилось, ISO C запрещает метку в конце compound
statement'а, т.е., перед '}
' -- метка должна
предшествовать оператору. Gcc'шники в maillist'е рекомендуют
ставить после метки пустой оператор -- ';
'.
Окей -- будем везде писать что-то типа "NEXT_S:;
".
Аналогичные приколы будут и еще в море мест -- типа cda.c.
Кстати, проверил cda -- там этот warning выдается на
"default:
", которая тоже считается за label. Решение
аналогично -- ';
'.
06.08.2004: yes! Gcc в CVS'е пропатчен --
-W[no-]missing-field-initializers
.
15.10.2004: вставил поддержку уставки флага
-Wno-missing-field-initializers
при GCC>=4.0.
Управляется хитрой переменной "GCCHASWNMFI
". Проверил --
сие работает.
30.11.2012: поскольку gcc-3 уже давно неактуален, то закрываем раздел как "done".
30.11.2012: ну посмотрел -- и что там такого? :-)
Ничего особо полезного (для портабельности всегда надо
корректно делать wait*()
, да и всё). Так что --
"withdrawn".
23.01.2004: похоже, make не удаляет промежуточные файлы (типа наших .dep), если они уже были до него (это явно упомянуто у make-3.79.1'2000 в "(make.info.gz)Chained Rules"). И, видимо, в какой-то момент (судя по содержимому STABLE/, в августе 2003) они почему-то остались, и с тех пор все время были.
Решил не возиться с объяснениями make'у, при помощи всяких специальных .NNN-заклинаний (там черт ногу сломит), а просто добавил "*.dep" прямо в команду rm, выполняемую по distclean.
04.02.2004: несколько дней (неделю?) назад выяснил, почему оставались эти .dep-файлы -- оно их создает, но не удаляет при "make -n". На эту тему сегодня написан bugreport -- #7575.
02.07.2004: во-первых, в любом случае стоит вынести все настройки, специфичные для кросс-компиляции, из drivers/cm5307/*_drvlets/LocalRules.mk в какие-нибудь отдельные .mk-файлы.
Во-вторых, самое простое решение -- собирать все это хозяйство под ppc в отдельной директории, указывая какие-то совсем отдельные параметры, и пусть оно в exports/ в этой отдельной директории складирует вместо обычных вещей все под target-платформу. В принципе, это работоспособный вариант.
"Идеальный" же вариант -- иметь внутри cx/src/ (или снаружи -- не суть важно) этакую директорию, в которой можно сказать "make что-нибудь", и оно вызовет сборку ТЕХ СТАНДАРТНОСТЕЙ, но так, чтобы .o- и прочие файлы валились не туда же, где исходники, а в другую директорию. Как это делается при компиляции gcc.
При любом варианте надо будет начинить "затрагиваемые" Makefile'ы/LocalRules.mk проверками на "FOR_CROSS_TARGET", чтобы некоторых действий не предпринималось -- например, совсем незачем при компиляции под ppc лазить в drivers/cm5307/.
И, кстати: ведь надо
будет-таки подшаманить ВСЕ абстрактные драйверы, чтобы они использовали
"DRIVERLOCAL" и могли бы компилироваться в
"cm5307camac*_drv.so".
03.07.2005: поскольку позавчера придумана/осознана замена для "DRIVERLOCAL" (privrec), то сей вопрос закрыт, а "пожелание" помещено в <S>.
CPPFLAGS=$(DEFINES) -I$(SRCDIR)/include
, и все! Нету
ни "LOCALDEFINES", ни "LOCALINCLUDES" -- в результате пользователям
Rules.mk приходится всячески выпендриваться, например, делая ПОСЛЕ
include оного всякие "CPPFLAGS+=...".
Лучше бы для гибкости ввести такие "вакантные места".
06.08.2006: это все сделано в 4cx/src/GeneralRules.mk, так что тут -- "obsolete".
12.10.2004: собрал gcc-3.4.2. Конфигурировал его (в /tmp/g3/{gcc-3.4.2,objdir}/) командой
../gcc-3.4.2/configure --prefix=/tmp/gcc-3.4 --enable-languages=c,c++,f77
с последующими "make bootstrap" и "make install". Без
"--enable-languages" оно затыкается где-то во внутренностях Явы.
Кроме того, пришлось удалить из $PATH упоминание об /usr/openwin/bin, иначе были проблемы с msgfmt, который зачем-то имеется и в xview-clients-*.rpm. На будущее в /etc/profile эта директория теперь добавляется в КОНЕЦ $PATH, плюс найден bugreport на аналогичную тему (#6728), к которому добавлен comment#4.
13.10.2004: проба компиляции с gcc-3.4. Что обнаружено:
((char*)(src))+=bytesize
". Причина оных -- "src"
является указателем на какую-нибудь структуру (либо
void*
). Таковые места -- n_{read,write}
, три
места в cxlib.c.
В принципе, это меняется на конструкцию вида
"*((char**)(&src))+=bytesize"
. А стоит просто такой
макрос сделать -- INC_PTR_BY_BYTES(p,bytes)
.
sl_enqueue_timeout_after()
, хотя
там был забыт return
-- пофиксено.
14.10.2004: проба компиляции с gcc-4.0-20041010.
Поскольку "cast expressions as lvalues" теперь превратилось в
ошибку "invalid lvalue in assignment", ПРИШЛОСЬ создать макрос
INC_PTR_BY_BYTES(ptr,bytes)
и начать его использовать,
иначе из-за misc_macros.h обламывалось сразу.
Как бы то ни было, использование макроса вместо того gcc'шного хака-extension'а есть "the right thing".
Впрочем, теперь вместо этого error'а имеем довольно зверский и слабопонятный warning -- "warning: dereferencing type-punned pointer will break strict-aliasing rules" :-). Надо разбираться, как его загасить..
30.11.2012: warning гасить не будем -- он особо не мешает, а скорее напоминает о наших хитромахинациях.
В остальном же -- цели выполнены, "done".
23.01.2013: всё-таки надо избавляться от этого приёмчика -- инкрементирование типизированного указателя на сколько-то БАЙТ -- полностью.
-O2
и выше
будут глюки, ибо подобные махинаторские приёмчики запрещены стандартом
C.
uint8 *ptr
, а при надобности структурированного обращения
по этому адресу делаем копию в указатель нужного типа". Жертвы --
fdiolib.c, connlib.c, misc_n_read.c,
misc_n_write.c, cxlib_client.c,
cx-server_channels.c.
INC_PTR_BY_BYTES(ptr,bytes)
удалено.
ABSTRZE()
+CNCRTZE()
.
20.01.2005: а вылезло это из-за того, что у istc/xmclients/ появилась временно-своя библиотечка -- Chl_histplot.c, которую надлежало как-нибудь корректно вставить в процесс компиляции/сборки.
Так что, нечего делать -- добавил в
programs/xmclients/LocalRules.mk к определению
ALLSOURCES=
еще и $(LIBSSOURCES)
.
А теперь надо будет подобное подобавлять еще и ко всем остальным **/LocalRules.mk. Ээээ... А ведь были какие-то соображения на тему "указывать в мэйкфайлах имена без расширения .c"...
06.08.2006: аналогично -- «это все сделано в 4cx/src/GeneralRules.mk, так что тут -- "obsolete"».
28.02.2005: прежнее название -- src/include/bigc/ (появилось 09.12.2004, для bigc_adc333.h), и целью было -- чтобы там лежали описания/разблюдовка параметров больших каналов.
Но по здравому размышлению стало ясно, что стоит иметь также описатели и раскладки обычных каналов -- для всяких экзотических драйверов типа пирометра и прочих КШД. Т.е., это никакое не "bigc", а "описание интерфейса драйвера", посему и название -- drv_i, а сами файлы будут иметь имена вида NAME_drv_i.h.
Предполагается, что, поскольку это именно описание ИНТЕРФЕЙСА, то даже при нескольких разных реализациях драйверов одного и того же блока все они будут использовать ОДИН _drv_i.h-файл.
Вначале "жертвой" пало описание istc/drivers/impacis10_drv_i.h, потом -- istc/drivers/kshd485_drv_i.h (оно называлось уже почти правильно, просто без "_i"), а теперь пришла пора и того, что в собственно cx/ -- adc333.
Осталось только следующее:
14.02.2006: мда, уже почти год прошел :-) Включаем include/drv_i/ в процесс сборки/экспорта:
SUBDIRS
.
16.02.2006: "приличия для" -- чтобы при отсутствии файла make обламывался прямо в drv_i/, еще на стадии "all", добавлена строчка:
files: $(DRV_INTERFACES)
06.08.2006: да, а сейчас -- "уже почти полтора года прошло", так что, как давным-давно надо было, "done".
$(LOCALLIBDEPS)
, а то пока оно есть только в
programs/xmclients/.
04.05.2005: кхм -- обобщил на programs/utils/. Провести полное обобщение затруднительно -- в Rules.mk точно не вставить, надо делать это именно в каждом конкретном LocalRules.mk. Строчки вида
$(TARGETFILES): $(LOCALLIBDEPS) $(...)
В частности -- ввел сие также и в programs/server/drivers/LocalRules.mk (хотя там это нафиг не нужно, но для полноты картины).
А вот в cm5307/{ppc,uclinux}_drvlets/LocalRules.mk вставлять не стал -- вот уж точно нафиг, нафиг. И в chlclients/ тоже.
Короче -- "done".
09.01.2013: с этим LOCAL_LIBDEPS
(теперь имя через подчерк) в его нынешнем виде есть одна проблема: он
указывается ЦЕЛИКОМ для ДИРЕКТОРИИ, а указывать подобные вещи для
отдельных target'ов индивидуально -- нет возможности.
Что просто ИСКЛЮЧИТЕЛЬНО неудобно, когда в одной директории имеются программы, требующие взаимоисключающих библиотек (например, с перекрывающимися символами внутри). Сейчас это вылезло на zsukhclient:pzframe и sukhclient:fastadc, а также может вылезти и на разных вариантах cxscheduler'а.
Ну и какое решение? В принципе можно ввести в
дополнение к *_COMPONENTS еще и *_LIBDEPS, содержимое которой в
_MKR_LINE
дописывать в зависимость ПОСЛЕ списка
*_COMPONENTS.
...сначала мысль, что придётся для работающести оного переставить в
TopRules.mk все определения LIB*:=
в точку ДО
include GeneralRules.mk. Но, скорее всего, оно не потребуется,
поскольку содержимое _MKR_LINE
парсится уже на этапе
отработки, когда все makefile'ы считаны.
Сделано -- работает.
- Список бинарников ВСЕГДА указывается в
$(EXES)
, вне зависимости от конкретной директории -- никаких там "XMCLIENTS", "UTILS", etc.Единственное исключение -- драйверы и прочие .so-файлы, для них что-нибудь придумаем.
В любом случае, список указывается БЕЗ расширений.
- Локальные библиотеки указываются другим символом.
- Библиотеки, которые из этой директории идут в exports/ -- еще каким-нибудь.
- Аналогично -- составные части для бинарников, если они не-однофайловые.
- ОБЯЗАТЕЛЬНО надлежит поддерживать "hook'и" типа
$(LOCALLIBDEPS)
.
Пока готовимся, а окончательно это введем -- с самого начала! -- в CXv4.
24.08.2005: BTW, ведь если мы сделаем-таки "все
правильно", включая автоматическую сборку параметра
$(ALLSOURCES)
(см. ниже раздел про SUBFLSSOURCES),
то можно будет также и перенести "include $(ALLDEPENDS)"
в
Rules.mk
.
06.08.2006: итак, уже в третий раз -- «это все сделано в 4cx/src/GeneralRules.mk, так что тут -- "obsolete"».
30.11.2012: да, там это сделано.
14.06.2005: главная проблема была в том, что зависимости-то для .so'шек указать можно, а вот autodepend оно б не делало.
Так что добавил в drivers/LocalRules.mk "входной параметр"
SUBFLSSOURCES
, используемый при организации списка
$(ALLDEPENDS)
. Так что локальная проблема решена.
Вообще-то это ОБЩАЯ
проблема, и нужно какое-то общее решание для таких
"автоматизированных" директорий -- типа drivers/,
utils/, xmclients/. Сейчас местами поддерживаются
LIBSSOURCES
, а нужно еще нечто для под-файлов.
Варианты:
$(SUBFLSSOURCES)
.
Просто, но немодульно.
Так вот -- можно для таких составных target-файлов задавать
переменные типа "DEST_COMPONENTS" (в данном случае это будет
marcankoz_drv_COMPONENTS=...
), а уж LocalRules.mk
пусть собирает списочек ALLSOURCES
при помощи
$(foreach)
, командой наподобие
ALLSOURCES=... $(foreach T, $(TARGETFILES), $($T_COMPONENTS))
Единственное что -- все это (да и вся система сборки вообще) очень Си-центрично. А как сделать полиязычным -- хз. Ну да и ладно :-).
25.12.2005: записываю задним числом, но лучше уж поздно, чем никогда: все перечисленные проблемы решены в системе сборки 4-й версии -- с GeneralRules.mk, см. bigfile-0002.html::"Система сборки", первую же запись.
Там сделано по проекту "dest_COMPONENTS", максимально общим способом -- и для бинарников, и для .so'шек, и для .a-файлов, и без Си-центричности.
06.08.2006: так что тут -- "obsolete".
а?.PHONY: firsttarget firsttarget: all
Это явно наследие от oldcx/ncx/curcx -- где, видимо, встречалось
указание terget'ов и ДО include
, а сейчас, похоже
(проверено grep'ом), можно сходу отовсюду эти заклинания поубирать,
поставив их в самое начало
Rules.mk.
02.11.2005: почему -- кажется понял, на примере
istc/xmclients/Makefile: пришлось указывать "компоненты"
программы "lebed", и вставлять зависимость программы от этих
"компонентов". А из-за того, что зависимость стоит ПОСЛЕ
include
, они и вставляются ПОСЛЕ всяких libcda, что дает
некорректную командную строку линковки.
А как только добавит это "firsttarget: all", так
преспокойно стало можно вставить зависимость ПЕРЕД
include
, и все заработало.
В идеале, конечно, проблема должна решаться системой сборки
автоматически -- чтоб автоматом подсовывались "компонентозависимости"
ПЕРЕД всякими библиотеками. Вопрос только -- как? Если
ALLSOURCES
еще худо-бедно соберется
$(foreach)
'ем, то как ставить такие
per-target-зависимости в команды?
03.11.2005: Есть, конечно, идейка -- заклинание вида
$($@_COMPONENTS:.c=.o)
... 30.11.2012: да, в конечном итоге в эту сторону и
сделано.
Попробовал. Почему-то хренушки -- просто не видит оно этого, и все тут...
Чуть позже: разобрался, почему -- потому, что "автоматические
переменные" существуют (в смысле, выполняется их подстановка) только в
момент выполнения команды, а в момент чтения списка-зависимостей их не
существует. Это четко прописано внизу раздела "10.5.3 Automatic
Variables" по крайней мере в www-фарианте, вместе с описанием
переменных $$@
(то, что нам нужно!),
$$(@D)
и $$(@F)
. Оное, судя по
ChangeLog'у, появилось 2002-07-10.
Так что -- или в системах выше RedHat-7.3 можно пользоваться
$$@
, или -- там же написано что-то на тему "In a native
GNU make file there are other ways to accomplish the same
results.", что стоит исследовать.
После обеда -- исследовал, ответ заключается во фразе "Static
Pattern Rules". Точнее -- вроде бы должен был бы заключаться.
Проблема в том, что внутри $(...)
символ "%",
похоже, не распознается. Что должно было бы распознаваться --
"$*", но оно-то работает только в командах!
В конце концов полез гуглить в архиве мэйллиста bug-make@gnu.org, и
сразу же наткнулся на
изложение
аналогичной проблемы за 11-Mar-2005, ответом к которому прилагалось
объяснение, почему такое работать не будет, и ссылка на функцию
$(eval)
, введенную, похоже, как раз для таких целей -- по
крайней мере, ее описание в make.info содержит пример как раз такого
использования.
И, кстати, там же объясняется, почему и $$@
работать не
будет -- по той же причине, т.е., потому, что expansion производится ДО
pattern-matching'а.
04.11.2005: (придумано в душе; что-то очено
много идей приходят мне в голову именно в душе :-) ну я и
осел! Ведь решение-то было прямо перед носом -- и
Paul
D. Smith на него указывал фразой "Then your only way is to use
the auto-re-exec trick." в ответ, что человеку надо поддерживать
3.79.1, и сам я пользовался этой фичей с незапамятных времен -- и с
.d-файлами, и с автогенерацией per-interface-правил для
абстрактных драйверов (i_*_rules.mk), и даже еще давным-давно
-- в 1999г. -- для очень развесистых (под сотню строк)
per-encoding-правил в Cyr-RFX.
Итак -- надо просто вытаскивать эти per-exe-определения в отдельные
файлы (например, .mkr), которые будут автогенериться, а потом
include
'иться. Поскольку списки компонентом могут
меняться, то всем таким .mkr-файлам надо уставлять зависимость
от самого Makefile. Итого:
%.mkr: Makefile $(MAKEFILE_PARTS) echo $*: $($*_COMPONENTS:.c=.o) >$@а где-нибудь попозже (в будущем -- рядом с
include
$(ALLDEPENDS)
, которое переедет в Rules.mk) -- что-то
типа include $(ALLTARGETRULES)
).
Упоминание $(MAKEFILE_PARTS)
-- на случай, если
директория будет содержать "составной" Makefile, чтобы в этой
переменной можно было указывать список доп. Makefile'ов, и
чтоб .mkr-файлы пересоздавались при их изменении.
05.11.2005: попробовал -- работает: .mkr-файлы с нужным содержимым создаются и перегенерятся когда надо, и, естественно, include'атся.
На будущее осталось только вставить в Rules.mk в список
зависимостей в нужную точку ссылку на
$($@_COMPONENTS:.c=.o)
, ну и .SUFFIXES: .mkr
;
ну а все остальное -- добавление оных к $(...DEPENDS)
--
подразумевается :-)
Оставался еще один вопрос (точнее -- он является частью большего вопроса): а что делать с составными не-exe-файлами -- у них же имена имеют вид, например, "somelib.so"? Ответ оказался прост -- GNU Make позволяет имена переменных вида "somelib.so_COMPONENTS".
06.08.2006: короче -- для CXv4 все сделано, так что помечаем тут как "obsolete" ("done", хотя реально как-бы-и-так, было бы некорректно).
30.11.2012: а теперь -- и РЕАЛЬНО есть и в v2, но переводить в "done" уже не станем.
26.02.2019: вылез косяк из-за принудительной ссылки на Makefile в указании зависимостей:
%.mkr: Makefile $(MAKEFILE_PARTS)
x10sae:~/work/tests% make -f test_sc_Makefile.mk /home/user/work/4cx/src/GeneralRules.mk:391: test_sc.d: No such file or /directory /home/user/work/4cx/src/GeneralRules.mk:391: test_sc.mkr: No such file or /directory make: *** No rule to make target `test_sc.mkr'. Stop.
touch Makefile
" ситуацию чудесным образом
вылечила.
$(MAKEFILE_LIST)
, первым в этом списке и будет
"базовый" файл, который был указан в -f
.
Замена
Makefile
на
$(firstword $(MAKEFILE_LIST))
решила проблему.
P.S. Предыдущая дата на GeneralRules.mk была 23-07-2014.
$(LIBnnn)
-- содержались в какой-нибудь
одной секции, легко заменяемой для других проектов.
Либо -- вообще переобозвать его во что-нибудь типа CommonRules.mk, а уж специфика пусть лежит в небольшом Rules.mk, напрямую включаемом всеми Makefile'ами, и, в свою очередь, включающем уже эти "универсальные" определения.
06.08.2006: да, именно так, как раз по
приведенному здесь проекту там и сделано -- GeneralRules.mk,
подходящий для произвольных проектов, включающих его из своих
Rules.mk, а уж в оных всякие $(LIBnnn)
и
определяются.
Посему -- "done".
D
,V()
возникает ошибка при попытке подсунуть
V()
параметр типа {a,b}
-- ругается, что
передано слишком много параметров вместо требуемого одного.
06.11.2005: собственно -- столкнулся с этим в lebed_{db,data}.h, там же сразу и нашел решение. Попытка "в лоб" -- заключить параметр в дополнительные круглые скобки -- ничего не дала: при сим компилятор ругается "braced-group within expression allowed only inside a function" (черт знает, что он имеет в виду и почему).
Зато отлично сработал другой вариант -- "macro varargs". Так что -- надо ВЕЗДЕ определять как
(Самое обидное, что в cpp.info вообще НАПРОЧЬ отсутствует комментарий, как можно решать такую проблему -- абсолютно легитимную! Посему я слегка обозлился и написал вопросительное письмо на gcc@gnu.org.)#define V(value...) = value
/usr/bin/ld: bigredcur.o: relocation R_X86_64_32 against `a local symbol' can not be
used when making a shared object; recompile with -fPIC
и
/usr/bin/ld: ../../../lib/tsycamlib/libtsycam.a(tsycamlib.o): relocation R_X86_64_32S
against `param_info' can not be used when making a shared object; recompile with -fPIC
26.06.2006: итак:
Суть ее заключается в том, что в .so-файлы НЕЛЬЗЯ линковать код, откомпилированный без ключа -fPIC.Смысл такой: раз уж мы делаем .so-файл, каковой может динамически влинковываться в любую программу и с любого начального адреса, то и все его компоненты должны быть position-independent.
А на x86, вследствие некоторой хохмы этой архитектуры, не-PIC-код в динамических библиотеках поддерживается (поэтому-то раньше ошибка и не вылезала), но линкер просто производит донастройку "по месту", размножая такие страницы и превращая тем самым код в совсем НЕ shared object.
И -- на многих процессорах (в т.ч. x86) PIC приводит к некоторой потере производительности. А вот на x86_64 -- нет!
Теперь предметно: первое, где это вылезло -- в
bigredcur.so. Там был просто тривиальный недочет -- забыл
строчку bigredcur.o:CFLAGS+=-fPIC
. Вставлена.
В остальных же местах -- некая неясность. Ну ладно, так и быть -- для tsycamlib.o флажок также включен. А вот libcx.a собирать с таким флагом уже жаба душит -- так что так-никогда-и-не-доделанный chaincx_drv.c просто отключен из компиляции. 07.06.2012: а теперь chaincx_drv.c и вовсе удалён, за полной (сейчас) ненадобностью (был сей артефакт за 27-05-2005).
В идеале, наверное, надо будет (в CXv4?) иметь ДВА набора
.a-библиотек -- обычные, libZZZ.a, и PIC -- libZZZ_pic.a;
плюс, естественно, _pic.a-варианты должны собираться из
_pic.o-файлов. Все эти правила должны присутствовать в
GeneralRules.mk, и: дело директории с библиотекой -- указать
среди целей _pic.a-вариант, а Rules.mk -- символы
вида LIBZZZ_PIC
, на которые могли бы ссылаться в списке
компонентов .so-файлы.
27.11.2012: а не слишком ли это? Пожалуй, правильнее/проще будет те библиотеки, что ТОЧНО потребны для прямой линковки в .so-файлы -- ВСЕГДА делать с -fPIC (тем более, что в AMD64/EM64T/x86_64 есть адресация относительно RIP, так что PIC-код вполне эффективен); а прочие, типа libcx* -- пусть уж линкуются не в .so, а в их "загружателей".
А пока -- пришлось вводить "мелкий хак": вместо
"-L/usr/X11R6/lib/
" там теперь стоит
"-L$(MOTIF_LIBS_DIR)
", и уж эта переменная смотрит куда
надо в зависимости от архитектуры.
В общем, собирается, помечаем как "done".
24.09.2008: насчет "MOTIF_LIBS_DIR" -- я тогда несколько схалтурил -- есть (по крайней мере, СЕЙЧАС :-)) ссылка на /usr/X1R6/lib еще в programs/utils/LocalRules.mk, оно там из-за надобности в Xt'шном планировщике.
Так что, корректности ради, сделал, что в Rules.mk в
зависимости от типа процессора определяется теперь
X11_LIBS_DIR
, и дальше стоит
MOTIF_LIBS_DIR=$(X11_LIBS_DIR)
, и во второй точке
использования также теперь ссылка -L$(X11_LIBS_DIR)
.
26.09.2008: тогда, в 2006-м, почему-то упустил
другое место -- chlclients. Так что сейчас и в
programs/chlclients/LocalRules.mk также добавлено
bigredcur.o:CFLAGS+=-fPIC
.
27.11.2012: just for record: также
-fPIC
было забыто в 4cx'ных programs/drivers/ и
lib/srv/ (для cxsd_plugins). Ну и в work/drivers/
тоже будет бо-бо. Плюс libpzframe_drv -- оно ж сейчас линкуется прямо
к драйверам, а не в сервер...
int
, long int
и void*
отличаются...
Да, проверил -- даже cda скопычивается каким-то дюже странным образом...
27.06.2006: предпринял некоторые "нулевые" действия:
size_t
и
с разницей указателей (который ptrdiff_t
) --- с
"%d" на "%ld".
Плюс, когда мы пользуемся "%*" то надо параметр
"точность/ширина" преобразовывать к int
, ибо именно этого
ожидает "*".
И -- вся печать типа time_t
ныне пользуется
"%lu", а само оно, естественно, преобразуется к
(unsigned long)
.
Для решения изготовил пару функций-макросов в misc_macros.h --
("lint" -- "long int") и порасставлял их в соответствующих местах (это практически всегда либо в callback'е, либо в его уставке).static inline void *lint2ptr(long v){return (void*)v;} static inline long ptr2lint(void *p){return (long )p;}
include"cx_proto.h"
из cxd.c и
cxd_config.c... Но потом решил забить -- слишком уж в этом
трехпроцессном сервере все поперезавязано.
Заодно пришлось унести реально нафиг никогда не нужный
DumpHdr()
из cx_macros.h в единственного
пользователя --- cxlib.c. А cx_macros.h теперь и
вовсе опустел -- все ценное давно ушло в misc_macros..
И -- ура-а-а, все работает!!!
Так что -- у нас теперь есть поддержка 64-битных ОС.
P.S. Также проапдейтил 4cx/ -- скопировал туда
include/{misc_types.h,misc_macros.h},
scripts/Sysdepflags.sh. А также добавил
bigredcur.o:SPECIFIC_CFLAGS=-fPIC
в
lib/Xh/Makefile.h и подфиксил fdiolib.c с
lint2ptr()
/ptr2lint()
. Более там
подозрительных мест не видно.
13.05.2011: кстати, после введения тогда для
печати size_t
спецификатора "%ld" вылезла другая
проблема -- валилось нечто в стиле (gcc-2.96@RH-7.3)
cxlib.c:1004: warning: long int format, int arg (arg 6)
Замена на очевидно более правильный "%lu" ситуацию исправила слабо --
cxlib.c:1004: warning: long unsigned int format, size_t arg (arg 6)
Но гугление подсказало, что ПРАВИЛЬНЫЙ вариант в этих случаях --
"%zu". Модификатор "z" появился в C99, и
gcc-2.96+libc6 даже в RH-7.3 2002 года уже его поддерживают. Для
ptrdiff_t
там предусмотрен модификатор "t". Для
ssize_t
, очевидно, предназначен "%zd". (Прикол
в том, что это C99-only (§7.19.6.1.7), а в C++0x, даже за 2009г.,
никаких упоминаний на эту тему вроде нету. С другой стороны --
printf-то один и тот же, так что работать бы должно.)
При переходе на "%zu" ругательства резко исчезли. Учитывая, что у нас и так используется кучка фич C99/gcc-extensions, можно смело переходить на этот спецификатор.
16.05.2011: ...но только не в uclinux_drvlets/ -- там используется egcs-2.91.66 19990314, ничегошеньки про модификатор "z" не знающий.
Так что пришлось там ставить формат "%lu" и явно кастить
параметры к (unsigned long)
.
Надоело, конечно, дико... Совсем, что ли, избавляться от uClinux? Неа, надо всё-таки вначале проверить -- вдруг uClibc РЕАЛЬНО поддерживает "z" (ой, не верю...).
29.12.2011: в дополнение к функциям, НЕ могущим
быть использованными в инициализаторах, добавлены макросы
LINT2PTR()
и PTR2LINT()
.
29.11.2012: продолжение возни с 64-битностью (SL-6.3/x86_64):
(int)
.
...а вообще надо бы от этого хака избавляться (пользоваться нормальным препроцессором и получать токены в виде NUL-terminated-строк, а не парой указателей-"скобок").
accept()
'у и
getsockopt()
'у надо передавать не int*
, а
socklen_t*
.
int64
прямо в misc_types.h.
27.06.2006: BTW, статистика, какие где размеры (по данным программы sizeofs.c):
Arch | char | short | int | long | long long | void* | size_t | float | double | long double |
---|---|---|---|---|---|---|---|---|---|---|
x86 | 1 | 2 | 4 | 4 | 8 | 4 | 4 | 4 | 8 | |
x86_64 "-m32" | 1 | 2 | 4 | 4 | 8 | 4 | 4 | 4 | 8 | 12 |
x86_64 "-m64" | 1 | 2 | 4 | 8 | 8 | 8 | 8 | 4 | 8 | 16 |
SGI MIPS "-mabi=n32" | 1 | 2 | 4 | 4 | 8 | 4 | 4 | 4 | 8 | |
SGI MIPS "-mabi=64" | 1 | 2 | 4 | 8 | 8 | 8 | 8 | 4 | 8 | |
alpha | 1 | 2 | 4 | 8 | 8 | 8 | 8 | 4 | 8 | |
07.03.2023 E2K -m32 | 1 | 2 | 4 | 4 | 8 | 4 | 4 | 4 | 8 | 16 |
07.03.2023 E2K -m64 | 1 | 2 | 4 | 8 | 8 | 8 | 8 | 4 | 8 | 16 |
16.03.2023 E2K -m128 | 1 | 2 | 4 | 8 | 8 | 16 | 8 | 4 | 8 | 16 |
17.03.2023 aarch64 | 1 | 2 | 4 | 8 | 8 | 8 | 8 | 4 | 8 | 16 |
Итак, выводы насчет интересующих нас платформ:
long long
-- всегда 8, т.е., это и есть
int64
.
size_t
всегда равен размеру указателя -- что
вполне логично.
long
ВСЕГДА равен
размеру size_t
(и, следовательно, размеру указателя). Так
что можно всегда для size_t
использовать "%ld".
long
всегда равен размеру
указателя, то можно и нужно преобразования int<->pointer
(требуемые для передачи чисел callback'ам) производить именно через
long
.
Итого -- сделал в misc_types.h int64
и
uint64
(это первое изменение с 01.10.2002).
Соответственно, из
drivers/canbus/{cac208,canadc40,candac16}_src.c дурацкие
махинации с "условным" определением int64
внутри
scale32via64()
выкинуты.
23.04.2008: собственно -- а чего это у нас
scale32via64()
дублируется в куче файлов?! Нефиг -- надо
ее переселить в misc_types.h. Сделано, а из
drivers/canbus/*_src.c определения убраны.
Замечание: НЕ в misc_macros.h, в отличие от
близкородственного RESCALE_VALUE()
, потому, что в отличие
от него, это все-таки не "хитрая" функция, а элементарная операция на
уровне типов, так что пусть рядом с их определением и живет.
В 4cx/ также скопировано.
01.07.2008: полезная статейка, Р.Кусков дал ссылку -- "20 ловушек переноса кода Си++ на 64-битную платформу (исходники)".
02.07.2008: и еще одна ссылка от Ромы -- Google C++ Style Guide (есть и локальная копия, но она почему-то не показывается...).
18.08.2014: в misc_types.h добавлен
char32
, в интересах v4 (первое изменение с 24-01-2010).
17.06.2021: чисто для информации:
short float
", т.е.,
16-битного.
float
и
имеет те же 8 бит экспоненты, но 7 бит мантиссы вместо 23 (т.е., просто
младшее uint16-слово обрезано). Так что даже если б и была поддержка
"short float
" в GCC на x86/x86_64, то проку с того нет --
форматы разные.
А полез я разбираться с этой темой из-за того, что некий контроллер вакуумного насоса (из Выборга, кажется) работает по Modbus-TCP, но данные там в собственном 16-битном вещественном формате, слегка напоминающем тот IEEE-754'шный binary16, но с 6-битной экспонентой (расширенной с 5 бит за счёт знакового -- числа у насосов всегда НЕотрицательные).
07.03.2023: проверил также E2K (E8C-SWTX) -- там в
режимах "-m32
" и "-m64
" (да, есть оба; по
умолчанию -- 64) результаты ровно такие же, как на прочих ранее
протестированных 32- и 64-битных платформах.
Кстати, заодно проверил на x86_64 и E2K тип "указатель на функцию" в виде
"void(*)(void)
" -- он формально (на гарвардских архитектурах)
может отличаться от просто указателей. Так вот, нет -- совпадает.
ЗЫ: для проверок сделана новая программулька -- work/tests/print_sizeofs.c, собираемая своим скриптом mk_print_sizeofs.sh сразу в вариантах "-m32" и "-m64".
15.03.2023: недавно встретил где-то (доки по gcc?)
упоминание типа long double
. Ну -- решил и его добавить в
проверку. Результат -- на тех системах, что остались в доступе -- добавлен
в изначальную табличку.
На x86_64 результат легко объяснился чтением "info gcc" -- ключики
-m96bit-long-double
и -m128bit-long-double
,
которые являются default'ными для 32- и 64-битных программ соответственно,
по требованиям ABI.
(Кстати, там же есть комментарий: "Notice that neither of these options enable any extra precision over the x87 standard of 80 bits for a 'long double'.".)
В любом случае: даже сейчас уже виден изрядный разнобой и слабая стандартизация данного типа, так что использовать его точно не стоит (да и не требуется на уровне СУ.
16.03.2023: поскольку E2K поддерживает и
"-m128
" (только там это не "адрес 128 бит", а некий
"дескриптор", и это
"защищённый" режим -- см.
"Эльбрус/ptr128"
и
"Режим Безопасных Вычислений"),
то попробовано и оно -- результаты в той же таблице.
Главное: это первый случай НЕсовпадения размера указателя и
long
/size_t
.
17.03.2023: попросил Лёшу Герасёва снять показания с ARM64="aarch64" -- результаты также в таблице. Полное совпадение с x86_64.
04.04.2023: гугля по "ptrdiff_t" (omniORB под e2k_128 имеет проблемы, не пользуясь этим типом) наткнулся на статью "About size_t and ptrdiff_t" от некоего Andrey Karpov за Sep 21 2009.
И там есть похожая табличка, только размеры в битах. Там фигурирует некая UNICOS, на которой ВСЁ 64-битное, включая short.
05.04.2023@вечер, перед засыпанием: встретилось в Телеге, что "в мире уже есть 4 процессорных архитектуры, активно использующих или планирующих использовать UEFI ...: x86, AArch64, SystemReady, LoongArch и RISCV64".
06.04.2023: погуглил:
dlopen()
32-bit
.so? А наоборот? Test it @carme!
08.11.2006: да, проверил. Результат -- НЕЛЬЗЯ. Детали/подробности таковы (это копия из письма Нишимуре):
First, all .so files *must* be built with "-fPIC" switch, as opposed to x86, where it is optional. In fact, *all* systems except x86 do require -fPIC, and only x86 allows non-PIC code to be relocatable.
Second, x86_64 does NOT allow mixing 32- and 64-bit .o/.so files (see 1st attachment, which creates several binary and .so files):
(gcc *does* allow to link a mixture of 32- and 64-bit .o files, only issuing a warning, but that mix doesn't work.)% uname -a Linux carme.inp.nsk.su 2.6.17-1.2141_FC4 #1 Fri Jun 30 14:53:14 EDT 2006 x86_64 x86_64 x86_64 GNU/Linux % ./32and64.sh % ./ldr32 ./func32.so v=12345, func(v)=-12346 % ./ldr64 ./func64.so v=12345, func(v)=-12346 % ./ldr64 ./func32.so ./ldr64: dlopen(./func32.so): ./func32.so: wrong ELF class: ELFCLASS32 % ./ldr32 ./func64.so ./ldr32: dlopen(./func64.so): ./func64.so: wrong ELF class: ELFCLASS64 % gcc -o mix -m32 caller32.o func64.o && ./mix /usr/bin/ld: warning: i386:x86-64 architecture of input file `func64.o' is incompatible with i386 output v=12345, func(v)=-134513741 ^^^^^^^^^^ garbage % gcc -o mix -m64 caller32.o func64.o && ./mix /usr/bin/ld: warning: i386 architecture of input file `caller32.o' is incompatible with i386:x86-64 output zsh: segmentation fault ./mix
06.03.2007: да, сделано -- в Rules.mk добавлена поддержка, которой управляет переменная/параметр PROFILABLE (по аналогии с DEBUGGABLE), которая по умолчанию -- не-YES. В 4cx/src/GeneralRules.mk также добавлено.
Заодно пришлось в ppc860_rules.mk и в
uclinux_drvlets/LocalRules.mk вставить
override PROFILABLE=NO
.
12.11.2012: как выяснилось, PROFILABLE бессмысленно без DEBUGGABLE -- gprof говорит "no symbols". Оно и понятно :-)
addrlen
в BSD-системах, из-за чего еще в 1997-м
был введен sysdeps-параметр OPTION_BSD_ADDRLEN_DELTA
--
это не "глюк" BSD, а моя недограмотность.
24.11.2007: дело в том, что какой-то долбень в
4.4BSD в struct sockaddr_un
ПЕРЕД полями
sun_family
и sun_path
добавил еще поле
u_char sun_len
. Отсюда и берется лишняя 1, а вовсе НЕ из-за
завершающего NUL'а.
Так что, "правильная" формула для вычисления addrlen --
offsetof(struct sockaddr_un, sun_path) + strlen(udst.sun_path)
а не наше
sizeof(udst.sun_family) + strlen(udst.sun_path)
И, в принципе, существует макрос SUN_LEN
, делающий
примерно то же самое. Но обойдемся уж как-нибудь сами :-).
А вообще, если погуглить на тему [offsetof sun_family] -- то находится море топиков, так что это проблема широкораспространенная, на сии грабли не я один наступал. Кстати, история вопроса внятно описана в http://mail-index.netbsd.org/tech-kern/1997/02/25/0014.html.
Кстати2 -- с завершающим NUL'ом (считать его или нет) полно своих
собственных непоняток -- какая-то BSD'шная документация говорит, что
считать, где-то -- что не надо, и определения SUN_LEN
также бывают разными. Из моего опыта -- вполне правильно НЕ считать
этот NUL, при условии, что используется формула с
offsetof()
'ом.
Вывод: переходить на формулу с offsetof()
'ом, а
OPTION_BSD_ADDRLEN_DELTA
-- искоренять.
26.05.2008: да, под-искоренил -- и из обоих sysdeps/-файлов убрал, и в исходниках вписал правильные вычисления (затронуты оказались cxlib.c, cx-porter.c и -- о, ужас! :-) -- cxsd_fe_cx.c). В 4cx/ пока сетевого кода нету, так что там просто обновил sysdeps-файлы.
В Linux работает, а вот под BSD пока что проверить не было возможности. Постараюсь попробовать под OpenBSD.
29.01.2009: проверил под OpenBSD-4.2 -- работает. Засим "done".
29.12.2022: скопирую сюда цитату из письма Иртегову на эту тему:
ЗЗЫ: за всё-всё зуб не дам, но из того зверинца, до которого я дотянулся в 1997-м, BSD'шное поведение было только у чистых BSD -- BSDI, FreeBSD, NetBSD, OpenBSD. А у Linux, IRIX, DEC OSF/1, Unixware и Solaris -- нет; что любопытно, у SunOS, которая 4 и вроде как BSD-based -- вроде бы тоже нет (это неточно, но раз SunOS был на основе 4.3BSD, то самоочевидно). Про OSX/Darwin не скажу -- её тогда не существовало.
19.05.2009: а это, случаем, не тот ли прикол с удалявшимися include/*.h(@), задокументированный 05-04-2009?
19.08.2009: неа, это НЕ ТОТ прикол.
Наблюл подобное на другой машине -- v5p1@CentOS-5.3, теперь на work/liu/, вполне воспроизводимо. Разобрался -- причина в драйверах, которые#include
'или файлы из /export/viper/bolkhov/work/cx/exports, а на ТОЙ машине НЕ БЫЛО никакого /e/v/b, так что оно и обламывалось.Собственно проблема была в том, что maintainer-clean делается НЕ за ОДИН проход, а зависит от distclean, который, в свою очередь, от просто clean; и вот собственно ПЕРВЫЙ же проход обламывается, так что .d-файлы с "неправильными" ссылками остаются.
Так что вопрос скорее в другом -- а почему ОСТАЛЬНОЕ не обламывалось? Расследование показало, что в рамках ОДНОЙ директории "make -k maintainer-clean" всё-таки отрабатывался, а вот если надо зайти в поддиректорию -- то уже ёк.
И, черт возьми -- а почему бы не подселить сюда модель *clean из GeneralRules.mk?!
20.08.2009: да, переделал все *clean по образу GeneralRules.mk.
Происходит это из-за "set -e" -- что прерывается shell-цикл "for", который вне контроля make. И при каждом следующем запуске оно всё-таки проходит на один цикл дальше.
Собственно: а ведь реально В ЭТОМ и был корень той проблемы недоочисткой-по-maintainer-clean -- флаг "-k" недо-действовал.
$(SET-E)
, при наличии "-k" уставляющуюся в пустоту.
Оно скопировано из
(make.info.gz)Testing Flags
только с маленькой доработкой в виде $(firstword...)
, на
всякий случай.
И -- пришлось ;
пхать прямо в переменную, чтобы и оно
тоже исчезало, а то этот дурной /bin/sh ругался
line 0: syntax error near unexpected token `;'
Теперь проблема уже ПОЛНОСТЬЮ ИСЧЕЗЛА.
В GeneralRules.mk это дополнение также портировано.
Итого -- "done".
-I$(SRCDIR)/include
. Это хоть и несмертельно, но
неправильно.
23.05.2008: подправил -- теперь, в зависимости от
$(OUTOFTREE), макрос CX_INCLUDE_DIR определяется в
$(SRCDIR)/include
либо в
$(TOPEXPORTSDIR)/include
, и делается уже
-I$(CX_INCLUDE_DIR)
.
Естественно, при этом вылезли другие ляпы -- в exports/ не копировалась директория pixmaps/ и еще куча файлов (причем не только новомодный Xh_plot.h, но даже и misclib.h!). Что ж -- забытые файлы добавлены, а pixmaps/ сделана нормальной директорией, со своим Makefile'ом. По-хорошему, еще бы и sysdeps/ так же "утвердить в гражданстве"...
26.05.2008: да, и с sysdeps/ сделал то же самое.
16.01.2009: ага, а сам cx_sysdeps.h -- забыл! И fdiolib.h (и прочие файлы от misc/ и useful/), кстати, тоже. Сейчас поправил.
и#ifdef __cplusplus extern "C" { #endif
#ifdef __cplusplus } #endif
24.10.2008: (заканчивал 31.10.2008) да, вставил в: cda.h, Cdr.h, Chl.h, ChlI.h, cxdata.h, cx.h (не менялся с 24-11-2003!), cxlogger.h, cxnetacl.h, cxproto.h, cxscheduler.h, cx_sysdeps.h, cx_types.h, cx_version.h, fdiolib.h, findfilein.h, gray2image.h, Knobs.h, KnobsI.h, Knobs_types.h, Knobs_typesP.h, memcasecmp.h, misclib.h, misc_macros.h, misc_types.h, paramstr_parser.h, seqexecauto.h, timeval_utils.h, tsycamlib.h, Xh_globals.h, Xh.h, Xh_plot.h, Xh_types.h, Xh_typesP.h.
Нетронутыми остались cx_macros.h (устаревший пустой
файл), fix_arpa_inet.h (за древностью),
Xh_fallbacks.h (поскольку в нем одни #define
'ы).
Заодно хотел было пройтись по lib/AuxMotifWidgets/, в которую имеются симлинки, но там все актуальное -- IncDecB.h, InputOnly.h, XmLiteClue.h, XmSepGridLayout.h -- уже было сделано от рождения, включая P.h-файлы, кроме XmLiteClueP.h, который и был доточен. А вот неиспользуемые Gridbox.h и Table.h нужных условий не содержат, но их трогать незачем; LiteClue.h же в порядке.
А вот содержащиеся в поддиректориях .h-файлы, могущие быть использованы при внедеревной сборке (в основном это касается programs/server/cxsd_driver.h), трогать пока не стоит -- т.к. там поддержка C++ и не предполагается (по крайней мере, ПОКА).
Плюс, как было проверено Ромой Кусковым, файлы .xpm и _drv_i.h прекрасно работают и так, посему они тоже не трогались.
31.10.2008: заодно прошелся и по 4cx/. Причем cxscheduler.h, cx_sysdeps, fdiolib.h, findfilein.h, memcasecmp.h, misc_macros.h, paramstr_parser.h, timeval_utils, Xh_globals, AuxMotifWidgets/XmLiteClueP.h -- просто скопировал из cx/src/.
__FUNCTION__
перестал быть "магическим", а
превратился в обычную переменную, точнее -- в static const char
[]
, так что теперь он ПЕРЕСТАЛ конкатенироваться со строками, и
нынче для его добавления в сообщениях нужно использовать "%s".
Впрочем, и так практически везде он выводился именно через "%s", по историческим причинам -- еще со старых gcc (2.7?), так что сия проблема осталась почти незамеченной. Единственное место было в 4cx/src/KnobsCore/KnobsCore_knobset.c, но и там поправлено.
22.01.2009: грохнул, и из EXPORTSTREE, и из {include,lib/{cda,cxlib}}/Makefile. (А {cda,cxlib}/Makefile, кстати, последний раз тогда -- 24-07-2003 -- и менялись, видать, для добавления этой так и не понадобившейся директории.)
Замечание: вчера, шарючись по списку "Features removed from Windows Vista", наткнулся на упоминание о неких "Microsoft Windows Services for UNIX". Утверждается, что там в поставке есть и gcc (3.3), и даже библиотеки для X11. Так что надо будет попробовать поставить -- возможно, сделать это (в будущем) одной из поддерживаемых платформ (наряду с Cygwin и "Win32-native-MSVC").
Сделано. К счастью, он никогда в exports/ не клался, т.к. был де-факто приватным файлом для cxlib и сервера, и использовался только ими. Так что везде, где он имелся, просто добавлены misc_macros.h+misclib.h.
26.01.2009: часть "по теме", часть -- просто исторически давно назрело, так что идем просто единым списком:
sighandler_t
,
который GNU extension. Пришлось сделать свой typedef mysghandler_t.
А вообще -- надо стараться отходить от применения
signal()
, в пользу более стандартизованного
sigaction()
. По крайней мере, в не-одноразовых, а
долго-живущих обработчиках -- точно. (Реально-то весь проект
"slave SIGIO handler" так никогда и не использовался, и никогда не
будет :-))
strtod("NAN", NULL)
. В 4.4 вроде уже поправили.
02.07.2010: замечание: для наличествования NAN
НЕОБХОДИМ ключ -DGNU_SOURCE
-- по крайней мере, с
gcc-2.96@RedHat-7.3.
Понять бы еще, как именно это влеплять к CXv4'шному GeneralRules.mk (или в Rules.mk?)...ifneq "$(wildcard /usr/include/X11)" "/usr/include/X11" CPPFLAGS+= -I/usr/X11R6/include endif
-I/usr/local/include
, но
вообще-то надо думать о каком-то более пристойном
(последовательно-ищущем?) решении, с применением
$(wildcard...)
и $(firstword...)
.
-L/usr/X11R6/lib
. Это-то сделано (заменил
единый $(MOTIF_LIBS_DIR)
на парочку
_L_X11_LIB
и _L_MOTIF_LIB
(последняя
уставляется там же, где -I/usr/local/...
С учетом же уже трех-четырех-пяти возможных вариантов (/usr/X11R6/{lib,lib64}, /usr/{lib,lib64}, /usr/local/lib) -- явно надо обдумать более streamlined-схему.
PATH_MAX
,
а вот взяться ему было неоткуда. Так что добавил туда
#include<limits.h>
. И в cankoz_pre_lyr.c
тоже.
InterceptSignals()
также был древний ляп -- вместо
#if OPTION_USE_ON_EXIT
стояло
#ifdef OPTION_USE_ON_EXIT
(явно артефакт от былого
#ifdef OS_LINUX
). Поправлено в обоих файлах.
-ldl
там не нужно, так что в Rules.mk
введена $(LIBDL)
, уставляемая только под Linux. В
GeneralRules.mk это тоже портировано.
27.01.2009: BTW, just for reference: и вообще, оказывается, последние эксперименты с чем-либо кроме os_linux и os_cygwin проводились аж в ucam, кторый сейчас ucam.19980607/ (а реально это было в 1997-м). И всё!!!
Причем, и там-то все os_*.h были только начаты, но и близко
не доделаны -- в них даже комментарии-то остались от
os_linux.h. Там реально всё делалось в
ucam_sysdeps.h. И так оно осталось и в ncx, oldcx, curcx. И
только в нынешнем CX (в 2001/2002/2003?) было переведено на архитектуру
os_*.h->OPTION_*
->cx_sysdeps.h.
29.01.2009: проверил -- работает, сервер запускается, клиенты к нему коннектятся, как локальные, так и линуксовые по сети. Так что -- "done".
03.02.2009: подпортировал в 4cx/:
OPTION_USE_ON_EXIT
было пофиксено еще тогда.
А вот с /usr/include/... и /usr/local/... пока не стал возиться -- это в нынешней архитектуре GeneralRules.mk неочевидно, куда вставлять, так что сделаем позже.
26.01.2009: резон: иногда Makefile'ам нужно знать
$(OS) и/или $(CPU) еще ДО включения Rules.mk. Например,
включать ли директории с кросс-компилируемыми драйверами в
SUBDIRS
(на не Linux@x86 -- НЕ включать).
Раньше-то -- в ucam, oldcx, curcx -- это сделать было несложно, поскольку там все мозги сидели прямо в Makefile'ах, бывших очень развесистыми. А сейчас -- переселились в Rules.mk.
Короче -- весь кусок "добычи конфигурационной информации" вытащен из
Rules.mk в свежесозданный Config.mk. Так что
прикладной Makefile может include'нуть его еще в самом начале, и тот
только уставит конфигурационные параметры. При этом там стоИт условие
по $(SYSDEPFLAGS_MK_INCLUDED)
, для всегда-однократного
включения.
Плюс, специально для директорий с кросс-исходниками там генерится
еще один параметр -- CPU_X86_COMPAT
, уставляющийся в YES
на x86-совместимых процессорах (x86 и x86_64).
В общем, с одной стороны, теперь надо портировать это в CXv4. С другой же -- как-то некрасиво использующие эту фичу Makefile'ы начинают выглядеть...
04.02.2009: портировал Config.mk в
4cx/. Заодно чуть управильнил $(CPU_X86_COMPAT)
-- теперь оно уставляется не IMMEDIATE "YES"/"" в if/else/endif, а
делается DEFERRED, распарсивающим -- через $(if...)
--
ТЕКУЩЕЕ значения $(CPU). При этом оно стало пригодно для использования
и в кросс-директориях.
OPTION_HAS_WORKING_SENDMSG
уже давно отпала (оно
тривиально нигде не проверялось) и в будущем более не возникнет.
Отпала -- потому, что начиная с Linux-2.0 (?) sendmsg()
уже корректно умеет передавать дескрипторы, да и нынешняя связка
cxd/cx-porter/cx-server всё равно иначе работать не будет. А не
возникнет -- поскольку в однопроцессном сервере никакой долбанутый
sendmsg() нафиг не сдался.
30.01.2009: да, выкинуто из всех 3 имеющихся sysdeps/os_*.h-файлов.
30.01.2009: исходная посылка -- достало, что
приходится в каждом Makefile делать ДВА include
--
локального для проекта ProjectRules.mk, а потом отдельно
какого-то файла из $(SRCDIR)/...
.
И вот, вчера, после населения свежесозданной istc3682/,
когда эти два include
стабильно шли друг за дружкой,
вызрела идея:
А можно ведь в каждом Makefile указывать просто переменную, содержащую имя специфичного для данной директории *Rules.mk-файла, и уж ProjectRules.mk пусть его сам включает. Если же переменная пуста -- значит, это директория не специфичная, и нужен просто Rules.mk.
В общем, так и сделал -- работает. Из дополнительных бонусов: туда
же, в общий для всей директории файл, ушло
"CPPFLAGS+=-I$(PRJDIR)/include
". А потом можно туда же
переместить и «"3rd-generation" component logic», имеющуюся
пока только в xmclients/.
OPTION_HAS_PROGRAM_INVOCATION_NAME
определяются в
sysdeps/os_*.h, но вот декларация самих переменных
program_invocation_name
и
program_invocation_short_name
почему-то присутствует во
всех файлах, использующих их (притом, что, очевидным образом, все эти
файлы включают cx_sysdeps.h!).
03.02.2009: так что -- перетащил декларацию в cx_sysdeps.h, а из cda.c, cxlib.c, fdiolib.c (там было хитрее), ndbp_staroio.c -- убрал.
Но самое странное -- Knobs_internals.c: там УЖЕ все
выглядело как предназначенное для "правильной" схемы использования, но
переменные-то нигде не объявлялись! И ошибки при этом не возникало...
Разборки показали, что в RedHat-7.3 они могут быть объявлены в
errno.h (непонятки только с __USE_GNU
--
откуда оно берется). Симптоматичненько...
В 4cx/ сделано аналогично -- Cdr_file_ldr.c и KnobsCore_knobset.c.
Засим case closed.
04.02.2009: постановка проблемы:
#include
каких-то .h-файлов, которые тоже
симлинковались. Но -- до того, как оно сгенерит список зависимостей
.d, оно не знало, что эти .h-файлы нужны, и не
симлинковало их. А чтобы сгенерить зависимость -- они уже были нужны,
иначе вылазила ошибка!
не помогало, поскольку в момент генерации .d-файла это правило было ни при чем. Так что я вставлял несколько дурацкое правило видаsomefile.o: includefile.h ...
так что make знал о необходимости сделать .h-симлинк еще ДО какого-либо использования .c-файла.somefile.c: includefile.h ...
А теперь решение:
Но немного подумав -- точнее, сходив в душ :-) -- нашлось очевидное решение: надо указывать в зависимости не только .o, но и .d!
А подумав еще лучше -- очевидно, что надо указывать зависимости ТОЛЬКО для .d, т.к. для .o они определятся позже, уже автоматически, обычным путем.
Итого -- подправил таким способом:
%: %_db.so
", ныне смененное на явно
более корректное "$(SIMPLECLIENTS): $(SIMPLECLIENTSLIST)
",
стало только хуже -- симлинки будут делаться всё снова и снова, пока
target-файл линка не будет пересобран и не станет новее
SimpleClients.lst (каковой, впрочем, меняется редко -- так что
хватит просто пересборки с нуля).
Но эта проблема уже вряд ли решаема, поскольку корень её заключается в проверке make'ом mtime'а именно TARGET-файла, в то время как смыслом правила является именно СОДЕРЖИМОЕ СИМЛИНКА и, как следствие, mtime симлинка, а вот его-то make смотреть ни при каких обстоятельствах не будет...
Но эту проблему уже впрямую никак не решить, так что забиваем (благо, что однократная пересборка оказывается достаточной).
(Note: в первоначальном варианте исправления вообще стояло
"%: $(SIMPLECLIENTSLIST)
", приводившее к диким
результатам: поскольку в части prerequisite теперь отсутствует
'%', то, будучи match-anything rule, оно пыталось применяться
ко всем подряд файлам, в т.ч. и к SimpleClients.lst, и к LocalRules.mk,
etc.)
В общем -- "done".
06.02.2009: но самое-то "прикольное" --
$<
стало уставляться в "не то" -- см.
bug #21198: "Wrong order
of prerequisites with 3.81/CVS").
Позаменял там все ".c" на ".d".
Итого -- непосредственная проблема решена. Но остался вопрос -- что же делать с этим больным на голову make-3.81. Поскольку проблема может вылезти и в будущем, когда у некоего файла будет более одной зависимости, и оно даже не то что симлинк -- а и с обычными правилами может лажануться... (...или не может? Почему никто особо не жалуется?)
С другой стороны -- понятно, что же такое случилось в январе-2009, при добавлении libmisc к кросс-компиляции и "унификации" правил симлинкования: раньше правила были специфичны для каждой "группы" файлов-симлинков, и в них указывался конкретный шаблон, автоматом отсеивавший "не те" источники -- типа
Сейчас же ОДНО общее правило на НЕСКОЛЬКО разных групп файлов-симлинков из РАЗНЫХ директорий, так что теперь static pattern rule сделать нельзя, и приходится пользоваться спецпараметром$(SYMLINKS): %: ../common/% ln -sf $< $@
$<
.
Вывод -- стараться использовать раздельные правила с прилагающимися static pattern rules.
Вроде бы всё, больше нигде таких дурных зависимостей ".c:.h" нету -- проверил grep'еньем по шаблону ':.*\.h'.
30.04.2010: в bug #21198 комментарий за 07-06-2009 от Paul D. Smith: "Fixed. This is gross: the way we construct then reverse the prereq list leads to some hackery due to second expansion, and the hackery was incomplete such that this bug occurred.". Так что -- теперь про БУДУЩИЕ конкретно ТАКИЕ проблемы 3.81 можно не беспокоиться :-).
26.12.2011: тьфу ж ты -- вроде и постулировал, что у симлинк-файлов должно быть по единственной зависимости, и нефиг прописывать зависимости .c/.h от .h, а вот обнаружил сделанную летом в cm5307/common/CommonRules.mk странную строчку вида
cm5307_dbody.h: cm5307_sl_dbody.h
Понятно, что это какая-то замуть с "частичным" введением SL_DBODY, но всё равно... Сейчас просто за-if'ил её.
07.02.2009:
$<
, и
применять $*
(для чего использовать pattern rules). Это
убъет проблему "неправильных симлинков" на корню.
$*
.
(Кстати, при единственной зависимости, и являющейся target'ом линка, вообще все замечательно -- пересимлинковка единожды при каждом изменении исходника. Вообще идеальный вариант -- в точности как для обычных пар "цель:зависимость".)
ACT_LIBS
.)
09.02.2009: Как показал поиск по
work/**/{Makefile,*.mk}, в подавляющем большинстве случаев
можно преспокойно перейти именно на $*
, и уж тем более-то
в GeneralRules.mk.
А в нынешнем cx/ -- надо будет уделить внимание местам, где
используется $(_C_O_LINE)
. В основном сложность будет с
i_*_rules.mk, поскольку там паттерн используется совсем
по-другому; но при переходе от косвенной компиляции к прямой --
проблема исчезнет.
11.02.2009: и вообще -- постараться максимально избавляться от симлинков, используя их в основном для теневой сборки, каковую стараться -- в будущем :-) -- производить в отдельных директориях.
11.02.2009: и еще -- для симлинкования следует
ВСЕГДА пользоваться $(SCRIPTSDIR)/ln-sf_safe.sh
, а не
ln -sf
напрямую (а то это пока только в
chlclients/LocalRules.mk было). Сделал.
01.04.2009: а в ln-sf_safe.sh-то был мелкий багчок -- был забыт "exit 1" после выдачи сообщения об ошибке. И это с января 2004г.! Вставил.
26.01.2012: был еще ляпчик -- после проверки, что target-файл НЕ симлинк, стояло просто "false". В результате оно не выдавало никакой ошибки, но make отваливал по "Error 1". Выглядело странновато.
Вставлен вывод сообщения об ошибке.
22.02.2009: да, введено определение в Sysdepflags.sh (там еще с CPU дрянь -- interix'ный "uname -m" отдает как "x86", что затеняло бы x86_64) и сделан os_interix.h. И в 4cx/ скопировано.
OPTION_HAS_PERSISTENT_SIGNALS
-- во-первых, оно реально
никогда ни на какой из поддерживаемых систем не было ==0
,
а во-вторых, при использовании sigaction()
-- хоть через
set_signal()
-- и не имеет смысла в принципе.
22.02.2009: да, выкинул отовсюду.
NOX=something
, и
оно б просто пропускало поддиректории, зависящие от X11/Motif.
05.03.2009: да, ввел в {lib,programs}/Makefile -- теперь SUBDIRS состоит из $CSUBDIRS и $XSUBDIRS, и при "$(NOX)"!="" XSUBDIRS уставляется в пустоту. 4cx/ too.
16.03.2009: и еще "прикольчик" -- ведь cdrclient-то зависит от libXt, а лежит в директории utils/, которая НЕ выключается из компиляции!
Так что надо переносить его в какое-то другое место... В runner/?
16.03.2009: да, перенес.
return
, и годящихся для разных функций -- void и не-void).
Так что, де-факто у нас и так всё рассчитано на gcc>=3, либо поставляемый в RH-7.3 2.96, который и является развитием 2.95 с примесью фич из 3.0 (точнее, 2.96 -- "essentially a development snapshot of the GCC tree, with a lot of Red Hat fixes").
В случае OpenBSD это означает версию>=3.7 (март 2005), где в i386 уже идет gcc-3.3.5.
30.03.2009: всё было просто -- в описании "install:" стоит shell-проверка на непустоту EXPORTSFILES, а оно оказывается равным двум пробелам. Пробелы брались из определения TARGETFILES, состоявшего из 3 $(...), все из которых в qult/drivers/ были пустыми -- но пробелы-то оно сохраняло!
Сделал просто -- в Rules.mk в проверках заключил
$(EXPORTSFILES*) в $(strip...)
.
31.03.2009: и, кстати -- это должно стать
ВСЕОБЩИМ правилом: при проверках в любых формах "if"
("ifeq/ifneq" для make, $(if...), "if" для shell) надо ОБЯЗАТЕЛЬНО
делать $(strip...)
проверяемой переменной.
Прошелся -- да, этого НЕ делалось только в нескольких местах
(ifneq "$(SUBDIRS)" ""
в Rules.mk и GeneralRules.mk, плюс
ifneq "$(ALLDEPENDS)" ""
в *_drvlets/LocalRules.mk),
теперь делается и там.
GENERATEDFILES+=$(AUXMOTIFHEADERS)
: проблема в том, что
оно сначала делает maintainer-clean именно include/, а потом
идет в другие поддиректории, и там, ПЕРЕД удалением файлов, пытается
воспроизвести все .d-файлы, а сделать местами этого не может
-- поскольку .h-файлы, являющиеся симлинками в
.../AuxMotifWidgets/, уже удалены.
Так что в 4cx/ эта строчка теперь тоже закомментирована.
С другой же стороны -- а что делать? В "info make" никаких правил, более крутых, чем maintainer-clean, не прописано... Вводить, что ли, свое правило -- типа "total-clean"?
17.04.2009: ну давайте назовём это правило
"crit-clean", а переменную для него -- CRITCLEANFILES
.
Да, так и сделал, и в **/include/Makefile заменил былое
закомментированное определение на уставление
CRITCLEANFILES=$(MOTIFHEADERS)
. В нынешнем CXv2 оно,
конечно, малополезно вследствие рекуррентных (а не прямых) ссылок между
clean-target'ами, но при желании пользоваться можно.
для прописывания статических/аллокированных указателей -- поскольку сам anonymous_array[2] аллокируется в стеке, и это место затем reused, так что статический указатель будет указывать на мусор.(const char *[]){"A", "B"}
А
просто не компилируется. Так что правильный вариант -- явно декларировать(static const char *[]){"A", "B"}
и ссылаться наstatic const char *line_name_list[] = {"A", "B"};
line_name_list
где надо.
Что странно -- проблема вылазит в CentOS-5.2/gcc-4.1.2, но НЕ в RedHat-7.3/gcc-2.96... Возможно, 2.96 не использовал стек, а аллокировал статически?
$(if
...)
первый параметр -- CONDITIONAL -- был просто
$(EXPORTSFILESx), БЕЗ $(strip)
. В результате после
ликвидации (в пятницу) cm5307_drv.c и cangw_drv.c в
их директориях DRIVERSOURCES стала пустой, но в какой-то НЕпустой
EXPORTSFILES это отражалось.
12.09.2011: странно, конечно, что это никак не вылазило раньше -- уж пробел-то от слепливания кучи не-определённых переменных должен был пролезть.
Короче -- $(strip)
вставлен во все 3 штуки.
09.12.2011:
Вывод: вообще, конечно, все эти мелкие различия между разными версиями даже просто Linux -- пожалуй, еще мерзостней, чем между разными Unix. Но пока проще решать такие мелкие проблемы по мере поступления, не заморачиваясь шибко глобальными способами.
31.10.2012: и в 4.4.6@SL-6.3 тоже понадобилось использовать sys/param.h. Надобность вылезла на старом impacis10_drv.c.
19.01.2012: причина -- достала старая система. При населении v2hw/, особенно в симлинкуемых директориях, приходится прикладывать дикие усилия, чтобы разобраться как что описывать (списки файлов (каких? .c или конечных?), по каким переменным распихивать), как указывать специфичные зависимости, и т.д. и т.п. В новой же эти проблемы давно решены -- ну так и какого ж чёрта?!
Основа так и лежит в GeneralRules.mk, а на время переходного периода "новые" правила лежат в 4Rules.mk, а все makefile'ы называем 4Makefile.
29.04.2012: да, перевод завершен.
Все 4* переименованы с удалением "4", так что система сборки работает штатно.
Впредь в этом разделе вряд ли что будет добавляться (только если совсем CXv2-specific), в основном -- уже в bigfile-0002.html.
24.05.2012: тогда при переходе был допущен ляп -- по ошибке удалён drivers/can/common_sources/CommonRules.mk. Других невольных жертв пока не обнаружено.
23.05.2012: все *.mk-файлы кросс-сборки
переделаны, плюс убраны определения TOOLSDIR
из обоих
TopRules.mk.
Из "неприятного" -- в x-compile/ppc860-linux/include/ пришлось завести свой экземпляр директории camac/ с файлом camac.h.
И, кстати, у bivme2 и cangw аналогично.
09.10.2012: а сейчас cx/Ztools/ (бывшая tools/) уже физически удалена.
19.03.2014: конкретная потребность -- чтоб студент по имени Илья мог на своём MBP собрать.
Действия:
!!!Ne zabyt' by v 4cx/ skopirovat'!!! 21.02.2023: скопировал, ещё тогда, 19-03-2014.
21.02.2023: дальнейшие записи, если работа продолжится, будут вестись уже в bigfile-0002.html в созданном сегодня разделе.
chdir()
перед запуском клиента; тем самым туда попадают
сохраняемые режимы и логи (mode_APPNAME_*.dat и
log_APPNAME.dat).08.11.2004: ой хрю-хрю! Сейчас-то сделана ОДНА директория -- только settings/. В ней и таблицы интерполяции, и режимы туда пишутся, и log'и, и event-log'и.
Плюс -- базовой директорией служит ${HOME}/pult/.
/*!!!DIRSTRUCT*/
.
30.11.2004: вроде пометил.
21.01.2007: да, по крайней мере cx-starter точно должно быть несложно настраивать .-директивами в config-файле.
Это чтоб или ограничивать "видимость" сервера частью интерфейсов, или вешать на разные интерфейсы разные сервера (но очевидно, что N, тем не менее, должен быть уникальным среди всех интерфейсов -- из-за /tmp/cx-N-socket).
accept()
'а? Ведь это
TCP, а не UDP...
(Вопрос навеян рассказом Antonio Lioy о том, как форточный RPC-сервер отвечал на бредовые пакеты, отправленные ему на 135-й порт.)
А можно б сделать -- чтобы из cda можно было заполучить и "сырое" значение. Для формульных каналов это, казалось бы, не очень осмысленно, но если реально вся формула зиждется на одном-единственном канале (например, линейная аппроксимация), то тогда и здесь есть смысл.
Вариант реализации:
cda_getphyschanval_raw(cda_physchanhandle_t chanh, int *rvp, tag_t *, rflags_t *)
(она и так нужна для chaincx_drv.c; для комплекта сделаем и
cda_setphyschanval_raw()
).
cda_execformula()
добавляется еще пара параметров
(sic! :-): int *rvp
и
int *rvp_useful
-- само значение плюс флаг,
осмысленно ли оно. Если формула делает не ровно одну операцию чтения
физ. канала (ни одной, две, и т.д.) то этот флаг сбрасывается в FALSE.
knobinfo_t
вводится поле
int curv_raw
плюс флаг curv_raw_useful
,
и в Cdr добавляется заполнение этих полей.
Замечание: raw-значения отдаются как int32
--
ничего вещественного там все равно нет, да и для доступа через cda к
более нижнему уровню -- для chaincx_drv -- это полезнее.
21.01.2004: начал делать. Поля в
knobinfo_t
добавлены, cda_getphyschanraw()
(а
не val_raw) сделана. Добавил также к параметры rv_p
и
rv_useful_p
-- вставил их перед localreginfo
.
Обсчет для этих полей также сделан. Поддержка в Cdr также вставлена.
Теперь дело за Chl_knobprops.
Кроме того, надо будет подправить staromakh.
22.01.2004: в связи с появлением
cda_getphyschanvnr()
перевел Cdr и execformula на нее.
05.02.2004: staromakh.c подправлен. BTW, предыдущая версия была за 24.07.2003.
05.02.2004: изготовлена и
cda_setphyschanraw()
, так что тему можно считать
исполненной -- помечаю как "done".
incdec_step
смысл "размер быстрого изменения" и при
|V(t=n)-v(t=n-1)|>incdec_step
сигнализировать о сим факте.
Некоторые замечания на сию тему:
curv=0.0
) проверка также бессмысленна.
09.01.2004: обсудил идею со Старостенкой. Результаты обсуждения:
Это нужно, как минимум, для OP_LINTRP
--
сигнализировать о том, что входное значение для интерполяции находится
за границами заданной таблицы.
14.01.2004: ты, собственно, функции
{cda,Cdr}_strrflag_{short,long}()
делать собираешься?!
14.01.2004: флаги перетасовал, и
CDAF_CALCERR
изготовил.
cda.c::cda_execformula()
его даже выставляет.
Возникли несколько тонкостей с точки зрения стыковки cda/Cdr :
CdrProcessKnobs()
"корректности ради"
оставлял в полученных от cda флагах только
CDA_FLAG_OTHEROP|CXRF_SERVER_MASK
. Посему ввел
cda.h::CDA_FLAG_CDA_MASK=CDA_FLAG_CALCERR|CDA_FLAG_OTHEROP
,
и использую его.
CDR_FLAG_HWERR_MASK
.
Таким образом, оно автоматически идет и в
CDR_FLAG_SYSERR_MASK
, и используется cx-starter'ом.
Теперь надо проверить.
19.01.2004: м-мать... В
{Cdr,Knobs_internals}.c::ChooseColorState()
по-прежнему оставалось сравнение с CXRF_SERVER_MASK
,
поэтому реально "побордовение" по CALCERR поддерживалось только
cx-starter'ом.
В Cdr.c поменял на CDR_FLAG_HWERR_MASK
, так
что все теперь бордовеет. А вот в Knobs_internals.c... Там
ведь нету никаких ни CDR_FLAG_NNN, ни CDA_FLAG_NNN, так что увы...
Единственное, что радует -- ChooseColorState()
там
используется одноразово, в очень экзотическом случае, из
SetAttnState()
, всегда с rflags=0.
ВЫВОД: надо-таки срочно приводить в чувство конгломерат Cdr/Chl/Knobs, чтобы там не было дурацкого дублирования кода!
09.02.2004: функции
cda_strrflag_{short,long}
и
CdrStrrflag{Short,Long}
изготовлены.
cda_strrflag_long()
уже используется в
Chl_knobprops.c.
01.11.2004: поскольку все означенные выше проблемы (*strrflag*, CALCERR, дублирование кода в Cdr/Knobs) в указанном выше объеме уже решены, переделываем оба "red" на "was_red".
А то эта схема (позаимствованная (с мелким упрощением) у XFree86) используется в CX повсюду, но реально "прописана" она только в cx_proto.h и cx_version.h (в обоих случаях "ручным" умножением и сложением), а в загружаемых модулях -- тю-тю.
Туда же выпихнуть helper-макросы *_STRINGIZE() и *_CONCATENATE().
28.01.2004: заглянул в cx_version.h -- а там, однако, используется как раз совсем НЕ упрощенная, а оригинальная кодировка от XFree86, разве что "SNAPSHOT" пока не задействован.
Короче -- надо наконец произвести унификацию, и, видимо, как раз с "чистым" вариантом XFree86 -- M.m.P[.s].
03.02.2004: сделал общую систему версионирования в cx_version.h. И везде перешел на нее.
Кодировка взята от XFree86 -- MmmPPsss. Это дает некоторые ограничения -- максимум 1000 snapshot'ов, 100 patchlevel'ов, 100 minor'ов, и изрядное (поскольку старшие разряды и может расти далеко) major'ов. Впрочем, для большинства случаев это более чем достаточно (из контрпримеров на ум приходит только ядро Linux, с номерами типа linux-2.1.132, но это уж проблема тамошней схемы нумерации -- 2.2 стоило назвать 3.0, 2.4 -- 4.0, и т.д.).
Для проверки совместимости версий имеется inline-функция
CX_VERSION_IS_COMPATIBLE(that,my)
, определенная как
major(that)==major(my) && minor(that)<=minor(my)
03.02.2004: макросы *_STRINGIZE() и
*_CONCATENATE() выпихнуты в misc_macros.h, под названиями
__CX_STRINGIZE()
и __CX_CONCATENATE()
.
CX_VERSION_IS_COMPATIBLE(that,my)
очень
тщательно выбирать, кого считать за "that", а кого -- за "my", т.е.,
какая из сторон допускает "большую продвинутость" другой стороны.
(Нечто подобное делалось в нашей с Эйдельманом программе
ekc.c, функция GetLargestFile()
).
01.07.2004: так, в похожую струю (плюс -- к
диалогам сохранения/восстановления режимов "без имен файлов"):
поскольку я дал новому студенту (avcher) задачу "написать аналог ls",
то он нашел забавную функцию -- scandir()
. Она хоть и
не-POSIX, но есть в BSD>=4.3, Linux (со времен libc4), в Irix
(где она в двух вариантах -- под BSD и под SysV) и в OSF1.
Честно говоря, не думаю, что нам в Chl будет полезна именно
scandir()
(тем более, что у студента программа с нею
как-то забавно подглючивает). Но рядышком с ней есть еще функции
fnmatch()
-- одноразовая проверка на соответствие шаблону,
и glob()
-- создание списка файлов, соответствующих
шаблону. Вот эта-то парочка стандартизована в POSIX. Плюс, есть еще
wordexp()
-- дюже навороченная вещь, обрабатывающая '~',
кавычки, ${...}, и кабы не $(...); она также стандартизована в POSIX.
(Меня мучит мысль: ведь когда-то давно, году в 94-м/95-м/96-м,
когда я возился с (о, вспомнил!) библиотекой GREMLIN и для ее файловых
диалогов обдумывал функцию под названием fnmatch()
, с
практически такой же функциональностью. Так вот вопрос: я сам пришел к
такому же имени, или уже в то время знал про unix'ную функцию?)
А еще не менее честно говоря -- думаю, для "безыменнЫх диалогов" вполне достаточно будет и "ручной" проверки типа
strlen(name)==... && memcmp(name, prefix, prefixlen) == 0 && memcmp(name+strlen(name)-suffixlen, suffix, suffixlen) == 0 && isdigit(name[prefixlen])...
30.11.2004: сегодня сделал в
XhFindFilename()
и XhLoadDlgShow()
именно по
последней схеме -- "ручным" сравнением. Плюсы -- полная портабельность
(хотя fnmatch()
и так в POSIX'е), а также явно бОльшая
скорость работы по сравнению с более общей проверкой по шаблону.
08.04.2007: ага, сделал-то
XhFindFilename()
в Xh, а теперь понадобилась совершенно
ОТДЕЛЬНАЯ реализация для cdrclient'а. Причем -- с некоторыми
можификациями: во-первых, число цифр "последовательного номера" должно
быть другим (5, а не 3), и/или во-вторых -- сам формат имени файла
должен включать не только YYYYMMDD, но и HHMM.
Пока-то сделано просто по-другому, но вообще-то следует завести (в
libuseful!) какую-нибудь стандартную функцию типа "choose_filename()",
которой бы передавался базовый шаблон (возможно, c %-спецификаторами
для strftime()
) и количество символов для подбора номера.
P.S. Вот тогда-то и создастся еще один модуль в useful -- filename_utils.[ch].
findfile(filename, "path1", "~/path2", ..., NULL)
Т.е., чтобы она "понимала" префикс "~" как home-директорию.
Это потребуется для всяких cx-starter'ов при поиске config-файлов. (Собственно, когда писал этот запрос в блокнот 18 мая, понимал зачем, а сейчас, 13 июня, когда вбиваю в bigfile-0001, уже подзабыл мотивировку... :-)
20.04.2007: а вот теперь очень даже понял, зачем:
OpenDescription()
и
lapprox_table_ldr()
, которые столь же "интеллектуально"
перебирают кучу мест, где файл может иметься.
И уже поднадоело писАть там эти case с хитрым содержимым.
Так что -- можно реализовывать, действительно где-нить в useful, причем -- оно должно быть достаточно общим и позволять "искать" и директории (для cx-starter'а), и .so-файлы.
Поэтому создаем новый раздел "findfilein", и дальше все идет там.
08.08.2007: поскольку цель достигнута, то "done".
Т.е., из chlclients/ удаляем всех, кроме widgettest; в xmclients/ оставляем только tsycam, и выносим в отдельные проекты sukhphase и ipp.
В принципе, drivers/ напрашивается на нечто подобное, но пока мне представляется осмысленнее держать "стандартные" драйверы прямо в дереве cx.
31.08.2004: из cx/src/programs/chlclients/ удалено все, кроме widgettest. Если что -- последние перед удалением варианты в STABLE/cx.20040802/.
08.11.2004: уже некоторое время назад (около 19.10.2004) ipp из programs/chlclients/ удален. Теперь свежий вариант (бывший nipp/ipp.c) живет в work/pult/xmclients/. А все старые варианты складированы в BACKUP/ipps/.
Остался же теперь из "левых" клиентов только sukhphase.
06.08.2006: фиг с ним, с sukhphase'ом -- считаем его "наследством", которое уйдет при случае, и помечаем раздел как "done".
Уже сейчас ВЕЗДЕ используется схема [0]=magicnumber, [1]=version, а стоит ее дополнить принципом "всегда [3]=char*-название".
12.06.2004: сейчас это НЕ так в
subsysdescr_t
(там перед именем идет kind
) и
в programs/daemon/CxsdFrontendRec
(там поля "имя"
вообще нет; это унаследовано еще из server/cxsd_module.h).
12.06.2004: сразу же взялся и все менять. Итак:
subsysdescr_t
передвинул sysname
перед
kind
. Естественно, CXSS_SUBSYS_VERSION_MAJOR
продвинул с 1 на 2.
CxsdFrontendRec
добавил поле name
, а в
DEFINE_FRONTEND_REC()
-- инициализацию оного. Версию
продвигать не стал, ибо однопроцессный сервер пока все равно в стадии
эмбриона.
ctime
, поскольку она locale-dependent, и стараться везде
писАть все руками при помощи localtime()
.
fgets()
: там стояла
конструкция
так что при пустых-строках-перед-EOF оно бы полезло вif (linebuf[strlen(linebuf) - 1] == '\n') linebuf[strlen(linebuf) - 1] = '\0';
linebuf[-1]
(в некоторых местах применялись аналогичные
конструкции с предварительным
linelen=strlen(linebuf)
).
Реально, конечно, такая ситуация вряд ли бы случилась -- скорее будет feof() или fgets()==NULL, но конструкция с linebuf[-1] всё равно крива.
18.12.2010@Снежинск-каземат-11: затронуты:
ReadConfigFile()
ReadLine()
(этот
реликт был за 24-11-2003!)
ReadLine()
ReadLine()
file_linereader()
file_lreader()
smp4td_stdio_lreader()
Многие из них были копиями. Это демонстрирует необходимость в ЕДИНОМ парсере.
06.05.2004: Были некие переговоры с Гусевым, из которых следовало, что будут полезны русифицированные New Century Schoolbook размеров 12 и 14, причем достаточно прямых (без Italic'а).
Посмотрел -- оказывается, у меня как раз 75dpi/ncen{R,B}{12,14}.bdf доведены до стандарта WGL4 (НЕ U0400). То есть -- сами шрифты готовы.
ВЫВОД: стоит сделать их доступными Гусеву и, в частности, поставить на пульт. Чтобы не сводить с ума всякие Netscape неполными наборами шрифтов, стоит обозвать их не по XLFD, а короткими именами -- типа ncen{r,b}{12,14}.
Зачем? Потому, что это даст Гусеву толчок в направлении "избавиться от Cronyx".
03.06.2004: сделано -- изготовлена директория
ncen, в файлах в строках FONT
имена "-adobe-..."
заменены на ncen{r,b}{12,14}, директория добавлена в
/etc/X11/fs/config::catalogue= на всех пультовых машинах (за
вычетом Athena), плюс отправлена Гусеву.
А уж как заставить Гусева их использовать...
04.06.2004: Гусев обнаружил глюк в ncenR14 -- он отображался в xfd, но в Motif был пустотой.
Оказалось, там закомментирована property FONT_ASCENT
,
так что у Motif'а ехала крыша. Видимо, это какой-то из XmBDFEd'ов так
порезвился.
КСТАТИ: когда зашла речь о шрифтах, и о мелких, я сначала упомянул/показал jmk-x11-fonts в моей русификации, а потом брякнул про то, что в Форточках шрифты красивы из-за того, что они не пиксельные, а векторные с anti-aliasing'ом. И что Linux/X11 это давно умеет (всякие Qt/Gtk), а не умеет только Motif. И полезли искать -- и нашли!!! Анонс, что в Motif-2.3 эта фича будет добавлена -- OpenMotif 2.3 Preliminary Specification - Anti Alias Fonts. 22.04.2006: сегодня рылся в недавновышедшем OpenMotif-2.3 -- да, оно там добавлено; White Paper на эту тему.
05.06.2004: баг в ncenR14 поправлен, исправленный файл распространен по всем пультовым машинам.
09.06.2004: ага, исправил баг -- как же. Он там оставался.
Задним числом разобрался, в чем был прикол -- стояла одна лишняя property. А так -- перестал заниматься кустарничеством и вместо тупого копирования рабочих вариантов шрифтов просто прогнал по ним скрипт, генерирующий файлы для koi8-1, так что теперь по всем "потребительским свойствам" они аналогичны остальным моим шрифтам. На пультовых машинах все обновлено.
Подготовить "общую бумагу" с описанием всех этих весчей, и постараться это всё продавить, дабы натренироваться на этом примере. (Будет сопротивление (вялое, болотистое) от Дуброва?)
17.09.2003@Bled: насчет "security policy для ИЯФ": сия бумага должна быть технической (минимум воды -- просто правила для firewall'а) и публичной. Если кто-то находит там дыру -- ok, пусть так, быстрее ее обезглючим (никакой "security through obscurity").
18.09.2003@Bled: в дополнение к ИЯФ policy: вытрясти-таки из дирекции домен "binp.ru".
14.01.2004: по результатам взлома Inpbox (которые нашли также отражение в докладе Шувалова на институтской сессии 10.01.2004, и, как следствие, сдвинули дирекцию в нужном направлении) начал писать бумагу "Как нам обустроить сеть ИЯФ?" (tex/binpnet/binpnet.tex). Идея писательства мной поддержана Шуваловым.
19.01.2004: Беловым прислана копия письма (ушедшего разному сетевому начальству СО РАН) на тему "Proposals on IP Allocation Policy (Internal RFC)". Оно как раз в струю перевода СО РАН на локальные адреса.
Кроме того, идут регулярные треп... sorry, обсуждения со Старостенко, который вкупе с Костей Лотовым и Кулипановым двигают идею оживления институтского сайта.
Плюс djb-*: publicfile, djb-dns, qmail, ezmlm, ucspi-tcp.
Вообще, DJB, похоже, пытается соревноваться со всеми сразу сетевыми службами -- publicfile vs. *-ftpd+Apache, djb-dns vs. Bind, qmail vs. sendmail, ezmlm vs. Majordomo/Pipermail/..., ucspi-tcp vs. inetd/xinetd. Надо найти независимые (от DJB ;-) overviews и сравнения его продуктов с конкурентами.
02.10.2003: некоторые ссылки:
20.01.2004: кстати, впомнил о втором конкуренте Sendmail'а -- Postfix (www.postfix.org). Но у него, мягко говоря, не видно особых для нас преимуществ перед Sendmail'ом (особенно учитывая, какие фокусы он, помнится, выкидывал с русскими письмами), хотя и написан он небезызвестным Wietse Venema.
30.01.2004: забыл тогда в Бледе добавить в этот список Bugzilla (класс -- "bug tracking systems"). А сегодня наткнулся на еще одну вещь: savannah.gnu.org. Там используется какая-то система, явно не являющаяся Багзиллой. На вид сильно смахивает на Sourceforge (PNG-файлы, отображение приоритета насыщенностью фона, etc.).
18.04.2004: припомнил еще одного конкурента sendmail'а -- Exim (www.exim.org). На него ссылается Alan Cox в своем интервью KernelTrap от 15.02.2002; цитата: "removing sendmail for exim".
16.04.2004: По утверждениям Алякринского, в ИЯФе будет в июне или июле workshop на тему нашего C13. Полезно (для "показаться народу") сделать там доклад.
19.04.2004: В докладе надо обязательно уделить внимание проблеме, приведшей к появлению seqexecauto. "Возможно, я изобретаю велосипед, но задача противная, нетривиальная, и решение мне представляется/кажется элегантным" (хотя, глядя сейчас -- как я сразу до него не догадался, ведь предпосылки очевидны).
23.08.2004: доклад был сделан уже больше месяца назад. Про seqexecauto там ничего толком сказано не было, но можно дополнить. Оно все в .ppt-файле, который надо обязательно выложить (в pdf) в "My publications".
26.06.2005: версия вторая, пришедшая в голову совершенно отдельно, без связи с написанным выше (кажется :-), зато уже послайдно:
- Постановка проблемы: людей, которые одновременно и понимают физический смысл задачи, и являются хорошими программистами -- немного. Имеется явный дефицит человеческих ресурсов. И вместе с тем при написании каждой программы авторам приходится проделывать ряд рутинных действий.
(Присказка словами -- "пройдемся по истории развития СУ ВЭПП-5".)
- 1996: 1-й вариант -- "roll" .
- 1997: Более общий случай: группировки->элементы(1|n)->текст.поля(r/w).
- 2000: Разные типы knob'ов (текст, onoff, alarm, button).
- 2004: вложенные элементы.
- Неудобство: имеем ТРИ разных сущности -- группировка, элемент, knob. Явно -- надо унифицировать, a-la inode.
- И -- все сделать plugin'ами.
- ИДЕЯ: элементы -- тоже plugin'ы.
Как и группировки. А "группировка" -- суть программа (пример -- pickup'ы кольца -- "кольцом" на экране).
Плюс -- декоративные knob'ы.
- Плюс -- bigc-knob'ы. Это позволит делать "хитрые" программы типа ipp etc. также -- из кубиков, создавая лишь "отображатор".
- Плюс -- user-specific-knobs. Это позволит тем же способом делать совсем нетривиальные программы -- типа impacis10 и phm-*.
- Итого -- имеем, что программы ПОЛНОСТЬЮ избавляются от рутинных, процедурных действий (таких, как создание окошек, организация интерфейса, связь с сервером), предоставляя лишь "мясо" -- отображаторы, вычисления, и специфичные действия.
Плюс -- стандартные ("частые") отображаторы стандартизуются, и из этих кубиков можно клепать программы прямо в БД.
- Это очень похоже на то, как работают всякие Delphi, LabWindows и EPICS'ный dm2k. Отличий два: во-первых, plugin'ы (разных типов!); а во-вторых -- эта древесная иерархия существует и БЕЗ отображаторов, самостоятельно. Это полезно, например, в мониторе-запускалке, которая является "мега-клиентом", плюс в генераторе web-страниц.
10.06.2006: и третье отличие -- если dm2k/medm лишь для "простых" программ, то в вышеописанной архитектуре мы стараемся привести к этому виду ВСЕ программы.
Вопрос еще, как бы сие назвать -- "Унифицированно-модульный подход к построению интерфейсов программ управления"? Бе... По-аглицки выходит лучше: "UI-biased approach for building [modular&/|extensible] control programs". (Вот только слово "biased" -- плохое, правильнее что-то типа "направленный", "ориентированный", "UI-центричный"...)
Во -- "UI-oriented approach for building modular control programs".
Модульность решает обе эти проблемы: сама программа превращается просто в дирижера-компоновщика, определяющего, какие модули-кубики активировать. "простые программы -- [картинка], чуть сложнее -- ..."
23.07.2014: на SourceForge.net созданы проекты memcasecmp, cxscheduler, fdiolib, paramstr-parser (пришлось поставить дефис вместо подчерка из-за дурацких правил -- видимо, следствие DNS).
Собственно файлы загружены пока только для memcasecmp -- memcasecmp.[ch].
29.07.2014: создан также sendqlib -- также пока без файлов, просто застолблено имя.
Собственно, с файлами "проблема" в том, что почти все требуют что-то из CX'ных библиотек -- как минимум misc_macros.h и misc_types.h. А местами (fdiolib?) и cx_sysdeps.h может понадобиться.
Плюс, там же для download-зоны надо заготавливать tarball'ы.
12.04.2004: IPv6 -- насчет него вообще, и насчет поддержки IPv6 в CX: в X11R6.7 была добавлена поддержка IPv6, и краткие комментарии на эту тему имеются в Overview of IPV6 Specific Changes (локальная копия).
Там, в частности, упоминается функция getaddrinfo()
, от
которой, "как за хвостик" (на нее есть man-страница), можно вытянуть
ссылки на еще функции.
Вообще мысль на эту тему:
uselib()
?".
08.01.2005: нашел одно
письмецо
от Tytso
(локальная
копия)
за 28.02.1993 (!!!), где он рассказывает историю появления этой вещи.
Вкратце -- этот syscall был создан, когда линуксовый
mmap()
еще ни черта не умел, кроме требовавшегося для нужд
X11, и совершенно не годился для реализации классического
dlopen()
из SysV.
Таким образом -- uselib()
реализован В ЯДРЕ, в отличие
от userspace dlopen()
. Сие имеет одно достоинство -- что
можно было б даже вставить в ядро "run-time integrity checking" кода.
Пара итальянцев
сокрушались
в своей бумаге
A
Format-Independent Architecture for Run-Time Integrity
Checking of Executable Code
(Luigi Catuogno - Ivan Visconti) о невозможности таких проверок при
dlopen()
.
А чуть позже также попалась цитата из Andreas Schwab за 1998/03/18: "uselib() is for a.out style shared libraries. You don't want to know more about it.".
Итого, мои выводы на тему uselib()
:
Этот системный вызов -- Linux-specific, и уже с этой точки зрения им пользоваться не стоит (кстати, даже man-страница помечена "Linux 0.99.11 1993-07-24").
Как я понял, при использовании
uselib()
имеются какие-то хитрые напряги с выбором адресов, куда библиотека должна грузиться. Типа того, что адрес для каждой библиотеки выбирается при ее компиляции/сборке, и есть специальный registrar для сих адресов (вот тут совсем не въехал -- то ли это автомат, то ли человек...).Напрочь ОТСУТСТВУЕТ как возможность в дальнейшем выгрузить библиотеку (или перевгрузить ее, например, для обновления кода "вживую"), так и, тем более, выцепить ссылку на конкретный символ внутри нее -- аналог
dlsym()
.Реально
uselib()
, похоже, никто не пользуется.ПОРТАБЕЛЬНЫМ является именно
dlopen()
, так что надлежит полагаться именно на него. Не говоря уж о том, что он лишен всех вышеперечисленных недостатков.
Так что, надеюсь, сей вопрос для меня теперь закрыт.
07.02.2005: на эту тему был натравлен Гусев (в связи с CAMAC'ом у Мамкина), и он нарыл описание от "сэнсэя" Дреппера -- futex.pdf (локальная копия). Acroread-4.0(24.01.2000) его почему-то не показывает, а вот xpdf -- за милую душу.
fd_set
'ы при появлении дескриптора стоят проверки -- а не
слишком ли он большой. (Какой я умный!!! :-)
26.01.2005: А сегодня в Bugtraq попалось advisory от Заразы, где говорится ровно про эту проблему, пропущенную в море разных продуктов (@3APA3A's security.nnov.ru, @securityfocus.com). Там упоминается также NetBSD'шный advisory на эту тему за 2002г. Хотя сама по себе идея -- на мой взгляд -- очевидна (еще раз аплодисменты мне, умному).
dlopen()
'нутые библиотеки НЕ защищены от записи (в отличие
от самих программ -- НЕ дают ETXTBSY), и даже более того -- что запись
в эти файлы отображается и в адресном пространстве, и просто тупое
копирование новых версий поверх старых приводит к SIGSEGV.
Причем разбирался с прицелом на написать bugreport -- даже testcase изготовил, в tests/dlopen_etxtbsy/, со скриптом, который и сам все компилирует, и запускает, и перепрописывает .so'шку нулями.
06.04.2006: итак, результаты: да, testcase приерасно падает по SIGSEGV. Думая, что это linux-specific bug, пошел проверять другие ОС, и...
printf()
НЕ отрабатывался, а вот идущий после
вызова printf()
в самой программе -- отрабатывает...
Странновато, конечно, но -- также запись НЕ обламывается.
Стал разбираться/думать, что ж за дела такие -- и нашел... Потянул
за "ниточку" ETXTBSY
-- в исходниках ядра ссылка на него
почти всегда сопровождается словцом "MAP_DENYWRITE". Да, в принципе,
существует флажок MAP_DENYWRITE
, который вроде как должен
давать желаемый результат, но -- не уставляется он. В man-странице
mmap(2) нашел объяснение:
MAP_DENYWRITE This flag is ignored. (Long ago, it signalled that attempts to write to the underlying file should fail with ETXTBUSY. But this was a source of denial-of-service attacks.)
Вот так вот -- как бы НЕТУ такого. Реально-то он есть, но ТОЛЬКО
для ядра: в
2.4:arch/i386/kernel/sys_i386.c::do_mmap2()
делается
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
Т.е., само-то ядро, когда ему говорят "exec()", этот флажок бинарнику
при первом маппировании уставляет (например, в binfmt_elf.c, а
вот ld-linux.so/ld.so -- уже нет. Как следствие,
если программу запускать не "как надо", "напрямую", а через
"/lib/ld-linux.so.2 ИМЯ-ПРОГРАММЫ" -- ничерта от записи не защищается.
Короче -- никакого смысла писать bugreport нету, проблема реально намного глубже... ("Ответчик" Шекли :-)
И -- вот она плата за userspace-загрузку, вместо "a-la uselib()"...
07.04.2006: решил-таки добить вопрос -- поискал Google'ом на тему "ld-linux denywrite", и нашел обсуждение в октябре 2001г.: Security question: "Text file busy" overwriting executables but not shared libraries?.
Кстати, наиболее разумное решение "нашей" проблемы -- именно
"корректное копирование", с предварительным удалением. Что ж --
использовать команду install
?
11.04.2007: проверил, при помощи
strace
-- install
действительно удаляет
destination-file перед писанием в него.
info libc 'customizing printf'
".
А потом наткнулся еще на одну возможность -- что можно (как это было
в Turbo Vision) создавать СВОИ типы потоков (streams), которые совсем
не обязательно привязаны к файлам (именно так внутри libc реализуются
sprintf()
и sscanf()
). Рассказ об оном -- в
подразделе "Custom streams" раздела
Other Kinds of Streams
или в "info libc 'Other Kinds of Streams'
".
ЗАМЕЧАНИЕ (совет от Herve Debar): НЕ ИСПОЛЬЗОВАТЬ nessus для проверки snort'а -- его правила затачивают как раз под nessus.
193.77.107.236 unknown - [18/Sep/2003:18:14:49 +0700] "GET /~bolkhov/files/fonts/univga/ HTTP/1.1" 200 6815 "sl" "-" "www.inp.nsk.su" "http://www.google.com/search?hl=sl&ie=UTF-8&oe=UTF-8&q=+yu+vga+font&lr=" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
193.77.28.46 unknown - [18/Sep/2003:19:20:05 +0700] "GET / HTTP/1.1" 406 446 "sl" "-" "www.inp.nsk.su" "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"при том, что всякие gif'ы и автогенеренные страницы оно прекрасно отдает:
193.77.28.46 unknown - [18/Sep/2003:19:20:57 +0700] "GET /~bolkhov/rtmp/ HTTP/1.1" 200 1872 "sl" "-" "www.inp.nsk.su" "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
193.77.28.46 unknown - [18/Sep/2003:19:20:57 +0700] "GET /icons/back.gif HTTP/1.1" 200 216 "sl" "-" "www.inp.nsk.su" "http://www.inp.nsk.su/~bolkhov/rtmp/" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
23.03.2005: @Shonan: "Device support"==layers! 13.04.2005: опечатка? Видимо, имелось в виду "DRIVER support.
Google search: "what is device support". Дает Under the Hood: Device and Driver Support.
11.04.2005: да, помучал Гусева. О-ей... Там реально ТРИ уровня: Record Support, опциональный Device Support, и еще более опциональный Driver Support.
Да, и еще -- ключевыми словами являются "entry table".
13.04.2005: и очень пользительным оказался google search на тему "device support" "driver support" "record support" -- он дал первым же результатом презентацию самого Джеффа Хилла (Jeff Hill) "EPICS Record/Device/Driver Support".
06.06.2005: еще пообщался с Гусевым. Теперь выходит, что "Device Support"==drivers.
Собственно -- ведь EPICS изготавливался БЕЗ понятия "драйвер", как некая среда для жизнедеятельности виртуальных сущностей "record". Поэтому там и есть "Record support", НЕ имеющий у нас в CX аналогов -- просто в CX сама сущность такая отсутствует.
И напротив -- CX изначально создавался как программное отражение "реального мира" -- блоков, каждый из которых может содержать по многу каналов, и потому драйверный API в CX ориентирован именно на блоки, а каналы там -- несколько вторичны.
03.10.2018: в общении с ЧеблоПашей (на тему MQTT) случайно проскользнуло: "Driver Support" -- это вообще не часть EPICS'а: так обозначается "BSP" -- тот софт, что выдаёт производитель железа, чтобы userspace-софт мог бы с этим железом работать. А в EPICS это понятие/"уровень" присутствует чисто для полноты картины, чтоб "стек" был цельным, не оборванным.
11.06.2005: поговорил на сию тему.
Хмырк... Правильная постановка вопроса -- "как бы он сделал поддержку ПЕРИФЕРИЙНЫХ крейтов".
Как бы то ни было -- ответ получен.
Это весьма общий механизм -- он называется asynDriver, написан by Marty Kraimer,
Сама идея с (initiator,completor) очень похожа на то, что у нас в seqexecauto, не так ли?
Собственно, сегодня-то вопрос встал после обзора имеющихся в CX драйверов "удаленной" аппаратуры -- CAN-bus, Impac IS10, КШД-485, плюс, чуть-чуть, cm5307_drv. И вопрос -- "а как в EPICS'е решаются подобные проблемы?". Вот, собственно, и ответ -- как.
P.S. Почитал я кратко описание asynDriver -- боже ж ты мой!!! До
чего ж EPICS наворочен и сложен! То есть -- да, эта система вроде как
много чего умеет, там навернуты весьма мощные и общие механизмы,
позволяющие [сравнительно] быстро склепывать нечто, чего там раньше
совсем не было (одна только многоуровневость и некий
interposeInterface()
чего стоят!). Но -- до чего ж это в
сумме сложно и необъятно!
13.06.2005: И -- у нас ведь это "очередь per device", а asynDriver -- per port. Другое!
13.06.2005: Мда, тем не менее -- НАДО будет иметь библиотечку "очередь" для cankoz, kshd485, impacis10: объекты вида
{ size_t elemsize; <<ring vars>> // ring{size,start,used} union {int i; double d; void *p} q[0]; // different types -- for alignment }
13.06.2005: кстати, заодно спросил Гусева, как бы он на asynDriver'е писал под КШД485, который когда едет, то отвечает только на "ping'и".
Ну написал бы он, в принципе, asynDriver позволяет добиваться нужного эффекта (в т.ч. при помощи блокировок), но до чего ж это выходит громоздко и завернуто!
11.04.2005: Гусев утверждает, что "PV" -- это "ссылка на record", в некотором роде что-то типа "имени record'а".
11.04.2005: порывшись в сети, нашел некий tutorial -- "Channel Access Portable Server. Application Interface (API) Tutorial". Там, в частности, имеется определение -- "In EPICS, a process variable refers to a record or a record's field.".
11.06.2005: хе-хе, в EPICS'е стандартная БД -- это текстовые (.db) файлы, с описанием -- record'ы такие-то, у них поля имеют значения такие-то.
Так что:
Ошибкой же была идея mkdb.pl -- вовсе НЕ надо было компилировать в бинарную форму, надо было прямо текстовые файлы и читать. Да, понятно, что парсер на Си был бы непотребно тяжел, но это бы работало, а так -- проект загнулся, а вместо него родился инвалидный формат blklist.lst.
Да, и у него "EPICS-сервер" -- т.е., код IOC'а -- запускается из командной строки с указанием параметра -- имени "config-файла", каковым реально является некий набор команд для EPICS-shell'а, содержащий в т.ч. и команды "загрузить такой-то .db-файл". Даже больше -- в начале этого текстового "config-файла" стоит "#!...<путь-к-бинарнику>", так что оно может запускаться аки скрипт.
24.06.2005: ага! Сегодня Гусев прислал ссылку -- EPICS IRMIS (Integrated Relational Model of Installed Systems).
Там есть "primer", в котором и описывается подход. Ребята отнеслись к задаче так же, как и я -- во-первых, считают ее глобальной и сложной -- "(IRMIS) is a project that undertakes this formidable challenge.", а во-вторых, так же хотят хранить там максимум информации.
Надо б натравить Антонова на эту вещь -- пусть анализирует!
11.06.2005: А очень просто -- динамической загрузки в EPICS'е НЕТ!
10.05.2017: кстати, тот cosylab'овский линк стал нерабочим -- какой-то пароль требует.
Поэтому ныне можно брать файл по ANL'евскому адресу, а также он сохранён локально.
Там, в частности, упоминаются как "DCT" (Database Configuration Tool), так и некий "display manager", который, если верить тамошним словам, является как раз тем, о чем я так жажадал повыступать -- "The display manager allows the application engineer to build display hierarchies that use the windowing capabilities of modern workstations...". А еще там упоминается некий EDD -- "graphic display editor" (похоже, предок MEDM'а/dm2k).
А еще попалась статейка " A Comparison of Vsystem and EPICS" -- очень пользительное чтиво, особенно с точки зрения истории.
И там есть золотые слова - "While EPICS has an internal IOC API and a Channel Access API for remote access, Vsystem has a unified API for both local and remote access."
В общем -- на мой взгляд, просто крайне переусложненная (overcomplicated) модель, слабо соотносящаяся с реальным миром. Да, все эти "forward link'и" и т.д. дают весьма навороченный механизм, при помощи которого можно наворотить весьма сложные конструкции, НО -- ведь во многих случаях такая потребность возникает ТОЛЬКО оттого, что нормальных, адекватных средств нету -- вот и приходится ту же проблему "сброс питания" решать совершенно неестественными способами, с привлечением дополнительных рекордов.
Прикидочный список: EPICS -- medm, dm2k; DOOCS -- ddd; "fesa" -- knobs, ...CX -- chlclient.
А вообще -- именно это и используется для рисования разнообразного "фона" типа колец, линаков, etc.
27.07.2006: у нас это теперь тоже есть --
ELEM_CANVAS
& Co. Пока статические, без смены
состояния, но как фон -- уже вполне идут.
28.07.2006: вот только, думается мне, для нас это уже несколько лишнее:
XmNmappedWhenManaged
-- также
некорректно, ибо XmTree такое в документации явно запрещает.
BTW, попалась фраза "Since MEDM displays merely provide connections, you don't need to worry about what happens to the beamline when you delete a display, or reboot your workstation. (Just as the guy on the other end of the telephone continues to exist after you hang up.)". Пять баллов!!! :-)
И, кстати, как он говорит - сейчас число типов рекордов сократили до пары десятков, вместо описанных 36.
21.07.2008: спросил -- нету. Никаких блокировок там нету (разве что самому организовывать, на уровне драйвера, но тогда блокировка от упавшей программы будет оставаться навеки).
29.07.2008: спросил -- да, там все окей, и отображают и, естественно, вычитывают. В этом смысле medm ведет себя разумно -- как наши ручки ;-)
Во-первых, как-то, мягко говоря, криво они сделаны -- они живут в сервере, являются каким-то подвидом "меню", и как-то весьма неудобно задаются.
А во-вторых -- они должны быть МОНОТОННЫМИ, поскольку там подразумевается, что они могут использоваться в обоих направлениях, что в реальности-то бывает очень редко. На эту тему в Known Problems R3.14.9 даже сказано, что это "the check is overly broad and prohibits breaktable use in some circumstances where it is actually safe to do so"
Counter*()
для
счетчиков, а в NI-DAQmx это уже идет как каналы вообще -- всё
управление производится через унифицированный API; с прочими видами
каналов, полагаю, аналогично.
Так что EPICS с его кучей разных видов рЕкордов -- это точно тупик.
28.06.2008: да, пошарился, попробовал поразбираться. Есть на его тему куцая страничка у NI -- DataSocket, с куцым же как бы "техническим" Integrating the Internet into Your Measurement System: DataSocket Technical Overview (в основном там просто маркетинговый треп).
Результаты узнавания:
08.07.2008: неа, все-таки ситуация немножко лучше! :-)
DS_Open()
.
16.09.2008: еще немного информации из того же источника:
16.09.2008: еще одна близкая к DataSocket'у концепция -- "NI Publish-Subscribe Protocol", сокращенно "PSP", отсюда и префикс протокола в современной LabVIEW/LabWindows-8.5 -- "psp:". Но это НЕ DataSocket, у которого "dstp:", а слегка другая хрень -- Shared Variable Engine (SVE). Еще у них отличия в формате URL'ов --
psp://computer/library/shared_variableлибо
psp://computer/process/data_itemсупротив
dstp://servername.com/named_tag
Ссылки по теме:
Вывод/резюме: разные виды линков-то там есть, но вот для использования они совершенно не приспособлены:
Плюс, в Форточках есть некая хрень именуемая "Creator Owner" (т.н. "специальный SID"), который Grimes атрибутирует как якобы аналог umask'а.
Ссылки по теме:
<?xml_version...>
" -- "<?
" -- ?
<!DOCTYPE...>
" -- ?
Общий синтаксис xml: <TAG...>...</TAG>
и
<TAG... />
-- ?
20.01.2004: с pyro-lo возиться не понадобилось, поскольку этот низкотемпературный пирометр нам вообще не нужен, был куплен для салимовцев, и несколько дней назад отдан им.
06.12.2005: (записано 08.01.2005) сделал,
работает. И даже скрипт collect
там имеется. Теперь
старое ~istc/work/{cx,istc}/ остались там исключительно в
качестве архива, работа в них более не ведется.
17.02.2004: поговорил. Ему выдано задание написать некую free-style-бумагу с описанием, чего ему бы хотелось от API мотороллерных драйверов.
Про статусы он озадачился -- придет время, заставлю везде их повставлять, но сюрпризом это уже не будет.
Еще результат: надо б добавить фичу, чтобы в blklist-файле после имени драйвера указывалась еще auxinfo ДЛЯ НЕГО. И, естественно, надо добавить это в протокол -- передачу такой строки. ДОБАВЛЯТЬ НАДО СРОЧНО, еще до перевода пульта на новый вариант.
19.02.2004: кстати, заглянул в протокол m5200_drv<->uclinux_slaves -- так там отсутствует версионирование. А протокол-то УЖЕ поменялся!
Несколькими часами позже: положение несколько осложняется тем, что в протоколе m5200_drv отсутствует понятие "handshake", во время которого можно было бы проверять соответствие версий.
Ввести прям в стандартный заголовок лишнее поле, что ль?
Еще чуть позже: вполне достаточно внести это поле в пакет CONFIG -- он-то и играет роль handshake.
19.02.2004: в сам протокол версионирование
вставил, причем backwards-compatible-способом -- добавил поле
config.proto_version
ПОСЛЕ busid. Есть и заполнение в
m5200_drv.c, и проверка
CX_VERSION_IS_COMPATIBLE()
в
uclinux_driverbody.c.
Заодно пришлось ввести команду M5200DRV_CRAZY
uclinux->m5200_drv -- "мама, я сошла с ума". Он вызывает
DisconnectBlock()
(а как бы иначе мотороллерная часть
посылала хоста подальше?).
Кроме того, добавил и поддержку "slaveinfo" -- через пробел после имени драйвера можно указать строку для него, которая будет передана в пакете CONFIG в качестве данных. В самом uclinux_driverbody.c это пока никак не отразилось.
20.02.2004: ввел поддержку slaveinfo в std_abstract_iface.h, uclinus_driverbody.[ch] и i_mtrlr.c. На той стороне оно называется "infostr".
20.01.2004: точно не скажу когда, но оно уже вполне подогнано.
21.01.2004: Потенциальная недоделка: может в Chl понадобиться hook для вызова юзерской функции по приходу данных, плюс доступ к самим данным.
23.01.2004: точно -- это гарантированно понадобится для управления колесом. Причем часть данных не должна никак отображаться на экране (нууу... то есть, в принципе можно и показывать, но ни к чему). Т.е., должен быть интерфейс для добавления каналов "задним числом". Либо, что будет проще, иметь понятие "скрытого элемента", который присутствует в группировке, но на экране не выводится.
06.09.2004: (реально сделано еще в 04.2004) был введен соответствующий интерфейс в Chl, и hook тоже, а на "скрытые" элементы etc. пока было решено забить.
UpdateChannels()
.
23.01.2004: наконец-то серьезно взялся за эту работу. После длительных размышлений решил запхать туда таблицу с почти всеми свойствами -- Name, Label, Value, Raw, Age, Flags, Ylw, Red, Disp, Step.
24.01.2004: сделал кнопки OK и Cancel и ужаснулся: они ж разного размера, и выглядит это весьма уродливо. Явно надо брать какой-нибудь готовый Motif'овский диалог и подтачивать его под себя. Вот только какой -- в этом уродстве есть только либо диалоги и с кнопками под сепаратором, и всяким барахлом типа готового поля ввода, либо без кнопок, а "диалога вообще" не предоставляется...
24.01.2004: Парой часов позже: ура, ура, ура!
Итак: полез в Motif FAQ by Ken Lee, и там в разделе "DIALOGS" нашелся вопрос "How do I make my own dialog?", в котором рекомендуется изготовить стандартный диалог, XtUnmanageChild() из него все лишнее, и добавить свое.
Во-первых, я так и сделал (XtUnmanage XmDIALOG_HELP_BUTTON и XmDIALOG_MESSAGE_LABEL), и добавил свой grid. Оно (XmMessageBox) подхватило grid и поставило его в нужное место, с нужными полями. Вудуистика какая-то: как оно поняло, что это именно "work area widget", а не кнопка -- неясно, ведь никаких constraint'ов-то там нету. Ну да бог с ним -- берет, и прекрасно.
Во-вторых, меня заинтриговало существование двух разных
функций: XmCreateMessageBox()
и
XmCreateMessageDialog()
. По запросу
"XmCreateMessageBox XmCreateMessageDialog difference" Google
нарыл страницу
Chapter 3. More Techniques for Using Widgets,
где (в разделе "Creating a Basic Dialog Box") имеется объяснение.
Разница в том, что XmCreateZZZBox() создают только сам виджет, а
XmCreateZZZDialog() -- еще и DialogShell для него. Я предпочел создать
Shell сам -- так можно указать ему
XmNmwm{Decorations,Functions}
, и потом прилепить
MessageBox.
24.01.2004: в минимальном варианте окошко работает -- отображаются имя/метка, диапазоны, значение/сырое даже меняются по мере прихода данных.
Что осталось: 1) Махинации со Step (как чтение его, так и disable для readonly-каналов и введение поля "step_useful"); 2) создание и отображение rflags; 3) изменение диапазонов; 4) запись/чтение диапазонов в режимах.
25.01.2004: черт, еще один прикол: похоже, в SGL глюки, когда содержащиеся в нем XmLabel пытаются поменять размеры при смене текста метки. По крайней мере, так оно внутри XmBulletinBoard'а.
Пришлось ставить всем таким меткам первоначальную строку
"Q"x20 и XmNrecomputeSize:=False
.
Надо поговорить с AAntonov, как проявится.
25.01.2004: все, ввод шага доделал. И из Knobs_text_widget.c весь код возни с шагом удален (теперь он остался в backup-копиях, самый современный -- в BACKUP/cx_src.20031017_proto_and_errstats).
07.02.2004: несколько дней назад поговорил с Антоновым -- он порылся и нашел глюк. Вроде исправил, теперь SIGSEGV не бывает, но все равно что-то не слава богу.
09.02.2004: вывод флагов сделан.
10.02.2004: насчет "не слава богу": придумал простой способ привести окно в чувство прямо сейчас. Поскольку индикатор флагов шире, чем все лэйблы, то мы просто ставим лэйблам alignment=left и filling=horizontal.
11.02.2004: окошко "смена диапазона" сделано, и работает (но подтверждение не спрашивает! И никакую переменную окружения не проверяет!).
Замечание: пришлось несколько перекурочить старую схему -- намного удобнее иметь все, касающееся этих трех диапазонов, массивами из 3 элементов, а не тремя отдельными переменными.
Возможно, стоит поменять содержимое knobinfo_t
и ввести
константы типа "KNOB_RANGE_{NORM,YELW,DISP}".
Осталось только сделать копирование измененных диапазонов из окна в Knob, плюс сохранение/восстановление в Cdr.
12.02.2004: сделал-таки подтверждение "Вы уверены..." -- это еще одно окошко. Файл потихоньку приобретает кошмарный вид -- надо б повыдергать манипуляции с разными компонентами в разные функции и посгруппировывать их в блоки "по компонентам".
13.02.2004: поменял knobinfo_t
как
планировал -- массивы rmins[] и rmaxs[] взамен былых
{min,max}{norm,yelw,disp}, плюс rchg[], и три константы тоже ввел.
Заодно обнаружил баг, проистекавший еще от последних версий oldChl_data.c: при неуказанном disp-диапазоне, но указанном norm-, брался не norm-, а yelw-диапазон -- опечатка была.
15.02.2004: сделал копирование измененных диапазонов из окошка в Knob.
15.02.2004: сделал в
Cdr.c::CdrSaveGrouplistMode()
сохранение
измененных диапазонов.
Сделал также и чтение. Вместо "общего" кода для чтения "в принципе любого поля" оно умеет читать один из трех диапазонов.
Поскольку строки потенциально могут стать весьма длинными, увеличил размер входного буфера строки с 100 до 1000.
На будущее: код парсинга разросся и стал
довольно некрасивым. Если захочется добавлять еще какие-либо фичи, то
надо будет перевести CdrLoadGrouplistMode()
на ту же
архитектуру, что и ReadCxdConf()
и
SimulateDatabase()
-- т.е., Get{Identifier,Int,Double,...}
10.04.2004: обнаружил, что контейнеру флагов
(fc
) не назначались ни Position, ни Filling, ни Alignment.
Просто SGL сам ставил его в "следующую" клетку. А с Gridbox позиция
сразу вылезла.
15.07.2004: наконец-то (всего-то через полгода
:-) допроставлял ресурсы XmNtitle
на всех dialogShell'ах в
Chl_knobprops.c.
08.01.2005: так во-о-от где пряталась первоначальная запись о желательности этой фичи!!!
Поскольку все было реализовано больше месяца назад (почти ровно через год после этой записи о потребности :-), то помечаем как "done".
Вопрос: это ж опасно?! Ответ: требовать "десять подтверждений".
Дополнение: а можно и сделать сохранение пределов в файле, как в свое время было добавлено сохранение шага -- опционально, через пробел в конце строки.
08.12.2003: после советования с Ильей: можно сделать фичу "смены пределов" по умолчанию запрещенной, а чтобы разрешать -- запускать программу с неким специальным ключиком. Правильно! А подтверждения хватит и одного.
15.12.2003: насчет "опционального" сохранения: дабы не становиться рабом формата, стоит после "шага" поля писАть не просто линейно, а с "селектором" -- т.е., например, "ylwlimits:NN.NNN,MM.MMM".
15.02.2004: в общем-то фича готова и работает, так что помечаем раздел как "done".
Единственное, что пока не сделано (хотя и не есть проблема, просто не сделано, поскольку пока не особо актуально) -- это разрешенность менять диапазоны только при некоторой выставленной переменной окружения.
05.11.2004: И-ГО-ГО!!! Неактуально, как же!!! Позвонил сегодня Федя-jr. и разорался: "да как же это так -- какой-то козел будет менять пределы в моей системе (да еще и в режим это сохранит!)!".
В общем, он потребовал, чтобы эта фича -- изменение пределов -- лучше бы вообще была убрана, а чтобы производилась эта смена только в исходниках (в будущем-то в БД, а там автоматом access control будет). Сошлись на том, чтобы поговорить с Логачевым и Старостенкой, и вердикт вынести совместно.
Блин, но ведь иногда-то такая "оперативная" смена очень нужна! А интерфейс-то лучше бы чтобы везде был один...
Хммм... А что, может еще один флажок в
subsysdescr_t.options
ввести -- "changeable_ranges"?
27.06.2014: сегодня вместо всего этого умничанья с вводом диапазона в под-окне с последующим "Are you sure?!" был сделан ввод прямо в самом окне "Knob properties". Ибо достало.
Засим можно считать, что на тех идеологических терзаниях поставлена точка.
21.02.2004: Раз плюнул. В смысле -- сделал.
На основе того же XmMessageBox'а, только здесь "Message" оставлен и используется для отображения имени knob'а. Из кнопок оставлена только [OK]. Вот с описанием шрифта пришлось помудрить -- в Xh_fallbacks.h вставлены сомнительной разумности строки
Так что оно хватает первый попавшийся пропорциональный bold-шрифт. Пропорциональный пришлось сделать, поскольку а) иначе она хватает и масштабирует какой-нибудь misc-fixed, и б) наверняка найдется что-нибудь векторное именно пропорциональное. "iso*" -- чтоб не хапнула какой-нибудь adobe-fontspecific или wingdings.#define XH_FONT_HUGESIZE "96" #define XH_HUGE_PROPORTIONAL_FONT "-*-*-bold-r-*-*-" XH_FONT_HUGESIZE "-*-*-*-p-*-iso*-1" ... "*.bigvBox.value.fontList:" XH_HUGE_PROPORTIONAL_FONT, \
Замечание 1: желанное раньше автоматическое удаление окна при выходе из него мыши (наследство от идеи "значение в tooltip'е") не реализовано. По здравому размышлению -- зачем? Это ж не tooltip.
Замечание 2: наверняка какая-нибудь Старостенка захочет иметь возможность крупного отображения сразу НЕСКОЛЬКИХ каналов. Это хоть и возможно, но значительно сложнее -- потребуется привернуть целый интерфейс добавления/удаления канала из списка.
Хотя... Лучше вместо интерфейса сделать так: Ctrl+Btn3 добавляет
канал к списку; сам список сделать grid'ом из 3 столбцов -- метка,
значение, кнопка [-] ("удалить"). Например, до десятка каналов. При
удалении список будет "сжиматься", при удалении последнего -- окно
автоматически закроется. При удалении/добавлении надо будет заставить
grid и MessageBox выполнить перерасчет размеров (в идеале --
unconditionally XtUnmanage, а потом обратно). Ну уж для этого-то точно
понадобится обезглюченный вариант виджета-сетки. Плюс, естественно,
флажок XmDIALOG_PRIMARY_APPLICATION_MODAL
придется убрать
-- а не возникнут ли глюки с перепозиционированием окна?
23.02.2004: мысль: а как насчет "тоже расцвечивать"
эти крупногабаритные числа? Ведь все потребные флажки-то у нас уже
есть (спасибо Cdr). И, естественно, вытащить "расцвечивалку" в
helper-функцию -- типа "Knobs_Colorize_Helper()
".
Замечание о разделении обязанностей: а нужна ли в Knobs вообще алхимия с выбором цвета? Ведь, опять же, флаги-то у нас есть -- пусть по ним и ориентируется...
06.05.2004: насчет отображения больших
символов... Можно ведь, чтобы не мучиться с подбором большого шрифта,
иметь "встроенный" bitmap-шрифт, содержащий "0123456789+-.enaif?" (это
все, что требуется для чисел и inf/nan), и выводить все "руками" при
помощи XFillRectangle()
.
07.11.2004: угу, а есть еще идея: не иметь свой шрифт, а прямо тем, который уставляется в качестве "*.value.fontList", писАть вышеуказанную строку на битмэпе, потом вытягивать этот битмэп к себе в массив, и дальше -- как в предыдущем варианте.
07.11.2004: угу, решил приступить к расцвечиванию крупногабаритных чисел.
Итого -- сделал. Теперь в
DisplayBigVal()
по-умному (при newstate!=oldstate)
принимается решение о выборе цветов. В качестве "defaults" берутся
цвета поля big.val
сразу после его создания.
Заодно, поскольку по ходу дела "клиентский" ki
может
меняться на другой, с тем же текущим colstate
, но с другим
color
, пришлось ввести в Knobs_types.h новое,
"никакое" состояние -- COLALARM_UNINITIALIZED
.
06.08.2006: давно уже сделан, даже уже два раза, и используется. "done".
GetParamInt()
. Ежели что -- оно осталось в
BACKUP/istc.20041212.messy/.
06.08.2006: со 2-й версии номер интерфейса указывается именно в bus_id -- в формате "IFACE,DEV_ID". "done".
22.06.2004: Недеогло прислал исходники драйвера для 2.6.
25.06.2004: несколько дней назад общался с Федоровым -- он обещался помочь с разбирательством с 2.6, но сам делать этого не будет, типа занятой.
25.06.2004: еще из прелестей, понятых в последние дни:
09.01.2004: поговорил со Старостенкой, который утверждает, что, как минимум, Губину&Co. это сильно требуется, да и вообще это чрезвычайно полезная фича.
Во время этого разговора в голову пришла схема довольно простой и элегантной реализации.
Основная идея -- пишутся не "регулярные snapshot'ы" (типа раза в
секунду), а ИЗМЕНЕНИЯ, тэгированные временем измерения. А точнее --
результаты драйверных ReturnChanNNN()
. Это дает всю
информацию, необходимую для "воспроизведения".
Оптимизация: для "быстрого поиска", чтобы не надо было "проматывать" весь файл, можно, по аналогии с Mpeg, время от времени (раз в минуту?) писать полный snapshot.
Замечание: запросы на запись при воспроизведении надо игнорировать -- ведь если мы, к примеру, смотрим телевизор, то он нас не слышит.
Вопрос: а не потребуется ли также записывать и запросы? И если да, то зачем? Ответ: да, нужно, для разборок -- чтобы понять, каким клиентом было вызвано какое изменение.
Очевидный вывод: записью может заниматься ТОЛЬКО модуль в cx-сервере, который вешается на hook'и в ReturnChanNNN() etc.
24.01.2004: только-только стал вспоминать об этой фиче...
Когда-то давно, еще во времена Эйдельмана, у предка Chl (mapp/uxil) была возможность вытащить до 3 каналов в окошко/область отображения графиков-самописцев.
Потом, уже в древних версиях Chl, эта идея трансформировалась в интерфейс "PopulateWithGraphs()", позволявший населить окно не элементами, а N самописцами, на каждый из которых можно было так же вывести любой из каналов.
С тех пор в А когда начал делать KnobProps, то, вставив туда и строчку для disp,
вспомнил про эту идею, и озадачился.
Заметка: как водится, надо обсудить со Старостенкой, в
каком именно виду нужнее/удобнее иметь такую возможность.
BTW, как по-английски "самописец"?
logchannet_t
/knobinfo_t
остались поля {min,max}disp
.
26.01.2004: обсудил (дату проставил по памяти, +/- день). Ответ:
Подвариант (идея Старостенки): поскольку при таком наложении графики могут полностью перекрывать друг друга, можно уменьшить разрешение по горизонтали. Т.е., при N графиках 1 шаг будет занимать N пикселов в ширину, причем графики отображаются со сдвигом в 1 пиксел.
12.04.2004: "самописец" по-английски:
- LingvoComputer-R:
- самописец: logger, recording meter, plotter, analog data recorder, recorder
- Polytechnical-R:
- самописец: chart-recording instrument, graphic instrument, recording instrument, registering instrument, curve-drawing meter, recording meter, analog (data) recorder, automatic recorder
06.09.2004: ну вот, сегодня еще и Лебедев прикопался -- что ему, в качестве дежурного на пульту, жаждется иметь подобную фичу, например, для слежки за вакуумом.
16.12.2004: срочно нужнО это для c13/vibro. И тут же пришла мысля:
А ведь буфер истории-то надо делать кольцевым! Безо всяких сдвигов.
Кстати, уже имелся ранее буфер "historybuf
",
используемый для LOGT_MINMAX
. Поскольку в него
складируются "исходные", а не "отображаемые" значения, то его
использовать для истории нельзя. Для корректности переименовал
historybuf{,cap,used}
в minmaxbuf{,cap,used}
.
Итого: ввел в knobinfo_t
поля
histring{,size,start,used}
, и если
histring_size!=0
, то CdrProcessKnobs()
автоматически ведет историю. Самая "старая" точка -- это
histring_start
. Плюс, в CdrDestroyKnobs()
не
забыто и safe_free(ki->histring)
.
17.12.2004: мдя... Для каждого канала надо также иметь параметр "частота обновления", и добавлять данные только каждый N-й раз (а верхнему уровню сигналом к перерисовке будет ctr==0). Частота 0 считается за 1.
20.12.2004: ввел еще пару полей --
histring_frqdiv
(частота обновления -- один раз в сколько
тиков) и histring_ctr
(счетчик). Счетчик каждый цикл
увеличивается на 1, пока не станет ctr>=frqdiv, после чего он
сбрасывается в 0. Добавление данных к истории происходит именно при
ctr==0; это же является для "рисовальщика" графиков и сигналом к
перерисовке.
Синтетические обновления полностью игнорируются.
22.12.2004: сделан "прототип" графиков в istc/xmclients/vibro.c. Они в принципе работают, как прототип -- приемлемо. Пока технология frqdiv не используется. На vibro.c все и обкатаем.
Уже сейчас в прототипе каждому виджету-графику придается инфраструктура: заголовок,
По ходу дела возникали идеи:
rmins[KNOBS_RANGE_DISP]
всех
задействованных каналов, maxdisp -- аналогично. И для изменения шкалы
приходится изменять параметры у всех каналов. А стоит ввести
аналогичную обратную возможность: менять пределы ГРАФИКА, чтобы при
этом они автоматически уставлялись и всем каналам в их
{rmins,rmaxs}[KNOBS_RANGE_DISP]
.
27.12.2004: еще разок посмотрел у Гусева на программку "StripTool", рисующую графики, и вот что увидел:
- Набор "автоматически выбираемых" цветов там тоже дурацковат.
- Но зеленый среди них есть -- ТЕМНО-зеленый.
- Так что имеет смысл первые 3 цвета сделать -- красный, синий, темно-зеленый. 27.12.2004: сделал.
- Там самописец идет не с координаты x=0, а как настоящий самописец -- просто ВСЕГДА тянется справа налево, т.е., данные появляются именно справа, а слева вначале пусто.
Подход "все всегда ползет справа" очень удобен: он автоматически решает проблему, когда разные каналы имеют разное количество намерянных значений (в т.ч., когда появляется свежедобавленный канал). Это очень хорошо соответствует "реальному миру". И все -- за смешную цену легкого усложнения арифметики.
Т.е., всегда рисовать ПОСЛЕДНИЕ n точек, где n -- минимальное из ширины-вместимости графика и количества имеющихся точек.
13.01.2005: начал использовать фичу "frqdiv" -- проверил, эта концепция в Cdr.c работает.
20.01.2005: похоже, пора выносить махинации с графиками из vibro.c в отдельный файл.
Вечер того же дня: вынес, назвав его Chl_histplot.c. Это пока ОЧЕНЬ ПРЕДВАРИТЕЛЬНЫЙ вариант, в т.ч. и тем, что он реально-то пока не привязан к Cdl вообще никак, и потому не имеет доступа к ее внутренним структурам (а точнее -- к datainfo).
Заодно вопрос: а НАДО ЛИ делать такую привязку? Или лучше развивать сию подбиблиотечку как достаточно автономную, могущую работать и отдельно от собственно Chl'ного ядра?
21.01.2005: переделал, что теперь графики всегда ползут справа. Усложнение там было, в общем-то, простейшее. И сразу проверил -- поставил разным каналам разные frqdiv, и оно действительно выезжало с разной скоростью.
20.12.2004: результат brainstorm'а:
Варианты (1) и (4) -- это примерно одно и то же (их обобщение -- combobox), и они примерно одинаково неудобны для среднестатистического? пользователя. (А так -- селектор удобнее, если каналов несколько штук, а текстовое поле -- если их десятки/сотни).
Вариант (2) неудобен в реализации -- куда-то надо прилеплять к каждому каналу по целому чекбоксу (который еще и место занимать будет). (Вот если бы у нас по правой кнопке меню появлялось, то в него можно б было вставить пункт "Show history graph".)
А вот вариант (3) с DnD -- и наиболее удобен пользователям, и наиболее элегантен. Проблема лишь -- ведь просто левая кнопка мыши уже во всех виджетах задействована. Использовать Motif'овский DnD нельзя -- мало того, что замутен, так еще и глюкав. А что, если повесить сие на ПРАВУЮ кнопку -- например, Shift+Btn3Down? Ведь правая кнопка уже задействована... Наверное, так и сделаем.
(И, естественно, по несколько каналов на график, с подписями, какой есть какой, и чтобы можно было их оттуда удалять, например, при помощи щелчка по [x].)
25.12.2004: кстати, а зачем SHIFT? Ведь можно окошко Properties выдавать не по нажатию, а по отпусканию кнопки, а по нажатию -- лишь фиксировать нажатие, и если мышь сдвигается более чем на N пикселов, то считать это за DnD, иначе -- за Properties. Так сделано в FVWM на заголовке окна -- это либо raise, либо move.
08.02.2008: собственно -- уж год как сделано Shift+Btn3. И как я тогда этот раздел пропустил? Короче -- "done".
Ну и где б это можно было рисовать? Места то везде жа-а-алко!!! (А так -- можно б иметь место справа от метки с именем канала.)
А что -- может, ЗАМЕНЯТЬ на это время метку? Или, может, иметь ВТОРОЙ виджет (уже XmText), который бы на время отображения лежал ПОВЕРХ метки?
18.09.2006: собственно, знание подспудно накапливалось, а последней каплей стала реализация adc200.c. Итак:
Делается это так: считываются текущие размеры shell'а, из них вычитаются текущие размеры drawing area, которая график, и это плюс 100*50 идет за минимум.
12.04.2004: ПРЕДЫСТОРИЯ: еще 01.04.2003 вставала необходимость запрета некоторых действий (там -- изменения частоты) при некоем условии (там -- при запущенности комплекса), а 09.06.2003 возникала идея "ставить LOGC_DIM на лету". Так вот, в ту же струю:
СЕГОДНЯ, при обсуждении нового поколения программы управления RFSYN (теперь уже, увы, гусевского) вопрос о "запрете" стал вновь. И возникла очевидная мысль: а как насчет делать на лету "disable" таким "ответственным" каналам?
РАЗМЫШЛЕНИЕ 1: Насчет "disable" (читай -- XmNsensitive:=False): с техническо-теоретической точки зрения это классно, но выглядит (в Motif'е) "уродски", да и разобрать, какая же "частота" нарисована на за-disable'нном селекторе -- сложно (а вот в гусиной схеме, с вводом и отображением отдельно -- проканает). Так что -- для таких случаев лучше именно "LOGC_DIM на лету", плюс запрещать "изменениям отрабатываться".
РАЗМЫШЛЕНИЕ 2: технически можно "запрет изменений" реализовывать и в
формулах. А можно, если уж что-то такое "специальное" с изменением
статуса на лету сделаем -- ввести в knobinfo_t
дополнительное поле "locked
".
14.01.2006: в продолжение тех мыслей:
09.02.2006: ну ладно, CXRF_LOCKED -- а почему не CXRF_READONLY?
19.08.2007: собственно -- а ведь имеющаяся сейчас (с 26-02-2007) в alarmonoffled фича "victim=" уже решает проблему с обес-sensitive'ньем, чего для >90% реальных случаев -- типа гусиного запрета изменений -- вполне хватит. Хотя, конечно, DIM красивее...
12.04.2004: мысль по мотивам того же RFSYN: в некоторых сложных системах требуется "сшить" несколько драйверов единой логикой, которую (в т.ч. из соображений времени отклика) надо иметь в сервере.
Современная модель -- делается "большой драйвер", который включает в себя код из драйверов всех задействованных блоков, и отдает "наружу" только несколько каналов для управления собой, а каналы тех блоков -- прячет. Так (кажется) было сделано Олегом в rfsyn, аналогично поступил А.Чугуев в какой-то своей алхимии для накопителя.
ВОПРОС: а не надо ль ввести в сервер некую инфраструктуру, чтобы драйверы могли переговариваться/договариваться сами?
"ПРОТИВ" этой идеи:
А не понадобится ли вводить совсем-совсем-"метасредства" типа callback'ов по обновлению значений в каналах "подчиненных" драйверов, запрета на запись в подчиненные каналы клиентами, и т.д. И вообще это скорее тянет на "сверхобщую" инфраструктуру типа EPICS'ной.
10.05.2004: у Гусева "ручная" работа с УШД сделана очень элегантно: стрелки "идти" совмещены с концевиками.
[<-][шаги][->]
Т.е., когда доходит до концевика, то красным подсвечивается стрелка
ходьбы в ту сторону. И это правильно -- ведь в ту сторону идти больше
незачем. (Но она не disable'ится -- может, и незачем, но возможность
идти туда остается.)
ВОПРОС: можно ли подобное сделать в рамках CX/Chl? Да, можно -- есть ведь и подсветка, и "протокол кнопок", и формульные вычисления, позволяющие подшаманить любой нужный набор битов.
ЕСТЬ ПРОБЛЕМА: вставить такой "блок управления УШД" в обычный элемент чаще всего не получится -- шибко много колонок занимает (еще ведь кнопка [СТОП], и еще кумулятивный счетчик шагов...). Т.е. -- специальный элемент "управление десятком УШД" сделать не проблема, но возникает-то необходимость вставлять такой "блок" в ОДНУ колонку.
Вариант решения 1: изготовить специальный Knob-type -- "УШД", который из 32 бит извлекает биты статуса и собственно число шагов, и имеет надлежащее поведение с отдачей команд и покраснением. ПРОБЛЕМА: во-первых, громоздко, а во-вторых -- упихивать всю информацию в 32 бита (которые к тому же передаются через double) -- мерзкое занятие (еще ведь надо будет ему должную хитрую формулу назначать).
ВАРИАНТ РЕШЕНИЯ 2: ход песцом: а что, если разрешить "вложенные элементы"? Т.е., чтобы в клетку для Knob'а впихивалась очень специальная сущность, представляющая собой сетку с каналами (W*H, обычно H=1), которая бы содержала ТОЛЬКО каналы (без меток и, наверное, без сепараторов). Сделать-то сетку -- не проблема, но КЛЮЧЕВОЙ ВОПРОС: как обеспечивать такой "вложенный элемент" связью-с-данными? Есть два варианта:
А вот это -- сплошная проблема. Во-первых, тогда перестанут
работать тривиальные вычисления типа "где находится [y,x]-й Knob". Так
что добавлять данные "потребителей" можно только в конец общего списка.
Во-вторых, очень нетривиальным станет подсчет "сколько же Knob'ов у
этого элемента" -- соотношение nrows=count/ncols
перестанет быть верным. И в третьих -- дюже неудобно будет описывать
такие элементы -- придется вручную подсчитывать, где же в списке
каналов начинается такой-то подэлемент (с при вложенности >1
начнутся вообще страшные напряги...).
ВЫВОД: да, "вложенные элементы" -- очень ценная фича, которую стОит сделать, "по первому" варианту.
Это не сверхсрочно, но надо обмысливать, и когда будет "моральная готовность" -- реализовать.
Несколькими часами позже: а мне эта идея нравится все больше и больше.
Кстати, возникла еще мысль: а как staromakh будет на такие каналы ссылаться -- "elem.echannel[.subchannel[.subsubchannel...]]"?
И еще: это ж надо будет ввести новый тип канала -- наподобие
LOGT_SUBELEM (и kind
в таких случаях игнорировать; о! -- а
может, ввести "поле-флаг" вроде is_nested?). Да еще
к тому же логика "не LOGT_WRITE -- значит, LOGT_READ" станет
неверной... Хотя -- только что порылся в исходниках на тему LOGT_*, и
что выяснилось:
LOGT_*
содержатся ТОЛЬКО в Cdr.c.
LOGT_READ
вообще НИГДЕ не упоминается.
ki->is_rw
. И это ВСЕ!
LOGT_{DEVN,MINMAX}
-- на
предмет специфичной работы с ними.
.kind=LOGK_{DIRECT,CALCED}
для понимания, надо ли работать
с физканалом или с формулой, а вопросы r/rw его не волнуют -- он это
оставляет на усмотрение CX-сервера. (Давний вывод -- ему дорога на
Cdr.)
Итого -- все изменения коснутся Cdr.c (конкретно --
CdrCvtLogchannets2Knobs()
, CdrDestroyKnobs()
,
CdrProcessKnobs()
,
Cdr{Save,Load,Log}GrouplistMode()
...) и Chl_gui.c
(ChlMakeElement()
, chanspec2knobinfo()
(а не
перетащить ли ее в Cdr?)). (Возможно, еще в Chl_knobprops.c
захочется в окошках Props и BigVal выводить ПОЛНОЕ имя канала.)
Еще встанет вопрос об обеспечении "методов" элемента. Здесь ВЕЗДЕ
можно с чистой совестью один-в-один вызывать соответствующий метод
parent'а; а в случае с Chl -- можно назначать тот же самый
emethods
, и лишь в E_ShowAlarm_m()
ввести
проверку: если "мы" -- самый верхний элемент, то выполнить работу, а
иначе -- вызвать самого себя, передав первым параметром parent'а.
(Побочный вывод: надо будет ввести поле
eleminfo_t.uplink
-- точную функциональную копию
knobinfo_t.uplink
.
Ой-ей... А ведь еще более усложнится загрузка из файла (запись-то сравнительно тривиальна -- лишняя вложенность рекурсии)...
Общее размышление: идея-то красивая, и реализовабельная буквально за пару дней, но -- не стоит ли остановить эту гонку постоянного улучшательства, и сосредоточиться на преддиссертационной доводке имеющегося?
Замечание в сторону: если реализую ЭТО, то для полного счастья будет не хватать только возможности поместить в элемент и БОЛЬШОЙ канал (bigc).
И еще мысль: вот в PSP есть такой тип -- вложенный PSP; здесь бо-о-ольшим прорывом выглядит идея вложенного элемента. ВОПРОС: а где еще в инфраструктуре CX возможны "рекурсии", которые сделали бы свою область проще/элегантнее/полнее?
11.05.2004: вдогонку: как бы еще этак научиться внутри элемента делать "сепаратор" -- горизонтальную линию на всю строку, а?
11.05.2004: еще мысль: при современной
схеме именования Chl-каналов ("элемент.канал") нельзя
перетасовывать каналы -- таскать из элемента в элемент, поскольку тогда
перестануть работать сохраненные файлы. А вот если позволить элементы
с пустыми именами, в которых каналы имеют имена уже сразу с точкой (или
вида ".aaa.bbb.ccc"?) -- тогда, при переделанном коде
CdrLoadGrouplistMode()
, который будет более
"интеллектуально" искать каналы в элементах/подэлементах, можно будет
тасовать.
26.09.2004: кстати, насчет "как делать сепаратор" и касательно управления внешним видом вообще:
Можно бы добавить в logchannet_t/knobinfo_t
еще одно поле -- layout_info
. Оно предназначалось бы для
parent'а, чтобы указывать ему, как располагать ручку/подэлемент --
{horz,vert}_{alignment,filling}.
Тогда сепаратор сделать будет тривиально -- LOGD_[HV]SEPARATOR плюс указания о его растягивании.
Аналогично, просто будет компоновать и весь экран в рамках единого элемента из подэлементов.
Хотя, честно говоря, я сильно сомневаюсь, что решусь вводить еще одно поле :-).
27.09.2004: подумавши про сепаратор чуть дольше:
А можно ведь вместо "layout_info" воспользоваться уже
готовым knobinfo_t.behaviour
. Заграбастать там два
дуплета битов (4 значения: 0-default, 1-begin, 2-end, 3-fill), и пусть
Knobs_separator_widget выставляет либо в горизонтальном, либо в
вертикальном дуплете тройку.
27.09.2004: все, сил нет -- хочу сделать вложенные элементы, хотя бы пока и без поддержки загрузки режимов.
Итак, что сделано:
LOGT_SUBELEM=3000
.
knobinfo_t.subelem
.
logchannet_t.formula
.
CdrCvtLogchannets2Knobs()
(там еще вставлен отдельный guard, чтобы оно не пыталось
регистрировать формулы обычным образом),
CdrDestroyKnobs()
,
CdrProcessKnobs()
,
Что не нравится -- проверки на LOGT_SUBELEM
введены
несколько "хаковатым" образом, а стоит корректно разделить случаи
SUBELEM/не-SUBELEM условиями.
CreateKnob()
) вынесен в отдельную функцию (поскольку оно
встречалось два раза -- для SINGLECOL и MULTICOL), в которой
и вставлена проверка на LOGT_SUBELEM
.
GiveBirthToKnob()
пришлось вставить
"дублирование" -- копирование e->container
в
ki->indicator
, чтобы дальше с элементом
манипулировалось так же, как с knob'ом.
Недоделанности:
Как бы то ни было, начальный вариант вложенных элементов работает!!!
Итого: на camsel'е опробовано, теперь надо повозиться с istcc:
перевести управление объективом и флаги КШД на вложенные элементы, плюс
переделать блок "под картинкой" на Knobs и
ChlCreateContainer()
.
Часом позже: с проблемами ShowAlarm() разобрался -- сделал все "по-правильному".
28.09.2004: вообще-то есть в результате и некоторая некрасивость -- дублирование информации. В частности, "имя" (ident) такого вложенного элемента есть в 2 местах: в содержащем его knobinfo, и в нем самом. И, например, вопрос -- какое из этих полей использовать при построении fully-qualified channel name?
05.10.2004: вроде бы Антонов сделал в SGL возможность указывать sepWidth/sepHeight, так что в Xh_grid.c введена соответствующая функциональность, и Chl_gui.c переведен на нее. В принципе -- работает, но что-то в SGL'е подглючивает, что надо подпроверить. 20.12.2004: давным-давно в SGL все поправлено, вроде больше не глючит.
29.11.2004: ню-ню, я-то перевел istcc на
вложенные элементы, но ведь там были явные ссылки (через
ChlGetChanVal()
) на имена вида "kshd.NNN", а
сейчас они де-факто -- "kshd.flags.NNN", так что нифига istcc не будет
работать!!! До тех пор, пока не появится ПОЛНОЙ поддержки
вложенных элементов -- в т.ч. и резолвинга имен.
04.12.2004: (записано 20.12.2004) все сделано --
см. раздел на тему CdrFindKnob()
: есть полная поддержка
вложенных элементов -- в т.ч. и резолвинг имен. И в istcc.c
теперь стоят корректные subelem-aware имена.
10.01.2005: ой-ей-ей!!! И как же это оно
работало-то!!! По какому-то странному глюку в
GiveBirthToKnob()
стояло копирование в e->container
в
почему-то ki->indicator
, вместо надлежащего
ki->subelem->container
. Наверное,
авторасстановка SGL'ем спасала, что ли...
Пофиксил.
Пример, где это нужно -- магнитная система: там недурственно бы уметь подсвечивать некоторые каналы не по их значениям, а по совсем другим -- которые сейчас вынесены в отдельные LOGD_LIGHT.
Возможно, удастся сделать нечто подобное, введя
дополнительное поле -- colformula
, которое также будет
рассматриваться как read-formula, и все колоризационные сравнения будут
делаться с ее значением; а если оно ==NULL -- то с обычным значением
knob'а.
19.04.2005: йоу-йоу!!! Итак: в CXv4 понятие "colformula" будет сразу. А в нынешнем CX -- появилась идея, как это можно сделать, см. раздел "colformula".
20.04.2005: сделал, "внешняя колоризация" изготовлена и проверена, работает успешно.
10.08.2004: возник в голове некий план-проект плавного ухода от размещения physinfo в клиентах. (Последней каплей стала потребность Феди-jr. как-то указывать physinfo в своей программе, а ему для этого пришлось #include всякого из chlclients/DB/.)
Итак, исходные данные:
Мысли по реализации:
:NUMBER n r [d [q]]
Наверное, разумно будет дать возможность указать n как "first-last" -- чтобы присваивать physinfo сразу группе каналов.
Возникает вопрос: а как насчет иметь возможность r и d указывать не готовым числом, а выражением? Сейчас такое активно используется. И даже более того: такой же вопрос останется и при переходе на реальную БД -- числа удобнее будет указывать не double'ами, а именно строками-формулами.
double_r_t c_phys_r[CX_MAX_CHANS]; double_r_t c_phys_d[CX_MAX_CHANS]; int32 c_phys_q[CX_MAX_CHANS];
А вот с тем, что есть double_r_t
-- отдельный вопрос.
Хотелось-то иметь возможность делать сервер, вообще никак не
использующий floating-point, чтобы он мог функционировать на cm5307-ppc
без эмуляции. Тогда надо делать
typedef char double_r_t[20]
Минусы строковости: во-первых, все равно откуда-то придется строку
брать (в случае с формулами -- в результате обсчета double'ов).
Во-вторых, при CX_MAX_CHANS=80000 занимаемые объемы памяти будут
офигенными. Преодоление минусов: можно пропускать blklist через свой
препроцессор, а CX_MAX_CHANS при компиляции для cm5307 сбрасывать в
10000 (да и реально за нас все ОС сделает).
А если же все-таки делать
typedef double double_r_t
то БЕЗ эмуляции fpp обойтись не удастся. Плюс, при отдаче клиенту
придется преобразовывать при помощи snprintf()
'а.
В общем, у обоих подходов есть и плюсы, и вполне преодолимые минусы.
'D'
, и,
соответственно, коды CXC_{GET,?SET?}V{GROUP,SET}_PROPS
.
Коды CXC_SETV{GROUP,SET}_PROPS
могут понадобиться для
оперативного изменения калибровок.
Структура форка:
typedef struct { uint32 OpCode; // Command code uint32 ByteSize; // Total size of fork in bytes int32 ChanId; // First channel ID int32 Count; int32 _padding_[4]; uint8 data[0]; } CxDbasFork;
На каждый канал понадобятся поля наподобие:
Как их распихать -- вопрос отдельный, но несложный. Видимо, самое разумное так:asciiz r; asciiz d; int32 q;
// For groups int32 q[Count]; ascii r_and_d[]; // List of "r_repr\0d_repr\0" pairs // For sets int32 n[Count]; int32 q[Count]; ascii r_and_d[]; // List of "r_repr\0d_repr\0" pairs
В запросах, естественно, присутствуют ТОЛЬКО номера -- никаких r,d,q.
На строковые представления d и r ставим такие сокращения: r_repr=="\0"=>"1.0", d_repr=="\0"=>"0.0". Это автоматически обеспечивает разумные значения по умолчанию.
Где, естественно, вint cx_initdbscan (int cd); int cx_getnextdbitem(int cd, chanaddr_t *chan_r, char *r_r, char *d_r, int32 *q_r);
r_r
и d_r
передаются
указатели на буферы не менее, например, 20 байт размером.
Плюсы и минусы этих подходов: синхронный -- прост, и не требует никаких хитрых функций-итераторов для получения данных. Зато нотификационный -- позволяет в принудительном порядке отсылать программе калибровки. (С другой стороны, может, лучше программе при получении от сервера уведомления "калибровки изменились" просто заново послать запрос "гони калибровки" для интресующих каналов?)
22.06.2005: да, и еще: если в CXv4 будет возможность указывать драйверу параметры измерения канала (D,T для Липа & Co.), то ведь и q будет меняться соответственно, так? Посему надо будет
07.01.2007: (в продолжение от 22-06-2005) вообще-то physprops в server's DB -- r, d и q -- могут уставляться драйверами -- для этого нужен какой-то API. И для удаленных драйверов также нужнО.
И имена групп каналов также могут просто браться из DriverRec'а.
20.01.2007: так, для заметки: возникает вопрос -- а как именно ссылаться на n-й канал некоего блока (например, CANADC40), если ему НЕ дано имя собственное?
В TINE (точнее, в его CDI -- см. у Фила Дюваля на PCaPAC'2006) используется синтаксис вида /base/name/#nnn, где "nnn" -- это просто номер канала. И даже поддерживаются диапазоны, в виде #1-#100, списки через ",", и их хитрые комбинации -- типа "#1,#3-#10,#99".
А нам -- можно в аналогичных целях использовать синтаксис "массивов". Т.е., например, если у CANADC40 описан сегмент "values" (40 каналов), то можно адресоваться к каналам как adc_blk_name.values[n].
Замечание 1: и при желании диапазоны тоже легко делаются, внутри скобок-то -- хоть через "-", как в TINE, хоть через "...", как в gcc; да и списки тоже.
Замечание 2: это хорошо кореллирует с тем, что в формулах локальные переменные также могут объявляться массивами, с адресацией через те же [].
21.01.2007: насчет того, как транспортировать данные такой хитрой структуры по сети: еще позавчера Паша Селиванов рассказал, что он в своем CAN Gateway использует "протокол" (точнее, "способ кодирования") ASN -- Abstract Syntax Notation.
Посмотрел в в Wiki -- да, действительно интересная вещь. Ее плюсы -- это то, что она стандартизована (в стандартах X.***), и даже распознается Ethereal'ом. И Паша-то использует еще и потому, что есть прямо готовая библиотека.
21.01.2007: и еще, просто "для протокола": разбирательство еще пару недель назад со всякими TINE & Co. вскрыло, что, оказывается, они там та Plug-and-Play считают не PnP в "обычном" понимании (изменение состава сущностей в рамках одного сервера/IOC'а "на лету"), а возможность на живой системе пристукнуть некий сервер/IOC с некоторыми именованными каналами, и запустить его позже хоть на другом хосте -- и клиенты при этом правильно переадресуются. Естественно, в этом смысле и EPICS -- тоже PnP.
16.08.2004: насчет поведения: вводим поле
"behaviour
", а в нем -- флаги
IS_ALARM
-- бибикать при !=0;
IS_BUTTON
-- срезать OTHEROP;
IS_LIGHT
-- подсвечиваться.
Нынешний LOGD_ALARM -- это IS_ALARM+IS_LIGHT.
Возникает вопрос: а как насчет иметь кнопки, которые могут и disable'иться, и краснеть (как гусиные для концевиков)? Видимо, надо иметь (формализованным!) примерно такой протокол:
18.08.2004: кстати, а как насчет ввести дополнительный цвет -- как гусино-epics'овый "белый" -- что данные еще не пришли. Частично это смахивает на нынешний "цвет гусиной кожи" -- как бы этак их скоррелировать?
20.08.2004: ага... а не ввести ли сразу флаг
CDR_FLAG_LIT
, для сигнализации включенных "лампочек"?
И вообще: давно пора 16.06.2006:
ага, хрен, а не битовое поле! С битовым полем пришлось бы для "общего
статуса" делать хитрые сравнения этих битовых полей на тему "кто
больше/серезнее", так что идея "битоых полей" давно похоронена.
CDR_FLAG_ALARM_*
перевести с
битовых флагов на битовое поле -- они ж все равно могут быть только по
очереди, вот и сделаем {NONE=0,RELAX=1,YELLOW=2,RED=3}. Тем самым и
проверки упрощаем, и экономим один бит (а если понадобится больше
состояний -- добавим еще один бит).
22.08.2004: ввел поле
knobinfo_t.behaviour
и флаги
KNOB_B_IS_{BUTTON,LIGHT,ALARM}
.
И ALARM-виджет даже стал пользоваться этим флагом взамен былых
махинаций с XmNindicatorType
и передачей
DoCreate()
'у параметра is_circle.
23.08.2004: запустил в эксплуатацию поддержку
флага KNOB_B_IS_BUTTON
. Он выставляется обоими
виджет-кнопками, и проверяется в CdrProcessKnobs()
.
23.08.2004: в
CdrCvtLogchannets2Knobs()
вставлено "предвосхитительное"
заполнение поля behaviour
по виду из look
.
Выкинуто так и не ставшее используемым поле
knobinfo_t.is_alarm
(предшественника
KNOB_B_IS_BUTTON
) и его инициализацию в
CdrCvtLogchannets2Knobs()
.
Надо поразмыслить, как все-таки отображать в [CDR_]rflags/cx-starter'е то, что в нормальных программах показывается красными LOGD_ALARM'ами? Надо ведь, чтобы все же состояние ALARM не путалось с YELLOW/RED.
22.10.2004: давно назревшие мысли:
Как отображать, как отображать... Понятно, как -- подсвечивать КНОПКУ подсистемы -- т.е., делать ее при надобности красной/зеленой.
Кстати: реально ведь
CDR_FLAG_RELAX
сейчас НИКАК не связан с RED и YELLOW. А
реально он связан с пока отсутствующим
CDR_FLAG_ALARM_ALARM
, каковой еще надлежит ввести.
В общем -- надо делить на группы ДВА вида alarm'ов -- бибиканье (none/ALARM/RELAX) и оцветнение (none/YELLOW/RED). Получается, что на обоих отведем по 2 бита. Так что либо останется зарезервированная комбинация, либо (по крайней мере, пока, простоты ради), сделаем все просто битами, а не группами. 16.06.2006: и надо НАВСЕГДА оставить именно битами -- а то получение "кумулятивного" статуса просто путем сложения OR'ом статусов разных knob'ов работать не сможет. Сие было понято еще давным-давно -- кабы не в момент comissioning'а cx-starter'а
Часом позже: ввел CDR_FLAG_ALARM_ALARM
, сделав его
самым старшим, остальные сдвинул вниз. Скорректировал и
CdrStrrflag*()
. Еще чуть позже вообще все перетряхнул --
теперь есть группа "ALARM" -- ALARM_ALARM и ALARM_RELAX, и группа
"COLOR" -- COLOR_RED и COLOR_YELLOW, плюс соответствующие маски.
Введено выставление этого всего в Cdr.c, и проверка в
cx-starter.c. Все работает -- кнопка краснеет.
А вот "зеленение" (relax) не работает -- ибо зеленение вызывается только самим виджетом...
Вывод: пора переходить на "полное разделение внутренней логики и отображения" -- т.е., решение об озеленении принимать в Cdr, а Knobs пусть отвечает только за включение/выключение бибиканья/моргания.
23.10.2004: вау!!! Да все же очевидно и просто!!!
А с какой вообще стати бибиканьем занимается Knobs? И логикой зеленения? Перекинем на Cdr :
- У нее и предыдущее значение есть, для сравнения.
- И доступ к
->ShowAlarm()
.- И тогда это будет и с любыми виджетами работать, у которых B_IS_ALARM.
Потенциальный минус: а как бибикать тем, кто не-Cdr?
24.10.2004: итак, приступаем к реализации сего начинания.
Первым делом перетряхиваем порядок операций в
CdrProcessKnobs()
-- чтобы "спецоперации" проделывались
сразу после получения значения от cda, а не после махинаций с
KNOB_B_IS_{BUTTON,ALARM}
. (Старая схема вообще в
принципе некорректна!!!)
25.10.2004: итак -- в Cdr определение флагов сделано, уже без оглядки на colstate -- полностью самостоятельно. Осталось теперь 1) перевести определение цвета на флаги; 2) перевести на это Knobs.
26.10.2004: что ж, доделано.
Во-первых, ChooseColorState()
переведена на флаги.
Теперь она выглядит так: первым делом идет проверка наиболее
низкоприоритетной вещи -- подсказки attn_colstate, а потом проверяется
некоторое количество флагов, по мере возрастания приоритетности.
Естественно, из Knobs_alarmonoffled_widget.c выкинуты
вызовы SetAlarm()
-- теперь это все делается в Cdr.
Во-вторых, удален из Knobs весь сегмент, относящийся к цветовым состояниям. (Естественно, выбор цвета по состоянию -- оставлен :-)
При этом вылезла одна "мелочь": часть выкинутости
(SetOtherop()
и связанное с ним) использовалась в
SetControlValue()
-- для сброса "оранжевости" при
изменении значения пользователем. Сейчас это заменено на просто
обнуление поля ki->attn_time
. В принципе -- не очень
красиво (нарушает инкапсуляцию), но единственная реальное неприятное
последствие/отличие -- что теперь с поля снимается оранжевость не
сразу, а по следующему приходу данных, когда произведется следующий
выбор цветности.
Если придумается, как "красиво" дергать "parent'а" в такой ситуации -- надо будет сделать.
Итого, что у нас теперь осталось
перекрывающегося/недоразделенного между Cdr и Knobs: из
существенного -- только SetControlValue()
. Возможно, в
этом есть своя глубокая сермяжная правда -- ведь библиотеки-то
предназначены для работы как в паре, так и по отдельности. Так что
каждая должна содержать этот интеллект. Так что придется так и вести
эту пару почти идентичных функций, поддерживая их
скоординированность/идентичность. По крайней мере, пока не вылезет еще
какой-нибудь не-Cdr Knobs-клиент.
Плюс, есть некрасивость в том, что в
Knobs_internals.c::ChooseKnobColors()
приходится
иметь дело с константами LOGC_*
, ради чего пришлось
вставить абсолютно там чужеродный #include"cxdata.h"
.
Сегодня же вечером -- обнаружил некоторый глюк, появившийся вследствие этого перехода, устранил его. Детали см. в разделе ChooseStateRflags().
В общем -- глобальная задача устранения переплетенности Cdr и Knobs практически выполнена.
01.11.2004: кстати, насчет "как красиво дергать...":
а почему, собственно, операция записи не проходит
через Cdr?! Казалось бы -- Knobs должно вызывать
->SetPhysValue()
, а оттуда (оно в Chl) -- надлежит дергать
что-то из Cdr.
Но вот -- хрен! В Cdr'овском SetControlValue()
в самом вызывается
->SetPhysValue()
, а функции, которая бы производила
cda_{setphyschanval,execformula}() -- нету! А ведь если б была -- то могла
бы сама и делать этот SetOtherop(,0)
.
Итого -- делаем такую функцию, CdrSetKnobValue()
, и
перекладываем на нее "мозги" из
Chl_gui.c::E_SetPhysValue_m()
.
Несколькими часами позже: приступил к реализации. Сделал функцию
int CdrSetKnobValue(Knob k, double v, cda_localreginfo_t *localreginfo)
Она и выполняет надлежащие действия, в частности -- сбрасывает флаг OTHEROP.
Соответственно, E_SetPhysValue_m()
переведена на нее.
Опять же, чует мое сердце -- надо б все это протестировать!!!
02.11.2004: проверил -- вроде такая архитектура работает, оранжевость пропадает мгновенно. Но надо тестировать еще.
04.11.2004: Хрю: похоже, почему-то (в magsys'е, на LOGD_LIGHT, "LensB 1 (2-5)") у "посинелости" приоритет перед краснотой.
Начал разбираться -- нет, фишка была в другом. В том, что в одной программе (cx-starter) помнилось ОЧЕНЬ СТАРОЕ значение, и делалось сравнение с ним, а в другой -- magsys -- этого не было.
Или все же не так? Эх, как бы это проверить!!!
07.11.2004: да, убедился -- действительно почему-то у синего приоритет...
08.11.2004: елы-палы, так ведь именно таким и
должно быть поведение! Это определяется порядком проверок в
ChooseColorState()
. Должно ли так быть? Вопрос... Теперь
припоминаю -- когда я вводил понятие "DEFUNCT", в
curcx/src/chl/chl_internals.c, то СПЕЦИАЛЬНО поставил эту проверку
последней. Обоснование -- какой смысл показывать какие-то красные/желтые
диапазоны, если значение уже устарело и, возможно, не отражает реальности.
24.11.2004: Еще остающаяся
кривость: что принятие решения о колоризации все-таки подразмыто
между функциями ChooseColorState()
и
ChooseStateRflags()
-- например, махинации с
attn_colstate/attn_time есть в обоих.
31.01.2006: вообще-то
"хуже": между ChooseColorState()
и
ChooseStateRflags()
есть взаимозависимость, хоть и
"конечная" -- colstate выбирается на основе rflags, но и часть rflags
выбирается на основании colstate. Это все касается
"временных/временнЫх" флагов/состояний -- которые проходят по классу
"attn".
Вообще-то, по идее ВСЕ махинации должны делаться ТОЛЬКО в
ChooseStateRflags()
(она и вызывается всегда раньше), а
ChooseColorState()
-- пусть лишь выбирает цвет на основе
этих флагов.
Замутненность сейчас лишь в том, что и в SetAttnState()
этот ChooseColorState()
также вызывается, и та
предполагает, что он что-то помахинирует с attn_time
.
А реально-то НАДО -- чтобы перед ним вызывался
ChooseStateRflags()
, каковой и произведет все проверки с
временем.
Хрю-хрю!!! Ну и -- когда исправлять будем?!
02.02.2006: попробовал разобраться -- ой-йо... Там так все поназапутано! И вызовы *attn*, и то, что wasjustset должен трогаться именно в определенный момент...
Может -- ну его пока нафиг, а? Вот в CXv4 реализуем пристойно...
03.02.2006: ночью посидел/подумал -- вообще-то можно/стоит сделать именно сейчас, просто надо, чтобы:
attn_time
сбрасывался ТОЛЬКО
CdrProcessKnobs()
(а сейчас туда суют свой нос и
ChooseColorState()
, и ChooseStateRflags()
--
что совсем не их дело!);
SetAttnState()
при onoff==0
.
ChooseStateRflags()
НЕ должно само вызывать (НЕ ЕЕ ЭТО
ДЕЛО!):
SetOtherop()
-- флаг OTHEROP все равно меняется
только по приходу данных либо по уставке значения от пользователя,
каковые точки четко локализованы в коде,
SetAlarm()
-- реально момент, когда параметр
new_v!=ki->curv
есть в одном месте, также по получению
значения
Т.е. ChooseColorState()
и
ChooseStateRflags()
-- пусть будут "чисто
математическими", ВЫЧИСЛЯЮЩИМИ, а не "меняющими состояние".
SetAttnState()
же также пусть ТОЛЬКО УСТАВЛЯЕТ
параметры ki->attn_*
, но НЕ вызывает
SetColState()
.
ChooseStateRflags()
, ChooseColState()
,
SetColState()
-- нехай исполняют те точки ("наивысшие" в
цепочке/стеке), откуда произведен вызов, потенциально меняющий
состояние. Так взаимозависимость/потенциальная "зацикленность" и
устраняются.
03.02.2006: начал реализовывать вышеуказанный
проект. На сегодня успел исполнить только первый пункт -- про
attn_time
, а то уж сильно спать охота :-)
06.02.2006: продолжаем пьянку.
А зачем, собственно, у нас есть
attn_time
+attn_period
? Ведь достаточно-то
иметь просто время, когда attn-срок ЗАКОНЧИТСЯ, ибо момент начала --
никого не интересует. Тогда экономится и лишнее сложение, и лишнее
поле в каждом knob'е. Вообще-то, это рудимент старой системы -- когда
был не "attn", а "relax", а потом еще и происходил переход на
relax+otherop. Потому:
knobinfo_t
:
attn_time
переименовано в attn_endtime
, а
attn_period
удалено вовсе.
SetControlValue()
это
отражено.
По делу:
ChooseStateRflags()
оставлено только выяснение и
уставление текущего состояния флажка CDR_FLAG_ALARM_ALARM
,
а определение изменения его значения и соответствующий вызов
SetAlarm()
переложены на CdrProcessKnobs()
.
SetOtherop()
.
И, поскольку есть ЕЩЕ точки, где меняется (точнее -- сбрасывается)
состояние этого флага -- SetControlValue()
и
CdrSetKnobValue()
то и туда вставлено
SetColstate(,ChooseColorState(...),...)
.
Как бы то ни было -- вроде бы работает. А почему точек две -- тема для отдельного разбирательства (и понимательства, как именно сейчас весь этот механизм работает).
07.02.2006: причесал "остатки" -- неиспользуемые параметры и переменные в функциях (поскольку все стало красивее, и потребность в "лишних сущностях" подуменьшилась).
Есть такие ситуации, когда в config-файле указываются некие сущности (драйверы в сервере, подсистемы в cx-starter'е), которые, в свою очередь, ссылаются на другие сущности (layer'ы и серверы, соответственно).
И при "первом использовании" эти "косвенные сущности" надо инициализировать. А при инициализации им нехило бы передавать некие конфигурационные параметры -- какой-нибудь baud_rate для CAN-layer'а, ключики (-w0) для cx-сервера, и т.д.
НО: сейчас-то config-файлы содержат только список и параметры "первичных сущностей", а при них указывать свойства для вторичных -- некорректно! (Некорректно потому, что ссылающихся на "косвенные" может быть много, и кто из них окажется тем, из-за кого layer/сервер будет "инициализирован" -- заранее непонятно. Не дублировать же во всех...)
Так что, проблему надо как-то решать...
27.10.2004: Напрашиваются два варианта:
.layerinfo LAYER_NAME parameters...
(в blklist.lst), и
.srvparams linac1:5 -wN
(в cx-starter.conf).
Первый вариант выглядит более "красивым" с академической точки зрения, а второй -- намного легче реализовать. И опять же -- УДОБНЕЕ иметь ОДИН config, а не пару. И с академической точки зрения тоже :-).
Кроме того, похоже, в обоих случаях стОит вычитывать эту информацию во внутреннюю таблицу/БД, к которой потом и обращаться при необходимости. А не лазить в файл.
Так что -- требуется простейшая БД, которая по ключевой строке
(can_lyr, linac1:5), возможно, case-insensitive, отдает в ответ
config-строку. (А уж что мы с этой строкой делать будем -- наше дело;
хоть через psp_parse()
пропустим...)
04.11.2004: угу, сегодня вылезло: некоторые сервера (magsys, мать его...) надо ОБЯЗАТЕЛЬНО запускать БЕЗ ключика "-wN". А сейчас, из-за того, что некоторым он, наоборот, ОБЯЗАТЕЛЬНО НУЖЕН, он ставится всем... И в результате -- синеет magsys по-страшному...
Кстати: ведь, учитывая, что cx-starter принимает НЕСКОЛЬКО config-файлов, можно все параметры серверов свалить в ОДИН, отдельный файл, и давать его всем starter'ам. А уж это будет то же самое, что вариант (1).
05.11.2004: файл-то для linac'ов уже сделал,
теперь надо будет вставить поддержку парсенья директив
".srvparams", плюс подсовывание этого файла в
cstart
'е.
04.12.2004: где там я файл для linac'ов тогда сделал -- великий вопрос, не нашел я его. Сегодня -- сделал-таки, называется он srvparams.conf.
Насчет собственно поддержки оного: теперь понимаются директивы вида
.srvparams SERVER:N PSP-SPECТ.е., собственно параметры сервера указываются psp-строкой. Возможные параметры включают собственно "params=" (служит для указания "-wN", каковой по умолчанию теперь НЕ делается), "start=" и "stop=" (если указаны, то используются вместо умолчательных -- нужно, например, для управления серверами в крейт-контроллерах на PPC).
Кроме того, в cstart
вставлено, что оно пытается
подхватывать этот файл, если он есть.
Еще вопрос -- как называть-то библиотеку будем? :-)
- Это должен быть массив на N элементов, где N задается драйвером конкретного устройства при инициализации (у ЦАП -- один размер, а АЦП -- другой, и т.д.), а размер элемента -- стандартный, определяется максимальным размером kozak-can-пакета.
- Операцию пере-отправки пакета при истечении таймаута выполняет сама очередь (точнее -- layer), без помощи драйвера.
- Также нужна операция "пробуй отправить следующий пакет" -- это когда на что-то пришел ответ.
- Должна быть операция "проверить, есть ли такой-то пакет в очереди" (проверка выполняется указываемой функцией, ибо просто побайтного сравнения может быть мало). Используется, когда приходит повторный запрос на некую махинацию.
- Плюс, операция "удалить такой-то пакет из очереди" -- это когда приходит адекватный ответ, и пакет запроса становится ненужным.
- Похоже, две предыдущих вещи можно унифицировать -- сделать итератором, которому указывается, что именно делать с пакетом, если он найден: то ли вернуть "да, нашел!", то ли грохнуть пакет и искать дальше.
- Требуется также операция "очистить очередь" -- при получении от устройства пакета "Я -- Вася Пупкин!". При этом, естественно, надо сразу же заказывать чтение всех каналов, т.о., очередь тут же и заполнится.
- Все это вполне может быть layer'онезависимым -- некая доп. библиотечка, общая для всех kozak-can-layer'ов.
В принципе -- вот и готовое ТЗ, можно реализовывать.
12.12.2004: Функции
makcankoz_q_NNN()
. NNN -- enqueue, sendnext, foreach,
clear.
13.12.2004: немного поразмыслив, понял, что надо вместо простой "очереди" делать кольцевой буфер. Начал делать именно в такой модификации.
17.12.2004: итак, после исправления нескольких
багов весь этот механизм работает. Хитрее всего организована пара поиск --
marcankoz_q_foreach()
и добавление пакета в очередь --
marcankoz_q_enqueue()
.
action
, константы
QFE_NNN
):
QFE_FIND_FIRST
-- искать первый попавшийся;
QFE_FIND_LAST
-- искать последний (после чего
можно определить, первый ли он в очереди, т.е., был ли уже послан этот
пакет устройству);
QFE_ERASE_FIRST
-- удалить первый попавшийся;
QFE_ERASE_ALL
-- удалить все такие.
QFE_NOTFOUND
-- нэма.
QFE_FOUND
-- найдено, но не первым.
QFE_ISFIRST
-- найденный пакет -- первый в
очереди.
how
) --
он может быть добавлен безусловно (QFE_ALWAYS
), если нету
(QFE_IF_ABSENT
), или если либо нету, либо первый
(QFE_IF_NONEORFIRST
).
Это нужно для "умных" посылок, когда после отправки пакета на запись надо тут же выполнить тестовое чтение. И если пакет на такое чтение уже имеется в очереди -- то второй посылать незачем; но если имеющийся пакет стоит первым -- то он уже наверняка ушел устройству, и ответ будет содержать устаревшие данные, так что еще раз послать все же надо.
Именно ради таких "умностей" и были сделаны навернутые варианты поиска.
23.02.2005: еще тогда, в декабре, я начал вводить
унифицированный механизм работы с входным и выходным регистрами --
объект ioregsinfo_t
и функции ioregs_NNN()
.
Этот механизм призван заменить предыдущую, примитивную и недостаточно
общую реализацию, имевшуюся в candac16 --
canadc16_request_{read,everything}()
плюс
kozak_process_regvals()
-- она не поддерживала запись.
Так вот -- механизм ioregs*()
был начат, но СОВСЕМ
недоделан -- оно даже не компилировалось. И почему я не делал записей
сюда в файл?!
20.04.2005: а сегодня в canadc40_drv.c напросилась еще одна фича: пометка в элементе очереди "удалить сразу после отправления". Это нужно для команды WRITEOUTREG -- на нее ответа все равно не приходит, и определить дошла ли она никак нельзя (так что и в очереди ей оставаться незачем), а сразу за ней ВСЕГДА все равно идет READREGS, на которую и прилетит ответ.
Флажок назван "oneshot
" -- "одноразовый".
От греха подальше, чтобы не пришлось уставлять либо сбрасывать этот
флаг во ВСЕХ местах использования marcankoz_q_enqueue()
,
пришлось сделать "двойной интерфейс с мультиплексором": введена еще
одна функция -- marcankoz_q_enq_ons()
, а реально они обе
вызывают мультиплексор _marcankoz_q_enqueue_s()
, коему
"oneshot" передается параметром. Таким образом, значение поля
"одноразово", переданное "клиентом", в расчет не принимается.
06.08.2006: Ё-ё-ё!!! Это мало того, что давным-давно было проверено и заработало, так уже и успело "уйти на пенсию" -- было заменено на 2-ю версию CAN-драйвера. (Хотя да, тогда -- зимой 2004/2005 -- это все для меня было какой-то неподъемнейшей проблемой, с которой я возился очень долго, отвлекаясь/бросая ее на несколько месяцев.)
"done" :-)
canqelem_t
будет !=0, то уставлять ЭТОТ таймаут вместо
стандартных 100мс.
sea_step_t.chkdelay
в
seqexecauto).
Например, можно ее указывать отрицательным значением поля "req-specific timeout".
Эти мыслишки появились после обсуждения с Гусевым устройства ЕГО варианта работы с CAN-bus'ом (ключевое слово -- "транзакция").
Собственно, для тех блоков, что сейчас есть, вышеописанная функциональность просто не нужна. Но в будущем может и понадобиться.
20.12.2004: произошло сие во время разговора с Гусевым, около 11:30, так, между делом.
Речь зашла о разных alarm'ах, как их надлежит отображать. Гусев выступал в том смысле, что после исчезновения alarm'а им надо еще несколько секунд светить -- чтобы оператор понял, что же бибикало. В ответ было сказано, что у меня и так соответствующая лампочка подсвечивается зеленым. Женя же стал возражать на тему о нескольких "уровнях" (severity) alarm'ов. Вот тут-то и вылезла идея:
После изчезновения alarm'а надо показывать его ТЕНЬ.
Причем для каждого состояния alarm'а тень -- СВОЯ.
(Корректнее было бы назвать это не "тень", а "след" (trace) -- то, что остается после того, как сам объект исчез. Но, похоже, я обчитался «Хроник Амбера» :-)
И -- тени бывают не только у alarm'ов, это более общее явление. Например -- у графиков/гистограмм, когда в дополнение к текущей картинке более бледно показывается еще и предыдущая.
20.12.2004: итак, два вывода:
CxConnection
в cxlib,
serverinfo_t
и chaninfo_t
в cda, косвенные
сущности), то:
Следует отводить массив в динамической памяти, можно с неким начальным количеством элементов, а можно и с 0; когда требуется добавить элемент, а свободных нет, то увеличивать массив, причем не на 1, а в общем случае на некий инкремент, чтобы на каждое добавление не требовались realloc'и.
Таким образом, на поддержание набора этих записей требуется примерно следующее:
NNN_t *NNNs; int NNNs_allocd; int NNNs_used; // Опционально enum {NNN_INCREMENT=10};
И уж точно НИ В КОЕМ СЛУЧАЕ нельзя иметь статический массив заранее
определенного размера, как это сейчас сделано в cxlib
(connections[]
) -- в одних случаях массив будет слишком
велик, так что много места пропадет впустую, а в других он окажется
слишком мал.
Так что -- по-хорошему надо б перевести cxlib (не говоря уж о cx-starter'е) и, возможно, cx-server_data, на такой механизм.
Для реализации подобного кольцевого буфера требуется следующее:
NNN_t *NNNring; int NNNring_size; int NNNring_start; // Oldest item int NNNring_used; // Number of items currently in ring
Удаляется элемент в точке start, при этом делается start:=(start+1)%size, а добавляется элемент в точку (start+used)%size.
Возможно, стоит эти вещи как-нибудь формализовать -- как это было
много лет назад сделано с другой концепцией, нашедшей отражение в
функции GrowBuf()
. И, близкая тема -- так же был сделан
менеджмент fd_set
'ов --
GENERIC_FD_{CLEAR,ADD,REMOVE}
.
07.02.2005: совсем забыл тогда про деталь
реализации случая (1) -- с "набором заранее неизвестного количества
записей": ведь записи могут не только добавляться, но и удаляться, и
тогда набор станет "дырчатым". (Это вылезло сегодня при реализации
"массива" bigcinfo
.)
В такой ситуации менеджмент начинает слегка напоминать оный у
fd_set
'ов -- надо всегда помнить количество реально
"живых" записей, и увеличивать/уменьшать его при добавлении/удалении
(для оптимизации циклов "использования"). Неиспользованные ("убитые")
записи помечаются неким образом (например, в вышеуказанноим случае --
.bigc_n=-1
); так же сразу помечаются и свежерожденные
записи после увеличения буфера. При создании нового экземпляра вначале
ведется поиск свободной позиции среди всех NNNs_allocd, а уж если не
найдено -- то буфер увеличивается и занимается первая из добавленных
ячеек.
07.02.2012@Снежинск-каземат-11: да, описанная выше 20-12-2004 модель с {s,s_allocd,s_used,_INCREMENT} была массово использована в 4cx/, например, как раз для менеджмента таблицы соединений. Только на оптимизацию касательно "дырчатости" (07-02-2005) повсеместно забито.
Реализация -- обычно пара функций
А затем и в других местах:
Проверить бы по архивам, не отсюда ль всё пошло...
Короче -- практически готовый код для помещения в misc_macros.h
рядышком с GetZZZSlot()
/RlsZZZSlot()
, плюс сервисная
(для Get()'а) FindFreeZZZSlot()
(ключевое слово -- "slot"). В
роли "ZZZ":
V4conn
-- cxsd_fe_cx.c, cx_client.c (разные стороны
CX-соединения).
Srvconn
-- cda_api.c.
Cmd
).
watched
(хоть и без формализации функций, но 02-08-2011 поиск
был вытащен в find_free_watched_slot()
).
Cblist
) оно даже было
25-04-2011 доведено до уровня макрос-библиотеки
DEFINE_SLOT_FUNCS()
, генерящей
GetCblistSlot()
/RlsCblistSlot()
. Причём там
весьма продвинуто -- и "storage class" указывается (auto/static), и
возможность манипуляции не только со статическими переменными, но и с полями
структуры (указывается через троицу параметров obj_pfx, obj_ref,
obj_arg...).
GENERIC_FD_*()
.
fastadc_knobplugin.c
-- полная копия, только без
макроса.
08.06.2005: исходная предпосылка: размер
структуры в принципе немаленький -- >=100 байт, а если
FD_SETSIZE==1024
, то получается, что 100Кб тратятся
практически "за просто так".
Проект решения: индексация массива по fd должна быть не прямой, а с дополнительным уровнем косвенности.
Т.е. -- заводится один массив int
'ов --
clnidx[FD_SETSIZE]
, и -- ПУЛ структур, растущий по мере
надобности при помощи GrowBuf()
.
Overhead будет минимальный: в начале каждой функции вместо
cp = clients + fd;
будет
cp = clients + clnidx[fd];
-- можно эту махинацию даже выносить в макрос наподобие "fd2cp(fd)".
Плюс, естественно, механизм выделения ячейки -- отрабатывающий только один раз, при "заведении" клиента. Либо находится свободная ячейка, либо пул увеличивается.
Дополнительные мысли:
clnidx[]
, то
можно и этот массив сделать динамическим -- в конце концов, чаще всего
дескрипторы лежат даже ниже 100, так что можно при добавлении клиента
устраивать так, чтобы массив становился не меньше fd
, и
при его росте для порядку забивать новорожденные позиции
-1
.
cp
-- как это сейчас делается с
si
в cda_register_formula()
.
handle_input()
не заниматься
тупым перебором для нахождения соответствия fd->cd.
10.06.2005: Хи-хи! В свете портабельности под
Форточки такая разреженность просто неизбежна -- см. раздел "Win32 и fd_set
" за сегодня. Плюс
более старое -- "Win32 и fdiolib" за
06.02.2005.
29.03.2006: в свете того, что прямая адресация по fd теперь исключена (в 4cx/ handle передается callback'у cxscheduler'ом), то и использование вышеописанного механизма в fdiolib излишне.
А вот в cxlib'е и cxscheduler'е таковой "лишний уровень
косвенности", возможно, придется использовать. Хотя в Win32
clnidx[]
будет все равно не при делах.
11.05.2007: да, clnidx[]
-то будет
излишним, поскольку вместо fd надо ВСЕГДА (в т.ч. и в fdiolib'е)
использовать именно "handle" -- индекс объекта во внутреннем массиве.
Этот handle будет указываться в качестве private pointer при
регистрации у обслуживающих уровней (для fdiolib'а -- это cxscheduler)
и передаваться callback'ам для быстрого доступа.
А вот просто лишний уровень косвенности -- handle вместо fd напрямую -- естественно, будет необходим.
sl_timeout_t
).
07.02.2012@Снежинск-каземат-11: состояние дел и понимания на текущий момент:
И в качестве указателей вперёд/назад при организации "списков callback'ов" тоже использовались бы такие дуплеты.
Тогда б ввели пару типов:
cb_array_t
-- растущий массив. Содержит текущий
указатель на realloc'ируемый массив и размер элемента в нём.
cb_array_ptr_t
-- указатель-индекс:
{cb_array_t *a, int idx}
.
(Вот зря я ТОГДА, в ноябре-декабре 2011, когда это придумал, сразу не записал!!! Попозже, ближе к вечеру: нифига -- частично записал, в 201112-SNEZHINSK-ACTIVITY.txt, "Заметки...", 20-12-2011, вечер-душ.)
Но -- увы, облом...
Задача выглядит принципиально неразрешимой -- вообще никакой ценой.
07.02.2012@Снежинск-вечер-в-УАЗике-буханке-с-полигона: (нас
забыли, и УАЗ прислали ближе к 8 вечера) сейчас для решения конкретной
потребности ist_xcdac20 можно поступить тупо и просто -- увеличить CX_MAX_DATAREF_EVENTS_PER_DEV
с 10 до
100.
Некрасиво, но зато мгновенное решение, для которого и делать-то ничего не надо.
07.02.2012@Снежинск-Снежинка-305-душ-вечер: придумалось решение. Исходные данные у нас таковы:
Размышления:
Тогда и каждый элемент остаётся на одном месте, и растущий массив есть. Минусом, конечно, изрядная фрагментация из-за отведения толпы мелких структурок. Зато адресация очень проста.
08.02.2012@Снежинск-каземат-11: засим поставленную в заголовке проблему считаем идеологически решенной, так что "done".
08.02.2012@Снежинск-каземат-11: по пунктам:
Это и используется во всех остальных применениях (4cx/ etc.), и идеологически вернее -- и высокоуровневее, да и операция "получить указатель на i-й элемент массива" корректнее, чем "получить индекс элемента из указателя на него и на массив".
И вообще это полезнее -- т.к. совместимо с прочими подвидами "массивов слотов".
GENERIC_SLOTARRAY_DEFINE()
:
Программа же может выбирать, кого из них использовать (фиксированный либо один из растущих, в зависимости от требований (фиксированные либо плавающие элементы)), прямо в момент компиляции, например, в зависимости от target-платформы.
(Естественно, кроме выбора определителя она должна также менять
и определение поля "массив" -- type *arr
либо type
arr[n]
.)
GENERIC_SLOTARRAY_DECLARE()
для разных вариантов будет
одна -- ведь прототипы (API) должны совпадать.
AccessZZZSlot()
,
которая бы отдавала указатель на конкретный слот. FIXED: array+i; GROWING:
array+i; GROWFIXELEM: array[i].
DestroyZZZSlotArray()
, которая в GROWFIXELEM-варианте делала бы
free() и каждому слоту.
А уж задача самой программы сделать СВОЙ RlsZZZSlot() для каждого элемента. А тот, в свою очередь, должен получать УКАЗАТЕЛЬ на элемент от AccessZZZSlot(), и для отсутствующих слотов будет NULL -- ничего не делать.
GENERIC_SLOTARRAY_DECLARE_FIXED()
,
GENERIC_SLOTARRAY_DECLARE_GROWING()
,
GENERIC_SLOTARRAY_DECLARE_GROWFIXELEM()
.
09.02.2012@Снежинск-каземат-11: кстати, эти вещи еще глобальнее, чем первоначально задумывалось: FIXED-вариантом можно заменить менеджмент DevID в нынешнем remsrv_drvmgr.c, а вообще любым -- менеджмент БД в 4cx'ном cxsd_db.c
27.02.2012: а можно еще и сократить время поиска свободного элемента в *FIX*-вариантах: если все свободные помещать в список.
Правда, не всё так тривиально:
По-хорошему, надо вводить 3-й макрос -- для объявления ПОЛЕЙ. Например, GENERIC_SLOTARRAY_DEFINE_NNN_FIELDS(). И получится у нас по факту нечто в стиле C++-template-класса: наборчик полей -- это собственно "объект", а функции -- его методы.
13.05.2012@Снежинск-каземат-11: первоначальная идея в предыдущем пункте -- бессмысленна (так что делаем её withdrawn), а вот замечания-следствия из неё -- однозначно заслуживают реализации.
(Сегодня появлялась еще идейка-"улучшение" предыдущего: кроме "max_used" помнить еще и "first_used". Типа, чтоб обходиться без списка. Но эта идея для сокращения времени поиска свободного элемента бесполезна -- само поддержание "first_used" потребует больше маеты. А вот для ускорения перебора -- выглядит осмысленно; хотя, насколько "часты" ситуации, в которых набор получается с "пустотой" в начале?)
06.01.2013: всё вышеописанное уже реализовано и используется, так что раздел "done".
Хотя насчёт дополнительного списка свободных элементов для быстрого поиска -- да, оно так сделано в новом cxscheduler для таймаутов.
reason
.
21.02.2012: по пунктам:
RemitBigcRequestsOf()
, ReRequestBigcs()
, ...
-- в сервере.
Основные сложности там заключались именно в том, что нижний уровень -- cxsd_bigc.c -- вынужден иметь дело со структурами, предоставленными более высокими уровнями (cxsd_drvmgr/cxsd_driver). И:
Если бы между уровнями передавались только handle'ы -- никаких бы проблем не было.
Кстати, аналогичный прикол и с devstat_evproc'ами -- из-за
clientside-supplied structs там даже деактивацию по DRVS_OFFLINE делает
не CallDevsOnchgCallbacks()
, как надо бы, а
inserver_devs_callback()
.
Отдельный вопрос: а что
в свете этого делать с cxscheduler'овыми таймаутами? 06.01.2013: сделано -- от них избавлено в пользу
sl_tid_t
.
on_stop
{chan,bigc}_cbitem'ах. А при
evproc с параметром reason -- достаточно добавить еще один код причины.
(И ведь было же именно так сделано в cxlib'е с самого начала! И fdiolib такой же.)
16.01.2005: эта "новая версия" вполне может быть и не вполне совместима с текущей, причем эта "революционность" будет не столько вынужденной, сколько "самополучившейся" -- если новые фичи просто вытеснят старые.
На данный момент мы имеем следующее:
Здесь, кстати, за компанию вылезет напрашивающееся "слияние" понятий
type/LOGT_* и kind/LOGK_*. Плюс, "повсеместность" поля
options
.
И, так, между делом -- можно будет иметь просто внеэлементные knob'ы, которые прямо в "корневом списке". (Да и вообще, наверное, исчезнут многие ограничения и появится много разных возможностей.)
В общем -- все эти изменения отлично станут причиной/основой появления "CX version 3". В нынешней архитектуре -- в принципе, все достаточно адекватно, и надо в конце концов на ней защитить кандидатскую. (А v3 отлично потянет на докторскую.)
16.01.2005: пройдясь по списку тех и других, напрашивается следующая таблица (отражающая те комбинации, которые сейчас и являются осмысленными):
LOGx_ | LOGT_ | LOGK_ |
---|---|---|
LOGx_NOP | * | LOGK_NOP |
LOGx_SUBELEM | LOGT_SUBELEM | * |
LOGx_WRITE1 | LOGT_WRITE1 | LOGK_DIRECT |
LOGx_WRITEC | LOGT_WRITE1 | LOGK_CALCED |
LOGx_{READ,DEVN,MINMAX}1 | LOGT_{READ,DEVN,MINMAX}1 | LOGK_DIRECT |
LOGx_{READ,DEVN,MINMAX}C | LOGT_{READ,DEVN,MINMAX}1 | LOGK_CALCED |
(Замечание: в свете вышеприведенной таблицы не столь уж и глупым выглядит древнее, так и не реализованное разделение LOGT_WRITE1/LOGT_WRITEM :-)
(Хочется отметить, что NOP и SUBELEM были отнесены к kind и type довольно произвольно -- оба в принципе могли быть как LOGK_, так и LOGT_: они отличаются тем, что их наличие делает второй копмонент "неважным" -- что собственно и привело к идее слияния этих двух полей.)
И какой напрашивается вывод? А такой: что реально наличие ДВУХ полей -- абсолютно правильно, они так и должны быть: первое (LOGT_) -- "тип", "класс" ручки/инода, а второе (LOGK_) -- "data source", которое имеет смысл только тогда, когда это именно ручка, а не NOP и не SUBELEM. Так что -- здесь все окей.
Вторая часть вывода: "NOP" попало в LOGK_ ошибочно, оно
должно быть именно LOGT_NOP
Третий (опциональный) вывод: можно бы, чтобы уменьшить путаницу, как-нибудь переобозвать концепцию "kind/LOGK_" -- в что-нибудь типа "srctype/LOGS_". Фиг знает... А можно перенять опыт/терминологию биологов -- у них же есть готовые "выстроенные в иерархию" названия: вид, семейство, класс, etc...
Кстати, порывшись в сети, нашел такую иерархию: [империя,] [доминион,] царство (regnum), [раздел,] [тип,] класс (clasis), [когорта,] отряд (ordo), семейство (Familia), [триба,] род (Genus), вид. (В квадратных скобках -- таксоны, которые имеются не во всех классификациях, а только в полной.) В скобках -- латинские названия, а можно бы нарыть английские.
Да, занятненько...
13.03.2006: поскольку (по здравому размышлению
:-) "*_CALCED" и вовсе не нужно (см. мыслю за
10-07-2005), то и вовсе концепция LOGK_/LOGS_ исключается, так что
из 4cx/src/include/Knobs_typesP.h поле
knobs_knob_data_t.srctype
удалено.
elemnet_t
/eleminfo_t
-- в "стандартизованный
инод", каковым будет выступать
logchannet_t
/knobinfo_t
?
А можно ли и саму "подсистему" положить в "стандартизованный инод"? И что насчет такого эфемерного понятия, как "группировка"?
16.01.2005: С группировкой в общем-то все
довольно просто -- ведь группировка является некоторым количеством
последовательных элементов, т.е. -- считай, что в точности те же
данные, что и для "элемента"/"подэлемента" (каковым и будет
подсистема), с отличием, что используется "количество", а не
NULL-terminated. Единственное -- поле fromnewline
, но уж
с ним-то разберемся -- например, можно упхать его в
options
.
17.01.2005: с полем options
надо
поступать так: ВЕЗДЕ (и для ручек, и для элементов, и для подсистем)
парсить его при помощи ОДНОЙ и той же таблицы, а ненужные поля просто
игнорировать.
18.01.2005: ну что -- просто берем и начинаем
смотреть, каких полей элемента и подсистемы не хватает в нынешнем
knobinfo_t
, а что, наоборот, хорошо ложится на имеющееся.
Явно просматривается пара мыслей:
А "корнем" сделать обычный элемент, введя в дополнение к типам MULTICOL и SINGLECOL еще "ROWCOL", который станет раскладывать свои подсодержимые так, как это сейчас делает группировка.
CMD_GETP_BYNAME_I()
.
Так вот -- а не сделать ли все-таки defserver полем элемента, чтобы каждый элемент мог иметь свой сервер по умолчанию, а если это поле -- NULL, то уже тогда наследовать его от "parent'а"?
19.01.2005: Итак, что мы видим -- многие вещи отлично унифицируются, и они ДОЛЖНЫ быть унифицированы (ident, label, options), но некоторые -- не очень-то. Так что у нас три пути:
#define
'ов для alias'енья
таких случаев. На подобном можно огрести проблем.
struct inode
.
Единственный минус -- более длинные ссылки на поля. (Плюс, при
не-БД источнике данных исходный текст определений будет выглядеть еще
кошмарнее. :-)
Так что, можно примерно прикинуть, как будет выглядеть описание
"унифицированного инода":
typedef struct _knob_inode_struct
{
char *ident;
char *label;
char *options;
int type;
union {
knob_inode_data knob;
bigc_inode_data knob;
elem_inode_data knob;
...
} u;
} knob_inode_t;
Что пропустил?
26.01.2005: полез в Cdr.c переделывать
LOGK_NOP
на LOGT_NOP
, и имел прозрение -- как
же, в свете появившихся уже ПОСЛЕ рождения Cdr вложенных элементов там
халтурно многое сделано! :-)
12.04.2005: да, а назвать-то надо -- не CXv3, а CXv4. Ведь протокол уже сейчас v3, а его надо будет на единичку продвинуть. Так лучше сразу унифицировать и сделать версии и системы, и протокола -- v4.
19.04.2005: насчет использовать ли поле
"tip
" в элементах: да, использовать. Привешивать его
tooltip'ом к заголовку элемента.
15.04.2005: Первоначально я рассматривал сие как временное явление (мда, оно так временно года с 1997-го... :-), пока не будет БД, из коей и должны читаться все описания.
Но потом появились Chl-, но не-simple-программы. А уж они НИКАК не должны быть привязаны к БД -- все описания их содержимого должны иметься прямо в них самих.
Так что -- надо будет
15.04.2005: что до alarm'ов -- то с ними пока вообще ничего не понятно.
А про таймеры -- поговорил со Старостенкой, что таймеры делают в LabView, и пришел к выводу, что нам это вообще нафиг не нужно. В LabView-то их смысл в том, что они "дергают" юзерские функции, поскольку там -- все программирование мышью. А здесь -- похоже, им делать просто нечего.
04.12.2005: стало окончательно ясно, что сей раздел в том виде, который обозначен в заголовке -- стоит закрыть. Посему -- "widthdrawn".
А "продолжение" -- в разделе от 01-06-2005 "не потребуются ли нам "невидимые объекты" -- как в LabView таймеры..."
look
плагин, но больше ничего делаться не будет -- этот плагин сам набъет
нужного содержимого.
Такое нужно для всяких не-simple/не-БД-программ, типа impacis10, istcc и прочих phm-*.
09.05.2006: возникает только небольшой "вопрос" (вопросец возник при создании linbpm) -- а как указывать этим LOGT_USER каналы, с которыми им надлежит работать?
Ведь частенько эти LOGT_USER просто берут и отображают данные с НЕСКОЛЬКИХ каналов -- например, из каналов chanX и chanY они рисуют двумерную "картинку" поперечного положения пучка.
Но, опять же -- криво это, неудобно (каково плагину шариться по своим под-knob'ам!), да еще и чревато ошибками (а вдруг не ту структуру вложенных сделаешь -- и ведь фиг разберешься, и Cdr ничем помочь не сможет, не проверит!).
Да, и для возможности проверки -- в описании компонента пусть наличествует указание, сколько каналов ему нужно ([min..max]).
10.05.2006: угу, а только это... а как с формулами? Хотелось ведь бы, чтоб можно было указывать не только обычные каналы, но и формулы. И?
А что, если вообще под-уровнять формулы с каналами? Т.е.:
cda_objhandle_t
(а не excmd_t).
cda_objhandle_t
будет
указывать "вид" -- канал/формула.
free()
формуле.
02.08.2006: да, надо еще в дополнение к собственно значениям не забыть о флагах и тэгах -- они тоже необходимы. (Правильно, минимум/максимум и прочие алармы "поддерживаться" не будут, ибо бессмысленно -- это уж дело компонента, как именно он с данными будет обходиться.)
02.08.2006: @пляж
идейка, как можно было бы прямо сейчас эмулировать LOGT_USER-knob'ы
с указанием им каналов: делать ЭЛЕМЕНТЫ, содержащие некоторое
количество каналов, а оно уж пусть оттуда берет. Бе-е-е, полный
повтор идеи от 09-05-2006 :-)
09.04.2007: а ведь идейка эта оказалась очень полезной:
14.06.2007: в продолжение идеи "под-уровнять формулы с каналами": да, надо и можно, и несложно. Highlights:
И иметь две функции: "getvalue()" и "setvalue()".
И -- да, есть вопрос, кто же и когда будет вызывать именно ИСПОЛНЕНИЕ формул (поскольку если канал можно дергать много раз, просто читая его текущее значение, то ФОРМУЛУ надо исполнить однажды, в момент прихода данных).
CxDataRef_t
.
15.06.2007: ой ли? С введением всяких
хитростей (типа параметров ручек) даже подготовка к исполнению формул
может стать подороже, так что -- имеет смысл для ЧИТАЮЩИХ "объектов"
выяснять, что они есть, чтобы, возможно, простые каналы спрашивать у
cda более простым вызовом...
Неа!!! Все и так очень несложно -- вопрос оптимизации: ведь накладные расходы нас волнуют только при МАССОВОМ вычитывании, так? Т.е. -- при обходе поддеревьев по приходу данных. А там мы можем просто сразу заготовить все "общие" параметры -- в т.ч. массив указателей на "localreginfo", а потом перед каждым вызовом подправлять только специфичное для данного knob'а -- в частности, [1]-й элемент переставлять на массив параметров.
И отдельно насчет "типа-записи CxDataRef_t
": да не
нужно оно!!! Пусть все инкапсулируется в cda за типом
cda_objhandle_t
-- например, отвести специальный код sid'а
(все единицы) на "формулу", и завести отдельный массив "формулы",
индексируемый objofs'ом; а уж там-то и помнить "тип" исполнителя такой
формулы.
BTW -- можно и большие каналы в cda "унифицировать" с обычными, например, выделив один битик на "тип".
18.06.2007: решение-то очень простое и очевидное:
16.04.2005: да, конечно, красиво было бы иметь на web-страничках даже "крутилочки", но -- не стоит. Т.е., сделаем эту вебификацию совершенно отдельно от libKnobs, в некоем специальном клиенте -- например, аналог chlclient. В нем будет собственный обход дерева, и легкая имитация Knobs -- он будет поддерживать стандартные типы контейнеров (ELEM_{SINGLECOL,MULTICOL} и будущий "ROWCOL" -- аналог нынешнего PopulateWorkspace) и несколько стандартных knob'ов, по умолчанию -- TEXT.
Единственное замечание: нынешнее устройство LOGD_SELECTOR действительно КРАЙНЕ НЕУДОБНО для не-libKnobs-использования -- список строк сильно привязан к самому виджету. В EPICS'е с этим намного проще...
И, кстати -- если сделаем какой-нибудь общий способ/интерфейс для перечислений, то, во-первых, он хорошо ляжет на LabView-подобные виджеты, у которых перечисление строк вместо диапазона, а во-вторых -- надо будет как-то учесть интерфейс "параметры каналов" -- как больших (adc333: timing, ranges, ...), так и обычных (Липа: T, D).
22.04.2005: сейчас ограничение на ввод устанавливается по "ужайшему" из указанных диапазонов; это неудобно. Особенно это неприятно стало после появления концепции "внешней колоризации", когда указываемые для пожелтения/покраснения диапазоны не имеют НИКАКОГО отношения к самому значению, а относятся к "колоризующему".
Так что: в CXv4 надо ввести еще один -- даже не 4-й, а 0-й диапазон -- "INPT" или "ALWD".
И есть два варианта: либо (1) ограничивать ввод ТОЛЬКО по нему, плюя на norm и yelw, либо -- (2) если хочешь иметь "типа неограниченный ввод", точнее -- отключить ограничение по norm/yelw, то надо указать какой-нибудь более широкий диапазон.
В общем, правила "наследования" вылазят такие:
НО! Если имеется colformula
, то norm и yelw НЕ
рассматриваются, учитывается только inpt (иначе -- совершенно бредовое,
нерелевантное значение диапазона, "унаследованного" от реально совсем
другого канала).
При этом автоматически будет обеспечиваться наиболее естественное/осмысленное поведение SLIDER'а и DIAL'а -- можно будет не указывать диапазон disp (что есть неестественно), а он автоматически будет браться из самого подходящего диапазона. А можно -- и указать, и при этом он НЕ будет влиять на диапазон разрешенных к вводу значений.
Кстати: а порядок полей, по опыту нынешнего
logchannet_t
, стоит изменить на такой:
step,
defval?,
mininpt,maxinpt,
minnorm,maxnorm,
minyelw,maxyelw,
mindisp,maxdisp
-- в порядке убывания нужности (точнее -- частоты использования).
А defval, возможно, вообще надо убрать -- оно НИКОГДА, НИГДЕ и НИКАК не использовалось. Возможно -- заменить на что-нибудь (см., например, тему «усовершенствования по манипуляциям "текущими" режимами»).
15.03.2006: в 4cx/ уже сделаны ЧЕТЫРЕ диапазона -- ALWD,NORM,YELW,DISP.
PSP_T_LOOKUP
, а в расширябельной архитектуре
такое не пройдет.
23.04.2005: хо-хо! Во-первых, можно было бы
просто перевести его на PSP_T_PLUGIN
, если бы осталась
нынешняя архитектура с числовыми идентификаторами
LOGD_NNN
.
А во-вторых, если перейдем на архитектуру со строковыми
идентификаторами, то тогда проблема исчезнет автоматом -- можно будет
сделать просто тип PSP_T_MSTRING
.
А еще лучше -- сделать-таки типом PSP_T_PLUGIN
, который
будет складировать в нужное поле уже прямо ссылку на соответствующую
VMT. (Это прямо напрашивается -- даже термины совпадают: для
парсинга plugin-архитекруры использовать plugin-парсер :-)
06.02.2006: ха-ха! По первому проекту --
PSP_T_PLUGIN
, добывающий по строке код
LOGD_NNN
-- и сделано еще 23-12-2005..26-12-2005. "done",
однако!
Т.е. -- как-нибудь указывать, что в иерархии внутри этого элемента
надо вызывать не обычный CdrSetKnobValue()
, а нечто из
юзерской программы.
А может, эту идею вообще генерализовать, и иметь "плагины установки значений"? По умолчанию -- Cdr, можно -- свой, а можно -- вообще что-нибудь типа Epics'а...
Точнее, так:
Даже плагин не "установки значений", а "интерфейса реальных данных". И доступ к нему тоже должен вестись через Cdr, стандартными же методами -- благо, там все уже вполне пристойно продумано и сделано.
28.04.2005: О! Точно!!! Уже ведь была идея --
чтобы можно было для каждого элемента указать, что его данные берутся по умолчанию с такого-то сервера -- т.е., собственное поле "defserver", которое если пусто -- то брать от элемента уровнем выше, и так далее.
А что, если обобщить:
Этот "defserver" может содержать не только ссылку на сервер, но еще и "префикс протокола" -- через '/' (что уже используется в tsycam.c), например, "cx/linac1:5", или "epics/linac", или "local/"
Да, и по-хорошему, нужна бы еще одна фича:
Чтобы поддерживались "макросы" -- т.е., чтобы можно было и ТУТ ТОЖЕ указывать не "cx/linac1:5", а "cx/$LINVAC".
Мдя... Есть 2 "замечания/контраргумента":
(Ага, из разумных идей -- остался только '@', да еще можно под регистры занять '#', хотя это и мерзко...)
05.02.2007: есть идея, как просто делать ДВА уровня макрорасширений с использованием ОДНОГО символа префикса:
Символ -- '$', как в shell и make, и, как в make, двойное указание -- "$$" -- дает литеральный $, который просто передается на уровень ниже. Таким образом, просто $NAME будет означать расширение на уровне БД, а $$NAME -- на уровне клиента.
Не самый красивый подход, конечно, но вполне работоспособный -- пока не придумаем что получше.
15.06.2007: да, разумнее всего будет использовать символы/префиксы следующим образом:
(Остается вопрос с двумя уровнями, ибо хочется мочь вбивать ОДНИ И ТЕ ЖЕ тексты и в БД, и в локальные файлы.)
(Определение не по скобкам, а тупо по имени -- зарезервированному слову; нефиг каналам давать названия типа "sin", "log", etc.)
А как это все с точки зрения security? Т.е., имеем в результате тотальную программируемость -- ровно то, что закладывал Microsoft, и за что его сильно критикуют, ибо это у них крайне плохоконтролировабельно. (Да и Java и всякие Symbian в этом смысле немногим лучше...)
В принципе -- тут-то программируемость чисто локальная: т.е., никаких тебе ActiveX, а плагины только те, что предоставляются либо библиотекой, либо программой. В отличие от microsoft'овских продуктов, где все динамически.
А с другой стороны -- ведь остается один-единственный шаг: позволить динамическую загрузку (как уже есть в сервере), и этот барьер будет сломан...
17.05.2006: проблема динамической загрузки того-что-не-прошено может быть смягчена такой мерой: в описании подсистемы добавляем (опциональное) свойство/поле "требующиеся библиотеки knob-плагинов" -- аналогично тому, как в ELF-файлах указывается список требующихся им динамических библиотек. А уж knobber будет пытаться загрузит указанные "библиотеки" из некоей стандартной директории.
И тогда если вдруг нужное не найдено -- не беда, будут использоваться default-плагины. И даже более -- в режиме "high-security" можно динамическую загрузку запрещать.
Как вариант, можно "список для selector'а" сделать каким-нибудь
отдельным стандартным параметром в widdepinfo
(будущем
options
).
Сегодня было сборище по поводу индукционного ускорителя. Там нужна система управления, не-EPICS (эт Гусев меня поразил!), некое простое, отлаженное и вылизанное ядро. И на роль оной вполне хорошо просится будущий CXv4 -- с его элегантной архитектурой с cxsd с простым cxscheduler'ом. Но оно еще когда будет! А можно -- сделать промежуточный вариант, CXv3: те же протоколы и клиентские библиотеки, что сейчас, но -- на основе cxsd.
Вопрос только: а стоит ли? Вот сейчас подумал -- это ведь затраты времени недетские, и ради чего?
12.02.2006: хо, а есть другой вариант: а если правда изобразить "CXv3", взяв старые сервер/cxlib/протокол, но новый UI -- Chl+Knobs+Cdr[+cda]? (хотя cda-то и сейчас вполне адекватен)
Так преспокойненько можно будет изготовить полностью новую clientside-инфраструктуру, как бы не "на пустом месте", а имея готовый backend для тестирования.
Вопрос скорее в Xh -- что делать с ней, в свете GRL?
(О сем поговорить с Кусковым -- надобно обсудить архитектуру и границу: {компоненты}{GRL}, или же {программа}{Knobs}{GRL}, или как?)
30.11.2006: во-о-о!!! А ведь правда можно легко сделать переходную версию "CXv3" -- с новым сервером, но старым протоколом (так проще переходить -- новый-то протокол пока покрыт туманом неясности).
А главное -- как это реализовывать: раз у нас будет вполне отдельный cx-protocol-frontend, то можно в него всунуть ДВА модуля: для протокола v3 (делаем хоть завтра), и (потом) для v4. А он, путем переговоров с клиентом, осознает, какой из модулей общения использовать.
Еще один плюс -- в модуле для "v3" можно сразу поддерживать множественные/смешанные обычные/большие каналы в запросе.
05.06.2007: и другой, "симметричный" вариант -- раз в cda будут "плагины для доступа к данным", точнее -- модули работы с протоколами (CX, EPICS, TINE, ...), то можно сделать и compatibility-реализацию такого клиентского модуля для работы с протоколом "v3".
(А вот поддерживать в одном СЕРВЕРЕ оба протокола --
затруднительно, ибо схема протокола такова, что сервер сразу на
connect()
отвечает пакетом с Res1=htonl(CX_PROTO_VERSION).
Вообще, конечно, надо будет продумать этот вопрос, и как-нибудь погибче реализовать handshake.)
22.07.2007: реализация в ОДНОМ СЕРВЕРЕ обоих протоколов -- возможна: просто надо разнести их по разным портам. Как это уже и сделано в EPICS/CA, где порт равен 5056+MAJOR_PROTOCOL_VERSION*2.
Но воизбежание таких проблем в будущем -- ввести handshake в протоколе CXv4 необходимо.
Вот только старый вопрос остается -- а как же ее назвать? Смысл ее -- это как бы быть "методами классов из Knobs.h" (в отличие от libKnobs, каковая реализует классы "параллельной структуры" -- отображаторов). Эх...
22.07.2005: а ведь собственно --
Правильно бы взглянуть на проблему с другой стороны: это надо ЕДИНИЦЫ АДРЕСАЦИИ -- knobs -- переименовывать, возможно, обратно в chainifo, тогда проблема и исчезнет. Т.е., если переименовать Knobs_typesP.h в "ChanInfoP.h", и библиотеку тогда обозвать типа "libChanStructs"...
16.01.2006: (записано на диванчике в комнатенке Лесосечная-8-179, во время ремонта; несколько повторяет побуждения за 17-05-2005):
Все махинации с Cdr, кроме конвертации (и Destroy) и, возможно,
processing'а (и уж точно -- Save/Load/etc.) -- т.е., статусные,
вроде SetControlValue(), ChooseColorState(), StrColalarm*(), -- надо
вынести в отдельную библиотеку -- типа "knobs type manipulation
library". Вопрос -- ИМЯ. И -- имя нынешнего knobinfo_t
.
28.01.2006: а реально-то разделение можно сделать примерно таким:
KnobPlugins // Нынешняя Knobs плюс плагин-архитектура для всех типов v KnobStructs // "Стандартные методы" структур v KnobsTree // "Стандартные методы" с деревом v [???] // Плагины к KnobsTree для доступа к данным и режимам
Т.е. -- Cdr как бы раздербанивается на 3 части: 1) то-чего-мы-так-долго-хотели для возможности использования в libKnobs; 2) поддержка древесной структуры -- то, чем реально и должна быть собственно Cdr; 3) "конкретизация" -- доступ к данным через cda, сохранение/восстановление/логгирование режимов через файлы и БД.
Встает, конечно, еще некоторый вопрос на тему "плагинов для доступа
к данным" -- а уровня Cdr ли это дело? Или -- cda? Опять же -- ведь
нынешняя Cdr сильно завязана на cda: одни флаги
CDA_FLAG_...
чего стоят.
Видимо, сие все ж таки дело для cda, но с оговорками о возможности "локальных" ручек -- они-то и будут единственной "иной конкретизацией доступа к данным".
06.02.2006: и, кстати: собственно УСТАВКА ЗНАЧЕНИЯ в конкретный экземпляр knob'а "от уровня получения данных" (хоть полученные-от-cda, как сейчас, хоть уставленные-программой в простую ручку) должна также проходить через эту библиотеку.
Смысл -- чтобы даже у simple-ручек корректно отрабатывались желтый/красный диапазоны, alarm'ы, и т.д.
03.03.2006: да -- и именно в ЭТОЙ ЖЕ библиотеке
должны располагаться "хитрые" функции доступа к меткам --
get_label_and_tip()
(сейчас она -- в Chl_gui.c и
отдельно в vacclient.c да еще в descr2html.c) и
GetKnobLabel()
.
18.06.2006: (описывая в диссертации cda и Cdr): насчет названия -- ответ-то действительно напрашивается: надо возвращаться к истокам! Что это у нас было изначально? Правильно -- логические каналы! А "ручки" (knobs) -- это уже вторично, служит для отображения. Так что --
Библиотеку должно назвать "liblogchans" -- библиотека работы с логическими каналами.
(Да, как хорошо перекликается с мыслями за 22-07-2005! :-)
05.06.2007: а может, лучше "libdatatree" -- библиотека работы с деревом данных?
Кстати, а Knobs надо разделить на две части:
30.08.2011: да, v2'шная libdatatree сегодня официально запущена в эксплуатацию.
int32
-значение, как-то их "характеризующее". А уж
обращение к "мелким" через интерфейс bigc -- это проблема небольшая.
c_type[]
, в котором ==0(r)/!=0(w). А можно ввести именно
формальный список типов -- RDONLY,RDWR,BIG.
И, кстати, давно напрашивается забить на "оптимизацию с режимом
адресации 386-го SIB", и свести ВСЕ c_NNN[]
в одну
структуру, которых массив.
Естественно, сие сделает чуть более сложной (реально -- просто
другой) работу во всяких ReturnChanNNN()
.
И постараться как-нибудь заунифицировать эту структуру с нынешней
bigchaninfo_t
. Конечно, совсем запросто это сделать не
удастся -- у больших каналов намного больше данных. Видимо, надо будет
завести параллельный "как бы разреженный" массив, в котором
наличествуют только большие каналы, но зато которых немного. И в
"унифицированной" структуре будет номер соответствующей структуры из
"параллельного" массива.
16.02.2006: располагаться-то большие каналы могут и ПОСЛЕ обычных, но -- в принципе, стоит объединить main_info[] и bigc_info[] в единый массив. Сама структура после этого станет union'ом, а первым полем будет селектор -- тот же самый, из формального списка RDONLY,RDWR,BIG.
После этого, собственно, никаких реальных причин запрещать смешивание по порядку обычных и больших каналов просто не будет.
Философия 1: да-да, "as little limits as possible"!
Философия 2: собственно, CxDataFork
и
CxBigcFork
уже унифицировали, а тут -- похожий процесс.
Замечание: и вот тут-то и можно будет повводить в эту унифицированную структуру-union строк "name", как обозначено за 15-02-2006.
29.08.2005: (давно, но все забывал записать) и при поиске приоритет у юзерских отображаторов -- чтобы можно было так переопределять стандартные типы.
22.12.2010@Снежинск-каземат-11: собственно -- в 4cx в
KnobsCore_knobset.c::GetKnobLookVMT()
запятая уже
обрабатывается (фиг знает сколько лет -- видимо, с момента реализации),
так что "done".
И чтобы потом можно было как-нибудь ссылаться на эти "невидимые" объекты.
Если их делать -- то явно надо будет иметь ОТДЕЛЬНЫЙ список таких "невидимых объектов", дабы они не мешали обычной "древесной" работе Cdr'а и элементов-отображаторов.
Правда, зачем нам могут понадобиться эти фичи, используемые в визуальном программировании -- большой вопрос :-). Видимо, для возможности реализации концепции "программы из кубиков", "программы без программирования" -- чтобы можно было те вещи, которые сейчас пишутся кодом программ, упихивать в стандартные компоненты, и потом все указывать прям в БД-описаниях, должным образом прописывая там связи между такими компонентами.
Конечно, очевидно, что реально на такие красивости у меня явно сил/средств/ресурсов не хватит. Просто стоит держать подобные вещи в уме, чтобы общая идеология системы их допускала -- это будет дополнительным формирующим "правильность" фактором.
(Кстати, еще из похожих вещей -- отдельный popup-list в Xt/Motif, а также те же формы в HTML'е.)
03.12.2005: сил/средств/ресурсов-то, может, но -- хотя бы для возможности реализации подобного, и для придания гибкости, стоит...:
"Grouping", "timers", ... -- просто разные секции, и сама grouping -- лишь одна из секций. Соответственно, "приложение" (то, что сейчас и есть "grouping") превращается просто в контейнер, содержащий секции.
Подобная структура рано или поздно вылазит во всех "крупных проектах": в NTFS'е содержимое файла является лишь одним из его атрибутов, "наравне" с датой, владельцем, permissions и прочими свойствами; исполняемые файлы (тот же ELF) состоят из энного количества (в принципе, расширяемого) секций.
Так что -- в CXv4 надо СРАЗУ закладывать структуру "приложения" как контейнер с некоторым количеством секций, заранее не специфицированных, но каждая из которых имеет имя.
(Кстати, "заранее не специфицированных" => расширяемость, а где расширяемость -- там опять напрашивается плагиновость :-)
06.12.2005: во -- насчет названий: то, что сейчас у нас именуется "grouping" -- то группировкой и останется; а контейнер верхнего уровня пусть именуется "подсистемой".
25.11.2006: еще: во всяких MEDM и ddd есть такая вещь как "вторичное окно" -- оное вызывается для "детализации" по нажатию на некую кнопочку из основного окна. У нас этого нету. Так вот: при переходе на "секционность" это станет очень просто -- кнопочке будет указываться метка-идентификатор такого под-окна, а его содержимое будет лежать просто в своей секции.
BTW, надо кроме имени в каждой секции еще обязательный параметр "тип" -- чтобы, к примеру, при "realize" приложения автоматом Cdr-создавались все группировки и их окна.
02.04.2007: попался еще один пример секционированного формата -- RIFF, лежащий в основе WAV, AVI, DIB и ANI (из-за vulnerability в ANI -- "Windows Animated Cursor Stack Overflow Vulnerability" -- и узнал). Там тоже весь файл состоит из "chunk'ов".
И, кстати, урок из этой vulnerability -- учитывать, что одна и та же секция может встретиться МНОГАЖДЫ (пусть это и некорректный файл, но быть такое может).
03.04.2007: кстати, а не такая ли идеология использовалась в моем формате UDF (Universal Datafile Format) для GREMLIN'а (~'1993)? (Ключевые слова -- .ICF, .MCF, .XR, .UTR, udfhdr.pas.)
Посмотрел на этот udfhdr.pas -- похоже, что нет, там использоалась другая технология. Но сами возможности, заложенные в формат UDF -- впечатляют, более широкий репертуар требует уже именно секционированного формата.
Кстати, а вот при отдельном чтении-то и можно будет вернуться к старой идее: что при "reload'е" БД читать все при живой системе -- в "отдельный объект-БД", а потом -- грохать старое и копировать в "реальное".
13.06.2005: и, кстати, можно будет вернуться к давним (древним!) мыслям о том, что системе можно сделать "stop" (pause) и "start".
22.06.2005: плюс см. мысли за сегодняшнее число в разделе о "CX server library".
13.06.2005: много :-). Вот они:
Может -- ввести, a-la GDB, общую команду-мультиплексор "list", которой уж говорить, что нас интересует -- "list WHAT", где WHAT -- "clients", "consoles", "devices", "frontends", "layers".
16.06.2005: Постановка проблемы: Сейчас-то по одному хватает, а вот для более "интеллектуального" использования CANKOZ-блоков может понадобиться и по две штуки: один -- для cankoz-layer'овской очереди, а второй -- для работы собственно внутренней логики драйвера.
Проект решения:
Так что -- надо будет в дополнение к нынешнему, единственному-умолчательному таймауту, заводить еще растущий по мере надобности пул при блоке из
sl_timeout_t
'ов.Именно -- ПРИ БЛОКЕ, со своим интерфейсом, а напрямую доступ драйверам к интерфейсу cxscheduler'а давать нельзя, поскольку 1) сервер должен мочь САМ выполнять cleanup; 2) драйверам надлежит жить "в песочнице" своего API, не зная деталей исполняющей среды (которые под Win32, в принципе, могут и отличаться).
А собственно "дополнительный" API будет включать функции "создать таймаут" и "погасить таймаут", плюс будет концепция "timeout ID" -- который будет per-block, и, например, default'ный будет иметь ID=0.
Все это выглядит довольно несложным, если не сказать тривиальным.
16.06.2005: мелкое замечание/исправление: вместо "в дополнение к нынешнему ... заводить еще растущий по мере надобности пул" -- надо заводить пул ВСЕГДА, и умолчательный таймаут просто будет в этом пуле первым (точнее -- 0-м).
Уже ведь проходили подобное в cda -- с нотификаторами :-).
13.01.2007: и еще тонкость: сейчас-то в privptr'е серверову timeout-handler'у передается драйверов handler, а magicid он узнает сам, вычитая из trec'а указатель начала массива.
Но: при множественных таймаутах надо будет передавать ДВА параметра -- magicid и timeout-ID (плюс еще handler, который можно уже и в пуле хранить). Как? Видимо -- кодировать magicid в 16 старших битах privptr'а, а ID -- в 16 младших.
13.01.2007: и, кстати, не только для таймаутов, но и для регистрируемых драйверами файловых дескрипторов также надо создавать ПУЛ, дабы один драйвер мог регистрировать их сколько надобно. А то то, как сделана якобы поддержка многодескрипторности сейчас -- это стыдоба, так, легкая халява.
И, возможно, отдельный серверный API-переходник к fdiolib'у -- обязательно!
13.01.2007: и еще: все такие "регистрирующие" серверные API-функции должны быть не void, а int -- чтобы возвращать статус (мало ли где что может обломиться, у fdiolib'а или у cxscheduler'а).
18.04.2011@Снежинск-каземат-11: а ведь оное реализовано еще ~16-02-2009, еще в CXv2, да и в 4cx/ тоже (где-то в середине 2009).
Посему -- "done".
26.08.2013: ...и вообще, уже вся концепция
"индивидуальных пулов" уволена в пользу прямого использования
cxscheduler'а, и все функции там int
'ы, так что раздел
стал тотально устаревш.
Кстати, можно будет воспользоваться для создания "закладок" (если самому будет лень их делать) комплектом Microline Widget Set, доступным либо в старой Mozilla, либо в новом NEdit'е. (Правда, там напряго с компиляцией, но разберемся.)
20.04.2006: да, а можно обойтись и стандартной motif'овской функциональностью -- оказывается, начиная с 2.2 в нем есть XmTabStack, который ровно что надо и делает. И точно также умеет приделывать закладки и слева/справа/сверху/снизу.
В любом случае, "типа проект":
ncols
-- игнорировать.
rownames
, либо, при "?" -- из метки n-й ручки.
В любом случае -- учитывая наличие стандартного компонента, изготовить сие будет легкой задачей.
28.04.2006: изготовил, в work/tree/testtree.c -- по вышеприведенному проекту. Детали:
В общем, считаем что "done".
01.05.2006: "доточил" -- теперь там корректно уставляются основные параметры отображения: XmNtabOffset:=0, XmNstackedEffect:=False, XmNtabMode:={XmTABS_STACKED:XmTABS_BASIC} (в зависимости от флажков stacked|oneline). И почему-то в режиме STACKED оно принудительно уставляет XmNuniformTabSize:=False, да еще и Warning пишет, так что в режиме STACKED мы сами его ставим в False.
И вылез ляп -- оно НЕ умеет работать с пустыми метками. Написан bugreport#1346, а пока проверяем, что если метка "", то уставляем ее в пробел. 03.05.2006: баг пофиксен Юрой.
04.05.2006: кстати, там еще есть один ляп -- почему-то виджетам, содержащимся в XmTabStack'е, со стороны закладок добавляется еще по несколько пикселов размера -- число пикселов равно значению XmNshadowThickness самого TabStack'а.
18.06.2005: у Славы к программе прикручен epics'ный CA-сервер -- как?
А не сделать ли в CX аналогично -- "ядро" cxsd -- "подселенцем"? Т.е., чтобы там была "симметричная" архитектура, БЕЗ принудительной ведущей роли cxsd?
(Опять же -- такая возможность нужна скорее для "корректности"/"красивости".)
20.06.2005: угу, ведь в принципе-то в cxsd действительно есть несколько ОТДЕЛЬНЫХ компонентов:
Напрашивающиеся выводы:
(Хотя -- такая фича дала бы возможность быстро присобачивать CX-интерфейс к готовым программам. А главное -- так можно было бы легко изготовить CX-доступ к epics'ным IOC'ам.)
20.06.2005: пообщался с Гусевым, и в результате выяснил про EPICS то, о чем Гусев сам-то не знал, ответ на вопрос "как?":
Там есть такая вещь -- "CA server library". Собственно -- из названия все и ясно.
А детали -- это хрень, тесно связанная с концепцией "portable channel access", и писимая (писуемая?) на C++ -- девелопер, использующий ее, "Provides interface classes and methods required by the server library".
А детали перечислены, например, в Jeffrey O. Hill, "A Server Level API for EPICS" -- в точности та же мотивация (и даже те же термины!) что у меня сейчас на тему "cxsd internal data API".
Собственно, технология "вроде как виртуальных классов" на обычном Си в CX давно используется (тот же connlib), так что вполне можно сделать ядро cxsd "подселяемым". И даже нужно!
Но -- это "second-order priority".
21.06.2005: вот только дурацкий вопрос -- а CX-протокол-то от добавления такой непредусмотренной функциональности (доступ к реально не-CX-объектам) не перестанет ли удовлетворять требованиям?
21.06.2005: да -- НАДО будет сразу делать как "CX server library".
Ввести флаг в записи "устройство" -- "builtin"/"non-resettable", а то, к чему подселяют, сможет при инициализации предоставлять свою БД с уставленным этим флажком.
И все упрощается тем, что у нас "симметричная" архитектура БЕЗ
транзакций, с непривязанными вопрос/ответами -- Return*()
можно делать в любой момент.
22.06.2005: да, надо будет изготовить сущность,
доступ к API которой будет через префиксы cxsd_
.
В т.ч. будет API для БД -- "создать объект-БД", "добавить в объект-БД строчку" (плюс API для надлежащего заполнением полей -- свойств каналов), "скопировать объект-БД в текущую БД" (плюс возможность делать "refresh", если изменились только поля, а не состав).
26.07.2008: за прошедшее время понимание вопроса несколько углубилось, так что вот текущее состояние "проекта":
У нас ведь сейчас фигурирует на схемах сервера компонент "channel manager" (появился на иллюстрации компонентов системы в кандидатской диссертации). Он -- «cxsd_data.c» (тут за 20-06-2005), «динамическая БД» (defence.ppt), «поддерживает существование каналов, реализуя их семантику и решая такие задачи, как мультиплексирование, разграничение доступа и т.п.» (carbotron-cs-memorandum).
Так вот, фактически, этот "channel manager", на пару с серверной реализацией CX-протокола, плюс некоторая примесь модуля/алхимии с БД, и составляют искомую сущность.
Т.о., при реализации СУ по имеющемуся на настоящий момент проекту-схеме, при условии хорошо продуманных интерфейсов между кубиками, кубики "channel manager" и "cxsd_fe_cx" фактически и будут составлять "CX-server-library".
24.07.2008: в продолжение темы подселяемого сервера:
если у нас всё такое модульное, то можно ж выпендриться еще круче: мочь подселять не только сервер, отдающий данные программы наружу, но и приделывать к программе серверное ядро (channel manager), доступное по специализированному "локальному" протоколу-frontend'у, БЕЗ каких-либо иных frontend'ов, зато с ДРАЙВЕРАМИ. Тогда можно из тех же кубиков собирать полностью локальные программы для малой автоматизации.
Таким образом, концепция "кубиков" будет доведена до предела, и из них можно будет конструировать как клиент-серверную, так и замкнутую/монолитную систему.
P.S. А «специализированный "локальный" протокол-frontend» естественно, тот же самый, что и внутрисерверный для драйверов, только под другим именем-схемы.
27.08.2008: битым текстом:
Короче - красиво выходит, хоть презентацию на следующий ICALEPCS готовь.
struct timeval
'ом.
Последние два пункта -- видимо, должны смотреться специальной командой -- типа "status <ID>".
Можно передавать не все строки, а -- как атомы в X11 --
вначале "словарь", а в описаниях knob'ов -- номера строк из этого
словаря, например, 16-битными числами.
Другое дело -- а оно нам надо? Да, экономия будет -- байт по 5 с каждого knob'а на больших группировках, зато -- сколько маеты: составить словарь, а в клиенте -- вначале делать lookup по словарю, а когда все будет раскодировано -- словарь выкинуть. Нафиг? У нас ведь все равно описания knob'ов будут состоять из моря varlength-строк, так что...
03.07.2005: нет, точно не надо :-)
Надо делать psp-парсинг auxinfo в параметры "принудительно" -- т.е., как это делается для модулей в ядре Linux.
Как такое сделать -- просто: в driverrec'е указывается также и psp-таблица, а там уж -- есть 2 варианта: либо парсинг прямо в privrec, либо -- указывать отдельно размер и прочие параметры спец. структуры. Но второй вариант мне нравится меньше.
И абсолютно аналогичный финт стоит проделать с knob'ами.
Если будет какой-то «visual design tool» для интерфейсов программ, то он сможет элементарно вычитывать поддерживаемые конкретным компонентом через widdepinfo "свойства" и представлять их в окошке "Knob specific properties" в виде списка -- название/значение, и даже сразу производить верификацию вводимых параметров.
И, хотя это чуть сложнее ---
Аналогично с параметрами драйверов: они могут вычитываться прямо из драйверов. Но тут проблема --- что редактор-то БД доступа к драйверам может ну СОВЕРШЕННО никак не иметь.
- Насчет "выделения" компонента:
Как махинировать с компонентами на design-экране: каждый компонент вставляется не "сам по себе", а -- на его место ставится форма, которая включает его (последним!), причем самым первым идет специальный прозрачный виджет (InputOnly), который блокирует (перехватывает) мышиные события -- он приаттачивается ко всем краям формы, плюс четыре тонких виджета, которые включаются при получении фокуса -- также приаттачиваются к краям.
Естественно, в таком разе придется вводить "параметризованное создание knob'а" (указанием функции, которую вызывать в качестве
CreateKnob()
) -- чтобы контейнеры вставляли в себя не knob-жертву, а эту форму.При этом проблема только, что КЛАВИАТУРНУЮ-то навигацию мы так не заблокируем...
И вообще -- а не лучше ли, наконец-то, научиться ЛОВИТЬ ПЕРЕМЕЩЕНИЕ ФОКУСА, и по оному событию как надо перемещать ту рамку на поверх "жертвы"? Проблема лишь в том, что
XmN[losing]focusCallback
имеют только XmText/XmTextField, а нам его придется имитировать...- При модификации некоего компонента надлежит его (со всей под-иерархией, естественно), грохать, и создавать заново.
- Нужен "flat" (spreadsheet) view.
- Оно должно уметь сохранять/загружать как из БД, так и из "локального файла".
Мда, с одной стороны -- сама идея такой тулзы весьма амбициозна, а с другой -- выглядит вполне реализовабельной.
Т.е., клиент (cxlib) просит сервера вместе с данными отсылать некие "handles", определяемые именно клиентом -- дабы клиенту не надо было делать у себя поиск по номеру канала (каковой, естественно, также надлежит присылать). Ну и, соответственно, некие callbacks в клиенте, также (со своими?) handles.
04.07.2005: ясно, что должно выступать в качестве
"handle" на маршруте server->cxlib->cda: просто номер fork'а.
Ибо и в cxlib'е (NnnnForkInfo *subsbuf
), и в cda
(ныне -- {chan,bigc}info_t *{chan,bigc}info
) этот номер и
служит индексом в "массиве свойств", являясь наилучшим handle'ом.
19.08.2005: кстати, а в протоколе CA именно так и делается -- там целый раздел уделен тому, как следует отводить идентификаторы.
Нынешний cx.h становится cxlib.h (a-la Xlib.h), а нынешний cx_types.h превращается в cx.h (a-la X.h -- "определения", глобальные для системы).
06.08.2006: в 4cx/ с самого рождения уже так и сделано -- ровно по этому проекту. "done".
А де-факто, по логике, им место в совсем отдельной библиотеке, которая будет пользоваться УСЛУГАМИ "интерфейса доступа к данным" (через некий API/интерфейс), и этим интерфейсом не обязательно должна быть cda (хотя чаще всего будет она).
При этом мы получаем свободу, как делать "универсальный доступ к данным" -- либо поддержка "Channel Access etc." будет прямо в cda, либо в совсем другой библиотеке, предоставляющей cda-подобный интерфейс, плюс что-то в Cdr'е.
14.06.2007: да, реально ЕСТЬ причина привязки
формул к cda: auxservers. Ведь cda_register_formula()
сейчас вручную добавляет каналы-по-имени.
Собственно, ответ, как это обойти, очевиден -- ввести API для сего действия.
11.08.2011: поскольку большая часть написанного тут уже учтена, да и с введением "сценариев" концепция слегка мутировала (и часть описанного стало irrelevant), то "done".
kind
(LOGK_{DIRECT,CALCED}
)? Ведь можно делать проще: если
формула указана -- использовать ее, если нет -- то номер канала.
13.03.2006: да, абсолютно незачем -- так что из
4cx/.../Knobs_typesP.h поле "srctype
" выкинуто.
fd_io()
устраняется вовсе.
init_blk()
будет возвращать статус.
bid
будет передаваться только в
init_blk()
-- как показывает опыт, он нигде в других
местах не используется.
06.08.2006: в "совместимом коллеге-прототипе будущего API" -- cm5307_dbody.h -- несколько месяцев назад именно такой интерфейс и сделан (только с массивовым businfo вместо busid). Удобен.
11.08.2011: тему уже давно можно считать за "done".
В частности, это просто очень-очень прет для той самой мерзкой задачи -- "процедура выполнения reset'а источнику". Видимо, я худо/бедно смогу реализовать оное и так, но уж дюже это будет неудобно!
Опять же -- camsel. Сейчас там тако-о-ое! Черт ногу сломит...
А еще и емановский demag, которому место явно в nmagsys'е.
Перед глазами стоИт пример SNL, где эти вещи могут делаться сравнительно просто (а главное -- понятно). Хотя и не там, где надо :-). (Там это только server-side, а надо -- client-side.)
16.09.2005: (понято во время написания раздела
про SEA в кандидатскую) часть того, КАК это объединение сделать --
понятна: action и checker -- это просто формулы (которые честно
пишут и читают каналы), возвращающие -1(fatal)/~=0(none)/+1(success);
плюс, возможно (для цикленья) какой-то способ из формулы сказать
"sea_goto()"; sea_check()
вызывается по приходу данных.
Неясности же --
17.09.2005: ну-ну... Чуток подпоразмышлял, и... В качестве варианта идеи: можно такие спец-sea-программы запихивать как раз в список "невидимых объектов" (см. на тему "таймеров" за 01-июня-2005).
Общее резюме от такого подхода -- что sea получается НЕ в формулах, а "выше"/отдельно, а формулы -- лишь подспорье. Это все-таки не совсем то, о чем были первоначальные думы: хотелось-то уметь именно к КОНКРЕТНОМУ KNOB'У привязывать не только формулы, но и "методы"/"действия".
Так что надо продолжать думать, генерить идеи дальше.
08.09.2013: в принципе придумалось -- см. раздел "О некоем скриптовом языке..." за сегодня. Здесь же ставим "obsolete".
14.11.2005: решение (когда пришло в голову -- не помню, но, вроде, не в душе :-): программу можно назвать "knobber".
А что до ее поведения -- можно сделать как планировалось давным-давно, еще после создания drc (1997? 1998?): чтобы можно было выбрать из списка интересующую подсистему. Например, если оно запущено под своим родным именем -- то так, а если под каким-то другим -- вот тогда, как сейчас, ищется соответствующее БД-описание.
Плюс, можно предусмотреть -- как в cern'овской "knobs" -- начинение окна некоторым набором ручек из разных подсистем.
07.03.2006: а синтаксис ее запуска -- таков:
SUBSYSNAME [SRVREF] # Load and run specified subsystem knobber SUBSYSNAME [SRVREF] # The same knobber # Interactive
12.03.2006: во -- а как будет слово "пульт" по-английски и по-французски?
13.03.2006: посмотрел в Лингве и вот: английский -- "stand", французский -- "tableau" (бе!), а вот немецкий -- "pult"!
Так что -- называем программку "pult", и директорию в $HOME также можно с чистой совестью именовать pult/, как и сейчас.
Столкнулся сегодня при создании phm-lebline.c на основе phm-tsyline.c -- они ж очень похожи, и реально уж по крайней мере функции-фильтраторы ну уж никак не должны отличаться!
02.11.2005: имеется некий "предварительный" ответ на этот вопрос, состоящий из двух частей:
Вопрос только, куда эту таблицу засовывать, чтобы она сканировалась Xh-обработчиком? Дополнительный "невидимый плагин"? А как он будет связан с собственно содержимым окна -- как он будет туда адресоваться? Или как-то диспетчеризовать команду по объектам -- как это делалось в TurboVision и GREMLIN?
#define
.
Вообще, надо принять как общий принцип -- все потенциально разнообразящиеся/расширябельные вещи должны кодироваться не числами, а строками.
09.06.2006: еще вариант -- чуть более реалистичный/приземленный:
А поскольку юзер, по сравнению с компом, существо медленное, то временнЫе затраты на этот бродкаст пренебрежимы.
20.07.2006: да-а-а, а не расширить ли понятие "текстовые команды" с просто строк на "командные строки"? И получим мы тогда аналог actions в Xt -- короче, повторим эволюцию, в результате которой два десятка лет назад Xt вышла такой, как вышла. Вопрос -- а оно нам надо? :-)
02.11.2005:
Так что -- из общеидеологических соображений все же надо делать всю
эту "дисплейную инфраструктуру" БЕЗ статических переменных (никаких там
onewin
!).
Разрешить указывать, например, до 10 чисел через запятую,
а в init_b()
передавать int32 *businfo, int
businfocount
.
При этом то, сколько таких чисел разрешено указывать для конкретного блока, может храниться в описании типа блока в БД.
А что-то более хитрое -- можно по-прежнему указывать в
auxinfo
.
(Навеяно разбирательством с гусиными конфигами для EPICS'а в мамкинской коробочке -- "@CAN1.0:15/0xfffffffc ...".)
17.12.2005: кстати, а в метрике драйвера можно указывать пару чисел -- min_busid_n,max_busid_n, чтобы они также проверялись сервером при чтении БД.
17.12.2005: насчет "резолвинга имен": с случае с
текстовым описанием (назовем его "hwinfo.lst", а "читалку" --
hwinfo_lst_reader()
) формат может быть таким:
devname: driver businfo|"-" ...
18.12.2005: и еще -- стоит ввести возможность более-менее полноценного отражения информации БД в этих текстовых файлах. Ну и что -- делать что-то типа того, что предусматривалось файлами формата ".uds"?
06.08.2006: угу, в "прототипе" будущего API в cm5307_dbody.h уже как раз businfo[businfocount] и используется.
11.08.2011: так что -- давно уж "done".
LOGT_DEVN
,
LOGT_MINMAX
, возможного будущего LOGT_AVG
и
им подобных.
Т.е. -- просто еще одно число в определении knob'а.
И, кстати -- а когда, собственно, вообще появилась концепция "LOGT_*" (в первую очередь -- LOGT_DEVN, так?)?
Когда, когда -- давно! Еще в UXIL (Ucam X Interface
Library) оно было -- там в uxil.h (06-11-1998, но наверняка
появилось раньше на год/два) имеется LOGT_DEVN=1001
.
Наверняка корни этого уходят куда-то в drc/ekc/..., но рыть туда уже
вломы.
Причина -- запрос-то на данные послан, но ответ придет только когда закончится очередной цикл сервера.
Так что -- в будущем, когда в протоколе в пакетах запросов появится флажок "когда" (немедленно, по обновлению, по концу цикла), то надо будет, чтобы cda посылала ПЕРВЫЙ запрос именно с когда=немедленно.
У нас же ведь у каждого клиента есть уникальный ID, так? Значит, можно вместе со значением канала отдавать и "ID последнего менявшего", а уж cda сможет просто сравнивать ID-изменившего-этот-канал со ID-этого-соединения (при установлении соединения выспросив его client-ID у cxlib'а).
Сложность (помимо протокольного overhead'а) -- в том, что вообще-то операция записи неатомарна, и нет такого понятия, как "вот этот персонаж выполнил запись"; собственно инициирование записи и возврат значения от драйвера на нее -- разнесены. Можно, впрочем, в момент отправки-драйверу-на-запись складировать в некое место (c_wr_requestor[]) ID инициатора, а по получению данных в этот канал -- копировать из c_wr_requestor[] в c_last_writer[]. Естественно, с надлежащей поддержкой ситуации "next" -- какой-нибудь c_next_wr_requestor[].
В некоторых приложениях (типа того же rfsyn'а) может быть ситуация, когда НЕЛЬЗЯ отправлять все значения сразу -- нужно в некоторой последовательности, с задержками.
Можно в структуру "knobinfo_t" добавить поле "приоритет" ("задержка"), определяющее, через какое время (в циклах? в секундах?) после загрузки режима надо отправить значение в сервер. (Аналогично полю "pass" в /etc/fstab.)
В случае использования такого подхода возникает несколько последствий или возможных задач:
XtSetSensitive(workSpace,False)
плюс блокировка
кнопок? 24.10.2012@Снежинск-каземат-11: фиг -- на subwin'ы это ж не
повлияет.). И, естественно, сие должно как-то
индицироваться, включая оставшийся до конца период. Плюс -- возможность
прерывания этого процесса.
01.04.2006: есть еще один "потребитель" упорядоченной/последовательной загрузки режимов: "полуискусственные" каналы, арифметически привязанные к настоящим. Сегодня это вылезло с ausacc, где есть "пары" GVI-каналов (стартовый/стоповый импульсы), и удобнее вместо второго значения работать с его СМЕЩЕНИЕМ от первого. Но при загрузке режима формула имеет доступ к СТАРОМУ значению "базового"/стартового канала, и именно к старому будет прибавлять считанное из режима смещение, что есть плохо. А так бы -- сказали б, что очередь "смещательных" каналов -- через 2 цикла, да и все...
02.04.2006: а для "второго потребителя" в принципе есть выход -- при всех операциях чтения/записи "базового" канала складировать значение в локальный регистр, и "канал смещения" пусть производит всю свою арифметику уже с числом из регистра, которое и является "самым свежим".
04.10.2012: еще прикол касательно режимов (реально уже неделю над ним голову грею).
Ну и что можно сделать?
defnorm
, реально так
нигде никак и не используемого?)?
Как сохранять и восстанавливать данные для больших каналов и для LOGT_USER? Да еще учитывая, что сохранялки/читалки могут быть разные -- file://, DB://, etc...
Ответ: строками!
Детали идеи:
free()
после использования.
А если в CxsrvMainInfoRec
ввести поле
"name"?
По-хорошему, надо б еще уметь и отдельные параметры в
CxsrvBigcInfoRec
как-то поименовывать...
10.03.2006: собственно, идеи-то приходили последовательно, все больше по командировкам:
(Навеяно также и разноцветными кнопками -- оранжевые, зеленые, синие... fotki/05_03_17_banya/P3170258.JPG -- справа. Вот как бы это сделать, а?)
И вот глядя на это все -- а нужно ли нам поле "color"? Если да, то зачем? Ведь оно полностью поглощается в случае хоть какой-то реализации концепции "style".
13.03.2006: это тоже наполовину по командировкам :-):
LOGD_ALARM
).
А с plugin-архитектурой по char*
,
','-separated, это будет и совсем неприемлемо.
Так что -- надо изобретать способ указать свойство "алармовости" прямо в описании knob'а. Это слегка смыкается с задачей указывать string-list для селектора.
Может - кроме "type" (knob|subelem|decor|user) ввести еще и "subtype" для knob'ов, в зависимости от которого иметь доп. поля? М-м-м... Не очень нравится...
E_ShowAlarm_m()
.
Хотя, с другой стороны, конечно, сейчас-то есть специализированная архитектура алармов, а то будет лишь "побочный эффект", который кто-то может и не[до]поддерживать. Да, конечно, идея "передачи аларма вверх по цепочке более корневому контейнеру" в нынешнем варианте работать не будет -- поскольку uplink будет у всех элементов, в т.ч. верхнего уровня, но уж это-то как-нибудь решим.
Т.е. -- вводим отдельные типы LOGT_ALARM и LOGT_BUTTON, которые по всему поведению идентичны LOGT_KNOB, но Cdr (точнее -- будущая "knobs-struct management library") при их "регистрации" уставляет флажки IS_ALARM, IS_LIGHT, IS_BUTTON.
(Можно было бы, в момент регистрации, вовсе заменять поле
type
на LOGT_KNOB
-- для упрощения кода, но
это уж точно недостойный хак, который где-нибудь да аукнется, так что
так поступать не следует.)
В принципе -- вполне работоспособный проект. Хотя и фиг знает, как же правильнее делать -- так, или же все-таки допридумать способ как-то отдельно прямо в описателе указывать "свойства" алармовости, лампочности и кнопочности.
Главная неприятность этого проекта -- нарушается однозначное
соответствие между полем type
и выбираемой по нему
структурой из union'а u
. Так что -- я все-таки предпочел
бы вариант с "указанием свойств".
02.07.2007: йоу -- проблема указания алармовости ручки решается очень просто! Итак:
Так что -- вуаля -- проблема исчезает, при взгляде под правильным углом ее просто не существует!
"Лампочность" и "кнопковость" можно указывать так же -- если только они вообще когда-нибудь зачем-нибудь понадобятся (cx-starter'у на них пока что глубоко начхать...).
03.07.2007: в продолжение темы: а еще ведь надо (для всяких веб-выдач) уметь не-глядя-на-look понять, что ручка является селектором.
Делается аналогично -- вводится флажок "_B_IS_SELECTOR", понимаемый этим веб-выдавальщиком & Co., а конвертеры будут понимать и словцо "selector", также приравнивая его к "knob", но заодно выставляя вышеуказанный флажок.
Кстати, проект другого подхода: можно было бы расширить синтаксис "knob ВИД" до "knob{/КЛЮЧ} ВИД" -- например, "knob/alarm alarm ...". И среди ключей кроме alarm, light, button и selector можно было бы также иметь "groupable" (в противном случае его придется делать ключевым словом, аналогично tip= и подобным).
27.07.2007: да, ровно по такому проекту в CXv4 в Cdr_fromtext.c и сделано -- оно понимает ключевые слова "selector", "light", "alarm", "button" как синонимы "knob", выставляя им соответствующие флажки в behaviour.
Так что, поскольку проблема решена во всей полноте -- "done".
02.12.2013: в новой версии парсера реализованы и ключики /BEHAVIOUR, могущие быть указаны пачкой (разделители -- либо тот же '/', либо ',').
- Группировка будет ПРОСТО ОБЫЧНЫМ ЭЛЕМЕНТОМ, ничем от него не отличаясь -- кроме того, что у нее не будет "parent'а" и располагаться ее описание будет в "секции".
- Т.е., вся та информация, что сейчас (в CXv2) составляет группировку -- app_name, app_class, [win_title], icon -- идет в свои, отдельные секции.
Хотя для удобства в бинарной структуре "подсистема" будут предусмотрены готовые поля-указатели для адресации к этим секциям -- для скорости, чтоб обходиться без поиска.
- Поля же defserver и ему подобные -- т.е., касающиеся именно СОДЕРЖИМОГО -- будут частью структуры элемента.
- А уж весь набор секций -- и именуется группировкой.
- Да, можно нарваться на некоторое дублирование информации -- например, sysname<->root.ident, win_title<->root.label, но сие преспокойно избегается минимальной осторожностью.
(А принцип file.so::rec[3]=название -- легко, туда ставится хоть дубль sysname'а.)
27.07.2007: выше написано не совсем корректно -- там везде вместо термина "группировка" следовало бы использовать "подсистема". А группировка -- это лишь часть подсистемы, и их может быть много.
В остальном -- все правильно, и уже именно так в CXv4 и сделано, так что "done".
Ответ-то лежал на поверхности: плагином будет именно группировка (ну или -- некий "содержащий" элемент. И она уж как надо "начинит" себя: создаст одну "закладку" с обычным набором подэлементов (которые будут сеткой), и другую -- с гистограммой, использующей ту же иерархию.
Методы (таблицы -- VMT) LOGT_KNOB'ов и LOGT_NOOP'ов должны быть унифицированы. Тогда knob-декорацию можно использовать хоть как просто декорацию, хоть как "меняющий вид/состояние" knob.
Просто для реально-только-декоративных knob'ов (типа
метки, сепаратора, etc.) метод SetValue()
будет NULL, т.е.
ничего не делающим.
(Ну и так же могут быть "с-унифицированы" сюда возможные LOGT_ALARM и LOGT_BUTTON.)
14.07.2007: да, это сделано. Точнее, так: у
декораций ВООБЩЕ никаких методов нету, и структура
dataknob_noop_data_t
также пустая -- посему вместо KNOB'ов
можно использовать NOOP'ы, и все окей. Плюс, естественно, поиск по VMT
подхватывает в качестве knob'ов и noop'ы тоже.
knobs_emethods_t.SetPhysValue()
?
31.07.2006: что ж, ответ напросился: СТАНДАРТНЫЙ libKnobs'овый метод SetPhysValue() будет просто пытаться вызвать метод SetPhysValue() своего "хозяина". А вот для элемента верхнего уровня -- группировки -- методы будет предоставлять уже Chl. Все равно всякие ShowProps() и ShowBigVal() может делать только он -- так и SetPhysValue() тоже. Это ведь и станет "сутью Chl'я".
(Или вообще элемент-группировку реализовывать именно в Chl'е? Надо подумать...)
Надо иметь возможность создавать ЛОКАЛЬНЫЕ иерархии, у которых бы "отправка" значений сводилась к вызову личной функции программы.
Пример -- набор управляющих компонентов для adc200 (для "параметров большого канала"). СОздавать оное вручную -- нуднейшая задница, а так бы -- указать иерархию с собственным "методом уставки значения", и все чики-чики.
Но: если оно начинается с '.', то это не "относительная", а "абсолютная" ссылка, и префикс от parent'ов не используется.
Это как бы объединение, надмножество той схемы с опциональным указанием defserver'а в элементе, и DOOCS'овской "базы".
07.02.2007: по первоначальной идее префиксом абсолютности предполагалось взять '/', а не '.' -- по образу и подобию TINE, но по здравому размышлению стало ясно, что это неудобно: во-первых, '/' используется в указании протокола и как оператор деления, а во-вторых, раз уж у нас компоненты имен разделяются точкой, то и префикс надо иметь такой же. Именно так делается и в файловой системе (с '/'), и в языках программирования (с '.').
Надо ввести свойство (большого?) канала -- строка-идентификатор ("adc200", "tsycamv"), чтоб программы могли проверять, что "это то", и для квази-plug-and-play.
Возможно -- стоит реализовать даже более общую вещь: некое описание "что есть что" для всей аппаратуры, чтоб можно было at-runtime понимать, что "вот это -- осциллограмма, с такой-то раскладкой данных", а "вот это -- матрица пикселов"; плюс, чтобы можно было понимать, что есть обычные каналы (текстовое поле для произвольного ввода, шкала/бегунок, селектор, ..., ???). Опять же -- из описания больших каналов должно быть ясно, какими именно типами ручек надо представлять на экране их параметры. (В идеале -- чтоб этого описания полностью хватало для изготовления пользовательского интерфейса [такого большого канала].)
Пока какие-то сумбурные мысли, безо всякой конкретики -- просто "а вот хорошо было бы чтобы..."... Хотя и очевидно, что все это должно храниться в базе данных и как-то пересекаться с тем, что есть уже сейчас (минимум/максимум, список значений для селектора, ...). Еще раз, более внимательно, почитать об ASN.1?
18.05.2007: Смысл/причина -- в "Вычислениях им. его преосвященства о.Федора Еманова" имеется некоторое количество коэффициентов, которые сейчас приходится вбивать в программу (формулы) гвоздями, а хочется, чтобы оно было легконастраиваемым.
Формат такого файла может быть простым -- энное число строк вида ИМЯ_ПЕРЕМЕННОЙ=ЗНАЧЕНИЕ, и, возможно, чтобы на одной строке могло быть несколько переменных -- как бы таблица. Переменные-то ведь будут адресоваться по именам, вот эти имена в файле и писать.
И -- можно бы дать возможность считывать/сохранять такие файлы и прямо во время работы программы (типа -- чтобы какие-нибудь ActionKnob'ы вызывали соответствующие функции из Cdr). (С загрузкой-то все просто, а вот сохранение -- наверное, надо как-то соответствующей Cdr-функции передавать список интересущих переменных, ибо сохранять все -- смысла мало.)
И -- не обязательно просто файлы, можно бы эти сущности, наряду с таблицами интерполяции, держать в БД.
19.05.2007: о -- есть проект решения на прямо сейчас!!!
Сопоставляем каждому такому параметру по "синтетическому" knob'у, которые все помещаем в отдельный элемент (возможно -- SUBWIN, чтоб редактировать удобно было скопом). Тогда можно просто генерить файлы режимов, которые будут содержать лишь этот элемент, и загружать их -- разные по мере надобности.
А вообще-то никакой загрузки "по мере надобности", видимо, и не потребуется -- просто эти коэффициенты так и будут сохраняться вместе с режимами (в рамках которых коэффициенты, кстати, и имеют смысл).
04.06.2007: Сейчас проблема решается размещением таких параметров в локальных регистрах и (опционально) связыванием с этими регистрами "синтетических" ручек. И инициализация оных довольно мутновата.
А -- мочь к каждому knob'у прилагать (опционально) несколько параметров -- массив -- доступных для формул и меняемых в окне "Knob properties".
Детали:
Главная же проблема будет -- КАК описывать эти параметры в описании ручки:
Неа -- надо заводить какой-нибудь спец. тип "массив/список
регистров" в cda и тут применять уже его. А Knobs от cda-то отвязана!
Ох... Завести еще какой-нибудь .h с соответствующими
определениями, включаемый и в cda.h, и в Knobs_types.h/datatree.h?
15.06.2007: угу -- cx_common_types.h, и в нем
тип CxKnobParam_t
.
В для-.so-файлов -- видимо, тоже массивчик, как сейчас делаются описания подэлементов.
В текстовых и from-server/БД описаниях -- видимо, тоже некая вложенность на основе фигурных скобок {...}.
После сего уж точно надо сделать, чтобы диапазоны и шаг также были доступны как "локальные параметры" (только для чтения?) и формулам, и tcl'ю. Или вообще сделать единый массив параметров, который ВСЕГДА начинается с диапазонов и шага?
15.06.2007: поскольку из-за этих параметров мы получаем как бы несколько наборов локальных регистров (что неудобно), то как бы с ними обращаться?
Решение довольно несложно:
И потом, считывая из формуло-программы "ссылку на регистр", адресуется примерно как
arginfos[(reg_n>>24)&0xFF].vect[reg_n&0x00FFFFFF]
Следствие 1: формулы могут быть использованы только для той ручки, для которой они зарегистрированы. В более общей формулировке -- только с тем же "контекстом", для которого они зарегистрированы.
Следствие 2: структуры и массивы локальных регистров и параметров должны быть унифицированы. И с ними -- естественно, унифицированы врЕменные регистры (если оные вообще будут нужны).
Замечание: вообще-то стоИт и вопрос сохранения локальных регистров с режимом -- ведь если параметры сохранятся с ручками, то как же регистры? Ответ -- надо и их тоже сохранять, ЛЮБЫЕ (а сейчас сохранятся те, которые маппируются на ручки). Но: часть-то (реально -- бОльшая) ведь являются рабочими, и их сохранять незачем. Как разделить? Ответ: пусть регистры и параметры, чьи имена начинаются с '_', не сохраняются. Это аналогично ручкам/элементам, у которых не сохраняются те, чьи имена начинаются с '-'. Дополнение: надо бы этот принцип с '_' распространить и на ручки.
Мысль: вообще-то можно рассматривать локальные регистры как "параметры группировки" -- тогда и унификация становится самоочевидной, и сохранение/восстановление совершенно аналогично
27.06.2007: вступление: мы ведь в любом случае хотим сделать в интерфейсе сервер/драйверы вызов "уставить параметры большого канала" -- просто уставить, на будущее, БЕЗ выполнения измерений.
Так вот: а можно же автоматически добавлять блоку "обычных" rw-каналов, по количеству параметров больших каналов, "маппируя" их на специальный как-бы драйвер, который будет сводиться к вызову метода "уставить параметры большого канала".
Другой вариант -- ввести кроме типов 0 (ro) и 1 (rw) еще дополнительный: 2 (bigc param). Этот вариант более streamlined и straightforward. (Можно, кстати, ввести еще и тип 3 -- bigc.)
Тогда можно будет адресоваться к таким каналам-параметрам напрямую, как к обычным каналам. И в БД им могут авто-даваться имена вида "...ИМЯ_БОЛЬШОГО_КАНАЛА.ИМЯ_ПАРАМЕТРА".
В любом случае, останутся проблемы:
MarkRangeForIO()
/ConsiderRequest()
упаковывает вместе для передачи драйверу в одном запросе все однотипные
каналы, принадлежащие одному драйверу. А надо -- по границе большого
канала делить. (Вопрос, конечно, технический, и несложно решаемый,
но все же...)
Не самая красивая реализация выходит, но -- довольно практичная и заведомо реализовабельная и работоспособная.
Понятно, конечно, что внутренняя реализация/мозги cxsd_channels/chsd_bigs для будущего более гибкого API получатся совершенно другими, и, возможно, описанные в этом разделе идеи совсем не пригодятся, либо проблемы в будущей парадигме решатся сами собой. Но -- есть шанс, что подобные проблемы возникнут и там.
24.02.2005: Идея Старостенки, как это сделать:
Чтобы к каждому такому полю прилагалось по checkbox'у, и когда пометишь несколько полей, а потом начинаешь менять одно из них, то одновременно на столько же меняются и остальные. (А если меняем канал, НЕ помеченный, то это больше ни на ком не сказывается.)
Естественно, первоначально мне сия мысль не очень глянулась: чекбоксы -- это лишнее место на экране... Стал думать -- а как бы этак сделать, чтобы ЛЮБУЮ ручку можно было так "выделить": обводить доп. бордюром -- места нет; ввести еще один colstate -- тоже нехорошо, будет интерференция (у кого приоритет -- у помеченности или у красноты?).
Кроме того, сразу в мозгу прозвучал звоночек: а, собственно, что -- менять каналы в группе только на то же самое число? Т.е., все, например, на единицу? А на что-то пропорциональное (INC*coeff) через некоторое время господа физики не захотят ли? -- Да, сам Старостенко об этом тоже задумывался, но ни к чему конкретному не пришел. Сошлись на том, что он подумает, а я поговорю с вэппманами -- у них подобные вещи используются.
Как можно было бы сделать "коэффициент": либо просто "распространять" событие нажатия по всем "помеченным" ручкам, а те пусть пользуются своим полем шага, либо ввести к шагу еще одно поле -- "шаг-2", которое и будет "коэффициентом" (по умолчанию -- 1.0).
И так случилось, что через пять минут позвонил Карнаев, и я с ним на эту тему пообщался. Как всегда, у них все оказалось проще, чем я прикидывал (ну еще бы -- сделано давно, на Одрятах, многое делать было просто лень :). Итак:
У них эта фича называется "связанные каналы". В описателе канала можно указать, что к нему привязаны еще такие-то (список), и когда его крутят, то те тоже крутятся (хотя оператор этого почему-то не может видеть "можно было сделать, но не стали" -- какие-то ограничения пользовательского интерфейса -- что мешает просто нарисовать в том же "окне" и привязанные каналы?). И при желании можно привязать каналы друг к дружке -- чтобы трогая любой из них, двигались оба.И сдвиг ВСЕГДА делается на ОДНО И ТО ЖЕ число -- они не стали вводить коэффициент. Собственно, Карнаев так и посоветовал -- сделать именно такой простой вариант, а если кому-то мало будет -- пусть улучшают сами.
Ну что ж, появилась некоторая ясность: можно сделать по-простому -- чтобы помечать несколько полей, и сдвигать их все на одно число, безо всяких коэффициентов.
Что касается пользовательского интерфейса "пометок":
(Где-то я эту идею подсмотрел -- в Excel'е, либо в Corel'е, да и в XFig'е при инструменте Edit все точки помечаются малюсенькими прямоугольничками.)
Осталась пара вопросов:
И, кстати -- а не ограничить ли "группы отмеченности" границами элементов?
Я склоняюсь к тому, чтобы сделать в простейшем варианте -- с чекбоксами при текстовых полях, при помощи "LOGD_TEXTGROUPABLE", и с какой-нибудь простенькой передачей событий -- либо события нажатия стрелки, либо события "сдвинься на такое-то число".
25.02.2005: да, дал прочитать все вышенаписанное Старостенке -- он согласился/одобрил. И -- стоит сделать именно в простейшем варианте. Ответы на вопросы:
И -- естественно, связывание ограничивается внутренностями одного элемента.
Итого -- что делать понятно, как -- тоже примерно понятно, да вот только время на реализацию всей этой радости будет только после возвращения из Японии и из Москвы -- т.е., в начале апреля.
11.04.2005: СДЕЛАЛ, именно по простой схеме, описанной выше. Highlights:
LOGD_TEXTGPBL
("text
GrouPaBLe").
knobinfo_t.is_ingroup
, каковое этим
чекбоксиком и меняется.
TextUpDownHandler()
имеется собственный цикл по
содержимому uplink'а, который проверяет, что если LOGT_WRITE1 и
is_ingroup, то проворачивается указанная операция. Сам "виновник" при
этом пропускается -- с ним махинация проделывается отдельно, как
раньше.
А по-хорошему следует завести в Cdr'е итератор, который бы проходил по всем child'ам указанного элемента, при надобности -- даже рекуррентно. (Хотя в данном случае рекуррентность -- малополезна, ведь если вызов из верхнего уровня нижний затронет, то из нижнего -- верхний оставит в неприкосновенности.)
curv
, то ли к
userval
. Если бы всегда использовать curv
,
то будет некорректно -- два нажатия подряд, БЕЗ прихода между ними
данных от сервера, использовали бы одно и то же значение поля
curv
.
15.04.2005: итак: с местом в nmagsys'е туго, чекбоксы там впихивать просто некуда, так что надлежит думать о других вариантах пометок. Есть пара вариантов:
Кроме того, насчет интерфейса активации/деактивации:
А не заменить ли отдельный тип виджета "...GPBL" на knobinfo-флажок "groupable"? Ведь группировабельность может требоваться не только LOGD_TEXT'у, но и в других "плавно изменяемых" (т.е. -- недискретных) виджетах -- в LOGD_SLIDER и LOGD_{DIAL,KNOB,GAUGE}.
(Ох, доунифицируемся мы текст, слайдер и ручки! :-)
27.05.2005: так, а почему дата не проставлена?! >:-| В любом случае -- идея реализована, так что -- "done".
Плюс -- еще дальнейшие усовершенствования.
05.05.2005: это потребует:
KNOB_B_IS_GPBL
.
Cdr{Save,Load}GrouplistMode()
поддержку
ключевого слова "grouped" и параметра "grpcoeff"
(сохраняемого при уставленном grpcoeffchg).
is_ingroup
-- чтоб он модифицировался при восстановлении
режима.
По нынешней поддержке text-widget'ом:
LOGD_GPBL
widdepinfo-флагом
"groupable".
05.05.2005: поехали.
Перевел Knobs_text_widget.c на psp, вставил параметр
"groupable", и ныне LOGD_TEXTGPBL
уже можно
выкидывать. Также сделан и флаг "grouped" для
пре-группировки.
Также введено поле double knobinfo_t.grpcoeff
, и
создан флажок KNOB_B_IS_GROUPABLE
(как водится, с
раздвижкой :-).
17.05.2005: продолжаем.
Введен knobinfo_t.grpcoeffchg
.
Сделано сохранение grpcoeff
и grouped
в
Cdr, и даже чтение, но считанное состояние "grouped" пока игнорируется
-- интерфейса для перещелкивания нет.
18.05.2005: н-начинаем з-завершать...
Недолго просуществовал LOGD_TEXTGPBL
-- он удален за
ненадобностью.
Групповые инкременты теперь учитывают поле grpcoeff
--
пока 1) для всех, кроме "инициатора"; 2) на коэффициент
просто домножается инкремент. Значение 0.0 приравнивается к 1.0.
Сделана возможность редактировать коэффициент.
20.05.2005: насчет того, что "нет интерфейса для
перещелкивания" -- ЕСТЬ!!! Есть метод PropsChg()
,
каковым и надлежит пользоваться.
20.05.2005: yes!!! Сделал -- работает!
Добавлен Knobs_text_widget.c::PropsChg_m()
, а
в ParseChanVals()
вставлено использование считанного
значения "grouped", а также вызов метода PropsChg()
--
скопировано с PropsOkCB()
.
Итого -- задача подружить группировабельность с режимами выполнена, помечаем раздел как "done".
А что если ввести специальный флажок элемента -- "sentinel", говорящий, что это -- "граница" иерархии элементов с точки зрения группоканалов? И что не надо эту границу пересекать ни вверх, при поиске начала иерархии, ни вниз, при обходе ее.
Это точно понадобится где-нибудь в magsys'е, по той же причине, что обозначена за 22.10.2004 -- элементы часто являются лишь способом расположения каналов на экране.
Кстати, это чем-то начинает напоминать поведение форм --
<FORM>
в HTML -- они создают "параллельную"
иерархию.
23.05.2005: да, а по-хорошему тогда надо бы делать и ГОРИЗОНТАЛЬНЫЕ барьеры, а не только по вертикали -- как раз в nmagsys это будет намного полезнее. Вот только сие реализовывать будет сложно -- барьер по окончанию-то несложно, а вот начало искать -- посложнее будет...
В пределе может захотеться делать формулы -- что менять не просто на коэффициент, а с учетом значений некоторых каналов, например,
delta = a + chan1*k1 + chan2*k2
Что ж -- формулы у нас есть, так что даже этот, наиболее фантастичный, вариант сделать можно. Надо будет только
userval
(или делать это через localregs?).
ci->physval
, но и
ci->"userval"
(ныне есть лишь usercode
) --
чтобы корректно отрабатывались формулы по ОТОБРАЖАЕМЫМ значениям, еще
ДО того, как вернется ответ-подтверждение от сервера. (Именно с этим
сейчас связано использование victim->userval
в
Knobs_internals.c::TextUpDownHandler()
.)
24.05.2013: есть две гипотезы:
Скорее всего, проблема во втором варианте. Или оба фактора сразу?
Второй -- в будущем явно надо как-то более корректно проверять; например, не сбрасывать "wasjustset (и userval_valid?)", пока age не покажет, что чтение драйвером произошло ПОСЛЕ модификации в клиенте.
27.02.2005: хронология была такой:
Напрашивающиеся выводы:
fd_p()
брал
именно оттуда).
Очевидно -- надо думать об обобщенном механизме, который бы подходил для всех этих трех случаев. Он, к тому же, скорее всего будет адекватен и для будущих аналогичных потребностей (а идеи об общности уже проскакивали -- при реализации в прообразе marcankoz, с прицелом на иные cankoz-интерфейсы).
И там же надо будет исправить некоторое количество "недодумок" -- в частности, сделать, чтобы "do_send" имел бы коды возврата не просто 0/+1/-1, а унифицированные с "условной постановкой в очередь".
Основная проблема на пути реализации такого обобщенного механизма/библиотеки -- разнородность клиентов, по-хорошему требующая механизмец типа C++'ных templates. Хотя -- в connlib'е ж мы выкрутились, хитрым сочетанием обычного C и препроцессора. Да и в CAN-реализации уже кое-какие наметки (чисто C'шные) имеются. Так что -- прорвемся :-)
Пусть у устройства есть отдельно команды "выставить значение в
A" и "выставить значение в B" плюс одна команда
"прочитать A и B" -- таков IMPAC IS10, у которого команда
"pa" возвращает море всякого. Тогда -- возможен сценарий, при
котором драйверу приходят запросы на запись в A и в B, он при этом, для
подтверждения, сразу после команды записи запросит и чтение, которое
вызовет ReturnChanGroup()
и для B.
НО!!! Ведь тем самым он в ответ на "запиши в B" вернет не подтверждение выставления запрошенного значения, а СТАРОЕ значение -- и оно будет сервером и клиентами воспринято как новое, а позже -- словно само по себе появится "опять изменившееся" значение. И это со всеми вытекающими последствиями, типа оранжевения не к месту, как было (есть :-) у глючных отокаревских CAN-драйверов.
Как решить эту проблему? Помечать при постановке в очередь запроса на запись этот канал как "не-следует-его-возвращать", т.е. блокировать? А когда тогда разблокировать, по какому принципу -- ставить в очереди вместе с запросом и пометку "как отправишь этот запрос -- разблокируй то-то"? Шибко уж это муторно, да и надо тогда не забывать при разных видах "сбросов" (очистка очереди, пропажа связи, etc.) надлежащим образом подобные пометки сбрасывать... Б-р-р-р!!!
20.04.2005: возникла еще одна "общая" потребность:
25.04.2009: насчет "Б-р-р-р!!!" -- надо ставить не пометки "блокировать", а пометки "вернуть", и ставить их надо в callback'е по отправке пакета. А при сбросе очереди -- нулить их (ну и при получении ответа -- тоже нулить :-)). Тогда всё становится ОЧЕНЬ просто.
(Идея-то эта оформилась в голове уже давным-давно, но почему-то до сих пор не была записана.)
05.05.2005: Для CAN-устройств это значение выходного регистра, для КШД-485 -- всякие serial'ы, для Impac IS10 -- аналогично. Плюс, в обязательном порядке -- все каналы записи, хранящиеся в устройстве.
Есть еще полурелигиозный-полуфилософский вопрос: при таком восстановлении связи -- очередь сбрасывать? Вот kshd485_drv.c сбрасывает ее прямо при ПОТЕРЕ связи; и там это к месту. А в остальных случаях? 06.02.2011@Снежинск-каземат-11: да, однозначно сбрасывать. Так сейчас везде и делаем.
27.04.2009: этот принцип соблюдается уже года три
-- с введением SetBlockStatus()
и нынешней (2-й) версии
CAN-драйверов, так что "done".
Заводим структуру -- "описатель очереди":
|
И некоторое количество методов:
|
"Поверх" же этой библиотеки, для ее использования в каждом
конкретном случае, наворачивается маленькая "конкретизующая"
библиотека-wrapper (как сейчас сделано в сервере -- *fdio.[ch]
поверх connlib'а), которая и реализует метод "sender", и
предоставляет своим клиентам "правильный" интерфейс -- с
ZZZelem_t*
вместо void*
.
22.06.2012: поскольку sendqlib наконец-то не только сделан, но уже и проверен, то "done".
23.02.2009: ... Неа, зад: если прилетает ДВА запроса на разные каналы, и 2-й еще до получения ответа на 1-й, то флажок 2 выставится сразу, и ответ на 1-й заодно вернет и 2-й, значение которого еще старое. И даже более того - по 1-му пакету флаг будет сброшен, и 2-й ответ проигнорируется...
И че ж теперь -- вводить в элементы очереди еще и "callbacks", вызываемые в момент отправки пакета (чтоб взводило флаг в нужный момент, а не раньше)?
Да, так и делаем! Еще некоторые детали будущей реализации:
oneshot
-- надо ли выкинуть элемент из очереди сразу после
отсылки.
timeout_duration
-- длительность ожидания перед
повторной посылкой (если ==0 -- то используется длительность по
умолчанию, указанная при инициализации очереди).
callback
+ privptr
--
функция-callback, дергаемая в момент отправки элемента. Вероятно, этой
функции надо также передавать флаг "элемент отправляется
впервые/перепосылка".
И, кстати, список клиентов sendqlib'а:
16.04.2005: идея эта была инспирирована разговором с Гусевым, который недоуменно потыкал в крейт магнитной коррекции, любопытствуя -- а почему, собственно, там все время горят (мерцают с высокой частотой) светодиоды L и Bus? И действительно -- а с чего я так неоптимально произвожу измерения -- ведь Липа позволяет заказать все каналы сразу, и как будет готова -- свистнет.
Вариант решения: драйвер должен ПОМНИТЬ, что его просили мерять, т.е. иметь список-набор каналов. Например, массивом, в котором ноликами обозначены "ненужные" каналы, а единичками -- запрошенные.
Наборов таких должно быть несколько (на примере Ц0612):
В случае с Липой Ц0612 надо будет находить не просто группы единичек, а еще и таких, у которых уставки (T,D) одинаковы.
09.11.2005: а вот хренушки -- НИКАК нельзя от этих CAN-АЦП попросить измерить канал #N: просто нету такой команды, да и сама идеология работы устройств подобного не допускает -- нету у них "одноразового" режима, не говоря уже о том, чтобы он корректно вклинивался в нормальную многоканальную работу.
c_next_wr_val[]
в
MarkRangeForIO()
и ShouldProcessChannel()
(видимо, последний и должен сохранять значение и уставлять
c_next_wr_val_pending[]
).
А в ReturnChan{Group,Set}()
-- соответственно, при
получении ответа на "повторно запрошенный для записи" канал надлежит
отправлять этот самый очередной запрос.
(Хи-хи, вот как раз уже и начинается этот внутрисерверный channel API :-)
c_wr_time[]
-- чтобы оно влияло на
FreshFlag()
, и уставлялось бы одновременно с отправкой
запроса на запись.
RegisterDriverWrFD()
и сопутствующую
инфраструктуру (и не забыть DeregisterDriverWrFD()
).
ReRequestData()
, использующий
ReMarkRangeForIO()
. Кстати, а оно ведь должно и большие
каналы пере-запрашивать (еще раз хи-хи -- опять про внутрисерверный
API, уже для bigc).
SetBlockStatus()
.
CXRF_WRONG_DEV
. 25.05.2005: сделано.
connect()
'ом
дескриптора в неблокирующееся состояние.)
И надо б иметь там флажок типа "is_suffering" по образу и подобию cda'шного -- чтобы оно ругалось только при переводе его в 1, а не постоянно. 28.06.2005: новый cm5307_drv окончательно протестирован -- работает.
20.05.2005: и еще пункт --
29.06.2005: поскольку давно (25.05.2005) стало ясно, что вышеозначенное требование -- бессмысленно (такие каналы и без того будут бордоветь/поболотневать), то помещаем его в <S>.
14.04.2006: поскольку все сделано еще прошлым летом, то -- "done".
- Юзер жмет в окошке кнопку [Reset].
- Запомнить текущее значение.
- Отправить в блок значение "0" (на экране же -- оставить то, что было!).
- Отправить куда-то еще команду "reset" (обычно -- просто, например, в канал УРа).
- Подождать некоторое время (несколько секунд).
- Отправить в блок старое, запомненное значение.
Если в течение этого процесса юзер начинает что-то делать -- обломить весь процесс нафиг.
Так вот -- похоже, все это прекрасно можно реализовать прямо на формульном языке cda. При этом используются механизмы:
CMD_GETTIME
-- для задержки в N секунд.
CMD_CASE
-- для проверки, что этот режим и потому надо
в читающей формуле отдавать не реально имеющееся в канале значение, а
то, что в регистре.
Что еще? Чего сейчас не хватает? Похоже, только условной операции записи -- как в канал, так и в регистр.
А может, все-таки сделать наконец именно ПРЕФИКСНУЮ архитектуру -- как в каких-то современных процессорах? Ее вариант в нашем случае будет таким: есть отдельно команда, могущая уставить "флаг пропуска", а ВСЕ команды реально делают свои действия только в случае, если этот флаг не уставлен. И по умолчанию этот флаг все время сброшен.
И -- кажется, при сим мы будем иметь-таки ситуацию, когда команда записи будет производиться из ЧИТАЮЩЕЙ формулы...
05.05.2005: насчет "команды записи из читающей формулы": похоже, придется ввести префиксную команду "игнорируй это нарушение". Некрасиво, конечно, но -- пока ничего лучше в голову не приходит, а хотелось бы.
А как реализовывать такие префиксные команды:
thisflags=nextflags; nextflags=0;
.
nextflags
, каковые и становятся эффективными для следующей
команды.
Подобная же архитектура понадобится и для "префиксов условного исполнения" (упомянутых за 18.04.2005).
18.10.2005: (@CERN, записано 19-10-2005 в бытовке левичевско-карнаевских) Как делать reset источникам (схема формулы-программы): read-формула делится на несколько "ветвей" ("after setting 1 - delay"; "resetting 1->0; ...), и иметь #define'ом вычисление условия для каждой из ветвей, и каждую из команд этой ветви префиксировать этим условием.?
Следствие 1: надо иметь префиксирующий опкод "разрешить запись из команды чтения".
Следствие 2: надо иметь префиксирующий опкод-проверку, уставляющий флаг выполнять/нет.
Следствие 3: префиксирующие команды не должны влиять на другие флаги (т.е., чтоб по ним не сбрасывалось) -- чтоб флаги аккумулировались.
05.12.2005: что ж, вроде все сделано...
OP_TEST='i'
("if") и
OP_ALLOW_W='w'
, плюс соответствующие
CMD_nnn
(естественно, CMD_ALLOW_W_I не существует
:-).
cda_exec_formula()
появились thisflags
и nextflags
, "сдвигающиеся" в начале команды как
предусмотрено проектом от 05-05-2005. И для них флажки --
FLAG_ALLOW_WRITE
и FLAG_SKIP_COMMAND
.
OP_SETP_I
и OP_SETREG
теперь
перед собственно записью проверяют, что
(thisflags&FLAG_SKIP_COMMAND)==0
.
OP_TEST
выполняет проверку на !=0
аналогично OP_CALCERR
, и при !=0 уставляет
FLAG_SKIP_COMMAND
, а при ==0 в принудительном порядке
сбрасывает его.
OP_ALLOW_W
просто мирно уставляет
FLAG_ALLOW_WRITE
.
OP_SETP_I
запись разрешается либо
при is_write
ЛИБО при уставленном
FLAG_ALLOW_WRITE
.
nextflags
делается не как
"nextflags=FLAG...", а как "nextflags=thisflags|FLAG...".
Теперь -- тестировать надо!
06.12.2005: кстати, ведь все эти "цепочки для выполнения reset'ов источникам" получаются весьма длинными, и, при том что большую часть времени они будут бездействовать, получается просто нерациональная трата процессорного времени впустую, на холостой их обсчет.
А что, если ввести еще опкод "BREAK", который бы работал
как OP_RET
, и исполнялся б условно -- если "цепочка"
бездействует?
07.12.2005: что ж -- ввел опкод
OP_BREAK=';'
(sic! :-) и соответствующие
CMD_BREAK{,_I}
. Вставить поддержку в cda оказалось крайне
просто -- обработка OP_RET*
там стоит хоть и ДО
switch(...)
, но уже ПОСЛЕ сдвига флагов и махинаций с
OP_imm
-- так что просто добавил туда еще одну
альтернативу, только условную -- по невыставленности
FLAG_SKIP_COMMAND
.
(Кстати, наличие обоих -- и OP_RETSEP
и
OP_BREAK
-- выглядит несколько избыточно, в принципе,
можно было бы RETSEP'у придать кондиционность. Но: во-первых, у них
все-таки разная смысловая нагрузка, а во-вторых -- RETSEP является лишь
временным хаком, который при полноценной поддержке colformula просто
исчезнет.)
Вот теперь -- действительно уже можно и тестировать, и начинать реализовывать кнопки Reset/On/Off для магнитной системы.
08.12.2005: йо-хо-хоу!!! Проверил на widgettest'е -- да, работает! Ну и намаялся ж я!!! :-)
Я там теста ради реализовал "программу", требовавшуюся для лебединого "reset'а" -- "Выставить в канал 1, подождать N секунд, выставить в тот же канал 0.".
Приведу тут весь "код" программы, записи для:
enum {chn = 0, r0 = 0}; static excmd_t w_proggie[] = { CMD_PUSH_I(1), CMD_SETP_I(chn), CMD_GETTIME, CMD_SETLCLREG_I(r0), CMD_RET }; static excmd_t r_proggie[] = { /* Is r0!=0? */ CMD_PUSH_I(0.0), CMD_SETLCLREGDEFVAL_I(r0), CMD_PUSH_I(1.0), CMD_PUSH_I(1.0), CMD_PUSH_I(0.0), CMD_GETLCLREG_I(r0), CMD_CASE, CMD_TEST, CMD_BREAK_I(0.0), CMD_PUSH_I(0.0), CMD_PUSH_I(0.0), CMD_PUSH_I(1.0), CMD_GETLCLREG_I(r0), CMD_ADD_I(4.0), CMD_GETTIME, CMD_SUB, // CMD_CASE, CMD_TEST, CMD_BREAK_I(CX_VALUE_DISABLED_MASK), CMD_PUSH_I(0), CMD_ALLOW_W, CMD_SETP_I(chn), CMD_PUSH_I(0), CMD_SETLCLREG_I(r0), CMD_RET_I(0.0) };
В общем -- работать-то оно работает, но как же тяжко на сим писать!!! За-дол-бешься!!! Даже мне очень сложно, а уж давать это кому другому -- так будет полный писец...
С одной стороны, правильный для ТАКИХ вещей подход -- это добавление возможности использовать sea.
С другой, а ведь можно добавить и команду ветвления
(OP_GOTO
? -- :-(... OP_BRANCH
?
-- Ok :-) -- также реагирующую на FLAG_SKIP_COMMAND
,
а "аргумент" ее (естественно, ТОЛЬКО immediate!) -- смещение (и
разрешать его только вниз), как на PDP-11. Естественно, "руками"
программировать с использованием этих "goto" -- задолбешься, но вот
если изготовить-таки транслятор с "нормального" языка (см. проект за
09-01-2004...12-01-2004), то там можно будет иметь и условные
конструкции.
29.01.2007: OP_GOTO_I
взял да
изготовил (='g'|OP_imm
), и его реазизацию тоже -- там
особо и говорить-то не о чем. И FLAG_SKIP_COMMAND
она,
естественно, также учитывает.
Но это, конечно -- исключительно на будущее. Вручную там ловить нечего.
01.02.2007: кстати, ведь для простейшего варианта
if (expr) КОМАНДА-ДЕЙСТВИЯ
(a-la старый Basic, без ":"), без составных команд ({...}), никакой
_GOTO реально не нужен -- хватит и _TEST.
Для записи: КОМАНДА-ДЕЙСТВИЯ -- это BREAK, SETP, SETREG/SETVAR, DEBUGP.
И именно по такому простейшему проекту и стоит реализовывать по крайней мере первую версию транслятора формул.
Кстати, а ведь команды сравнений (<, >, =, !=, <=, >=) прекрасно реализуются прямо сейчас при помощи _CASE по следующей схеме:
Единственное неудобство -- шибко уж длинно и громоздко. Но -- а какова альтернатива? Иметь трехоперандную команду "сравнение", которой первыми двумя операндами указывать A и B, а третьим -- непосредственным -- тип сравнения? Вообще, тоже вариант.
02.02.2007: угу, изготовил OP_CMP_I
,
берущую immediate-операнд как тип сравнения. Плюс, она работает в двух
вариантах -- либо просто возвращает 0/1, либо, при флажке
OP_ARG_CMP_TEST
, сразу выполняет по результату _TEST,
уставляя флаг.
Собственно макросы CMD_XXX имеют имена CMD_IF_op_TEST
-- чтобы пока что всегда только TEST-вариант.
Кстати, обычные сравнения -- это 6 штук, а оставшиеся два кода -- 000 и 111 -- это по смыслу, похоже, FALSE и TRUE.
14.06.2007: да, проверил -- работает. И уже используется в turnmag'е.
Хотя, конечно, нынешний набор операций -- CASE, TEST, CMP -- как-то странноват, и на будущее надобно разработать более стройную систему команд.
14.06.2007: пользоваться имевшимся OP_GOTO_I было решительно невозможно -- рассчитывать смещения руками и так полная задница, а еще с учетом возможности изменения состава команд между командой перехода и точкой назначения...
Так что перешел на другую парадигму: добавил код
OP_LABEL_I
(='l'|OP_imm
), имеющий параметром
строку -- он при регистрации формулы превращается в OP_NOP, а OP_GOTO_I
в исходнике также имеет параметром строку-метку -- которая и ищется в
исходнике (причем только ВНИЗ), и в displacement прописывается
расстояние до нее. При ненахождении метки издается ругательство на
stderr, а команда заменяется на OP_NOP.
Проверил, на том же turnmag'е -- ОЧЕНЬ удобно!
14.06.2007: тогда оставил мелкий ляп,
сказывающийся при компиляции с GCC >=3: после метки
END_GOTO_LABEL_SEARCH
не было точки с запятой. Добавил.
Надо обеспечить "подчиненным" драйверам ОБЫЧНЫЙ интерфейс, такой же, как предоставляет cxsd_driver.h.
01.07.2005: да, в
развитие той мысли -- идеи, родившиеся о вопросе "расшивки"
DriverBody()
(постановка проблемы там -- чтоб драйвлет мог
иметь более хитрую архитектуру, чем просто тупые "ответы на вопросы" о
чтении/записи/bigc -- например, для ИПП).
DriverBody()
в
"HandleInput()", вызываемую по select()
-готовности
дескриптора 0 на чтение. Она возвращает код -- -1:Fail, 0:Nothing,
1:Config, ?:Chanop, 2:Bigc, ..., и заполняет передаваемый ей union
данными из пакета.
Замечание: буфер этой функции предоставляется ее вызывателем
ReturnChanGroup()
и
ReturnBigc()
, с тем же списком параметров, что и в cxsd.
DriverBody()
становится просто
"клиентом" вышеописанной системы.
RegisterDriverTimeout()
с теми же параметрами, что в
сервере; работает с "0-м таймаутом" (будущего многотаймацтного
интерфейса).
RegisterDriverFD()
-- аналогично.
DoDriverDebug()
в
DoDriverLog()
-- сам бог велел :-)
magicid
.
Соответственно, если список пуст -- надо "засыпать", и заново активизироваться по приходу запроса.
И, возможно, здесь к месту будет то, что описано в теме "Алгоритм автоопределения набора измеряемых каналов".
Замечание 1: для ЦАП'оподобных и прочих "не-интегрирующих" блоков такая алгоритмика не требуется -- ведь работа их драйвера сводится к чтению/записи регистров, каковые операции происходят практически мгновенно.
Замечание 2: а вот для случаев типа ППИ и/или периферийных крейтов -- т.е., когда выполнение самого NAF'а не является "мгновенным" -- такой алгоритм, возможно, и не самый лучший. Тут уж можно выбирать из него и того, что сейчас используется для CAN-драйверов.
Решение на сегодня: Собственно, для решения проблемы ИПП достаточно будет "малой крови" -- первых трех пунктов. Остальные же "глобальности" оставим на будущее -- если когда-нибудь реально потребуется возиться с "не-slave" CAMAC-драйверами. Пока это -- нафиг не нужно.
16.05.2006: dbody 25.07.2013: сейчас всё сделано даже лучше -- унифицированный API, вместо dbody реализация через remdrvlet+remcxsd на основе cxscheduler, с LAM-via-pipe.
Короче: в отличие от CAN, где только посылки и легко сделать layer для селивановского CANGW, NAF'ы требуют и ответов, так что сделать "интерфейс" для ППИ -- не удастся, там вся структура драйверов должна радикально меняться. Так что, ППИ -- в топку.
25.07.2013: на свеженькой libremdrvlet/remcxsd с LAM-via-pipe сделаны драйверы c0609 и c0612 (почти близняшки) по модели из п.9, только чуток упрощённой. Отдельной регистрации таймаута они не делают, а просто имеют heartbeat с частотой 1Гц, в котором проверяют, не прошло ли с начала измерений шибко много времени, и если да -- то пинают останов с возвратом текущего канала с пометкой "IO_TIMEOUT" и переходом к следующему.
21.12.2013: конкретно c0609@linac1:10 (sukhphase)
проявляет странности: через какое-то время после запуска (неделя,
месяц) начинаются сплошные таймауты. Ручное выполнение NAF(A=*,F=26)
-- старт -- одноразово спасает ситуацию. Как будто бы в
StartMeasurement()
он это забывает исполнить -- что
странно (поскольку условием для сработки в TOU_CB является cur>=0, а
оно ставится ТОЛЬКО за несколько строк до F=26.
13.01.2014: разобрался в причине глюков c0609 (в c0612 они тоже должны б были быть):
ReadAndReturnMeasurement()
сброс cur=-1 стоял не вместе с
прочими "rqn--", а в цикле поиска следующего (в куске инициализации),
выполняемом условно, при rqn>0
.
StartMeasurement()
не
делалось, а только сплошь молотился возврат с CXRF_IO_TIMEOUT из
TOU_CB()
(ведь cur-то оставалось >=0).
Но как решить такой прикол -- уже неясно, поскольку получение прерывания отделено от его обработки, и в момент таймаута нет возможности гарантированно погасить LAM/IRQ.
Касается, кстати, и PCI'ного железа, типа ADC200ME.
14.01.2014: да, в c0612 глюки тоже были -- вылезло на магнитной системе линака. Там оно выражалось в том, что каналы бОльшую часть времени не мерились и стояли гусино-синие. А при натравливании туда клиента c0609 (они совместимы по карте) резко всё начинало мериться -- поскольку запрашивались все каналы, без дыр.
А если не реализовать -- то хотя бы заложиться на нее, например, заложить под нее номера каналов -- пусть пока незадействованные.
06.05.2005: причина этого "крика души": сегодня на C13 поменяли контроллер КШД-485 -- старый оглюкавел. А новый Цуканов выдал с кривыми настройками. Поменять-то настройки можно -- но лишь до следующего щелчка питанием. Пришлось прибегать к услугам Форточной машины.
А вот если бы драйвер позволял исполнять команду "пропиши в NVRAM" (например, по записи в некий командный канал) -- то все бы делалось просто, при помощи cx-setchan'а. Сейчас же это уже никак не сделать -- даже номера под такой канал не зарезервировано, увы...
14.05.2005: еще идея в ту же струю: для защиты от "случайной записи" в случае с КШД-485, например, можно требовать, чтобы в командный канал писалось значение, равное его serial'у (а можно -- и serial*(1+65536), для вящего выпендрежу).
15.02.2012: сейчас насчёт конкретно ЭТОЙ фичи понимание иное: подобного драйвер делать и не должен, это функционал для совсем отдельных утилит. В случае с адресом -- для setupkshd485.
22.08.2012: и всё же сейчас канал "SAVE_CONFIG" введён, а в утилиту программирования отправка команды 10 так и не добавлена. Причина 1: вломы было в утилиту пихать код оборачивания по PIV485. Причина 2: всё же вживую бывает полезно сделать "сохранение" для прочих настроек (ускорения, CONFIG_BYTE).
DisconnectBlock()
.
Подобное поведение хорошо для отлова проблем с железом либо кабелем, НО: при hotplug'е оно ОЧЕНЬ МЕШАЕТ, поскольку в момент включения подобные ляпсусы по кабелю вполне могут приходить. Как решать?
err_ctr++
"), и
отстреливаться только при превышении некоего порога -- например, 5...10
раз.
24.09.2009: по сегодняшнему опыту, с запиныванием всё того же КШД-485: драйвер ВООБЩЕ НЕ ДОЛЖЕН застреливаться. Наоборот -- он должен всячески стараться поддерживать свою жизнеспособность. В данном случае -- например, можно при переполнении входного буфера тупо его очищать. Если и пропадёт какой пакет -- не страшно, следующий же ping его добудет.
А "застреливаться" можно только при 100%-уверенности в неправильности конфигурации -- например, если по этому адресу обнаружено другое устройство.
04.02.2011@Снежинск-каземат-11: конкретно насчёт
kshd485_drv.c -- да, там все DisconnectBlock()
'и
расположены в fd_p()
, и кроме первого (по read()<=0) можно
все позаменять на "реинициализацию" -- сброс очереди, очистка запомненностей
(типа ioregs -- а они тут есть?),
SetBlockStatus(,DRVS_NOTREADY,)
,
SetBlockStatus(,DRVS_OPERATING,)
.
Несколькими часами позже: вроде сделал, функция
DoReset()
; надо будет проверить. (Но понаворочено там
"логики" работы с очередью -- у-у-у!)
06.02.2011@Снежинск-каземат-11: ну конечно же, с этим клятым RS232 такого простого решения оказалось недостаточно. Проблема -- как раз в "первом" (по read()<=0); там в один прелестный момент вернулся 0 -- т.е., как бы EOF (errno=0). Видимо, это оно так на выдёргивание кабеля отреагировало. Ну и чё делать?
Наверное, только закрывать этот дескриптор и открывать порт заново, да?
Сделал, будем посмотреть.
Но очевидно, что это -- лишь паллиативное решение; по-хорошему же надо реализовывать "стандартный" механизм "reconnect по таймауту через секунду", с соответствующим подавлением повторных сообщений об ошибках.
10.02.2011@Снежинск-каземат-11: неа, конкретно в случае с RS232 то "пере-открытие /dev/ttyS0" не особо помогло.
DoReset()
(забыл), и оно всё равно не работало -- писало раз в секунду о "Success"
(r=0, errno=0).
11.02.2011@Снежинск-каземат-11: тьфу ты, там всё было просто и тривиально -- результат дурацкого misdesign'а, сделанного еще при рождении драйвера (в 2003-м?).
ResetBlock(magicid)
--
и он давал результат!!!
read()
НЕ делалась проверка на переполнение буфера, и оно
просило 0 байт -- а по стандарту, "If count is zero, read() returns zero
and has no other results.". Так что это был никакой не EOF. ТщателнЕе
надо!!!
DoReset()
его сбрасывает, делая inbufused=0.
Итого -- локальная проблема решена, но вообще-то нефиг больше писать такие халтурнейшие драйвера.
07.09.2011: ёлки, ведь еще тогда в Снежинске проверил, всё работает -- так что "done".
SetControlValue()
, и произойдет отправка команды, что есть
плохо.
А ведь все кнопкоподобные knob-виджеты при записи ВСЕГДА отдают значение 1.0, а текущим является 0.0 (ибо оное возвращают командные каналы). Так что -- надобно исполнять "команды" ТОЛЬКО при значении, равном 1.
А еще такая же проблема имеется у синтетических командных каналов. Там также надо прерывать пишущую-формулу при значении !~=1.0.
28.02.2006: и вообще -- надо иметь такую константу в cx_types.h, чтобы была полная согласованность между всеми компонентами.
Да, изготовлено CX_VALUE_COMMAND
.
28.02.2006: А насчет "прерывать пишущую-формулу": сравнивать-то надо с допуском, т.е. "примерно равно 1". Самое разумное --
if (abs(v-CX_VALUE_COMMAND)>CX_VALUE_COMMAND/10) return;код для этого будет таким:
CMD_PUSH_I(0.0), CMD_PUSH_I(0.0), CMD_PUSH_I(1.0), CMD_QRYVAL, CMD_SUB_I(CX_VALUE_COMMAND), CMD_ABS, CMD_SUB_I(CX_VALUE_COMMAND/10.0), CMD_CASE, CMD_TEST, CMD_BREAK_I(0.0)
И, кстати, можно оное вообще в стандартный #define
засунуть.
Несколькими часами позже: проверил, вышеприведенный код правильно
исполняет свою задачу. И для унификации сделал в cxdata.h
макрос CMDMULTI_CHECK_IF_REAL_BUTTON_CLICK
с оным
содержимым.
(Ну да, в CXv4 должон быть интерфейс индивидуальной адресации параметров, но все же.)
Так что -- надо ввести некую управляемость этим, причем в двух аспектах:
Следствие -- adc200/333 надо переводить с abstract на "intelligent"-драйвер.
(Отдельный вопрос -- ЧТО возвращать по [СТОП]у -- массив нулей? А rflags какой?)
04.01.2008: собственно, это выходит вполне общедрайверский принцип -- надо мочь указывать, СКОЛЬКО ждать, а драйвер пусть инициализирует этот параметр каким-нибудь разумным default'ом.
И -- да, все такие драйверы должны быть не-абстрактными, чтобы корректно мерять интервалы времени, а не в каких-то условных циклах.
07.09.2011: во-первых, это всё уже реализовано во всех новых драйверах быстрых АЦП. Посему "done".
Во-вторых, с введением каналов SHOT и STOP параметр "время ожидания" фактически стал ненужным. Он, конечно, остался, но реально используется чисто для отладки.
20.12.2010@Снежинск-каземат-11: это вылезло на драйвере panov_ubs_src.c.
Правда, конкретно с УБСом отправка запроса GETDEVSTAT сразу после команды не помогла. Проблема в том, что у Панова статус меняется НЕ СРАЗУ, а через 20-60мс (поскольку он собирается от под-устройств, с которыми связь по внутреннему CAN).
Так что принцип уже ОБЩЕДЕВАЙСНЫЙ: при такой взаимозависимости статусов от прочих команд (т.е., биты статуса являются как бы "подтверждающим чтением" для команды "записи") уже само устройство должно по собственной инициативе высылать GETDEVSTAT (0xFE); в случае с УБСом -- после ответов от под-устройств на команды.
Панов так и сделал (точнее, у него теперь GETDEVSTAT отправляется сам при любом изменении статусных битов) -- и проблема исчезла.
10.02.2011@Снежинск-каземат-11: вопрос вылез при реализации начального указания уставок в kshd485_drv.c (детали см. в 201102-SNEZHINSK-ACTIVITY.txt за 09.02.2011).
Тут есть 2 крупных аспекта:
По-простому можно элементарно прямо в конце _init_b()
(т.е., когда драйвер уже полностью готов к работе) вызывать для всех
указанных собственный _rw_p()
-- тем самым имитируется запись
от юзера.
Хочется ведь максимально унифицированный (годящийся для всех видов драйверов и вариантов каналов), элегантный и расширябельный вариант, так? Тогда в принципе подходит такой проект, основанный на "в конце init_d() вызывать собственный _rw_p()":
_rw_p()
.
_init_b()
.
28.02.2014: возможно, решение ИЗНАЧАЛЬНОЙ задачи (чтоб при старте настройки в устройствах были прописаны) лежит совсем в другой области: аналогично TANGO, где персистентность значений обеспечивается встроенным компонентом сервера, или EPICS'ному autoSaveRestore.
Т.е., недурно бы какой-то такой функционал иметь в сервере, чтоб можно было ОДИН раз произвести настройки, и потом бы они при старте сами загружались и передавались бы драйверам как просто команды записи в каналы.
01.04.2014@Снежинск-каземат-11: вот вчера сделал возможность указывать в auxinfo умолчательные значения параметров измерения, типа NUMPTS -- чтоб на ЛИУ сразу при включении ставились "правильные" значения.
...а сегодня после дополнительных расспросов стало ясно, что это всё вовсе НЕ в чугуне отлито, что значения тех уставок в реальности зависят от времён, указанных в dl200 и по-хорошему надо б иметь кнопку "инициализация", которая бы рассчитывала NUMPTS и PTSOFS на основе значений в dl200. И, главное, что указание в auxinfo -- СЛАБОполезно.
24.02.2011: смысл -- надо, чтоб ВСЕ параметры СУ были б доступны через стандартные механизмы работы с каналами, ВСЕМ программам одновременно. Т.е., надо минимизировать количество "local state" в клиентах.
Конкретно для "запуск раз в N секунд" будет еще пара плюсов:
Конкретно в драйвере dl200me свободные каналы под это есть; nfrolov_d16 -- расширим (он пока не использовался); проблема только в cgvi8 -- там свободных каналов нет (разве что xcgvi8 сделать, по аналогии с xcac208).
21.06.2011: в dl200me это было реализовано еще в апреле, сейчас проверено -- работает (хотя в апрельском fast-варианте был баг, обусловленный инвертированием prog/ext между железом/каналом; пофиксен).
02.08.2011: это всё, конечно, хорошо, но не перевести ли эти времена с СЕКУНД на что-то более мелкое? И тогда вопрос -- на что?
Короче -- останавливаемся на МИКРОсекундах.
03.08.2011: а вот и нет -- МИЛЛИсекунды!
06.09.2011: итак:
Да и на остальные каналы (ЦАП, АЦП) можно выделять блоки, кратные 100.
Дополнительный плюс -- в том, что все карты сервера будет просто прикидывать, при кратности 100.
Единственный реальный минус -- лишний расход памяти и, теоретически, процессорного времени на такие каналы. Но и тем, и другим можно пренебречь: расход памяти смехотворно мал, а обработка таких каналов (вычитывание w-каналов) будет лишь однократно при старте сервера либо при реинициализации устройства (точнее -- при переходе устройства в состояние OPERATING).
08.09.2011: кстати, доп. бонус от расширения количества стандартных config-каналов до 50 -- можно будет часть из них сделать не w, а r. Это нужно, например, для каналов NUMCORR_V.
08.09.2011: в продолжение темы -- удобно будет вообще ПОЛНОСТЬЮ унифицировать все козачиные блоки, чтоб у ВСЕХ была карта одинакового размера. Тогда можно будет вообще прозрачно менять их.
29.02.2012: начинаем делать пока что для обновлённого xcdac20, только не 50:50, а 60:40. Стандартная карта располагается в kozdev_dv_i.h (#include'щем cankoz_common_drv_i.h). Разбивка такая:
09.11.2011: более подробно:
01.09.2012@Снежинск-Снежинка-307-вечер: все ЛОКАЛЬНЫЕ (с шиной) системы -- *test, все УДАЛЁННЫЕ (send/receive) -- *mon.
15.02.2013: также надо для "служебных" задач, типа смены адреса kshd485 (эта -- setupkshd485), сразу предусматривать утилитки, а не откладывать это "на потом".
16.04.2013: созрели -- начат/сделан cm5307_test.c.
25.10.2021: ещё в эту же степь: всем таким утилиткам,
могущим "отваливать по ошибке В/В", надо давать ключик "-K
"
(Keep going) -- НЕ отваливать по ошибке.
Код, занимающийся В/В по сети/CAN/RS485, надо обязательно снабжать включабельным логгингом пакетов.
15.02.2012: пусть даже это "неудобно", как было с kshd485/piv485lib, где
Ответы на те проблемы просты и прямолинейны --
DriverBody()
в
HandleInput()
и
ReturnChanGroup()
+ReturnBigc()
.
#define
'able buffer-size -- см. проект за 07.04.2005.
02.08.2011: поскольку задача уже давным-давно решена, то всё это помечаем "done".
chanaddr_t
из НОМЕРА в HANDLE -- он может
быть произвольным. Это и открывает возможность для PnP (пример
кодирования: старшие 16 бит -- ID устройства, младшие 16 -- номер
канала в устройстве).
Таким образом, при disconnect'е устройства можно будет всем клиентам сообщать "тю-тю", и они (cda) будут сбрасывать его handle в 0.
14.01.2006: кстати, так, в дополнение -- "упрочнение" этого механизма:
cda_physchanhandle_t==0
,
чтоб он также отображался на nop/null.
sid==0
.
27.04.2011@Снежинск-каземат-11: господи, какая фигня -- "Превращаем chanaddr_t из НОМЕРА в HANDLE"!
Давным-давно ж уже понятно (но, видимо, нигде явно не записано) -- при перечитывании БД сервер должен посылать всем клиентам уведомление о смене конфигурации, и они выполнят пере-резолвинг имён в chanaddr_t.
Вопрос: а нам такое не потребуется ли? Например -- в магнитной системе, где бывает и программный обсчет, и размагничивание, да еще мало ли где -- в любых программах с обратной связью (типа той же тренировки).
Вопрос-следствие-1: а как это технически реализовывать? Флаг
"было-изменено-программно" явно должен быть свойством
knobinfo_t
, и должен иметь смысл только при
is_rw!=0
. И Cdr'у это учитывать совсем несложно. А вот
КАК ПРОГРАММА БУДЕТ УКАЗЫВАТЬ "ИСКУССТВЕННОСТЬ" ИЗМЕНЕНИЙ? Особенно --
формулы (они-то -- completely cda!)!
Вопрос-следствие-2: какой цвет? Идея 1 (подсмотренная случайно, в момент вдохновения на каком-то сайте): бледно-зеленый. Он и не слишком бросается в глаза, но и заметен, и при этом легко отличим от всего остального. Например, как на Google Books -- "#dcf6db" (но лучше, для заметности -- чуть менее бледный). Идея 2 (старостенкина): цвет-Феди-младшего "#d8e3d5" -- практически такой же бледно-зеленый :-).
(После того, как попенял Гусеву, что он не придерживается единых стандартов цветности, и в разных его программах розовый означает разное)
14.01.2006: вообще-то -- уже есть ответы на оба пункта!
CMD_PRGLY_SETP*()
(Как? Отдельный вопрос... Если б их
унифицировать с просто SETP*...)
cda_prgsetphyschan{val,raw}()
, которые внутри будут
маппироваться все на тот же setphyschanvor()
, к коему
добавится еще один параметр.
16.01.2005: Приступаем. Протокол:
(Естественно, это все в основном касается именно PRGLYCHG, а NOTFOUND -- постольку-поскольку.)16.01.2006: начал, точнее -- сделал почти все, кроме собственно поддержки в cda :-) :
- cda.h
- введены
CDA_FLAG_PRGLYCHG=1<<18
иCDA_FLAG_NOTFOUND=1<<19
.Плюс -- все флаги откомментированы.
- cda.c
- поддержка в
cda_strrflag_{short,long}()
.- Knobs_types.h
- добавил
COLALARM_NOTFOUND
,COLALARM_PRGLYCHG
.Заодно сделал, что теперь каждое состояние на своей строчке, с комментарием.
- Knobs_internals.c
ChooseKnobColors()
знает про новую пару состояний.- Cdr.h
- ввел наконец-то
CDR_FLAG_CDR_MASK
.Плюс -- теперь все флаги содержат
//
-комментарии.- Cdr.c
- в
ChooseColorState()
введено уставление этих состояний по CDA-флагам; PRGLYCHG -- самое низкоприоритетное, NOTFOUND -- самое высокоприоритетное.А главное -- внесены "концептуальные" изменения в
ChooseStateRflags()
: теперь прямо вначале, если!ki->is_rw
илиKNOB_B_IS_BUTTON
, то "измененческие" флаги (OTHEROP, PRGLYCHG) срезаются. Из проверки на OTHEROP, соответственно, тестированиеis_rw
убрано.- Xh.h
- добавлены
XH_COLOR_BG_PRGLYCHG
иXH_COLOR_BG_NOTFOUND
.- Xh_colors.c
- соответственно, поддержка этих цветов.
- Chl_help.c
- добавил строчки об этих цветах, пока, правда, за //.
BTW, общее правило: определение константы (флага, состояния, ...) в .h-файле должно сопровождаться //-комментарием -- той же строкой, что отдает соответствующий str*long(). Именно это и было сделано.
26.01.2006: опкоды (и команды) ввел -- понадобился еще один символ, ну а что самое заметное/отличающее в словце "PRGLYCHG"? -- Правильно, буква 'y', вот ее и выбрал,
OP_PRGLY_SETP_I='y'|OP_imm
,OP_PRGLY_SETP_BYNAME_I='Y'|OP_imm
.27.01.2006: ой, елы!!! А зачем отдельные команды-то?! Ведь более адекватным будет префикс -- и реализация проще (не надо проверять на море опкодов, вычленяя оттуда флаг "программности"), и достаточно ОДНОГО опкода!
Так что -- вчерашние произведения выкинул, сделав взамен
OP_PRGLY='y'
.Далее -- в cda.c:
- Бедный
setphyschanvor()
теперь имеет просто море параметров:Былой
int raw
заменен наdbl_or_raw_t dbl_or_raw
, гдеради большей читабельности (а то в 0 и один можно уже запутаться), плюс чтобы дать gcc хотя бы потенциальную возможность отслеживать ошибки (хотя это и не Ada с ее "type xxx is new yyy", что сходу запрещало умолчательное преобразование между этими типами). Былое "typedef enum {VALUE_DBL, VALUE_RAW} dbl_or_raw_t
raw?
", естественно, сменено на "dbl_or_raw==VALUE_RAW?
".И добавлен параметр
usr_or_prg_t usr_or_prg
, гдеТеперь флагtypedef enum {CHG_BY_USR, CHG_BY_PRG} usr_or_prg_t
CDA_FLAG_PRGLYCHG
всегда срезается, вместе сCDA_FLAG_OTHEROP
, и уставляется приusr_or_prg==CHG_BY_PRG
.- Добавлены вызовы
cda_prgsetphyschanval()
иcda_prgsetphyschanval()
, отличающиеся от своих не-prg-аналогов указаниемCHG_BY_PRG
.- В
cda_execformula()
вставлена тривиальная реализацияOP_PRGLY
, плюс чувствительностьOP_SETP_I
на сей флаг (FLAG_PRGLY
).- В
DecodeData()
рядом с уставлениемCDA_FLAG_OTHEROP
добавлен сбросCDA_FLAG_PRGLYCHG
.Итого -- вроде все сделано, надо проверять.
30.01.2006: проверил -- работает. И уставляется этот флаг по префиксу
CMD_PRGLY
, и сбрасывается при обычной записи в канал либо при возникновении OTHEROP.И при (тестовом) уставлении из cda NOTFOUND -- все расцвечивается как надо.
30.01.2006: некоторые размышления о будущем NOTFOUND (в cda@CXv4):
physlist[]
первоначально уставятся в "никакой"
(то ли 0, то ли -1), и cda-флаги (chaninfo[].rflags
) им
будут по умолчанию ставиться в CDA_FLAG_NOTFOUND
.
physlist[]
и флаги NOTFOUND сбрасываются -- естественно,
именно для НАЙДЕННЫХ каналов.
PRGLYCHG же реализовано, так что помечаем раздел как "done".
30.01.2006: кстати -- было забыто, что
CDA_FLAG_NOTFOUND
должно числиться в
CDR_FLAG_SYSERR_MASK
. Добавил.
И, кстати, для полноты добавил проверку на
CDA_FLAG_NOTFOUND
в cx-starter'ово расцвечивание -- оно
все равно вот-вот переедет в Knobs.
31.01.2006: и еще -- ввел
CDR_FLAG_4WRONLY_MASK = CDA_FLAG_OTHEROP | CDA_FLAG_PRGLYCHG
для срезания "измененческих" флагов в только-читающих программах, типа
того же cx-starter'а. В каковой оное срезание (скорее -- на будущее) и
вставлено. Плюс -- срезание лишнего в ChooseStateRflags()
также переведено на эту константу.
CdrStrcolstateShort()
и
CdrStrcolstateLong()
, и перевод
Chl_knobprops.c::DisplayCurVals()
на них.
COLALARM_FIRST
,
COLALARM_LAST
.
16.01.2006: сделал, и
DisplayCurVals()
перевел.
"Done".
14.03.2006: дополнение: нужна функция для
обратной конвертации -- CdrName2colalarm(char*name)
.
Сделана.
Замечание: при ненахождении она возвращает -1
,
что, с одной стороны, не совсем корректно -- т.к. оная не принадлежит к
пространству значений типа colalarm_t
, а с другой --
позволяет максимально просто детектировать проблемы.
Надо изготовить (в misc_macros.h) general-макрос для *short и *long:
MACNAME(typename, min, max, table, short_miss_format, long_miss_format, short_func_name, long_func_name, ???)Плюс -- макросы DECLARE_?_TABLE (="static struct {val, short_text, long_text}") и ?_TABLE_LINE(v,s,l).
ВОПРОС: а как быть с "log2" -- т.е., когда у нас отсутствуют линейные константы, а есть только уже готовые битовые маски?
ОТВЕТ: добавить макросу MACNAME() параметр "
is_shift
",А к вопросу, как это все обозвать -- конечно, ENUM! Т.е.,
DEFINE_ENUM_STRFUNCS
,DECLARE_ENUM_STRTABLE
,DEFINE_ENUM_STRTABLELINE
.
Уж с такой-то вещью можно будет спокойно с чистой совестью 1) полноценно добавлять все будущие подобные наборы констант и флагов; 2) выкинуть нынешнюю разнородную реализацию str-функций.
16.01.2006: Кстати -- а откуда вообще взялась концепция short/long?
19.01.2006: Нашел -- запись в bigfile.html за 23-11-2003, ключевое слово "cx_strrflag_short".
19.01.2006: кстати, а ведь кроме линейного и log2-наборов могут быть и просто произвольные -- просто абстрактный список констант.
Что ж, решается это несложно -- вместо "is_shift
"
делаем параметр "enum_kind
", который может быть одним из
{ENUM_LINEAR, ENUM_SHIFT, ENUM_RANDOM}
.
ЗАМЕЧАНИЕ: в любом случае, все эти различия между linear/shift/random -- это пока слабоважно, скорее на будущее.
26.01.2006: начал населять этим
misc_macros.h. "Почти все сделал", но пока что заткнулся на
собственно "цикле поиска" -- как именно его реализовывать, чтобы
подходил хотя бы для случаев LINEAR и SHIFT? И какие обязательства
(типа корректного указания параметра min
) должна принимать
на себя библиотека-пользователь?
14.03.2006: а еще -- ВСЕГДА надо иметь функцию для обратной трансляции, строка->число! И она также должна создаваться теми макросами. Вот это-то и будет реальное "заведение констант как «граждан первого класса»".
program_invocation_short_name
.
reporterror()
", которая всегда и используется, безо
всяких там "fprintf(stderr,...)".
И эта функция должна выдавать сообщение в формате
CURTIME: PROGNAME[/SUBSYSNAME]: LIBNAME: ТЕКСТ
10.03.2006: что для этого нужно:
04.03.2006: Ход мыслей таков:
- Надобно уметь указывать текст help'а для chl-like программ. Как? В «секции» "атрибутов"!
- Секцию "атрибутов" можно ввести уже сейчас -- в конце subsysdescr'а.
- Структура -- таблица {"метка","значение"}, конец -- {NULL}.
- А уж в CXv4 все делать так (в т.ч. -- саму группировку, sea-подпрограммы, icon, etc.).
- "ShowHelp()'у" передавать строку вида
т.е. -- набор пар ТИП:ДАННЫЕ, разделенных '\v', где возможные PARTYPE и их данные --"PARTTYPE:data\v" "..." "\0"И тогда оное можно будет указывать прямо в .c/БД/где-охота-еще."KEYS:key\fcomment\?" {~} "\v" "TEXT:...\v" "COLORS:fg\fbg\flabel\ftext\?" {~} "\v"(Плюс -- если вся "data" секции состоит из единственного слова "default", то берется стандартная, builtin, секция.)
(И еще: вместо "fg\fbg" можно будет указывать "@colstate".)
- Понадобится "XhGetColorByName()". 09.03.2006: сделано.
- А для "редактора" -- еще и "XhStrColindex(idx)" и "XhColorCount()". 09.03.2006: также сделано.
И еще -- для "@colstate" понадобится "Name2Colstate()". 14.03.2006: сделана.
- Toolbar'ы -- тоже секцией, как и, возможно, меню.
04.03.2006: а первоначальный-то план, отвергнутый в пользу вышеприведенного (впитавшего некоторые решения из первоначального), был таков:
02.03.2006: Для того, чтобы удобно уметь вставлять help-окна со специфичным содержанием во всякие ipp, vacclient, ausacc etc., создаем Chl_help.c::
ChlShowHelpWindow()
, такой, что
- Ему передается список "компонентов" с классификаторами ???_COLORS, ???_KEYS, ???_TEXT (последний -- как arappable-текст в cx-starter'е).
- В секции "colors" положительными числами указываются цветовые состояния -- +COLALARM_*, отрицательными указываются Xh-цвета -- -XH_COLOR_*.
- Если поле "заголовок" компонента непусто, то создается XmFrame с label'ом, иначе -- компонент помещается напрямую в grid.
- Иметь default_COLORS и default_KEYS, которые вставляют "стандартные" Chl-комментарии (как сейчас).
Этот план даже начал реализовываться, с прицелом, чтобы потом
нынешний ChlShowHelp()
заменить на него, но -- стало ясно,
что далать надо именно СО СТРОКАМИ, так что первоначальный проект
закрывается.
(С самого появления идеи плагин-архитектуры (в январе 2005?) сей вопрос как-то молча обходился. Видимо, подразумевалось то ли использовании отдельных программ, регистрирующих свои компоненты, то ли некоей директории-репозитария, a-la /usr/lib/mozilla*/plugins/ или {/usr/lib/gimp*,.gimp*}/plug-ins/, откуда б просто сходу всасывались все .so-файлы.)
07.03.2006: собственно, раз ручки "ссылаются" на отображаторы, каковые могут располагаться в неких "динамических библиотеках", то можно прямо в группировке иметь отдельное поле со "списком библиотек" -- как исполняемые файлы содержат список требуемых .so'шек.
Минус такого подхода -- ?( затрудняется/исключается возможность запускать программы "в разных видах", как это сейчас можно делать с вакуумом, по разному выглядещим при использовании chlclient и vacclient ?).
06.08.2006: комментарии по теме:
И, кстати, в конце концов ничто не мешает ввести в будущем knobber/pult ключик, который бы подавлял подгрузку указанных в описании подсистемы plugin-библиотек.
07.03.2006: Имеется некоторая "проблема", вызванная тем, что много-много лет назад в качестве MULTISTRING_SEPARATOR был избран ^K (VT) -- '\v'. Тогда-то это казалось естественным, но затем понадобился еще один символ ("опции"), на роль которого пошел ^L (FF) -- '\f'. Это уже было плохо -- ибо "серьезность" этих control-символов в смысле печати как раз в обратной последовательности.
Сейчас же требуется некоторая ИЕРАРХИЯ символов -- разделитель секций, разделитель записей, разделитель полей, разделитель подполей/опций. (А еще все слегка осложняется тем, что '\n' имеет право быть частью текста -- как обычный символ -- и посему не может использоваться как разделитель.) В принципе-то на роль этого ансамбля вполне идут ^L/FF -- секции, ^K/VT -- записи, ^I/HT -- поля, ^H/BS -- опции. Вот что тут неудобно -- ^I/HT уж шибко невидим.
А ведь еще в ASCII имелось готовое решение этой проблемы -- ^\/FS, ^]/GS, ^^/RS, ^_/US. Проблема лишь в том, что оные символы НЕ имеют \-последовательностей в Си, и должны тогда вводиться кодами -- \x1C-\x1F.
09.03.2006: в общем, хоть и хочется использовать FS,GS,RS,US, но -- придется остаться с поддерживаемыми в Си \-символами. И -- надо будет в CXv4 повсеместно (в т.ч. в multistrings) переходить на иерархию \f,\v,\t,\b.
12.03.2006: да, стало совершенно очевидно, что надобно вводить стандартные определения
CX_GS_C='\f', _GS_S "\f" // group separator CX_RS_C='\v', _RS_S "\v" // record separator CX_US_C='\t', _US_S "\t" // unit separator CX_SS_C='\b', _SS_C "\b" // subfield separator
14.03.2006: так что -- вставил оные в cx_types.h и в 4cx/.../cx.h.
04.07.2009: в 4cx/-то оно уже сделано правильно и
используется, а в cx/ -- глубокого смысла перепахивать нету. Плюс,
важна скорее унификация этих версий по библиотекам. Так что -- оставил
только переделанные CX_US_C='\v'
и
CX_SS_C='\f'
, зато они теперь используются в качестве
определений для MULTISTRING_SEPARATOR
и
MULTISTRING_OPTION_SEPARATOR
.
Подробности/мотивацию см. в bigfile-0002.html за сегодня, а этот раздел ставим как "obsolete".
02.08.2006: во-первых, первичнее -- rownames, а не colnames, как сейчас (потому что столбцы -- штука опциональная и вторичная, имеющая смысл только для ELEM_MULTICOL).
Во-вторых, нынешний подход "метка берется из {row,col}names, если она "?" -- то от первого knob'а" -- неудобен. И тем, что для стандартного варианта "метку брать от knob'а" приходится писать нужное количество "?\", и тем, что метку "?" сделать никак нельзя (а для колонок -- хотелось, и не раз!). Так что его слезует изменить:
Дело в том, что там надо отображать числа в каких-нибудь осмысленных временнЫх единицах, но разброс уж дюже большой: от 100ns до 214.7s. В результате приходится отображать 3 знака до запятой, и 7 после ("%11.7f"). Но это некрасиво -- т.к. "младшим разрядом" являются сотни наносекунд. Значит, вроде как правильнее использовать "%13.9f" -- но это еще более некрасиво.
А причина-то всего этого в том, что отображаемые значения имеют не равномерный диапазон, а 16-битовые с дополнительными 4 битами внешнего 2-логарифмического множителя ("прескалера").
Так что по-хорошему и отображать бы их "по-умному", т.е., a-la %e/%g, и соответствующим образом подписывать префикс единиц...
31.03.2006: Ну и как это сделать? Чтобы система автоматом выбирала -- при 1.0 показывать "1.0s", при 0.100 -- "100ms", 0.000100 -- "100us", и т.д.?
02.03.2009: и еще потребность в ту же степь -- уметь отображать время в человеко-читаемом виде, т.е., например, "1м35с", "2ч15м12с", ну или хотя бы "1:35" и "2:15:12". Спрашивется -- КАК это можно было бы сделать, а?
Ну типа проект мог бы быть таким: вводим собственный, нестандартный
формат "%t", который форматируем сами -- как
("%d:%02d:%02d",secs/3600,(secs/60)%60,secs%60)
. При этом
ширина поля определяет, надо ли включать часы и минуты (>3 --
+минуты, >6 -- +часы), а флажок "0" указывает, надо ли
включать нулевые часы (secs<3600) и минуты (secs<60).
Минусами такой штуки было бы:
GetTextColumns()
"хитро" считать ширину поля; хотя
это-то самое простое.
sprintf()
'а -- либо обычный, либо, при "%t",
вышеописанный хитрый.
(Уж после этого-то точно будет ну совсем не проблемой туда же упхать и поддержку "логарифмичных" форматов, типа указанных в начале этого раздела за 31-03-2006.)
Отображать ДВУМЯ полями, расположенными друг за другом: число ("мантисса") и readonly-селектор с единицами ("экспонента" -- ns/us/ms/s).
Детали:
24.03.2009: а вот контр-идея: ведь в
datatreeP.h в dataknob_knob_data_t
ВСЕГДА есть
поле list
. Так вот -- а что, если сделать такой
специальный ручк, который как бы текстовое поле, но чтоб он сам
производил такой хитрый процессинг числа, деля его на "числовую" и
"единицевую" части. Только надо ж ему как-то указывать "числовой
фактор" -- шаг между единицами (который обычно 3, но бывают-то и
нелинейности -- сантиметры(-2), дециметры(-1), метры(0),
километры (+3)).
А вообще -- подумать, что ли, об общей более навороченной системе отображаемых единиц, a-la LabVIEW? Неа, в качестве мечтаний -- может быть, но в реальности в наших масштабах потребность в ней минимальна, и не окупит трудозатрат на её разработку и поддержку.
Ну и -- вот как такое можно сделать? (И -- как (каким цветом?) выделять?)
04.04.2006: после обеда -- в принципе, делалось бы легко. Как:
COLALARM_FIRSTALARM
.
LOGD_ORDALARM
(ORDered).
KNOB_B_IS_ORDALARM
.
eleminfo_t.alarm_first
(тип --
Knob
), которое:
alarm_on==0
к !=0.
alarm_on==0
.
ChooseColorState()
смотрим, что если
"IS_ORDALARM и выставлен бит VALUE_LIT_MASK, и alarm_first==ki",
то colstate=COLALARM_FIRSTALARM.
А "бы" -- потому, что у нас "SetAlarm()" скачет по дереву вверх, и
есть некий вопрос, КУДА делать "???->alarm_first=ki": тому, кто
непосредственный "содержатель" виновника, либо тому, чей
ShowAlarm_m()
дергается.
Плюсы и минусы есть у обоих вариантов. При ki->uplink: +: все просто; -: "первость" будет ограничена только непосредственным элементом. При "topmost(ki)": +: "первость" будет работать в пределах всей содержащей виновника иерархии; -: проверка "alarm_first==ki" сильно усложняется.
И, в качестве "мелкой сопутствующей проблемы" -- все-таки не очень
красиво сейчас сделана поддержка alarm'ов: сходу НЕ удается все
махинации инкапсулировать в Cdr. Не говоря уж о том, что придется
к _k_ealarm_f
добавлять параметр "initiator".
04.04.2006: а вот Старостенко заявляет, что нафиг это все не нужно, и, мол, надо просто уметь "заглянуть назад" (то ли самописцем, то ли log-файлом).
06.04.2006: свел Старостенко с Клющевым, в результате сошлись, что действительно от такой фичи для вакуума толку совершенно никакого.
Так что -- "withdrawn", а если вдруг возникнет подобная потребность, то готовый проект реализации -- есть выше.
И тут-то я вспомнил о существовании некоей Microline Widget Library от давно покойной фирмы NeuronData. Эта библиотека содержала, в частности, виджет "дерево", и доступна в исходниках ранних версий Mozilla и нынешнем NEdit. Я даже когда-то с год назад пробовал ее откомпилировать, но там какие-то сложности с Makefile'ами -- они вроде как заточены под другую иерархию директорий, коей нету (и в Mozilla тоже), и попросту не работают.
18.04.2006: что ж -- взял библиотеку из NEdit'а. Отдельная сборка действительно не работает, а вот nedit'овский "make linux" часть библиотеки собрал. "Часть" -- только контейнер-закладки, поскольку только оно нужно самому NEdit'у (кстати, а tooltips он берет LessTif'овские). Пришлось компилировать и библиотеку, и директорию examples/ ручками. В конечном итоге вполне заработало.
И -- йо-о-оу!!! А вещь-то это действительно классная. Сделано просто ИСКЛЮЧИТЕЛЬНО качественно, работает великолепно, и демонстрашки в том числе. Коммерческий проект, блин... Summary наблюденного:
18.04.2006: хо, а ведь можно сделать функциональное подобие дерева при помощи вложенных сеток! Т.е., каждая "ветка-уровень" -- это сетка из 1+n клеток по вертикали: одна клетка на пиктограмму+заголовок, а остальные -- содержимое; при сворачивании просто делаем unmanage всем, кроме заголовка; каждое "содержимое" может быть либо "листом", либо такой же веткой. Или даже "круче": каждый уровень -- ДВЕ клетки по вертикали, заголовок и содержимое.
Реализуется это весьма просто, причем прямо хоть сейчас -- вся инфраструктура уже есть, а надо лишь создать компонент-контейнер.
Проблемы же такого подхода -- следуют из "разрозненности", "необъединенности" экземпляров компонентов: во-первых, между разными ветками не будет горизонтального выравнивания, а во-вторых, как следствие, не будет и линий.
19.04.2006: сегодня потратил-таки немного времени, и вытащил директорию Microline в ~/work/Microline, заменив тамошние долбанутые неработающие Makefile'ы на свои, нормальные, которые все собирают. Заодно пришлось "потрогать" файлы .uil и .uih, в которых имелись слишком длинные строки комментария -- компилятор uil выдавал на них ошибки.
20.04.2006: по делу: Старостенко с картинкой-схемой показал, что именно нужно для системы синхронизации. Там НЕ требуется сворачивание, а нужны именно СВЯЗИ, и чтобы можно визуально "задисэйблить" одним переключателем то, что висит в одной ветке. Межветочное выравнивание -- абсолютно неактуально.
20.04.2006: мы с Гусевым лохИ -- оба! XmOutline/XmTree, Motif-2.2+. Хотя и тормозновата.
XmNparentNode
-- его "владелец" в древесной иерархии,
каковая полностью отдельна от обычного parent/child-relationship.
20.04.2006: короче -- просто из вредности решил-таки сделать контейнерный компонент "дерево" при помощи вложенных сеток. Начал делать...
Кстати, будет некоторая проблема с "отключением" ("дисэйбленьем"): ведь "заголовок"-то -- обеспечивается ЭЛЕМЕНТОМ. И что -- получается, что элемент заодно должен также быть и каналом, дабы имитировать LOGD_ONOFF, и в состоянии оного "off" делать disable содержимому элемента?
24.04.2006: ну сделал. Практически по тому, "очевидному" проекту. А толку-то!!! Оно вроде как все правильно отображает, но... С клавиатуры -- не ходится!!! :-( Пес его знает, почему -- но вот не ходится, и все тут... Причем не только по иерархии, но и внутри одной сетки тоже!!! Черт знает, чья это вина -- Motif'а или Саши Антонова, но как-то и непонятно, и особого желания разбираться пока нет, хотя и стоило бы...
И -- да, на пробу сделанное отключение (вместо метки поставлен XmToggleButton) вполне работает, "отключая" нужную часть иерархии.
24.04.2006: (уже выходя из института, мило
пообщавшись с той дЕвицей на сигнализации): насчет "КАК указывать
иерархию" (при использовании XmOutline) -- да
просто, вот ровно так же, как и сейчас, просто пусть
компонент-контейнер идет не просто по своему
eleminfo_t.content
, а и "бредет по дереву" (с помощью
некоей рекуррентной функции), и создает компоненты, уставляя их
indicator
'ам в качестве XmNparentNode
наджежащий виджет.
Будет лишь одна проблема: по дереву-то надо идти не "безусловно, до
упора" -- а нырять в подэлементы только тогда, когда этот подэлемент
имеет тот же самый тип (чтоб не мешать внутри дерева создать, например,
ELEM_MULTICOL). Вопрос: как это реализовать в CXv4, где поле
look
будет не числом, а comma-separated-строкой? Ответ:
надо будет иметь функцию вроде "look2vmt(look)", и ее результат
сравнивать с указателем на собственную VMT. 14.07.2007: опять
пришел в голову этот вопрос, и опять придумал тот же самый ответ.
Совершенно независимо :-)
25.04.2006: некоторые идеи/план действий, раз уж я так в этом завяз :-):
27.04.2006: начал. Итак:
Кстати1: попробовал также вернуть XmNtraversalOn=True для DrawingArea -- не помогло.
Кстати2: оно не то что "не ходится" -- оно и НЕ ВВОДИТСЯ! Т.е., почему-то ВЕСЬ клавиатурный ввод уходит "в никуда", а самому виджету это и вовсе не поступает.
30.04.2006: придумал, как делать "disable" при использовании XmTree/XmOutline: точно так же, как и создавать -- т.е., идти по дереву, и если "обрабатываемый" knob -- это подэлемент, и с тем же типом, что наш, то "нырять" в него и проделывать то же самое.
01.05.2006: ага, щас -- там же надо учитывать, что задисэйбленность могла быть и локальная -- и тогда мы "по ошибке" вложенные компоненты "раз-дисэйблим"... :-(
Сделал так -- все равно не вполне корректно: надо все же имитировать протокол "is_ancestor_sensitive", а то сейчас НЕ делается disable переключалке вложенных веток, и ею можно дергать туда/обратно, а еще при ссылке на канал будет то же самое...
Да, и, кстати --
Кстати, по чистой случайности XmTree/XmOutline уставляют между
ручками нужные интервалы в пикселах, чего grid-based и form-based
компоненты НЕ делают. А все оттого, что эти параметры -- НЕ публичные,
а зашиты прямо в Chl_gui.c константами "2
".
Надо будет в CXv4 это все сделать частью "публичного
интерфейса" libKnobs.
А вот насчет "элемент заодно должен также быть и каналом" -- черт его знает, как-то не могу пока придти к согласию с самим собой.
С одной стороны -- это было бы "хорошим общим решением" (показателен пример Motif'а, где manager'ы НЕ имеют свойств обычных (primitive) виджетов, и оттого происходят некоторые проблемы -- типа невозможности иметь focus-outline вокруг XmDrawingArea).
С другой же -- это будет изрядно мерзкое забарахление, напрочь уничтожающее всю элегантность деления на container/noop/knob/bigc/user...
В идеале -- придумать бы какое-нибудь "стороннее" решение, чтоб как-то ДОПОЛНИТЕЛЬНЫЙ knob мог "управлять" задисэйбленностью контейнера!
03.05.2006: точно -- ведь, к примеру, XmFrame-то не сам рисует надпись, а у него есть "сервисный" виджет-метка, которого он ставит поверх рамки. Так и тут -- можно, например, где-то в опциях указывать имя управляющего knob'а, и пусть "дерево" поглядывает на его значение.
16.05.2006: дурень!!! Не нужно этого, все намного проще -- см. комментарий за сегодня. Так что -- "withdrawn".
P.S. Ну вот потратил я почти полмесяца своей жизни на всю эту хрень -- а нафига?! Понятно, конечно, что и узнал много нового, да и багрепортов понаписал, но все же -- занимался б лучше реально нужными СЕБЕ вещами!!!
03.05.2006: вообще-то проделанная работа оказалась реально полезной/нужной/востребованной, по двум причинам:
10.05.2006: в продолжение
Насчет "потери клавиатурных событий":
HookPropsWindow()
(правая кнопка мыши,
кстати, тоже не работала!) -- так после закомментировывания установки
тамошнего KbdHandler()
'а клавиатура "включилась"!!!
Т.е., даже если выкинуть установку Mouse3Handler()
и
TextUpDownHandler()
-- все равно ввод "пропадает"
11.05.2006: стал разбираться с пропаданием --
проблема явно концентрировалась в KbdHandler()
'е. И
нашел, довольно быстро...
Там имеется eleminfo_t *ei=ki->uplink
, и далее
код имел такой вид:
if (ei != NULL && ei->emlink!= NULL) { [[проверки на кнопки и реакция на них]] else return; } *continue_to_dispatch = False;
Т.е., раньше-то все было отлично, поскольку единственным типом
элементов были ELEM_SINGLECOL/ELEM_MULTICOL, а вот во всех
tree-подобных и в tabstack'е -- "протокол элементов" пока не
неализован, условие не отрабатывало, и всегда делалось
*continue_to_dispatch=False
, вот события и убивались.
Идиот!!! :-)
В общем -- ура-ура-ура!!!, ошибка найдена и исправлена, и нефиг было на Motif бочку катить :-).
15.05.2006: повставлял во всех elem-plugin'ах
Но толку-то -- сие работает только тогда, когда хоть где-то "вверху" есть обычный, Chl_gui.c'ный элемент, который и предоставляет сии методы.if (e->uplink != NULL) e->emlink = e->uplink->emlink;
Так что -- либо втаскивать эти knobplugin'ы в Chl_gui.c, либо делать некий "стандартный" API, либо -- что самое лучшее -- как-то реорганизовывать этот API: например, чтобы Chl заполнял некое "вакантное место" (hook) "показать свойства knob'а".
13.12.2009: итого:
KnobsKbdHandler()
теперь
*continue_to_dispatch=False
делается ТОЛЬКО в случае
обработки кнопки.
Так что -- "done", давно пора.
27.08.2013: сейчас потребность в использовании элемента-дерева возвращается, поскольку надо забирать у Гусева rfsyn. Дополнения по сравнению с тогдашним:
Все намного проще -- надо считать ПЕРВЫЙ из вложенных в элемент каналов -- как раз "выключателем".
Чтобы сие реализовать, требуется 3 шага:
knobs_emethods_t
методом "пришли данные" --
аналог knobs_vmt_t.SetValue()
, который вызывался бы Cdr'ом
сразу после CdrProcessEleminfo()
для содержимого.
И уж в этом методе в зависимости от текущего значения в 1-й ручке менять sensitivity.
22.05.2006: в продолжение -- пара мыслей по теме:
ncols
(в v4 -- param1) для указания, сколько именно
knob'ов надлежит поместить "наверх" -- они укладываются рядышком в
форму, каковая и уставляется "корнем".
23.12.2010@Снежинск-каземат-11: да вот нифига -- под ncols,nflrs заняты param1,param2, а param3 свободен.
LOGC_VIC
, с возможностью желтеть в отключенном состоянии.
Вот им можно и пользоваться.
knobs_emethods_t.NewData()
, то можно в нем вычитывать
colstate knob'а-"выключателя" и дублировать его в свой background (это,
конечно, подходит только для form/grid-based-вариантов).
26.02.2007: и еще по теме:
13.12.2009: поскольку придумано более общее решение (см. следующий раздел), решающее также и эту проблему, то помечаем как "withdrawn".
13.12.2009: детали/идеи:
Аттачится метка слева к форме, а справа -- либо к форме, либо к ближайшему соседу. "Штучки" же аттачатся справа налево: к правому краю самый крайний, следующие -- к нему; кнопки -- справа, каналы -- перед ними.
*TitleClickHandler()
может
понадобиться аккуратность. Но в нынешней реализации неактуально --
там используется ссылка на сам элемент (v2) либо его
container
(v4).
23.12.2010@Снежинск-каземат-11: очевидно, что никакой firstattitle не нужен, а количество для-в-заголовок указывается в param3.
firstattitle
-- начинать расстановку с F-го канала, а
число считать на F меньшим.
Резоны:
*CreateContainer()
'ом.
*CreateContainer()
это очевидным
образом и вешается. На крайняк -- можно будет кроме
populator
'а передавать также и "attitle_populator".
P.S. Да, собственно, зачем всё это НЫНЧЕ нужно -- для управления пановскими модуляторами: там он хочет, чтобы управление дисэйблилось, если УБС не-online.
27.03.2011@Снежинск-каземат-11: кстати, очень полезна возможность размещения ручек в заголовке была б для еще одной фичи: если сделать ELEM_SUBMENU, то можно было бы в заголовках стоек размещать "локальные меню" с командами типа "Включить все УБСы", "Выключить все", и т.д. -- аналог "общего управления", но только для конкретной стойки.
19.04.2011@Снежинск-каземат-11: за вчера-сегодня эта концепция реализована (хоть и чуток по-другому), посему "done".
Смысл: когда температура входной воды становится слишком большой, надо начинать бибикать, и оператор должен принять меры. НО! Остывать то эти кубометры воды будут долго, и бибикать все это время -- трындец.
Потому -- надобно, чтобы оператор имел возможность бибиканье заглушить. Видятся такие варианты:
Реализовывать же любой из этих вариантов можно двумя способами:
Самоотключение делается при помощи CMD_GETTIME
и
сохранения в регистре этого значения "плюс час".
Отключение при переходе -- просто отключаем при переходе ALARM->NO_ALARM.
XmNvalueChangedCallback
-- для readonly-лампочек
восстанавливаем там правильное значение; так вот -- по щелчку на такой
readonly,turn-offable,alarm-лампочке делать это самое отключение.
В MEDM/ALH есть понятие "to acknowledge alarm" -- оператор подтверждает, что он этот alarm увидел. Пока этого сделано не будет -- оно будет моргать и бибикать.
Так почему бы и у нас не ввести такое понятие -- что
alarm бибикает и моргает, пока юзер не щелкнет кнопочкой, что "он
увидел". Когда щелкнет -- то лампочка остается гореть, но иных
аудиовизуальных эффектов не производит. А при появлении любого другого
alarm'а (т.е. -- при инкременте alarm_on
) флаг
"acknowledged" сбрасывается и оно начинает орать опять.
Таким образом -- реализуем вариант 3+c, реализуем очень просто -- на уровне элементов (где реально и живет "интеллект бибиканья"), с минимумом действий со стороны knob'ов.
Пара замечаний:
- В EPICS/MEDM/ALH понятие "alarm" несколько отличается он нашего:
- У них "alarm" состоит из двух понятий -- severity (no_alarm/minor(yellow)/major(red)/invalid(white)) и status (hihi/high/low/lolo/...).
- У нас же есть "constate"/"colalarm_status" и, совершенно независимо, флажок "alarm".
- И alarm'ы в EPICS'е "существуют" в БД, а не в клиентах, и потому есть даже понятие "to globally acknowledge alarms".
(Хотя у ALH'а есть и режим "local", когда весь acknowledgement сводится к пометкам в памяти самого экземпляра ALH'а.)
В общем -- с одной стороны просто другая архитектура (не хуже, не лучше -- другая, хотя и более навороченная; где "считать" alarm'ы -- вообще религиозный вопрос, плюсы и минусы есть у обоих подходов); с другой -- на мой взгляд, у них все-таки "несколько слишком переусложненно".
- Там есть "залочивание" alarm'ов -- даже если alarm уже неактивен (исчезла его причина), то сам факт его существования запоминается, и для сброса надо его "acknowledge". (Ключевые слова -- "transient", "поле ACKT".)
Реализовал 3+c -- детали см. в разделах по alarmonoffled и Chl_gui.
Так что -- считаем раздел за "done" (хотя "полный проект" со всякими автоотвисами через указанный срок и не исполнен, но, скорее всего, оное и не потребуется.)
Читать датчики предполагается через CANADC40, и надо данны отображать "красиво", как бы на карте помещений. И есть желание, чтобы сия система была "масштабируемой" -- на весь ИЯФ (хотя бы просто копированием).
23.07.2006: понятно, что в принципе не есть проблема при наличии железа (датчики плюс кабельные трассы) сделать такую систему хоть сейчас -- при требуемой точности CANADC40 сможет опрашивать все каналы с частотой порядка 1Гц. Но!
02.08.2006: КоКо рассказал о такой вещи -- что там не статическое отображение, а можно "наезжать" (zoom), меняя степень детализации; и что в Германии (DESY? BESSY?) ему главная инженер показывала аналогичную систему, отображающую вообще ВСЕ инженерное оборудование комплекса, и там даже писалось название папки (в шкафу), в которой можно найти техническое описание указанной точки.
02.08.2006: и вообще -- существенные результаты от реализации прототипа "предполагаемой системы" мы уже получили: создан ELEM_CANVAS (вау!), он же послужил толчком к управильниванию Chl.
А собственно система термо-давло-контроля... Даже если ее и сделает "Горный+" ("Поздемный-" :-), то оно и к лучшему -- мы не будем отвлекаться от основной деятельности.
Вопрос -- а как бы этак подобного добиться? Чтобы и в CX каналы могли вычитываться только при РЕАЛЬНОМ использовании, а не "всегда все, хоть когда-то троганные программой"?
31.10.2006: решение-то довольно простО (и хорошо стыкуется с предполагаемой для CXv4 канально-ориентированной cda):
physlist[]
), а некий другой массив -- "список используемых
в данный момент каналов", являющийся выборкой из списка
задействованных.
(А уж обычными запросами выспрашивается, или же подпиской -- не суть важно.)
@07.11.2006: можно при "появлении" уставлять colstate=COLALARM_JUSTCREATED. Так хоть и будет "видно", но зато будет вполне ясно, если/почему канал еще не успел обновиться.
16.03.2009: возникло это при общении с Лешей Пановым, который хочет для отладки своей CAN-железки (в будущем долженствующей сидеть внутри модулятора, и даже вообще быть скрытой за общим интерфейсом) иметь НЕпериодичность, т.е., чтобы работа производилась ТОЛЬКО по запросу.
Естественно, такое извращение надо реализовывать исключительно как standalone-программу, НО: ведь всю обвязку-то для CAN писать отдельно с нуля ломает! Несколько мыслей/замечаний по этому поводу:
А то ведь драйверы имеют некоторые предположения о том, что от них будут хотеть -- на основании базовой идеологии работы CX-сервера (например, драйверы козачиных ADC сами всегда заказывают приход данных).
(Естетсвенно, речь о будущей модульной архитектуре в CXv4, с подселябельными сервером и драйверами.)
17.03.2009: неа, лучше не возиться с драйверами, а делать упор на удобство/простоту/скорость создания ИНТЕРФЕЙСОВ -- чтобы такие тестово-отладочные программы, как для Панова, можно было бы нарисовать в .subsys-файле, а программа имела бы удобный API к ручкам (отработку нажатия кнопок, и возможность в любой момент сообщить новое значение текстового поля). А доступ к CAN -- на основе cxscheduler.
22.04.2009: собственно, а в чем проблема-то -- ну так и сделать такой типа умный драйвер, который бы тупо ПОМНИЛ последние значения и всегда отдавал бы их (чтобы не синело), а РЕАЛЬНО читал бы по каналу-команде.
При этом более "внешняя" библиотека, получив отлуп от более низкоуровневой, сможет сформировать свое сообщение из сообщения нижнего уровня, дополнив его своей информацией -- например, "адресом". К примеру, если cda отказала в register_formula(), то Cdr'овское сообщение будет состоять из причины (что именно с формулой не так) и имени knob'а, на котором случилась ошибка.
Технологически реализация может быть такой: статический буфер в библиотеке, в точке обнаружения ошибки складывается описание, и функция с именем типа LIBPREFIX_lasterr(), возвращающая указатель на этот буфер.
Т.е., смысл сего -- чтобы доносить до верхнего уровня ошибки с самых нижних, нечто в стиле плюсовых exception'ов.
И по-хорошему, надо бы отходить от практики печатания сообщений о подобных ошибках внутри нижних библиотек -- переходя на "exception'оподобность"; из нижних же библиотек можно оставить печать лишь асинхронных ошибок -- типа разрыва соединения.
25.01.2007: вопросов/проблем при реализации всего пара:
25.01.2007: в первом приближении реализовано -- в Chl+Cdr+cda на ветви создания иерархии. Работает. Детали:
CLEAR_ERR()
, который просто нулит первый байт
сообщения.
29.01.2007: во-первых, кроме
требовавшего-этого-тогда linipp.c добавил также проверку и
печать в ChlRunSimpleApp()
, в
vacclient_meat.c::CreateMeat
и в
linbpm.c.
Во-вторых, стало очевидно, что отведенных тогда 100 байт на каждый буфер маловато (имена функций шибко длинные) -- так что сдалал в cda 160, в Cdr 200, в Chl 240.
05.06.2007: под-доделал -- в
ChlRunApp()
заменил ручное stderr'ное ругательство по
ChlSetServer() на в _Chl_lasterr_str
. Плюс, теперь оно
отдельно ругается на "-что-нибудь", что это "unknown command line
option".
Кроме того, во всех ChlRun[Simple]App-клиентов вставлена печать ошибки с префиксированием strcurtime()'ом.
Конкретная проблема -- что на linac2 при переходе на локальную pult/bin, но с еще не скопированной pult/lib при попытке запустить программу вылезло сообщение (для сюда порезано на ширину 80 символов)
-- обкорнано посреди названия ошибки, да еще и пара мусорных символов в конце.2011-01-28-12:14:19: /export/linac2/oduser/pult/bin/linthermos: OpenDescription( linthermos): /export/linac2/oduser/cx/lib/chlclients/linthermos_db.so: cannot op en shared object file: No such file or direct<МУСОР>
Возможно, конечно, там просто старая версия chlclient'а за 04-08-2008 -- уж попытка лезть в cx/lib точно из-за этого.
[0]!='\0'
), иначе --
strerror(errno)
.
Смысл -- чтоб при "дурных" ошибках в глубине (типа обломившегося
malloc()'а и прочих open()'ов) функции библиотек могли бы просто делать
return ERROR
, а не возиться с заполнением описания ошибки.
Естественно, предполагается, что в самом начале делается *ClearErr().
fdio_lasterr()
.
21.04.2014: да, надо бы, но пока забиваем.
Алгоритмы там весьма несложные ("столько-то раз проделать такую-то операцию", "прогнать температуру от стольки-то до стольки-то", "если такой-то параметр выходит за эту границу то сделать то-то"), но разнообразящиеся -- заранее почти невозможно предсказать, чего захочет очередной заказчик. И -- там есть некоторое количество входных параметров, уставляемых юзером ПЕРЕД запуском процесса, в пользовательском интерфейсе.
И эти алгоритмы надлежит подселять к обычной chlclient-подобной программе.
Имеющиеся варианты реализации такого:
param NAME "title" description...
-- это позволит автоматически генерить панель настроечных параметров.
Все переменные должны также объявляться --
var NAME
как и используемые измеряемые параметры --
mes SPEC
(И не префиксировать ли эти имена символом '$' -- и вообще синтаксис сделать похожим на будущий язык формул.)
22.03.2007: а еще такой "язык" может быть полезен в управлении магнитной системой накопителя -- для всяких вещей типа подъема энергии.
06.04.2007: а еще -- для всяких хитрых махинаций с пучковым датчиком, типа пресловутого выжигания люминофора (ради которого и был рожден cdrclient).
И -- для всяких салимовцевских алгоритмов с ихними промышленными ускорителями.
А вообще очевидно, что этот "язык" и cdrclient -- это два разных способа делать одно и то же.
И еще: если реализовывать такой "язык", то там центральной сущностью должен быть "контекст исполнения". Тогда можно будет, во-первых, иметь в рамках одного приложения несколько "программ-алгоритмов", исполняющихся одновременно, а во-вторых -- динамически загружать/убивать такие "модули". Например, нажатие некоей кнопки в приложении может вызывать загрузку и исполнение такого модуля.
18.05.2007: (уже давно напрашивалось) а можно и поискать какой-нибудь готовый скриптовый язык, который был бы реализован в виде библиотеки, годящейся для "подселения" в свои программы. Тогда -- достаточно будет просто изготовить "биндинги" между этим обиблиотеченным языком и cda/Cdr, и обеспечить ему "среду исполнения".
24.05.2007: кстати, и в отзыве ИТЭФа (ведущая организация) тоже сказано, что
"Переусложненной представляется проблема преобразования данных при помощи формул, рассмотренная в параграфе 3.5.2. Для решения этой задачи можно, в частности, эффективно использовать механизм динамической компиляции кода или встроенный интерпретатор типа tcl. Существуют и другие подходы."В точности совпадает с моими собственными мыслями, и при этом выражено максимально кратко и ясно (респект москвичам! :-).
04.06.2007: насчет параметров -- см. также сегодняшний комментарий касательно "локальных параметров ручек".
08.06.2007: собственно, с "языками" все понятно -- надо иметь их ДВЕ штуки:
- Виртуальную машину формул -- для вычислений; с транслятором, по проекту cda::12-01-2004.
- Embedded TCL -- для махинаций (типа demag'а и прочих длинных действий типа термоциклирования и салимовскостей).
Плюс -- организовать бы какой-нибудь state machine, в идеале -- чтоб action'ами и checker'ами (или что там будет) могли работать как формулы, так и tcl-куски (плюс, и обычные C-функции, естественно :-).
Вроде бы выработано решение -- надо, и тут будем обсуждать детали технологии.
08.09.2013: краткое резюме мыслей на настоящий момент:
Тогда "три компонента строки-команды" превращаются просто в три последовательных опциональных команды, выполняющих роли действия, паузы и проверяльщика.
Поэтому технология должна годиться и для использования в сервере -- как работает SNL@EPICS.
19.05.2007: то есть -- чтобы программулька просто запускала на контроллере некую программку, и постоянно (раз в N секунд) слала бы ей application ping.
Вообще-то это делается даже тривиальнее -- создается некий
специальный тип драйвера, этакий "noop для подчиненного контроллера", и
из него регулярно пытается вычитаться некий канал. Неа, такое не
прокатит -- сервер пошлет запрос и будет ждать уже до TCP'шного
таймаута (900 секунд).
Так что -- простенький протокол, чтобы на стороне контроллера просто делался "echo reply", а на стороне хоста программа ВНЕ ЗАВИСИМОСТИ от прихода/неприхода ответов раз в N секунд слала бы запросы.
20.05.2007: реально от такой программы толку будет крайне мало -- она будет показывать лишь отключенные в данный момент контроллеры, и все. Но:
Так что -- "withdrawn".
27.05.2007: собственно, вот почему в EPICS'е есть
все эти beacon'ы и вообще UDP -- они таким образом делают keepalive'ы,
посылая их раз в $EPICS_CA_BEACON_PERIOD
=15 секунд; и если
за интервал $EPICS_CA_CONN_TMO
=30 секунд этих пакетов не
будет, то оно посылает серверу state-of-health probe, и если не
получает ответа, то считает соединение мертвым (см.
CAref.html#Disconnect "Disconnect Time Out Interval").
Это приведет к тому, что
Только надо будет разобраться с тем, как бы все-таки сделать "RemitBigcRequestsOf(), или хотя бы имитировать его для данного случая.
Это прекрасно реализуемо даже сейчас, с единственным драйверным таймаутом -- поскольку Reconnect- и AppPing-таймауты нужны только поодиночке, а не оба сразу.
30.05.2007: да, сделал более простую половину работы -- ввел в протоколы пакеты _PING=0x50696e67, а также реакцию на него в cm5307_dbody.c, canserver_drvmgr.c и даже в cm5307_drvletbody.c (т.к. абстрактные драйверы все еще работают на нем).
14.06.2007: однако, привычка наших дежурных и прочего персонала дергать питание крейтов -- уже положительно достала!!! Теперь Пирогов постарался, и мне звонил -- мол, он и программу перепустил, а ничего не работает...
Надо, ОЧЕНЬ надо иметь "heartbeat"/"application ping", чтоб такие кривые ситуации разрешались побыстрее, чем за два часа!
27.07.2010: угу, в очередной раз аналогичный прикол -- на сварке у Семенова, похоже, кто-то дернул питание CANGW, а драйвера этого не заметили. Да, НЕОБХОДИМО вводить heartbeat!
Делаем:
case
***_PING:;
. Оно и так бы ничего не вякало, но -- для
правильности.
is_ready
.
HeartBeat()
.
privrec_t.fd
, заполняемый одновременно с проставлением
is_ready=1
.
Теперь надо брать да проверять.
И, кстати, заодно наткнулся на странную штуку: в начале
InitializeDrivelet()
стояло
set_fd_flags(fd,O_NONBLOCK,1)
, хотя должно б было
стоять ,0
. Смысла в том совершенно никакого, т.к. оно
туда ВСЕГДА должно входить в состоянии 1. Но править от греха подальше
пока не стал.
28.07.2010: да, проверил, именно на сварке (RH-73@2.4.18-3) -- всё отлично работает.
Сценарий:
ScheduleReconnect()
стоит 10-секундная пауза.
Видимо, это уже личное поведение самого ядра -- оно так же шлет ARP и при отсутствии в течение некоторого времени просто TCP'шных ACK-ответов (если погасить питание контроллера в процессе обычного обмена пакетами).
И еще замечание на тему "как вообще могла возникнуть та ситуация, если клиенты постоянно генерят read-запросы": да очень просто -- при включенном CANGW и отключенной CAN-аппаратуре запросы уйдут ОДИН раз, будут считаться отправленными, и больше сервер контроллеру ничего слать не будет. В этот момент спокойно дергаем питание, и имеем искомое состояние.
02.08.2010: кстати, сегодня заметил еще одно прелестное следствие: на этой же семёновской сварке проверял ноут Довженко, мимикрирующий под bike (тот же IP 192.168.8.254) с его же "коробочкой" CANGW, также мимикрирующей под cangw-vch300.
Так вот -- вследствие появления _PING'а появился прямо-таки hot-swap/hot-plug: можно было оперативно переткнуть кабели (Ethernet+CAN) из одной коробочки в другую, и комп макс. за 5 минут сам переключался на эту коробочку, и, аналогично, можно было переткнуть Ethernet-кабель из одного компа в другой, и он также резво сам снюхивался с коробочкой!
31.08.2010: ёпт -- в cm5307_dbody.c
обратный пакет отправлялся размера rcvbufsize
вместо
rcvpktsize
! Т.е., после заголовка пёрло N килобайт
мусора. К счастью, проявиться успело только на dds300 на сварке.
Позорище!!! :-)
tvtime-command
, т.е. -- надо уметь вызывать внешние
команды. Чего всегда и боялись, ибо это сразу откроет банку с гнусными
пауками (can of worms)...
22.05.2007: итак, есть три различных проблемы:
Идея решения: сделать список разрешенных команд,
как в sudo
(там это /etc/sudoers). Все, что не
разрешено -- то запрещено. Отдельный вопрос, ОТКУДА брать этот список
(локальный файл? БД? ...?).
options
указывалась команда. Но это ограниченный вариант.
FLAG_SKIP_COMMAND
!), которой бы также указывалась
командная строка. Это проканает с любым типом knob'а, и, собственно,
является единственным подходящим вариантом для нашей конкретной задачи
(camsel).
options
то, из каких регистров брать параметры.
OP_POLY
).
И -- в ИМЕНИ команды (т.е., до первого пробела) форматы запрещаются, из соображений security. Надо такую параметризацию -- делайте скрипты.
Вариант с CMD_EXEC удобен, кстати, еще и тем, что может использовать собственно уставленное в knob'е значение -- хоть это текстовое поле, хоть селектор. А при наличии CMD_EXEC кнопкоподобный "KNOB_EXECCMD" эмулируется тривиально.
В общем, правильнее будет остановиться именно на формульном варианте.
Кстати, а как это сделано в MEDM/dm2k?
31.07.2007: конечно, частично (хоть и не в этом случае) нас могла бы спасти идея от 27-06-2007 насчет "доступа к параметрам больших каналов как к обычным каналам".
А вот как делать ТУТ?
Но -- кто ж будет заниматься сдвигом и обновлением истории? Ведь не Cdr же, которая о таких созданных-вне-ее-дерева ручках будет не в курсе.
Оба варианта кривы, не хватает им ни красоты, ни общности.
01.08.2007: по некоторому размышлению -- все становится совершенно ясно и несложно!
Во-первых, весь подобный сервис будет обеспечиваться ТОЛЬКО для knob'ов -- не суть важно, обычных или же simple, и откуда там берутся данные (параметры большого канала, локальные) -- тоже неважно. Главное -- что именно у структуры "ручка" есть и история, и "методы".
Во-вторых, в CXv4 все будет крайне просто: "parent'у" ручки
будут (как-то) назначаться методы ShowBigval() и ToHistPlot(), а
сдвиг истории будет выполняться обычным вызовом
CdrProcessKnobs(), который увидит локальность этой ручки, так что не
станет дергать cda, а сразу использует значение curv
, и
проделает лишь оформительство типа сдвига истории.
В-третьих, сейчас, в CXv2, это проделывается примерно тем же макаром -- Widget заменяется на Knob, которому "назначаются" методы для отправки на bigval и график, а сдвиг -- делается "руками", по приходу данных в большой канал. Вопрос только, когда и кто будет дергать обновления в отображающих окнах? Видимо, пусть сами идут, раз в секунду.
12.12.2007: и еще Малютин с Танькой-Рыбкой захотели уметь отправить на график также linipp'шные числа (число частиц, и т.п.), рисуемые на графиках. Задача из той же оперы.
Первоначально -- был послан. Да и внятно обсудить как-то пока неспособен. Но вообще-то -- мне самому мысль о подобном "менеджменте" режимов еще несколько лет назад приходила в голову. Так что делать придется, а как?
30.11.2007: собственно, несколько вопросов -- идеологических:
И еще вопросы технологические -- а как бы это реализовать-то?
ParseChanVals()
станет выполнять несколько
другие действия вместо SetControlValue(,the_v,1) -- например, ",0" и
производить "сравнение".
03.12.2007@бассейн: а можно ведь показывать не каким-то другим цветом, а инверсией -- менять местами foreground и background. Тогда, с одной стороны, никакой дополнительный цвет изобретать не надо, с другой -- все будет отличнно читабельно, а с третьей -- будет показываться и "статус" новых значений.
Реализовать же это можно было бы -- введя доп. битик в rflags, при наличии которого в самом конце выбора цветов fg и bg просто менялись бы местами.
09.12.2007: ага, битик -- фигушки:
ChooseKnobColors()
, выбирающая цвета, доступа к rflags не
имеет, действуя только на основе newstate. И это правильно, ибо нефиг,
цвет должен однозначно определяться состоянием. Так что
SetColstate()
-- v4'шная set_knobstate()
смогут работать корректно, просто сравнивая номера состояний.
accept()
успеет прислать FIN (например, сдохнет), то
запрос из очереди будет удален, и когда сервер дойдет до принятия
соединения, то просто заблокируется до следующего запроса (который
может и никогда не придти).
08.12.2007: тэкс, смотрим -- fdiolib делает ВСЕМ своим дескрипторам O_NONBLOCK, так что с ее клиентами все окей.
Опять же -- cx/src/programs/daemon/ трогать не будем.
Итого -- остаются 3 точки:
Итого -- остался ТОЛЬКО cx-porter. Скорбно, он не менялся аж с 15-05-2005, а теперь придется :-). Подправил его -- делов-то.
Разве что -- м-м-мать! -- пришлось и в этот файл вставить
свою копию set_fd_flags()
. Ну сколько можно-то, а?! Еще
ж в bigfile.html за 13-01-2003 было отмечено как "надо!"!
Замечание 1: в CXv4 и сервер также использует fdiolib, так что там вопрос не стоит вовсе.
Замечание 2: rrund, строго говоря, также по-хорошему бы не должен зависать, а быть параллельным -- но в CXv4, похоже, будет некий хитрый гибрид rrund+"canserver", или просто rrund поверх fdiolib'а.
В общем -- "done".
16.12.2007: в принципе, он скорее прав, чем неправ -- что-то с этим делать нужно. Конечно, частично проблема снимется в CXv4 -- с введением текстовых описателей, вполне user-editable. Но -- какой-нибудь способ сохранения параметров отдельно от режима совсем не помешает.
06.02.2008: собственно идея -- ручку с usertime!=0 надо как-то выделять. Первая мысль -- цветом; вторая -- цветным бордюрчиком вокруг. На бордюрчике и остановился.
"Очевидная" идея -- включать/выключать бордюрчик одновременно с
уставкой/обнулением поля usertime
. А вот КАК это делать
-- некая хитрость:
UserBeganInputEditing()
, куда и вставлен вызов
XhShowHilite()
, то СБРОС usertime
и,
соответственно, ОТКЛЮЧЕНИЕ рамки может требоваться из нескольких мест:
SetControlValue()
-- к ней
сводятся CancelInputEditing()
(Esc и увод фокуса) и Enter,
а также SetKnobValue()
по истечении 60 секунд для простых
ручек. Здесь пока что все просто -- это все тот же самый
KnobsInternals.c.
SetControlValue()
-- при истечении
60 секунд, когда ручка возвращается к автообновлению. Вот это уже
хуже, поскольку libKnobs и libCdr формально вообще никак не связаны, у
них даже нет общей, используемой ими обоими библиотеки, через которую
можно было бы их связать.
Для этого введен hook cdr_unhilite_knob_hook()
,
вызываемый, если он != NULL (таким образом, для GUI-less
клиентов/иерархий ничего не меняется), при сбросе usertime
в ноль. Уставляется этот hook в довольно неочевидном месте -- в
ChlSetGrouplist()
; просто потому, что, как было
установлено, эта функция вызывается всеми существующими на данный
момент видами клиентов.
Реально, простоты ради, коли уж все равно пошла такая халтура, Chl
не "связывает" Cdr с Knobs, а самостоятельно делает
XhHideHilite(k->indicator)
. Это, конечно, халтура, ибо
дублирование кода, а должно бы делаться какой-то одной Knob'овской
функцией, но пока потянет.
usertime
в 0, то
обнуление делается не безусловно, как раньше, а только при
ki->usertime!=0
.
usertime
исполняет именно она.
Поэтому в ней можно будет предусмотреть пару hook'ов (вкл/выкл рамки),
которые будут заполняться by libMotifKnobs (самой правильной на данный
момент точкой выглядит MotifKnobs_HookButton3()
-- она
вызывается при создании любой ручки).
Just for information:
08.02.2008: а покамест -- "done".
24.03.2009: насчет CXv4 -- какие, нафиг, hook'и?! Во-первых, нужна ОДНА функция, которой бы передавалось состояние (on/off). А во-вторых -- не hook, а просто МЕТОД контейнера! И тогда мы автоматом получаем все бонусы тамошней архитектуры -- и потенциальную множественность и "переопределяемость" (у разных элементов -- разные способы выделения).
А на бОльшее время вызова -- можно забить, поскольку эти операции вызываться будут очень редко, на скорости юзера.
24.03.2009: а вот и нет -- попытка реализации через методы оказалась просто ДИКО затратной!
dataknob_cont_vmt_t.ChgEdtState
. А уставлять ВСЕМ
контейнерам -- ну тогда какая уж тут, нафиг, "гибкость"...
Как бы вроде решение -- тогда уж надо было бы ставить этот метод в
ChlTopVMT
, втихушку подпихиваемый Chl'ем. Но в таком
случае это будет только для Chl-программ, что неправильно.
Simple_fakecont
/Simple_VMT
, а там уж никаких
"оформительских" методов не может быть по определению.
Так что -- надо возвращаться к идее с hook'ами:
Короче -- так и сделал. Для уставки hook'а сделана функция
set_knob_editstate_hook()
-- это чтоб не светить саму
переменную наружу и не маяться с extern и инициализацией.
P.S. Вот за каким, спрашивается, чертом полез СЕЙЧАС портировать hilite в 4cx/, а? Огреб только кучу сложностей. А занялся бы этим потом, при возникновении РЕАЛЬНОЙ потребности -- всё прошло бы легче и само собой.
25.08.2011: нынче ответ на вопрос "за каким чёртом" появился: зато теперь будет очень просто перевести CXv2 на datatree -- потому что вся чёрная работа была проделана тогда, два с половиной года назад, а сейчас осталось просто заиспользовать этот код.
05.11.2013: еще в продолжение -- сегодняшние мысли на тему "как правильно сделать в v4" и результаты обдумывания с учётом результатов за 24-03-2009:
Хотя, тултипы-то работают.
13.02.2008: затронутые файлы --
include/misc_macros.h
lib/cxlib/cxlib.c
qult/drivers/abstract_camac/a_sukh_comm24.h
Поправлено, вкючая 4cx/include/misc_macros; старые варианты -- curcx, ncx, oldcx, ucam, pult -- естественно, трогать не стал.
03.07.2008: вообще-то впервые эта проблема была замечена еще на ICALEPCS-2005 в Женеве, в последний день:
FR2.6, M.Clausen, "EPICS Office": Важная идея: разделения приложения как сущности, выполняющей некий обсчет/действия, и отображения (интерфейса). Приложение может быть запущено постоянно, "standalone", а интерфейс к нему -- по мере недобности. 2 следствия:
- Архитектура становится не 3-, а 4-уровневой.
- Приложение словно бъется пополам -- надо как-то координировать "приложение" и "отображатор".
А в остальном -- словно воспроизводство нынешней границы "сервер<-+->клиент", т.к. с точки зрения cx-starter'а, при запуске "интерфейса" надо стартануть "приложение".
А вот по результатам нынешнего разговора:
Плюс такого подхода -- что доступ к таким "данным" будет идти по тому же самому протоколу, что есть однозначно правильно.
(Но как именно реализовывать почтовый ящик? В смысле "оповещений" клиентов об изменении данных? Видимо, просто noop-драйвером. Но неприятно, что "конфигурация" каналов будет в ДВУХ местах -- у собственно насчитывающей программы, и в конфиге такого "почтоящичного сервера".)
И -- должна ли быть возможность пускать сервера, к которым нет клиентов, такие как бы "демоны"? И как вообще таких пускать?
(А "зависимость" -- все равно понадобится, если в сервере будет "chaincx".)
19.10.2008: Список требуемых доделок/фич примерно таков (главное -- БД!):
Все равно остаются вопросы (они касаются, возможно, и идеологии в CXv4 тоже):
20.10.2008: насчет проблемы под номером 2 ("мочь спокойно реагировать...") -- а решение-то очевидно:
MarkRangeForIO()
-- чтоб он "втискивал" запрос в рамки
имеющегося набора каналов, плюс
FillDataPacket()
, чтобы он таким "несуществующим" каналам
ставил что-то, приводящее к CDA_FLAG_NOTFOUND
. Вопрос
только -- что? Поскольку это именно cda'шный флаг...
CXRF_UNSUPPORTED+CXRF_INVAL, плюс age=255?
14.11.2012: давно уж вызрела идея -- в v4 маппировать "несуществующие" каналы на 0-й, а у него будут и должные флаги, и бесконечно старый возраст. Дальнейшие рассуждения на эту тему -- уже в bigfile-0002.html, в разделе про libcxsd.
CheckFork()
с результатом
CXT_EINVCHAN
просто выкинуть нафиг.
21.10.2008: во-первых, забыл упомянуть в "амбициозном списке" о требующихся для адресации-по-именам добавлениях в cda и Cdr.
А во-вторых -- глядя на пункт 9 ("парсер текстовых файлов описания..."), становится опять очевидно, что надобно будет просто реализовать эту штуку в CXv4, а сюда сделать back-port. "Эта штука" включает, естественно, и CXv4'шные Cdr+KnobsCore+MotifKnobs, и новые структуры дерева -- datatree*. А вот cda останется практически старым, просто с модификацией -- поддержкой адресации-по-именам.
22.02.2009: Некоторые детали/комментарии:
Таким образом, можно сравнительно небольшими силами изготовить промежуточный вариант, на котором обкатать модель будущей модульной архитектуры из CXv4.
31.01.2009: детальное обсуждение:
Так что -- отдавать!
Но не всё так просто --
Замечание на будущее, для CXv4: вот вам и пример, когда атрибуты нужны не только для каналов АЦП (D, T) но и для каналов ЦАП. Так что надо будет пообдумать возможности реализации там именно атрибутов, как сущностей, на взгляд клиента привязываемых к каналам, а не как отдельных каналов. В частности, чтобы можно было прислать запрос "выполни такое-то измерение или запись с таким-то атрибутом".
02.02.2009: спросил у "целевой аудитории" -- Малютина и Еманова. Резюме такое: надо ограничивать именно СКОРОСТЬ изменения, время же никого не волнует (точнее, волнует -- для одновременного синхронного подъема нескольких источников, но тогда правильнее использовать режим ЦАПИ -- это совсем другая задача). И еще: делать это надо только при подъеме, при сбросе можно менять уставку скачком. НО! Могут быть источники, у которых и уменьшение тоже надо делать плавно (вроде как -- на ВЭПП-2000).
Спросил у Гусева -- у него сделаны пара лишних полей рекорда, шаг (100) и время на шаг (0.1с). И он на КАЖДЫЙ канал запускает по таймауту. И еще -- он НЕ проверяет, прописалось ли (впрочем, он и на пакеты 0xFF не реагирует :-)).
Так что -- надо параметр использовать следующим образом: =0 -- менять сразу; >0 -- рассматривать как предельную скорость (в секунду!), только при повышении; <0 -- по модулю предельная скорость всегда, и вверх и вниз.
Замечание: "повышением" считать не увеличение, а увелчение ПО МОДУЛЮ. Поскольку полярность иногда попутана.
02.02.2009: да, прототип новой программы нарисовал -- linmagx, linac1:31. С кнопочками [...] оно выглядит интереснее, а у каналов V1000 вместо этих кнопок пустота -- намного информативнее, чем былые за-disable'нные.
18.02.2009: поскольку множественные таймауты уже
есть, приступаем к реализации драйвера. Он назван
xcandac16_drv.c, и имеет xcandac16_drv_i.h, с
префиксами XCANDAC16_
. Каналы "скорость изменения" пущены
ПОСЛЕ регистров, их база называется
XCANDAC16_CHAN_CHSPD_N_BASE
.
31.10.2009: поскольку всё в конце концов воплощено в xcac208_src.c, то xcandac16* убран -- за ненадобностью. (Возможно, еще реинкарнируется, но скорее -- в следующей версии CX и CAN-драйверов.)
Сам же раздел -- давно пора было делать "done".
Проблема в том, что "удержание юзерского значения", обеспечивающее круглые числа (1800, а не -1799), работает только при ОДИНОЧНОЙ операции. Если же затребованное значение будет получаться не сразу, а через сколько-то шагов, то логика с квантом просто отключит "удержание", и в конечном итоге отобразится именно некруглое число. А в магнитной системе это малоприемлемо.
Ну и что -- весь проект "плавного изменения уставок" накрывается медным тазом?!
27.03.2009: некоторая последовательность выводов и размышлений:
Разве что "квант" поддерживать и в вещественночисленном драйвере -- но как? Неа, это плохая идея :-).
val_to_candac16(val)
, а
val_to_candac16(val+quant/2)
?
Вопросов 2:
А хбз, но, вероятно, достаточно вставить прибавление
quant/2
прямо в val_to_candac16()
.
Видимо -- НАДО химичить, учитывая, что ВНАЧАЛЕ делается умножение/деление, а ПОТОМ сдвиг.
30.03.2009: каким боком может вылезти -- уж совместимость-то с "пре-q/2"-режимами будет слегка нарушена. Хотя, вроде бы, несильно.
10.04.2009: насчет "накрывается медным тазом" -- поговорил с Гусевым о том, как этот аспект плавного изменения сделан у него, и вот что узнал...
У него есть ДВА РАЗНЫХ поля -- value
и
readback_value
. В первом содержится то, что запросил
уставить юзер, а во втором -- текущее точно-известно-что-уставленное.
Таким образом -- при плавном изменении в value попадает запрошенное, и
оно там остаётся, а вот readback_value потихоньку бежит до
затребованного. И в не-medm/dm2k-программах у него рядом с полем
"уставка" есть поле "уставленное", отображаемое на readback_value, а в
medm/dm2k на этот аспект либо забивается (видна только уставка), либо
так же можно показывать текущее-уставленное. И, да --
текущее-уставленное всегда показывается "как есть", типа 9.997 при
уставке 10.000.
Так что -- в принципе можно поступить аналогично: завести каналы с
базой типа XCANDAC16_CHAN_CURWV_N_BASE
, в которых и
выдавать ТЕКУЩЕЕ ЗАПИСАННОЕ в ЦАП, а в обычных каналах -- при плавном
изменении! -- оставлять запрошенное.
31.10.2009: поскольку в xcac208 именно так всё и реализовано, то "done".
04.04.2011: история:
Короче -- убрал то "|| 1
" из проверки перед
ReturnChanGroup() в _fd_p(), помеченной "This includes table-driven
channels".
05.03.2012: уж и не знаю, что там ТОГДА было
откуда убрано, но вот сейчас "|| 1
" в
xcdac20_drv.c стояло, так что:
И что, спрашивается, с этим всем делать?
Но не только убрать проверку, а еще учесть, что:
if(ch_isc[l]){ch_isc[l]=0;num_isc--;}
).
Гадко это всё выглядит, ОЧЕНЬ гадко...
06.03.2012: пробуем это сделать...
ch_nxt[]
).
ch_fst[]
, т.е.,
при возврате ch_trg не сразу, а по первому ответу, тоже всё будет
корректно.
#if
.
SHOW_SET_IMMED
. Проверено
-- работают оба режима.
06.09.2013: название "SHOW_SET_IMMED" кривое: по факту это "показывать в поле SET текущее значение (CUR)", а по названию кажется, будто "отражать в канале SET уставку-target сразу, а не по окончании (как было раньше)". Но в любом случае надо ТОЛЬКО ==1
Тот комментарий "No mangling with now-changing channels..." означает, что с уже-меняющимися каналами мы НЕ пытаемся "оптимизировать" и уставлять в них значение одним скачком (зачем?), и в любом случае программируем плавное изменение; а вовсе не запрет менять цель.
А вообще уже напрашивается сделать "общее место", выполняющее стандартные операции (как предусмотрено для v4) -- например, kozdev_common.c; чтоб всё, сейчас отлаживаемое, потом не копировать в несколько других файлов, а сразу упхать в одну "библиотеку".
23.05.2013: заметил, что num_isc
делается только ++, но никогда --.
06.09.2013: угу, так и есть -- просто забыто, похоже, изначально, еще 4 года назад.
Исправлено -- при сбросе ch_isc[l]=0
также делается и
num_isc--
(причём, на всякий случай, условный -- если
перед сбросом ch_isc[l]!=0
).
06.09.2013: кстати, интересный вопрос -- а что будет, если запустить сначала плавное изменение, а потом табличную работу с ЭТИМ же каналом?
08.09.2013: плавное изменение взглючивает, если
"на лету" -- во время собственно плавного изменения -- сбросить
скорость в 0. Канал при этом просто замирает. Хотя надо бы прямо в
_hbt()
просто сразу прыжком переходить к конечному
значению.
09.09.2013: решаем две описанные выше проблемы.
_hbt()
вставлена проверка "если ch_spd[l]==0 то
сразу переходим к цели".
(Следующим утром) Проверил, работает и то, и другое.
MIN_ALWD_VAL
и MAX_ALWD_VAL
.
Причём перепутаны именно ИМЕНА, а вот сравнение шло корректно:
иMIN_ALWD_VAL = 9999999, MAX_ALWD_VAL = -10000305
if (val > MIN_ALWD_VAL) val = MIN_ALWD_VAL; if (val < MAX_ALWD_VAL) val = MAX_ALWD_VAL;
Случилось это, похоже, при переходе от забитых чисел к константам, еще в конце 2005г.
20.03.2012: исправлено, путём тройной замены (MIN>ZZZ, MAX>MIN, ZZZ>MAX).
Затронуты были cac208_src.c, vv2222_src.c, xcac208_src.c, xcdac20_src.c, xceac124_src.c, vdac20_drv.c.
Причина понятна -- в _hbt()
используется запрос
таймаута в варианте "after" (а не "at" с точным указанием), так что оно
потихонечку съезжает как из-за просто подтормаживания (таймаут
запрашивается чуть позже истечения предыдущего), так и из-за
квантования (100Hz у ядра при желаемой частоте таймаута 10Hz как раз и
дадут 10% неточности).
С одной стороны, можно б было перейти и на "at"-указание, но не стоит. Плавное изменение -- это чтоб "не быстрее, чем", а для точного должны использоваться таблицы. Зато нынешний вариант безопасен, поскольку оно адаптируется к производительности проца.
Насколько это правильно?
Если нет, то надо по is_a_reset делать me->num_isc=0
и
занулить все isc (bzero(me->ch_isc,...) в v2 и out[*].isc=0 в v4).
Отдельно, кстати, видно, что v2'шные table-enabled-драйверы НЕ переходят в режим TMODE_NONE.
27.02.2017: вообще-то, если причина -- BUSOFF, то совсем не факт, что нужно прерывать плавное изменение.
Главный вопрос -- как отличить первое от второго. Ведь ТОЧНО можно понять, лишь сравнив текущие значения с "надлежащими" (причём учитывая, что последнее отправленное могло не дойти), а текущие будут получены намного позже, когда от юзера может успеть придти еще толпа заказов...
_rw_p()
, в обработке записи в CHAN_OUT*, когда
проверяется, что при spd>0
(т.е., "вниз" можно скачком)
движение действительно "вниз" -- т.е., новое значение меньше текущего
("Or is it an absolute-value-decrement?"), используется такой код:
abs(val) < abs(me->out[l].cur)
Но это неверно! При переходе между +9V и -8V условие выполнится, хотя там будет сегмент не только вниз, но и вверх.
Надо еще проверять знак: что либо val==0, либо sign_of(val)==sign_of(cur).
P.S. Косяк был найден глазами при просмотре кода, а потом проверен вручную. Вживую никогда не вылезал, т.к. почти всегда скорости стоят со знаком "минус".
30.04.2017: в v4'шных драйверах везде исправлено.
19.02.2009: краткий обзор их предполагаемых характеристик:
(Реально это, по словам Козака, пока нигде не требовалось. Так что можно и забить.)
#define
'ами).
22.04.2009: детали проекта:
CHAN_READ
и CHAN_WRITE
-- на более внятные CHAN_ADC
и
CHAN_DAC
. Дополнительный плюс -- у имен будет одинаковая
длина. (Но потенциальный минус -- повышается вероятность опечаток,
причем трудноуловимых.)
30.04.2009: неа -- лучше не "ADC" и "DAC", а "ADCI" и "DACO" (Input и Output). Не шибко красиво, конечно, зато вероятность опечаток близка к нулю.
13.07.2009: на данный момент -- стабилизировалось "ADC" для АЦП и "OUT" для ЦАП.
02.11.2009: да, и в devtype-файлах в следующей версии тоже используется это соглашение. Так что -- "done".
13.07.2009: highlights:
Общая идея -- чтобы ВСЕ CAN/ADC/CAC-драйверы козачиных блоков поддерживали унифицированный набор управляющих каналов (как это уже сделано в fastadc).
22.07.2009: еще: поскольку там некоторое
количество внутренних функций, которым помимо privrec'а нужен также и
magicid
, то, чтобы не передавать постоянно лишний
параметр, создано поле privrec_t.magicid
, идущее сразу за
iface
, перед handle
.
Видимо, надо сделать такую раскладку стандартной на будущее.
Только мучает меня один вопрос: вроде ж, кажется, где-то неявно использовалось, что некое поле (iface? или handle?) где-то (в cankoz?) идёт по некоему стандартному смещению в privrec'е. Где именно -- хрен бы помнил, не не здесь ли?
12.08.2009: попробовал -- плавное изменение работает!!!
Теперь надо разбираться сдеталями:
DoSoftReset()
-- в каких случаях и как её вызывать,
и как она должна взаимодействовать с SendMULTICHAN()
(или
прочими, в зависимости от режима)?
31.08.2009: Перевел xcac208_drv.c на
принцип "указатель на приватную структуру называется
me
" (как в CXv4).
Далее, принято соглашение, что запросы с ninfo==0
-- это "чтение" (также принудительно запись игнорируется при
активированной или запущенной таблице). Вообще, нынешняя схема работы
больших каналов СОВЕРШЕННО НЕАДЕКВАТНА для таблиц:
02.09.2009: за прошедшие несколько дней
собственно bigc_p()
допилена. Она принимает/возвращает
данные больших каналов в удобном-для-клиента виде, сами же данные
хранятся в виде общей таблицы в privrec'е. Принимает она данные в
локальные буфера, и лишь убедившись, что они корректны, копирует в
privrec.
Обработка большей части DO_TABLE_* также сделана -- она несложна:
эти команды шлют нужные пакеты и меняют "состояние" при помощи
SetTmode()
, каковая также уставляет значения (0/2/3) в
каналах, связанных с этими командами.
Теперь осталось: во-первых, собственно ACTIVATE, а во-вторых -- реакция на табличные пакеты в _in().
06.09.2009: да, предварительную проверку провёл -- по минимуму функционирует. Детали:
Но это было только для тестов, а реально загрузку надо явно переделать -- чтобы оно насчитанные данные старалось грузить ПЛАВНО.
Кроме того, надо сделать корректную реакцию/проверку по пакету "CLOSE" -- чтобы объем совпадал.
06.09.2009: вечером: сделал НОРМАЛЬНУЮ загрузку таблицы, любого размера.
Формулировка проблемы:
Введен слегка жульнический способ регулирования нагрузки: после каждого пакета 0xF4 отправляется также и 0xFE (GETDEVSTAT), и все эти пакеты шлются не напрямую, а через очередь -- 0xFE обычным
q_enqueue()
, а 0xF4 --q_enq_ons()
.Соответственно, отправка очередного 0xF4 производится только при получении ответа на предыдущий 0xFE, и тем самым достигается автоматическая адаптация и к пропускной способности интерфейса, и к скорости обработки блоком.
add()
, увеличен на (макс.размер.таблицы+6)/7*2.
send_frame()
на q_enq_ons()
-- ведь за ней
всегда следует обычное чтение.
fsync()
'нутый логгинг).
Всё работает очень надёжно, но проблема может возникнуть при одновременной загрузке таблиц в НЕСКОЛЬКО блоков -- она ведь пойдет независимо, и выходной буфер чипа всё равно может переполниться. Видимо, придется в layer'е/HAL'е предусматривать какую-то проверку готовности к отправке (уж реакцию на ошибку при забитом буфере -- точно).
02.11.2009: переделываем карту каналов под стандарт v4:
Резоны:
02.11.2009: да, ввел
XCAC208_USECS_IN_STEP=10000
и позаменял все пересчетные
10-ки на него. Пока, правда, не проверял.
02.11.2009: за компанию сменен подход к функциям-конвертерам:
kozadc_decode_le24()
, по указанному куску байт из пакета и
gain-фактору отдающей code
и val
, а
возвращающей rflags -- это взамен парочки
cac208_24_to_code()
+cac208_code24_to_val()
с
сопутствующей логикой в самой _in()
.
kozdac_c16_to_val()
и kozdac_val_to_c16()
.
28.07.2010: помнится, как раз из-за этой новой функции преобразования тогда в конце прошлого года была проблема -- там из-за опечатки путались байты. Но разобрался и пофиксил быстро.
И, поскольку драйвер давно беспроблемно работает -- считаем за "done".
16.08.2016: только не совсем «CEAC124 -- это "половинка" CAC208/CEAC208»: у него единственная таблица, и потому "номер файла" может быть только 0, а в 208-м используется 7. Вот эта 7 и не работала -- в ответ на 0xF5 (DESC_CLOSE) не присылалось ничего, и драйвер затыкался на бесконечной отсылке этого пакета.
С переходом на TABLE_NO=0 всё заработало.
16.08.2016: кстати, как показал опыт разборок,
по-хорошему лучше бы отдавать значение t_mode
в виде канала.
Для отладки крайне полезно.
(Этот раздел расположен тут, а не двумя разделами ниже, как по хронологии, чтобы быть поближе к разделу "3rd generation CAN drivers".)
15.04.2009: собственно общий список:
15.04.2009: отдельный вопрос -- как этим УПРАВЛЯТЬ (какими параметрами включать такой режим, как указывать его свойства), и как сочетать его с обычной работой. Смахивает на проблему "как реализовать осциллографический режим у CAN-АЦП".
И еще вопрос -- а зачем бы такой режим может быть нужен. Поскольку делать такой "стоп" программно или из блока синхронизации незачем, проще пользоваться обычным режимом (программно/руками -- всё равно в произвольный момент случится, а аппаратно -- ну так дать обычный импульс запуска заранее за нужное время).
Так что -- надо бы повыспросить у знающих людей, насколько такой режим нужен, и для чего он используется.
16.04.2009: продолжение банкета:
П.Шатунов: не очень ясно, но вроде как практически нет. И вообще у них с CAN-АЦП сделано махарайчато, местами используется многоканальный режим, местами осциллографический, а если надо переключаться -- то работают двумя программами по очереди.
Короче -- с одной стороны, оно пока не особо нужно, но сделать по большому счету несложно, т.к. ответ на вопрос "как этим УПРАВЛЯТЬ" очень прост: вводим еще один параметр -- "РЕЖИМ_САМОПИСЦА" (у nadc4 он будет =-1), на который чуток по-другому реагируют как сами драйверы, так и fastadc_common.
02.03.2010: еще поговорил с Батраковым, на тему «почему в ADC200ME нету бита "переполнение"/"счетчик хоть раз перешел через ноль"».
15.04.2009: а главное -- КАК это будет поддерживаться в *fastadc_common.*? Оно-то рассчитано на режим "запустили, померялось -- получили LAM, прочитали, отдали".
16.04.2009: дятел!!! Ведь LAM-то (или IRQ) возникнет после измерения ВСЕХ страниц, а не после каждой.
А означенная "проблема" решается просто -- надо по LAM'у не безусловно считать "всё готово!", а вызывать некую функцию из драйвера, которая проверит (при надобности -- увеличит счетчик полученных страниц), и если она вернет 1 -- значит, готово; 0 -- не готово, ничего не делать, -1 -- ошибка, отвалить.
06.06.2014: еще одна более хитрая потребность: мочь отдавать "наверх" осциллограмму не целиком, а вырезками.
Конкретно на ЛИУ-2: есть 2 импульса, обмеряемые ADC200ME; они отстоят друг от дружки довольно далеко, и участок между ними совершенно не нужен. Вот вычитывать бы прямо из ОЗУ блока именно эти 2 кусочка...
Как? Например, так:
(А "реальный NUMPTS первого сегмента" будет считаться равным NUMPTS-NUMPTS2.)
adc200me_x2xs()
посмотрит, что NUMPTS2!=0 и при x>=TRUE_NUMPTS1
прибавит к нему еще и PTSOFS2.
...ох и криво же это!!!
10.08.2014@обдирание-обоев: а чё б 2-импульсный режим не делать страницами в adc200me?
12.08.2014@утро: а то, что никто нам триггер перед вторым импульсом не даст. Ибо не предусмотрено.
09.10.2014@Снежинск-каземат-11: в продолжение темы:
17.10.2014: дальнейшее обсуждение драйвера -- adcslice -- в его собственном разделе.
...хотя, возможно, лучше подойдёт эта же модель -- например, из-за отсутствия ограничения на количество "сегментов-вырезок": тут они делаются сколько угодно, просто указанием в конфиге, а с "отражениями" придётся закладывать ограничение (да и вообще ПРЕДУСМАТРИВАТЬ, в драйвере; в то время как драйвер-паразит можно натравить на любой реальный драйвер).
Но оно так будет только расти -- значит, нужен еще канал "забудь".
10.10.2014@Соболь-из-Толмачево-на-Большевистской: PTSOFS удобен тем, что и времена будут показываться правильно (при условии, конечно, что в реальном устройстве стоит PTSOFS=0).
12.10.2014@утро-душ: для красоты надо бы вытащить создание реперов в отдельную функцию -- чтоб "дирижер" мог бы их создать в себе, а не у отображатора.
12.10.2014@Быстроном: экранные "дирижеры" надо делать контейнерами (подэлементами). Тогда у них будет возможность прилепляться к "дирижируемым" гарантированно уже после их создания.
n>=NUM_PARAMS
.
09.06.2014: да, в pzframe_drv сделано --
param_elapsed
.
Но ведь cm5307_fastadc_common.h-то рассчитан на получение ОДНОГО сигнала LAM, а тут перед вычитыванием надо будет дождаться НЕСКОЛЬКИХ.
P.S. А вот более общий "adc50vx" (с указанием числа блоков в конфиге) так просто не сделаешь -- там ведь всякие retdata/outbuf/... указываются статически... Ну да ладно -- можно сразу параметризовать, и потом "vN" генерить по мере надобности средствами препроцессора.
16.04.2009: насчет "нескольких" LAM'ов -- всё тривиально, то же решение, что и для страничных измерений: функция в драйвере, которая return-числом скажет common-уровню, что все измерения готовы.
А надо -- после, пока продублировав-таки для опыта -- сделать некий "общий" кусок кода, который бы подходил как для cm5307-драйверов, так и для обычных. Ведь API у них почти одинаковый.
02.03.2010: да, сделал для adc200me_drv.c (и будущего adc812me_drv.c) кусок cpci_fastadc_common.h.
Но тут вопрос скорее к нынешнему cm5307_dbody.[ch] -- его недотягивающесть до API сервера уже отмечалась 12-01-2009.
03.03.2010: да, очень напрашивается в CXv4 сделать "dbody" прямо на основе cxscheduler/fdiolib -- пусть и на один дескриптор. Оно как бы некоторый overkill, но на "лишние накладные расходы" можно забить, зато будет унификация с canserver'ом. 11.08.2011: да, в CXv4 изначально так, и новый cm5307_sl_dbody тоже сделан так же.
08.11.2011: поскольку
Предполагаемые фичи:
-D
.
pci4624_irq_proc
), в cm5307 -- что еще вылезет...
11.11.2011: назвать надо даже не "fastadc", а как-то со словом "frame" -- поскольку ровно эта же модель отлично подходит и для всяких камер.
А нынешняя ДРАЙВЕРНАЯ модель "fastadc_common" -- это в первую очередь связка {cur_args,nxt_args}[NUM_PARAMS] плюс параметры SHOT, ISTART, WAITTIME, STOP; PTSOFS же и NUMPTS -- там не используются (присутствуют в закомментированном и давно ненужном отладочном коде).
И еще, касательно общей архитектуры:
InitParams()
) не принадлежат к сей модели, а должны
относиться к компетенции "прослойки для конкретной архитектуры".
Единственная "тонкость", могущая возникнуть -- это для не-локальной аппаратуры, типа CAN и DIMEX: поскольку там
Придётся проапгрейдить модель, введя 2 свойства:
Следствие: драйверы, конечно же, НЕ будут иметь права самостоятельно
уставлять DRVS_NOTREADY -- а только говоря об этом framework'у, который
соответственно проапдейтит состояние, и уже сам вызовет
SetBlockStatus()
.
AbortMeasurements()
и ReadMeasurements()
--
должны мочь возвращать результат "в процессе...".
StartMeasurements()
.
13.11.2011@душ: название можно взять "parframe" -- parametrized frame.
09.06.2014: оно всё уже реализовано, в основном как описано выше (хотя и с изменениями касательно "зависнуть" -- не потребовалось), как pzframe_drv плюс *_fastadc_common.h.
А с циклом 1Гц одно-единственное сканирование растягивается на 50*2 секунд (*2 -- поскольку надо проконтролировать, что уставилось, да потом еще и померять). А весь цикл, по всем магнитам/корректорам, растянется тогда на час -- это совершенно неприемлемо; во-первых -- просто долго, а во-вторых -- за час сама установка может успеть "уплыть".
Так что -- надо как-то мочь достигать частот намного выше 1Гц, а частоту цикла поднимать низзя, иначе совершенно незачем начнет жрать процессор linmag.
Ну-с, и что делать, как можно решить имеющуюся проблему?
30.03.2009: с пирометром-то решение оказалось в большом канале. Но вот тут -- ну ОЧЕНЬ не хочется вводить что-то в драйверы; тем более, что сами-то CAN-драйверы и так выжимают максимум того, на что способны устройства.
Самая напрашивающаяся идея -- просто взять да прямо сейчас расширить протокол на "отдачу значения по получению измерения".
А вот это будет включать в себя несколько аспектов:
API самих драйверов, естественно, затрагиваться НЕ будет.
P.S. А вообще -- как попёрли-то недостатки/тупики: сначала вылезла проблема из-за только-целочисленности, теперь -- принудительная цикличность... Знак свыше? :-)
Вообще-то что-то подобное вроде есть на ВЭПП-4.
Вопрос -- а как это сделать? Формально -- у нас же есть консольная команда "clients"; а что, если также ввести протокольное сообщение "уставить статус", и чтобы в cda имелся соответствующий вызов, который бы отправлял бы такое сообщение по ВСЕМ aux-соединениям такого-то sid'а...
А не маппировать ли такой информационный сервер везде на :0?
Идея такая: пусть отладочная печать наличествует ВСЕГДА, но отрабатывает только если некая переменная окружения определена и имеет значение "1".
Естественно, не каждый раз проверяется эта env-переменная, а где-то вначале по результатам проверки уставляется булевский флажок, который и участвует в if()'е.
03.07.2009: общие правила именования: переменные
окружения -- "БИБЛИОТЕКА_ДЕЙСТВИЕ_ОБЪЕКТ", флажки --
config_действие_объект
.
Первым примером стала выдача результата работы
cda_do_linear_approximation()
. Переменная окружения --
"CDA_DEBUG_LAPPROX", флаг --
config_debug_lapprox
.
06.07.2009: добавил еще пару переменных -- CDA_DEBUG_SETREG и CDA_DEBUG_SETP, смысл очевиден.
24.11.2009: добавил еще параметр -- "CDA_DEBUG_PHYSINFO". Оно включает проверку, что в physinfo нет дублирующихся строчек (т.е. -- с совпадающим полем "номер канала"). Такие дубли уже встречались и они дают весьма странный эффект. Так что даже вопрос -- не сделать ли эту проверку безусловной.
24.11.2009: выдача по CDA_DEBUG_SETP
была халтурновата -- печатался chan-handle, а не ссылка на канал.
Переделал -- теперь выдаётся результат
cda_srcof_physchan()
от handle.
11.09.2014: добавлена CDA_DEBUG_SETCHAN --
отрабатывается в setphyschanvor()
(сделано для разборок с
подозреваемыми глюками cdrclient'а на weldcc).
10.07.2009: несколько исходных соображений:
Что надо делать:
Технология очень простая -- иметь список пар (номер_канала,значение), и по "RESET"'у идти по этому списку и вызывать саму себя функцию записи.
Следствие: надо ОТДЕЛЬНО помнить:
Например, CAC208:
06.06.2012:
magicid
->devid
c_magic
->c_devid
bigchaninfo_t.magic
->devid
driverfd2magics[]
->driverfd2devids[]
active_magic
->active_dev
*MAGICID*()
->*DEVID*()
magic
/s_magic
->devid
/s_devid
MAGIC_NOT_IN_DRIVER
->DEVID_NOT_IN_DRIVER
magicptr
" переименован в более адекватный
opaqueptr
.
Дополнительно в cx-server_data.h:
CHECK_MAGIC()
, никогда не использовавшийся, выкинут.
ENTER_DRIVER_S()
/LEAVE_DRIVER_S()
переделаны на полную самостоятельность, а использовавшиеся ими (и более
давно никем!)
ENTER_DRIVER()
/LEAVE_DRIVER()
выкинуты.
Так что теперь слово "magic" осталось только в смысле "magic number" (почти canary) для разных структур.
22.06.2012: а вот и нет -- еще в
cankoz_pre_lyr.c как-то осталось magic2handle()
-- сейчас исправлено на devid2handle()
.
08.06.2012: продолжаем:
[*_]privrec_t
.
me
, вместо info, rec,
...
TYPE_privrec_t
..._privrec_t *info
->*me
(а
info
там осталось для call_data)
me->field
.
TYPE_privrec_t
rec
,info
->me
(кроме
того, что касается диалоговых окон -- там вроде как не совсем объекты,
ибо они единственные; но как минимум histplot в будущем радикально
изменится, под множественность)
09.06.2012: далее:
devptr
вместо privptr.
d_privptr
->d_devptr
privptr
вместо
usrptr
/usrptr
. МОРЕ мест.
ВСЁ!
24.07.2012: приступаем (пока в основном переименования):
CXSRV_DRIVERREC_VERSION_MAJOR
продвинута до 8.
CXSD_DRIVER_MODREC_VERSION_MAJOR
-- до 10.
REMDRVP_VERSION_MAJOR
продвинута до 3.
REMDRV_PROTO_VERSION_MAJOR
-- до 4.
RegisterDriverTimeout()
:
d_timoutrec[]
.
devrec_t.trec
осталось для подчистки semi-connected.
CxsrvDrvBigcCmpFunc()
и
CxsrvBlkBigcReprFunc()
тоже выкинуты.
RegisterDriver*()
/DeregisterDriver*()
в
RegisterDev*()
/DeregisterDev*()
.
CxsrvBlk*{Func,Proc}
"Blk" заменено на
"Dev".
Cxsrv
-- на Cxsd
.
(Потребовались изменения и в паре Makefile'ов, т.к. таблицы драйверов для remsrv-серверов генерятся.)
init_blk
, term_blk
стали init_dev
, term_dev
.
drv_name
->mod_name
,
blk_name
->dev_name
.
init_d()
, term_d()
переименованы в
init_m()
, term_m()
, в основном ради...
init_b()
,
term_b()
в init_d()
, term_d()
(нуднейшее занятие!).
CxsdDrv*
->CxsdMod*
;
init_drv
/term_drv
стали
init_mod
/term_mod
.
CX_MAX_BIGCHANS
стало
CX_MAX_BIGCS
(для унификации).
SetDevStatus()
стал SetDevState()
-- воизбежание разнобоя "state/status". В 4cx/ тоже.
DRVS_
("DRiVer Stat") стал
DEVSTATE_
; 4cx/ too.
DisconnectDev()
-- бывший
DisconnectBlock()
-- изведён, с заменой на
SetDevState(,DEVSTAT_OFFLINE,)
.
ReRequest*()
стали
ReRequestDev*()
.
CxsdDevFDProc
стал просто
CxsdFDProc
-- поскольку, по аналогии с таймаутами, может
регистрироваться и для devid=DEVID_NOT_IN_DRIVER.
25.07.2012: продолжаем -- теперь больше по параметрам:
RegisterDevFD()
.
InitDev()
и
remsrv_drvmgr.c::ProcessPacket()
, и переменная в
них из dev_fd
стала state
.
CxsdDriverRec
.
DEFINE_DRIVER_REC()
.
DEFINE_DRIVER_REC()
в драйверах.
state<0
добавлено
также:
Ведь ПО-ХОРОШЕМУ -- по умолчанию устройство должно рождаться (т.е., ставиться ДО вызова init_dev) в DEVSTATE_NOTREADY.
06.08.2012: чуть хитрее: в случае отсутствия
init_dev
-- сразу в OPERATING, т.к. в таком случае
предполагается, что вся инициализация свалена на инфраструктуру. Так и
сделано -- пре-инициализируется state=DEVSTATE_OPERATING.
d_doio[devid]
и
d_dobig[devid]
перетащено в ДО вызова init_dev.
return
'ов в init'ах всех
драйверов.
Практически повсеместно -- просто замена "return 0" на "return DEVSTATE_OPERATING" (чисто повышение читабельности).
А вот менять "return -1" на "return DEVSTATE_OFFLINE" пока не стал -- "-1" тоже достаточно наглядно. Хотя и надо бы...
SetDevState()
в
cm5307_dbody.c и remdrvlet_c.h -- убрана трансляция
v4->v2.
(В remsrv_drvmgr.c не требовалось, поскольку он работает с серверовым, а не драйвлетным API.)
26.07.2012: далее -- списки параметров у драйверовых методов (удаление лишнего, добавление privptr):
devptr
2-м параметром (после devid)
во ВСЕХ методах.
bid
отовсюду, кроме
CxsdDevInitFunc
.
fd
отовсюду, кроме
CxsdFDProc
.
CxsdFDProc
добавлен параметр
mask
-- на будущее, когда API "FD" будет на cxscheduler'е.
Пока в нём отдаются константы 1, 2, 4.
RegisterDevFD()
в RegisterDevRdFD()
.
27.07.2012:
privptr
к CxsdFDProc
и
CxsdToutProc
.
CxsdFDProc
и CxsdToutProc
, но отсутствовали
Register*()
, так что использоваться оно никак никем не
могло.
Поскольку всё равно этот и предыдущий пункты будут меняться на libremdrvlet -- то определения просто выкинуты, и вопрос закрыт.
privptr
к методам добавлен.
RegisterDevTtFD()
(кстати, в remsrv
реализации WrFD не было, так что прототип убран) и
RegisterDevTout*()
-- тоже.
cxsd_devtinfo_t.privptr
, массивы
devfdprivptrs[]
, devwrprivptrs[]
,
devexprivptrs[]
(возможно, стоило бы сделать ОДИН
массив, а не три, чтобы приблизиться к будущему sl-based API, ну да
ладно...).
cxsd_devtinfo_t.privptr
, плюс
devrec_t.fdprivptr
.
С последним вскрылось страшное: оказывается, нынешний remsrv реально работает с ОДНИМ fd на драйвер -- оно всё в devrec'е, а не в массивах (видать, еще от canserver_drvmgr.c осталась). Так что надо б побыстрее переводить "внутренности" libremsrv на общую с libremdrvlet архитектуру, с полноценной поддержкой всего API.
11.10.2012: вылезло еще другое: при регистрации
дескрипторов на NOT_IN_DRIVER оно еще и privptr
не
передаёт. Так что с несколькими линиями ни CANGW, ни Moxa (на которой
и налетели) работать бы не смогли. Посему -- сейчас переведено на
архитектуру с массивами по [MAX_ALLOWED_FD+1], как в сервере.
30.07.2012: вносим изменения в
CxsdDriverRec
и DEFINE_DRIVER_REC()
, для
унификации их с v4'шными.
Есть также мысль попробовать сделать "гибридную" схему, чтоб некий промежуточный вариант сервера (v2.5, v3?) мог грузить и использовать ОБА варианта драйверов. Т.е.:
- В самом сервере чтоб внутренности были в стиле v4 (с cxdtype), но с дополнительным функционалом "больших каналов", существующим параллельно (как сейчас они существуют параллельно обычным каналам).
- При загрузке драйвера сервер смотрит дополнительное поле "variant", и если оно говорит "v4", то используется нативный интерфейс (
CxsdDevChanProc
), если же "v2" -- то своя функция-адаптер, транслирующая v4'шный API вCxsdDevRWProc
.- "Обратные" же интерфейсы -- возврата данных -- поддерживаются оба:
Насколько это всё осмысленно и получится -- посмотрим, но пока стараемся делать так, чтоб ничего не противоречило такой возможности.
Теперь собственно действия:
CxsdDriverRec
: устраняем
поля magicnumber, version, name, init_mod, term_mod, заменяя их
cx_module_rec_t mr
в начале. И соответствующие
исправления в коде сервера.
И типы CxsdModInitFunc
, CxsdModTermProc
устранены.
#include
оного.
DEFINE_*DRIVER*
поле
comment
.
DEFINE_DRIVER_REC()
в cxsd_driver.h и
remsrv_cxsd_driver.h.
DEFINE_DRIVER()
--
добавлен неиспользуемый параметр.
DEFINE_DRIVER()
--
добавлен параметр, передаваемый дальше в
DEFINE_CXSD_DRIVER()
(где он присутствовал изначально).
DEFINE_DRIVER()
--
аналогично. Только этот макрос нигде не используется, всё сразу на
_CXSD-версии.
CxsdDriverRec
в
CxsdDriverModRec
.
31.07.2012: продолжаем:
CxsdDriverModRec
и со списком параметров
DEFINE_*DRIVER*()
-- всё для приведения к более-менее
единому виду:
layerver
в
CxsdDriverModRec
и в DEFINE_DRIVER_REC()
.
(В прочих DEFINE[_CXSD]_DRIVER()
оно уже было.)
layer
.
init_m
, term_m
перетащены
в начало, сразу после name, comment.
privdsize
и поле
privdatasize
переименованы в privrecsize
и
перенесен из середины в начало после {init,term}_m.
paramtable
,
min_businfo_n
, max_businfo_n
.
DEFINE_DRIVER()
(cm5307_dbody.h,
cm5307_sl_dbody.h, bivme2_dbody.h):
layer
+layerver
перенесена почти к
началу, сразу за businfo_n (в v2-серверном -- из конца, в остальных --
из середины).
В параметрах, плюс work/drivers/.
Всё, с данного момента у нас есть всего несколько вроде разных
DEFINE_*DRIVER*()
:
- Серверный
DEFINE_DRIVER_REC()
и ставший ему идентичным драйвлетныйDEFINE_DRIVER()
.- v4'шный
DEFINE_CXSD_DRIVER()
, отличающийся специфичными для v4 фишками:
- Вместо двух таблиц main_nsegs:main_info и bigc_nsegs:bigc_info плюс методов rdwr_p и bigc_p, есть таблица chan_nsegs:chan_info и список chan_namespace.
- Также добавлена таблица команд cmd_table.
- Драйвлетный (_sl-версий)
DEFINE_CXSD_DRIVER()
, почти идентичный предыдущему, но с добавлением bigc_p.- ...и
CxsdDriverModRec()
'ов:
- Серверный (и идентичный remsrv'шный).
- v4'шный, также отличающийся определениями касательно каналов (вместо обычные+большие -- "вообще") плюс наличием cmd_table.
- _sl-драйвлетный, вообще не содержащий таблиц определения каналов, а еще -- отсутствует cmdtable. Зато есть do_big.
Теперь задача -- постараться как-то это всё максимально сблизить.
01.08.2012: вариантов сближения просматривается 2:
При этом в рамках одной версии (v2) структуры всё же должны совпадать.
Пожалуй, стоит выбрать 2-й подход -- он хоть и потенциально менее-всеподходящий, но зато проще в реализации и в будущем, при полном переходе на v4, позволит полностью избавиться от старого наследия, а не тянуть его до второго пришествия.
Поехали:
DEFINE_DRIVER_REC()
в
DEFINE_DRIVER()
.
DEFINE_CXSD_DRIVER()
из
cm5307_sl_dbody.h и bivme2_dbody.h, переводя
функционал в обычный DEFINE_DRIVER()
.
Ибо -- пусть уж v2'шные будут сами собой, не пытаясь выглядеть v4'шными.
CxsdDevRWProc
и CxsdDevBigProc
.
CxsdDriverModRec
добавлены поля таблиц
{main,bigc}_{nsegs,info}. (Они реально никем не используются (и не
могут), но зато теперь всё совпадает.)
02.08.2012: переход от единственного busid/bid к businfo[]+businfocount (это, пожалуй, самое заморочное из всех изменений -- оно затрагивает очень много уровней).
*businfo
"
сменен на более адекватный "businfo[]
".
CxsdDevInitFunc
, пара драйверов да вызов из
cxsd_hw.c; плюс в drivers/can/src/.
do_init()
брал businfocount[{1,2}]
вместо {0,1}. Драйвер SIGSEGV'ился при обращении к N=0.
d_busid[]
заменён на
пару d_businfocount[]
и d_businfo[][]
.
AddDev()
, и
парсинг в SimulateDatabase()
. 06.08.2012: была халтура. Исправлено.
04.08.2012: продолжение -- меняем уже собственно API "DevInit".
Заодно -- сейчас проще всего -- добавлен параметр
typename
, для унификации с v4.
05.08.2012: немного поразмыслив, выкинул его нафиг.
Нужен он лишь нескольким драйверам, остальным же лишь захламляет список
параметров. Желающие (реально -- только remdrv) смогут добыть искомое
через свежевведённую
cxsd_driver.c::GetDevTypename()
.
data[]
) -- сначала идёт businfo[], а после него auxinfo.
Выглядит кривовато, можно б оптимизировать -- прямо в init_d() после privrec'а сразу отводить блок businfo[]+auxinfo, там всё подготавливать, и в запуске драйвлета уже сразу слать этот готовый блок. Ну да фиг с ним -- в 4cx/ всё равно код уже другой; но, может, там потом поменяем.
Заодно обновлён интерфейс cm5307d_init_f
И все танцы с REMDRVLET_C_H_BUSINFO_MODE
устранены (и
года не прожили :-)).
int
, а в remdrv_proto -- int32
, то
в/из пакета делается не memcpy(), а именно поэлементное копирование.
Хотя и была мыслишка и в API постулировать int32...
Уф, теперь -- всё!!! Самая муторная и критичная фаза закончена.
Дальше надо проверять, а затем начинать пользоваться новыми фичами и двигать реализацию API. Конкретно -- изготавливать libremdrvlet, libpzframe, sl-based cxlib, новое ядро сервера.
04.08.2012: заюзываем
CxsdDriverModRec.paramtable
.
psp_parse()
в:
InitDev()
.
ProcessPacket()
.
ProcessPacket()
.
InitDevice()
.
HandleDriveletInput()
-- нет,
поскольку там архитектура другая (БЕЗ метрики) и ему всё равно жить
осталось недолго.
Но не во всех -- в нескольких, старых (типа *kshd485 и impacis10), оно оставлено, чтоб не возиться.
#include
"paramstr_parser.h"
, ставший излишним.
06.08.2012: кстати, было забыто
psp_free()
'ние. Добавлено (в серверные варианты, в
драйвлетных удаления устройства нету).
Также добавлена проверка (во ВСЕ, кроме dbody), что заданный businfocount попадает в указанный в метрике диапазон [min,max], и если нет -- то автоотлуп.
Оставался один недоправленый API: SetDevState()
--
отсутствовал v4'шный параметр "строка объяснения". Добавлен. Но пока
вообще никак не используется, да и по протоколу удалённых драйверов не
передаётся.
09.08.2012: еще некоторые трепыхания по приближению:
privptr
в fd_p():
pci4624_irq_fd_p()
-- убран
поиск нужного handle сравнением по всем.
cankoz_fd_p()
выкинута
fd2line()
.
piv485_fd_p()
--
аналогично (ибо копия).
TRUE_DEFINE_DRIVER_H_FILE
, и тогда оно
сделает его #include
вместо своего определения
DEFINE_DRIVER()
.
Соответственно, remsrv_cxsd_driver.h (к этому моменту уже унифицированный с обычным) удалён, определение перенесено в свежесозданный remsrv_DEFINE_DRIVER.h, и вся обвязка в Makefile'ах поменяна. 31.07.2013: remsrv_DEFINE_DRIVER.h уже давно -- как минимум с 13-08-2012 не содержит ничего специфичного, так что удаляем его. (А вот сам механизм -- используется, для libremdrvlet'ных драйвлетов.)
Это открывает дорогу к "правильной" реализации драйвлетных окружений -- с подменой DEFINE_DRIVER() в стандартном cxsd_driver.h, вместо нынешних *_dbody.[ch].
Засим, поскольку всё в основном проверено и работает, считаем раздел за "done", а все прочие движения пойдут отдельно.
23.07.2012(реально 22.07.2012, ближе к полуночи, из дому): часть 1 -- файлы:
23.07.2012: часть 2 -- флаги:
CXRF_SERVER_HWERR_MASK
тоже скопирована, теперь это
перечисление флагов, а не байт.
CXCF_
(16..31).
Там существенное отличие -- положение WEIRD в группе CDR (CXCF_FLAG_COLOR_MASK), а не CDA.
_cx_rflagslist[]
также
заменено и теперь содержит все 32 бита.
23.07.2012: добиваем.
Посему -- проведена перетряска, исходно в 4cx/ -- ...
CXCF_FLAG_COLOR_WEIRD
переведён в группу cda.
(Первоначально была мысль просто переклассифицировать его в ту группу/маску, физически оставив на месте (ну и ладно, что группы стали б не-непрерывными). Но потом это показалось некрасивым. Минус же нынешнего решения -- что уже группа COLOR_MASK стала прерывной; ну и фиг с ней.)
CDA_FLAG_*
и
CDR_FLAG_*
, переводя весь код на CXCF_FLAG_*
.
cda_strrflag_long()
в Chl_knobprops.c; заменено
на cx_strrflag_long()
.
DecodeData()
.
...Интересно, не вылезет ли это где-нибудь? В simple-Knobs всё равно пока не применяется.
22.08.2012: ну да -- вылезло :-): оказывается, перестало посиневать по неприходу данных от сервера и обрыву связи. Пофиксено.
30.08.2012@Снежинск-каземат-11: еще одно вылезло: на серверах с
cyclesize<1000000 каналы стали посиневать при мелком возрасте --
-b200000
(5Гц) давало посинение при 1. Тривиально -- оно
сравнивало еще НЕмасштабированный возраст. Исправлено перетаскиванием
условия погусения в точку после масштабирования.
09.08.2012: поскольку всё собирается, то можно было вообще сразу ставить "done".
#include
с
"cx_types.h" на "cx.h" (для клиентов -- не забыв
предварительно сменить "cx.h" на "cxlib.h").
DEFINE_DRIVER_REC()
и DEFINE_CXSD_DRIVER()
на DEFINE_DRIVER()
--
проще это сделать "с нуля", чем перестановкой.
bid
'а теперь
businfocount
businfo[]
.
privptr
, могущий
оказаться полезным.
13.08.2012: добавлено поле
CxsdDriverModRec.extensions
-- для совместимости с v4.
Именно только поле, НЕ параметр (ибо нефиг; надеюсь, тут мы уж сможем
прожить без этого функционала, переход произойдёт раньше).
17.10.2012@Снежинск-каземат-11: оно также добавлено в remdrvlet_h.h, а заполнение его NULL'ом -- в cm5307_sl_dbody.h и bivme2_dbody.h. (Смысл -- чтоб даже уже в нынешних (еще не-true-libremdrvlet-based) драйверах можно было пользоваться pzframe_drv.)
04.01.2013: итак, обновляем файловый API
драйверов: добавляем к CxsdFDProc
параметр
fdhandle
-- ПЕРЕД fd.
CXSRV_DRIVERREC_VERSION_MINOR
продвинута до
1 (теперь 8.1).
Пока в качестве "fdhandle" передаётся просто сам fd.
06.01.2013: продолжаем:
fdhandle
".
(Ох и маятная же была работка! Кстати, для упрощения собирабельности пришлось эту троицу typedef'ов внести также и в cm5307_dbody.h -- иначе возникали сложности с old_camac/src/cm5307_nadc*.c, из-за tid'ов.)
Итого -- теперь API рассчитан (и используется драйверами!) в концепции "handle'ов", хотя внутренняя реализация реально пока на тех же fd.
Но вот переделывать именно саму нынешнюю реализацию -- НЕ будем, ибо незачем. Новая реализация будет сделана в унифицированном lib/srv/cxsd_oslike.c, который и заменит всю нынешнюю троицу. 01.07.2013: неа, НЕ будет никакого cxsd_oslike.c
Мыслишки в сторону:
- Вот раньше API был простым и очевидным -- драйвер просто указывал, с каким файловым дескриптором и что делать.
- А теперь стало навороченнее -- для дальнейших действий надо запомнить еще "fdhandle" (хотя собственно драйверу он нафиг не сдался; а еще надо не забывать делать его =-1 при дерегистрации...).
Оно, конечно, вроде бы как "более правильно" и позволяет выкручиваться в более сложных ситуациях (когда просто по файловому дескриптору было бы сложно/неудобно работать), и стало вроде бы более "масштабируемым". И теперь полностью эквивалентно Xt'шному API -- что вселяет надежду на непотребующесть дальнейших модификаций.
Не движемся ли мы в сторону шибко заматерелых и закрученно-запутанных систем, которыми новичку уже сложно (или даже невозможно) пользоваться? Вот конкретно этот переход от fd к handle -- не есть ли он граница (водораздел, Рубикон)?
Вроде всё собирается -- теперь радо проверять работающесть.
08.01.2013: BTW, внутренности 4cx'ного
cxsd_driver.c УЖЕ (изначально?) используют архитектуру со
слотами, так что туда модель с cxsd_fdh_t
ложится
автоматом -- и делать почти ничего не надо, скорее наоборот, убрать
лишнюю трансляцию/поиск.
...чуть позже: сделано. Это, кстати, и есть прототип/предшественник унифицированного lib/srv/cxsd_oslike.c
03.05.2013: проверено -- не глючит, "done".
Следующими кандидатами на пути к v4 выглядят однопроцессный сервер (на унифицированной lib/srv/) и Knobs/Cdr/datatree.
cda_snd_ref_data()
явный запрет
(а в
cda_process_ref()
нету!)).
CX_TIME_SEC_TRUSTED
.
SetChanRange()
...cda_range_of_ref()
.
FDIO_STREAM_PLUG
для бинарных данных, плюс возможность просить STRING-потоки прочитать блок
бинарных данных -- fdio_string_req_binary()
.
._devstate
(-1, 0, +1 -- отключение, рестарт, включение).
-l
, в опциях
(:log=mask), динамическое через канал _logmask.
Консольный интерфейс после пунктов 1 и 3 практически не нужен.
Много работы со сторонними СУ (EPICS, Tango) и с разной электроникой (NI DAQmx, VME):
RESCALE_VALUE()
", когда нулевые значения
на графиках рисовались на 1 пиксел выше нулевой оси.
cxsd_hw_{layers,devices,channels}[]
переведены с реально статических массивов на аллокирование.
cxdtype_t
переведён с 8 на 32 бита.
И Cdr_via_ppf4td c cxsd_db_via_ppf4td на неё переведены.
rpycn
и
replydatasize
хранятся прямо в v4clnt_t
, что
позволяет "мгновенные" ответы от драйверовых _fd_p() добавлять в текущий
формируемый пакет, а не отбрасывать.
select()
/poll()
для
получения IRQ от CAENVMElib и драйвера a3818.c.
Заодно запинана сборка под E2K и E2K-128 и EPICS (и Tango, но он c -128 несовместим из-за целочисленности "thread ID" в их библиотеке).
cda_set_type()
.
cda_req_ref_read()
. Работает и для
клиентов (cda_d_cx+cxsd_fe_cx), и для драйверов (cda_d_insrv).
$CDA_D_CX_SYNC_CYCLES
, чтобы
GUI-клиенты лучше работали при плохой связи.
being_processed
/being_destroyed
для возможности
формулам "самоубиваться" (в интересах sim_dir_drv.c, чтоб формулы
могли делать STOP самим себе (в т.ч. "косвенно") и
"._devstate=0
").
SIGCHLD
в связке с
wait()
и SIG_IGN
(26.05.2024).
vdev_init()
(05.06.2024).
fileDescriptorManager
.
Также собственно ADC4X250 и ADC1000 -- практически готово, добиться только стабильной работы (будет также vme_fastadc_common).
(Кстати, УЖЕ протормозил -- оно скоро распространится на чёмскую сварку, и это "оно" будет всё ещё v2.)
Для этого: ppf4td_get_double() чтоб умел парсить выражения -- если
начинается со скобки '(' -- и перевести на него
ParseCpointProps()
.
И для этого -- драйверы frolov_ie4, f4226, еще...?
А то сейчас архитектура такова, что это де-факто не пашет (на примере trig_exec'а).
(07.01.2021) и ловля отпада/возврата крейта тоже.
rpycn
и
replydatasize
хранились бы прямо в v4clnt_t
, чтоб
работали мгновенные ответы из драйверовых _rw_p() -- добавлялись бы прямо в
текущий формируемый пакет, а не отбрасывались бы.
Для отчёта: "Конфигурирование Modbus-драйвера сравнительно простое, за счёт возможности получать информацию о свойствах каналов через внутрисерверный API интроспекции, введённый в 2020г."
$CDA_D_CX_SYNC_CYCLES
.
Число-то перекладывать из уставки в измеренно несложно, но: 1) нужно двигаться с ограниченной скоростью; 2) надо имитировать битики включенности (перекладывать из бита "включиться"?) и т.п.
04.06.2024: сделан simkoz_drv.c.
13.05.2024: сделан sim_dir_drv.c.
И вообще этот год (или хотя бы остаток зимы) имеет шанс пройти под знаком шаговиков.
(Кстати, с 01-12-2021 в CanserverShadowRules.mk и в
VmeserverShadowRules.mk и с 28-12-2021 в moxa/Makefile
как-то странно определяется lyrtable[]
: там в НАЧАЛО пихается
строчка NULL
, а потом уже список; как же делается поиск и
сопоставление/назначение layer'а драйверу? Чуть позже:
разобрался -- всё ясно и криво: т.к. lyrid'ы идут не с 0, а с 1, точнее, с
-1 и вниз, то этот NULL
в начале -- как раз и занимает позицию
[0], а "назначение" пока делается вырожденно:
-remsrv_drvmgr.c::ReadDriverName()
просто исполняет
if (remcxsd_numlyrs > 0) dev->lyrid = -1;
;
видимо, надо будет в эту точку вставить реализацию описанного там в
комментарии "!!!strip-off optional "@layer" and try to find such
layer?" -- благо, оно несложно (сложнее будет со стороны сервера
добывать и присылать этот "@layer").
ЗЫ: чуть позже -- прямо в генерацию файлов с lyrtable[]
к
этому начальному NULL
'у добавлен комментарий "Note: NULL
goes first to occupy lyrtable[0]", во все 3 места.
А ещё при проверке наличия layer'а надо проверять
dev->lyrid==0
и ругаться "none specified"; чуть позже --
сделано.
Но увиден ещё один косячок, ранее не ролявший: проверка совместимости
делается только внутри
#if MAY_USE_DLOPEN
-- очевидно, в предположении, что layer единственный и всё внутрь собрано
корректно; но теперь-то будет >1 шт, так что становятся возможны ошибки
конфигурации...
А ещё исправлен 4cx/src/GeneralRules.mk на тему ошибочно
имевшегося $MAKEFILES
вместо должного
$MAKEFILE_LIST
; а ещё надо бы понять, когда и как он умудрился
разойтись с cx/src/GeneralRules.mk и можно ли его в последний
скопировать.)
Чуть позже: разобрался. 30-07-2019 была сделана "полная поддержка"
"компиляции плюсовых исходников", это в cx/src/ попало. 21-09-2020
была реализована "create-gitignore", и вот это в cx/src/ уже НЕ
попало. 13-06-2023 введена "вся шобла *_CXX_CPPFLAGS
", и это
тоже НЕ попало. Итого: копирование признано безопасным и сделано. Также
проверено пере-сборкой: единственные *.c?? есть в
lib/Qcxscheduler/ (там последние изменения 14-11-2013 -- далеко
отстаёт от 4cx'ной, в т.ч. поддержки разных версий нет), собралось без
проблем.
29.11.2004: были последние пару месяцев наезды со стороны Лебедева и Клющева по термостабилизации -- что якобы моя программа что-то не то им дает в 0-м канале (ГРУП_П): ставишь в программе 30.00C, а оно держит 31.50C. В прошлом уже бывали сходные истории, и всегда оказывалось, что либо это глюк у Лебедева/Клющева, либо битый ЦАП. Сейчас проверили -- ЦАП выдает именно те вольты, которые ему указываются. И к блоку Лебедева также приходят эти же самые вольты. А глюк -- опять оказался у Лебедева, что его блок криво меряет напряжение.
06.09.2005: из той же оперы. Часть 1 (простая): были попытки заявить, что nmagsys с v1000 делает что-то не то -- типа, старая программа v1000 работала, а nmagsys -- нет. Ню-ню!
Часть 2 (понеприятнее): было обвинение от Феди-jr., что candac16 глючит -- "меняет знак значения". Разборки показали, что он это делал с новым блоком, у которого отсутствует калибровка (1X=1V), и при записи +10V происходило переполнение и число превращалось в -10V.
30.04.2006: имелись наезды от гг. Лебедева и Мосина, что, якобы, программа lebed работает как-то не так: то "какую-то чушь меряет", то меряет вместо одного канала два (т.е., некое значение показывалось не в одной точке графика, а в двух), и т.п. Вначале они договорились что вроде "5-й канал CAC208" сдох, и блок поменяли; но глюки продолжались, и они опять стали катить бочку на мою программу. Заставил их просто подоткнуть тестер к CAC208 и подать некий сигнал (1В, 2В, etc.). Естественно, все работало правильно! Т.е., как и раньше, что-то глючит в лебедевских железках (в коммутаторе, управляемом УРом, похоже, ключи сдохли, и имеют место ложные срабатывания).
28.01.2014: еще радость: почему-то никак не включался В1000 в ИП2 (1L1). Левичев-мл все мозги проел -- "ну разберись, только на тебя надежда". Анализ происходящего и кода показал, что оно включается, а потом входной бит "работа" (C20C_OPR) исчезает, и драйвер реагирует на это отключением устройства (переходит в SW_OFF_DOWN). ПОЧЕМУ источник так себя ведёт -- определить программно никак. А разборка на месте показала, что там отрубилась одна из 3 фаз (светодиод не горит), вот источник и отрубается. А воплей-то было -- а-а-а!...
15.05.2017: и еще прикол -- уже моего авторства. Демонстрировал Медведеву с Сизовым работу КШД485 через USB-485-адаптер (у них не пахало, а у меня вроде да). Оно блок видит, но через секунду драйвер застреливался с диагностикой "r=0 errno=11: Resource temporarily unavailable". Т.е., статус вначале показывает, и если быстро дать команду ехать -- оно едет, но потом йок. Я уж чего только не думал -- что какой-нибудь "modemd" (или как оно называлось) в RHEL лезет ко всем наличествующим COM-портам... Стал проверять при помощи fuser -- и увидел, что /dev/ttyUSB0 держит МОЯ же программа liu (запущенная 5 днями раньше для Старостенко и того же Сизова): это её key_offon_knobplugin пытается ключ найти, и должным образом пере-открывает /dev-файл.
15.04.2019: и снова веселуха: в районе 17:00 перестало работать "всё". Федя всех отсылал ко мне, бедный дежурный Андрей Новиков говорил, что "ничего не работает!". Анализ показал, что почему-то действительно НЕ отрабатывается запись в ЦАПы: у ist_cdac'ов стоит Iset=ЧТО-ТО, а Iset_cur -- не совпадает (ну и в самих ЦАПах аналогично). Потом, заглянувши в логи (тогда -- в /var/tmp/4cxsd.log, а сейчас при писании сюда -- в /var/log/messages), увидел, что системное время вдруг дрыгалось: после Apr-15-17:16:09 оно после перезагрузки стало Apr-16:00:19:06, а потом сигануло назад с Apr-16:00:19:45 на Apr-15-17:19:45, о чём chronyd заявил, что "System clock wrong by -25199.169468 seconds, adjustment started". Вот теперь всё стало ясно: это Федя игрался с системной настройкой LOCAL/UTC (которая на узле devpult была неверной), по ошибке подправил её и на canhw, а там сейчас работает start-cx-servers.service, и CX-сервера запустились как раз непосредственно ПЕРЕД chronyd. И у CAN-драйверов успел запуститься advdac'овский 10Hz-heartbeat, а тут вдруг следующий тик переместился на 7 часов в будущее (из-за того, что системные часы сиганули на 7 часов назад). Вот ничего никуда и не "шло". А через 7 часов просто тихо-мирно исправилось бы. Выводы: 1) во всём был виноват Федя; 2) надо бы поизучать, как вообще люди справляются с последствиями DST, когда время прыгает на час вперёд-назад (возможно -- при "system clock in UTC" проблема не стоит вовсе, т.к. gettimeofday() может выдавать UTC'шное время, а оно при переходе на летнее/зимнее вроде меняться не должно); 2.1) пользоваться каким-то иным интерфейсом, а не gettimeofday()? 2.2) cxscheduler'у (всем реализациям?) уметь ловить какие-нибудь системные события "время изменилось!" и сдвигать запомненные в цепочке времена? 3) а если будут случаи, когда клиент потребовал отработку не через такое-то время, а именно в такой-то момент? может, тогда расширить API cxscheduler'а, чтоб туда добавить поле "флаги", в которых и дать возможность указывать, считать ли этот запрос абсолюбным или интервальным? (ой, тогда придётся при наличии абсолютных запросов выполнять новую сортировку списка таймаутов...)
Google "linux time skew gettimeofday": https://blog.habets.se/2010/09/gettimeofday-should-never-be-used-to-measure-time.html https://lwn.net/Articles/23313/ https://habr.com/ru/company/southbridge/blog/326298/
09.12.2019: и ещё одно "веселье". В 15:30 Федя рестатровал сервер cxhw:25, обслуживающий клистроны, после чего всё на скрине v5kls стало болотным. Рестартовалось ради проверки драйвера (отдача диапазонов), поэтому первая реакция Феди -- "твой драйвер всё сломал!". ОК -- драйвер я вернул старый, но жизнь от этого не наладилась. Но даже после этого оказалось, что контроллеры v5-l-kls1...kls4 даже не пингуются. Тут сразу следующий наезд -- "это твой драйвер что-то сделал, что контроллер завис". Проверяли даже свитч, но на нём всё выглядело нормально. В конце концов выяснилось, что сами Федя со Славой как-то умудрились залить в DNS-сервер старый конфиг, где у контроллеров адреса 192.168.131.4n, вместо должных 192.168.130.4n (изменение было сделано в сентябре, и контроллеры перенастроены на 130-ю). Вот cxhw:25 и пытался обращаться к 131-й сети, в то время как контроллеры слушали 130-ю. А старый экземпляр сервера, ДО рестарта, имел uptime больше месяца и при старте разрезолвил в правильные адреса (тогда конфиг был ещё неиспорчен).
23.07.2021: ещё случай: на маленькой сварке стал хреново работать CAN: с Сеньковым связь вроде нормальная, а вот с Жариковым -- одноразово после его включения и потом никак. Моё подозрение -- попортили кабель во время недавнего ремонта в помещении. Причём с CANGW самого Жарикова всё работало, как с егошним кабелем, так и с этим.
Резюме: присутствовало ДВЕ проблемы: 1) плохой кабель; 2) несовместимо переделанная прошивка, о чём мне Жариков сказать забыл :-).
11.08.2021: и ещё что-то подобное, похоже, происходит у Сенькова: его VIP время от времени откливается как devtype=60 вместо =10, а на команду 0x06,0x01 "прочитай канал ЦАПа #1" просто не отвечает. Сеньков вроде признался, что что-то переделал, и обещал подготовить описание.
02.11.2021: и ещё веселье оттуда же: звонит Юра Семёнов и говорит, что при включении CAMAC-крейта почему-то перестаёт работать CAN-линия. Опросом его удалось определить, что они воткнули один из DB9 CAN-линии в разъём RS232 (тоже DB9) на CM5307 (?). Ну ещё бы после этого что-то работало -- надо молиться, что ничего не сгорело после такого "эксперимента".
25.01.2022: вчера ЕманоФедя пожаловался, что почему-то bridge_drv на cxout:1 не бриджует каналы с cxhw:0. Я подготовил отладочную директорию на v5p2 с копией devlist'а, запустил -- всё бриджуется. ...наконец-то догадался заглянуть в логи, конкретно в /var/tmp/4drivers.log, а там весёлое:
(и подобного там от других бриджей, вроде k500_bpm -- выше крыши). Смотрим прямее:2022-01-24 17:31:47.686 cxout cxsd#1: ddm[1]ddm/DEFAULT: cda_dat_p_report[1:"cxhw:0"]: 2022-01-24 17:31:47.686 cxout cxsd#1: ddm[1]ddm/DEFAULT: Connection failed: Unknown host; will reconnect. 2022-01-24 17:31:47.688 cxout cxsd#1: beam_dcct[2]dcct/DEFAULT: cda_dat_p_report[2:"cxhw:0"]: 2022-01-24 17:31:47.688 cxout cxsd#1: beam_dcct[2]dcct/DEFAULT: Connection failed: Unknown host; will reconnect.
Т.е., были косяки с резолвингом (как потом оказалось, у этой машины шибко много интерфейсов, и, похоже, последний оказался институтским, и именно его DNS стал первым в списке, так что в конечном итоге имя "cxhw" попросту не резолвилось.[oper@cxout]~% host cxhw Host cxhw not found: 3(NXDOMAIN)
28.01.2022: вчера Семёнов настаивал, что на "новой" маленькой сварке в 14-м вакуум показывается неправильно. При разбирательстве оказалось, что АЦП меряет ОТРИЦАТЕЛЬНОЕ значение, чего быть вроде бы не должно никак. 02.02.2022: веселье продолжается (скоро неделю как) -- Косачёв утверждает, что, судя по распиновке в описании Козака, подключено всё верно; я же вижу -3.448V; причём на всякий случай (ну мало ли, накосячил где, или R=-1} проверил даже байты с CAN-линии, подсмотрев sktcanmon'ом -- да, там старший бит в старшем байте (data[5]), так что значение точно отрицательное. 03.02.2022: позвонил Цыганов -- ДА, он вчера заставил (Косачёва?) сфотографировать разъём, чтобы посмотреть, что же они там напаяли, и оказалось, что таки перепутали полярность...
26.04.2024: и ещё: некоторое время назад на "сварку для Улан-Удэ" присобачили дополнительно корзинку от Сенькова с управлением через CAC208; интерфейсом -- по просьбе Сенькова просто скрин cac208, где прямо в нужные каналы вводить прямо вольты. Сегодня оно Алякринскому+Косачёву опять понадобилось, я им скрин запустил. Через некоторое время звонит Алякринский с воплем "твой костыль херню показывает!!1" -- у него при уставке положительных значений они отрабатыаются, а отрицательные -- как-то не так. Сказал ему "программа непогрешима!" и отправил к Сенькову. 02.05.2024: спросил Сенькова -- тот ответил, что Алякринский к нему не обращался, а проблема, скоре всего, в больном CEAC208, который "большие" коды, выше ~5V (и отрицательные тоже, из-за кодировки), как-то обрезает.
15.07.2004: хи-хи, а ведь у нас же часть строк проходит через Xh, совсем в обход Chl, и определяется при этом в программе сразу константами -- например,
actdescr_t.tip
.И че с таким делать? Или, вводить в Xh специальный hook для этого, который Chl будет переводить на себя?