CREATION DATE: 24-09-2003.

28.04.2005: ВСЕ "<A NAME=...>" ОБЯЗАТЕЛЬНО снабжать "</A>"!!!

CX:
Chl:
  • 10.01.2004: интернационализация: а как насчет сделать совместимое подобие GNU gettext -- чтобы при старте программы указывалась таблица соответствия, а потом по ходу дела вызывалась бы функция ChlIntl(const char *text), возвращающая локализованную строку, если она найдена в таблице, или просто text в противном случае?

    15.07.2004: хи-хи, а ведь у нас же часть строк проходит через Xh, совсем в обход Chl, и определяется при этом в программе сразу константами -- например, actdescr_t.tip.

    И че с таким делать? Или, вводить в Xh специальный hook для этого, который Chl будет переводить на себя?

  • ??.01.2004: давно пора перетащить код 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.

  • 04.04.2004: надо б добавить в окно "Knob properties" еще строчку "Исходный физический канал" (номер), рядышком с "Raw". Естественно, потребуется и кооперация от cda -- что-то типа 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: итак, пытаемся подгонять Chl под нужды не-chlclients, в данном случае -- C13 (istcc.c).

    05.04.2004 (тогда начато, реально записано 26.04.2004):

    • То, что обычно делает ChlPopulateWorkspace(): ChlSetGrouplist(), ChlAddLeds(), ChlStart().
    • Доступ к данным (Chl работает просто тоннелем): ChlGetMainsid(), Chl{Get,Set}ChanVal() плюс ChlSetDataArrivedCallback().

    Общее впечатление/осадок после всего этого процесса -- а нафиг нам вообще для не-chlclients нужен этот Chl? Все "добавленности", по крайней мере, как они есть/сделаны сейчас -- лезут глубоко во внутренности Chl'я...

    29.04.2004: собственно, вроде основное сделано -- так что "done".

  • 22.04.2004: пообщался с Гусевым, и еще очевидная идея на тему "user interface enhancements" в ту же струю, что и [+]/[-], двойные щелчки на заголовках колонок, etc.

    Собственно ИДЕЯ: по двойному щелчку на заголовке элемента он "сворачивается"/"разворачивается" -- т.е., делается 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: Поговорил на эту тему с Гусевым и заимел еще несколько мыслей.

    • Он выдал идею, что для "горизонтально-слипшихся" элементов самым прикольным было бы сворачивать их в тонкие вертикальные полоски, на которых вертикально же пишется название элемента. Расследование показало, что в X протоколе вообще напрочь отсутствует понятие "направления" -- XDrawString() просто НЕ ИМЕЕТ параметра "поворот/направление". Единственное, кто это умеет -- Xft, но сей зверь находится за пределами доступности Motif'а.
    • Я же родил ответную мысль, за которую он, кажется, ухватился: создавать 2 pixmap'а -- горизонтальный (временный) и вертикальный, делать XDrawText() на горизонтальный, а потом ручками, по пикселу, отображать его на вертикальный с поворотом на 90 градусов на вертикальный, а тот ставить уже в качестве XmNlabelPixmap.
    • Кроме того, пришло в голову, как "окрасивить" современные элементы: надо делать unmanage и сетке, и сепаратору, а потом приаттачивать нижний край заголовка к форме с bottomOffset=form.shadowThickness (при разворачивании элемента -- проделывать противоположные действия в противоположном же порядке).
    • (Дальнейшее записано 06.05.2004) Возможно, это будет "взмаргивать" из-за двойного изменения размера. В принципе, можно для компенсации "эффекта" сделать "shadow title" с той же самой меткой, но сразу с bottomAttachment'ом, и выполнять такую последовательность: 1) unmanage(сетки,сепаратора); 2) manage(теневого заголовка); 3) unmanage(основного заголовка);. При этом следует позаботиться, чтобы теневой заголовок был "выше" основного и перекрывал бы оный.
    • (О! А можно сделать так: иметь теневой заголовок всегда, но под сеткой, и над "настоящим", а делать unmanage/manage только сетки и сепаратора. При этом "настоящий" реально и будет теневым, и будет играть роль просто "attachment point'а" для сепаратора. Надо будет, для атомарности, вызывать не XtManageChild() 2 раза, а XtManageChildren().)
    • БЛИН!!! Понял -- ведь атомарность "перетряски" набора managed решается тривиально -- надо пользоваться 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: появилась мысль, как имитировать "блокировку" обновлений на экране на время "перетряски содержимого" -- чтобы не мерцало (нужно скорее Гусеву, а не мне). Идея такая:

    • Создаем в не-managed состоянии какой-нибудь простой виджет (типа XmPrimitive либо XmDrawingArea), ставя ему все аттачменты в XmATTACH_FORM (так что он всегда занимает всю форму), причем так, чтобы он был САМЫМ ВЕРХНИМ -- т.е., в managed-состоянии закрывал бы всех. (Естественно, надо также сделать XmNtraversalOn:=False.)
    • Ставим ему backgroundPixmap в 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 выдает...

    Пара идей/возможностей (вообще-то -- надобность в них исчезающе мала):

    • Можно бы и "овосклицательнутым" заголовкам свернутым колонкам давать tooltip наподобие "Double click to expand column". Но тут есть небольшой проблем: при развороте ведь надо восстанавливать нормальный tooltip, а его-то могло и не быть. А вызова типа "XhDelBalloon()" у нас нету.
    • Технически несложно сделать сворачиваемость не только колонок, но и строк. Помимо сомнительной полезности такой фичи, есть еще практический вопрос -- чем заменять метку свернутой колонки? Ведь ЛЮБАЯ строка ее шрифта будет иметь минимум высоту одной строки.

    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) работать не будет. Так что если все-таки захочется использовать ту "скрывающую" технологию -- то надо использовать старый подход.

  • 02.05.2004: случайно обнаружил, что с давних времен (curcx/oldChl) осталось поле logchannet_t.bininfo.

    02.05.2004: убрано.

  • 12.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. Пофиксено.

  • 18.05.2004: а стоит ведь отделить создание "оболочки" в 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.

  • 03.06.2004: обнаружил мелкий, несмертельный баг в Chl_knobprops.c: если вызвать окно knobProps, а в нем нажать на [...] для вызова окна knobPropsRange, и затем закрыть knobProps при помощи window manager'а (double-click на [-] или click на [X]), то окно knobPropsRange останется.

    04.06.2004: исправлено очень просто: на knobProps.shell повешен XmNpopdownCallback, вызывающий ту же PropsCancelCB(), что и по [Cancel], а в ней добавлен вызов XtUnmanageChild(pdp->r.box).

    Поскольку knobPropsRange создается сразу вместе с knobProps, то никаких проверок "а существует ли..." делать не надо.

    Общий рецепт на будущее -- в "parent"-окнах при закрытии делать XtUnmanageChild() всех их child-окон.

    13.03.2007: угу, а про r.yesno_box тогда забыл -- позорище! Добавлено.

  • 10.06.2004: обнаружил в Chl_gui.c прискорбнейший ляп: в вызове 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: усовершенствования по манипуляциям "текущими" режимами.

    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()).

  • 12.07.2004: кстати: учитывая существование еще как минимум двух клиентов кроме chlclient/Chl_simple -- istcc и vacclient, может, стоит вытащить часть функциональности (тот же RecordEvent) из Chl_simple.c в, например, Chl_gui.c?

    03.05.2005: да, вытащил. Детали см. за сегодня.

  • 13.07.2004: кстати, а не пора ли ввести окошко и кнопку "Help"? Чтобы давалась краткая справка -- список используемых цветов (синий -- "не приходят данные от блока", бордовый -- "проблемы с железом, ткните правую кнопку и получите окошко, в котором будет указано в чем проблема"), список клавиш (надо ли?) и прочая подобная информация.

    Если делать -- то это тоже должно пойти в 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 по цветам показывается. Аллилуйя!

  • 13.07.2004: с учетом всех "появившестей", похоже, надо сегментировать Chl_gui.c на два файла; видимо -- _gui и _data (да, опять!).

    С другой стороны, разделить их будет трудновато -- уж шибко содержимое Chl_gui.c поперезавязано.

    12.12.2005: а вот нехрен, нехрен -- надо в CXv4 либо вообще все махинации с данными ПОЛНОСТЬЮ перевешивать на Cdr, либо сразу выделять в отдельный (от _gui) файл.

    06.08.2006: да ну нафиг -- надоел этот пункт в режиме "несделанный"; с учетом недавно происшедшего -- появления Chl_app, вытряхивания многоколоночности в Chl_multicol.c -- оно становится уже слабоосмысленно, а в будущей версии и вове будет по-другому; так что -- "obsolete".

  • 19.07.2004: так, надоело, что cda на всяких chlclient's в качестве сервера ссылается на "".

    Надо добавить к 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 напрямую).

  • 20.07.2004: по-моему, стоит вытащить "мясо" функций 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(), а остальные (как и подэлементы) ищет сама в "текущем" элементе.

    Интересные детали:

    • Замечание 1: в принципе, становится легко сделать "корневые" каналы -- парсер это позволяет; естественно, сейчас это имеет смысл только для "глобальных" имен.
    • Замечание 2: совершенно между делом получилось, что "глобальными" могут быть не только каналы, но и элементы. Это вышло "почти задаром" -- пришлось только вынести все содержимое CdrFindKnob() во внутреннюю функцию DoFindKnob(), избавив последнюю от проверок на тему что же найдено -- канал или подэлемент. Архитектура стала существенно элегантнее -- правильное разделение обязанностей, елы-палы :-).

      Есть одно ограничение: элементы самого верхнего уровня НЕ МОГУТ быть глобальными: ведь их ищет DoFindKnob(), возвращающий knobinfo_t*, а у них оного просто нету!

    • Еще замечание: поскольку имена ныне могут начинаться с '.', пришлось ее вставить в SkipIdentifier() в качестве допустимого символа в идентификаторе.
    • Вначале думал было делать функцию нерекуррентной, а просто иметь в буфере удлиняющееся/укорачивающееся имя-префикс, и сплавлять это имя CdrFindKnob()'у, но потом передумал и сделал рекуррентно -- чтобы не возиться с вопросом, а сколько же надо отводить буфер, чтобы влезла произвольная требующаяся вложенность.
    • Для выдачи сообщения об ошибках и последующего "облома-продолжения" изготовил хитрый макрос BARK_AND_NEXT(), с varargs'ами.
    • А вообще ясно видно, что сложность кода парсинга начинает зашкаливать, и надо бы, как минимум, почитать умные книжки по этому вопросу.

    BTW, обнаружил в старом коде загрузки еще один идеологический ляп: там считывалось значение для канала, делалось SetControlValue(), и лишь потом читались пределы. Но считываемое-то значение могло выходить из "текущих" пределов, а помещаться только в считываемые!

    Пара выводов по результатам изготовления полной поддержки вложенных элементов:
    1. Наша структура действительно несколько неидеально подходит для реализации деревьев:
      1. Верхний уровень делается другой структурой -- NULL-terminated массив groupelem'ов, причем "общие" характеристики указываются отдельно, в SimpleClients.lst; в то время как все глубже -- это элементы, содержащие массив известного количества knob'ов, плюс имя и прочие свойства указываются в самом элементе.
      2. Дублирование информации -- имя подэлемента имеется и в соответствующей строчке верхнего элемента, и в его собственном eleminfo_t. (А вот в файловой системе -- имя указывается в директории, а свойства -- в нем самом.)
      3. Путь от некоей точки "вверх" по дереву -- слабовозможен.
      В общем, устройство Unix'ных файловых систем более элегантно. Хотя, всякие наши мелочные условности слегка напоминают устройство NTFS'а -- там тоже нет четкого деления на "имя" и "свойства" -- и то, и другое содержится вместе в одной и той же строчке, например, в MFT.
    2. ОЧЕНЬ удобно оказалось использовать концепцию, появившуюся полгода назад в psp -- "если кто-то из пары [откуда парсить, куда складывать] -- NULL, то делать только пол-работы". Там указывался source=NULL, и оно только выполняло инициализацию.

      А в RecLoadLevel() весьма ко двору пришлась концепция, "если элемент-получатель -- NULL, то выполнять парсинг, но никуда результат не складывать". Это полезно в ситуации, когда в файле встретился элемент, которого у нас нет, но надо бы его содержимое из файла корректно пропустить.

    05.01.2005: удалил старый код CdrLoadGrouplistMode(), вместе с сателлиткой RangeName2R().

    05.01.2005: собственно, обозначенная цель достигнута, так что помечаем как "done".

  • 29.07.2004: сделал сходу: по мыслям от cx-starter'а переделал в Chl_leds.c LED'ы с индикации в состоянии "вдавлено" на "выпукло". Для этого везде позаменял XmNselectColor на XmNunselectColor, и вместо XmNset=True поддерживается состояние XmNset=False.
  • 04.08.2004: роясь вчера в Chl_gui.c, заметил, что там в ChlAddLeds() нет проверки, что тулбарная кнопка "leds" создана.

    04.08.2004: вставил в ChlAddLeds() проверку, что потенциальный parent!=NULL.

    В принципе, этого хватило, поскольку автоматически и srvcount'у ничего не присваивалось, так что он оставался нулем, и leds_update() отрабатывала вхолостую. Но от греха подальше вставил и туда проверку.

    12.11.2005: молодец, блин -- в той проверке было просто "return", а оно вообще-то значение должно возвращать. Почему не заметил -- вопрос. Вставил туда 0.

  • 21.09.2004: поскольку уже есть возможность пользоваться ChlCreateContainer()'ом, то теперь потребно иметь и "ChlCreateSimpleContainer()", чтобы не надо было заполнять свой eleminfo_t...

    05.10.2004: сделано.

    Есть некоторое "идеологическое" отличие от обычного ChlCreateContainer(): ему передаются два флага -- notitle и noshadow, а Simple-варианту -- все, включая эти флаги и заголовок, указывается вместе в psp-строке. Несмертельно, но -- просто отличие.

    18.04.2011@Снежинск-каземат-11: а ведь API ChlCreateContainer() ВООБЩЕ НИКЕМ не используется...

    (Об этом уже говорилось 28-03-2009 -- и что он вообще применялся только в старом istcc@2257.)

  • 30.09.2004: давняя проблема: что многоколоночные элементы можно делать только в случае, если count%ncols==0, т.е., если число каналов кратно числу столбцов. А могут быть случаи, когда это неудобно -- например, каналов 7 штук, их бы упхать в матрицу 2x4, а -- облом.

    Надо сделать так, чтобы оно корректно завершало цикл ДО исчерпания ncols*nrows, а по исчерпанию count. Соответственно подкорректировать и вычисление nrows.

    30.09.2004: сделано. Вставлено, во-первых, "умное" вычисление nrows -- (count+ncols-1)/nrows взамен простого count/ncols, а во-вторых -- дополнительное условие завершения цикла по столбцам -- на превышение count'а.

  • 30.09.2004: обнаружил, что в заголовках элементов не поддерживается многострочность -- т.е., '\n'.

    30.09.2004: точнее, обратил внимание, что там используется просто XmStringCreate() вместо XmStringCreateLtoR(). Естественно, поправил.

    Заодно совершил набег на тему "где XmStringCreate*() не-LtoR". Обнаружил!!! Вот результаты:

    • Knobs/Knobs_*_widget.c, где в список "*" входят alarmonoffled, button, dial, slider -- создание метки. Поправлено.
    • Knobs/Knobs_selector_widget.c -- и создание метки, и создание пунктов меню. Поправлено.
    • Xh/Xh_fdlg.c: там заголовок окна и шаблон имен файлов, где многострочность ни при чем, плюс надпись на кнопке [OK] -- вот там поправил.
    • Xh/Xh_statusline.c: определение строки для вывода. А вот тут, во-первых, многострочности делать нечего, а во-вторых, давно пора перейти с XmLabel на XmText.
    • runner/cx-starter.c -- море. Во-первых, при создании LED'ов -- там не тронул. Во-вторых, на кнопке с именем подистемы -- там тоже многострочность ни к чему. А вот дальше -- при создании меню "Server" и "Application" и в заголовках колонок на основной панели; вот в тех местах -- для полной корректности (на будущее?) поправил.
  • 21.10.2004: временами возникает потребность населять окно не строками, а столбцами.

    Вариант реализации -- вводим в ChlPopulateWorkspace() параметр is_vertical, а в Chl_simple.c::text2simpleopts[] -- флажок "vertical" (каковой можно будет указывать в SimpleClients.lst).

    21.10.2004: начал делать по вышеуказанному сценарию. Доп. параметр и опцию ввел, осталось только сделать "мясо".

    Часом позже: все сделал. Имена переменных сменены с "горизонтально-центрических" на "нейтральные": left->prev, row->line, prevrow->prevline. А там, где стояли аттачменты внутри контейнеров и самих контейнеров, вставлены дополнительные проверки "is_vertical?A:B".

  • 25.10.2004: захотелось иметь возможность указать tooltip прямо в канале так, чтобы он отображался только на метке, а на самом канале -- нет. Это затем, что писать tooltip в поле elemnet_t.rownames -- хреново (приходится руками синхронизировать при тасовке каналов). Понадобилось для nmagsys.

    А что, если -- когда первый символ 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.

  • 04.11.2004: надоело, что период протоколирования в Chl-клиентах -- 3 секунды. (Это, видимо, было введено ради istcc.)

    05.11.2004: сделал. Поскольку в istcc.c свой код, то в Chl_simple.c вставил 60 секунд.

  • 15.11.2004: итак, теперь есть возможность делать свернутые элементы вертикальными. Но нужно-то это не всегда -- если куча элементов друг под другом, то их надо сворачивать горизонтально.

    То ли выбирать направление в зависимости от направления группировки? То ли -- по параметру в options? А лучше -- по параметру, но в зависимости от направления выбирать значение по умолчанию.

    15.12.2005: лучше-то лучше, но внутри элемента пока нет способа узнать major-direction группировки. Так что -- это "лучше" пока забудем.

    А вот "по параметру" -- реализовал.

    • Флаги fold_h=0 и fold_v=1, оба не-default, маппирующиеся на поле 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? :-) -- надо делать хорошо ПРОДУМАННЫЙ и правильным образом ФРАГМЕНТИРОВАННЫЙ/инкапсулированный интерфейс "контейнеров".

  • 12.01.2005: пора, ой пора реализовывать проект "layoutinfo", через поле 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".

  • 14.04.2005: хочется иметь возможность СРАЗУ СОЗДАВАТЬ элементы уже свернутыми. Например, специальный флаг в options -- "folded". (Это понадобилось в первую очередь для МНТЦ/C13 -- чтобы панель КШД485 по умолчанию на экране не маячила.)

    15.04.2005: сделал.

    • Вытащил переключение свернутости в отдельную функцию SwitchContainerFoldedness(), в которой теперь заодно есть и проверки -- если хоть один из виджетов, меняющих managedness, ==NULL, то ничего не делаем.
    • Добавил в ChlCreateContainer() еще параметр folded, при включенности которого в конце сразу и вызываем сворачивание.
    • Вставлен парсинг флага "folded" в оба места -- ChlCreateSimpleContainer()/text2containeropts и ChlCreateElement()/text2elemopts.

    Кстати, а как будет обстоять дело с этим дублированием при переходе на CXv4 с плагинной архитектурой? :-)

  • 16.04.2005: обнаружилось, что при нажатии на лампочку-LED средней кнопкой мыши оно падает по SIGSEGV. И это и в обычных программах, и в cx-starter'е.

    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.

  • 03.05.2005: (первоначально было в разделе "Xh") и раз уж пошла пьянка про стандартизацию -- а как насчет стандартизировать также и {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_".

  • 03.05.2005:На примере вышеупомянутой парочки стало ясно, что ОЧЕНЬ не хватает в Chl возможности узнать "app_name" (реально это -- system_name), причем много для чего; а бедный Chl_simple.c даже держит оное в 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, для сохранения/загрузки картинок.

  • 03.05.2005: сходу -- выкинул из Chl_simple.c все упоминания про "print" -- и #include "pixmaps/btn_print.xpm", и закомментированную кнопку тулбара. А cmPrintMode исчезнет само, при стандартизации команд.

    (Это был артефакт много-многолетней давности -- еще с автоматизации запяткинского клистрона; прекратил свою реальную жизнь он еще с curcx/ года два назад.)

  • 04.05.2005: по результатам вчерашнего внедрения app_name/sysname в Chl: а чего это, собственно, у нас там есть деление Chl'ных данных на priv и priv2 (с уже сейчас весьма странным критерием деления)? Надо ввести ЕДИНЫЙ privrec, содержащий ВСЕ эти данные, и сделать внутри-Chl'ный модуль Chl_privrec.[ch], осуществляющий менеджмент. А для удобства можно и оставить нынешние функции-аксессоры типа DataInfoOf() -- уж откуда они будут брать свою информацию, их клиентов не касается.

    Собственно, это и правильно: Chl -- единая библиотека, логически -- "объект", и пусть она свои данные держит в единой структуре.

    В CXv4!

    16.07.2006: да, а priv2 мы оставим для "клиентов" -- пусть программа также имеет возможность хранить свои per-window данные.

  • 17.05.2005: еще давным-давно Паша и Федя-jr. высказывали пожелание, чтобы некоторые колонки элементов прямо сразу были бы свернуты.

    Чисто технически сворачивать их сразу после создания -- не проблема, как показало введение флага "folded".

    Проблема -- как сие указывать в описании элемента? У нас есть только поле colnames.

    Что, кроме опционального tip'а писАть там и опции, также через '^L' (MULTISTRING_OPTION_SEPARATOR)?

    20.05.2009: ну, собственно -- вот ровно в струю тех advancements с обычными пунктами селекторов (см. за 12-05-2009). Так что надо формат "Метка[|Тултип[|Стиль]]" (где "|" -- MULTISTRING_OPTION_SEPARATOR) обобщать, и в соответствующие API добычи заголовков столбцов/колонок тоже это добавить. Правда, есть тонкость: ведь при взятии общей строки от базовой ручки, СТИЛЬ-то НЕ надо использовать! И как? Тоже махинировать с '\n'?

  • 18.05.2005: сходу выкинул реально никогда не использовавшееся поле kpropsdlg_t.incdec_step.
  • 04.11.2005: а не отображать ли в окошке Knob_props еще и colstate?

    16.11.2005: сделал -- ввел строчку "State" между Flags и Source, которая обновляется вместе со значением. Аналогично bigval'у (откуда "мозги" и скопированы) -- обновление цвета производится только при реальном изменении. Кроме цвета, естественно, отображается еще и "надпись", поиск которой ведется по таблице (а не индексирование массива самим colalarm_t, что создало бы проблемы при перетряхивании списка возможных состояний).

    05.02.2007: стыдоба -- при том копировании мозгов забыл сделать отдельные поля deffg/defbg для этой строчки "State". Добавил отдельные поля col_deffg/col_defbg.

  • 12.12.2005: в linmag'е потребовалось иметь такой тип элемента, который как бы "много колонок одноколоночных" -- т.е., к каждой колонке данных -- своя колонка rowlabel'ов.

    Вообще-то, более правильно иметь не "колонка меток для каждой колонки ручек", а более общее "колонка меток для каждых 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: однако, сделал!!! Конкретика:

    • Количество "колонок на rowlabel" указывается в старших 16 битах, с надлежащим умолчанием (0=>numcols).
    • Код сделан так, что поддерживает ВСЕ варианты -- и "нормальный" MULTICOL, и SINGLECOL также эмулируется.
    • Посему -- после отладки старый код просто удален.
    • Формула для получения col-позиции x'той ручки --
      x + ((x / ncplc) + 1) * (opts.norowtitles == 0)
    • Заголовки столбцов и колонок теперь делаются не отдельными циклами или "снаружи", а прямо в самом нутри -- рядом с созданием knob'а: collabels -- при y==0, а rowlabels -- при (x%ncplc)==0 (оные создаются в колонке "x_knob'а-1").
    • Собственно тексты rowlabel'ов для не-первых колонок берутся ВСЕГДА из соответствующих ki -- для x!=0 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": надо б

    1. Все хорошенько заинкапсулировать (ну это, наверное, будет "Knobs_grid_container.c").
    2. Вытащить "координатные вычисления" в отдельные функции, чтоб не было дублирования.
    3. Как-то унифицировать хранение "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 вне диапазона" оно уставляется равным nrows; а при count==0 (пустой элемент) становится nrows==0, так что потом nlanes=nrows/nflrs вываливалось.

      Вставлена проверка, что при nflrs==0 делается nflrs=1.

    • Давний-давний баг (еще с mapp/uxil): если ncols==0, то вычисление nrows, производимое прямо в объявлении параметров, приводило бы к тому же SIGFPE.

      Вычисление вытащено из объявления, и теперь есть проверка if(ncols==0)ncols=1.

    А вообще -- ну сколько ж можно говорить, что перед делением всегда надо убеждаться, что делитель не равен нулю!

  • 14.12.2005: при реализации проекта "колонка меток на произвольное количество колонок данных" вылезло, что при метке "?" -- т.е., брать текст метки из knob'а -- если ki->label==NULL, то, как и указано в XmLabel(3Xm), ставится не пустая метка, а имя виджета -- т.е., "rowlabel".

    14.12.2005: ну исправил -- теперь get_label_and_tip() проверяет, что если в конце вышел NULL, то он возвращает "".

  • 14.12.2005: достало -- надо в Chl тоже вводить ABSTRZE()/CNCRTZE(). (Достало на примере Chl_gui.c.)

    14.12.2005: итак -- создал Chl_internals.h, который всего-то и содержит, что эти два макроса.

    И пошел шерстить Chl_gui.c. Ох и мутотень!!! И, кстати -- поменял в начале файла в декларациях attach*() "Widget" на "XhWidget".

  • 20.12.2005: эх-эх, нужен все-таки тип ЭЛЕМЕНТА -- "располагалка", как это делает группировка, с теми же правилами...

    20.12.2005: угу, эмулировать его как-то "кисло" -- шибко уж навернуто получается с вложенными элементами (да и у Антонова при этом кой-чего подглючивает :-), потому и захотелось такой тип элемента.

    НО: и как, спрашивается, будем, например, указывать флажки "fromnewline" содержимым?

    Одно радует: делать для такого элемента форму вместо сетки мы можем -- просто изготовим в дополнение к ChlGridMaker()'у еще и ChlFormMaker(), и указывать для ELEM_POPULATOR'а именно его.

    15.03.2006: этого хотелось в основном ради программы linmag, где хоть так, хоть этак имелись проблемы с расположением разнородных и разноразмерных элементов -- маленького BeamPC+/BeamPC- в "остатках площади" от магнитных системы и коррекции.

    Сейчас же (в основном -- благодаря многоподъездности) проблема решена -- те два канала просто подселены в конец магнитной системы.

    Так что помечаем раздел как "withdrawn", а уж в "правильной" организации GUI все получится автоматом.

  • 10.03.2006: имеется сильное желание заполучить element-plugins уже сейчас -- для "vacclient'а на Chl".

    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 был забыт. В нем тоже подправлено.

  • 03.05.2006: хоцца все-таки иметь возможность в многоколоночном элементе сменить "базовое направление" -- чтобы не только "по-европейски -- по строкам с переносами, и поток строк/этажей переносится по подъездам/страницам", а и "по-японски -- по столбцам с переносами, и поток столбцов/квазиподъездов переносится по корпусам/страницам".

    Это нужно, когда "родственные" ручки стоят рядом друг с другом не по горизонтали, а по вертикали; и по горизонтали идут "экземпляры" таких наборов.

    И вообще, кстати -- переименовать в 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: реализация также сделана. Краткие заметки по теме:

    • Ох и мерзкое ж это вышло занятие!!! Формально-то работа была straightforward -- казалось бы, просто повтори код для другой альтернативы, да и все; но повторять -- уродство, надо параметризовать. И в результате получается СЛИШКОМ много параметризации -- от этого код/алгоритм стал намного менее красивым, и весьма слабочитабельным.
    • Координаты-то транспонировать -- не проблема, для этого была введена функция sethv(), при включенном флаге transposed меняющая местами горизонтальное и вертикальное числа. От этого-то усложнилось несильно.
    • Проблема же в метках: то, как их называть ("rowlabel" либо "collabel"), а также h_align/v_align зависит именно от того, чем они являются НА ЭКРАНЕ, а не в "логической структуре". Так что пришлось вводить на все это переменные, которым вначале, в зависимости от 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 если б в элементе хранился...)

  • 04.05.2006: захотелось иметь возможность "заморозить" обновления данных в окне, как это было сделано в ipp, но только и для чисел тоже, именно в Chl-программах.

    Занадобилось это для программы 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.

  • 28.06.2006: в соответствии с замечаниями по vacclient'у от 26-06-2006, вытащил ту часть ChlPopulateWorkspace(), что занимается раскладыванием готовой группировки, в отдельную публичную функцию ChlLayOutGrouping().
  • 07.07.2006: надоело, что в каждую программу приходится копировать из Chl_simple.c кусок кода. По-хорошему, надо б эту функциональность "опубликовать".

    Проблема же -- в том, что там есть несколько СТАТИЧЕСКИХ переменных, а надо бы их сделать per-window. Добавляемся к priv2?

    08.07.2006: да, надо именно так. Несколько замечаний:

    1. Все "стандартные" команды должны быть объявлены в Chl.h и иметь имена cmChlNNN.
    2. Обработкой этих команд занимается публичная функция ChlHandleStdCommand(), возвращающая 1, если команда ей известна, и 0 -- если нет.

      Так что юзерские CommandProc'ы могут просто вызывать эту функцию перед/после своего switch()'а, по вкусу.

    3. Все {load,save}_{dialog,nfndlg} уходят в priv2rec_t.
    4. Переменная 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".

  • 21.07.2006: для программ, у которых стоит флаг "notoolbar", надо бы как-то уметь куда-то пхать лампочки -- а то полуслепота выходит...

    18.08.2006: "как-то" -- концептуально-то понятно как: надо иметь лишний вид knob'а, который бы и работал такой лампочкой, и впихивать его как-нибудь в группировку.

    Дело в том, что никакого иного разумного решения не прослеживается, просто потому, что при "notoolbar" у Chl'я просто НЕТУ места, куда он мог бы упхать лампочку -- ведь весь workspace занят не им, и предсказать, где будет свободное место, он не сможет. А вот у самой группировки такая информация есть.

    Проблема же -- в том, что knob'ы вообще-то НИЧЕГО не должны знать про Chl... Значит, реализовывать оное в виде knob-плагина в Chl'е, да?

    31.08.2006: да, надо делать именно так. См. раздел о "Chl_leds_knob". Этот же пункт прикрываем, считая за "done".

  • 29.07.2006: реализовал в Chl поддержку "alarm acknowledgement" по проекту "AckAlarm".

    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).

  • 31.07.2006: надо произвести еще более глубокое разделение Chl -- точнее, Chl_gui.c:
    • Во-первых, добавить в Chl элемент-"закладочник" -- поместив его в Chl_tabber.c.
    • Во-вторых, для полной уравниловки вытащить в отдельный файл Chl_multicol.c реализацию ELEM_MULTICOL/ELEM_SINGLECOL.
    • Таким образом, собственно "создание элементов" будет отделено от ELEM_MULTICOL, и станет самостоятельным набором функций -- ChlMakeElement() плюс ChlSetCustomElementsTable().
    • Далее, "стандартные методы" стоит также унести в отдельный файл -- например, Chl_std_emethods.c, а также опубликовать их, для использования в других element-плагинах (типа linipp'шных ELEM_IPP_nnn). При этом они переименуются из E_Nnn_m() в Chl_E_Nnn_m().
    • Ну и всякие там get_label_and_tip() надо опубликовывать во внутренний интерфейс.
    • И "container creation staff" -- то ли в отдельный файл (Chl_container.c?), то ли оставить вместе с менеджментом элементов.

    01.08.2006: поехали!

    • Поддержка ELEM_MULTICOL/ELEM_SINGLECOL переехала в Chl_multicol.c.
    • В ChlMakeElement() же остался простой перебор по eleminfo_t.type для выбора соответствующего типа element-плагина, с вызовом его "создавальщика".

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

    • Заодно пришлось унести в Chl_internals.h всякие IEH_SPACING/IEV_SPACING и "прототипы" attach_sss().
    • Также пришлось опубликовать для внутри-Chl'ного использования 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 прототипы удалены).
    • С применением вышеозначенных опубликованностей был добавлен модуль Chl_tabber.c, реализующий тип ELEM_TABBER (свежедобавленный в cxdata.h). Реально оное было с минимальными изменениями взято из work/karpov/xmclients/linbpm_knobplugins.c; linbpm теперь можно переводить на ELEM_TABBER.

      В методах этого элемента в позициях ShowAlarm, AckAlarm и NewData стоят NULL -- чтобы "визуальная иерархия" заканчивалась внутри закладок.

    • Поддержка "стандартных опций элементов" была стандартизована (sorry за тавтологию) -- структура имеет теперь имя _Chl_std_elemopts_t, таблица содержится в Chl_internals.c (каковой пришлось создать ради этого), а ссылку на нее отдает _Chl_GetStd_text2elemopts().

    Заодно:

    1. ChlSetChanVal() переведена с тупого вызова E_SetPhysValue_m(ki,v) на "кошерный" вызов ki->uplink->emlink->SetPhysValue(ki,v).
    2. Пофиксен также давний ляп -- почему-то в ChlCreateContainer() была строчка e->emlink=&emethods (видать, оставалась с момента добавления интерфейса контейнеров); убрана.

    Таким образом:

    • Все типы-элементов содержатся в своих отдельных файлах-модулях.
    • В Chl_gui.c осталась фактически некая смесь "_gui" и "_data" -- "high-level"-работа с данными плюс некая базовая поддержка GUI. В т.ч. -- "расстановщик" элементов, который в v4 будет отдельным типом элемента.
    • Остальные модули (состоявшие бы из растащенного по частям Chl_gui.c) решено было не делать -- нафиг.

    В общем, "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: интересный вопрос: а почему в окне BigVal отображается только число, а units -- нет?

    21.08.2006: сделал отображение и units тоже. Собственно, просто внаглую воспользовался функцией SetTextString(), доступной в KnobsI.h. А та уж так удобно устроена, что смотрит не на ki->is_rw, а на w::XmNeditable (видимо, для всяких крутилок и бегунков в режиме "value"), и, поскольку big.val всегда не-editable, то units добавляются всегда.

  • 31.08.2006: давным-давно пора "опубликовать" размер spacing'ов между ручками, которые сейчас забиты в 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.

  • 08.04.2007: при изготовлении cdrclient'а стало очевидно, что всякие CHL_{MODE,DATA,LOG,OPS}_PATTERN_FMT должны определяться вовсе не в Chl.h (ибо они вовсе не привязаны к GUI), а в каком-то другом месте. В Cdr'е, что ли?
  • 09.04.2007: сделал в 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: еще мысли по теме:

    • 1. Есть ведь поле elem_private -- может, использовать его?

      Неа, нельзя -- ведь использование этого поля зависит от конкретного типа элемента, а Chl_E_SetPhysValue_m() сейчас всегда используется один и тот же.

    • 1.5. Ну так что -- ввести дополнительное поле типа "_windowref"?
    • 2. А вообще-то надо бы ввести для использующих метод 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'а:

    • Если у самого subwin'а нет ключика notitle, то при double-click на заголовке-элемента имеем SIGSEGV.
    • Причина -- в Chl_subwin.c есть строка
      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: надо в дополнение к "DataArrivedCallback" иметь еще и некий callback по изменению конкретной ручки пользователем. Конкретная потребность -- для kls, чтобы СРАЗУ предпринимать какие-то действия. Т.е. -- как бы локальные ручки.

    07.05.2007: вообще-то надо бы хорошенько подумать. Реально ли надо делать именно "callback по вводу юзером"? И как именно программа должна будет этим пользоваться? И -- ведь, по идее, "синтетические" ручки (те, которые без привязки к каналам) должны бы все равно как-то фунциклировать через локальные регистры.

    Думать, думать, и еще раз обдумывать!

    20.05.2008: хо -- а это оч-чень пересекается с уже реализованной недавно фичей cda_set_lclregchg_cb()! Поскольку сейчас того вполне хватит, а в CXv4 вообще будут настоящие локальные каналы, то тему можно закрыть с классом "obsolete".

  • 07.05.2007: хочется по образу и подобию adc200 уметь сворачивать элементы не в надписи, а в полосателькие сепараторы.

    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() с кучи булевских параметров на один параметр "флаги".

  • 14.05.2007: ввел, для потребностей ndbp_staroio.c, функцию-переходник к CdrFindKnob() -- ChlFindKnob(). Она позволяет получить прямой доступ к интересующему каналу.
  • 20.05.2007: увеличил количество локальных регистров с 100 до 1000 -- для потребностей linmag'а (коэффициенты для fedcalc'а).

    13.07.2010: ага, а в cx-starter'е-то тогда забыл тоже увеличить -- бодро оно на liucc орало про "invalid local register"! Поправлено.

    08.10.2014@Снежинск-каземат-11: поскольку в liu число регистров уже приближается к 1000, увеличиваем уже до 4000 -- скорее всего, более не потребуется.

  • 19.05.2008: хочется уметь -- для всяких nsukhphase -- приаттачивать элементы и к правым/нижним границам окон, чтобы они ресайзились вместе с окном.

    (Тема тесно связана с флагом 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, которая тут отсутствует... :-(

    А что сделал:

    • Обычный "fromnewline" переделан во флажок GU_FLAG_FROMNEWLINE.
    • Введены флажки GU_FLAG_FILLHORZ и GU_FLAG_FILLVERT.
    • При наличии флага по соответствующему направлению аттачится к правому (нижнему) краю своего parent'а и ew, и контейнер тоже.
    • Причем, оказывается, ни элементы "перпендикулярно направлению" (т.е., top обычно и left при is_vertical), ни контейнеры вдоль него (left/top) не аттачились, так что при аттачментах к правому/нижнему краям они только к этим краям и оставались. Так что теперь соответствующие аттачменты делаются.
    • После растягивания по "базовому" направлению автоматически force'ится "перенос строки" со следующего элемента.
    • Плюс, при растягивании ПЕРПЕНДИКУЛЯРНО базовому направлению строка-то должна быть последней. Поэтому вставлена проверка, и при появлении еще строк выдается warning на stderr.
    • А самое главное -- код теперь стал окончательно монструозным, и напоминает жуткостью код 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: захотелось уметь менять расстояние между элементами группировки на экране -- также для liucc.

    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" -- для отключения XmSeparator'а под заголовком. Сегодня очень-очень понадобилось -- для weldcc с его под-элементами, чтобы элементы 2-го уровня визуально отличались от 1-го уровня.

    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: Поскольку сворачивание вложенных элементов иногда заставляет SGL глючить, то очевидным образом надо иметь флажок "nofold".

    04.04.2009: типа протокол:

    1. Наконец-то пора перевести ChlCreateContainer() с кучи параметров на один параметр flags. Это и было сделано -- флаги имеют префикс CHL_CC_, а в 4cx/ -- MOTIFKNOBS_CF_.
    2. Во-вторых, был введен флажок "nofold" -- исполнены все щелчки хвостом для его объявления, парсенья, передачи и использования.
    3. В-третьих, имелась историческая кривость с флагами "fold_v" и "sepfold" -- что они передаются не параметрами, а существует отдельным полем в eleminfo_t. Поправил, выкинув эти поля opt_fold_v и opt_sepfold, и сделав обработку аналогично прочим флагам (в 4cx/ именно так было сделано изначально).

      (Встаёт вопрос -- а какого лешего они вообще были сделаны полями, а не передавались параметрами? Не хотел список параметров перетрясать?)

    4. Переделал с дурной схемы "fold_v=>1" на "fold_v=>0, fold_h=>1", как в 4cx/ (кстати, и ТАМ имелась ошибка в описании параметров -- исправил).
  • 25.06.2009: уже давно точит такая мысль-потребность: а надо ведь уметь располагать содержимое основного окна не только строками/столбцами, но и более прихотливо. Примерно как окна на экране -- чтобы некий элемент можно было впихивать в свободное место, оставшееся от элементов из НЕСКОЛЬКИХ строк.

    (Заново эта потребность вылезла всё в том же пресловутом weldcc.)

    25.06.2009: да, хочется-то хочется, но уж шибко это мерзко и муторно. И вот резоны:

    • Сам такой алгоритм -- фиг знает, где добыть. Из FVWM'а взять, что ли?
    • Элементы-то могут и размер поменять, и что тогда делать?
    • Если совсем невтерпеж -- то можно, в принципе, нужного эффекта добиться и canvas'ом.

    Так что -- нафиг-нафиг, не будем делать, "withdrawn".

  • 13.11.2009: возникает потребность уметь делать ЛОКАЛЬНОЕ сохранение/восстановление режимов -- per-element.

    13.11.2009: хочется это для LIU-программы balkon -- там в одном окне есть несколько сравнительно слабосвязанных элементов, которые надо бы уметь сохранять/восстанавливать независимо.

    Идея реализации -- завести маленькие пиктограммки "Save" и "Load", и чтоб они могли помещаться справа от метки элемента.

    Следствия:

    1. Размещать их там может и, следовательно, будет ChlCreateContainer(), соответственно, жить оно будет в Chl_gui.c.
    2. Потребность в этих кнопочках указываться будет через параметр flags.

    Детали проекта реализации:

    • Поскольку обработка команд пока централизованная, а не broadcast'ная, да и команды числовые, а не текстовые, то будем придерживаться идеи "в конкретный момент может быть открыто только одно окно сохранения или загрузки", и где-то per-window хранить указатель на элемент-клиент.
    • Префикс имени файла, видимо, должен формироваться как SUBSYSTEM.PATH.TO.ELEMENT.
    • Собственно РАБОТА -- сохранение/восстановление -- делается Cdr'ом, а уж там-то оно давно сделано поэлементно, так что нужно лишь правильные функции вызывать (возможно, понадобится сделать парочку функций-адаптеров).

    13.12.2009: (ровно через месяц!) насчет того, как именно будут помещаться кнопочки рядом с заголовком -- см. в секции "Еще немного о красивостях" раздел за сегодня о помещении ручек/подэлементов рядом с заголовком.

    18.08.2010: кстати, а собственно само сохранение и считывание режимов per-element на уровне Cdr делается просто -- достаточно сделать публичные API-обёртки к функциям RecSaveElement() и RecLoadLevel().

    А дальше вопрос будет уже в создании обвязки, обеспечивающей а) fdlg-"распознавание" таких "локальных" режимов; б) файловые диалоги.

    И, кстати, отдельный вопрос -- не делать ли эти файлы "локальных режимов" годными для загрузки и напрямую в основную группировку тоже? Тяжковато, ибо варианты таковы:

    1. Можно в файл добавлять "обвязку", вписывая ПЕРЕД элементом строчки ".element NNN" для всех его содержателей, а ПОСЛЕ -- соответствующее количество "}". Тогда этот файл будет загружабелен и в качестве глобального режима; соответственно, и грузиться такие файлы ВСЕГДА должны будут как локальные режимы.

      Но тут проблема будет в случае, если кто-то из содержателей имеет не-IdentifierIsSensible()-имя: без такой обвязки элемент можно было бы сохранить/восстановить, а с ней -- фиг.

    2. Если же такого НЕ делать -- то локальная загружаемость будет доступна всегда, а глобальная -- всегда НЕТ.

    Итого: как сделать -- всё понятно (и со стороны Cdr, и со стороны размещения кнопок на элементе), но собственно КРИТИЧЕСКОЙ надобности сейчас нет, так что -- откладываем, а как понадобится, сделаем за день.

  • 18.04.2011@Снежинск-каземат-11: делаем некоторый улучшайзинг по проекту bigfile-0002.html::04-02-2011 -- чтоб можно было:
    1. Заголовки элементов размещать с любого бока,
    2. Часть ручек помещать прямо в заголовок.
    3. Помещать в заголовок мини-тулбарчик.

    18.04.2011@Снежинск-каземат-11: последовательность действий:

    1. Первым делом -- некоторые инфраструктурные изменения: структурируем опции разных типов элементов:
      • Убираем _Chl_std_elemopts_t+_Chl_GetStd_text2elemopts().
      • Вводим в Chl_internals.[ch] _Chl_containeropts_t+text2_Chl_containeropts[].
      • В Chl_multicol.h -- _Chl_multicolopts_t+text2_Chl_multicolopts[], оно "включает" обще-контейнерные поля.
      • В неиспользуемом ChlCreateSimpleContainer() собственная "containeropts_t" была изведена в пользу стандартной INCLUDE-таблицы.
    2. Затем, решено, что останется схема "передаётся слово флагов", а не будет перехода на "передавать указатель на выпарсенную структуру" (как задумано в 0002'шном проекте).

      Посему -- сделаны значения:

      • 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.
    3. Knobs_typesP.h::eleminfo_t:
      • Странное название "compact" сменено на "folded", как было в liu/fastadc/.
      • Добавлены поля-виджеты toolbar и attitle.
    4. ChlCreateContainer() -- махинации с собственно размещением виджетов:
      • В отличие от проекта bigfile-0001.html::13-12-2009, тулбар и attitle-ручки помещаются в РАЗНЫХ формах, по РАЗНЫЕ стороны от заголовка.
      • Заголовок теперь помещается не напрямую в container, а в ttl_cont.

        09.11.2015: а вот это дало и минус: при моргании ALARM'ом заголовок теперь не мигает, поскольку не является непосредственным child'ом container'а.

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

        Соответственно, caption аттачится к ним (а при отсутствии -- к форме), так что эффективно центрируется в отведённом ему месте.

      • Сделано размещение пары "Заголовок+сепаратор" по разным сторонам.

        Код стал ПРЕМОНСТРУОЗНЕЙШИМ -- толпа последовательных и вложенных if()'ов для аттачментов.

      • Ну и обработка выпарсенного title_side для передачи через флаги во всех юзеров вставлена.

    19.04.2011@Снежинск-каземат-11: продолжаем:

    1. Реализация "attitle" в 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.
      • И теперь поедет восстановление метки при обратном разворачивании колонок -- при условии, что метка берётся от ручки, а не из colnames...

        14.07.2014: Решение просто: надо nattl сохранять в privrec'е и при разворачивании учитывать.

    2. В ELEM_CANVAS_Create_m() внесены аналогичные изменения (хотя и в меньшем объёме).
    3. Потребовался также дополнительный обслуживающий функционал:
      • ChlPopulateContainerAttitle() -- набивает виджет attitle, горизонтально или вертикально.
      • В его интересах -- ChlGiveBirthToKnobAt(), создающая ручку/подэлемент в УКАЗАННОМ parent (вместо всегда-в-e->innage).

    Хотя мини-тулбар и не работает (он -- прерогатива предыдущего раздела), всё остальное есть, так что "done".

    21.04.2011@Снежинск-каземат-11: тонкость-неприятность -- по дурости контейнеру attitle делалось XmNtraversalOn=False, так что заголовочные ручки не фокусировались (для LOGD_TEXT -- это критично).

    Пофиксил.

    (Что интересно, сам контейнер заголовка -- фокусировке вовсе не препятствует.)

    26.09.2011@Снежинск-каземат-11: довольно уродливо выглядит то, что поля заголовков (title) ОТЛИЧАЮТСЯ от полей обычных ручек -- в результате ручки в заголовке с заголовком не выровнены.

    Улучшаем:

    1. Имя виджета "заголовок" изменено с безликого "caption1" на "elemCaption" -- для определённости.
    2. В Xh_fallbacks.h добавлена секция для *.elemCaption -- скопирована с *.rowlabel.
    3. И еще одна проблема -- из-за которой это всё вообще было замечено -- располагалась в Chl_submenu.c: у виджета "кнопка выпадания меню" было имя "submenu_btn".

      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'ам -- они были б весьма полезны в однострочном "элементе" для разделения смысловых групп ручек.

  • 27.10.2011@Снежинск-каземат-11: сходу: введена ChlGetGrouplist() -- чтоб сторонние knob-плагины могли пользоваться Cdr_script'ом.
  • 12.05.2012@Снежинск-каземат-11: теперь common_elem_macros.h поселен в cx/src/include/, поскольку задрало уже при каждом добавлении не забывать копировать во все директории проектов.
  • 30.10.2012: сходу: введена ChlIsReadonly() -- аксессор к флагу is_readonly, для fastadc/pzframe-knob-plugin'ов.
  • 26.02.2013: сделана пиктограмма "маленькая дискетка", и все мини-кнопки "сохранения" переведены на неё.

    Это коснулось fastadc_gui.c, его будущего варианта yzframe_gui.c, liu/y/manyadcs_knobplugin.c, а за компанию -- и Chl_histplot.c.

Chl_app:
  • 19.07.2006: Новый модуль -- "Chl_app".

    Смысл его заключается в следующем: стандартизация функциональности, располагавшейся в первую очередь в 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: итак, последовательность действий была такой:

    1. Первым делом все содержимое ChlRunSimpleApp() было обобщено и помещено в функцию ChlRunApp() -- каковая исполняет ВСЕ стандартные действия по "раскрутке" программы (в т.ч. fallbacks и регистрацию Xt-окружения для cda). (Всякие специфичности, типа загрузки группировки и регистрации plugin-компонентов, должны делаться ПЕРЕД ее вызовом.)

      Были добавлены параметры toolslist, CommandProc, newdataproc+userptr, и все параметры пересгруппированы по "смыслу" -- сначала argc+argv[], потом описание окна/GUI, а в конце -- описание "данных".

      И -- ChlRunSimpleApp() теперь содержит просто ее вызов, с указанием СВОИХ тулбара и обработчика команд.

      P.S. Да, этот интерфейс НЕ подходит для многооконных программ, но -- у них обычно несколько иное устройство, да и -- когда они последний раз требовались? :-)

    2. Затем туда же переехал ChlHandleStdCommand() со своими helper'ами.

      Так что Chl_simple.c стал маленький-маленький и простенький-простенький -- переходник-wrapper.

    3. Далее последовали сами "стандартные действия", вызываемые стандартным обработчиком команд (причем RecordEvent() и его использование сами переехали в _gui из _simple прошлой весной).

      Ради них пришлось перетащить в Chl_priv2.[ch] и datainfo_t+DataInfoOf(). Реально шаг к объединению этих структур в одну.

    Так что -- в Chl_app.c теперь и живут всякости, которые "составляют суть Chl'я".

    А в Chl_gui.c остались три класса вещей:

    1. Расселение в окне группировки и реализация элементов -- то, что потом уйдет в "libKnobs v4" (а досточтимая get_label_and_tip(), которой, наверное, уже давно икается -- в "liblogchans").
    2. Махинации с данными: собственно "поддержка" их (datainfo etc.) и API для прикладных программ (ChlSet{Sysname,Server,PhysInfo,Grouplist}(), ChlFreeze(), ChlSetDataArrivedCallback(), ChlGetMainSid(), Chl{Get,Set}ChanVal()).

      Собственно -- это и есть то, что желалось 13-07-2004 унести в отдельный модуль _data. И "поперезавязанность" уже практически исчезла.

    3. "Остальное", "всякое разное некатегорируемое" -- оное представлено лампочками Chl_leds.c, коие действительно нифига не категорируются.

    Подперетряхнул порядок всех этих вещей -- чтоб шло пологичнее, "по функциональным группам". Все окей -- кроме одного: бедненькая UpdateChannels() вынуждена вызывать CycleAlarm() для каждого элемента. А по-хорошему-то это должно делаться прямо при ОБСЧЕТЕ -- тот самый метод knobs_emethods_t.NewData() пусть и делает.

    Также кардинально перекурочил файл Chl.h. Он был результатом долгой эволюции -- полным бардаком. Теперь все упорядочено по группам -- как в .c-файлах.

    Итак -- "done".

    28.07.2006: да, перевел вызов CycleAlarm() в метод NewData().

  • 28.07.2006: було забыто стандартизировать "freeze".

    28.07.2006: сделано.

    1. Добавлена команда cmChlFreeze.
    2. Ее обработка в ChlHandleStdCommand().
    3. А в ChlFreeze() появилась возможность указывать не точное состояние, а "переключи" -- -1. Так что теперь -- и это надо бы сделать общим принципом! -- >0 -- включить, 0 -- выключить, <0 -- переключить.
  • 24.08.2006: наткнулся на давний misdesign в ChlRecordEvent() -- он не добавлял в конце "\n". Раньше-то его использование было ограничено внутренностями самого Chl'я (которые передавали "\n" сами), а вот сегодня наконец-то появилось первое внешнее применение -- сообщения о работе с фоном в linipp.

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

    P.S. Собственно, общее правило -- нефиг! Плюс -- унификация с XhMakeMessage().

  • 22.01.2007: обнаружилось, что для приложений -- ChlRunApp() -- отсутствует флажок compact, который бы маппировался на XhWindowCompactMask.

    Сделал.

  • 25.01.2007: в ChlRunApp() отсутствовала проверка результата вызова ChlPopulateWorkspace() -- стыд и позор! В результате группировка с глючками приводила к появлению окна с оформлением -- тулбар, statusline, ..., но без содержимого.

    Исправил.

  • 07.03.2007: для ndbp понадобилась возможность указывать несколько измененные fallback'и (для эмуляции гусиных шрифтов). Первое побуждение -- сделать в ndbp.c собственную копию ChlRunApp(); но оно слишком велико. Так что -- просто добавил к ChlRunApp() параметр fallbacks (после options), который если NULL -- то используются обычные фоллбэки. Точек использования ChlRunApp() было немного, все они подправлены.
  • 21.05.2007: потребен еще один ключик -- "smalltoolbar", который бы приводил к созданию toolbar'а с МАЛЕНЬКИМИ кнопками. Потребно это для всяких программ с небольшими окнами (типа subharmonic'а), у которых toolbar выходит шире содержимого окна.

    21.05.2007: Проблема только -- что этот ключик легче отрабатывать ChlRunSimpleApp()'у, поскольку он тогда мог бы подменять toolslist[], но -- он ведь является просто wrapper'ом, и никакого отношения к ключикам не имеет...

    Первоначально-то (29-04-2007) была мысля воспользоваться XhHalvePixmap()'ом, чтобы он сам кнопочки уменьшал, но -- тот проект с треском провалился.

    Но потребность-то никуда не делась, надо задачу как-то решать...

  • 06.06.2007: был некоторый ляп -- что при указании chl-клиентам непонимаемых ими ключиков, то Xt эти ключики не съедала, и программы это воспринимали как ссылку на сервер и пытались приконнектиться к этому "-option".

    06.06.2007: вставил в ChlRunApp() проверку, что при srvspec[0]=='-' просто ругаемся.

  • 01.05.2008@пляж, прогулка: у нас уже много специализированных программ-мультиклиентов, функционирующих подобно chlclient'у (загружающих группировку по argv[0]), но с отличиями -- регистрируют свои ручки/элементы, используют другой toolbar, и т.д. Таковыми являются bpmclient, ippclient, cdmclient, tantalclient, будущий "istcclient" и, возможно, "sukhphaseclient". (Замечание: примером НЕ попадающего в эту категорию клиента является vacclient)

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

    А ведь явно напрашивается стандартизовать эти общие для мультиклиентов действия: сделать еще один уровень-надстройку над 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]
      Но это вопрос нескольких часов мутроного кодирования -- зато сразу ВСЕ программы-юзеры научатся.
    • Проблема "курицы и яйца" покамест решена радикально -- unconditionally берется argv[0], а app_name/app_class берутся из загруженной группировки.
    • Собственно загрузка указанного в командной строке файла покамест отсутствует (хотя параметр и предусмотрен) -- см. ниже.
    • И вообще, надо не ChlRun{,Simple,Multiclient}App() сводить к друг дружке, а сделать некую глобалистичную функцию, выполняющую ВСЮ работу (ну, кроме загрузки группировки), а этих всех сделать wrapper'ами к ней.

      Очень-очень похоже на execve() с кучей ее frontend'ов.

    20.06.2009: сегодня на ChlRunMulticlientApp() переведены практически все обозначенные в исходном ТЗ программы. И chlclient, и ndbp, и ippclient, и bpmclient. Вновь создаваемые мультиклиенты также делаются на нем.

    Никакой "глобалистичной функции" делать не стал -- удобнее оказалось именно как описано в первоначальной пляжной идее.

    Хотя супер-задачи (типа загрузки файлов и запускан напрямую) пока и не сделаны, но сделано основное -- так что помечаем как "done".

    14.06.2010: хочется всё-таки уметь и загружать файлы, и уставлять прямо из командной строки значения в каналы. В двух целях:

    1. Грузить при старте программы всякие коэффициенты, которые живут в регистрах, плюс usrtxt-метки.

      Для этого можно из файлов режимов выкусывать все "настоящие" каналы, оставляя только такие локальные.

    2. Уставлять некие "стандартные" значения, в т.ч. живущие исключительно в памяти драйверов, а не в аппаратуре -- типа скорости/ускрения у КШД485.

    Как это можно реализовать:

    • Общий подход взять из fastadc_common.c::RunFastADC() & Co. -- с PK_xxx.
    • Только синтаксис определения чуть другой:
      • вместо PK_BIGC -- PK_SRVR (и p_xc фтопку);
      • вместо PK_PARAM -- PK_CHAN, причем, естественно, имя может кроме only_letters() содержать и '.'.
    • За компанию можно и '-'-ключи как бы поддерживать.
    • Поскольку всё может указываться вперемежку, в т.ч. -- значения могут указываться еще ДО сервера, то надо парсить в 2 прохода: на первом брать только srvspec, потом между проходами "реализовывать" программу, а на 2-м проходе -- уже уставлять значения и грузить файлы.
    • Замечание: в случае с НАСТОЯЩИМИ Chl-программами -- никакая floadproc и не нужна, вполне достаточно обычной ChlLoadWindowMode(), так что можно смело этот параметр выкидывать.

    22.06.2010: да, сделано, ровно по указанному проекту.

    Единственное что -- уж очень уродско и наколенно это всё выглядит, особенно рукодельный парсинг USRTXT-knob'ов.

  • 19.05.2008: для всяких нестандартных-клиентов типа nsukhphase надо бы позволять resize окна.

    20.05.2008: сделал -- ввел appopts-флаг "resizable", при котором бит XhWindowUnresizableMask сбрасывается.

    (Поскольку в 4cx/ вообще пока win_options не используется, то forw-портировать пока некуда, но в будущем -- потребуется.)

    21.05.2008: да, и в 4cx/ также портировал.

  • 12.07.2008: касательно логгинга -- требуется иметь прямо GUI-интерфейс для возможности смены интервала протоколирования и для опустошения log-файла. Это потребовалось Жмурикову, в программе impacis10.
  • 04.08.2008: и еще из той же оперы -- Губину захотелось иметь в программе gubtherm интервал логгинга не стандартные 60 секунд, а 3. Очевидное решение -- дать возможность указывать этот период в опциях программы.

    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).

    Корень-то проблемы в том, что

    1. vacclient НЕ пользуется стандартным ChlRun*App().
    2. "Глобальные" флаги (logperiod, notoolbar, ...) лежат в одной таблице с ChlLayOutGrouping-specific (hspc, vspc).

    Но пока просто перенес проверку if(period==0)period=DEF_LOG_PERIOD в собственно ChlSwitchLogging().

    Проверил -- работает.

    04.04.2010: да, собственно -- теперь при логгинге выдаются и миллисекунды, через точку (и в числе секунд, и после stroftime() -- аналогично canmon'у (но в том МИКРОсекунды)).

    Вышеописанная проблема и вылезла при проверке работающести.

    01.07.2010: ну не очень-то 04-04-2010 была пофиксена работа logperiod'а -- в ChlRunApp() имелась странная строка

    lr->defperiod = opts.logperiod > 0;
    
    так что ЛЮБОЙ указанный период сводился к 1 секунде. Поправлено на нормальное условное присвоение.

    01.07.2010: собственно, ради чего я туда полез: Карнаев хочет иметь протокол с частотой ВЫШЕ 1Гц -- для ращенкятника, там хочется 5Гц. А у нас всё в целочисленных секундах...

    По факту -- хочется иметь 2 фичи:

    1. Возможность указывать ДРОБНЫЙ период.
    2. Возможность указать "логгируй данные по приходу от сервера".

      (Да, имеет смысл только для первичных серверов, с aux бесполезно.)

    Как можно сделать:

    1. Все API-вызовы перевести с целых секунд на ЦЕЛЫЕ же МИЛЛИСЕКУНДЫ (микросекунды низзя -- не влезет 86400с в 32 бита, да и незачем).
    2. Парсить info в ВЕЩЕСТВЕННЫЕ числа, при передаче в API домножая их на 1000.
    3. Указывать же "по приходу" надо, видимо, отрицательным периодом -- например, -1.
    4. Для чего придётся перейти от нынешней хаковатой модели "lr->период<0 => это первый раз" к более вменяемой модели с отдельным полем "как сейчас логгируем: никак, первый_раз, продолжаем".

    Первые 2 пункта реализованы -- так что, в принципе, для текущих потребностей достаточно. (Кстати, единественным внедеревным файлом, использующим данный API, оказался impacis10.c, где пришлось заменить 3 на 3*1000.)

  • 19.06.2009: добавляем еще один "модуль" -- Chl_stdtoolbar.c.

    (Оно хоть и отдельным файлом, но по смыслу -- часть 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: давно уже юзеры высказывают пожелание, чтоб программа при загрузке "подхватывала" некие установки/параметры интерфейса. Как то -- подписи, коэффициенты, ...

    28.12.2009: предварительные соображения:

    1. Очевидно, что оные "настройки" должны считываться стандартным образом из файлов режимов.
    2. Конкретно к желаемым настройкам относятся (кроме будущих подписей/LOG?_меток): пределы, incdec_step, параметры в регистрах, а также локальные-для-программы штучки, типа границ отображения графиков в ndbp (оные можно свести к ручкам, отображаемым на регистры).

    Идея решения довольно проста:

    надо при запуске вызывать считывание некоего файла-режима со специальным именем, но при этом блокировать собственно отправку данных.
    Это решалось бы прямо в Chl'е -- поскольку Cdr вызывает ki->uplink->emlink->SetPhysValue(), каковой по факту является Chl_E_SetPhysValue_m(), то можно б было в оном проверять некий флажок в datainfo_t-записи окна. Но не всё так просто:
    1. Для начала -- ведь Cdr::SetControlValue() ВСЕ РАВНО будет вызывать у каждой ручки ki->vmtlink->SetValue(), так что в ней отобразится неиспользуемое значение.
    2. Дальше -- хуже: и внутренние переменные в ручках оно потрогает (userval, userval_valid, wasjustset).
    3. И -- ведь при "блокировании" в Chl_E_SetPhysValue_m() и регистровые параметры также будут проигнорированы (а не только запись в каналы). А иначе -- уставка регистров ведь делается формулами, а там может затесаться и запись в каналы.

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

      1. Флаг/параметр "не трогать физические каналы" должен присутствовать ВО ВСЕХ 3 уровнях -- Chl, Cdr, cda. (Конкретно в SetControlValue() -- видимо, поменять параметр fromlocal на source, с вариантами SCVS_FROMSERVER=0, SCVS_FROMLOCAL=1, SCVS_FROMCONFIG=2.)
      2. В appopts_t добавляется флажок типа "loaddefsettings", при указанности которого в options оно загрузит файл в таком полу-режиме.

    30.12.2009: хорошенько подумал -- да ну его нафиг!!! Столько мучений и лазанья в самые глубокие внутренности библиотек ради довольно-таки дурацкой потребности, которая вылезает ТОЛЬКО в не-DataBase-endbled-системах, причем ТОЛЬКО для локальных-стендоподобных программ. Так что -- "withdrawn".

    Хотя в CXv4, конечно, надо будет сию потребность учитывать и предусмотреть параметр "flags/options" для всех участвующих в загрузке функциях, чтоб можно было указывать "не исполнять запись в каналы".

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

    • Возможность указывать загружатору необходимость игнорировать часть классов данных -- параметр options с битовыми флагами.
      1. Вводим флажки CDR_OPT_NOLOAD_VALUES и CDR_OPT_NOLOAD_RANGES (они на всякий случай идут с 31 вниз, так что не пересекаются со всякими READONLY).
      2. Добавляем параметр options функциям ChlLoadWindowMode(), CdrLoadGrouplistMode() и её подчинённым RecLoadLevel(), ParseUsrTxt(), ParseChanVal().
      3. В ParseChanVal() содержит проверки. Причём incdec_step и grpcoeff отнесены к VALUES.
      4. Метка в USRTXT тоже пока относится к VALUES. Хотя это вопрос -- возможно, её надо считать за "конфигурацию" и считывать при старте (но тогда поедет всё в liu).
    • Собственно вызов загрузки: во всех имевшихся местах указывается CDR_OPT_NOLOAD_RANGES.
    • Добавлен appopts-флаг loaddefsettings, пока по умолчанию =0.
    • И если он указан, то делается попытка загрузки из файла с именем "mode_SYSNAME_ranges.dat" (да, пока халтурно -- формат указан прям там, а не рядом с прочими *_FMT).

    27.02.2012: продолжение:

    • Формат имени вынесен в CHL_RNGS_PATTERN_FMT.
    • Для сохранения стиля работы значение loaddefsettings теперь сохраняется в свежесделанном datainfo_t.is_protected. И при загрузке обычных режимов CDR_OPT_NOLOAD_RANGES указывается лишь при is_protected.

      В противном случае в не-loaddefsettings-программах диапазоны вообще никогда бы не могли быть считаны из файла.

      ...но в actionknobs лишь-значения грузятся ВСЕГДА -- в том смысл.

    Проверено (на widgettest) -- вроде пашет.

  • 09.04.2010: мысль в ту же сторону: а чё б не сменить понимаемую ChlRunApp()'ом парадигму запуска приложений с нынешней
    prog [DEFSERVER]
    на
    prog {ARGUMENTS}
    -- аналогично fastadc_common-программам, где среди ARGUMENTS могут быть и defserver, и имя файла для загрузки, и прочие команды.

    В таком случае можно было бы (через cx-starter-*.conf) указывать параметры/команды "при старте загрузи такой-то файл" и "при выходе сохрани екущий режим туда-то".

    09.04.2010: пара замечаний насчёт того, КАК надлежит сохранять режим:

    1. НЕЛЬЗЯ сохранять режим, если еще не подконнектились к серверу.
    2. Сохранение -- поскольку оно делается всегда в ОДИН И ТОТ ЖЕ файл, надо делать В ДВА ПРИЁМА: сначала писать в некий временный файл, а потом rename() его в нужный.

    И вообще -- а настолько ли правильно ВСЕГДА при выходе записывать текущие настройки? Вот взяли, поигрались, а вдруг испортили хорошие настройки -- и чё? Может, всё-таки записывать именно ПО КОМАНДЕ? Ну или, как минимум, при записи нового файла -- переименовывать старый в что-нибудь-такое, что будет доступно из окна загрузки режимов?

    26.05.2010: еще в продолжение: пусть в командрой строке среди ARGUMENTS можно будет указывать и "запиши в такой-то канал то-то" -- например, в формате ИМЯ_РУЧКИ=ЗНАЧЕНИЕ (где ИМЯ_РУЧКИ указывалось бы в формате для ChlSetChanVal()/CdrFindKnob()).

    Сие будет полезно для случаев небольших-стендиков, когда надо прописывать в железо некоторые "стабильные" параметры (например, в КШД485@ЛИУ -- рабочий и удержательный токи).

    06.08.2010: странно, что тогда (06.07.2010?) не записал, но -- "парадигма" на таковую от fastadc_common давно заменена. Естественно, с некоторыми отличиями. Детали:

    • Вместо прямого сравнения с "--help" etc. добавлен тип PK_SWITCH.
    • PK_PARAM заменён на PK_SETTING, с соответствующим изменением эвристики определения.
    • Соответственно, PK_BIGC сменено на PK_SERVER.
    • Собственно парсинг идет в 2 стадии:
      Стадия 0
      в конце неё выполняется "binding" -- уставляется сервер, physinfo, делается populate.
      Стадия 1
      тут уже реально исполняются "уставки" -- PK_FILE и PK_SETTING.

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

    В общем:

    1. конкретно идею "указывать, что надо автосохранять при выходе" делаем "withdrawn" (вместе с прочими "командами").
    2. Остальное же сделано, так что "done".

    03.02.2011@Снежинск-каземат-11: с тех пор старый вариант еще оставался в коде за-#if-0'енным. Выкинул.

    21.09.2011@Снежинск-каземат-11: за компанию с добавлением ключика "-readonly" также сделано, что при неизвестном ключе оно ругается и exit(2).

  • 06.09.2010: публика (Карнаев) просит мочь уметь замораживать график. Реально -- думаю, достаточно будет просто Freez'ить всё. Для чего надо иметь команду Freeze прямо в стандартном toolbar'е.

    Но оно нужно совсем не везде. Так что -- введём ключик freezable, который будет разрешать наличие этой кнопочки.

    06.09.2010: делаем.

    1. Для этого понадобился новый Xh_ACT_NOP, на который заменяется код у кнопки "Freeze" для её отключения.
    2. Собственно строчка в Chl_stdtoolbar.c добавлена.
    3. Также добавлено appopts_t.freezable и соответствующий ключик в text2appopts[].
    4. Ну и сделано выкашивание кнопки при !freezable.

    Работает, "done".

    25.12.2011: ага, и оно просто внаглую лезло в toolslist[], даже не глядя, есть ли он вообще.

    Исправлено, и заодно добавлено форсение opts.notoolbar=1 при toolslist==NULL.

    27.06.2012: тьфу ты, это втихушку повлияло на linipp, где давно имелась кнопка "freeze" -- она стала отключаться. Решил тривиально -- добавлением туда ключа freezable.

Chl_canvas/ELEM_CANVAS:
  • 23.07.2006: тэк-с, все больше и больше напрашивается тип элемента, который бы расставлял свое содержимое по указанным координатам -- чтобы можно было изображать в окошке словно "карту" установки, для большей визуальности.

    23.07.2006: соображения по теме:

    • Во-первых, тут уж 100%-понадобится наличие поля "layoutinfo"/"placement", в котором бы сия информация указывалась.
    • Во-вторых, надо мочь указывать не просто координаты, а АТТАЧМЕНТЫ -- чтоб можно было объекты приделывать друг к дружке (см. ниже).
    • Далее -- надо уж тогда реализовывать и декоративные "безвиджетные объекты", как в MEDM'е. А именно -- прямоугольник, эллипс, многоугольник, надпись. И чтоб они рисовались именно на XtWindow() самого элемента.
    • Но! Поскольку к декоративным объектам также должны быть доступны привязки, то -- как? Ответ, похоже -- виджеты-то создавать НАДО, но -- с InputOnly-окнами. И свои размеры прямоугольник и эллипс при рисовании должны вычитывать из виджета, а не просто помнить уставленное им.

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

    1. Аттачменты.
      • "Внизу" это должно реализовываться формой (XmForm).
      • Синтаксис можно взять примерно такой: SIDE=SPEC, где SIDE -- left, right, top, bottom, а SPEC может быть одним из:
        • NNN -- т.е., просто координатой. Например, left=45.
        • [NNN]@SIBLING -- аттачмент к другому виджету; NNN -- опциональный оффсет (может быть и отрицательным), SIBLING -- метка (label) "соседа". Например, left=5@t2.
        • ![NNN]@SIBLING -- то же самое, но аттачмент к противоположной стороне -- XmATTACH_OPPOSITE_WIDGET.
        • !NNN -- аналогично XmATTACH_OPPOSITE_FORM, хотя и фиг знает зачем.
        Таким образом, общий синтаксис -- [!][NNN][@SIBLING].

        Можно еще NNN дополнить опциональным суффиксом -- чтобы кроме пикселов мочь указывать в "условных" единицах, например, как в Форточках -- в четвертинках размера шрифта.

      • "Механизм" отдачи этой информации форме такой: вначале создаются ВСЕ knob'ы, а потом делается проход по ним с уставкой соответствующих аттачментов. Так мы решаем проблему forward-ссылок.
    2. InputOnly-окна: просто втихушку подменить Motif'у окно может оказаться не столь просто. В пределе, возможно, придется изготовить собственный виджет на основе XmPrimitive -- но это-то не проблема.
    3. КАК именно декоративные виджеты будут рисовать на окне своего parent'а? Ведь ExposeEvent-то будет приходить ЕМУ!!! Видимо, как-то им придется договариваться -- например, у Canvas'а заводить список-программу команд рисования.
    4. Параметры декоративных knob'ов надо будет унифицировать. Например, border=N, color=SPEC; причем цвет указывать либо "физический" (#rrggbb или red), либо -- предпочтительнее! -- логический (%1); логические цвета можно сделать настраивабельными.

    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 (он и хорошо заметен, и неярок), а для бордюра -- черный.
    • Декоративные виджеты работают в одном из двух режимов:
      1. Внутри ELEM_CANVAS'а -- тогда они используют XmInputOnly() и сами нифига не рисуют.
      2. Внутри другого элемента -- тогда они "имитируют" 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, да и точка с запятой также может получить где-нибудь аналогичный статус.

    • Внутренняя реализация определяется неким не самым очевидным набором соображений:
      1. Поскольку точная/актуальная позиция декорации на canvas'е становится известна только в момент рисования, то хранить абсолютные координаты точек -- нельзя. Надо уметь смещать всю фигуру на (x0,y0).
      2. (А еще возможна ситуация, когда рисуем не на canvas'е, а на DrawingArea самого виджета. Тогда ничего смещать вообще не надо.)
      3. Хочется мочь указывать не только абсолютные координаты точек, но и относительные.
      4. X11 поддерживает режим "относительных координат" в операциях XDrawLines() и XFillPolygon() -- режим CoordModePrevious.
      5. В X11 нет операции "нарисовать замкнутый многоугольник", в отличие от "закрасить замкнутый многоугольник" -- XFillPolygon(). Зато в качестве такой операции работает XDrawLines(), если последняя точка совпадает с первой -- в man-странице битым текстом сказано, что это специально поддерживается, и даже ни один пиксел не будет отрисован дважды.

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

      1. Храним именно относительные координаты (первая точка, естественно, абсолютная). Используется именно CoordModePrevious.

        И при отрисовке прямо в массиве к координатам первой точки прибавляем (x0,y0), так что вся фигура смещается. После отрисовки -- вычитаем обратно.

      2. (Локальное рисование -- частный случай, проблем не создающий.)
      3. Относительные координаты поддерживаются автоматически -- они и оказываются основными.
      4. НЕ относительные координаты приводятся к относительным -- парсер постоянно помнит абсолютные координаты предыдущей точки.
      5. Хранится не 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" можно считать реализованным. Из незавершенностей/несовершенств:

    1. Отсутствие поддержки логических цветов (%ccc).
    2. Неучет толщин линий в размерах декораций.
    3. Возможно, понадобится поддержка как обычных, так и закрашенных арок (ну тут-то проблема исключительно в понимании их геометрии).
    4. Возможно -- "динамические" декорации, могущие как-то менять свой вид (цвет?) в зависимости от значения канала/формулы.

    В общем -- "done".

  • 27.07.2006: пришло понимание, как можно будет реализовать "динамические" декорации:

    Когда декорация принимает решение "сменить свой вид", то она уставляет некий флажок в privrec'е. Метод же NewData() canvas'а проходится по всем своим knob-декорациям, и проверяет этот флажок. Как только натыкается на уставленный -- для всех декорации начиная с этого вызывает перерисовку (тем самым, если кто-то накладывается сверху на изменившуюся декорацию, то он также будет перерисован); флаги же при проходе сбрасываются. Здесь используется тот факт, что NewData() вызывается ПОСЛЕ обсчета knob'ов.

  • 01.08.2006: "магниты" и прочие элементы частенько стоят не ровно, а "под углом" -- например, в кольце. И вот вопрос -- а как бы этак такое поддерживать, а?

    Во-первых, вопрос указывания таких вещей: кроме ширины и высоты писать также угол поворота?

    Во-вторых, собственно "менеджмент" -- отрисовка, реакция на события, и проч. Как бы это сделать, обойдясь БЕЗ шейпованных окон, а? Разве что работать именно совсем "врукопашную", как бы на поверхности canvas'а...

  • 14.07.2007: думал о том, как бы этак получше сделать программу управления поворотным магнитом. Соль там в том, что лучше всего было б отображать прямо графически положение этой хреновинки. То есть -- через canvas. Но -- пока никак...

    14.07.2007: в результате имеем такие соображения:

    • У декораций надо б завести "динамическость" -- т.е., возможности:

      1. Менять свою видимость в зависимости от канала/формулы. (В случае с turnmag'ом завели бы 4 хреновинки на одном месте, каждая из которых бы показывалась только когда в концевиках соответствующий ей код.)

      - и/или -

      2. Менять внешний вид в зависимости от канала/формулы -- "анимация". (В случае с turnmag'ом завели бы 4 кадра, по количеству вариаций из 2 бит.)

    • По-хорошему бы и вовсе завести тип декорации "drawing area", чтоб можно было рисовать более хитрые вещи в рамках одного NOOP'а. Естественно, для рисования нужен некий "язык" -- а реально чтоб указывалась (в widdepinfo, где ж еще) просто последовательность команд типа "линия", "прямоугольник", etc.
    • А еще -- ради всяких GIS'ов и подобного -- иметь такие декорации масштабируемыми: чтобы реальные координаты для операций отрисовки брались пропорционально текущему размеру.
  • 10.12.2010@Снежинск-каземат-11: исправил сходу -- FindChild() не проверял ident!=NULL и SIGSEGV'ился -- что за позор!
Chl_multicol:
  • 01.08.2006: пришло время унести поддержку ELEM_SINGLECOL/ELEM_MULTICOL в отдельный модуль, получивший название Chl_multicol.c.

    01.08.2006: сделано.

  • 08.02.2007: раз уж в vacclient'е метки столбцов гистограммы в смысле мыши приравнялись к самим столбцам -- то хочется, чтобы и в обычных программах с сеткой щелчок на метке (строки) перед каналом также вызывал окна ChanProps/BigVal.

    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: некоторое время назад наблюлась странность: при указании ключика "fold_h" и потом некоей ошибки в строке options -- почему-то указание fold_h оставалось в силе, хотя вроде бы должно было сброситься в default=fold_v.

    12.02.2007: оказалось все просто -- при ошибке делается bzero() того, куда парсятся опции, а fold_h -- это как раз 0. Так что для решения проблемы пришлось после bzero() поставить и opts.fold_v=1.

    Ошибка при дизайне этих опций была в том, что значение опции по умолчанию НЕ является нулем. Так что, на будущее: надо ВСЕГДА подбирать значения опций так, чтобы все умолчания имели нулевые значения.

    04.04.2009: да, конкретно с fold_v ляп исправлен -- теперь у нас поле fold_h, по умолчанию равное 0.

  • 16.02.2007: есть ведь такая опасность -- если многоколоночному элементу указывается count<ncols, то при изготовлении заголовков колонок он копытнется -- когда попробует доступиться к sk=e->content+x при x>=count...

    16.02.2007: а вот и нет -- в этом новом алгоритме расстановки (с эмуляцией SINGLECOL'а MULTICOL'ом, и уж тем более в варианте с n-этажностью) метки колонок создаются в основном цикле расстановки ручек, а там есть условие y*ncols+x < e->count, так что до x>=count дело никогда не дойдет. Посему -- "withdrawn".

  • 26.02.2009: понадобилась возможность менять расстояние между элементами сетки (grid spacing) -- для программки liucc.

    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 & Co." (связано с началом использования PSP_T_INCLUDE).

    01.02.2011@Снежинск-каземат-11:

    • Чтоб можно было парсить опции В ДРУГОМ МЕСТЕ (как это нужно для SUBWIN'а) -- перетаскиваем всё, кроме парсинга, в 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).
Chl_leds_knob:
  • 31.08.2006: давно назрело создание специального типа knob'а в Chl -- "leds". Чтоб его можно было вставлять в notoolbar-группировки.

    31.08.2006: ввел LOGD_SRV_LEDS=4000.

    И саму хреновинку сделал -- в файле Chl_leds_knob.c.

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

    (Да-а-а, а если б ее еще можно было подселять прямо в заголовок элемента -- совсем бы было роскошно...)

    Засим -- "done".

    27.09.2006: ай, маладца-а-а!!! А keepalive-таймаут-то не работал! Так что при скопычивании сервера индикатор продолжал обманчиво показывать зеленый!

    Оказалось все просто: ему передавалась ki вместо ожидаемого rec, так что и sid был несколько миллионов, и count тоже. Вот фигня и происходила (и почему оно не segfault'илось? Загадка...).

Chl_knobprops:
  • 09.11.2006: эти "клятые юзеры" (в лице Мерихлюндина) завозмущались, что при появленном окне "bigval" становится невозможно работать с самой программой.

    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: а вообще-то уже можно переходить на "множественное" отображение bigval'ов, что было предусмотрено еще года два назад (через grid).

    09.11.2006: мыслишки в сторону:

    • Разные Малютины, конечно, поинтересовались/захотели -- большое значение для каждого канала будет показываться в своем отдельном окне, да?
    • С одной-то стороны это, конечно, глупость: мало того, что несчастные юзеры просто запутаются в этой куче окон, так еще и реализовывать сие будет понапряжнее -- это придется каждому knob'у сопоставлять по окошку (ну, типа, заводить лишний указатель в knobinfo_t), да еще и вводить некий дополнительный метод в knobs_emethods_t -- "обновить bigval", который надо будет вызывать из CdrProcessKnobs().
    • С другой же стороны, на будущую в CXv4 архитектуру "обновления данных по callback'ам" это легло бы великолепно -- там как раз наоборот придется немного поприседать, чтобы сделать отображение всей шоблы вместе.

    Впрочем, с точки зрения эргономики будет именно держать все bigval'ы в одном окне. На этом и порешим.

    06.02.2007: сделано, в одном окне. Кнопки [^], [v], [-] позволяют передвигать строчки вверх/вниз и удалять их.

    Теперь надо проверять на юзерах.

    Кстати, в старой версии был memory leak в Chl_E_ShowBigValWindow() -- не делалось XmStringFree().

    09.03.2007: уже больше месяца работает, вполне успешно, так что -- "done".

  • ??.11.2006: кстати, разным Пироговым значение нужно не слишком-то крупным, но просто чтобы выделялось. Им и шрифта 24 бы хватило, а не 96. Как бы это должно выглядеть для юзера -- чтобы при нажатии Ctrl+Shift+Btn3 значение бы отображалось "крупным", а не "гигантским" шрифтом.

    Единственная проблема: хоть ловить-то это в Knobs_internals.c::Mouse3Handler() несложно, но метода "knobs_emethods_t.ShowMedVal()" у нас нету -- увы. (Как и параметра "степень увеличения" у .ShowBigVal().)

  • 13.03.2007: перевел модуль на dlgrec_t *rec/ThisDialog(), XhCreateStdDlg/no-rec->shell.
  • 24.08.2009: поддостала модальность окна "Knob properties" -- хочется, чтобы оно не мешало работать, как в CXv4.

    24.08.2009: да, сделал. Не стал, как хотел сначала, выпендриваться и внедрять "правильную" архитектуру от новой версии, а просто занулил флаг XhStdDlgFModal у основного диалога (у range и yesno -- пока нет) и проверку XtIsManaged(). На вид -- всё окей.

  • 22.12.2010@Снежинск-каземат-11: захотелось видеть в окошке кроме ident самой ручки также и "полный путь" к ней (понадобилось для liucc/globact -- чтоб быстро узнавать полные имена ручек, в которые должны писаться числа).

    22.12.2010@Снежинск-каземат-11: Сделано -- в самый верх таблицы был добавлен pth_dpy.

    Фокус в том, как для него добывается значение.

    • С одной стороны -- поскольку писать надо "от корня", то вроде надлежит городить рекуррентную функцию, прущую вверх до корня, и в конце (прямо перед возвратом, т.е. -- как бы "на обратном ходу" дописывающую к концу строки очередной компонент.
    • С другой же стороны, действие функции одноразово, сделал-и-забыл, никаких ветвлений, так что по логике задачи -- должно бы делаться итеративно, а не рекурсивно.
    • Но направление строки одно (от корня к ручке), а движения по дереву -- другое (от ручки к корню).
    • Решение -- тривиально: мы приписываем компоненты не к началу строки, а к концу. При этом направление записи совпадает с направлением движения, и обходимся сравнительно простым циклом.
    • Единственная алхимия -- как именно там устроено само дописывание, в т.ч. проверка, чтоб оно не вылезло за буфер ограниченного размера. Но это всё несложная рутина.
  • 02.03.2011: надо бы сделать, чтобы ширина полей ввода диапазонов была бы ШИРЕ, чем просто GetTextColumns()+1. Чтоб юзеры имели право на опечатку -- а то сегодня умудрились в поле "%5.0e" (вакуум) ввести что-то типа 1e-044, которое туда почему-то не влезло, и отображалось как "1e-04".

    02.03.2011: угу, добавлено +2 колонки.

  • 29.05.2013: по жалобе Феди поле "Name" переведено с XmLabel на XmText. Смысл -- чтоб использовался моноширинный шрифт, дабы отличались заглавная "I" и маленькая "l" (в LucidaSans они очень похожи -- вертикальные полоски, а в LucidaTypewriter отличаются засечками).

    29.05.2013: имя поля при этом сменено с nam_lbl на nam_dpy.

  • 27.06.2014: переделываем архитектуру (и идеологию!) в отношении диапазонов.

    Если раньше они отображались в окне, а менялись в отдельном под-окне, да еще с дополнительным "Are you sure?", что безмерно раздражало, то теперь прямо в самом окне отображаются текстовыми полями, прямо в которых и менябельны.

    27.06.2014:

    • Префикс функций -- RangeDpy.
    • Нужные им поля содержатся в поле-структуре kprops_range_t kpropsdlg_t.z.

      Плюс дополнительно поле is_closing.

    • Всё это огорожено #if'ами RANGES_INLINE -- при окончательном переходе старое выкинуть будет просто.
    • О механизме отображения:
      • Реализована хитрая архитектура, когда диапазон может быть отключен -- не отображаться и не вводиться.
      • Сделано это путём предварительного создания окон-"закрывателей": при отключении диапазона не только поля делаются sensitive=False, но еще и XtManageChild() закрыватели, а они созданы раньше (т.е., видны "выше") и приаттачены по размерам полей ввода.
      • Но в v2 оно не используется, а понадобится только в v4.

        12.11.2015: а тут некая проблема: при /fixedranges (DATAKNOB_B_RANGES_FXD) диапазоны не показываются вовсе, что неприятно.

    • О механизме работы:
      • На поля навешивается XmNmodifyVerifyCallback, делающий rec.chg.ranges[which]=1.
      • И потом, в PropsOkCB(), если chg стоит, то делается ExtractDoubleValue() с проверкой, и если бо-бо, то окно остаётся на экране.
      • Бонус ExtractDoubleValue() -- можно использовать свежевведённую арифметику.
      • Для лучшего предохранения от ошибок навешивается также XmNlosingFocusCallback, делающий холостой вызов ExtractDoubleValue() -- чисто для бибиканья, но ничего не запрещающий.
      • Чтоб не бибикало по Esc, введено is_closing -- оно выставляется перед прятаньем окна.
      • Единственное, что пока НЕ сделано -- использование hilite. Потому, что его версия на OverrideShell еще не допилена.

        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-стадийное считывание; проблема ушла.

Chl_histplot:
  • 09.03.2007: модуль для отображения истории каналов на графиках-самописцах -- приступаем к изготовлению по проекту от 18-09-2006.

    12.03.2007: (сделано за прошедшие несколько дней, но записываем только сейчас) модуль называем так же, как в свое время в istc/vibro, поскольку тот был его предком.

    Summary:

    • К knobs_emethods_t добавлен метод ToHistPlot().
    • Он вызывается из Knobs_internals.c по нажатию Shift+Btn3 и Shift+ПРОБЕЛ.
    • Упоминание об этом вставлено в Chl_help.c.
    • В Chl.h добавлено объявление "стандартного" реализатора Chl_E_ToHistPlot() (чей код живет в Chl_histplot.c), и ссылка на него вставлена во все таблицы elem-plugin'ов.
    • Собственно реализация была несложной и в основном свелась к использованию имеющихся наработок:
      • Менеджмент таблицы каналов был взят из bigvals -- просто тупо копировались огромные куски кода.
      • Оформление графиков -- из ndbp.
      • А собственно отрисовка, с выездом справа -- из istc'шного Chl_histplot.

    "Недостаток" по сравнению с istc'шным вариантом -- график ОДИН, а не множественные. Но:

    1. Области применимости у этих фич все же несколько разные.
    2. Возможно, ТОТ вариант реализовабелен при помощи element-plugin'а -- который бы вел графики всех своих вложенных knob'ов.

    И еще: я-то рисую "нахаляву", XDrawPoint+XDrawLine*(n-1), а по-хорошему надо бы набивать массив из XPoint'ов и отдавать его ОДНОМУ XDrawLines() -- это должно сильно экономить (пакетирование плюс меньший объем пересылки серверу). Гусев так и делает. Но: у меня и так все отлично работает, и мерцания почти не видно! И даже resize отрабатывается НЕСРАВНЕННО быстрее гусиного (или дело в том, что у него pixmap?). Так что -- пока в XDrawLines() глубокого смысла нету.

    РЕАЛЬНО же существующая проблема -- что довольно хренова компоновка окна: там XmMessageBox, с кнопкой [Ok] снизу, в который вставлена XmForm. Поэтому --

    1. Очень нерационально расходуется место -- а надо бы делать ВСЕ окно одной светлой формой.
    2. Очень погано ресайзится, из-за неудачной модели аттачментов.

    Так что -- надо радикально переделывать компоновку.

    29.03.2007: поскольку уже не первую неделю используется, и приемлемо работает, то -- "done", а остальные улучшения отдельным разделом.

  • 13.03.2007: перевел модуль на dlgrec_t *rec/ThisDialog().
  • 29.03.2007: общее правило: если по удалению последней строки окно автоматом закрывается, то надо ВНАЧАЛЕ сделать unmanage окну, а уж потом -- строке. Так исключается нежеланный эффект "съеживания" окна перед исчезновением. Собственно -- сделал :-).
  • 29.03.2007: дальнейшие улучшения в компоновку.

    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".

  • 07.05.2007: надо-таки переходить на XDrawLines(), это должно СУЩЕСТВЕННО снизить нагрузку на сервер.

    07.05.2007: поскольку в принципе количество точек может быть довольно большим, а как-то к чему-то (DEF_RINGSIZE тут, и MAX_PIX_W в adc200) привязываться не хочется, то простейший общий алгоритм таков:

    • Имеем в функции отрисовки массив (массивы) фиксированного размера, например, в 1000 XPoint'ов; плюс -- переменную, сколько точек массива использовано.
    • В цикле по x, когда надо "нарисовать" очередную точку, смотрим -- если использован весь массив, то "выталкиваем" его -- вызываем отрисовку, и сбрасываем количество использованных в 1, оставляя в качестве этой единственной точки последнюю.
    • И -- просто добавляем координаты точки к массиву.
    • По окончанию цикла проверяем, не ноль ли использованность массива, и если не ноль (так всегда и будет), то вызываем отрисовку.
    • Отрисовка же -- при numpoints==1 просто сделает XDrawPoint, а иначе -- XDrawLine. Это вообще может быть некая универсальная функция, годная для всех мест, работающих с графиками.

    10.05.2007: да, сделано. Функция "выталкивания" именуется FlushXLines(). Хотя, насколько именно такой вариант меньше грузит процессор -- ХЗ, ибо linvac, к примеру, в top'е выглядит одинаково, что старый вариант, что новый.

    На будущее, кстати, для уменьшения возможности мерцания: можно было бы стирание старого и отрисовку сетки также засунуть в отдельную функцию, которую вызывать перед САМЫМ первым FlushXLines() -- это потенциально уменьшло бы интервал между стиранием и отрисовкой. Хотя, скорее всего -- Xlib'овская работа с буфером, скорее всего, и так достаточно умна.

    Итого -- "done".

    09.03.2010: во всех экземплярах FlushXLines() имелся дурацкий ляп: ставилось *npts_p=0 вместо надлежащего =1, и в результате на границе -- где выталкивался буфер -- образовывался разрыв, т.к. следующий сегмент НЕ соединялся с выталкиваемым. Не замечал же я этого в основном потому, что редко когда графики бывали шире 1000 пикселов (размер буфера).

    Исправил.

  • 01.09.2007: давно пора было дать юзеру возможность менять параметр frqdiv.

    01.09.2007: да, сделал, полный комплект: и поле "Hist. frqdiv." в окошке "Knob Properties", и ввел knobinfo_t.histring_frqdivchg, отражающий модифицированность -- и работающий ровно так же, как и флаги измененности для диапазонов, и поддержку в режимах -- сохранение/считывание frqdiv'а в Cdr.

    30.08.2008: кстати, в недавно проведенных измерениях какого-то бочонка Корепановым и Баком -- программа korepanov8hrs -- эта frqdiv показала себе очень полезной: у них родная частота сервера была 1Гц, и с ней же надо было складывать лог в файл, а вот на экранном графике хотелось видеть побольше -- например, час, так что значение 4 было в самый раз.

    Хреново только, что нельзя указывать это значение в самом описании группировки. Ну да, понятно, что дело в нежелании в очередной раз менять формат _db.so-файлов, и что в CXv4 оно будет, но все равно -- жаль!

  • 25.01.2008: сходу -- сделал возможность делать линии "толстыми" (line_width=2). Сразу делается 2 GC, и то, какой из них использовать, выбирает новый чекбокс "Wide", расположенный над кнопкой "Close".

    19.04.2012: по просьбам трудящегося Еманофеди добавил возможность вместо непрерывной линии отрисовывать точками.

    • Чекбокс был сменен на селектор "---"/"==="/". . .".
    • Заменено histplotdlg_t.wide на histplotdlg_t.mode.
    • Введена функция FlushXPoints(), плюс тип do_flush_t.
    • К сожалению, рисование точек игнорирует GC-параметр "width", так что толстые точки столь дешево не сделать. А жаль -- можно б было ввести еще вариант селектора "***".

    03.05.2012: естественно, такие тощенькие точки его не устроили.

    Сделан всё-таки 4-й вариант "***". Реализован отдельной функцией FlushXBlots() (blot -- клякса), все точки рисуются поштучно, в цикле, в виде "ромбика". Делается это через XDrawLines(), 5-точечным массивом, с CoordModePrevious (так мы только 0-й точке присваиваем координаты очередной точки в цикле, а остальные 4 прописываются заранее -- {(+1,+0), (-1,+1), (-1,-1), (+1,-1)}).

  • 07.02.2008: Старостенко, порадовавшись возможности уставлять frqdiv, выдал еще пожелание: хочется, чтобы сохранялось и рисовалось не просто довольно случайное измерение, попавшее на момент сохранения, а ("выбирается селектором"): 1) минимум; 2) максимум; 3) среднее.
  • 02.04.2008: Малютин возжаждал иметь не ОДНО окошко с графиками, а НЕСКОЛЬКО. Резон -- хочется иметь сразу более 5 каналов, а раз на один график столько не влезет -- пусть будет несколько графиков/окон. Причем пока что задача такая возникла в программе linbpm -- там много каналов, которые хочется видеть ОДНОВРЕМЕННО.

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

    А потом договорились до другого варианта: не делать НЕСКОЛЬКО окон, а разбить ОДНО окно по вертикали на несколько зон -- при помощи XmPanedWindow.

    "Типа проект":

    • по умолчанию создается ОДНА панель графиков;
    • но на ней делается кнопочка [+], создающая еще панель.
    • Плюс, на каждой панели имеется по одной радио-кнопочке, выбирающей эту панель в качестве "получательницы" нажатий Shift+Btn3.

    Потенциальные вопросы:

    1. Наверняка будут отдельные заскоки с менеджментом касательно ресайзинга -- по опыту возни с формой и друг-поверх-дружечными DrawingArea, а уж тут, с добавлением XmPanedWindow, еще что-нибудь вылезет. А тут -- еще новый менеджер со своими sash'ами добавит fun'а.
    2. Пользовательский интерфейс -- точнее, подход -- придется изменить: сейчас окно появляется только при добавлении каналов, и автоматом исчезает по удалению последнего. А сейчас -- во-первых, будет возможность иметь пустые панели (без каналов), во-вторых -- надо б заиметь какой-то способ появить окно и БЕЗ добавления канала.
    3. Отдельная задача -- как осуществлять менеджмент присутствующих на графиках каналов? Сейчас-то просто массив, занятый от 0 до 5 каналами, а тут -- какая-то разреженная хрень?

      Нет, видимо, не "разреженная хрень", а поступать так же, как и раньше -- просто вместо одного массива "до 5 каналов" иметь N массивов "до 5 каналов" каждый (например, N=4), из которых "включено в отображение" может быть от 1 до N. Соответственно, с самими этими массивами обращаться так, как сейчас обращаемся с каналами внутри панели -- так же сдвигать в памяти при удалении "коллеги", и т.д. и т.п.

    Вот только времени на все это сейчас нету :-)

  • 02.07.2009: а ведь бывают параметры, которые по смыслу не линейные, а ЛОГАРИФМИЧЕСКИЕ (в основном -- вакуум), и их хотелось бы видеть на графике именно в логарифмической шкале...

    02.07.2009: пара замечаний:

    1. "Логарифмичность" должна указываться именно per-channel.
    2. И вопрос -- откуда брать пределы для отображения? Видимо -- высчитывать точно так же логарифмически, как и значение, из значений RANGE_DISP.
    3. Теоретически есть и паллиативное решение: прямо на экране вместо ЗНАЧЕНИЯ вакуума отображать его логарифм.

    Вопрос -- КАК указывать логарифмичность канала? Пара идей, обе кривоватые, хотя и рабочие:

    1. В CXv4 можно было бы сделать флажок DATAKNOB_B_LOGARITHMIC, который уставлять из .subsys-описания ключиком "/log".
    2. А можно смотреть на dpyfmt -- если он e, то считать канал логарифмическим.

    20.04.2010: идея: а чё б для логарифмического отображения каналов (конкретно этот вакуум на сварке) просто САМО ЗНАЧЕНИЕ не сделать логарифмом (перевести канал на LOGK_CALCED и делать CMD_LOG10 (которую ввести))?

    Вот только неясно -- а насколько адекватно это будет? Ведь нынче вакуум отображается как 1E-NN (аналог 1*10-NN), а с логарифмом получится просто -NN.M.

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

    02.03.2011: да, НАДО внедрять логарифмичность в histplot'е, а то на сварке с вакуумом уже совсем задолбало (еще Аркадий Спесивцев добавил "фана" -- хочет чё-то видеть, а что -- не понимает).

    План таков:

    • Логарифмический режим использовать при формате 'e'.
    • Способ вычисления -- в качестве min_disp/max_disp использовать не сами min/max, а их логарифмы.
    • Соответственно, и от значения перед RESCALE_VALUE() также брать логарифм.
    • Воизбежание EDOM/ERANGE проверять перед циклом рисования, что обе границы диапазона >0, иначе -- рисовать не-логарифмически.
    • Сами значения перед ологарифмиванием также проверять, и при <=0 уставлять их в min_disp.
    • Естественно, какая-то аналогичная алхимия и в DrawAxis().

    04.03.2011: сделано, по вышеприведённому проекту. Тонкости:

    • "Просто" использовать log() нельзя, поскольку при значении, стремящемся к 0, логарифм стремится к бесконечности. Потому введено ограничение -- 1e-100.
      1. При любом из пределов <1e-100 функция ShouldUseLog() вернёт "нет", так что график будет рисоваться линейно.
      2. И все числа <1e-100 считаются за 1e-100.

      Вообще, возможно, это даже слишком сильное ограничение -- поскольку 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", но линейно...

  • 08.07.2009: Купер и Юра Семенов на сварке высказали пожелание: надо мочь посмотреть график как "детально" -- за последние N секунд, так и "за последние M часов".

    Если б у нас была полноценная навороченная программа типа striptool'а -- она бы это всё умела сразу. Но это не так, и ваять прямо сейчас такие навороты -- неохота. Так что надо делать простым способом...

    08.07.2009: да, можно будет сделать так:

    1. Графики запоминать за бОльшее время -- например, за 8ч=8*3600с=28800с.
    2. Иметь чекбоксик (над Wide), переключающий масштаб -- чтобы каждый пиксел брался не из одной ячейки, а из каждой N-й, где N -- например, 60.

    08.12.2009: да, Семенов и Co. по-прежнему хотят. Причём -- чтоб можно было смотреть, например, за последние M часов, и при надобности -- чтоб можно было смотреть нужное место детально. И еще: им частоты 1Гц мало, надо б около 10Гц.

    05.03.2010: другой вариант этой потребности, от Логачева: вот сейчас в weldcc показывается за 12 последних минут (700 пикселов); а чтоб можно было включать также и за последний 1 час и 3 часа. А более мелкий масштаб -- вроде не нужен.

    Ну что -- можно сделать селектор (рядом с кнопкой [Wide]), который бы выбирал масштаб. Несколько тонкостей:

    • Надо будет и подписи к горизонтальной оси нужным образом масштабировать.
    • Размер буфера для записи должен будет быть увеличен (до 86400?).
    • Главное -- а КАК рисовать? Просто отрисовывать все точки, пусть даже многие в один X? Или находить минимум/максимум/среднюю?

    07.03.2010: да, решение вообще-то тривиально:

    • Отрисовывать ВСЕ точки.
    • Иметь селектор "масштаб": 1:1, 1:10, 1:60, ...
    • При выборе масштаба записывать его в некую x_scale, а при отрисовке вместо "-x" использовать "-x/x_scale".
    • И можно попробовать навести оптимизацию: отрисовывать не каждый цикл, а лишь каждый x_scale'тый. Тут, конечно, есть свои приколы -- фактически-то точки едут (перетекают между соседними x'ами), но посмотрим.

    08.03.2010: и еще:

    • А можно ль учесть и размер цикла сервера? (например, брать от [0]-го ki, и делать *1000/cycle_size?
    • А frqdiv? Домножать x точки на него? И - если будет масштаб, то не выкинуть ли frqdiv вовсе?

    09.03.2010: приступаем к реализации.

    1. Для начала, с нынешней моделью отрисовки -- СЛЕВА НАПРАВО, x=grf_w-count+t -- никакое "-x/x_scale" не прокатит: поскольку деление (count+t) при (t>=count-x_scale) даст 0, и станет x=grf_w, что неприемлемо.

      Так что надо переходить на отрисовку справа налево -- при этом арифметика становится корректной.

      Перешел -- проверил, вроде работает.

      (А та отрисовка слева-направо -- шла еще со старого istc'шного xmclients/Chl_histplot.c.)

    2. Подписи к осям теперь пишутся не просто секундами, а, в зависимости от точной величины, в форматах S, M:SS, H:MM:SS (этот сильно длинен...).
    3. Сменил DEF_RINGSIZE с 1000 на 86400.
    4. Добавлен селектор -- 1:1, 1:10, 1:60 (ставится над "Wide"). Сей набор масштабов очень удачен -- т.к., между подписями по 60 единиц, то, например, точка 240 имеет, в зависимости от масштаба, подписи -4:00, -40:00, 4:00:00.

      Селектор управляет свежедобавленным полем histplotdlg_t.x_scale, суя туда 1, 10, 60.

      P.S. Селектор сделан simple-knob'ом!

    5. Ну и -- собственно, в вычисление x-координаты точки добавлено /rec->x_scale, а в выводе подписей к осям -- *rec->x_scale.
    6. Плюс -- собственно граница цикла -- count -- была grf_w, а теперь -- grf_w*rec->x_scale (это вначале забыл, и оно всегда рисовало слишком мало точек).

    Глядя на нынешнюю таблицу масштабов, лезет мысль: а может, секунды вообще не печатать?

    Но тут же встречная мысль: а ведь при base_cycle_size<1000000 оно вполне может давать и не-нулевое поле секунд...

    P.S. Проверил по рекомендациям гуру -- разделитель минут и секунд ОБЯЗАТЕЛЬНО должен быть двоеточием, его НЕЛЬЗЯ делать точкой...

    P.P.S. Но мерцает оно при нескольких десятках тысяч точек просто страшенно. Увы, тут уж ничего не сделаешь...

    11.03.2010: заканчиваем:

    1. Учёт размера цикла сервера сделан -- только наоборот: ПОДПИСИ к осям пишутся с учётом масштаба, т.е., при вычислении секунд для подписи делается cyclesize/1000000. А cyclesize берётся не от [0]-го ki, а от mainsid'а.
    2. И frqdiv также учтён -- действительно на него просто домножается x точки.

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

    Короче -- добито.

    08.02.2011@Снежинск-каземат-11: имелся ляпсус: размер-цикла-сервера оно узнавало далеко не сразу, а попозже -- уже ПОСЛЕ первой отрисовки осей. И в результате потом, по Expose, оно перерисовывало НОВЫЕ временнЫе подписи прямо поверх старых.

    От перерисовки-другого-поверх избавился просто -- завёл дополнительно histplotdlg_t.prev_cyclesize, куда сохраняю последний-известный cyclesize, и при несовпадении текущего с предыдущим форсим do_clear=1.

    Но осталась проблема: оно ничего этого не сделает вплоть до первого DrawAxis() после получения первых данных, которое, увы, может вызваться очень нескоро. Это следствие нашей криво-хачной модели хранения и отрисовки истории...

    23.03.2011@Снежинск-каземат-11: юзеры (в первую очередь на сварке и вакууме) давно хотят иметь возможность прокручивать график далеко назад. Это можно сделать -- потребны 2 вещи:

    1. Собственно скроллбар и связанное с ним:
      1. Создание/аттачмент.
      2. Менеджмент -- с учётом текущих:
        1. максимума запомненных измерений;
        2. ширины графика;
        3. масштаба (x_mul/x_div).
      3. Изменение в отображении -- рисовать не с "текущей" точки, а с horz_offset скорректированного на x_mul/x_div.
    2. Возможность указывать желаемый histring_size для каждой программы отдельно (в идеале бы для каждой ручки, но это уж никак).

    Начинаем с простого -- со 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.

    Сейчас сделано:

    • Создание horzbar'а, правильный его аттачмент и внесены изменения в аттачмент graph и axis.
    • Сделаны (копированием с минимальной модификацией) 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'а радикальные изменения внесены в 1) рисование графика, 2) вертикальных линий сетки, 3) тиков+подписей на горизонтальных осях.
    Вообще, конечно, выглядит оно всё преотвратнейше:
    • где счётчики цикла идут в реальных "координатах" (т.е., в отсчётах в буфере), а где в экранных (со сдвигом на horz_offset);
    • где всё считается в отсчётах (t), а где в экранных пикселах (т.е., уже в масштабированных координатах);
    • где расчёты и сравнение делаются в "прямых" координатах (в t -- т.е., как бы по времени начиная с начала буфера), а где -- как бы в экранных (т.е., уже отзеркалено).

    А ведь давным-давно, еще при написании какого-то графического редактора (пиктограмм?) на БК-0010.01 я сделал вывод -- надо стараться ВСЕГДА работать в реальных координатах, пересчитывая их в экранные по мере надобности.

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

    Короче -- надо, понаступав тут на грабли, разработать внятную модель, годную как для прямого (осциллограммы), так и для обратного (самописцы) рисования, и реализовывать её в Xh_plot'е. Главное -- модель должна быть ОБЯЗАТЕЛЬНО ПРОДУМАНА ЗАРАНЕЕ.

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

    14.07.2011: P.S. а вот «в liu/{plot,y}/ горизонтальное масштабирование сделать аналогично» не получится. Этот цирк с конями надо переделывать радикально -- просто переписывать с нуля, делая при этом сразу 3 запланированных вещи:

    1. Скроллинг и масштабирование -- в ЧЕЛОВЕЧЕСКОМ варианте.
    2. Обновление не по приходу данных от сервера, а с некоей фиксированной периодичностью.
    3. Множественные панели histplot'а.

    Но этому будет посвящен уже другой раздел, а тут -- навеки "done".

  • 10.03.2010: кстати, графики ведь могут налазить друг на дружку и мешать. Не ввести ли чекбоксики перед каждой меткой канала, которыми можно было бы выключать отображение -- аналогично тому, как в nadc*?

    10.03.2010: делаем:

    • Собственно чекбоксик -- просто сменен тип меток с XmLabel на XmToggleButton (с XmNselectColor=XmNforeground).
    • Введен массив histplotdlg_t.show[].
    • К RenewPlotRow() добавлен еще параметр showness, который и уставляет show[] плюс состояние чекбокса.
    • При отключенном чекбоксе не рисуется ни график, ни его подписи к вертикальным осям.

    Засим -- "done".

    01.08.2011: была мелкая дурь -- для каналов с пустыми метками чекбоксики становились микроскопическими. Потому, что оно ставило метку "", а она имеет нулевой размер.

    Пофиксено -- теперь ставится метка из пробела.

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

    Ну что -- видать, нужно изготовить пиктограммку "маленькая дискетка" и уметь прилеплять кнопульку с нею слева от нижнего control'а ([Close] или Wide).

    02.08.2010: угу, и Довженко тоже хочет, для своих стендово-отладочных программ.

    Интерфейс-то для этого сделать можно -- аналогично обычному сохранению; хоть муторновато (команды мочь ловить -- или, правильно перенаправлять histplot'у; а их еще и НЕСКОЛЬКО может быть!).

    А вопрос еще в другом -- КАК сохранять в файл данные, у которых разная длина колонок? Или в те колонки, которых на каком-то времени еще нету, писать NaN?

    30.09.2010: да, и Ращенко на стенде тоже это надо -- чтоб экспортировать ИМЕЮЩИЕСЯ графики для анализа в Excel'е.

    07.10.2010: за позавчера и сегодня сделал. Highlights:

    • Кнопка для упрощения сделана не маленькой дискеткой, а просто надписью [Save]. Она помещается в самом низу -- ПОД "Wide". 26.02.2013: а теперь и дискетка сделана.
    • Поскольку формат выходного файла должен быть легко читабелен всякими Excel'ами с Origin'ами, то никакого комментария там нету, так что (раз не нужно окна SaveDialog) не требуется и никакого шаманства с командами -- прямо по нажатию кнопки сразу и пишется файл.
    • А вот имя файла подбирается при помощи XhFindFilename(), по шаблону CHL_PLOT_PATTERN_FMT -- "plot_%s_*.dat".
    • Сам формат сделан точно таким же, как у log-файлов:
      1. Первая строчка -- заголовки, начинающиеся с '#' (БЕЗ пробела перед "Time(s-01.01.1970)".
      2. В этой строчке после времён пишутся ident'ы задействованных каналов, с единицами (units) в скобках, если единицы определены.
      3. Времена --
        1. Time(s-01.01.1970)
        2. YYYY-MM-DD-HH:MM:SS.mss
        3. secs-since0 (время с ПЕРВОГО, т.е., самого левого/давнего измерения).
    • В колонки, значений которых за какое-то давнее время еще не велось, пишутся NaN.

    Засим считаем раздел реализованным.

  • 18.04.2010: пора избавляться от "единственности", окончательно отделив Chl_histplot от Chl_gui/Chl_privrec: чтоб только стандартный вызывался по Shift+MB3, а так бы histplot'ы создавались в нужном количестве.

    (Потребность возникла для первой версии стенда у Ращенко.)

    18.04.2010: несколько замечаний:

    • Для реализации старой семантики -- чтоб можно было указать "этот работает базовым", тогда именно ему будет передаваться Shift+MB3 (чтоб, например, в weldcc можно было менять список плоттируемых каналов).
    • Пусть событие "пришли новые данные, обновим графики" не одну функцию вызывает, а транслируется по per-Xh_window-списку callback'ов.
    • Histplot должен мочь быть объектом, которому можно ПРОГРАММНО указать список knobinfo_t для показа.
    • Надо не забывать перед обновлением проверять XtIsManaged() и отваливать, если НЕ (чтоб работать в куче окон, не всегда отображаемых).

    13.07.2011: повторные мысли в ту же сторону:

    А как бы всё-таки научиться иметь МНОЖЕСТВЕННЫЕ панели "histplot"? Чтоб, например, если есть несколько под-окон, то каждое могло бы включать историю для СВОИХ ручек.

    По пунктам:

    • ВЫДЕЛЕНИЕ СТРУКТУРЫ, адресация по ней, а не по XhWindow;
    • PRIMARY-histplot -- который получает событие "Shift+Btn3";
    • Флаг "fixed" влечёт отказ от primary;
    • Таймерное обновление -- проходится по ВСЕМ histplot'ам (которые при создании заносят себя в список).
  • 30.09.2010: Ращенко высказал еще пожелание: чтоб можно было "делать простейший анализ" графиков: смотреть среднее/минимум/максимум, стабильность (среднеквадратичное отклонение?) за указанное время -- раз все данные там всё равно есть.

    02.10.2010: технический вопрос -- а как/где эти значения показывать?

    Ответ может быть таков: сделать селектор (рядом с селектором "временной масштаб"), где можно было бы выбрать режим отображения чисел: текущее/под_курсором/мин/макс/ср.квадр.

    Это заодно решает и старую проблему (за ??.??.200?) "как посмотреть, какое значение было тогда-то".

  • 13.07.2011: мысль: а чего б РАДИКАЛЬНО не решить проблему частоты обновления?

    Главный корень зла -- что обновление/сдвиг истории происходит ПО ПРИХОДУ ДАННЫХ ОТ СЕРВЕРОВ. В результате -- толпа косяков:

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

    В общем, очевидно, что нынешняя модель -- крайне неудачна, от неё сплошные сложности (правильно -- она ведь выбиралась под односерверные программы, да и вообще весь этот механизм был сплошным "врЕменным" хаком, чисто для быстро и удобно посмотреть; и не претендовал на "правильность" в стиле прочих striptool'ов и data-archiever'ов).

    Мысль: надо сделать ход песцом и перейти от сдвига/обновления по приходу данных на ПЕРИОДИЧЕСКИЙ -- просто по таймеру.

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

    13.07.2011: проект реализации:

    • Формально есть проблема: а как понять, какие ручки надо "историть" по таймерному событию? По приходу данных -- было просто, там всё равно все ручки перебирались. А тут?!
    • Посему -- надо завести список:
      1. Добавить поле knobinfo_t.hist_next.
      2. Где-то завести "начало списка".
    • Сделать в Cdr 2 метода:
      1. "Завести историю для указанной ручки" (что сейчас делается в Chl_history.c::AddToHistPlot()). При этом:
        1. Аллокируется буфер.
        2. Ручка добавляется в список "историзируемых".
      2. "Занести очередную точку в историю для ручек из указанного списка" -- что сейчас делается в CdrProcessKnobs.
    • Базовый период обновления ставится 1с. Но его можно менять Chl_app-параметром "histperiod".
    • За компанию надо бы поменять способ передачи всех подобных параметров от Chl_app: сейчас он просто лезет private-структуру каждого модуля, а надо -- завести в priv2 под-структурку, например, .config, в которую и класть.
    • Соответственно -- заводится таймер, щелкающий с указанным периодом.

      Проблема только в точном отсчёте времён -- у нас ведь интерфейс не cxscheduler с возможностью указывать момент сработки, а Xt, требующая интервал в миллисекундах. Ну, всё просто -- копируем мозги из Xh_cxscheduler.c::sl_enqueue_timeout_at().

    • А по его срабатыванию --
      1. дергается Cdr"занести-очередную-точку";
      2. вызывается "обновить панель истории" для всех зарегистрированных histplot'ов (сей момент -- для единственного, но в близком будущем их станет много).
    • Соответственно, всю алхимию с добыванием периода сервера можно выкидывать.
    • Аналогично -- frqdiv.
  • 14.07.2011: пора переписывать Chl_histplot.c с нуля -- в следующих целях:
    1. Множественные панели histplot'а.
    2. Скроллинг и масштабирование -- в ЧЕЛОВЕЧЕСКОМ варианте.

      (В идеале -- надо сразу делать с масштабированием как в "минус" (сжатие), так и в "плюс" (растягивание) -- чтоб потом этот код можно было унести в Xh_plot.)

    3. Обновление не по приходу данных от сервера, а с некоей фиксированной периодичностью. Для чего, в т.ч.,
      1. Собрать все махинации с histring'ом в отдельное место в Cdr.
      2. ...и устранить frqdiv.
      3. Забыть тут про дОбычу размера цикла сервера.
    4. Передача параметров от Chl_app'а не через лазанье им в приватные структуры со-модулей, а через отдельную подструктуру .config.

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

    14.07.2011: начинаем потихоньку, с простого -- сделан пункт 3a: knobinfo_t.hist_next, CdrActivateKnobHistory(), CdrHistorizeKnobsInList().

  • 01.08.2011: еще несколько месяцев назад появилась мысль: можно ведь не ограничивать количество графиков количеством цветов -- а делать его больше, но при рисовании использовать GC номер не row, а row%XH_NUM_DISTINCT_LINE_COLORS.

    01.08.2011: делаем:

    • Chl_histplot.c:
      1. Во всех обращениях к chanGC[] и chan_finfo[] добавлено %XH_NUM_DISTINCT_LINE_COLORS.
      2. Аналогично -- в указании цвета при создании меток.
      3. Цикл аллокирования GC и шрифтов идёт до наименьшего из MAX_HISTPLOT_LINES_PER_BOX и XH_NUM_DISTINCT_LINE_COLORS.
    • Chl_histplot_types.h:
      1. Соответственно, размер chanGC[] и chan_finfo[] теперь определяется количеством цветов, а не графиков.
      2. А количество графиков сделано -- от балды -- вдвое больше.

    Ну чо -- работает. Вот только подписи к вертикальным осям стали совсем нечитаемыми -- слишком много. Видимо, надо разносить по чётные и нечётные налево/направо.

    24.08.2011: кстати, там не хватало psp-параметров plot9...plot16. Сделаны, причём хитро -- они "активируются" только при достаточном MAX_HISTPLOT_LINES_PER_BOX, иначе alias'ятся на [0].

    24.08.2011: да, приступаем к окрасивливанию подписей.

    1. С давних времён оставалось, что границей для подписей является граница axis, т.е., [0...cur_h), так что подписи к вертикальным осям могут налазить на подписи к горизонтальным, что дюже некрасиво. В Zz_plot'е же границы изначально сужались на (m_top,m_bot), что выглядит лучше.

      Переделано аналогично, стало намного симпатичней.

    2. Собственно разнесение.

      Что разносить налево/направо -- не такой тривиальный вопрос:

      1. можно четные/нечетные (это если каналы идут парами),
      2. а можно и первую/вторую половины (это если каналы идут двумя группами).

      Посему -- делаем оба варианта. Какой вариант использовать -- определяется переменной 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: в продолжение темы:

    1. В y/ фича evenodd добавлена. Правда, проверить её там пока никак (по причине отсутствия adc812* и пустоты manyadcs_knobplugin).
    2. А ведь тогда, в конце августа, в liu/plot/ было сделано только окрасивливание подписей (evenodd), но НЕ возможность иметь MAX_LINES>XH_NUM_DISTINCT_LINE_COLORS.

      Добавляем поддержку большого количества линий сразу в оба liu/{plot,y}/Zz_plot.c.

      • Технология тут радикально отличается: если в Xh_histplot имеется XH_NUM_DISTINCT_LINE_COLORS ячеек с GC и finfo, из которых берётся по row%XH_... (а канал использует row'тую ячейку), то тут GC и finfo фиксированно принадлежат каналам, а % используется для выбора цвета при их заполнении.
      • Отдельно добавлено %XH_... в fastadc_gui.c, где выбирается цвет для метки/чекбокса.
      • И проверить опять же никак -- т.к. там нигде не больше 8 каналов.
    3. P.S. Самое забавное, что вскорости из этих 3 реализаций должна остаться в использовании ОДНА -- как раз y/'овская (или это будет уже z/?).
  • 24.08.2011: весьма неудобна необходимость ЯВНО писать в параметрах "plotN=...", т.е., прямо перечислять номера. К тому же, это мешало бы делать за-#if'оиваемые списки.

    Намного практичнее была бы возможность указывать что-то типа "addplot=...", чтобы оно само находило первую свободную ячейку. Очевидно, что для этого потребуется PSP-плагин (а вот тут-то и пригодился бы предложенный 26-12-2005, но так и не реализованный value-parsing-support-API в PSP).

    24.08.2011: да, сделано, через плагин. Ключ назван add, сам парсер -- AddPluginParser().

    Проверено, работает. Позиционные спецификации также оставлены -- чтоб можно было указывать список "вразброс".

  • 18.05.2012@Снежинск-каземат-11: поскольку "правильный" вариант (с обновлениями по времени etc.) пока не готов, а мерцание уже достало (в liu, где по 5Гц приходят данные от 11 серверов -- 55Hz? пользоваться невозможно...), то делаем ограничение частоты обновления, аналогично fastadc.

    18.05.2012@Снежинск-каземат-11: делаем:

    • Добавлено histplotdlg_t.timeupd.
    • Сравнение скопировано из fastadc_data.c::FastadcBigcEventProc().
    • Максимальную частоту волюнтаристски ставим 5Hz.

    Стало намного приятнее.

  • 16.10.2012@Снежинск-каземат-11: неудобно, что на полях "текущие значения" справа от графика жмётся только просто правая кнопка (Свойства), а Ctrl+правая -- нет.

    Кстати, и Chl_bigvals тоже касается.

    16.10.2012@Снежинск-каземат-11: делаем. Все тамошние проверки заменены на простой прямой вызов KnobsMouse3Handler(). В Chl_bigvals.c аналогично.

  • 19.10.2012@Снежинск-каземат-11: идея от Ярика Куленко:
    По одной из горизонтальных осей подписывать не относительное время (сколько-то назад), а абсолютное (HH:MM:SS).

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

    Сложность же -- в таком случае надо будет ось с "настоящим временем" перерисовывать каждый раз.

    04.03.2013: вот опять та же идея пришла в голову -- по результатам решулярного просмотра графиков сварки "за вчера". Было б удобнее сразу видеть время, а не вычитать 14 часов из текущего.

    Вопрос -- КАК это регулировать?

    1. Переключателем.
    2. Времена "до 1 часа" писать как -MM:ss, а давнее -- уже в астрономическом времени.
  • 11.04.2013: юзеры (это высказывал Карнаев с неделю назад) хотят иметь возможность чтоб набранный в histplot список каналов сохранялся и потом восстанавливался.

    Само-то это действо не особо сложно, но вот КАК (куда) и КОГДА запоминать, а главное -- КОГДА ВОССТАНАВЛИВАТЬ (при запуске?)?

  • 31.08.2016@Снежинск-каземат-11: в некоторых случаях дефицитным является вертикальное пространство на "панели управления" справа от графиков, а не горизонтальное. Например, конкретно на ЛИУ на графиках управления пучком (Ярик Куленко возжелал).

    Надо сделать ключик, чтобы [Save], [Mode], [XScl] расставлялись не снизу вверх, а справа налево.

    31.08.2016@Снежинск-каземат-11: сделано.

    Ключик назван "horz_ctls", реализация тривиальна -- на каждый из управляющих элементов по if()/else'у, а можно б было сделать парой указателей на функции, которым при вертикальной ориентации присваивать {attachbottom,attachtop}, а при горизонтальной -- наоборот.

Chl_bigvals:
  • 13.03.2007: пора, ох пора выносить махинации с bigvals из файла Chl_knobprops -- ибо нефиг смешивать разное. Помимо "нефиг" еще резон -- чтобы ВЕЗДЕ указатель на "запись" именовать rec, а не "pdp", "pdp->big", "hpp", и чтоб тип этого rec'а можно было всегда указывать как dlgrec_t, в начале каждого из Chl_***.c-файла typedef'я его с нужного конкретного типа.

    13.03.2007: да, вынес. С точки зрения переименований -- да, теперь используется dlgrec_t *rec, плюс, функция "доступа-к"/создания диалога называется ThisDialog(). Кроме того, перешел на XhCreateStdDlg и избавился от помнимой rec->shell.

  • 29.03.2007: вслед за histplot'ом перешел на "при удалении последней строки сначала делается unmanage окна, а потом уж строки".
  • 02.04.2007: а не перенести ли кнопку [Close] у bigvals в правый нижний угол окна, как у histplot'а? Она как раз занимала бы место под [^][v][-] самой нижней строки. Была бы экономия места.

    02.04.2007: неа, не перенести.

    Во-первых, проблемно -- в bigvals нету формы, а непосредственно в box вставляется сразу сетка.

    Во-вторых, Малютин сообщил, что с введением графиков большими числами пользоваться практически перестали -- они стали слабо нужны ("большие числа -- это для ламеров").

    Плюс, в конце концов, окно bigvals, в отличие от графиков, скорее врЕменное, а не постоянно-нужное, так что на нем особо экономить смысла нет.

    Так что -- "withdrawn".

Chl_dlgutils:
  • 13.03.2007: а в этом модуле надлежит жить всяким стандартным функциям-helper'ам, типа "создай кнопку".

    14.06.2007: ага, записать-то записали, а сделать -- так и не сделали. Видать, не надо...

Chl_help:
  • 13.03.2007: (давно пора было завести отдельный раздельчик -- вот и заводим)
  • 13.03.2007: вытащил определение helprec_t в Chl_help_types.h (чтоб было как у остальных диалогов) и перевел модуль на dlgrec_t *rec, XhCreateStdDlg/no-rec->shell.
  • 19.05.2007: под-уменьшил высоту окна (аж на 60 пикселов!), сделав обоим grid'ам spacing:=0,0 и padding:=0,0 (для разделения по вертикали вполне хватает и собственных полей XmLabel'ов); плюс, для отделения всего от рамок -- XmFrame'ам уставлено XmNmarginWidth:=1.
  • 22.03.2011@Снежинск-каземат-11: добавил еще "часть" -- CHL_HELP_MOUSE. Теперь описания ПравойКнопкиМыши выдаются только при наличии этого битика.

    Смысл -- чтоб simple-knobs-программы могли использовать help с описанием кнопок, не смущая юзеров неработающими сочетаниями.

  • 11.05.2012@Снежинск-каземат-11: сходу -- теперь по нажатию на [?] при уже наличествующем окне Help это окно убирается. Для полного соответствия функционированию SUBWIN'ов.
Chl_actionknobs:
  • 15.03.2007: Г-н Долбостенко вчера опять прикопался, что, мол, нужна кнопка "Обнулить все" в linmag'е. Просто загрузить режим -- ему не катит, неспособный он.

    Поскольку просто взять и добавить кнопку на тулбар нельзя (она тогда ВЕЗДЕ добавится), то придуман такой выход: вводим специальный knob-plugin "mode button", который грузит режим, указанный в widdepinfo, а для Cdr является NOP'ом.

    15.03.2007: протокол реализации:

    • Создан новый модуль Chl_actionknobs.[ch], имя дано с дальним прицелом -- если кроме загрузки режима понадобятся еще какие-то "действия".
    • В cxdata.h добавлен LOGD_MODE_BTN=5000.
    • Реализация MODE_BTN -- создает кнопку, указанного цвета (color=...), которая по нажатию грузит режим из указанного файла (file=...). Можно указать, что при нажатию на кнопку должен удерживаться Shift (флаг shift) и/или Alt (флаг alt) -- это в качестве "подтверждения", чтобы случайно не нажали; при ненажатости затребованных Shift/Alt просто ничего не делается (а Ctrl низзя -- из-за transtalion'а "c<Btn1Down>:MenuButtonTakeFocusUp()").

    В linmag вставлена красная кнопка [!000!], ко которой грузится mode_linmag_zero.dat (первоначально на ней был тултип "Устаростенить все в ноль, нах!").

Chl_subwin:
  • 07.05.2007: пришла в голову мысль, как можно прямо СЕЙЧАС реализовать возможность делать дополнительные окна с ручками, появляющиеся по нажатию на [Кнопку] (то, что в будущем делать доп. "секциями"):

    Ввести специальный тип подэлемента -- ELEM_SUBWINDOW, который в своем parent'е генерил бы XmPushButton [...], а собственно свое содержимое располагал бы в дополнительном popup-окне, которое бы и появлялось по нажатию на кнопку.

    08.05.2007: делается просто "на раз":

    • Ввел ELEM_SUBWIN=12
    • Сам элемент живет в Chl_subwin.[ch].
    • Кнопка пока что создается с текстом "..." (поскольку фиг знает, где брать ВТОРУЮ надпись -- {elemnet_t,eleminfo_t}.label-то используется для заголовка окна...

      И она же ставится tooltip'ом на кнопку (in conformity with cx-starter's approach).

      Кстати, а не ввести ли в CXv4 еще одно текстовое поле -- "text2"? Или обходиться каким-нить ключиком в widdepinfo/options?

    • Поскольку сейчас лишь симуляция, а не реально доп. окно с набором элементов, как будет с отдельными "секциями", то волюнтаристски постулируем, что семантически элемент SUBWIN является MULTICOL'ом, просто вставляемым в popup-окно вместо контейнера. Естественно, никакое сворачивание нафиг не нужно.

    19.05.2007: собственно, оно ведь работает -- так что "done".

    19.05.2007: кстати, ELEM_SUBWIN -- величайшее изобретение после LOGT_SUBELEM!

  • 12.05.2007: а еще оч-чень полезно будет менять цвет кнопки в зависимости от кумулятивного состояния подэлемента.

    В принципе -- ничего сложного, просто надо завести свою 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 делавшая исключение. Дало ровно нужный эффект (хотя я и не вполне понимаю, почему).

  • 19.05.2007: придумал, как все-таки мочь указывать метку вместо "..." прямо сейчас: надо ввести в _Chl_std_elemopts_t поле char auxlabel[100] и отразить оное в text2_Chl_std_elemopts[].

    19.05.2007: сделал, это было несложно.

  • 21.05.2007: вылез прикол -- ввод в текстовые поля завершается нажатием [Enter], а он еще и приводит к "нажатию" defaultButton'а, так что окошко прикрывается.

    Что странно -- [Esc] отрабатывается почему-то ровно как надо: если она приводит к CancelInputEditing(), то автоматом гасится, а если нет -- то прикрывает окно. То ли дело в action'е process-cancel()? Загадка...

    21.05.2007: начал разбираться...

    Первой же идеей было гасить соответствующий XEvent при отработке в TextChangeCB(). Поскольку это НЕ event-handler, то никакого continue_to_dispatch там нету, поэтому попробовал просто тупо уставлять keycode:=0.

    Не помогло, стал выяснять почему, и -- задница:

    дело в том, что XmText'ов action Activate() после вызова callback'ов просто молча передает этот event своему parent'у, дергая _XmParentProcess() (что эффективно просто вызывает метод parent_process) с process_type:=XmINPUT_ACTION и action:=XmPARENT_ACTIVATE, а уж BulletinBoardParentProcess(), к которому все в конечном итоге и сводится в этой ситуации, в ответ на XmINPUT_ACTION+XmPARENT_ACTIVATE просто дергает action arm_and_activate наличествующего defaultbutton'а, НЕ ГЛЯДЯ на содержимое event. Короче -- с event'ом можно сделать что угодно, и он уже НИКАК ни на что не влияет...
    Вот так по-дурацки устроена обработка событий в Xt/Motif...

    Так что -- единственным доступным способом является погасить нафиг default'ность кнопки [OK].

    22.05.2007: так и сделал -- воспользовался свежевведенным как раз для этого флагом XhStdDlgFNoDefButton, а чтоб кнопка не выглядела куце, заменил [OK] на [Close].

  • 27.02.2009: хочется, чтобы subwin мог бы расцвечиваться полностью аналогично обычному button'у -- т.е., чтобы и LOGD_xxx можно было указывать, и цвет фона, а в идеале -- вообще все поддерживаемые LOGD_BUTTON'ом опции внешности. (Понадобилось всё для того же liucc -- чтоб кнопки модуляторов были vic'овые и красивые.)

    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":

    • Перешли на PSP_P_INCLUDE -- теперь параметры для ELEM_MULTICOL'а парсятся в под-поле subwinopts_t.elem, и оттуда уж передаются в ELEM_MULTICOL_Create_m_as().
    • Соответственно, вся алхимия с SUBWIN_PARAM_SEP убрана -- она теперь более не нужна.
    • Бывшее поле _Chl_std_elemopts_t.auxlabel превратилось в subwinopts_t.label, и сам параметр теперь называется label.
    • Во все DB/*.h-файлах внесены изменения в соответствии с 2 предыдущими пунктами.
  • 02.03.2009: еще одна деталь -- сделал, что при активации кнопки через Btn3 она также фокусируется, для соответствия обычному поведению.
  • 14.07.2010: Логачёв попросил сделать, чтобы при нажатии на кнопку уже открытого под-окна оно бы убиралось -- дабы можно было быстро взглянуть на содержимое, а потом, не елозя мышью, тут же его спрятать.

    14.07.2010: ёпт, а что ж мне это самому-то в голову не пришло!!! Идея-то очевидная, и полностью кореллирует с поведением сворачиваемых-по-заголовку элементов.

    Сделал. Заодно и в 4cx/.

    P.S. Если где-то всё-таки понадобится старое поведение -- то всегда можно добавить булевский флажок, определяющий его: например, onoff=0/steady=1.

    22.07.2010: публика в лице Корепанова возмущается, что кнопки открытых окон никак не выделяются.

    А ведь они правы -- идея-то (теперь уж точно!) очевидная, что при открытости под-окна надо кнопку как-нибудь маркировать.

    Самое разумное -- инвертировать её, по аналогии с toolbar'ными и с choicebs.

    23.07.2010: за эти два дня сделал, хотя и не без маеты:

    1. Специальная функция ReflectShowness(), сравнивающая текущее значение XtIsManaged() с хранимым в rec->is_pressed, и при их несовпадении инвертирующая. Эта функция вызывается из ShowHideSubwin(), плюс -- из специально для этого созданного CloseCB(), вешаемого на parent-shell box'а (аналогично тому, как это сделано в Chl_knobprops.c).
    2. Поскольку сама кнопка используется для отображения текущего colstate'а, то просто тупо делать ей XhInvertButton() было бы нельзя.

      Была мысль сделать в стиле CHOICEBS -- "dlyd_clrz" (delayed colorization), но тут такой подход неприемлем, т.к. кнопки должны ВСЁ ВРЕМЯ показывать текущее состояние, в т.ч. и в "нажатом" виде. Посему:

      • Первоначально просто меняли местами topShadowColor и bottomShadowColor. Терпимо, но некрасиво/неэргономично. Идём дальше.
      • Собственно "расцвечивание" кнопки вытащено в отдельную функцию DoColorize().
      • В ней есть хитрость -- при is_pressed вместо defbg используется defam (каковой добывается при создании кнопки).
      • В ReflectShowness() инвертирование делается так:
        1. XhInvertButton();
        2. Уставка armColor:=is_pressed?defbg:defam;
        3. DoColorize().

    Вроде работает -- в понедельник опробуем на живых клиентах.

    26.07.2010: да, попробовал -- вроде хорошо работает.

    12.08.2010: дополнение: для того, чтобы еще-ни-разу-не-обойденные кнопки были цвета JUSTCREATED, в конец Create_m() вставлен вызов DoColorize(). Иначе оно окрашивалось только после нажатия.

  • 21.07.2010: возникла потребность мочь делать под-окна resizable, чтоб можно было туда помещать, например, ADC200ME (потребность liucc).

    21.07.2010: да-а-а... Ну -- делаем:

    1. Для начала -- надо мочь указывать, чтоб окно становилось "resizable". Для этого введён buttonopts_t-флажок "resizable", приводящий к включению stddlg-флагов XhStdDlgFResizable|XhStdDlgFZoomable.
    2. Но: ведь надо еще, чтобы собственно внутренности аттачились к краям контейнера.

      С обычной сеткой это проделать не удалось -- там при включении horz=fill и/или vert=fill у единственной (точнее, и так самой широкой) клетки слетает башня, и ставится какой-то коцаный размер.

      Посему пришлось извращаться:

      1. Идея -- если заменить сетку на форму, то всё сводится к уже знакомым по fastadc_common и Chl_histplot вещам, когда содержимое аттачится ко всем сторонам формы.
      2. Для начала -- введены _Chl_std_elemopts_t.one и соответствующий psp-флаг "one".
      3. Далее работа ELEM_MULTICOL_Create_m(), на который это свалилось. При наличии флага one и при e->count==1 он
        1. Вместо ChlGridMaker() использует ChlFormMaker() (свежесделанную, хотя раньше была аналогичная).
        2. Вместо обычной раскладки содержимого по строкам и колонкам -- создаёт единственного child'а и аттачит его ко всем сторонам формы.

      А дальше уж оказалось, что XmMessageBox (точнее, его XmBulletinBoard'овская сущность) как раз нужным образом взаимодействует с содержимым на тему ресайзинга.

    Вообще, конечно, по результатам этого хаченья стало очевидно, что для SUBWIN'а выбрана неправильная модель. Надо было его делать не как

    "а в под-окошке он ведёт себя как ELEM_MULTICOL"
    а
    "у ELEM_SUBWIN'а должен быть ЕДИНСТВЕННЫЙ child, который уже может являться хоть MULTICOL'ом, хоть CANVAS'ом, хоть вообще просто ручкой"
    -- в стиле Shell'а.

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

    На вид -- вроде задача решена, пусть и кривовато.

    28.04.2011@Снежинск-каземат-11: а эта фича оказалась ОЧЕНЬ полезна не только для под-окон, но и для резайзабельных обычных окон -- когда элемент с ключиком FILLHORZ. Она решила тамошнюю глобальную и первоначально казавшуюся тупиковой проблему.

  • 14.09.2010: надо бы уметь -- для графиков на ЛИУ -- делать окна со спартанской декорацией: а) без бордюрчика; б) без кнопки [Close]; в общем, как для histplot'а.

    14.09.2010: готово. Опции называются compact (как и в Chl_app) и noclsbtn соответственно, и указываться должны в разделе subwin-specific -- после SUBWIN_PARAM_SEP.

    Собственно отсутствие кнопки [Close] особо не мешает -- окно отлично закрывается как по клавише Esc, так и средствами window manager'а.

  • 29.03.2012: создаём подвид элемента, работающий по "правильной" схеме (21-07-2010) -- "с единственным child'ом".

    29.03.2012: тип назван ELEM_SUBWINV4, реализация живёт тут же. Собственно вся работа перетащена в DoDreate(), принимающая также флаг v4, при котором она вместо вызова ELEM_MULTICOL_Create_m_as() сама делает ChlGiveBirthToKnob(e->content) (проверив вначале, что count>0).

    • Естественно, создаётся только 1-й в списке, а остальные остаются невидимыми.
    • В common_elem_macros.h соотв. макрос SUBWINV4_START() сделан (параметр count в нём оставлен -- чтоб можно было отключать содержимое; а вот ncols убран).
    • Теперь потихонечку будем избавляться от "старых" ELEM_SUBWIN'ов, переходя по возможности на новые -- но старые тоже останутся, т.к. для совместимости режимов их наличие критично (разница в 1 промежуточном элементе).
Chl_histplot_???:
  • 14.05.2007: надобно иметь возможность вставить самописец прямо в экран группировки -- чтобы, к примеру, под управляющими элементами kls'а мог ехать временной график, как у Гусси.

    Примерно такое уже было в istc -- как раз тамошний Chl_histplot.c.

    14.05.2007: как делать -- понятно:

    • Делаем, то ли элементом, то ли декорацией, плагинчик в Chl.
    • Ему в параметрах (options или widdepinfo) указываются интересующие knob'ы -- параметрами knob1= knob2= ..., до 5 штук. И он, при помощи CdrFindKnob(), находит этих жертв в группировке и запоминает ссылки на них, так что пользуется именно ИСХОДНЫМИ knobinfo'ами.
    • Единственный вопрос -- а как бы этак ОПЕРАТИВНО реагировать на изменения knob-properties? Кто б его об этом уведомлял?
    • Размер -- надо иметь параметром, а для красивости можно будет указывать в .placement ключик horz=fill, чтобы график был по ширине окна. И оно должно корректно отрабатывать такой принудительный resize.

    25.06.2009: угу, и на сварке -- в weldcc -- такое тоже нужно. Поскольку с предыдущей записи прошло более двух лет, то вот сводка последних мыслей на эту тему:

    • Делать эту штуку надо уже на Xh_plot'е. Ибо нефиг опять реплицировать код отрисовки графиков.
    • Кроме заранее-предопределенного списка для самописирования, надо бы уметь и динамически его менять. И вот тут мысль -- программа может перехватывать метод ToHistPlot() (на самом верхнем уровне), и тогда добавление/удаление каналов с этим "встраиваемым" самописцем будет устроено точно так же, как и с обычным окном Shift+MB3.

      Правда,

      1. Chl_E_ToHistPlot(), в отличие Chl_E_*Alarm_m() и всех методов в CXv4, НЕ пытается найти "самый верхний определенный метод по иерархии ручек/элементов", а делает всё сам.
      2. Поскольку, в отличие от CXv4, в Chl/Knobs v2 НЕТУ никакого выделенного "верхнего" элемента -- группировки, то и заменять формально не у кого.

      Но и это решаемо, довольно просто, и вот как:

      • Можно приподвыпендриться и сделать в Chl_histplot специальный "hook" -- чтобы если он не-NULL, то вызывалась бы эта функция.
      • А уставлять этот хук можно прямо из встраиваемо-histplot'овского метода _Create_m.
      • И -- хук должен быть per-XhWindow!

    И вот после всех этих типа-сильно-умных соображений вылазит еще одна, кристалльно-простая и очевидная мысль: а может, вставить реализацию встраиваемого-histplot'а прямо в обычный Chl_histplot.c? И тогда:

    1. Уж он там сам бы внутри себя разбирался, какой вариант отображения использовать.
    2. Можно было бы применять физически просто один и тот же код -- даже копировать ничего не надо, просто вместо отдельного окна график будет встраиваться прямо в содержимое того "элемента".

      А отличаться будет только "обвязка" -- создание оформления и реакция на всякие resize.

    25.06.2009: P.S. Кстати, а ведь если эту штуку сделать, то будет в точности то, что было в старом-старом cmapp (или mapp) -- та программа для запяткинского клистрона.

    30.07.2009: да, сварганил именно так -- с реализацией LOGD_HISTPLOT прямо в Chl_histplot.c. Детали:

    • Создание "внутренностей" вынесено в отдельную функцию CreateHistPlotContent(), в ThisDialog() же осталось только создание оформления (окна, формы, кнопки [Ok], ...).
    • Аналогично, собственно добавление ручки на график вынесено в AddToHistPlot().
    • В качестве признака, работает ли график отдельным окном или встроен, используется rec->box -- при ==NULL считается, что график встроен, и НЕ делается попыток появлять/скрывать окно.
    • Собственно -- это и всё с переделкой имевшихся внутренностей Chl_histplot.c! После этого всё нормально пашет -- даже ресайзится прилично (ага, ведь реагировать на ресайз окна-то не нужно, КАЖЕТСЯ).
    • Параметры для графика указываются в widdepinfo:
      1. width и height определяют размер собственно графика (желаемый полный размер для компонента/ручки, увы, указать не удастся -- шибко уж там будут нетривиальные вычисления; да и модель работы тогда придется менять, чтобы оно пыталось поддерживать фиксированный размер КОНТЕЙНЕРА, а не рабочей области...).
      2. То, какие каналы поместить на график, определяется параметрами plot1...plot5 -- указанием имен, со стандартными для Cdr соглашениями (имя просто скармливается ChlFindKnob(), так что халявные короткие ссылки .ИМЯ поддерживаются).
    • Для тестирования создан programs/chlclients/DB/db_histplottest.h.

    Из недоделанностей/недостатков:

    1. Что-то иногда подглючивает -- если поменять пределы у некоей ручки так, чтобы длина числа изменилась (и окошко бы разъехалось), то потом не работают кнопки [^][v][-].
    2. Еще с давних времен -- почему-то не совпадают нули у вертикальной оси и у графиков. Например, при height=200, если значения 0.0, то они рисуются на 1 пиксел выше нуля на оси и сетке.

      25.08.2011: пытался разбираться -- непонятно... Есть подозрения, что это может быть а) результат разного вычисления Y -- в одном случае зеркаленье прямо в RESCALE_VALUE(), а в другом -- отдельное вычитание; не оправдалось; б) погрешности округления в RESCALE_VALUE() -- хбз, как проверять.

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

      25.08.2011: неа, НЕ надо. Более актуально сделанное сейчас разнесение подписей к разным каналам налево/направо.

    31.07.2009: продолжение:

    • Добавлены ключики wide/thin для управления толщиной сразу; и чекбокс "Wide" также уставляется.
    • Ключ nowide убирает этот переключатель.
    • Ключ fixed убирает [^][v][-] и блокирует добавление новых каналов через Shift+Btn3.
    • Для передачи пары последних параметров в CreateHistPlotContent() добавлен параметр flags, флажки имеют префикс CHPC_.

    19.04.2012: в связи с переходом от "wide" к "mode" ключики также переделаны -- режим определяется lookup-параметром mode (значения -- line/wide/dots); отключение -- nomode; флаг -- CHPC_NO_MODE_SWCH.

  • 17.12.2018: пытаемся разобраться в причине несовпадения на 1 пиксел между вертикальными осями и графиками.

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

    17.12.2018: итак:

    • Условия эксперимента таковы:
      • CXv4 (но арифметика там совпадает); измываемся над MotifKnobs_histplot.c.
      • Проверяется утилитой histplot, где высота графика по умолчанию 400 пикселов.
      • 1 канал, читающийся значением 0, с диапазоном, указанным как [-100,+100].
    • Наблюдённые результаты:
      • Значения -100 и +100 чётко укладываются на крайние пикселы графика (верхний и нижний, прилегающие к рамке-осям).
      • А вот значение 0 -- на 1 пиксел выше нулевой оси.
      • Однако если сменить высоту с 400 на 401 -- т.е., на НЕчётную, то значение 0 попадает на ось.
      • Отладочная печать показывает вполне осмысленные цифры: координата Y для отображения значения 0 рассчитывается как 200 для высоты 401 пиксел и как 199 для высоты 400 пикселов.
    • Анализ:
      • Для начала, при ЧЁТНОЙ высоте вообще невозможно однозначно поместить горизонтальную ось (и значение 0) в центр, по причине отсутствия центрального пиксела.
      • Но даже для чётной высоты координата нуля как-то считается, и надо, чтоб она считалась одинаково для осей/тиков и значений.
      • Возможно, разница из-за разного "направления" ("ориентации") вычислений: значения берутся как есть и приводятся к экранным координатам, а тики на вертикальной оси наоборот -- там пляшется именно от экранных координат. Вот при округлении и скачет в разные стороны на 1 пиксел: для значений координата оказывается 199, а для тиков 200.
      • Как бы то ни было, в самой RESCALE_VALUE() всё корректно, а проблемы в её использовании.
    • Почему значение канала (справа от графика) подсвечивается состоянием RED?

      20.01.2019: косяк был в histplot.c.

    30.12.2018: по некоторому размышлению, есть трое "подозреваемых", почему имеем это расхождение в 1 пиксел при округлении:

    1. Координаты для точек графика получаются пересчётом ЗНАЧЕНИЙ в координаты, а для осей/сетки -- пересчётом условных "номеров" линий сетки (т.е., де-факто тоже координат).
    2. Координаты для точек графика делаются "отражением" (чтоб плюс был сверху) прямо внутри пересчёта координат в RESCALE_VALUE(), а для осей/сетки -- сначала считаются, и потом делается отражение.
    3. Комбинация двух предыдущих.
    4. Пересчёт для осей делается в целых числах, с соответствующим отбрасыванием дробной части прямо во время деления, а для значений -- в вещественных, и дробная часть отбрасывается уже только при окончательном присваивании целочисленной переменной y.

    Краткий анализ:

    • Более простым для правки был бы (b).

      Но просмотр кода вроде бы опровергает эту версию: там идут 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);
      (т.е., с отражением, совмещённым с перемасштабированием); результирующий является второе, но, как показывает отладочная печать, они дают одни и те же числа.
    • Вариант (d) -- сомнителен, т.к. формула RESCALE_VALUE() устроена таким образом, что деление является последней операцией, а следовательно, отбрасывание будет выполняться в тот же момент.

      Получасом позже: а вот и да, именно оно!!!

    • Таким образом, наиболее основное подозрение падает на вариант (a).
    • Вариант идеи: пересчитывать "номер" линии сетки в значение, а уже его потом в координату.

      Авотфиг! Ибо:

      1. При пересчёте из целочисленного номера в вещественное значение вполне может заиметь место некоторая "потеря точности", в результате которой получится значение, маппирующееся на координату, отличающуюся на 1 пиксел.

        Т.е., будет только усложнение, но не будет результата.

      2. И вообще: а к КАКОМУ вещественному значению приводить? Ведь на одной панели (Xh_plot и histplot) может рисоваться целая толпа РАЗНЫХ графиков, с совршенно разными пределами.

        И калькуляция координат для сетки прямо в "номерах" тут как раз к месту.

    • Резюме: причина в том, что вычисление координат из разных источников (номера линий (целочисленные) и значения (вещественные)), в идеальном (математическом) случае должное давать одинаковые результаты, в реальности вычислений процессором даёт результаты, на 1 целочисленную единицу отличающиеся.
    • Ещё идеи, что делать?

    Продолжаем думать и тестировать...

    • Возвращаемся к результатам от 17-12-2018, когда при высоте графика в 400 пикселов вертикальные координаты линий сетки получались 300, 200, 100: а, собственно, ПОЧЕМУ так -- почему не 299, 199, 99?

      Выходит, что оно округляется ВВЕРХ?!

    • Проводим тест: передаваемое в RESCALE_VALUE() значение сначала переделываем из i в (double)i.

      ...и-и-и -- да!!! В результате получаем именно 299, 199, 99!

    • Это что же получается: причина -- всё-таки некая комбинация вариантов (d) и (b)? Т.е., "способ" проведения "отражения" всё же влияет, по крайней мере, в случае вещественных чисел?
    • А как бы эдак управильнить вычисления прямо в ЦЕЛЫХ числах, не прибегая к вещественным?

    Ещё немного материала для размышления:

    • Насчёт вариантов формул:
      • Впервые значения 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 гарантированно работающих способа решения проблемы:

    1. Расчёт координат осей с использованием double.
    2. Расчёт координат графика с отдельным последующим отражением.

    Но надо б поразбираться, чтобы понять, где же происходит перескок на 1 пиксел, и постараться найти ещё 3-й вариант.

    Часом позже:

    • Попробовал -- добавил отладочной печати для вывода промежуточных результатов вычислений.
    • Идей пока не появилось...
    • А появилось только понимание причины: разница -- между вариантами с использованием (double) и без него -- образуется в момент ДЕЛЕНИЯ.
      • Которое в случае координат осей/сетки является делением на количество клеток.
      • И там срабатывает как раз "интегрированное отражение": в результате деления получается некоторое отрицательное число, которое при работе в целых числах округлится до ближайшего целого в сторону нуля, а при работе в вещественных дробная часть останется, вычтется (поскольку отрицательная!) далее из результата, и при приведении к целому приведёт к отбрасыванию лишней единички.
      • Например, при высоте 400 пикселов имеем в "средней" точке (там, где ось 0, и координата колеблется между 199 и 200):
        i=4 v_tick_segs=8 grf_h-1=399
        что даёт:
        1. При ОТДЕЛЬНОМ отражении:
          1. Через вещественные
            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
          2. В только целых
            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

          Т.е., результат ОДИНАКОВ.

        2. При ИНТЕГРИРОВАННОМ отражении:
          1. При использовании (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.
          2. При исключительно целых же, т.е.,
            y = RESCALE_VALUE(i, 0, v_tick_segs, grf_h-1, 0);
            имеем
            4 * -399 / 8 + 399 = -1596 / 8 + 399 = -199 + 399 = 200
    • Где тут что можно "оптимизировать" вариант с интегрированным отражением прямо в обычных целых числах для получения результата "199" -- совершенно неясно.
    • Зато укрепляется убеждение, что "корень зла" именно в интегрированном отражении и нужно просто делать его отдельно.

    Надоела уже эта маета до чёртиков -- делаем!

    • Переход к "отдельному отражению" сводится к 2 пунктам:
      1. Координаты сетки/осей считаются как
        y = grf_h-1 - RESCALE_VALUE(i, 0, v_tick_segs, 0, grf_h-1);

        В т.ч. и в отрисовке axis -- DrawAxis() и DrawPlotAxis(), где УЖЕ было именно так, т.к. там отражение делается с учётом m_top (margin top -- смещения виджета "график" внутри виджета "axis").

      2. Координаты точек графика --
        y = grf_h-1 - (int)(RESCALE_VALUE(v,
                            mindisp, maxdisp,
                            0, grf_h - 1));
        
    • Изменения коснулись:
      • MotifKnobs_histplot.c -- на нём все опыты и делались.
      • v2'шный Chl_histplot.c -- модификации идентичны предыдущему.
      • Xh_plot.c -- тут вообще не факт, что вообще были проблемы:
        1. Вычисления для сетки/осей там УЖЕ были с отдельным отражением.
        2. Вычисления для графика хоть и переделаны, но отображение на вид не изменилось (сравнивалось старое с новым на примере v5kls, adc200 3-го клистрона).
Chl_elog:
  • 13.09.2007: пора делать модуль взаимодействия с e-logbook.

    В нем -- пока что -- будут два вида функциональности: взаимодействие с пользователем (диалог "сделать запись...") и собственно функция отправки.

    13.09.2007: добавлена пиктограмма btn_elog.xpm -- перышко с подписью "e-log" (выглядит, надо сказать, мерзковато).

    02.07.2009: поскольку оно так пока и не используется (да и не работает), то строку cmChlDoElog в Chl_stdtoolbar.c::stdtoolslist[] закомментировал.

Chl_rowcol:
  • 27.07.2010: видимо, придётся-таки изготовить ELEM_ROWCOL -- для того, чтобы паковать в одно окошко несколько одинаковых графиков, чтобы они синхронно меняли размер при растягивании/сжатии окна (это если для liucc захотим размещать все осциллографы стойки в одном окне). И там будет XmNpacking=XmPACK_COLUMN.

    12.05.2012@Снежинск-каземат-11: да, делаем -- понадобился для под-окошка liubpms в db_liu.h.

    • ELEM_ROWCOL=16.
    • В common_elem_macros.h добавлен ROWCOL_START().
    • Реализация -- в Chl_rowcol.c.
    • Используется, естественно, XmPACK_COLUMN.
    • Параметр ncols поддерживается, отображаясь на количество колонок (XmNnumColumns).
    • Ориентация управляется флагами horizontal (default) и vertical.
    • Граничные поля по вертикали и горизонтали ставятся в 0.

    Теперь о грустном: изменение размера (путём тягания за границу окна) оно поддерживает, но:

    • Только ПОПЕРЁК ОСНОВНОГО направления (т.е., для horizontal -- по высоте, для vertical -- по ширине), продольное направление игнорирует; ...
    • ...и только для ПОСЛЕДНЕЙ строки/колонки.
    • ...хотя как-то, похоже, пытается поддерживать единый поперечный размер во всех колонках (но не особо успешно).
    • В результате использовать оное в многоколоночном режиме с ячейками, могущими менять размер (как те же графики) -- невозможно, т.к. они только растут, но нет никакой возможности

    На будущее -- мож, захочется мочь указывать интервал между клетками (к сожалению, это ОДИН параметр XmNspacing).

    И уж точно будет желание разобраться с ресайзингом (хотя как, и возможно ли в принципе -- непонятно; ask Гусси? Мне-то казалось, что он умеет ресайзить всё пропорционально...).

    Пока же --

    1. Считаем за "done", т.к. работает и в минимальном варианте (т.е., для liubpms) уже вполне юзабельно.
    2. Но общее впечатление слегка разочаровывающее -- прямо хоть ELEM_LRTB делай, поскольку в исходном liubpms окно группировки (оно реализуется как lrtb) работало лучше. А я-то так надеялся!!!

    12.05.2012@Снежинск-каземат-11: нашел: XmColumn, а не XmRowColumn!!! Одноколоночный, и пропорциональность поддерживает при XmNstretchable=True. Одна проблема: утверждается, что XmNstretchable -- только CG, без S, так что не удастся созданному-в-другом-месте виджету уставить stretchable...

    12.05.2012@Снежинск-Снежинка-305-душ-вечер: а ведь ВСЁ желаемое (растягивание "поперёк" по максимальному, пропорциональное растягивание, ...) должен уметь виджет-сетка XmSepGridLayout. Нынешняя неумеющесть -- просто результат бага, причём наверняка не супер-сложного (некорректный обсчёт при fill). Ну что, потратить несколько дней на его исправление? Тогда можно будет Chl_rowcol уволить нафиг...

Chl_scenario:
  • 23.12.2010@Снежинск-каземат-11: создаём новый тип ручки/NOP'а -- scenario, модуль Chl_scenario.[ch].

    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'е.

    1. Дерганье юзером - как угодно.
    2. Вот как его с формулами скрещивать?

    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: проект готов, можно приступать.

    23.12.2010@Снежинск-каземат-11: начинаем:

    • LOGD_SCENARIO=5001
    • Скелет копируем с Chl_actionknobs.

    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: предварительный вариант сделан -- недурненько, пашет. Теперь надо б под-украсивить:

    1. В момент старта включать "подсветку" кнопки (lit), по окончанию -- гасить.
    2. По ошибкам (-1) делать lse_stop() и гасить подсветку.
    3. Главное -- пока очень крупно не хватает внятного error-signaling'а. Типа вылазит -1, а из какого места -- хбз.

      Для начала надо б хоть просто stderr-диагностику сделать

      А потом -- думать о чём-то lasterr-подобном.

    25.01.2011: да, сделано и подсвечивание на время исполнения, и гашение+торможение по ошибкам и окончанию.

    Также добавлено парсенье декораций и их использование -- скопировано из button_widget. Поскольку кнопок двое (start+stop), то декорирование вытащено в отдельную функцию TuneButton(); она прям напрашивается на унос в KnobsI.h+Knobs_internals.c.

    На данный момент заявленные цели достигнуты, так что "done".

  • 21.04.2011@Снежинск-каземат-11: косметика -- при отсутствии кнопки [X] форма не создаётся, а start-кнопка делается непосредственно в указанном parent'е.

    Причина -- чтоб можно было такие кнопки помещать в ELEM_SUBMENU.

Chl_invisible:
  • 23.12.2010@Снежинск-каземат-11: Создаём новый тип элемента -- invisible, модуль Chl_invisible.[ch].

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

    23.12.2010@Снежинск-каземат-11: Делаем:

    • ELEM_INVISIBLE=13.
    • собственно модуль -- тривиален, он создаёт Core размером 1*1, плюс регистрирует emethods с единственным методом Chl_E_SetPhysValue_m -- он необходим для отработки ручек-писателей, все прочие методы NULL.
    • ну и в ChlMakeElement() альтернатива ELEM_INVISIBLE добавлена.

    Осталось проверить.

    Замечание: а не будет ли эта штука де-факто частичным ответом на вопрос 01.06.2005:"не потребуются ли нам "невидимые объекты" -- как в LabView таймеры"?...

    21.04.2011@Снежинск-каземат-11: Замечание по "мозгам":

    • Никакие "мозговитые" типы ручек (вроде scenario) в invisible помещать никакого смысла нет -- ибо сами ручки создаваться никогда не будут.

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

    • Соответственно, смысл имеет -- только то, что работает через cda.
    • А вот в v4 и сценарии также будут сделаны на Cdr/cda, так что там автоматом всё станет "правильно".

    14.12.2011@Снежинск-каземат-11: проверил. Имелся баг -- оно забывало проставить e->emlink=&invisible_emethods, так что реально оно как бы и НЕ работало (из сценария key в liucc -- просто физически не проходило уставление значения). Пофиксил, теперь работает, так что можно считать за "done" (но в этом раздельчике его ставить некуда :-)).

Chl_submenu:
  • 27.03.2011@Снежинск-каземат-11: очень пользителен будет такой специальный тип элемента -- "выпадающее меню". Чтоб за масенькой кнопочкой прятались бы несколько кнопок. Нужно, например, для глобальных-для-стойки команд в liucc.

    И особенно полезной эта штука будет при возможности располагать часть ручек в заголовке.

    (Да, конечно, засовывать в подменю можно будет только то, что понимается Motif'ом -- кнопчатости, метки и alarmonoffled'ы, но тем не менее.)

    20.04.2011@Снежинск-каземат-11: Делаем:

    • ELEM_SUBMENU=14.
    • Собственно функционал в начальном варианте реализован.

      Ну и мерзко ж в Motif'е организована работа с меню!!!

    • Сейчас оно сделано как связка: MenuBar{PulldownMenu,CascadeButton}.
    • Держим в уме, что кнопка должна расцвечиваться по кумулятивным флагам/состоянию своего содержимого. Сделано всё, кроме собственно расцвечивания кнопки.

    21.04.2011@Снежинск-каземат-11: продолжаем:

    • Попробовал поиграться с tearOff (XmNtearOffModel=XmTEAR_OFF_ENABLED) -- работает, но вылезла техническая проблема -- где брать для него заголовок?
      • Просто из метки кнопки-меню -- некорректно, т.к. эти метки обычно будут супер-короткими.
      • Тултипа, который бы отлично подошел -- нету
      • Остаётся только заводить параметр "label", аналогично SUBWIN'у -- тогда его и тултипом можно будет ставить самой кнопке.

      Так и сделали -- параметром.

    • Досделана колоризация по содержимому.
    • Сделана "фишка" -- значение ncols используется: при ncols>1 делается многоколоночное меню (уставляется XmNnumColumns=ncols, XmNpacking=XmPACK_COLUMN).

      Естественно, ни nflrs, ни nattl не поддерживаются :-).

    • Поскольку Motif плохо приспособлен к ситуации "много MenuBar'ов", и частенько забывает убирать тень от кнопки, то пришлось выпендриться и тень убрать вовсе.

    22.04.2011@Снежинск-каземат-11: заканчиваем:

    • Проверено, что и колоризация также работает.
    • В tearoff'ах -- выглядит забавно, когда оно махинирует с вроде бы присутствующими В ДРУГИХ местах виджетами. Хотя в момент, когда основное меню выпадено -- то содержимое tearoff'а меняться перестаёт.
    • "MenuBar" ведёт себя странненько: при ширине кнопки <12 пикселов оно всё равно выглядит так, словно 12 там есть -- справа от кнопки немного пустого пространства.
    • Сама кнопка выглядит хреновенько -- слабо кореллирует по стилю/геометрии с прочими.

    Как бы то ни было -- оно вполне работоспособно, так что "done".

    27.09.2011@Снежинск-каземат-11: слабокореллирующесть определялась в первую очередь тем, что у виджета стояло имя "submenu_btn".

    • После смены на "push_i" ситуация чуток улучшилась, но совсем не до конца (в т.ч., и из-за отсутствия тени).
    • Самое странное: тень-то при этом появилась (поскольку пришлось закомментировать XmNshadowThickness:=0), и, вроде бы, ничего не глючит.
    • "Не до конца" ситуация улучшилась потому, что XmCascadeButton форсит XmNhighlightThickness=0.
    • Посему -- исправил ситуацию, дав отдельное название -- "submenuBtn" -- и сделав для неё отдельные fallback-строки, с размером highlightThickness, переведённым в marginHeight.
    • Теперь всё окей.

    29.09.2011@Снежинск-каземат-11: попробовал улучшить внешний вид -- добавлять справа треугольничек через XmNcascadePixmap.

    Нарисовал треугольничек -- submenu_triangle.xpm, вставил создание пиктограммы (с упрозрачниванием) и уставление её виджету. А вот хрен -- почему-то не работает, просто не появляется...

Chl_outline:
  • 27.08.2013: будем делать элемент-дерево, по результатам разборок в 2006г., на основе XmOutline.

    Отличия в том, что уберём нафиг "дисэйбленье" веток, зато добавим сворачиваемость.

  • 27.08.2013: приступаем.

    28.08.2013: аллокирован ELEM_OUTLINE, создан файл, сделана начальная набивка.

    Из-за Motif bug #1234, он же Bug 74502@RH-7.3, исправленный не вполне ясно в какой версии, пришлось завести "внутреннюю" директорию Xm/ с XmStrDefsI.h внутри.

    29.08.2013: идеологически всё совсем не тривиально. Сделать сразу, в лоб, правильный/идеальный элемент -- хбз как.

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

      Но для rfsyn'а такое не подходит -- там и корни веток тоже должны быть листиками, представляющими поля для ввода задержек.

    • Т.е., просто набор вложенных элементов, из которых Create_m() сготовит цельное дерево, просто так не проходит.
    • Врое бы можно помудрить так, чтоб ПЕРВАЯ ручка в элементе ставилась в строчку к пиктограммке фолдера. Но тогда сама структура получается неочевидной, неоднозначной и кривоватой -- даже код реализации выйдет некрасивым и, вероятно, с дублированиями.
    • ...пришла даже мысль сделать такой outline вообще ЕДИНЫМ элементом, БЕЗ ПОД-элементов, чтоб в .placement у чайлдов указывать, что вот данный -- это начало новой ветки (и как -- fixed/foldable/folded), а вот этот -- последний в вышестоящей.

      Но тут мало того, что "указания" будут множественными (корень ветки может одновременно быть последним у родителя); так еще и просто такая структура тоже кривовата -- чисто идеологически, и просто неудобно у ЧАЙЛДОВ указывать последнесть (вот если б маркером каким-то отдельным, типа "}", но то уже как раз обычная структура вложенностей).

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

    Ну и что делать?... Воспользоваться подходом "ждание преполняет"?

Chl_panes: (или Chl_split:?)
  • 06.10.2014@Снежинск-каземат-11: надо бы иметь компонент "split'абельное окошко" -- для графиков, Ярик Куленко дюже просит, чтоб liu'шное под-окно "BPMs" аранжировать по-всякому.

    В Motif оное есть, только б понять, кого использовать -- XmPaned или XmPanedWindow, и в чем вообще между ними различие?

    23.10.2014: приступаем.

    • Название выбрано "Chl_split" -- во-первых, чтоб имя файла с Chl_priv2.* не интерферировало, а во-вторых потому, что в Qt соответствующий компонент называется QSplitter.
    • ELEM_SPLIT = 18.
    • Панель будет "голой" -- без title/hline/shadow, просто в силу специфики использования в них надобности не видно.
    • Сделано пока в очень простом варианте -- безо всяких параметров.
    • Теперь о выборе виджета:
      • Сначала было сделано на XmPanedWindow.
      • ...и глючило -- считай, что не работало: окно (испытывалось на переделанном v5h1adc200s) появлялось какого-то крохотного размера.
      • Замена на XmPaned всё исправила -- работает как надо.
      • Поиск по bugs.motifzone.net ничего на конкретно эту тему не дал, хотя какие-то иные глюки с похожим описанием упомянуты.
      • Но там в #1476 в комменте за 2009-04-06 сказано
        You may use Paned widget instead. 
        It has almost the same functionality as PanedWindow.
        
        PanedWindow is deprecated as duplicating widget of Paned.
        
      • Кроме 2.2.2-5@RH-7.3 проверено также на 2.3.3-4@SL-6.3, эффект ровно тот же -- баг в XmPanedWindow и окей с XmPaned.

      Так что останавливаемся на XmPaned.

    23.10.2014: оформляем:

    • Указание ориентации -- флаги horizontal и vertical (умолчание).
    • Кстати, замечено, что fastadc'шные панели внутри split'а слегка придуриваются: сворачивание cpanel'а по [-] проходит нормально, а разворачивание по [+] визуально ничего не меняет, пока не ресайзнешь окно.
Chl_scroll:
  • 24.10.2014: и для комплекта еще и "скроллировабельная область".

    24.10.2014:

    • Название "Chl_scroll" -- на "s" уже много всякого; жаль, что еще на "sc" -- перекрывается с "Chl_scenario", но в v4 оно будет "_cda_scenario", так что проблема исчезнет.
    • ELEM_SCROLL = 19.
    • Первоначальная версия внутренностей сделана, очень простой.
    • Попытка указывать желаемый размер (предусмотренными параметрами width и height) ничего не дала -- по крайней мере, внутри split'а.

    Проще пока не заморачиваться (Motif славен своими глюками на тему geometry management), а дождаться реального примееения и тогда добить.

???:
Knobs:
  • 23.01.2004: обнаружил в "man XmFrame" фразу
    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.

  • 25.01.2004: сделано с ходу: необходимость иметь функцию типа ExtractDoubleValue() появилась и вне lib/Knobs. Посему вытащил ее в Xh, переименовав в XhTextExtractDoubleValue().

    Заодно (для единообразия) переименовал также XhSetTextCursorCallback() в XhTextSetCursorCallback().

    25.01.2004: заодно добавил в ExtractDoubleValue(), что при ошибке оно "фокусирует" виджет-текст.

  • 04.04.2004: забавный эффект: с нашим "протоколом отработки кнопок" (LOGD_BUTTON) cda/Chl рассматривают возврат значения !=1 как "управление другим оператором", и кнопка подсвечивается оранжевым.

    Некрасиво! (Хотя, с другой стороны -- оно показывает, что "эту кнопку только что нажали".) В принципе, конечно, не смертельно, но -- именно некрасиво. Если можно пофиксить -- надо фиксить.

    Вопрос -- как фиксить? Пока в голову приходит идея, что так же, как делается "унифицированная бибикалка (нынешний 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.

  • 18.04.2004: надо изготовить некий интерфейс для "простого" создания knob'ов, без предварительного описания длинного и муторного содержимого структуры 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()'а.

  • 06.05.2004: разбирательства с Knobs_typesP.h::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: еще о показе "статуса" (расшифровки rflags)...

    30.06.2004: вчера обсуждал с Гусевым проблему показа статусов ошибок на графических элементах в его программах. Он пришел к той же схеме, что и я: при возникновении "блокировок" можно нажать на кнопку-label, появится окошко с расшифровкой (набор лампочек-чекбоксов, нужные -- подсвечиваются) и кнопкой [Reset]. (Поскольку я ему показывал свою реализацию, то, возможно, его вариант навеян моим.)

    Я ему предложил (хи-хи! сможет он это сейчас сделать, как же!) альтернативный, более "легкий" для пользователя вариант: что расшифровка показывается в tooltip'е при наведении мыши на кнопку, а [reset] делается сразу её нажатием, безо всяких дополнительных окон.

    Но это-то ладно, а сегодня пришла в голову мысль, как Я могу сделать аналогичное "легкое" для пользователя отображение расшифровки rflags: у нас ведь кроме tooltip'ов предусмотрен statusline, и еще с незапамятных времен (1997/1998гг.) имеется специальный интерфейс XhMakeTempMessage() (как раз для tooltip'оподобных целей -- hint'ы для пунктов меню). Так вот: а что, если при EnterNotify смотреть, что если ki->colstate==COLALARM_HWERR (или как корректнее определять "побордовелость"?), то сваливать в некую строчку список "кратких" имен имеющихся проблем (*_strrflag_short()) и делать ею XhMakeTempMessage().

    С этой идеей есть две проблемы:

    1. Что надо будет корректно убирать этот tempmessage при выходе мыши из элемента (LeaveNotify?) и при исчезновении проблемы (а вот это чуть сложнее -- вопрос, куда вставить проверку, "на что повеситься"). Но это-то вопрос технический.
    2. А вот что существенно сложнее идеологически -- поскольку libKnobs формально никак не привязана ни к cxlib, ни к cda, ни к Cdr, то у ее содержимого нет НИКАКОГО способа превратить содержимое rflags в текст!!!

      Отсюда вывод: а на 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 уставляется строка "", восстанавливающая основное сообщение.
    • И -- что важно! -- делается и обновление по приходу данных: в "callback"'е NewDataArrived() вызывается та же DisplayBarStats (если в статической переменной NULL -- то просто ничего не делает).
  • 03.07.2004: кстати, как насчет сделать возможность вызывать окошко "properties" с клавиатуры? А "bigval"? И если да, то какими кнопками? Симпатичным выглядит Ctrl+ПРОБЕЛ, но Ctrl -- это для bigval... Или использовать Alt+Enter, как в Форточках? Или, поскольку "props" вызывается по правой кнопке мыши -- то Shift+F10 (как аналог правой кнопки для вызова локального-контекстного меню)? BTW, кнопки [...] в propsWindow у нас non-traversible.

    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: обнаружил при рассказе Феде-jr, что отсутствует функция типа "SetKnobValue()" -- чтобы пользователям simple-knobs не надо было лазить в методы.

    02.07.2004: добавил функцию SetKnobValue(k,v) в Knobs_simple.c (просто скопировал кусочек из Cdr). Она потребуется только пользователям simple-knobs, а Cdr, вследствие своей "отвязанности" от реализации Knobs, всегда будет лазить в методы напрямую.

  • 03.08.2004: кстати, имеется проблема, аналогичная Xh'ной: надо иметь функцию для отдачи виджета-индикатора для указанного Knob'а, чтобы клиентской программе не приходилось включать Knobs_typesP.h.

    03.08.2004: сделал. Всего-то -- XhWidget GetKnobWidget(Knob k); испытал на nipp/ipp.c -- работает. И в доке ее описал.

  • 23.08.2004: с какого-то бодуна поле 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: как насчет ввести вызов типа "ColorizeKnob()", чтобы всякие не-Chl/Cdr-клиенты (типа ipp) тоже могли баловаться колоризацией?

    24.08.2004: ввел SetKnobState(), просто скопировав из Cdr.c::SetColstate(). Оно уже используется в новом ipp.c.

    А вот поддержку "otherop" нормальную сделать сложнее -- которая 5 секунд держится -- что теперь, вытаскивать еще море всякого из Cdr в отдельную библиотеку?

  • 14.09.2004: похоже, может иметь смысл еще один метод в 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".

  • 26.09.2004: (назрело давно):

    Теперь, с введением 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'а.

  • 05.10.2004: обнаружил давний-давний ляп: CreateKnob() возвращала значение "ki->indicator!=NULL", т.е., 1 при успехе, 0 при обломе. А везде было рассчитано на протокол "0:успех,-1:облом". Сейчас, когда вставил реальную проверку в CreateSimpleKnob(), это вылезло.

    05.10.2004: исправлено.

  • 24.10.2004: (назрело-то давно) насчет протокола B_IS_{BUTTON,LIGHT,ALARM}: надо бы константы "1" и "2" вытащить в какие-нибудь символьные имена, а то дюже уж некрасиво сейчас...

    26.10.2004: готово -- сделал в Knobs_typesP.h константы KNOB_VALUE_LIT_MASK и KNOB_VALUE_DISABLED_MASK. И заменил ими "1" и "2" в Cdr.c, Knobs_{alarmonoffled,button}_widget.c.

  • 07.11.2004: назрело, по-моему -- надо ввести дополнительное цветовое состояние "неинициализировано", например, COLALARM_UNINITIALIZED. Это понадобилось для расцвечивания крупногабаритных чисел (см. соотв. раздел). В остальных-то случаях все и так шло гладко и линейно, когда надо что надо выставлялось, а в bigval -- там клиентский ki может меняться по ходу дела...

    07.11.2004: сделал, теперь значением 0 у нас обладает именно COLALARM_UNINITIALIZED. Посмотрим, где это может вылезти боком -- в принципе, нигде не должно, но если будет -- то это проявится ошибка, которую надо будет пофиксить.

  • 07.11.2004: еще прикол: обнаружилось, что "яркими" желтым и красным цветами выделяется только 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.

    Что обнаружил ("что показали археологические изыскания" :-):

    • Реально корни "цветности" идут из старого drc (~начало 1998г.), где даже имен для цветностей и состояний не было, а только цифры 0, 1, 2. Там было выделение чисел цветом -- нынешние LOGC_{NORMAL,HILITED,IMPORTANT}.
    • В mapp/cmapp также не было подсветки фона -- LOGC_* влияло только на цвет букв. (Там, похоже, вообще не было понятия "alarm".)
    • В oldcx оно появилось. Состояние colstate по-прежнему мерялось цифрами 0, 1 (yellow) и 2 (red). Выбор цветов там выглядел весьма запутанно -- условная конструкция с 3-уровневой вложенностью. И, похоже, уже тогда "яркие" цвета ставились только для LOGC_IMPORTANT...
    • В curcx наконец-то появились константы COLALARM_*. И по ним теперь делался switch(newstate) для выбора bg, fg же выбирался отдельно, своим "switch(bi->color)". А старая 3-уровневость была закомментирована в "#if 0". И там тоже "яркость" ставится только для IMPORTANT.
    • В современной версии фактически остался код из curcx -- разве что добавилось вариантов LOGC_* (DIM и VIC) и COLALARM_* (HWERR, OTHEROP).

    Так что -- "яркость только для IMPORTANT" была задумана с самого начала.

    Посему -- эта особенность отражена в doc/lib/Knobs.html, раздел же помечаем как "done".

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

  • 05.01.2005: устранение значительной части warning'ов "incompatible pointer type", вызванных смешиванием Widget и XhWidget.

    05.01.2005: ввел в Knobs_internals.h макросы CNCRTZE() и ABSTRZE() (скопировал из Xh_utils.h), и начинил ими все подряд. Плюс, все методы, указываемые в vmt, теперь "кошерные" -- в их определениях всегда указывается XhWidget, а уж они сами дальше делают конверсию.

    12.11.2005: продолжаем пьянку -- исправлено одно место в Knobs_widgetset.c. Иных таких косяков пока вроде больше нету.

  • 14.01.2005: а можно ли сделать, что отсутствующий у виджета метод 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-метода.

  • 11.04.2005: Раньше CancelInputEditing() всегда сбрасывал значение в curv, что есть неприятно. Сейчас, по опыту сегодняшних же разборок с "изменением кучи связанных каналов стрелкой в одном из них", ввел интеллект, возвращающий к "наиболее недавнему значению" -- с использованием, при надобности, userval.
  • 19.04.2005: Ммм... По опыту вчерашнего запинывания text-виджета под "почти стандартную модель": похоже, виджеты text, slider и dial/knob/gauge имеют между собой ОЧЕНЬ много общего. По крайней мере, с точки зрения поведения (отображение хитрых деталей -- отдельный вопрос). Начинают легонько напрашиваться дальнейшие шаги по унификации...
  • 06.05.2005: по опыту использования первоначально read-write-widget'ов в readonly-целях:
    • Во-первых, они при этом начинают выглядеть не слишком-то красиво -- из-за того, что без теней, но остаются цвета CTL3D.
    • Во-вторых, readonly-widget'ы должны "сливаться с фоном", а read-write -- "бросаться в глаза", как текст со своим белым фоном.

    Явно -- надо распространить принцип, используемый текстовыми полями (разные имена -- "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:

    • Исчезла пара "input" и "value" -- они превратились в "text_i" и "text_o". Это доставило наибольшие неудобства, поскольку старые имена использовались в море мест.
    • Перевел также alarmonoffled, button, selector и slider.
    • Dial-подобные пока трогать не стал -- с ними не вполне понятно, что делать.
    • А уж декоративные -- label, separator, плюс отмирающий light, трогать тем более не стал.

    16.05.2005: короче -- пора помечать как "done".

  • 17.05.2005: поле 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?! Вот сегодня-то в них и добавил.

  • 17.05.2005: мыслишка: дабы в будущем не огребаться с постоянной раздвижкой флагов в behaviour -- а не поделить ли это поле на две 16-битных группы, и просто мирно линейно добавлять к ним по мере надобности? Естественно, это уже опять в CXv4.
  • 23.05.2005: давно пора -- надо сделать внутренний вызов "ParseWiddepinfo()", который бы надлежащим образом ругался при ошибке -- а то этот код дублируется в море мест...

    23.05.2005: сделал оный. И перевел на него "клиентов" -- их было-то всего ничего: dial, slider, text.

    24.09.2006: легкое дополнение: при ошибке теперь делается не просто очистка -- bzero() -- rec'а, а "инициализация умолчаниями": если ошибка -- PSP_R_USRERR, то оно вызывает psp_parse("", ...), тем самым заполняя структуру умолчательными параметрами; иначе -- если PSP_R_что-то-другое -- то тогда уж bzero().

  • 23.05.2005: а также надо сделать метод "GetKnobLabel()", каковой и определяет наличие/отсутствие поля "label", и отсекает начальный '\n'.

    23.05.2005: метод сделал. Клиенты: alarmonoffled, button(+), dial(+), label, light, selector(+), slider(+), text(+). Те, что помечены "(+)" -- в них сходу использовалось ki->label напрямую, а остальные и так были "умные", и они только сократили объем кода.

  • 22.12.2005: (После разговоров с Федей-jr и Пашей на тему программки для автоматизации ауслендеровского ускорителя в 3-м здании): вообще-то надоело уже не-chlsimple-клиентов клепать, которые занимаются "умным" расположением содержимого workspace'а.

    Собственно -- а что мешает уже прямо сейчас сделать зачатки плагин-архитектуры? Т.е., чтобы программа могла зарегистрировать свою таблицу "виджетов" (да, пока что -- по идентификаторам), и 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".

  • 16.01.2006: надо бы "подрасширить" публичный интерфейс Knobs на тему "цветовые состояния/флаги->цвета", чтобы вся такая логика была сосредоточена в одном месте. Уже сейчас просматривается потребность в двух классах функций:
    1. Для cx-starter & Co. -- раздельное уставление alarm (CDR_FLAG_ALARM_MASK), color (CDR_FLAG_COLOR_MASK), syserr (CDR_FLAG_SYSERR_MASK).
    2. Для клиентов типа ipp (и для будущих bigc-plugins).

    31.01.2006: впрочем -- спешить с этим не будем, как раз и подождем до следующего клиента "типа ipp" (видимо, это будет ausacc).

    08.06.2006: потребность-то вылазит, но не с тем, про кого думали, а с linbpm'ом.

    Идея же -- вообще-то, очевидная (уже с полмесяца) -- заключается в следующем.

    1. Для "ipp & Co.", т.е. для knob-plugin'ов от ЦВЕТОВ толку мало -- они оперируют GC'ами. Потому для них нужно "опубликовать" интерфейс ChooseColorState() -- выбор состояния на основе флагов.

      А они при инициализации составляют таблицу из GC, индексируемую по colstate'ам. И при рисовании просто выбирают из таблицы надлежащий GC. 04.08.2006: сделано.

    16.06.2006: "опубликовал" ChooseColorState(), переименовав его в CdrChooseColorState(). И параметр Knob k теперь имеет право быть NULL -- тогда на тему "attn" просто не проверяется.

    16.07.2006: мдя, вот только при k==NULL не работает не только OTHEROP, но и RELAX! (А он-то для вакуума очень даже нужен.)

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

    1. Непотребная взаимозависимость между флагами (rflags) и состоянием (colstate). Хоть и конечная, но все же это плохо.
    2. Флагу CDR_FLAG_ALARM_ALARM не соответствует никакое состояние -- это как бы "нормально".

      Это, конечно, имеет свою причину: сей флаг как бы ортогонален/параллелен обычной колоризации, и отображается он отдельно -- ЗНАЧЕНИЕМ лампочки либо цветом КНОПКИ в cx-starter'е (а не лампочками состояния), но все же -- кривовато.

    Вывод: надо разработать СТРОЙНУЮ схему флагов и колоризации для использования в CXv4.

  • 05.07.2008: надо ввести еще один тип колоризации -- LOGC_HEADING, для меток, чтоб был "инверсный", белый на vga-синем (навеяно "заголовками разделов" в окошке RightMark CPU utility под Форточками).

    05.07.2008: угу, сделано:

    1. cxdata.h: добавлена LOGC_HEADING.
    2. Xh.h: добавлены XH_COLOR_FG_HEADING и XH_COLOR_BG_HEADING.
    3. Xh_colors.c: вставлены соответствующие строчки (синий фон #1818B2 взят из моей версии fte045/src/con_x11.cpp).
    4. Knobs_internals.c::ChooseKnobColors(): вставлен case LOGC_HEADING в селектор по "расцвечиваниям".

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

    04.03.2009: оказалось, что тогда я забыл Xh-related-часть в 4cx/ портировать -- сейчас сделал (в выборе цветов, в MotifKnobs_ChooseColors(), естественно, кусок "color/zzzzzz" пока пустой, так что туда портировать некуда :-)).

  • 29.10.2008: захотелось, по аналогии с Qt, сделать, чтобы колесом мыши значение в текстовом поле менялось, как стрелками.

    29.10.2008: да, сделал -- все это в Knobs_internals.c.

    • Общая идея -- на input-поля вешается дополнительный ButtonPressMask-обработчик, TextWheelHandler(), реагирующий на Button4 (вверх) и Button5 (вниз).
    • А собственно "реализация" отработки вверх/вниз для этого теперь вынесена из KeyPressMask-обработчика в отдельную функцию, HandleTextUpDown().
    • Заодно KbdHandler() переименован в KnobsKbdHandler() -- консистентности ради, а TextUpDownHandler() -- в TextKbdHandler(), корректности для.

    Проверил -- работает. Осталось это портировать и в 4cx/.

    30.10.2008: да, портировал. Кстати, в 4cx/ УЖЕ использовались имена TextKbdHandler() и MotifKnobs_CommonKbdHandler().

  • 06.03.2009: чтобы не плодить в разных модулях lookup-таблицы цветов, сделал одну общую -- 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, расширившийся и упорядочившийся сегодня.

Общие вопросы:
  • 03.08.2006: давно уже ясно, что надобно иметь некую публичную функцию, которая бы заполняла ту самую таблицу GC'ов, индексируемую по colstate'ам. Но: вот просто так иметь ее прототип в Knobs.h -- нельзя, поскольку там участвует тип 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-плагинах.

    Кроме того, всем теперь стали доступны ParseWiddepinfo()Chl_canvas.c был собственный экземпляр) и CNCRTZE()/ABSTRZE(). С последними есть фишка -- бедный Chl_canvas.c включает ОБА файла с их определением -- Chl_internals.h и KnobsI.h. Но это не есть проблема, препроцессор переопределения допускает :-)

    Короче -- "done".

    16.08.2006: писец -- там параметр "w" передавался как Widget, вместо долженствующего XhWidget. Исправил (хотя дело и мутное).

    07.03.2007: имелся за-а-амечательный баг -- там использовался цикл до countof(table), при том, что table -- это параметр, объявленный как GC table[]; в результате table[COLALREM_JUSTCREATED] пропускалось. Огрехи переноса, блин!!! Пофиксил.

  • 29.03.2007: опубликовал в KnobsI.h функцию-обработчик правлй кнопки мыши, переименовав ее в KnobsMouse3Handler() (чтобы всякие Chl'евские и прочие element-plugin'ы могли навешивать его на метки строк/колонок).
  • 04.06.2007: обнаружилось, что почему-то сразу после нажатия кнопки (LOGD_BUTTON и LOGD_ARROW_*) почему-то загораются. А это ведь ни к чему -- им надо просто команду исполнить.

    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 занят как раз ЭТИМ аспектом.

  • 16.05.2009: замечание: надо стараться ВСЕГДА сразу после CreateSimpleKnob() делать ему SetKnobValue(,CURRENT_KNOWN_VALUE).

    В первую очередь это касается декоративно-управленческих ручек (типа чекбоксов включения видимости всяких фич). Иначе -- чекбоксики-то теперь создаются в состоянии INDETERMINATE, а поскольку меняются только юзером (а не программно), то и выглядеть будут уродсковато (вылезло на чекбоксах отключения реперов в nadc333).

    (Да, думал вставить SetKnobValue(,0) сразу в CreateSimpleKnob(), но это неправильно -- тогда не будут видны огрехи в программах.)

  • 09.12.2010@Снежинск-каземат-11: стандартизация парсинга widdepinfo/options.

    09.12.2010@Снежинск-каземат-11:

    • Поскольку МНОГО мест используют триплет ('=',"ПРОБЕЛ\t,,"") в качестве (equals_c,separators,terminators), то оные были декларированы в Knobs_I.h как (Knobs_wdi_equals_c,Knobs_wdi_separators,Knobs_wdi_terminators) и заполнены в Knobs_internals.c.

      ЗАМЕЧАНИЕ: Knobs_simple.c использует ДРУГИЕ сепараторы.

    • Аналогично в 4cx/ -- объявлены в KnobsP.h, реализованы в свежезаведённом KnobsCore_misc.c. Поскольку парсинг опций там стандартизован, то коснулось изменение всего нескольких файлов -- KnobsCore_knobset.c плюс парсинга layinfo в MotifKnobs_{grid_cont,lrtb_grpg}.c.
    • Возможно, драйверы тоже напрашиваются на нечто подобное! (Только в CXv2.)

    01.02.2011@Снежинск-каземат-11: там в .c-файлах оно было объявлено как extern, на что gcc ругался в стиле

    warning: 'Knobs_wdi_equals_c' initialized and declared 'extern'
    Так что extern убран.
  • 01.02.2011@Снежинск-каземат-11: поскольку у нас уже МНОГО knob/elem-plugin'ов с кнопками (button, subwin, scenario), а теперь еще и PSP_T_INCLUDE появилось, то пора уже вместо копирования функционала декорирования кнопчатостей вытащить его в общедоступный API.

    01.02.2011@Снежинск-каземат-11: делаем.

    • Структура "опции кнопок" -- Knobs_buttonopts_t.
    • Указатель на PSP-таблицу для неё возвращает _Knobs_GetStd_text2buttonopts()
    • В дополнение к Knobs_colors_lkp[] также сделано стандартный Knobs_sizes_lkp[].
    • "Настройка" внешнего вида -- TuneButtonKnob().
      • Чтоб НЕ пыталось вставить пиктограмму, можно указывать флаг TUNE_BUTTON_KNOB_F_NO_PIXMAP.
      • Чтоб НЕ пыталось махинировать со шрифтом -- флаг TUNE_BUTTON_KNOB_F_NO_FONT (т.е., чтоб химичило только с цветом фона).

    Переведённая на этот функционал клиентура:

    1. Chl_scenario.c.
    2. Chl_subwin.c.
    3. Knobs_button_widget.c; но ARROW_Create() уставляет размер самостоятельно -- поскольку там нет никакого fontList; (а раньше оно, оказывается, bg не использовало).
    4. Knobs_alarmonoffled_widget.c -- Knobs_sizes_lkp[].
    5. А вот Chl_canvas.c, который вроде напрашивался на использование Knobs_sizes_lkp[], переделывать не стал -- уж шибко там всё по-другому.

    Но в сумме, конечно, видно, что всё это -- лишь жалкая пародия на надлежащую реализацию стилей.

    18.04.2011@Снежинск-каземат-11: заменил _Knobs_GetStd_text2buttonopts[] на text2Knobs_buttonopts[].

    15.12.2011@Снежинск-каземат-11: к Knobs_sizes_lkp[] добавлена альтернатива "tiny" - 8.

simple:
  • 31.01.2005: насчет simple-интерфейса: сегодня пришел Чугуев и пожаловался, что если щелкнуть мышью в некоем текстовом поле, а потом убрать с него фокус, то значение сбрасывается в 0.

    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.

  • 23.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".

  • 25.04.2005: а вообще-то -- пора бы иметь ОБОБЩЕННЫЙ "интерфейс манипуляции значениями", который бы использовался и в Cdr, и в Knobs (пресловутый SetKnobValue(), который сейчас существует в ДВУХ идентичных экземплярах), и в Knobs_simple, и где еще понадобится в будущем.

    И уж в этом-то интерфейсе будет зашита вся умность типа wasjustset.

    06.02.2006: на эту тему -- которая вообще-то шире -- все "обсуждение" идет в разделе начатом 17-05-2005.

    06.08.2006: так что этот пункт с чистой совестью маркируем "obsolete".

  • 29.10.2012: расширяем API CreateSimpleKnob() -- добавлен параметр options (сразу после spec), и можно указывать флаг SIMPLEKNOB_OPT_READONLY, приводящий к принудительному "ro".

    Смысл -- возможность централизованно форсить readonly (нужно для fastadc/pzframe).

selector:
  • 31.05.2004: SELECTOR-WIDGET -- было обнаружено и исправлено несколько приколов.

    31.05.2004: итого:

    1. При widdepinfo==NULL оно падало по SIGSEGV. Вставлена проверка и присвоение пустой строки.
    2. Отсутствовала инициализация list_count -- тут warning был правильным. При widdepinfo=="" оно содержало бредовое значение. Вставлено =0.
    3. Никак не поддерживался режим readonly (is_rw==0). Сделал аналогично alarmonoffled -- в ответ на действие юзера оно само делает SetValue_m(ki,ki->curv).
    4. Поле ki->label не использовалось -- метка бралась только из widdepinfo (атрибут "#L"). Это явный рудимент времен, когда widdepinfo еще не существовало и вместо него использовалась label. Поправлено -- при label!=NULL она копируется во внутренний буфер, в который потом складируется содержимое "#L" (т.о., последний имеет приоритет).
    5. Заодно подправил определение ActivateCB(), чтобы оно не давало warning'а.
  • 22.11.2004: а как насчет сделать в LOGD_SELECTOR, по аналогии с LOGD_SLIDER, возможность ставить селектор ПОД меткой, а не справа? Сам-то Motif такое поддерживает...

    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: а не сделать ли, чтобы у селектора уставлялся behaviour-флаг "right justify", чтобы все селекторы были друг под дружкой?

    13.01.2005: ну сделал, а что толку? :-) Работает, но не слишком-то это красиво -- т.е., не всегда стОит делать такую уставку. По-хорошему, вопросы расположения ручек должны решаться другим механизмом -- ВНЕ компетенции самих ручек, уровнем выше.

  • 17.03.2005: @KEK: Readonly-widget: "text collection" -- аналог селектора, отображающий "ready", "working", ... (Навеяно какой-то ихней программой, умеющей рядом с каналом (или просто его именем?) писать эти строчки "состояния".)

    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".

  • 06.05.2005: надо в будущем CXv4, после приведения к нормальному psp-интерфейсу, не забыть ввести ключи "nolabel"/"withlabel" -- что сейчас достигается параметром #L.

    14.07.2007: ввел.

  • 20.06.2006: обнаружилась какая-то дичь: в rfsyn'е при указании ключа "#C" (compact) почему-то смещается диапазон чисел...

    21.06.2006: разобрался -- все очень просто: безпараметровые кдючи ТОЖЕ требуют завершающего '\v'. Глупо, но факт -- в противном случае они просто "съедают" все до этого самого '\v'.

    Чтобы работали оба варианта, вставил в код всех таких опций строчку

    if (*p == '#') strend_p = p - 1;

    Все рудименты, рудименты, блин!

  • 02.01.2007: заметил забавную весчь: в режиме readonly хоть "хреновинка" и не отображается (поскольку shadowThickness:=0), но место под нее по-прежнему зарезервировано, и выглядит это странновато. Как бы этак от хреновинки избавляться -- каким ресурсом?

    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'а, похоже, проще забить на проблему...

  • 11.05.2007: захотелось (Лебединскому) иметь некоторые из пунктов селектора невыбираемыми (insensitive). Т.е. -- если мы знаем, что теоретически эти значения (тут -- камеры кольца) предусмотрены, но пока не сделаны, то чтобы юзер не мог их выбрать.

    12.05.2007: в общем ("общечеловеческом"? "общекорректном"? ...?) случае попытка решить эту проблему открыла бы банку с пауками (can of worms): ведь в идеале надо уметь динамически (at run-time) указывать, что такой-то пункт надо задисэйблить/раздисэйблить. Как это делать -- не то что в нынешнем протоколе, а даже в cx-proto/v4 -- фиг знает.

    Но сейчас-то легко можно сделать простенький "хак", который отлично решит имеющуюся проблему: ввести некий префикс "текста пункта", который будет указывать, что этот пункт надо сделать insensitive. Самое тривиальное -- в качестве такого префикса использовать '\n'.

    Так и сделал -- работы было минут на пять. "done".

  • 26.03.2008: а не сделать ли возможность кроме "текстовых" пунктов делать и картиночные? Т.е., чтобы брался какой-нибудь .xpm, и делалось бы что-то типа "AssignPixmap".

    Например, чтобы картинка бралась, если "метка" имеет вид "=#!=ИМЯ_КАРТИНКИ" (опциональный '\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'ом.

    Осталось несколько вещей:

    1. ???
    2. Добавить ПОИСК пиктограмм, при помощи findfilein() -- чтобы оно брало не только в текущей директории, но и в директории, откуда программа запущена, и в ~/pult/icons/ (каковую надо еще ввести).
    3. Портировать в 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. Для этого:

    1. Завел директорию lib/icons/, с добавлением ее в EXPORTSTREE (так что -- будет pult/lib/icons/).
    2. Создал 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: сделал поддержку разноцветных пунктов.

    13.05.2009: база -- добыча и парсинг этих style-strings -- скопирована из choicebs. А вот РАСЦВЕЧИВАНИЕ -- сделано "вручную":

    • В priveterec'е заведены еще два поля: 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() вызывается колоризация, так что сразу будет стоять нужный цвет.

slider:
  • 07.02.2004: надо б изготовить виджеты "LOGD_HSLIDER" и "LOGD_VSLIDER". Плюс, естественно, ввести такие константы.

    Это нужно для sukhphase -- чтобы полностью перевести его на libKnobs.

    Встает только вопрос -- размер будем делать фиксированным (как у Dial), или позволим указывать в widdepinfo?

    09.02.2004: обнаружилась неиспользуемая с давних времен константа LOGD_SCALE.

    Как бы то ни было, она оставлена на месте, а добавлены константы LOGD_HSLIDER и LOGD_VSLIDER.

    23.02.2004: начал делать -- изготовил Knobs_slider_widget.[ch] и зарегистрировал его в Knobs_widgetset.c.

    Оно пока практически ничего не умеет -- только создает слайдер. Надо еще думать о:

    • Внешнем виде -- пока очень средненько.
    • Способе корректного отображения числа в лэйбле (чтобы не оно само, а уже пересчитанное в соответствии с min/max.
    • Установке лимитов/шага. Прослеживаются как минимум два подвида: вещественные числа от min до max, с шагом step, и целые -- аналог селектора, для диапазонов типа 0..255.

    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:

    • Отображение сделано по вышеуказанному проекту -- вычитывается precision из dpyfmt, ставится в XmNdecimalPoints, плюс мухлюется в 10^precision раз при передаче значения виджету и при получении от него.
    • Почему-то пришлось в SetValue_m() вставлять round() перед преобразованием к целым -- иначе оно как-то умудрялось округлить 3.0{0-в-периоде} до 2 (причем, 3.0 получалось как 0.3*10, а 0.3 бралось из виджетового 3/10.0).
    • Вытаскивание dpyinfo "доверительное" -- если не-NULL и не-"", то ищем '.' и делаем 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:

    • Он использует psp. Есть 4 параметра:
      size
      длина scrollbar'а.
      type
      scale|scrollbar|thermometer -- внешний вид.
      valdpy
      input|value|novalue -- поле ввода, поле отображения, никакого поля.
      layout
      компоновка компонентов (пока толком не реализована).
    • Все махинации с созданием, поддержкой (стрелки, [Enter] и прочие callback'и и event handler'ы) и обновлением выпихнуты из Knobs_text_widget.c в Knobs_internals.c. Так что и DIAL, и SLIDER переведены на этот интерфейс.
    • На тему "при наличии текстового поля ввода делать scrollbar/dial не-traversable" -- решил не заморачиваться, и оставил эти вещи независимыми.

    21.09.2004: примерно доделал "ввод и отображение". Итак:

    • По аналогии со вариантом XmScale мы узнаем количество десятичных знаков и домножаем все значения для передачи виджету на 10^decimals, в т.ч. min, max и step/XmNincrement.
    • Шаг (XmNincrement) также прекрасно реализовался.
    • Поскольку у XmScrollBar, в отличие от XmScale, проблемы с отображением min!=0, то мы сдвигаем для виджета диапазон так, чтобы он всегда начинался с 0. И, соответственно, в SetValue_m() и ChangeCB делается сдвиг.

      (BTW, как показало разбирательство в lib/Xm/Scale.c, он ВСЕГДА ставит своему scrollbar'у диапазон 0-1000000000/*SCROLLBAR_MAX*/ и сам маппирует на него то, что указано в [XmNminimum,XmNmaximum].)

    19.11.2004: вносим некоторые изменения, обусловленные, в основном, отдельным цветом для нажимабельностей:

    • Теперь контейнер ВСЕГДА создается с именем "slider", без деления на "hslider" и "vslider". И ему теперь принудительно, а не через fallback'и.
    • Явно напросился отдельный выбор цветов для scrollbar'а -- а то ему ставился background формы-контейнера. Сделан.

      Плюс, теперь уставляются цвета и контейнеру -- чтобы оно краснело "все целиком". Забавно, что при этом остаются тени (?) у поля "value".

    • Оно вякало на тему "The specified scrollbar value is greater than the maximum scrollbar value minus the scrollbar slider size.".

    21.11.2004: стал разбираться с вяканьем -- понял, что я, мягко говоря, очень вольно обращаюсь с понятием "XmNsliderSize" -- всегда ставлю его в 30, вне зависимости от масштаба (sf), диапазона и т.д. -- короче, я неправильно его понял. (Собственно, в man'е четко написано, что "XmNvalue: Specifies the slider's position, between XmNminimum and (XmNmaximum-XmNsliderSize)".)

    (Вообще-то это скорее ляп именно 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: еще ляп: при создании бегунок ставился в начальное положение, хотя оно могло быть вовсе не нулем (при min_disp!=0).

    22.11.2004: Это обеспечивалось устаревшим начальным значением XmNvalue -- min_i(ki), которое было раньше, с XmScale. А теперь нужно было ставить -ofs_i(ki), что и было сделано.

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

    Вставил "принуждение" взамен былого безусловного -ofs_i(ki).

  • 17.01.2005: надо б иметь еще один подвид layout'а -- чтобы значение было СЛЕВА от слайдера (нужно для термостабилизации, где три числа идут подряд, а уж справа пусть будет температура).

    17.01.2005: ну что, назовем его "rtl"?

    Парой часов позже: да, так и сделал -- это ключик "rtl". Порядок такой: [label][value][scrollbar] (так что метка и значение идут словно одной строкой (хотя интервал между ними и есть)).

    А мудрая функция, которая расставляет подвиджеты, теперь устроена так: вначале виджеты забиваются в массив row[] в надлежащем порядке (в зависимости от opts.layout), а затем содержимое этого массива расставляется слева направо.

  • 17.01.2005: стоит у readonly-термометров совсем убирать "slider mark" -- оно там совсем ни к чему, только мешает.

    17.01.2005: готово -- делов-то...

  • 17.01.2005: и потребовался параметр "nolabel", чтобы даже у виджетов, имеющих "для наружи" метку, она б не рисовалась.

    17.01.2005: ну сделал. А противоположный, дефолтный параметр, назвал "=", чтоб его указать никак нельзя было.

    18.04.2005: переименовал "=" в "withlabel" -- для унификации с LOGD_TEXT*.

  • 11.04.2007: сходу -- переделал "compact" для HSLIDER'ов на более разумную: в первой строчке друг за другом label и value, а под ними -- slider. Это, во-первых, более компактно, а во-вторых -- полностью аналогично раскладке VSLIDER'а. Побудительным мотивом стал новый вариант tsycam'а.
button:
  • 10.05.2004: возникла (будущая) потребность иметь виджет "кнопка-стрелка". Это понадобится для вещей типа УШД.

    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".

  • 23.08.2004: обнаружил, что и LOGD_BUTTON, и LOGD_ARROW_* игнорируют флаг 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: и, кстати: надо б вставить в LOGD_ARROW_* (а в идеале -- и в LOGD_BUTTON) поддержку "лампочности" -- чтобы при маске 1 (или как мы ее назовем) они б краснели.

    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 и отображаемое значение синхронизовывались.)

  • 16.04.2005: а как бы это уметь указывать, чтобы кнопка была БЕЗ наружной тени -- с тенью только самой стрелки? Это требуется, например, когда оно используется как readonly-widget, например, для показа состояния концевиков.

    Мммм... А ведь по крайней мере при !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 не проблема.

  • 06.06.2005: хочется уметь управлять "цветом подсветки" -- сейчас-то зашит XH_COLOR_JUST_RED, а хочется -- мочь указывать, чтоб оно бывало и зеленым, и оранжевым, и синим.

    Что -- делаем psp-параметры-флаги "red" (default), "green", "amber", "blue"?

    И, кстати, можно на такую же идеологию перевести alarmonoffled -- чтобы НЕ БЫЛО отдельных типов led'ов для разных цветов, а указывался бы цвет (для alarm'ов это, конечно, малонужно :-).

    28.05.2006: да, для alarmonoffled'а сделано. Ежели чего -- можно скопировать оттуда.

    27.01.2007: да, скопировал. Понадобилось для управления системой выпуска пучка в linmag'е -- чтоб стрелки, когда мотор едет в соответствующем направлении, зеленели.

  • 28.04.2007: сходу -- добавил в behaviour уставку KNOB_B_INCDECSTEP_FXD (и в button, и в arrow).
  • 11.12.2007: возникло желание уметь делать большие и разноцветные кнопки -- как предусмотрено (полем "style") в 4-й версии. Понадобилось оное для программы beamctl -- "малютинизатор пучка".

    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 (с текстом вообще не факт что покатит, русского шрифта такого размера я не делал).

  • 17.11.2009: надо б мочь и УКАЗЫВАТЬ кнопкострелкам LOGD_ARROW_* желаемый размер -- чтоб оно не только подстраивалось под аналогичные кнопки, но и можно б было делать БОЛЬШИЕ стрелки.

    P.S. И, кстати, а на стрелки-то указание параметра size, работающего с обычными кнопками, НЕ влияет -- делалось уже позже. Хотя это б нас наполовину спасло.

    17.11.2010: да, сделано -- полностью аналогично LOGD_BUTTON'у. Просто при указании size оно само создаёт XFontStruct/XFontList, и пере-добывает размеры уже с него.

alarmonoffled:
  • 01.06.2004: ALARM-ONOFF-LED -- мелкое усовершенствование, сделанное под потребности ipp.c нового поколения (который целиком основан на Knobs).

    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":

    • Во-первых, оно без индикатора делается другого размера (там ставятся другие вертикальные margin'ы) и оно по baseline не выравнивается с остальными.
    • Во-вторых, и это известно давно, какие-то странности при множественных щелчках на readonly-XmToggleButton'ах (просто с alarm+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 есть в дистрибутиве.

  • 22.09.2004: сделано сходу: теперь все обитатели Knobs_alarmonoffled_widget.c (т.е. -- разные подвиды чекбоксов) используют протокол
    "&1 -- подсвечиваться, &2 -- disable'иться"
    вместо былого
    "!=0 -- подсвечиваться"

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

  • 22.09.2004: обнаружил, что у нас нету LOGD_BLUELED...

    С другой стороны, ненужный и давно забытый LOGD_SCALE идет как раз за блоком "LOGD_*LED". Ну что -- займем этот код под LOGD_BLUELED?

    22.09.2004: так и сделал.

    26.09.2004: кстати, доописал его в doc/lib/Knobs.html.

  • 27.09.2004: заметил, что 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".

  • 29.09.2004: пришла в голову мысля, как можно решить давнюю проблему с XmToggleButton'ными виджетами (т.е., с обитателями Knobs_alarmonoffled_widget.c), что при пустом label'е "индикатор" имеет совсем мелкий размер:

    А что, если в таком случае ставить при создании XmNlabel="пробел", затем вычитывать XmNindicatorSize, а потом ставить метку в ""? Судя по man'у, после первого же прямого указания XmNindicatorSize он перестает вычисляться автоматически на основе метки.

    29.09.2004: попробовал. Работает. Хотя и несколько не совсем так, как следовало бы.

    "Не совсем так" -- из-за того, что если уставлять XmNindicatorSize в то же значение, что уже есть, то ничего не происходит. Это потому, что замечается не наличие его в списке параметров, а именно ИЗМЕНЕНИЕ -- в ToggleB.c::SetValues() стоит проверка

    if (newcbox->toggle.indicator_dim != curcbox->toggle.indicator_dim)
    {
            newcbox->toggle.indicator_set = TRUE;
    }
    
    Так уж устроены Xt/Motif. С аналогичным приколом уже приходилось сталкиваться и мне -- в инспирированных Гусевым разборках с XmForm на тему изменения размеров окна и включения/отключения графиков (см "Chl:05.05.2004"), и Антонову -- что для дерганья geometry manager'а приходилось щелкать borderWidth'ом.

    Я решил эту проблему "рекомендованным" же способом -- перед общим большим модифицирующим 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().

    Итого -- теперь все в полном ажуре.

  • 23.04.2005: захотелось иметь еще один alarm/onoff/led-like виджет -- который бы можно было использовать для "самодельных" radiobox'ов. Естественно, это имеет смысл только в качестве simple-knob'а, чтобы программа сама управляла его состоянием.

    (Понадобилось -- в 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 он создавал не круглым, а ромбовидным.

  • 28.02.2006: некоторые пуристические модификации "интерфейса" между double-числами и UI:
    1. Вместо неявно подразумеваемого, что невключенный XmNset равен 0, а включенный -- 1, вставлены явные проверки --- в SetValue_m() что XmNset:=is_set?XmSET:XmUNSET, а в ChangeCB -- v:=info->set==XmUNSET?0:CX_VALUE_LIT_MASK.
    2. И с точки зрения "протоколо-корректности", теперь уставленное значение не магическое число 1, а CX_VALUE_LIT_MASK.
  • 28.05.2006: еще давно было желание уметь указывать конкретный цвет для led'ов и "radiobutton'ов".

    А сегодня прикинул: если делать замену для олеговых "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 :-).

  • 10.06.2006: пришлось предпринять меры против memory leak'а, случайно, косвенно вызываемого методом 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: реализовано "alarm acknowledgement" по проекту "AckAlarm".

    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: узрел, что ключ "color=" работает только с лампочками, а на ONOFF он не распространяется -- там явное условие стоит. Неудобно!!!

    31.07.2006: сделал, что теперь ЛЮБОЙ led/onoff-подобный понимает указание цвета. Техника -- оно выбирает не цвет, а его селектор (XH_COLOR_nnn), ставя для ONOFF'а по умолчанию -1, и уставляет XmNselectColor:=XhGetColor(selcol) только при selcol>=0.

  • 28.08.2006: хоцца иметь в дополнение к обычным onoff'ам еще и "тики" -- т.е., "галочки" -- которые, например, при XmNindicatorOn==XmINDICATOR_CHECK_BOX. Полезно для включателей индивидуальных каналов в adc200.

    28.08.2006: а надо ли? Реально -- эти хреновинки всегда рисуются цветом foreground'а...

    И, в любом случае, если делать -- то, наверное, дополнительными widdepinfo-ключиками, а не отдельным типом отображатора. Тогда можно и всякие CROSS поддерживать...

    11.09.2006: да, нафиг реально не надо. И не только из-за принудительного использования цвета foreground'а, а еще и из-за того, что и просто обычное указание цвета (в widdepinfo-параметре "color") имеет тот же самый эффект -- все равно цветные маркеры отображаются ТОЛЬКО во включенном состоянии.

  • 25.02.2007: захотелось иметь возможность делать так, чтобы некая другая ручка enable'илась/disable'илась в зависимости от состояния выключателя. (Конкретно -- чтобы поле ввода задержки некоего канала GZI11 меняло свой вид в зависимости от разрешенности/запрещенности данного канала.)

    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 теперь стало окончательно излишним :-). Хотя разнообразие, вносимое во внешний вид, вполне оправдывает его использование.

  • 28.04.2007: сходу -- добавил в behaviour уставку KNOB_B_IS_LIGHT|KNOB_B_INCDECSTEP_FXD. Там даже B_IS_LIGHT не было!!! (С другой стороны -- отдельный вопрос: а ЧТО такое B_IS_LIGHT, и зачем он нужен? В bigfile'ах на эту тему ничего внятно не сказано...)
  • 08.05.2009: надо завести еще один подвид LED'а -- лампочку переменного цвета. Чтобы, в выключенном состоянии (0) она была одного цвета, а во включенном (1) -- другого; например, красная и зеленая, или наоборот.

    08.05.2009: как вариант -- можно ввести параметр "offcol", который бы влиял на XmNunselectColor. Отдельный интересный вопрос -- а как это будет работать в варианте с panel?

    Получасом позже: да, сделал, совершенно тривиально. Работает. И с panel проверил -- тоже работает как надо, расцвечивается нужным образом (правда, состояние INDETERMINATE игнорирует -- воспринимает как UNSET, но это неважно).

    И в 4cx/ too. Засим -- "done".

  • 08.05.2009: а надо бы уметь и LED-ручки делать КРУГЛОЙ формы. Чтобы были на вид как alarm-лампочки, но НЕбибикающие. Будет очень полезно для рядов лампочек сигнализации/блокировки, например, в weldcc.

    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'е

  • 13.07.2009: надо как-то расшивать/обобщать ALARM'овость: чтобы ЛЮБОЙ LED мог вызывать бибиканье/моргание-красным, и отдельно -- чтобы мог зеленеть по пропаданию alarm'а.

    А то сейчас на сварке актуально зеленение по пропаданию блокировок, но блокировки эти сделаны НЕ LOGD_ALARM'ами, а обычными LOGD_GREENLED с "offcol=red,shape=circle" (и там вообще alarm -- при ==0, а не ==1).

    Вопрос причем очень глубокий, касается НЕ только alarmonoffled'а, а скорее Cdr'а, и в CXv4 тоже должно быть сделано в этой же струе.

    25.07.2009: решение очевидно: надо делать ДВА РАЗНЫХ behaviour-бита:

    1. B_ALARM_BEEPER приводит к бибиканью/морганию контейнера при появлении маски 1.
    2. B_ALARM_RELAXER -- к позеленению самой ручки при исчезновении маски 1.
    (И, по идее, наличие BEEPER должно влечь и наличие RELAXER.)

    В CXv4 -- практически то же самое, с поправкой на тамошнюю более streamlined/straightforward архитектуру.

    P.S. А то, что на сварке alarm при ==0 -- ну надо приводить к стандартному, при помощи phys{r=-1,d=-1}.

  • 21.08.2009: возникло желание иметь такие хитрые лампочки, которые бы горели в течение некоторого времени -- даже если в них уже пришел 0. Нужно это для имитации однократно взмаргивающих светодиодов.

    25.08.2009: авотхрен -- там privrec'а-то нету...

    25.08.2009: privrec-то, конечно, там надо бы завести.

    Но вот как именно реализовывать это "залипание" лампочек -- отдельный вопрос. Есть куча возможных сценариев. И, с другой стороны -- а насколько это РЕАЛЬНО надо?

    По-хорошему -- надо бы реализовать предыдущий пункт, с отдельным RELAXER'ом, и тогда его будет как раз вполне достаточно.

  • 17.11.2010: сходу -- сделал возможность указывать размер шрифта и bold/medium, скопировав это из button'а.

    (Понадобилось для liucc -- чтоб сделать там мелкие panel'ы статуса БЗ1 и БЗ2)

    16.05.2012@Снежинск-каземат-11: мелкий хак в продолжение -- делаем, чтобы для размеров (size) меньше normal тень лампочки становилась вдвое меньше.

    Халтурновато, конечно -- во-первых, вообще что отсюда лезем в компетенцию Xh_fallbacks.h, а во-вторых, как делается проверка: что "size<=10". Уполовинивание делается только при толщине>1.

  • 11.12.2010@Снежинск-каземат-11: понадобилось уметь делать "микролампочки", состоящие только из индикатора, БЕЗ полей (это в liucc -- чтоб прямо поверх кнопки модулятора были пара "светодиодиков", показывающих состояние включенности накалов).

    11.12.2010@Снежинск-каземат-11: сделал:

    • указывается -- доп.параметром "nomargins", и отрабатывается ТОЛЬКО при no_label.
    • технология -- оно нулит все поля (*margin*), оставляя только XmNmarginLeft и XmNmarginTop, делая их =isize-2. Также делается XmNhighlightThickness=0.

    Недостатки/разборки на будущее:

    1. Способ указания -- кривоватый. Во-первых, и сам термин "nomargins" так себе (взять "led"? красиво, но термин уже задействован). Во-вторых, не лучше ли иметь ОДИН параметр "внешний вид", который мог бы быть "normal", "panel", "led"?
    2. То, КАК "убираются" лишние поля -- отдельная песня. Нынешний подход подобран методом тыка. Как именно оно там вписывает индикатор в имеющееся пространство -- большая загадка; оно слегка съезжает (на 1px) от казалось бы правильного места, и "-2" -- паллиатив, чтоб не резалась тень, а так бы -- "-3" (на больших размерах -- "-4"). А так тонкая полоска "не того" фона на краях остаётся. Во-первых, надо б поглядеть в ToggleB.c (какие поля СТОИТ трогать и как), а во-вторых в разных версиях Motif'а это может разниться.

    И уж в 4cx/ оно НЕ идёт -- пока не будет причёсано до неотвратного вида.

text:
  • 14.09.2004: озаботился, что у LOGD_TEXT виджет [v\^] не подвергается колоризации.

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

    Второе побуждение -- ввел в text_privaterec_t еще пару полей -- incdec_def{fg,bg}, и с ними производятся махинации аналогично обычным wdci.{deffg,defbg}.

    Помогло -- и в widgettest'е даже стрелочки из черных стали голубенькими.

  • 11.04.2005: обнаружилось, что в DoCreate() после XtMalloc() не делалось bzero(). Из-за этого стало валиться в core после введения поля grpmrk. Поправил.

    11.04.2005: А по-хорошему надо б сделать аудит ВСЕХ мест, где вызывается XtMalloc().

    Чуть позже: да, навел аудит. Везде все окей -- Knobs_text_widget.c был единственным проблемным местом. (Есть еще пара мест, где результат используется под строку, а не под структуру, но и там сразу же делается str*cpy(), так что все корректно.)

  • 16.04.2005: хочется, для полноты картины, уметь прилеплять к LOGD_TEXT* свою собственную метку.

    Естественно, не безусловным порядком визуализировать 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: о-ей... Еще с самых древних времен "поведенческие" ресурсы текстовых полей (editable, editMode, traversalOn, etc.) имелись лишь в fallback'ах (Xh_fallbacks.h).

    11.05.2005: в конце концов было провернуто то, что давным-давно напрашивалось -- теперь CreateTextValue() и CreateTextInput() не сами создают виджет, а вызывают единую функцию MakeTextWidget(), каковая его и создает унифицированным образом, в зависимости от параметра rw лишь меняя кое-какие параметры.

    Именно в нее и переехали все "проблемные" определения из Xh_fallbacks.h.

    Ага, и вылезло в других программах -- istcc.c, phm-tsyline.c -- курсоры там видимы, и поля traversable... Впрочем, это уж их проблема, и в них должна исправляться, что и сделано.

  • 22.05.2005: сходу -- сделал, что правая кнопка мыши перехватывается теперь также и grpmrk'ом, и меткой.
  • 10.02.2006: а почему у нас сама форма-контейнер не колоризуется?

    10.02.2006: да-да, и метку расцвечивать тоже забыли!!!

    23.05.2006: сделал -- теперь форма колоризуется. Заодно нашел баг, что полю form_w() никогда не уставлялось значение (из-за чего при расцвечивании получали SIGSEGV) -- поскольку в DoCreate() имелась собственная переменная container, на которую только и делались ссылки.

    И метка также колоризуется.

    Внешний вид теперь стал более целостным -- ручка смотрится как единое целое. "Done".

    P.S. А почему форма не колоризовалась -- ответ очень простой и касается эстетики: текстовое поле в колоризованном варианте теперь имеет однопиксельную обводку, что выглядит менее "красиво", чем когда оно, со своими 3D-тенями, словно "висело" само.

  • 08.06.2006: выспросил-таки Столкистенку, в чем именно приятность гусиных программ. Он долго мялся, и в конце концов сказал, что у Гусева можно жать в текстовых полях Ctrl+Z -- тогда там возвращается предыдущее значение (одно).

    Ну что, сделаем? Это ж тривиально!

    08.06.2006: типа проект.

    • Это именно исключительно для текстовых полей, а потому тут никак не задевается SetControlValue(), и потому загрузка режима -- не влияет.
    • Заводим FIFO на ДВА числа.
    • В TextChangeCB() в дополнение к SetControlValue() "вдвигаем" число в эту FIFO.
    • В TextUpDownHandler() ловим также Ctrl+Z и Alt+Backspace, и при их появлении делаем "Undo".
    • "Undo" же заключается в том, что берем число из конца FIFO, и... Либо -- 1) просто уставляем его в поле, НЕ отсылая в сервер; либо -- 2) и отсылаем в сервер тоже.
    • Вопрос 1 (простой): а что делать, если в очереди еще либо одно число, либо вообще 0? Ну 0-то -- ничего, или CancelInputEditing(). А одно -- ну и фиг с ним, брать это одно число. (Это почти то же самое, что Esc, но если число менялось еще кем-то -- то запомнено будет другое.)
    • Вопрос 2 (посложнее): а как сия фича должна взаимодействовать со стрелками вверх/вниз, а?

    Вечером, несколькими часами позже: сделал, ровно по вышеприведенному проекту. Внутренние функции 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 -- две проблемы:

    1. В undo_fifo[] сохраняются только значения, лично введенные пользователем. Так что -- если мы ПЕРВЫЙ раз что-то вводим, то Ctrl+Z предыдущего имевшегося значения не вернет. И если другой юзер что-то менял -- то его значение также останется незамеченным.
    2. При последовательности 10+Enter, 20+Enter, 30+Enter, Undo, 40+Enter, Undo первое Undo вернет 20, как и надо, а вот ВТОРОЕ -- уже 30, хотя должно бы стать 20. А все потому, что у нас именно FIFO и ТОЛЬКО FIFO, а надо бы имитировать undo-STACK.

      Ну эта-то проблема решилась просто -- в PerformUndo() вставлено undo_fifo_used-- и "обратный сдвиг стека" [0]=[1].

    А вот что делать с первой проблемой...

    Вообще -- это я просто явно лоханулся тогда с разработкой модели, ведь fifo из ДВУХ элементов (из которых второй -- мимо кассы) уже должно было насторожить!

    Правильная-то модель такая: в StoreUndoValue() надобно запоминать то число, которое имеется в этот момент в ki->curv и взводить некий флажок "ki->undo_val_saved".

    Так что -- переделываем!!!

    Несколькими минутами позже: переделал. Работает. Во всех интересующих ситуациях работает. И, что характЕрно -- реализация на порядок проще первоначальной, безо всяких там FIFO. Еще одна отсылка к выводу от 21-02-2006/22-02-2006 на тему "первым делом в голову приходит НЕоптимальное решение"/"если проблема сходу красиво не решается -- отложи ее, решение придет само"...

    Вот теперь -- действительно "done"!

  • 10.06.2006: еще вчера, при разборках "куда уходит память" (см. в разделе по alarmonoffled за сегодня) обнаружилась какая-то странная утечка при использовании компонента text: при переходе фокуса тратятся по несколько аллокаций. Самый простой способ -- зажать Tab.

    Причем: эта утечка имеется, даже если оставить ОДНУ ручку 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".

  • 27.08.2006: надобно-таки сделать поддержку параметра "layout", по аналогии со slider'ами. Основная причина -- дабы можно было ставить метку НАД числом.

    15.02.2007: да, для началу сделал флажок "compact" -- ибо захотелось в ndbp иметь как раз метку-сверху-числа.

    16.02.2007: а поскольку никакие другие варианты с текстовым полем и не нужны, то закрываем данный вопрос и считаем за "done".

  • 18.10.2007@ICALEPCS'2007:grpmark -- может, наглядности ради, сделать ее в состоянии "on" более видимой? A-la MacOS X -- светло-голубой (XmNselectColor) и с галочкой?

    03.11.2007: да, сделал. Детали:

    • Ввел новый код цвета -- XH_COLOR_INGROUP, отображающийся на "#82D2FF".
    • При создании grpmark'а ему явно ставится XmNselectColor.
    • Кроме того, поскольку с умолчательной тенью это выглядело фиговато и слишком похоже на обычные onoff'ы, явно уставляется XmNdetailShadowThickness:=1.
    • Галочка же -- не смотрится: она, к сожалению, НЕ может торчать за пределы квадратика (как в MacOS X), а вписывается в него. Так что -- просто другой цвет.

    02.01.2008: и в 4cx/ также это все портировал.

  • 03.03.2009: очень полезна всё-таки была бы возможность показывать units даже у rw-ручек. Например, дополнительным полем, приделываемым справа от поля/стрелок.

    03.03.2009: да, сделал. Прилепляется ТОЛЬКО для rw-ручек и с непустым ki->units; причем, надо ОБЯЗАТЕЛЬНО указать "withunits" в widdepinfo. И колоризуется тоже.

    04.03.2009: и в 4cx/ too. Там при наличии оно приделывается по умолчанию, а для отказа надо указать в options флаг "nounits".

  • 31.03.2014@Снежинск-каземат-11: Старостенко (старший) возопил, что жаждет "интеллектуальное поле ввода", которое якобы имеется в куче всяких программ и просто мега-удобно.

    Суть в том, что при надобности добавить к уставке, например, 30, не надо делать вычисления в уме, а просто прямо в поле к уставке дописывается "+30", и оно само вычисляет нужное значение. А еще можно писать "*0.9" и тому подобные удобности.

    31.03.2014@Снежинск-Снежинка-504(ночь): в принципе, сделать можно сравнительно просто (и без привлечения всяких библиотек, как предлагали Стенка+Фатыкин):

    Достаточно в ExtractDoubleValue() чуток добавить интеллекта -- чтоб он после strtod() вместо требования обязательного конца строки смотрел бы, если там дальше идёт знак арифметической операции -- +,-,*,/ -- то попробовать от-strtod()'ить еще одно число, и его сочесть с первым указанной операцией.

    А в случае + и - разрешить еще указывать в конце числа %.

    И в 4cx/ не забыть скопировать!

    16.06.2014: сделано.

    • На проценты (%) забито -- оно достижимо умножением на сколько надо.
    • Количество компонентов не ограничивается -- можно писать "1+2+3...", и "1*2*3..."
    • Одно ограничение: приоритет операций в цепочке можно только понижать, но НЕ повышать. Т.е., "1*2+3" -- можно, а "1+2*3" -- нельзя (т.к. с этим немудрёным "калькулятором" оно бы дало (1+2)*3=9 вместо 1+(2*3)=7.
    • Побочный эффект: эта арифметика будет работать и в knobprops.
    • В 4cx/ скопировано.

    22.07.2014: считаем за "done".

dial:
  • ??.04.2004: обнаружил, что программа subharmonic, содержащая LOGD_DIAL, при запуске падает по SIGSEGV.

    22.04.2004: разобрался. Проблема появилась при добавлении PropsWindow, когда всем подвиджетам чохом делалось HookPropsWindow(). А прикол в том, что у dial'а подвиджет "title" может и не создаваться, в результате оно передавало w==NULL.

    Вывод на будущее: тщательнЕе надо, тщательнЕе!

  • 17.02.2005: кстати, друг Горацио, а не сделать ли, чтобы "тело" dial'ов и knob'ов также желтело, краснело, оранжевело, и т.д. -- в общем, когда "background(currentstate) != background(COLALARM_NONE)"? А то как-то неприлично выглядит, что все расцвечено, а диск -- белый...
  • 06.05.2005: а не ввести ли, чтобы у dial-widget'а тоже, как и у slider'а и у text'а была возможность через widdepinfo ключом "nolabel" отключать метку?

    11.05.2005: угумс, "done".

  • 29.08.2005: глядя на слегка кривоватую программу subharmonic, пришла в голову идея: а что, если устроить так, чтобы циферблаты dial'ов растягивались в ширину клетки (флаг сетки hfill=true, 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.

  • 31.08.2005: кстати -- глюк с focus-circle воспроизводится довольно просто: запускаем widgettest (сфокусирован при этом верхний селектор), жмем Btn2 на верхней крутилке (вылазит окно Properties), а потом переводим курсор мыши с этого окна на крутилку -- и voila! Т.е., надо, чтобы за границей popup-окна была сразу крутилка.

    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: сходу -- сделал центрирование текстового поля числа (valdpy, input).

    19.05.2009: побудительным мотивом стала программа nliustend -- где, в стиле Леши Панова, LOGD_DIAL используются для отображения времени работы. А с полем, прилепленным слева (а не по центру под циферблатом) это выглядело уродски.

    Так что сделал горизонтальное центрирование по образцу slider'а -- через Attachment=ATTACH_POSITION, Position=50, Offset=-wid/2. Пара замечаний:

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

      (Хотя, в принципе, проблема решаема -- помещать val_w в дополнительную формочку, которую аттачить как раньше, а центрировать уже внутри неё.)

  • 21.05.2009: обнаружилось, что на ro-dial'ах правая кнопка мыши на циферблате НЕ вызывает окно "Свойства...".

    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: надо бы ввести оптимизацию -- чтобы по приходу того же самого значения оно бы всё время не перерисовывалось. А то при крупном размере и большой частоте обновления оно просто стоит и помаргивает.

    21.05.2009: да, сделал по-простому -- вставил проверку, что если cur_v(ki)==v, то ничего не делать. Хотя вещественные числа на равенство вроде и нельзя сравнивать, но в данном случае -- во-первых, числа из одного источника (и, проходя одну цепочку арифметики, просто ДОЛЖНЫ совпадать и побитово), а во-вторых, максимум, что может случиться -- ну перерисуется лишний раз, а это нефатально.

    И -- просто ничегонеделанье при совпадении дало побочный эффект: при нулевом значении текстовое поле как было "------", так и оставалось дальше. Так что переставил проверку, что именно ПЕРЕРИСОВКА делается только при v!=old_v.

    Замечание на будущее: если будет делать расцвечивание и диска тоже, то надо не забыть вставить вызов DialDraw() в Colorize_m() -- поскольку если раньше оно бы просто перерисовалось на следующем цикле, то теперь -- уже фигушки.

separator:
  • 12.01.2005: ну что, вводим декоративные виджеты HSEPARATOR и VSEPARATOR?

    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: проверил в реальной программе (impacis10). В основном -- приемлемо. Хотя есть и некоторые замечания.

    16.01.2005: итого:

    1. У горизонтальных сепараторов высота оказывается весьма приличной -- ажно 8 пикселов. Это объясняется тем, что соответствующая rowlabel, пусть и пустая, имеет marginHeight=3, плюс еще highlightThickness=1, сложенные и умноженные на 2 эти вещи и дают 8.

      В принципе -- это совсем даже и не проблема: во-первых, может, оно так и к лучшему -- выглядит пристойно, а во-вторых, уж если припрет, то вполне можно сделать элемент с "norowtitles", тогда, при отсутствии сопутствующей метки, высота сепаратора сбрасывается до 4 пикселов (собственно shadowThickness=2 плюс свой highlightThickness=1).

    2. Сепаратор-то простирается лишь на ширину колонки "ручек", а колонка подписи -- пустая. Не очень это красиво смотрится. А как сие решить -- и близко не знаю...

      (Если б у SGL была поддержка COLSPAN (а у Gridbox она есть -- gridWidth, то можно б было ввести какой-нибудь KNOB_B_-флажок, по которому уставлять координату на 1 меньшую, и COLSPAN=2.)

    12.11.2005: использовал LOGD_SEPARATOR в одноколоночном элементе БЕЗ меток строк ("norowtitles") -- отлично! (istc/xmclients/[lebed::]phm_db.c::"Пимпочки") На вид занимает ровно правильное количество места.

    Несколькими минутами позже: елы-палы -- так у них highLightThickness не отключено!

    Уставил его в 0 -- стало еще более правильное количество места :-)

label:
  • 12.01.2005: мдя, уже возникло-таки желание завести такой виджет -- который просто текст-метка.

    13.01.2005: да, видимо, стОит иметь отдельный виджет, безо всяких там сепараторов. Ибо сепаратор должен бы будет (пока, по крайней мере) выставлять флажки KNOB_B_HALIGN_FILL либо KNOB_B_VALIGN_FILL, а метке это ни к чему.

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

    14.01.2005: в первом приближении сделано -- коды LOGD_{HLABEL,VLABEL}. И зарегистрировано везде, где надо.

    Оно "косит" под "rowlabel", поскольку для того уже имеются все нужные fallback-ресурсы на тему margin'ов, а "ручка"-метка -- по смыслу почти то же самое, что и rowlabel.

    Из минусов:

    1. Оно не колоризуется -- ибо овертикаливается один раз, при создании. Что ж -- такова уж наша парадигма вертикаленья текста. Тут ничего не поделаешь. 24.01.2005: пофиксено, см. ниже. Йоу-йоу!!!
    2. Для многострочных меток -- нет возможности указать alignment, оно всегда центрируется -- и для горизонтальных меток, и для вертикальных. Впрочем, если припрет -- всегда можно ввести парсинг из widdepinfo.
  • 24.01.2005: надоело, что с vlabel'ами не работает колоризация.

    24.01.2005: и даже тут же и исправил: подшаманил XhAssignVertLabel() (см. там комментарий за это число), и сделал метод Colorize(), который сначала вызывает стандартный COmmonColorize_m(), а затем -- повторно дергает XhAssignVertLabel(). И все стало ОК!

  • 28.04.2011@Снежинск-каземат-11: собственно -- а чего это вообще МЕТКИ должны колоризоваться?!?!?!

    Раньше-то от этого проблем не было, а теперь, с введением оптимизации по "точкам изменений" -- метки навсегда остаются JUSTCREATED.

    28.04.2011@Снежинск-каземат-11: короче -- пока отключаем у меток колоризацию, вставив в начало "return". Если где-то это вылезет -- будем разбираться.

winswallow:
  • 29.05.2006: мыслишка по размышлениям обо всяких camsel и gussel: а что, если сделать специальный тип knob'а, который "захватывает"/"всасывает"/"заглатывает" (swallow) указанное ему окно?

    Тогда б сходу решилась проблема разделенности программ отображения (tvtime, гусиный adc200) и переключения (camsel, gussel).

    29.05.2006: соображения по теме:

    • Knob-то должен бы быть "user", но пока придется обойтись LOGK_NOP'ом.
    • То, КАК захватывать -- это явно XReparentWindow() -- у нас есть пара мест, где можно посмотреть: FvwmButtons (но он-то работает модулем в кооперации с WM'ом) и plugger (у него есть директива "swallow", которая, к примеру, захватывает gnumeric).
    • Момент захвата: видимо, проверять один раз вначале -- при создании, а если не найдено -- то потом раз в N секунд (можно в 2 этапа: первые несколько раз по 1 секунде, а потом раз секунд в 5).

      А можно ль как-то мониторировать факт изменения списка окон?

    • И надо следить за ситуацией, когда засосанное окно отваливается.
    • И наоборот -- прибивать его в момент собственного destroy'я.

    31.07.2006: а ведь можно не только захватывать, а еще и иметь параметр exec="command" -- чтобы в случае ненайденности "жертвы" в момент рождения knob'а запускать ее.

    Замечание: в таком случае надо будет корректно уставлять той программе $DISPLAY -- на случай, если сама программа запущена с ключом -display ЧТО-ТО-ДРУГОЕ (исп. XDisplayName()).

    Да, а насчет "поиска" -- видимо, использовать код SearchForWindow() из cx-starter.c. Соответственно, "жертву" указывать можно будет либо через app_name=, либо через title=.

choicebs:
  • 18.12.2007: нужен еще один вид ручек -- "включатель".

    По смыслу это -- практически то же самое, что onoff или селектор на 2 положения, но вид должно иметь совсем другой: этакая "ручка", или "выключатель", как для комнатного освещения. Чтобы жмешь на одну его сторону -- включается (1), на другую -- выключается (0). Т.е., нечто вроде группы из 2 radiobutton'ов, только работающих как ЕДИНЫЙ объект (а не как сейчас у нас). В LabVIEW таких штучек -- хренова гора вариантов, разных видов и в разные стороны (хотя они поддерживают и просто "щелчок"-переключение, а не только детерминированные нажатия).

    В Motif'е ничего подобного и близко нету. Так что -- придется делать вручную, через XmDrawingArea.

    (Понадобилось для включения/выключения ("малютинизации") пучка на линаке -- там хочется иметь этакий "рубильник".)

    18.01.2008: посмотрел, как аналогичные вещи сделаны в MEDM'е. Там есть объект, именуемый "Choice Button" -- он представляет из себя линейку кнопок, как на радио, из которых нажата может быть только одна; "нажатость" кнопки -- сменены тени и armed/baackground. По смыслу -- аналог селектора. Так вот -- если вариантов всего ДВА, то эта хрень становится как раз "двухпозиционным включателем".

    Т.е., простейший ответ на задачу "как бы реализовать включатель?" -- это именно пара кнопок (работающие как единый объект), из которых в конкретный момент нажата только одна.

    А вообще можно реализовать и более полный аналог этого "Choice Button'а" -- чтоб работал как селектор, но на кнопках. Highlights:

    • Линейка может быть как вертикальной, так и горизонтальной (а матрица-поток?).
    • Кнопки надо делать одного размера по направлению, перпендикулярному "основному" -- видимо, надо пхать в форму.
    • Отдельный вопрос -- как обойтись с highlight rectangle и прочими тенями -- чтобы линейка, с одной стороны, была бы "непрерывной", а с другой -- выравнивалась бы с обычными кнопками. И -- поддерживать ли tracersal с клавиатуры.
    • В нажатом состоянии кнопка приобретает фон "armed", в дополнение к смене теней -- надо сделать XhInvertButton().
    • А вот widdepinfo пока, наверное, надо бы подунифицировать с selector'ом.

    22.01.2008: за последние два дня сделал. Highlights:

    • Название оно получило "choicebs" -- сокращение от "Choice ButtonS".
    • Основа взята с selector'а.

    А если захочется сготовить "матрицу-поток" -- то надо будет внедрять XmRowColumn.

    26.04.2008: начал использовать (в tantal'е), и заметил странность: размер у choice-кнопок отличается от оного у обычных кнопок...

    Потом доперло: ведь я дал этим кнопкам имя-класса "choiceItem", и они НИКАК не корректируют свои геометрические параметры относительно motif'овских умолчаний -- ибо в fallback'ах про них ничего и нету. Заодно, кстати, они и НЕ следуют "протоколу CTL3D".

    Решение очевидно -- теперь ставятся те же имена-класса "push_i"/"push_o", что и кнопкам. Сделал, работает.

    12.05.2009: замечание: кнопки имеют одинаковый размер только ПЕРПЕНДИКУЛЯРНО основному направлению, а вот вдоль -- нет. Если припрет -- то действительно надо будет переходить на XmRowColumn. Но -- похоже, просто незачем. И даже более того: в принципе можно было б сделать на сетке, в т.ч. и многоколоночные. Тогда будет прямо противоположный упор: одинаковый размер станет уставляться только в ОДНОЙ колонке/строке, а в РАЗНЫХ он будет делаться "оптимальным".

    Кстати, давно уже работает -- так что весь раздел ставим в "done".

    16.03.2013@утро,душ: зачем нам для "потока" какой-то XmRowColumn?! Да вручную делать -- по той же технологии, что LRTB! Слегка муторно, но зато всё под нашим контролем.

  • 07.04.2008: кстати -- а как насчет поддерживать и метку (в точности аналогично selector'у)? И прилеплять ее в начало "линейки".

    12.05.2009: да, сделал. Довольно просто. И теперь элементы "линейки" аттачатся не к [y-1], а к prev. И -- метка НЕ колоризуется.

  • 10.05.2009: захотелось уметь РАЗНЫЙ цвет у нажатых кнопок. Например, чтобы у некоего выключателя активный пункт "Выкл" светился красным, а активный "Вкл" -- зеленым.

    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 сделана -- довольно просто:

    1. Увеличен размер l_buf[] с 200 до 1000.
    2. Введена проверка на 2 штуки MULTISTRING_OPTION_SEPARATOR'а, с целью добычи tip_p и style_p.
    3. Созданы структура itemstyle_t с таблицей text2itemstyle[], куда и psp-парсится style_p (ради чего скопирована вся троица wdi_* из ParseWiddepinfo()). Единственный параметр lit=ЦВЕТ отображается на поле armidx.
    4. При непустом tip_p он используется для уставки тултипа.
    5. А если 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).

  • 04.06.2009: при запуске программы weldcc, использующей CHOICEBS в формульном режиме, с отображением по одному каналу (назовем его "cur"), а уставкой в другие 2 ("setoff" и "seton"), обнаружилась неприятность: при нажатии для смены состояния оно подсвечивается оранжевым.

    Механизм-то понятен: мы что-то писали в "setoff", а в "cur" вроде бы нет, и cda видит, что значение поменялось -- следовательно, зажигает OTHEROP. Ей-то неоткуда знать, что каналы связанные...

    04.06.2009: с одной стороны, аналогичная штучка у нас уже есть -- KNOB_B_IS_BUTTON, и уставка этого флажка сразу решит проблему. С другой же:

    1. Такое поведение от CHOICEBS требуется далеко не всегда, а только в экзотических случаях с использованием 3 разных каналов.
    2. А главное -- в коде библиотек флаг KNOB_B_IS_BUTTON используется в ДВУХ разных случаях:
      1. Для неоранжевения (23-08-2004).
      2. Для незагорания кнопок при нажатии на них. И тут в записи за 06-06-2007 даже написано, что это лишь совпадение, и надо "думать, думать, и еще раз думать...".

    1-я-то проблема решается просто -- придётся сделать widdepinfo-параметр, указывающий на необходимость взвести флажок. А в 4cx/, соответственно, специальный тип/ключик при описании ручки.

    А вот 2-я -- явно надо делить флаг на ДВА: B_IS_BUTTON оставить как указание "не возвращать значение в ручку при уставке", а создать еще "B_IGN_OTHEROP", который бы отвечал за срезание OTHEROP.

    И тут есть еще пара аспектов:

    1. Флаг OTHEROP в смысле срезания завязан еще и с PRGLYCHG. Вопрос: а это реально надо? Ну для !is_rw -- да, а вот для is_rw, но IGN_OTHEROP -- да ли?
    2. В случае с CHOICEBS напрашивается некоторая оптимизация: срезать OTHEROP только при wasjustset, чтобы при реальном изменении другим оператором оно всё-таки показывалось бы. А для button-alikes это надо ли? Видимо, нет -- поскольку там есть еще CX_VALUE_DISABLED_MASK, который должен лишь менять вид кнопки, но никак не оранжеветь её. Так что -- такую оптимизацию не делаем.

    BTW, не забыть эту всю замуть отразить и в 4cx/.

    16.12.2010@Снежинск-каземат-11:

    • добавлен KNOB_B_IGN_OTHEROP, сразу после KNOB_B_IS_BUTTON; все следующие сдвинуты, а KNOB_B_{H,V}ALIGN_* вместо 6,9 теперь 16,19.
    • Cdr.c::ChooseStateRflags() теперь использует B_IGN_OTHEROP (и НЕ использует B_IS_BUTTON вовсе!).
    • а в Knobs_button_widget.c::*_Create_m() добавлено уставление KNOB_B_IGN_OTHEROP.
    • т.е., теперь эти флажки ПОЛНОСТЬЮ разделены, каждый отвечает только за своё, и B_IS_BUTTON не влечёт B_IGN_OTHEROP.
    • в селектор и choicebs добавлен ключ #!, уставляющий IGN_OTHEROP.
    • соответственно -- в клиентов (liu) повставлял.

    Внедрение IGN_OTHEROP в 4cx/:

    • сам флажок вставил туда же (вопрос, конечно, место ему там или в группе "с конца"; но пока оставляем как в CXv2).
    • choose_knob_rflags() переведена с B_IS_BUTTON на B_IGN_OTHEROP.
    • флаг взводится в Cdr_fromtext.c::CONTENT_fparser()::"button" и MotifKnobs_button_knob.c::EquipButtonKnob().
    • в MotifKnobs_selector_knob.c добавлен ключик "ign_otherop".

    "done".

usertext:
  • 28.12.2009: это -- реализация для LOGK_USERTEXT.

    28.12.2009: покамест сделан только "скелет" -- по образу LOGD_HLABEL'а создаётся и колоризуется XmLabel; плюс, сделан метод PropsChg() -- поскольку именно ТАК будет меняться текст метки. Но вот появление по правой кнопке мыши окошка для изменения метки -- пока НЕ сделано.

    29.12.2009: дореализовал штуку полностью, теперь по правой кнопке мыши появляется окошко редактирования, и по [Ok] в нём метка меняется (оно просто вызывает свой PropsChg() -- через VMT!). Да -- оно поддерживает ТОЛЬКО однострочные метки.

    Считаем за "done".

  • 26.03.2010: еще некоторое время назад вылезли какие-то странные косяки -- на wolf (CentOS-5.2@x86_64) при изменении метки она ИНОГДА целиком не показывается, будто остаётся тот же XmNwidth, а показывается только при изменении следующей.

    26.03.2010: самое противное -- это проявляется ОЧЕНЬ иногда. При запуске программы по X11 с wolf на экран viper'а -- всё окей. На самом wolf -- тоже воспроизводимость странная...

???:
Xh:
Общие вопросы:
  • 13.04.2004: сделал сходу: XhTextExtractIntValue() с той же самой функциональностью, что и XhTextExtractDoubleValue().

    06.02.2021: в ней тогда почему-то в вызове strtol() было использовано значение base=10, из-за чего шестнадцатиричные числа считаются синтаксической ошибкой.

    Тут-то, в CXv2, уже ничего исправлять не будем, а вот в CXv4'шной MotifKnobs_ExtractIntValue() переделано на base=0.

  • 18.04.2004: понадобилось иметь XhVMakeMessage()va_list-параметром вместо "...").

    18.04.2004: сделал, при этом собственно функциональность перекочевала именно в XhVMakeMessage() -- благо, подобный опыт с logline()->vlogline() уже был.

    Заодно обнаружилось: там РУКАМИ имитировалась strzcpy() -- strncpy(buf,...)+buf[sizeof(buf)-1]='\0'!

  • 18.04.2004: обнаружил, что XhMake{,Temp}Message() определены без __attribute__((format,...)). Надо исправить!

    19.04.2004: добавил.

    21.04.2004: благодаря этому добавлению обнаружил, что gcc, оказывается, умеет вякать "zero-length format string".

  • 07.05.2004: для некоторых "сильно наполненных" программ шрифт lucidatypewriter-12 великоват, и им надо дать возможность указывать шрифт помельче.

    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 отклонения туда/обратно бывают).

  • 07.05.2004: для меток столбцов элементов в Chl_gui.c требуется возможность вычитывать координаты виджета в сетке.

    08.05.2004: ввел XhGridGetChildPosition(). Для подстилающего виджета TABLE эта функция не реализована -- там с этим вопросом большой зад.

  • 04.06.2004: разбирательства с тем, что Motif'овские окошки почему-то всегда позиционируются в (0,0).

    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() вызывается именно она.

    И наступило нам счастье!!!

  • 04.06.2004: были какие-то странности в Xh_window.c с то static, то не-static определением AddMotifCloseCallback(). Просто перенес все внутренние функции-утилиты в начало файла и сделал их все static.
  • 11.06.2004: Старостенко опять разорался, что нужен красный курсор.

    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: еще дополнения:

    1. Опрошенный вчера Старостенко заявил, что тот вдвое-увеличенный красный курсор огромен и уродлив. Основной недостаток -- слишком толстый бордюр. (Оно и ясно -- пикселы 2*2 :)
    2. Вставил после вызова XCreatePixmapCursor() освобождение исходной пары Pixmap'ов через XFreePixmap().
    3. И вообще -- закомментировал создание этого огромного в "#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'а. Но получилось. Вылезла только пара неожиданностей:

    • Во-первых, функции типа XtIsShell() реализованы как макросы, вызывающие _XtCheckSubclassFlag(). Сделал соответствующий символ и с-alias'ил на него искомое --
      #define _XtCheckSubclassFlag ptr__XtCheckSubclassFlag
    • Во-вторых -- проблема со всеми именами типа "XtXname..." -- у нас это XtHrealizeWidget и XtNchangeHook. Дело в том, что они делаются через хитрую ссылку на массив XtStrings[]. Также пришлось делать aliasing --
      #define XtStrings ptr_XtStrings
      А фишка-то в том, что XtStrings[] -- это НЕ массив указателей на строки, это просто строка -- куча подстрок, разделенных '\0'. А определения-ссылки на этот массив -- содержат не НОМЕРА строк, а их offset'ы. Как я понял, эта вся алхимия генерится специальной программой из некоего исходного файла имен...

    В общем -- можно начинать прикалываться над гусиными программами... :-)

  • 01.07.2004: наткнулся сегодня на проблему: что окно теперь увеличивается по statusLine.

    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().

    1. Во-первых, при переводе на XmText, копируя код из XhMakeMessage(), умудрился забыть поменять в уставке строки виджету mainmessage на tempmessage.
    2. Во-вторых, когда правил 13-04-2003 проблему "вызов XhMakeMessage() для окна без statusline" (см. bigfile.html), то про XhMakeTempMessage() забыл.

    Мда, самая "крупная" проблема с Xh_statusline.c за все время его существования :-). Так что на собственны раздел этот файл так и не тянет :-). Короче -- все исправил.

  • 07.07.2004: (реально это бы не сюда, а в Chl, но Xh_fallbacks -- тут) заметил, что у заголовков колонок шибко широкие горизонтальные поля (заметил на программе vacuum, в которой очень узкие столбцы, и заголовки бывают шире).

    07.07.2004: даааа... На тему "*.collabel" в fallback'ах вообще ничего не было, так что мало того, что брались умолчания от XmLabel, так еще и shadowThickness=2.

    Исправил -- скопировал строчки от rowlabel, опустив при том margin{Height,Top,Bottom}.

    14.06.2012: сейчас скопировал и [почему-то] опущенные тогда margin{Height,Top,Bottom} -- иначе фигово смотрелось, не выравнивались collabel'ы с rowlabel'ом вмещающего элемента.

    Короче -- теперь ВСЕ "места"-метки и прочие отображаторы заточены под одинаковые размеры, чтоб всё со всем сочеталось.

  • 11.07.2004: кстати, может, стоит сделать и XhVMakeTempMessage(), по аналогии с XhVMakeMessage()?

    11.07.2004: сделал, стандартным уже способом -- вытянув функциональность именно туда, и оставив в XhMakeTempMessage() просто va_start/.../va_end.

    И так же заменил "имитацию" strzcpy() на нее саму.

    Что не понравилось: там имеется vsprintf(), после которого и идет strzcpy(). Может, корректнее-таки перейти на "сразу check_snprintf()", или завести какой-нибудь zsnprintf()?

  • 03.08.2004: надоело, что в программах, использующих Xh, приходится включать Xh_typesP.h для того, чтобы получить доступ к 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'ные интерфейсы цветов и файловых диалогов.

  • 07.08.2004: сделал реально работающим XhACT_FILLER -- XhXXX_TOOLFILLER().

    07.08.2004: теперь все, что до него, аттачится как и раньше -- влево. А все, что после -- аттачится вправо. Чтобы не было наползания "растущих справа" и "растущих слева", между ними вклеивается пустой виджет (Core).

    Вроде корректно отрабатываются граничные случаи -- когда FILLER идет либо первым, либо последним в списке.

    Опыты показали, что лампочки (LEDS) лучше все-таки оставлять слева -- последними. А справа -- после FILLER'а -- только "дверь".

    04.01.2005: удалил из Xh_toolbar.c старый вариант, который не поддерживал FILLER.

  • 21.08.2004: захотелось в дополнение к обычным иметь в Xh_fallbacks.h стандартные спецификации еще и bold-шрифтов.

    21.08.2004: сделал -- XH_FIXED_BOLD_FONT и XH_TINY_FIXED_BOLD_FONT определяются так же, как и их не-BOLD-версии, только вместо "medium" стоит "bold".

  • 31.08.2004: так, из разбирательств по ходу дела: случайно сунул нос в константы XhWindow*Mask, и слегка удивился.

    Что такое XhWindowOnTopMask? Оно мало того, что нигде не используется, так его еще и в доисторическом mapp/ нету, а есть только начиная с oldcx/*/chl.h, и там оно реально тоже нигде не встречается, кроме как в определении.

    Есть также некая XhWindowSecondaryMask -- она реально поддерживается в Xh_window.h, приводя к флагу XmNtransient:=True, но никем не используется. Видимо, делалось для ipp-подобных (но в старом ipp -- не используется). Когда оно появилось -- вопрос, но в oldcx аналогичная (но не идентичная!) функциональность висела на флаге XhWindowTitleOnlyMask.

  • 05.10.2004: Xh_grid.c XmSepGridLayout{.c,.h,P.h}: вследствие появления вложенных элементов возникла необходимость перейти от "тривиальной" схемы с возможностью указания "distances" (aka "CELLPADDING" в HTML) к возможности указывать также и расстояния между клетками (aka "CELLSPACING"). При указании именно "cellspacing" содержимое клеток во внешней и внутренней таблицах может быть выравнено, что невозможно при "cellpadding" (там образуется набег лишних пикселов).

    05.10.2004: несколько дней назад (точнее -- 30.09.2004) Саша ввел возможность указывать ширины/высоты места под сепараторы независимо от наличия этих самых сепараторов.

    Так что надо теперь добавлять вызов "XhGridSetCellspacing()" (а в идеале -- и переименовать XhGridSetDistances() в XhGridSetCellpadding()).

    Итак:

    • XhGridSetDistances() переименована в XhGridSetPadding().
    • Добавлена функция XhGridSetSpacing(). Пока она работает только с SGL.
    • По-хорошему, надо б разобраться -- часть того, что делалось раньше с Gridbox и Table, реально есть именно spacing, а не padding.

    Итого -- по минимуму, уже получилось, и во вложенных элементах все выровнялось с обычными.

    Правда, есть два замечания:

    • Во-первых, у Саши там стоит странное ограничение -- что указываемые размеры должны быть не меньше 1, иначе они игнорируются. Правильно смотреть, чтобы было не меньше, чем требуется для указанного типа сепаратора -- 0, 1, 2.
    • Во-вторых, что-то там странное в Editres при попытке чтения этих полей...

    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: давно назрело: надо завести Xh-флаг, говорящий, что надлежит attach'ить workSpace на расстоянии не 8, а на толщину тени parent'а.

    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.

  • 20.10.2004: кстати, по-моему, пора завести возможность ставить в качестве workSpace'а Grid. Для чего -- переделать "XhWindowRowColumnMask" в "XhWindowWorkspaceTypeMask", и ввести XhWindowWorkspace{Form,RowColumn,Grid}.

    18.07.2005: сделал, хотя конкретно Grid и не проверял -- куда он нафиг денется? Заодно перетряхнул собственно флаги и рассовал их по группам.

  • 20.10.2004:

    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: давно была потребность заиметь возможность писать текст вертикально. И потребность эта -- в нескольких разных программах...

    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: Вылезла странная вещь во время перехода на файловые диалоги без-имен-файлов: похоже, Motif-программы locale-unaware, так что приходится переключать клавиатуру с Cyr (0x6NN) в Koi8(0xNN), иначе ничего не вводится.

    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: надоела кривость, что во многих местах, пользующихся Xh'ем, приходится иметь собственные объявления типа "extern XtAppContext context".

    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: довольно известная потребность -- уметь ПРОГРАММНО нажимать/отжимать кнопку на тулбаре.

    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 я тогда забыл. Сейчас сделал (хотя зачем он нужен? :-).

  • 02.03.2005: может, и не совсем к Xh, но -- куда-то это надо: пришла пора сделать некоторое количество стандартных констант-команд 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: хочется все-таки чтобы РЕАЛЬНО поддерживались hotkeys -- т.е., чтобы, например, нажатие F2 вызывало окошко "сохранить файл".

    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: обнаружилось, что в некоторых window manager'ах Xh'ные окошки не двигаются. Т.е. -- пытаешься тянуть его мышью за заголовок, а оно стоит на месте.

    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".

  • 27.04.2005: а еще чрезвычайно полезна будет, в дополнение к XhSetCommandOnOff(), возможность сделать disable/enable (sensitive) кнопкам с указанной командой.

    Смысл -- для "красивой" работы кнопок типа "поехали"/"пауза" и "однократно" -- чтобы при нажатой кнопке "Поехали" кнопка "Однократно" была б отключена.

    02.05.2005: угу -- сделал, функция XhSetCommandEnabled(). Проверил на adc333.c -- работает (а чему б там глючить? :-).

  • 02.05.2005: по результатам изготовления 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.

    Пока не проверял, но сработать вроде бы должно (хотя все равно остается вопрос, ПОЧЕМУ произошло то, что произошло -- почему фиксы ТАК с-интерферировали?).

  • 03.05.2005: кстати, а ведь можно и воспроизвести механизм (15-летней давности!) из Turbo Vision -- 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".

  • 03.05.2005: кстати, а мы, собственно, XhACT_SEPARATOR поддерживать собираемся?

    03.05.2005: да, сделал. Даже в двух вариантах -- как просто пустоту (виджет Core, как и filler), и как вертикальный сепаратор. Выбирается условной компиляцией.

  • 03.05.2005: сходу: в Xh_{toolbar,statusline}.c при создании сепараторов отсутствовало указание XmNorientation:=XmHORIZONTAL. Вставил.
  • 10.05.2005: раз уж я так "подсел" на плагины: а как насчет ввести понятие "tool button plugins"? Чтоб можно было некоторые полезные вещи помещать прямо на toolbar, где место обычно есть, а не занимать под них место в самом окне.
  • 11.05.2005: произвел ревизию Xh_fallbacks.h.

    11.05.2005: во-первых, переместил не-к-месту-присутствующие ресурсы для "value" в Knobs_internals.c.

    Во-вторых, ушли где-то полтора-года-назад-устаревшие ресурсы для окна шага -- "stepShell"/"stepForm".

    В-третьих, туда же отправились и "hslider" с "vslider".

    В-четвертых -- проверил, что тут нет НИКАКИХ поведенческих ресурсов -- только геометрические, шрифтовые и метки/заголовки. Единственное "исключение" -- "*.pathMode:PATH_MODE_RELATIVE".

  • 03.11.2005: (замечено на lebed) почему-то если первоначальная XhShowWindow() вызывается не после создания всего содержимого, а сразу после XhCreateWindow(), то вылетает ошибка:
    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
    
    Собственно, возможно, это проблема даже не Xh, а Knobs, или Chl, или еще чего-нибудь.

    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: проблема исправлена.

    • Ее причиной было то, что сетка с ncols/nrows==0 ставил соответствующий размер в 0, что запрещается X-протоколом (это и дает BadValue).
    • Почему протокол запрещает нулевой размер -- для меня странно, это выглядит явным misdesign'ом, но уж так прописано в документации.
    • Почему в случае, когда окно не-было-realized до создания сетки, таковая проблема не возникала -- тоже вопрос; видимо, где-то в недрах Xt имеется некая проверка на эту тему.

    Но, по результатам "работы" с пустой сеткой, и с realize-до-наполнения:

    • Зато вылез другой прикол -- сетка как-то странно ставит свои размеры (или это с формой оно так взаимодействует?), что все расползается.
    • Выглядит такое появляющееся и "разъезжающееся" окно, конечно, классно -- впечатляет. Но:
    • Ведь идеологически-то правильнее "появлять" окно уже ПОСЛЕ заполнения содержимым, по одной простой причине: window manager'у-то нужно подобрать на экране место для окна, в соответствии с размером. А со всей этой анимацией мы его попросту обламываем.

    Так что выводы: Саша пусть все-таки разберется, что ж там подглючивает, но 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".

  • 12.11.2005: ища по warning'ам от всей компиляции на тему "return" наткнулся, что в Xh кое-где НЕ делается конверсия CNCRTZE()/ABSTRZE(). В первую голову это вылезло в Xh_window.c, на функциях XhGetWindowNNN().

    12.11.2005: прошелся. Функции-accessor'ы подправил. Заодно подкорректировал и весь Xh_window.c. Далее прошелся по Xh_utils.c... И все! В Xh на данный момент больше таких проблем нету!

  • 07.08.2006: не то чтобы это уж только Xh, но -- подумавши, решил, что следует ВЕЗДЕ при аллокировании GC указывать и "функцию" (флаг GCFunction, поле XGCValues.function) GXcopy. А то бы, вдруг (хотя и врядли, но все же) выдали нам GC со всеми указанными полями совпадающими, а функцией -- другой. И были бы превеселейшие глюки!

    07.08.2006: сделал. Это коснулось:

    • Knobs_internals.c::AllocXhGC().
    • Chl_canvas.c::AllocPixelGC().
    • adc333.c:: (ой-е... А вот ее-то в istc/xmclients/, где она и зародилась -- валом!)
    • AllocOneGC(), имеющейся пока что во всех наших qult/xmclients/.
    • Приличия для (точнее, из предосторожности, что оно может и в Chl либо Knobs войти) --

    А потом стал думать: но ведь, по логике, программа НЕ ДОЛЖНА пытаться указывать все-все -- а только нестандартности, так? А то ей еще и fill_style пришлось бы всегда указывать, и проч. Так что -- наверное, это излишне. И дальше править не стал.

    Кстати, попробовал разобраться в логике в xc/lib/Xt/GCManager.c -- нифига толком не въехал. Интуитивно чувствую, что такой подленки оно вроде бы не подложит -- судя по тому, что макросы CHECK()+GCVAL() имеют и параметр default, используемый при отсутствии бита в маске, и для поля function оным default'ом является GXcopy, но -- 100%-уверенности нету.

    Хотя очень, очень похоже.

  • 24.03.2009: для записи: поскольку мы стремимся унифицировать Xh_*.c в cx/ и 4cx/, а именно в 4cx/ используется более стройная система типов, то и в cx/ потихоньку будем переводить Xh на неё -- в частности, на 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/):

    • 31.07.2015 файл Xh_types.h удалён, а его содержимое переведено в cxv2_common_types.h.
    • 01.08.2015 типы XhWidget и XhPixel повсеместно заменены на CxWidget и CxPixel.

      А XhWidget и XhPixel (бывшие к тому моменту typedef'ами на CxWidget/CxPixel) удалены.

      Буквосочетание "XhWidget" осталось только в именах функций XhWidgetOfPlot() и XhWidgetOfMonoimg(), где его образование чуть иное и оно скорее к месту.

    • 02.08.2015 cxv2_common_types.h переименован в cx_common_types.h -- полностью совместимо (для v2'шных исходников) с v4'шным, и из 4cx/src/lib/v2cx/Makefile заботы о нём удалены.
  • 05.04.2009: поддостали warning'и вида
    /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'ные исходники точно собираться перестанут :-)

  • 31.10.2012: сходу -- выкинута (из Xh_app.c) никогда не использовавшаяся (и неясно когда введённая) "инфраструктура" XhAddSignal()/XhNoticeSignal().
  • 30.08.2014@дома: надо б ввести возможность навешивать дополнительные callback'и на команды, в дополнение к XhWindow.commandproc; очевидно, реализация на SLOTARRAY.

    Смысл -- чтоб не только основная программа (де-факто -- Chl) могла б ловить команды, но и прочие потребители, хоть как-то имитируя v4'шную архитектуру с текстовыми командами и broadcast'ами по дереву.

    Потребность конкретно сейчас -- для хитрого плагинчика в weldclient (для техпроцесса "3D-принтера", интерфейс к 3dp.sh), чтоб дать ему возможность сохранять наборы уставок в файлы.

Xh_fdlg:
  • 13.01.2004: давно надо было сделать интерфейс для файловых диалогов. Сделал -- интерфейс XhCreateFdlg()/XhFdlgNNN().

    16.01.2004: перевел Chl_simple.c на XhFdlg.

    11.06.2004: НЮ-НЮ!!! Недоделано оно было по-страшному!!! И вот почему.

    1. Во-первых (и это еще полбеды), из-за того, что мы вызывали для перечитывания директории XmFileSelectionDoSearch() с параметром dirmask, предварительно полученным при помощи XtVaGetValues(,XmNdirMask,...), эта тварь (FileSB) всегда перепрыгивала обратно в текущую директорию. Т.е., она использовала указанное в качестве фильтра имен, а директорию -- всегда делала "", т.е., текущую. Bugware, блин!!!

      А чего в man-странице не было указано, так это того, что можно передавать dirmask=NULL, и тогда оно само возьмет текущие значения из диалога, и все будет OK.

      Так что -- выкинуто к чертовой матери все на тему dirmask.

    2. Во-вторых (и это хуже!), я тупо брал в качестве результата выбора XmNvalue от child'а по имени XmDIALOG_TEXT. Это было некорректно, поскольку содержало только имя, БЕЗ директории.

      Но мало того -- даже ресурс самого диалога XmNdirSpec, долженствующий содержать ПОЛНОЕ имя файла, содержит тоже имя БЕЗ директории -- это баг в Motif'е, спасибо Марку Эделю, nedit-5.2/util/getfiles.c::HandleCustomExistFileSB().

      Так что -- надо "руками" делать конкатенацию XmNdirectory и XmNdirSpec.

      Сделал, практически по той же логике, что и в NEdit'е. Что забавно, вставлять '/' между директорией и файлом не надо -- директория его уже и так содержит.

  • 29.11.2004: еще несколько дней назад обнаружилось, что почему-то, если поставить в списке директорий курсор на что-нибудь, затем закрыть окно, а потом вызвать его заново, то оно будет "зайдено" в эту директорию.

    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: у-ф-ф-ф, наконец-то реально приступаем к изготовлению "файловых диалого нового стиля".

    29.11.2004: в конечном итоге стал все делать на основе XmSelectionBox; в частности, окошко SaveDlg -- как PromptDialog.

    С LoadDlg же решил отступить от первоначального плана (когда под списком располагается value с приклеенным к нему справа push [...]). И сделал [...] обычной кнопкой, которую диалог сам запихивает в строку кнопок внизу, рядом с [OK]. А строку для имени файла сделал просто виджетом, безо всякой формы, и диалог прекрасно прилепляет ее прямо под списком.

    30.11.2004: собственно -- сделал. Заметки по теме:

    • При создании -- функции XhCreateLoadDlg() -- передаются параметры pattern -- шаблон имен, и filter -- функция, которая определяет, годится ли файл с указанным именем, и если да, то возвращает его дату создания (читая изнутрий файла) и комментарий. Эти параметры запоминаются и используются в XhLoadDlgShow() при подготовке списка. На всякий случай перед вызовом фильтра делается "упреждающее чтение" времени из st_mtime файла.
    • В список попадают строки вида "DD-MM-YYYY HH:MM: Comment", причем если комментарий пустой, то вместо него используется имя файла.
    • Сортировка делается при помощи 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() имелась пара позорных багов:

    1. Цикл подсчёта maxserial был не 1..NDIGITS, а 1..NDIGITS-1.
    2. Сравнение "не зашкалил ли номер" было "x>=maxserial" вместо надлежащего "x>maxserial".

    В результате последним номером становилось "098", а потом шло "ZZZ". Это обнаружилось в первый раз за девять с лишним лет -- старостенкины студенты, делая курсовик, сохранили толпу файлов на blm'е.

  • 07.04.2005: пришел Чугуев, что диалог "Open" почему-то не показывает ни единого файла. Действительно -- не показывает.

    07.04.2005: точно, там был баг -- я вставлял доп. условие, что длина имени не меньше суммы длины префикса и суффикса, и вместо "<" поставил ">=".

    Что интересно -- файл был помечен как "06.01.2005", и ровно за эту же дату выше имеется комментарий "давно работает, проверено" :-)

  • 03.05.2005: сходу -- сделал, что по умолчанию уставляется отсутствующий комментарий, так что функция-фильтр может на эту тему вообще ничего не делать (например, если формат файла не содержит комментария).
  • 09.05.2008: сегодня прилетело письмо от японцев, с ответами на наши с Ромой Кусковым вопросы на тему ZLog'а, так там вылезла интересная деталь -- у них screenshot'ам даются имена вида "yyyy_mm_dd_HH_MM_SS".

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

    Надо огрокать плюсы и минусы такого подхода в их полноте (да, "Stranger in a Strange Land" читаю в оригинале...)

Xh_stddlg:
  • 19.09.2005: давно назрело -- что надо иметь некий "интерфейс стандартных диалогов общего назначения" (см. проект).

    19.09.2005: сделал, почти строго по проекту. Передаются параметры "имя" (к которому добавляется "Shell" и "Box"), заголовок окна, опциональная (если не-NULL) метка для кнопки "OK", строка сообщения (если NULL -- виджет отключается) и флаги, определяющие набор кнопок, поведение (resizable, zoomable) и модальность.

  • 13.03.2007: для расширения области применимости stddlg надо уметь ему указывать, не отключить ли autoUnmanage (это нужно для именно ДИАЛОГОВЫХ окон, а не просто отображающих -- типа knobprops).

    13.03.2007: добавил "behaviour"-флажок XhStdDlgFNoAutoUnmng, при наличии которого делается XmNautoUnmanage:=False.

  • 13.03.2007: хочется уметь отключать ВООБЩЕ ВСЕ оформление -- включая не только кнопку [Ok], но и сепаратор.

    13.03.2007: ввел флажок XhStdDlgFNothing, наличие которого отключает сепаратор плюс автоматом сбрасывает все флаги наличия кнопок.

    29.03.2007: и еще один флажок -- XhStdDlgFNoMargins, уставляющий box'у XmNshadowThickness, XmNmarginWidth и XmNmarginHeight в 0. Это занадобилось в histplot -- там вообще никакое оформление не нужно, и поля тоже не нужны.

  • 13.03.2007: еще несколько дней назад заметил, что почему-то флажок Zoomable не работает.

    13.03.2007: стал разбираться -- оказалось, что дело вовсе не в stddlg, поскольку xprop показал наличие указанных hint-флажков -- похоже, просто FVWM их игнорирует. Почему? Может, оттого, что окошко -- secondary/popup? И как сие обойти -- использовать другой Shell-класс, или XtVaCreateКакойНибудьДругойShell()?

  • 22.05.2007: занадобилось иметь флажок "НЕ иметь default-button'ов".

    22.05.2007: занадобилось -- ради ELEM_SUBWIN'а, чтобы по кнопке [Enter] окно само бы не захлопывалось. Да и для всяких ndbp_adc200_elemplugin'ов, где уставки будут в отдельном окошке, knob'ами, оно тоже понадобится.

    Так что -- ввел флажок XhStdDlgFNoDefButton, который при уставленности приводит к XmNdefaultButtonType:=XmDIALOG_NONE.

    Единственная гадость -- что кнопки в отсутствие default'ности выглядят мерзковато -- outline у них отделен от тени расстоянием XmNdefaultButtonShadowThickness, которое при сбросе default'ности XmMessageBox'у никуда не девается, но "вдавленность"-то НЕ рисуется.

    Так что -- при уставленном XhStdDlgFNoDefButton всем кнопкам делается также XmNdefaultButtonShadowThickness:=0. Кнопка [OK] стала после этого выглядеть как-то сиротливо и куцевато, ну да и ладно.

  • 26.02.2009: напрашивалось в XhStdDlgShow() вставить, кроме XtManageChild(), еще и XRaiseWindow().

    26.02.2009: да, сделал.

    Заодно выкинул проверку "if (XtIsManaged(dialog)) return;" -- нафиг она не сдалась. Тем более, тогда ВООБЩЕ НИЧЕГО б не делалось.

    И в 4cx/ это добавлено.

  • 26.02.2009: захотелось засунуть в 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.

    Работает нормально -- безо всяких отлётов. Только пара тонкостей:

    1. Практически везде в Chl использовалось XtManageChild() вместо XhStdDlgShow() (кое применялось ТОЛЬКО в subwin'е да в cx-starter'е (для help-окна)); пришлось исправить. С XhStdDlgHide() аналогично (хотя уже и неважно). 4cx/ too.
    2. Конкретно в FVWM-2.4.6 это выглядит слегка странно -- из-за неотрабатывающести XRaiseWindow(); под Metacity же в CentOS-5.2 всё окей.

    Так что -- меняем с "withdrawn" за 27-02-2009 на "done".

    P.S. А под современным FVWM-2.4.20/2.5 еще надо проверить.

  • 26.02.2009: закомментировал в Xh.h флажок XhStdDlgFApply и выкинул все связанные с ним упоминания из Xh_stddlg.c.

    26.02.2009: дело в том, что в используемом у нас типе диалогового окна -- XmMessageBox -- нету никакой кнопки "Apply", так что это изначально никак работать не могло. Да и не нужна нам эта кнопка вообще низачем.

    А в 4cx/ оно было ровно так же покоцано изначально.

  • 26.10.2010: вылезла странная штука: почему-то в liucc stddlg-based SUBWIN с единственным содержимым LOGD_LIU_ADC200ME_ONE по нажатию на Esc НЕ ЗАКРЫВАЕТСЯ.

    26.10.2010: провел разборки -- оказалось страннейшее поведение, похожее на заскоки TaskManager'а в WinXP (там при исчезновении задачи, на которой стоял курсор в списке, Esc также перестаёт работать. И в Help'е аналогично.).

    А именно: поскольку те элементы создаются со свёрнутой панелью управления, то никаких сфокусированных виджетов НЕТ. И, видимо, оно в таких случаях вообще теряет keypress'ы.

    Причём:

    1. Если cpanel просто развернуть double-click'ом, то ничего еще не фокусируется, А вот если увести курсор мыши из окна и вернуть обратно -- то фокус отдаётся селектору "Timing" и всё становится окей.
    2. И -- ПОСЛЕ этого если свернуть cpanel обратно, то Esc всё равно продолжает отрабатываться, несмотря на вроде бы отсутствие сфокусированных виджетов!

    Рытьё по bugs.motifzone.net не дало ничего релевантного.

Xh_colors:
  • 31.07.2004: махинации с цветами -- COLOR_NNN, Xh_color.c.

    31.07.2004: наипоследнейшая потребность возникла при переводе программы "ipp" на Xh. Там требовалось некоторое количество цветов, а вводить туда код, преобразующий имя цвета в Pixel, когда такое уже есть в Xh, как-то странно. Плюс -- пора же, наконец, все эти цвета доунифицировать!

    Кроме того, еще энное время назад (в Бледе?) пришла в голову мысль: а что, если сделать цвета "палитрами"/"темами", чтобы их при желании можно было легко менять в одном месте, а уж это место пусть само добавляет в Xrm-БД соответствующие ресурсы. (Что-то не могу найти тогдашнюю запись этой идеи.)

    Итого, выбрана следующая стратегия:

    • Вводится энное количество enum-констант XH_COLOR_NNN (для гарантированной нумерации, вместо "постарайся-не-ошибиться-в-соответствии", использовавшейся ранее).
    • В #define'ах COLOR_NNN былые циферки заменяются на эти константы.
    • В Xh_colors.c заводится "база данных по цветам", содержащая 1)имя (стрингифицированную константу без префикса "XH_COLOR_"), 2)строку-определение цвета, 3)Pixel, изготавливаемый в XhAllocateColors().

      Определяются строки БД не линейно, а "[XH_COLOR_NNN]=...".

    • В этой БД содержатся ВСЕ цвета -- как присутствовавшие раньше, так и те, что жили в fallbacks, плюс, цвета для графиков/гистограмм.

      И вообще -- стараемся иметь ПОЛНУЮ палитру (помним уроки MultiEdit'а :-).

    • Для модификации палитры вводится интерфейсная функция XhSetColorBinding(name,defn), которая подменяет стандартное определение указанным.
    • (на чуть более дальнее будущее) Xh_colors будет сам из этой палитры дополнять Xrm-БД надлежащими ресурсами, а из fallbacks их можно будет удалить.

    Если программа захочет менять палитру, то это должно делаться ДО первого создания окна -- поскольку именно из 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().

    • Имеется мелкая проблема: при выставлении Xrm'ом ресурса типа "*background" уставка того же ресурса через "-xrm" перестает работать. А это неправильно -- у "-xrm" должен быть приоритет.

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

    • Из fallback'ов определения цветов пока не убраны.

    07.08.2004: несколькими часами позже:

    • полностью перевел цвета из Xh_fallbacks.h в 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: еще насчет цветов: стоило бы по образу и подобию SGI'ного Motif'а сделать все "нажимабельности" другим (более темным?) цветом.

    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: ага, "проапдейтил" тогда -- как же! Лучше надо смотреть на диагностику:

    [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
    
    Т.е. -- оно тогда просто не поставилось, из-за того, что надо синхронно обновлять openmotif и openmotif-devel.

    А вот при сборке из исходников -- проблемы пофиксены, всё хорошо расцвечивается.

    19.04.2011@Снежинск-каземат-11: поставил 2.3.1-5.el5_5.1 -- всё пофиксилось, ура!

  • 15.11.2004: черт возьми, давно пора найти SGI'шные формулы для вычисления теней и проч. из базового цвета, и перестать ходить на поклон к sky'ю!

    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: обнаружил "прикол": встроенные цвета почему-то всегда имели приоритет перед ресурсными (хоть из fallbacks, хоть от "-xrm".

    19.11.2004: резво разобрался: дело в том, что спецификации вида "*.nnn" попадают в БД в форме "*nnn", т.е. лишняя "." выкидывается. А в ResourceChecker()'е проверка делается НАШЕЙ, ВНУТРЕННЕЙ спецификации с той, что лежит в БД. И она, естественно, всегда не совпадала!

    Так что -- попеределывал defcoldb[] с "*.nnn" на "*nnn", и проблема исчезла.

  • 20.12.2004: требуется какой-нибудь свой цвет для "реперов" на графиках.

    20.12.2004: ввел новый код -- XH_COLOR_GRAPH_REPERS, он пока соответствует черному.

  • 25.12.2004: стало четко видно, что цвета GRAPH_BARn хороши для гистограмм, но, например, зеленый совершенно не годится для обычных графиков. Наверное, стоит ввести отдельные коды GRAPH_LINEn, причем -- можно не 3, а 4 или даже 5.

    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".

    Несколькими часами позже: сделал:

    1. XH_COLOR_GRAPH_LINE6: #EE82EE -- "Violet" в X11;
    2. XH_COLOR_GRAPH_LINE7: #006400 -- "Dark Green" в X11;
    3. XH_COLOR_GRAPH_LINE8: #B26818 -- то же, что и JUST_BROWN, VGA'шный цвет 6.

    С учётом ранее имевшегося цвета 5 "foggy lightcyan" получается как бы 2 группы цветов: первые 4 яркие и контрастные синий, красный, зеленый, золотой, а за ними еще 4 как бы менее ярких (или более бледных) их варианта (мутно-голубой, фиолетовый, темно-зеленый, коричневый).

    Проверил в Chl_histplot.c -- вылезла побочная неприятность: при таком количестве линий подписи к вертикальным осям уже фигово помещаются -- начинают налезать. Чередовать их как-то, что ли? Или писАть реже?

  • 27.12.2004: подсмотрел у Гусева -- вполне недурственно для каких-нибудь целей может подойти светло-бирюзово-голубой фон #88ddff: он и отличается от других -- легко замечается глазом, и красив, и контрастен со всеми foreground-цветами. (Женя его использует при отсутствии связи с блоком -- как наш DEFUNCT.)

    12.06.2005: а-а-а... Ну блин, давным-давно, 03.02.2005 уже заиспользовал примерно этот цвет, правда, почему-то в варианте "#c0e6e6" -- но отличие видно, только если их рядом ставить, а так -- один черт.

  • 05.05.2005: по опыту использования цветов графиков: есть некоторая неудовлетворенность текущим состоянием.

    Во-первых, черный цвет в качестве 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".

  • 07.03.2006: захотелось иметь "полный" интерфейс к цветам -- с возможностью добыть цвет по "названию", "название" цвета по индексу, а также узнать количество цветов.

    09.03.2006: сделал -- XhGetColorByName(), XhStrColindex(), XhColorCount().

  • 22.04.2006: еще давно обратил внимание -- в FC3/FC4 Xh/Chl-программы выглядят немного "криво": там "почему-то" все текстовые виджеты имеют белый фон. Энное время назад разобрался почему -- этот долбаный KDE (или Gnome?) уставляет экранные ресурсы (которые через xrdb), и в них указывается, что XmText -- белые, и много еще всякой лабуды.

    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* у большинства есть еще одна проблема -- что из-за русской локали в качестве десятичного разделителя используется запятая -- ',', а не точка ('.'). В результате все нахрен едет -- и если для просто чисел еще можно было бы с этим смириться, то у нас ведь в файлах частенько запятая работает как разделитель, например, пары чисел.

    Очевидные выводы:

    • Во-первых, постараться НИКОГДА не использовать запятую в качестве разделителя. Даже в widdepinfo/options -- лучше там обходиться точкой с запятой (';').

      (Хотя если вещественных чисел там нет, а только целые -- то и запятая будет безопасна.)

      Во многих случаях можно применять либо точку с запятой, либо слэш ('/'). А для указания диапазонов -- просто сам напрашивается минус ('-').

    • Во-вторых же, если все-таки припрет, то самый простой способ отключения запятой в числах -- это уставить $LC_NUMERIC, либо сказав
      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: Старостенко постоянно тычет, что кнопки-чекбоксы на тулбаре какие-то невнятные -- их нажатость плохозаметна. И что, мол, надо в нажатом состоянии делать совсем другим цветом -- как это в нынешнем Word'е (там -- оранжевый).

    18.07.2006: сделал -- ввел новый индекс цвета XH_COLOR_BG_TOOL_ARMED, ресурс-строку "*toolButton.armColor", смотрящюу на него, и сам цвет -- светлый зеленовато-голубой "#80ccf0".

  • 06.08.2006: круп-пные переделки в вцетах столбцов гистограмм (XH_COLOR_GRAPH_BARn).

    06.08.2006: некоторое время как стало ясно, что следует заменить ярко-зеленый цвет #00FF00 (   ) на более "мягкий" #00C080 (   ), опробованный на vacclient'е.

    А дополнительно вылезло -- что порядок-то цветов у нас странноватый: 1:красный, 2:синий, 3:зеленый. А следовало-то сделать бы наоборот, чтобы "раздражающий"/"опасный" красный использовался уж в самом крайнем случае.

    Посему -- переставил, так что теперь идут 1:зеленый, 2:синий, 3:красный. Да, будут проблемы со старыми ipp-подобными программами -- в т.ч. с самим work/pult/xmclients/ipp.c. Но что уж тут поделаешь -- все равно им скоро на покой...

    P.S. А там, где нужны КОНКРЕТНЫЕ цвета -- как синий и красный в ipp-спектрометре -- надо указывать именно эти цвета, а не условные индексы.

  • 11.09.2006: еще немного о цветах: появилась идея сделать "стандартные утилиты" в некоей другой цветовой гамме -- чтобы они визуально сразу отличались.

    11.09.2006: Варианты (учитывая использование зеленовато-серого в cx-starter'е) -- либо какой-то просто "другой", либо теплый желтоватый:

    • Cold bluish-gray #d0dbff. (Это кнопки в cx-starter'е.)
    • Желтовато-серый #f0f0d0.
    • Теплый желтый #f9eaa0. (Идея: желтый -- это R+G, а B поменьше; значит -- сбрасываем B, а к R и G добавляем примерно пропорционально, в соответствии с формулой I=R*0.30+G*0.59+B*0.11 -- R+=90/3, G+=90/6.)

    К конкретному решению пока не пришел, но, видимо -- теплый желтый #f9eaa0.

    10.11.2006: поскольку в adc200 "теплый желтый #f9eaa0" уже прижился, то его и считаем за стандарт. Остальные компоненты -- TopShadow:#fdf8e3, BotShadow:#bbb078, Armed:#fcf4d0.

    Кстати, тогда, в сентябре, Саша Александров, увидев adc200 в этом цвете, спросил -- "это что, EPICS?". Оказывается, у них в SNS такой цветовой стандарт: все эпиксности -- желтые.

    А так -- "done".

  • 10.11.2006: уже давно назревали мысли, что стоило бы кроме варианта "светлый фон, темные/цветные линии" иметь и классический "темный фон, светлые линии":
    • 30.03.2005@CAN-expo-2005: Для "осциллографоподобных" (adc200.c, adc333.c) можно использовать Tek/LeCroy-подобные цвета -- белые буквы на темном серо-голубом фоне.
    • 25.10.2006@PCaPAC-2006: Добавить в adc200 режим "black" (только ключом).

      А в Xh_colors -- еще блок цветов (a-la Tek/Agilent).

      И в adc200 иметь массив n2colidx[2][n], с которого и отводить цвета.

    10.11.2006: в Xh_colors оное сделал. В дополнение к XH_COLOR_GRAPH_NNN теперь есть и XH_COLOR_BGRAPH_NNN.

  • 11.03.2007: сходу -- ввел константы XH_NUM_DISTINCT_LINE_COLORS и XH_NUM_DISTINCT_BAR_COLORS.
  • 10.04.2007: имеется некоторая методическая недоработка: при наличии кучи разных XH_COLOR_JUST_NNN у нас НЕТУ XH_COLOR_JUST_BLACK.

    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.

  • 04.03.2009: давно пора удалить старые #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: поднадоело, что cx/.../Xh_colors.c и 4cx/.../Xh_colors.c отличаются, причем сущими мелочами.

    10.03.2009: поскольку "более правильно" всё сделано в 4cx/, то берем за основу его. Добавляем в Xh_types.h строчку

    typedef XhWidget CxWidget;
    и теперь можно спокойнейше юзать исходники от более новой версии, что и сделано.

    10.03.2009: кстати, конкретно для Xh_colors.c тот typedef совсем не нужен, ибо там теперь в _XhAllocateColors() вообще передается просто Widget. А CxWidget на ДАННЫЙ момент не используется нигде. Но сейчас начнет :-).

Xh_utils:
  • 25.07.2006: случайно заметил -- в 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.

  • 02.03.2007: новая функция -- XhProcessPendingXEvents(), содержащая
        while (XtAppPending(context) & XtIMXEvent)
            XtAppProcessEvent(context, XtIMXEvent);
    

    Т.е., она обрабатывает все накопившиеся события от X-сервера.

    02.03.2007: история такова:

    • Несколько дней назад заметилось, что linipp на пульту почему-то начинает дико тормозить с отработкой ввода (нажатия кнопок и мыши, tooltip'ы) -- иногда, если система (или только X-сервер?) под сильной загрузкой. А вот отрисовка данных -- очень даже идет! Причем происходило это ТОЛЬКО на пультовых машинах -- на viper это сымитировать не удалось, как я ее (и X-сервер, при помощи "xlock -inwindow -mode goop") не грузил.
    • Идеи были -- что-то там такое с синхронизацией, и не делать ли XSync() после отрисовок с DrawingArea? Хотя запуск с ключом "-sync" уже капитально ухудшал ситуацию.
    • И тут -- вчера пришел Гусев и пожаловался на что-то подобное. Идею про синхронизацию откинули -- явно не то. И тут осенило -- это дополнительные дескрипторы -- которые по XtAppAddInput().
    • Я как-то разбирался с работой XtAppNextEvent() (см. "Xt обнаруживает истекшие таймауты и дергает их"), и тогда заметил глюкавость -- что у таймаутов безусловный приоритет, и до X-событий дело долго так и не доходит.
    • Так вот -- тут то же самое: данные от cx-сервера приходят чаще, чем программа с X-сервером успевают выполнить отрисовку, и потому Xt почти постоянно отрабатывает только доп. дескрипторы.
    • А Гусев с таким уже сталкивался раньше -- у него при вычитывании CAMAC-видеокамеры через ППИ, чтоб интерфейс не замерзал, использовалась функция common.c::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()?

  • 11.04.2007: обнаружился еще один багец в Motif'е, проявляющийся при использовании 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: захотелось иметь возможность использовать уменьшенные вдвое пиктограммы -- для mini-toolbar'а.

    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'е есть.

  • 02.05.2007: перетащил в Xh_utils.c, в дополнение к уже имеющемуся 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.

    Что и сделано. Итак:

    • Оно теперь прёт вверх по дереву, и всем, чей содержатель XmForm, делает такую коррекцию.

      (Заодно, кстати, был исправлен изначальный ляп, что при вызове функции для topmost-widget, у которого XtParent()==NULL, оно бы падало по SIGSEGV -- теперь проверка делается.)

    • Вызывать же XhAdjustPreferredSizeInForm() надо для всех собственно подверженных ресайзингу виджетов -- т.е., graph, axis, scroller (что во все ныне существующие графикопрограммы и вставлено).

    01.04.2010: после тех изменений стало всё хорошо в *adc*, но начались проблемы в программах с LOGD_HISTPLOT -- weldcc и kuznitsa: графики в них имели размер типа 1*1.

    1. Вставил в функцию выдачу размеров встречающихся при путешествии вверх по дереву виджетов -- и вылезла интересная особенность: resize вызывался и просто где-то между созданием и появлением окна, и иногда попадался размер 0*0!
    2. Окей -- вставил guard, что НЕ прописывать в preferred_{width,height}, если какой-то из размеров ==0.
    3. Не помогло.
    4. Но -- обратил внимание, что иногда при этом преждевременном resize встречается также размер 1*1. А это, как известно -- НАЧАЛЬНЫЙ/УМОЛЧАТЕЛЬНЫЙ размер всех виджетов.
    5. Так что вставил доп.условие -- НЕ прописывать при w==1&&h==1.
    6. А вот это -- помогло! Всё пришло в чувство.
    7. (Приколу с не-работающим show=NNN при NNN<100 это, правда, не помогло. Но там требуется отдельное разбирательство.)
  • 27.03.2008: возникло желание заиметь XhAssignPixmapFromFile() -- чтобы назначала метке/кнопке пиктограмму аналогично простой XhAssignPixmap(), т.е., с корректной трансляцией "прозрачных" цветов none/#00FFFF и с добавлением при надобности armPixmap'а.

    27.03.2008: сделано. Поскольку

    1. в изначальной XhAssignPixmap() уже было два практически идентичных куска (назначение обычной и armed пиктограмм), а
    2. функции XpmCreatePixmapFromData() и XpmReadFileToPixmap() имеют одинаковые списки аргументов (только 3-й у первой указывает на массив строковых данных в памяти, а у второй -- на имя файла),
    то этот самый общий кусок был вынут в отдельную общую функцию DoAssignPix(), коей передается указатель на функцию "добычи" (getter) и void*'й тот самый 3-й аргумент.

    XhAssignPixmap() стала коротюсенькой.

    XhAssignPixmapFromFile() же отличается от своей предшественницы тем, что к переданной строке добавляет ".xpm".

    02.04.2008: поскольку добавление ".xpm" там было явно не к месту, то оно убрано -- теперь клиент обязан предоставить полный путь к файлу.

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

    Несколькими минутами позже: да, так и сделал, для ОБЕИХ функций. Старый, ранее за-#if0'енный вариант, удален.

    "done".

  • 03.04.2009: добавилась новая функция -- XhSwitchContentFoldedness(). Сюда она переехала из adc200.c, и используется и там, а теперь и в Chl_gui.c.

    4cx/ too.

  • 28.10.2009: грохаю давно (или вообще никогда?) никем не используемую XhStrDup().
  • 28.10.2009: в порядке подготовки к унификации с 4cx/: поменял везде XhWidget на CxWidget.

    И собственно унификация сделана -- теперь файлы идентичны.

  • 30.10.2009: новая пара функций-скобок -- XhENTER_FAILABLE() и XhLEAVE_FAILABLE().

    30.10.2009: перетащены из cx-starter.c, с несколькими модификациями:

    1. XhLEAVE_FAILABLE() возвращает "статус" -- 0 если ошибки не было, и 1 -- если была.
    2. Теперь вначале сохраняется текущий handler, а в конце он возвращается на место (а не NULL).
    3. В cx-starter'овом варианте был идиотский ляп -- снятие hadnler'а делалось при may_fail==1, а не ==0, так что хбз почему оно вообще не падало (видимо, BadWindow образовывались на более глубоких уровнях).
    4. И еще в cx-starter'е в поиске по окнам использовалась сама переменная was_X_error прямо сразу, для проверки не обломилась ли XGetWindowProperty(). Переделал по-правильному -- используется прямо её результат (Success/!=Success).
  • 31.10.2009: для унификации с 4cx/ компания XhText{SetCursorCallback,ExtractDoubleValue,ExtractIntValue} перенесена обратно в libKnobs -- доступны через KnobsI.h. И префикс "XhText" убран -- так что теперь всё вернулось к ситуации почти 6-летней давности.
Xh_window:
  • 08.09.2006: возникло желание уметь ограничивать размер resizable-окна. Т.е. -- надо махинировать со всякими XmN{min,max}{Width,Height}.

    08.09.2006: наиболее правильным местом для этой функциональности является Xh_window. Потому туда добавлены две новые функции: XhGetWindowSize() (берет размер от mainForm) и XhSetWindowLimits().

    Наличествующие проблемы/тонкости:

    • Идея -- чтобы при помощи XhGetWindowSize() программа могла узнать получившийся по умолчанию размер окна, и разрешить добавлять к нему сколько-то.
    • Размер же становится известен после realize, т.е. -- реально после отображения окна (до того момента размер -- 0*0).
    • Но: при попытке уставить нужные ресурсы идет работа с 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 это, наверное, и описано, но туда, во-первых, мало кто вообще заглядывает, да и утонуть там можно...)

  • 04.05.2008: в window-manager'е в CentOS-5.1 (надо посмотреть, что там за GUI и WM) напрочь игнорируется параметр "noresize" -- тянется напрочь. В чем, интересно, дело -- extended wm hints ему нужно, что ли?

    (Кстати, оно там вообще олигофрен -- ключик "-iconic" у xterm'а также игнорирует.)

    17.10.2009@ТНК: это игнорирование "-iconic" -- известный баг window-manager'а Metacity, RedHat bugs #215175, #215759.

    Насчет resize -- точно не знаю, но есть аналогичный bugreport #188576 "Doesn't follow Xt's size hints".

Xh_toolbar:
  • 22.09.2006: позорище -- XhSetCommandOnOff() и XhSetCommandEnabled() кидались в бой, даже не проверяя, а есть ли toolbar вообще! Результат -- SIGSEGV...

    22.09.2006: исправил. долго ль было вставить проверку про window->toolHolder==NULL.

  • 07.05.2007: (еще на прошлой неделе) возникла потребность делать и "другие" тулбары, а в частности -- тулбары из простеньких кнопочек (для adc200), но чтоб они функционировали также -- и чтоб команды генерили, и чтоб зажимались, и чтоб дисэйблились.

    07.05.2007: ежу понятно -- надо вместо нынешних махинаций с xmNchildren toolHolder'а вводить некий per-XhWindow список, в который добавлять все кнопочки. И тогда функции создания "альтернативных" тулбаров будут просто так же добавлять кнопки в этот список, и эти кнопки далее ничем не будут отличаться от обычных.

  • 01.04.2010: введен новый тип "кнопки" -- XhACT_LABEL, макрос XhXXX_TOOLLABEL(lab). Служит для создания в тулбаре крупных меток-надписей. Потребность такая возникла у fastadc_common-based программ: они шибко похоже выглядят, так что чтоб различались.

    06.09.2010: хм -- тогда сделал только в liu/xmclients/'ных программах, а в src/programs/xmclients/'ных -- забыл.

    Сейчас добавил и в весь тот зоопарк.

  • 06.09.2010: понадобился еще один "тип" -- Xh_ACT_NOP. Смысл -- чтобы программа могла грохать часть кнопок из заранее сделанного массива.

    06.09.2010: Реализовано. Всё несложно, только пришлось не просто пустой селектор в switch() вставить, а еще count-- -- т.к. оно в конце цикла безусловным порядком делает count++, и всё бы съезжало.

    А макрос делать не стал, ибо незачем.

Xh_cxscheduler:
  • 07.05.2007: пришла пора делать "адаптер", имитирующий API cxscheduler'а через Xt, чтобы можно было использовать fdiolib в графических программах.

    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".

  • 30.12.2014@пешком-мимо-бассейна-Юность-по-пути-в-Амиго: не обобщить ли Xh_cxscheduler в "Xt_cxscheduler", чтоб можно было его сделать публично-доступным, вместе с cxscheduler (и Qcxscheduler)?

    Всё, что удерживает от этого -- использование xh_context. Достаточно заменить фиксированные ссылки на него на некую переменную, чьё значение клиентам указывать отдельным вызовом, ПОСЛЕ инициализации Xh и еще ДО любых прочих действий.

    04.01.2015: угу, указывать надо сразу после вызова XhInitApplication().

    Вот только мест таких -- слегка дофига (как в v2, так и в v4).

Xh_hilite:
  • 06.02.2008: сделан новый модуль -- для рисования зеленой рамки "выделенности" вокруг некоего виджета (реально -- ручки).

    07.02.2008: собственно, оно сделано -- очень легко и быстро. Технология -- 4 виджета (класс Core), образующие стороны рамки, приаттачиваются в такие координаты в mainForm, чтобы обрамлять указанный виджет. Единственное что -- неприятной была возня с XtTranslateCoords(), которая непроходимо неадекватна, в отличие от XTranslateCoordinates().

    Поскольку stacking order виджетов -- "кто раньше создан, тот и выше", то XhCreateHilite() вызывается из XhCreateWindow() сразу же после создания mainForm; иначе пришлось бы -- при создании при-первом-использовании -- выпендриваться с mainForm'овым XmNinsertPosition.

    И еще легкая халява -- поскольку прямоугольничек привязывается к mainForm, а не к самому виджету, то при перетряхивании (например, при сворачивании/разворачивании колонок или элементов) виджет съезжает, а прямоугольничек остается на месте. Но, увы, человеческого решения для этой проблемы нету -- поскольку виджет может располагаться внутри произвольного менеджера, чаще всего -- сетки. Неприятность снижается тем, что XhShowHilite() вызывается при КАЖДОМ обновлении ki->usertime, так что при нажатии следующей же кнопки прямоугольничек прыгает в правильное место.

  • 25.03.2009: а ведь есть халтурка -- XhShowHilite() и XhHideHilite() не проверяют переданный им виджет на !=NULL...

    25.03.2009: да, сделал проверку -- для этого теперь еще и window = XhWindowOf(w) делается не в декларации, а дальше, после проверки.

  • 24.08.2009: еще проблемка: ведь эта штука работает ТОЛЬКО в основном окне, в под-окнах же (ELEM_SUBWIN, создаваемых через XhCreateStdDlg()) -- нет, и при данной схеме -- В ПРИНЦИПЕ не могут, ибо привязаны к самому окну.

    (И, кстати, фиг знает, почему она ПРОСТО НЕ ПОКАЗЫВАЕТСЯ, а не отображается, например, в бредовом месте основного окна.)

    23.07.2010: продолжение этой же темы:

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

      А сегодня понял -- это потому, что она пытается вывести hilite ровно в том месте mainForm'а, над которым располагается "обрамляемый" XmText; а оный лежит на экране за пределами основного окна -- вот оно и подгоняет размер mainForm'а так, чтобы типа "вместить" hilite.

    • И -- теперь-то ясно, "почему она ПРОСТО НЕ ПОКАЗЫВАЕТСЯ, а не отображается, например, в бредовом месте основного окна": она именно ПОКАЗЫВАЕТСЯ, причем В ПРАВИЛЬНОМ МЕСТЕ основного окна -- просто это ровно над этим местом расположено под-окно, закрывающее сей прямоугольник.
    1. По-хорошему, конечно, надо переводить Xh_hilite на более корректную схему -- с использованием OverrideRedirect-окон (или, возможно, через OverrideShell).
    2. А по минимуму -- хотя бы не отображать hilite в случае, если вызвавший виджет НЕ относится к иерархии, идущей от mainForm.

    Поскольку ни в Xt, ни в Motif'е не нашлось способа определить "является ли виджет Y иерархическим потомком виджета X", то просто была взята почти копия функции Xh_utils.c::IsAncestorOf(), с легкой адаптацией с Window на Widget, и названа IsAncestorWidgetOf().

    Засим подрыгивание основного окна исчезло.

  • 14.06.2014: да, НАДО переходить на OverrideShell -- благо, опыт в cx-starter.c есть.

    15.06.2014: вроде сделано -- по образцу с cx-starter'а, но работает кривовато.

    • Появление в нужном месте -- да, окей.
    • А вот дальше с фокусом косяки: у клиента-XmText'а, похоже, НЕ вызывается losingFocusCallback...

    Но XmLiteClue-то ведь работает, значит как-то можно. Как?

    ...пока оставляем отключенным по #define USE_OVERRIDE 0.

    28.06.2014: еще немножко поразбирался -- странно:

    1. В popup-окнах оно НЕ глючит -- всё работает ровно как надо.
    2. В основном окне, похоже, ЛИШНИЙ раз вызывается losingFocusCallback ПРИ ПОЯВЛЕНИИ hilite'а: в свежезапущенных программах, не приконнекченных к серверу, в пустое поле при начале редактирования вписывается "0.00...".

    Типа протокола дальнейших разборок:

    • Сравнил editres'ом ресурсы с XmLiteClue: различие было только в XmNsensitive, которое тут тоже сделал :=1 -- один чёрт, не помогло.
    • Получасом позже: приподразобрался -- помог анализ XmLiteClue.c: конкретно popup-shell'ы надо появлять/прятать не при помощи XtManageChild()/XtUnmanageChild(), а через XtPopup()/XtPopDown()!

      Вот же ж чёртовы хитрости Motif'а, нет бы всё сделать унифицированно...!!!

    • После этого глюки с фокусом пропали. Ура!!!
    • Появился другой прикольчик: время от времени -- например, при нажатии MB3 на другой ручке -- hilite показывается в каком-нибудь бредовом месте. Ктоб его туда вызывал, почему вообще показывается?

      Еще пятью минутами позже разобрался: это прикол конкретно нового Chl_knobprops.c, с RANGES_INLINE -- оно делает XhShowHilite() в своём modifyVerifyCallback'е, коий вызывается при прописывании значений в поля.

      Так что просто убрано использование hilite в Chl_knobprops (раз пока не умеет себя вести :D).

    • И сделано
      #define USE_OVERRIDE 1
      -- т.е., переходим на новую версию.
Xh_plot:
  • 23.04.2008: еще давным-давно зрела потребность иметь "компонент-график" -- который умел бы рисовать энное число графиков, корректно бы ресайзился, имел/поддерживал бы scrollbar, и был бы расширябельным (для всяких вещей типа обработки событий мыши, реперов, и прочих специализированных фич). Т.е., умел бы делать то, чем страдают все программы nadc*.c и специализированные программы, включающие отображение графиков (sukhphase, tantal, liu*).

    Можно было бы поместить его в отдельную библиотеку, но, по аналогии с сеткой -- пусть будет модулем в Xh'е.

    05.05.2008: кстати, надо бы битым текстом прописать пожелания/требования к этому компоненту:

    • 3 типа графиков --
      1. Y_OF_X -- обычные графики y(x=0...n), для разных там adc200.
      2. X_Y -- задается ПАРА массивов, координаты каждой точки (x,y); для приложений типа tantal'а.
      3. Y_OF_T -- график бегущий справа налево, для самописцев.
    • Scrollbar опционален -- может быть, а может и отсутствовать.
    • Реакция на resize: график может быть "оконным/viewport'ным", как ныне у adc*, а может быть "растягиваемым" (подразумевает отсутствие scrollbar'а)?
    • (23.05.2008) еще -- по каждой оси отдельно должно конфигуриться, как рисовать подписи: индивидуально для каждого графика, или же одни для всех (актуально, если все графики реально имеют один масштаб, и незачем загромождать экран повторяющимися числами). "Тип" же лишь уставляет умолчания, которые могут меняться параметрами.

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

    08.05.2008: в продолжение --

    1. Перво-наперво -- надо уровнять в правах Y с X'ом. Для чего ввести еще и тип X_OF_Y. Вопрос только, как рисовать много подписей к оси X -- видимо, друг под/над дружкой.
    2. Соответственно, отношение к ресайзингу по каждой координате также указывается отдельно -- как по X, так и по Y может быть как viewport, так и stretchable; и либо иметь scrollbar, либо не иметь.
    3. Да, надо железно отделять отрисовку от ресайзинга. ПОЛНОСТЬЮ. Так что --
      • Отрисовка просто пользуется текущими диапазонами [xmin,xmax] и [ymin,ymax], приводя их к экранным координатам при помощи RESCALE_VALUE(), а также, при возможности, рисуя только то, что попадает в эти диапазоны.
      • Блок ресайзинга же отвечает за уставление этих диапазонов, в корелляции с экранными -- в зависимости от затребованного типа ресайзинга.

        Отдельная проблема тут -- что viewport-type график может иметь размер, превышающий наличествующий объем (например, ширина ADC -- 500px, а намеряно 100 точек). Что делать?

        1. Использовать какой-то более хитрый вариант RESCALE_VALUE()?
        2. Заставить код ресайзинга умничать и махинировать с {x,y}{min,max}, подтасовывая их для получения нужного результата?
        3. Иметь раздельные диапазоны -- ЛОГИЧЕСКИЕ, определяемые в первую очередь программой, и ДЛЯ-ЭКРАННЫЕ, используемые для пересчета?
        Более адекватными выглядят 1-й и 3-й варианты. (Вспоминаем, вспоминаем тот графический редактор на БК-0010, и урок, полученный на нем -- надо стараться использовать наиболее натуральные системы координат!)
      • Доп. вопрос о ресайзинге -- что графиков-то не один, так что и "пределов" также несколько -- на ком базироваться? Или тут случай в стиле Шекли?
    4. И как бы еще отдельный блок -- уставка размеров "полей" для подписей, в зависимости от форматов и чисел. А вот ЭТА уставка делается лишь при инициализации и добавлении графика (или его программной модификации) -- так что изменение размера (и вплывание в отображаемую область бОльших чисел) на эти поля не влияет.
    5. Плюс, естественно, с этими пересчетами -- будет доп. API для расширяемости, чтоб программа-юзер не занималась этими пересчетами. Так что ей и в событиях от мыши будут передаваться и координаты, пересчитанные в логические, и будут предоставляться функции для пересчеты в экранные -- ежели она захочет сама рисовать, например, реперы.

    P.S. Да, кстати, по умолчанию все буфера будут считаться кольцевыми, чтобы легко делалась работа с "историей". Лишняя арифметика при этом пренебрежимо тривиальна, а параметры ring_start и ring_size будут по умолчанию уставляться в совместимые с не-кольцевыми буферами значения.

    13.05.2008: еще парочка пожеланий, по опыту имеющихся программ:

    1. (impacis10) возможность требовать отрисовки не всего графика, а только конца (в пределе -- произвольной части). Нужно для ускорения -- там самописец работает с большой скоростью, так что данные на глазах "добавляются", а постоянно перерисовывать весь график -- моргать будет.
    2. (tantal) позможность разные части графиков рисовать разными цветами (там цвет точки зависит от того, в какую сторону идем).

    Как это можно было бы реализовать --

    1. Просто ввести вызов "отрисуй точки с N по M".
    2. Иметь доп. флаг у каждого графика -- "многоцветный", и при каждой точке кроме координат -- еще и тэг (т.е. -- плюс к массивам координат еще массив тэгов). А тэг -- это индекс в per-plot-массиве GC. И, для продолжения оптимизации по 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".

  • 18.04.2010: вследствие последних событий с графиковыми программами стало очевидно: именно из-за отсутствия внятной модели и не удалось тогда его хоть сколько-то продвинуть -- имевшаяся от histplot+fastadc просто недоадекватна.
  • 17.12.2010@Снежинск-каземат-11: всё-таки пора делать общий более-менее вменяемый компонент для отрисовки графиков.

    17.12.2010@Снежинск-каземат-11(записано нигде не было, токмо мысли, так что записываю их 10.01.2010): некоторые примерно-приемлемые достижения в графикостроительстве были получены при реализации fastadc-графиков в liu/fastadc/.

    • Надо отделить ДАННЫЕ, описывающие графики (добываемые, например, из fast-АЦП) от компонента ОТРИСОВКИ.

      Т.е., отдельно:

      1. Например, plotdata.h -- который прекрасно может использоваться (для заполнения данных) и не-GUI-кодом
      2. Собственно Xh_plot, использующий тот plotdata.h.
    • На навороченность вариантов графиков -- X_Y, Y_OF_T -- пока можно и забить. Самое важное -- всё же Y_OF_X.
    • Зато требуются РАЗНЫЕ ТИПЫ ДАННЫХ, а не только int/int32 -- в первую очередь, вещественные (поскольку именно они получаются в результате калибровки, плюс в результате суммирования кучи графиков с разными калибровочными коэффициентами).
    • Общий подход -- как в той попытке 2008 года: для компонента даётся МАССИВ графиков.

      Отличие -- КАЖДЫЙ график может быть своего отдельного типа.

    • Одна из задач GUI-обвязки -- отображать значения в реперах и под курсором, с учётом этого самого типа.
    • Каждому графику может указываться "для отрисовки вызвать user-supplied callback" -- это нужно для ЛИУ'шных "все подряд графики...".
  • 17.12.2010@Снежинск-каземат-11: приступаем, для начала -- в "песочнице" liu/plot/, а как добъём -- переедет в основное дерево.

    17.12.2010@Снежинск-каземат-11: начинаем начинять plotdata.h.

    21.12.2010@Снежинск-каземат-11: на тему корректного изменения размера (пока что -- по опыту liu/fastadc/).

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

    • Проблема в том, что при увеличении полей оно увеличивает размер формы, а при уменьшении -- оставляет тот же размер, увеличивая размер графика на размер "избытка".
    • Т.о., при каждом увеличении полей общий размер растёт, но он никогда не уменьшается. В итоге оно всё время потихоньку увеличивается, совсем не уменьшаясь.
    • Возможно, стоит при пересчёте стараться оставить тот же размер графика. ПОПРОБОВАЛ: фигушки. На попытки ставить после изменения margin'ов тот же размер графика оно плюёт. На попытки соответственно корректировать размер формы (в минус) -- тоже (хотя, по ощущениям, вроде увеличивается медленнее? По крайней мере, иногда форма уменьшается).
    • Возможно, надо пытаться ставить размеры ВСЕМ растягиваемым виджетам -- включая axis и scrollbar?

    23.12.2010@Снежинск-каземат-11: идейка, как можно при скроллинге графика "сдвигать" фазу чёрточек на сетке, не лазя при этом в GC.dash_offset: можно ставить начальную координату линии "левее", в МИНУС-сколько-надо -- при этом лишняя часть просто не нарисуется (отсечётся), а фаза -- прокрутится. 31.01.2011@Снежинск-каземат-11: сделал -- работает!

    29.01.2011@поезд-туда:

    • НАДО делать график И со скейлингом по границам ("резиночка"), И со скроллбарами.
    • Поэтому постараться ограничить прямой доступ клиентов к рисованию до минимума - пусть вся отрисовка (и подсчёт границ!) производится методами компонента.
    • Проблема с множественными графиками:
      1. Рисование для "ALL" надо вызывать для множественных графиков, а обсчёт "границ" -- по как бы одному (максимумы всех?)
      2. Как вообще от >1 РАЗНОРОДНЫХ графиков делать "обсчёт"? ОТВЕТ: Видимо, по приведённым к граф.координатам значениям.
    • Наличие/отсутствие скроллбаров указывать psp-опциями hscroll/nohscroll, vscroll/novscroll.
    • GUI-компоненту "plot" все режимы передаются через структурку "plotopts_t", в которую и деется psp'инг.

    31.01.2011@Снежинск-каземат-11: кстати, о реперах: можно приподвыпендриться и сделать, чтобы при уставке/передвижении реперов еще и курсорчик менял бы форму. А именно:

    • Для вертикальных реперов (по умолчанию) -- XC_sb_up_arrow.
    • Для горизонтальных (при нажатом Ctrl) -- XC_sb_left_arrow.
    • При "выделении «резиночкой»" (Shift?) -- XC_tcross.

    Главный вопрос тут -- выбор момента смены курсора. Ведь даже если курсор стоит на графике, то сфокусирован совсем другой виджет, и KeyPress-event НЕ попадёт графику. Так что:

    1. Смену курсора можно производить уже "задним числом" -- по началу действия, вызываемого кнопкой мыши.
    2. "Отключать" курсор -- по ButtonRelease.
    3. А курсором-по-умолчанию можно вообще всегда держать вертикально-реперов XC_sb_up_arrow.

    04.02.2011@Снежинск-каземат-11: вот получается, что мы весь этот набор заклинаний -- в {Graph,Axis}{Exposure,Resize}CB() -- тащим уже в очередной новый файл; а ведь старые вопросы (включая проблемы с изменением размера не только в плюс, но и в минус) еще не решены до конца.

    • А может ПОЛНОСТЬЮ ОТДЕЛИТЬ стандартные махинации с "отображающей поверхностью" от специфики графиков? Т.е., чтоб некий модуль "Scrollable Graph Area" занимался исключительно взаимодействием с GUI/Motif/Xt -- созданием виджетов и реакцией на события Expose и Resize, плюс поддержкой scrollbar'ов и т.п.
    • Т.е., по факту -- делаем как бы "мультивиджет" (типа диалоговых окошек), предоставляющий функционал "скроллибельная поверхность/viewport для рисования".

      Тем самым мы действуем в рамках ООП -- инкапсулируем чётко ограниченный функционал, не примешивая туда никаких графиков, а уж графики как бы "наследуют" этот "класс".

    • И его struct будет открытой/публичной.

    Вопрос, конечно, не перекрывается ли этот "мультивиджет" с 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".

  • 22.12.2011@Снежинск-каземат-11: Сильно бесит, что при переключении горизонтального масштаба (CMPR) оно просто оставляет то же положение horzbar'а (т.е. -- vprt.horz_offset).

    А можем мы просто постараться сделать так, чтобы точка, ДО изменения масштаба находившаяся в центре, и ПОСЛЕ также оставалась бы в центре?

    Т.е., в XhPlotSetCmpr() также уставлять и horz_offset (для чего надобен бы какой-нить viewport-API), а если оно будет "за границей", то XhViewportSetHorzbarParams() "впишет" его в разумные пределы.

  • 15.08.2012: вылезла неприятность с nadc200, у которого диапазон [0,255] (а не [-nnn,+mmm], как у прочих): толком не работает вертикальное масштабирование. Поскольку там 0 внизу, то оно оттуда и домножает -- в результате значение в "истинном нуле" уже при x2 улетает за верхнюю границу.

    Что странно -- подписи к вертикальной оси при этом масштабируются нормально.

    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'у.

    05.03.2013: да, внутренняя реализация сделана аналогично histplot'у, а выбор режима -- XhPlotSetLineMode().

    Для связанной фичи -- возможности размещать нечто "поверх" графика, в обеспечиваемом viewport'ом контейнере angle -- введена XhAngleOfPlot().

    В fastadc_gui.c использование вставлено -- создаётся переключатель режима отображения. Плохо лишь, что он "обычного" размера, а "small" choicebs'у указать нельзя никак.

    Всё вместе работает.

    28.05.2013: поскольку возможность указывать "size=small" появилась, то добавлено.

  • 02.04.2014@Снежинск-каземат-11: возникла потребность ИНВЕРТИРОВАТЬ сигнал АЦП -- именно ОТОБРАЖЕНИЕ на экране, а не просто коэффициенты ставить (coeffXX=-1).

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

    А в Xh_plot вот сходу красивого быстрого решения не видно...

    02.04.2014@Снежинск-каземат-11: (часом позже) результат разборок и анализа: вертикальное масштабирование в конечном итоге сводится в параметр magn у функции-рисовальщика (в основном XhPlotOneDraw()), содержащий прямо коэффициент.

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

    А собственно параметр (у fastadc) назвать straight/inverse.

    Делаем:

    1. Xh_plot:
      • Поле one_plot_t.plrt ("polarity").
      • Метод-установщик XhPlotOneSetInvp(): invp==0 => plrt=+1 else plrt=-1, и в XhCreatePlot() инициализация =+1.
      • Использование -- magn*plrt вместо старого просто magn -- в DrawPlotView() и DrawPlotAxis().
    2. fastadc:
      • Поле fastadc_dcnv_one_t.invp.
      • perchan_params[]: добавлены ключики "straight" и "inverse".
      • ctl_kind FASTADC_GUI_CTL_LINE_INVP.
      • Замечено: нынешние дуплет-массивы ChnSwch_prm[] и Magn_prm[] используются для одного и того же -- в них пишутся {gui,nl}.

        Поэтому вместо введения еще одного (Plrt_prm[]) оба старых сведены в один Line_prm[] (пишет туда любой из троицы, но пишет одно и то же).

    Проверено (всё, кроме экранной ручки, никем не создаваемой) -- работает.

  • 14.05.2014: пофиксен ГИГАНТСКИЙ баг в Xh_plot.c с отображением длинных чисел.

    14.05.2014: подробности:

    • После обновления на ЛИУ-2@Снежинск-каземат-11 софта (на pzframe-based xmclients/) программа liu повадилась время от времени падать. Причём плохопредсказуемо -- в какой-то непонятный момент, при неясных условиях, хотя и всегда при проведении мышью по графику manyadcs (что дало основания -- оправдавшиеся! -- подозревать именно новый manyadcs).
    • Попытки отловить проблему под gdb ничего не дали -- стек оказывался фиг знает где, так что "bt" не имела смысла. Попытки прогона под valgrind тоже ничего не принесли. Гонялось (уже в ИЯФе) под несколькими разными ОС -- безрезультатно.
    • А потом случайно поймалось прямо на viper/RH73 -- детали см. в 201403-SNEZHINSK-ACTIVITY.txt за 14-05-2014.

      Вкратце -- оно для отображения в statusline пыталось печатать огромное (длинное) число в буфер char[100], которое его переполняло, перетирая стек. Ведь double может быть до e308, что в формате "%8.3f" даст 308+ символов.

      (Да, проблема проявлялась из-за ошибки в manyadcs_knobplugin.c -- вместо double-данных брался мусор, но библиотеки ж должны защищаться от переполнения сторонними non-trusted данными.)

    • Выполнен комплекс мер для исправления позора:
      1. Тупой sprintf() заменён на snprintf(), причём местами с проверкой результата (в смысле переполнения).
      2. Добавление результатов в общий буфер -- для XhMakeTempMessage() -- делается интеллектуально, с проверкой "а осталось ли еще место в буфере".
      3. Буфера увеличены до 400 символов.

      Это сделано как в отображении для statusline, так и для реперов в pzframe/fastadc_gui.c, и еще в нескольких похожих местах.

    21.05.2014@Снежинск-каземат-11: за вчера-сегодня доделан перевод всего и вся на snprintf_dbl_trim().

  • 19.09.2014: в дополнение к свежевведённому параметру nofold оказалось, что параметр noscrollbar не работает (а он когда-то работал? Хотя бы в старых версиях? В Xh_viewport'е-то оно было -- флаги XH_VIEWPORT_HORZBAR и XH_VIEWPORT_VERTBAR).

    Стал делать -- и тут началось...

    19.09.2014: оказалось, что НЕТ способа указать XhCreatePlot()'у какие-либо параметры. Даже "nofold" делался извратно -- подселялся к cpanel_loc.

    Стало ясно, что надо переделывать cpanel_loc в более общий options. Что и было сделано.

    Нововведённый флаг получил название XH_PLOT_NO_SCROLLBAR_MASK.

  • 03.10.2014: придётся-таки реализовывать растяжение в дополнение к сжатию (CMPR). Корепанов ну очень уж истерично-гуманитарщически требует; вменяемо объяснить не может, но по косвенным признакам ясно, что ему оно надо чтоб "лучше видеть полочку".

    Хоть информация при растяжении не добавляется, но отрисовка линий между соседними x'ами действительно даёт не очень чёткую картинку.

    Just for record: еще давно решено, что

    1. Реализация -- везде вместо просто "/x_cmpr" делается "*x_xpnd/x_cmpr".
    2. Растяжение будет указываться отрицательными кодами fastadc_data_cmpr_factors[], и тогда растяжение делается в -N раз.
    3. Соответственно, если factor>0 -- x_cmpr=factor,x_xpnd=1 иначе x_cmpr=1,x_xpnd=-factor.

    07.10.2014@Снежинск-каземат-11: да, и Ярик Куленко подтверждает про "полочку".

    30.07.2015: в основном сделано, по тому самому проекту.

    • Умножение+деление делается через scale32via64().
    • В основном всё просто, но «"хитрый" алгоритм компрессии ... инкрементирует x по алгоритму ЦДА: каждый x_cmpr'ный раз» (201104-SNEZHINSK-ACTIVITY.txt за 24-04-2011) был рассчитан только на сжатие, а для растяжения нужно просто x+=x_cmpr, поэтому туда вставлен if(x_cmpr>1).
    • НЕ СДЕЛАННОЕ: при растяжении, в отличие от компрессии, скроллингом начало viewport'а может ставиться в "дробную" точку (и сейчас график сдвигается только кратно x_expn точкам).
      1. И по-хорошему надо бы должным образом "округлять" начальную координату x. В идеале -- начинать на 1 точку до начальной, чтобы линия приходила "слева"; при этом также указатели на начало данных аналогичным образом сдвигать "влево" на 1.
      2. ...и ограничение "справа" надо делать не "x<grf_w", а "x<grf_w+x_xpnd-1" -- чтобы оно последнюю точку (точнее, линию к оной) рисовало справа за границей экрана, а то иначе график обрывается, не доходя до границы.

    25.08.2015: сделано -- чтобы при внедрении в v4 уже было бы всё готово "как надо".

    1. В качестве правой границы вместо grf_w теперь используется rgt_lim = grf_w + plot->x_xpnd - 1.
    2. И начальная координата x тоже ставится "слева от начала" -- на horz_offset%x_xpnd.

      ...а вот «указатели на начало данных аналогичным образом сдвигать "влево" на 1» совсем НЕ надо. Вначале попробовал, и долго не мог понять, почему оно неправильно рисует. Ведь ЭКРАННАЯ КООРДИНАТА-то уже сдвинута, а данные сами "сдвинутся" с ней (и при дополнительном -=1 указателю оно начинало брать из [-1]-й ячейки).

    Засим считаем за "done".

  • 07.10.2014@Снежинск-каземат-11: на liu в manyadcs_knobplugin возникает потребность иметь ЧЕТЫРЕ репера -- по паре для каждого из импульсов, чтоб можно было и значения смотреть, и диапазоны для обсчёта указывать.

    Как 3-й и 4-й реперы ставить? Например, при нажатой Shift... (А вертикальные -- при Ctrl.)

    Мрак...

    09.10.2014@Снежинск-каземат-11: поразмысливши стало ясно, что нефиг такое усложнение -- надо liu'шные графики бить на 2 части каждый, и с частями уже обращаться как с обычными, где и одной пары реперов хватит. Подробнее об сим за сегодня в данном bigfile-0001 и в 201410-SNEZHINSK-ACTIVITY.txt.

    Так что -- "withdrawn".

  • 14.07.2016: встал вопрос: насколько правильно рисовать подписи к осям для тех линий, которые show=1, но plotdata_t.on=0?

    Это вылезло с ADC333 (в v4, с балакинским диссектором), у которого часть линий может отключаться. Выглядит странновато -- график один, а подписей толпа.

    14.07.2016: причина нашлась в 201110-SNEZHINSK-ACTIVITY.txt за 13-11-2011:

            - Кстати, а надо ли там вообще использовать .on?  Казалось бы --
              раз show включено, то и рисуй подписи, а .on пусть влияет уже
              только на графики.
              Так и сделал -- и в отрисовке, и в подсчёте.
    

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

    Ну и заодно стало ясно, почему в своё время это было убрано -- потому, что при старте, когда данных еще нет совсем, то и подписей к вертикальным осям тоже нет совсем (ибо все on==0). Тоже, конечно, не шибко красиво, хотя и корректно :)

Xh_viewport:
  • 04.02.2011@Снежинск-каземат-11: создаём модуль, являющийся базой для "компонентов отображения графиков", в первую очередь -- для Xh_plot. Резоны его создания расписаны в разделе Xh_plot за сегодня.

    Сам модуль будет жить в Xh_viewport.c, а его интерфейс -- в Xh_viewport.h (но никак НЕ в общем Xh.h). Его публичная структурка -- Xh_viewport_t.

    04.02.2011@Снежинск-каземат-11: битым текстом: конечно же Xh по факту является просто личным расширением/надстройкой для Motif'а, и совсем никак даже близко не тянет на "абстрактный тулкит", каковым он пытается всё-таки быть; так -- лёгкий закос, не более.

    Вот в том же Xh_viewport.h будет ПРЯМЕЙШЕЕ использование X11/Xt/Motif.

    04.02.2011@Снежинск-каземат-11: а вообще, конечно, вся эта работа -- в чистом виде "изобретательство велосипеда". Работа с графиками -- задача не из простых, и получается дикая трата времени на то, что другие уже сделали (XRT -- скорее всего, качественно).

    Поскольку Motif, похоже, действительно устаревший/умирающий тулкит, то под него никто никогда уже ничего толкового/доступного не сделает; а мы на нём засели, и на современные (пусть не самые красивые, но ЖИВЫЕ и развивающиеся) не переходим. Хотя, QWT -- тоже не сахар :-).

  • 04.02.2011@Снежинск-каземат-11: приступаем.
    05.02.2011@Снежинск-каземат-11: изготовлен скелет, умеющий просто раскладывать основные виджеты, так что они есть на экране. Ну и аттачменты тоже уже делаются.
    24.03.2011@Снежинск-каземат-11: меняем способ сворачивания cpanel'а -- вместо нашей экзотичной полоски-dashed-сепаратора и необходимости кликать фиг-знает-где делаем кошерно: кнопочки [-]/[+], скрывающие/появляющие cpanel.

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

    Всё стало изрядно красивее и элегантнее -- простой и очевидный код в Xh_viewport, а из fastadc_gui многочисленная ловля ButtonPress'ов убрана, вместе с

    (Конечно, пришлось помучаться -- как водится, из-за шаманства с аттачментами, поскольку сам виджет cpanel использовался для приаттачивания gframe'а; раньше менялось содержимое cpanel (либо набивка, либо виджет folded), теперь же он стал исчезать целиком. Решение простое -- добавлен промежуточный sidepanel, используемый для аттачментов, и никуда не unmanage'мый, а махинации делаются по-прежнему с cpanel'ом. Недостаток -- что sidepanel полностью не исчезает, а остаётся тонкой полоской в 1 пиксел, но это уже прикол X11.)

  • 05.03.2013: для удобного размещения селектора выбора стиля рисования графиков -- [-][...] -- нужна помощь этого модуля.

    05.03.2013: делаем просто -- viewport ВСЕГДА создаёт пустой XmForm-контейнер, под названием angle (поле Xh_viewport_t.angle), только не-Managed. Кому надо (adc) -- с-manage'ат его. Размещается angle аналогично [-][+], в противоположном от того угле вдоль стороны, с которой cpanel.

    Проверено -- работает.

  • 02.04.2013: надо ввести еще один вариант расположения cpanel -- CPANEL_AROUND. Для возможности распихивать элементы управления вокруг отображатора, как у обычных осциллографов и в ndbp_*adc200_elemplugin.c.

    03.04.2013: несколько замечаний:

    1. С реальным аттаченьем в этом варианте будет туго.

      Сами if()'ы, конечно, тоже усложнятся, но это мелочи по сравнению с аттаченьем в середине.

    2. Отключаемость при этом, видимо, должна отключаться. Или возможно как-то так организовать аттаченье, чтоб можно было делать unmanage только содержимому?
    3. Вместо 2-битового поля придётся делать 3-битовое, с неиспользованностями.
  • 22.05.2014@Снежинск-каземат-11: почему-то иногда подглючивает обновление графиков.

    Конкретно иногда при переходе между десктопами (table/CentOS-5.2) НЕ обновляется график manyadcs. При том, что axis -- обновляется. И начинается это не сразу -- на свежезапущенной программе всё окей, а потом, в какой-то момент, перестаёт обновлять по Expose, а остаётся только по собственной инициативе.

    22.05.2014@Снежинск-каземат-11: Натравливание gdb на живую программу --

    gdb /proc/`/sbin/pidof liu`/exe `/sbin/pidof liu`
    -- показало, что там постоянно горит ignore_view_expose=1.

    Точно какое-то нарушение баланса =1/=0 в ViewResizeCB(). А поскольку там всё ВРОДЕ БЫ просто (даже тривиально), то концы ведут к XhCompressConfigureEvents(). Но там тоже всё просто. Итак -- наверное, дело в сочетании с XhRemoveExposeEvents(), уже опять во viewport, в ViewExposureCB()?

    ...а ведь раньше где-то такой баг уже встречался (в файлах нет -- видимо, в органайзере на E90). И на пульту частенько вылазит в adc200me или nadc200...

  • 19.09.2014: добавлена возможность отключить сворачиваемость панели управления (fastadc_gui'шный параметр nofold). Со стороны Xh_viewport'а это делает флаг XH_VIEWPORT_NOFOLD.
Xh_monoimg:
  • 10.07.2012: модуль для отображения монохномных картинок с камеры -- аналогично тому, как Xh_plot служит для графиков.

    Был выпестован в 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: Стенка высказал желание, чтоб камерную картинку можно было крутить: хоть зеркалить, хоть на 180 (надо, если камера вверх тормашками).

    18.01.2013: в принципе, это не особо сложно -- надо копирование из mes_data в dsp_data выполнять с соответствующим преобразованием, а не просто линейно.

    Другое дело, что это только СЕЙЧАС копирование всегда делается тупенькой PerformCopyMskd2(), которую подменять несложно, а вообще-то может быть и PerformUndefect*(), где маеты поболее.

???:
AuxMotifWidgets:
IncDecB:
  • 24.01.2005: замечено, что IncDecButton не учитывает при своей отрисовке состояние sensitive.

    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: желание некоторого апгрейда -- чтоб клиентам можно было НАПРЯМУЮ ловить события "стрелка вверх/вниз", без эмуляции KeyPress'ов.

    20.12.2010@Снежинск-каземат-11: мотивировка -- Панов предложил сделать, чтобы реперы можно было двигать не только мышью на графике, но и с клавы. С клавы -- хрен, т.к. пришлось бы вводить механизм фокусировки реперов.

    Но вот точную настройку сделать можно: если рядом с полями "время репера" разместить кнопочки [v\^].

    Только для этого придется ввести в IncDecB возможность работать БЕЗ клиентского виджета -- т.е., не KeyPress'ы посылать, а вызывать callback'и.

XmInputOnly:
  • 24.07.2006: новый виджет -- "прозрачный", с окном InputOnly.

    24.07.2006: изготовлен, путем копирования нужных компонентов из IncDecB{.c,.h,P.h}, и в Realize() класс окна указывается InputOnly. Помимо уставки в Initialize() значения core.depth=0, "от греха подальше" были предприняты специальные меры:

    1. В Initialize() и SetValues() принудительно сбрасываются в 0 traversal_on, highlight_on_enter, highlight_thickness, shadow_thickness, border_width.
    2. Имеются свои, а не унаследованные методы 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/ также проапдейчено.

XmFocusAwareManager:
  • 30.06.2008: Нужен такой виджет-менеджер. Давно уж доставало, что у dial-подобных ручек нет возможности корректно отрисовывать фокусированность, просто потому, что у XmDrawingArea отсутствуют focusCallback+losingFocusCallback.

    30.06.2008: решение проблемы с отрисовкой круга фокуса у крутилок -- это просто сделать свой manager widget, который бы имел focusCallback+losingFocusCallback.

    01.07.2008: видимо, наследовать надо от XmManager. А вот где брать функциональность фокуса... Возможно, ключевые слова -- Core и accept_focus.

    03.04.2009: СТОП! А если подумать хорошенько -- конкретно в случае dial'а, как нас спасет наследование именно от XmManager? В какое место иерархии его там вставить?

    Из общих-то соображений -- да, такой класс был бы полезен. Но конкретно для LOGD_DIAL -- нет, поскольку кроме собственно dial'а в его форме еще может быть TextInput, который также фокусабелен.

XmSepGridLayout:
  • 28.02.2009: порядком надоело, что в SGL'е нету параметров "colspan" и "rowspan" у клеток. Стало бы ИСКЛЮЧИТЕЛЬНО удобно, а то приходится выпендриваться, делая лишние вложенности элементов...

    Ну что, полезем в сашин код? А не дурканёмся?

    28.02.2009: кстати, такая фича станет дикой нагрузкой на Chl_multicol.c/MotifKnobs_grid_cont.c -- ведь учитывать эти colspan/rowspan при раскладке ручек по сетке тоже довольно нетривиально. А еще и метки надо учитывать, и многоподъездность, и проверять, чтобы ничей x+colspan не превышал numcols, а y+rowspan не превышал nflrs; да и вычисление nrowsnflrs по умолчанию) будет нетривиально.

    Видимо, выход будет в создании массива флажков, в котором при размещении каждой ручки будут уставляться флажки, соответствующие занимаемым ею клеткам. Соответственно, следующие ручки будут "перескакивать" через занятые позиции. А еще надо будет проверять перекрытия (например, @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: но есть еще отдельная задача -- а как бы этак делать, чтобы некая ручка могла занимать ЦЕЛУЮ СТРОКУ, включая колонку меток (левую)? Это может быть нужно для впихивания сепаратора во всю ширину сетки; причем сепаратор может быть как линией, так и меткой.

    Возможно, надо всё-таки пообдумать какую-нибудь другую, более общую и навороченную, модель контейнера-сетки.

XmForm2:
  • Попытка реализовать GU_FLAG_FILLHORZ и GU_FLAG_FILLVERT в мае 2008г. обломилась из-за невозможности ловить resize у формы.

    А решение-то очевидно -- надо сделать свой виджет, наследник XmForm, но с добавленным XmNresizeCallback.

    Ну и, полноты ради, можно ему также добавить и XmNfocusCallback с XmNlosingFocusCallback.

    И назвать оное -- XmForm2. Это позволит поиском находить ссылки на оба варианта.

???:
Cdr:
  • 24.12.2003: надо уметь "сбросить режим в файл одной строкой" -- как oldChl умел делать для logging'а.

    ??.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: кстати! У нас ведь тэг и флаги, получаемые от cda, нигде не сохранялись -- решение по колоризации принималось сразу, и все. А для KnobProps оно понадобилось!

    24.01.2004: ввел в knobinfo_t поля curtag и currflags, и в CdrProcessKnobs() в них сохраняются полученные от cda значения.

  • 02.05.2004: (считаем, что cxdata.h относится к этой епархии) С давних времен у нас в cxdata.h имеется 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, куда он успел пробраться за компанию :-).

  • 05.06.2004: мысль насчет CdrLoadGrouplistMode(): ведь парсинг параметров {norm,yelw,disp}_range там очень бы хорошо лег как PSP_T_PLUGIN через psp_parse(). И автоматически получили бы расширябельность на возможные будущие параметры.

    НО -- тогда образовалась бы зависимость Cdr от libuseful, а этого не хочется...

    17.05.2005: а вот сегодня я добавлял парсинг параметров grpcoeff и grouped -- и как меня это достало! Все-таки НАДО будет перейти на psp, и фиг с ней с зависимостью -- от psp и так реально везде все зависит.

  • 10.06.2004: вводим общее правило: лимиты/диапазон считаются указанными, когда в паре [min,max] имеем min<max (вместо былого min!=max). В paramstr_parser.c::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: обнаружилось, что каналы, имеющие красные/желтые диапазоны, в которых значение 0 вызовет расцветку, НЕ расцвечивались прямо при пуске программы. Это стало особо критично для LOGC_VIC, который и в нормальном состоянии имеет не-серый background.

    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 -- все пришло в чувство.

  • 26.08.2004: похоже, надо в дополнение к типам 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" -- стабильность любого канала. Т.е., либо среднее за некоторый период, либо среднеквадратичное отклонение. И чтоб еще можно было покрутить, по скольки измерениям.

  • 25.09.2004: почему-то когда revformula содержит CMD_REFRESH, то тот канал, который собственно и вызвал запись, мгновенно обновляется в старое значение (ибо новое от сервера еще не вернулось). А при обычной записи это как-то избегается (поле 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". Пофиксил.

  • 27.09.2004: при введении вложенных элементов обнаружил, что CdrProcessGrouplist() возвращает rflags только ПОСЛЕДНЕГО элемента, забывая остальные.

    27.09.2004: там стояла тупая передача rflags_p дальше, в CdrProcessEleminfo().

    Пофиксил -- вставлена корректная аккумуляция.

  • 27.09.2004: обнаружились две "глупости" в разделении обязанностей между шоблой Chl/Cdr/Knobs (артефакт годовалой давности после деления oldChl):
    1. (Chl<->Cdr) часть работы метода elem.ShowAlarm() выполнялась в Knobs.
    2. (Chl<->Cdr) почему-то поле knobinfo_t.uplink заполнялось не в CdrCvtLogchannets2Knobs(), а в ChlMakeElement().

    27.09.2004: во-первых, перенес всю алхимию с ShowAlarm() в Chl, а в Knobs_internals.c::SetAlarm() остался только вызов его да "свое внутреннее" -- SetRelaxing().

    Во-вторых, теперь проставление uplink'ов перенесено в CdrCvtLogchannets2Knobs(). Для этого пришлось поменять интерфейс -- ей теперь передается дополнительный параметр "holder", которым и заполняется поле uplink. (Поскольку никто, кроме самой Cdr этим интерфейсом не пользовался, то изменение прошло безболезненно.)

  • 24.10.2004: обнаружил, что поле 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".

  • 04.11.2004: вылез косяк с оцветнением в ситуации, когда значение ОЧЕНЬ близко к границе диапазона. Имеем в thermosm: ylwrange=[25.0..40.0], value=24.999, dpyfmt="%5.2f". В результате отображается "25.00" -- вроде как в границах диапазона -- но ЖЕЛТОЕ. Сравнение-то делается с реальным, а не отображаемым значением...

    Ну и что теперь -- вставлять сравнение с поправкой на q? А какие негативные последствия могут от такого вылезти?

  • 29.11.2004: понадобилось (для "упрощенных" файловых диалогов) вызнавать из файла режима его комментарий и время создания.

    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, то запишем здесь:

    Надо бы кроме числа-time_t иметь в файле и человечески-читаемое обозначение времени. И, с учетом того, как устроен парсинг единственного параметра у "#!CREATE-TIME:", лучшее место для этого обозначение -- через пробел сразу после числа-time_t. Парсер, видя, что после числа идет пробел, будет удовлетворен, а человек -- и дату сможет прочитать.
    (Придумано было для потребностей ippclient'а, но очевидно полезно и для других тоже. И, справедливости ради, надо сказать, что в adc333.c::SaveToFile() в точности такая последовательность -- time_t,ПРОБЕЛ,ctime() -- уже использовалась; но там чтения нету, только сохранение.)

    Да, вставил в:

    • ndbp_image_elemplugin.c (15-12-2007...),
    • tantalclient.c (10-05-2008...),
    • во всю шоблу adc200.c и неиспользуемый fastadc_common.c (этот был за 26-12-2007...) -- в nadc333.c пока нету сохранения,
    • и, для унификации, в CdrSaveGrouplistMode() тоже.
    (а вот трогать старичков в istc/xmclients/ уже не стал).

    Одно ма-а-аленькое замечание: поскольку ctime уже само выдает в конце '\n', то завершать printf-формат для строки crtime_lp_s теперь не нужно.

  • 29.11.2004: и, кстати -- надо ж ведь теперь уметь указывать в CdrSaveGrouplistMode() и комментарий.

    29.11.2004: что ж, добавлен дополнительный параметр -- comment. Изменение API повлекло за собой и соответствующее изменение в API Chl. А то, в свою очередь потребовало подкорректировать своего пользователя -- Chl_simple.c::CommandProc().

    Комментарий в файл пишется "творчески" -- по символу, с проверкой, что не-iscntrl(), а если "да" -- то вместо него пробел.

  • 11.01.2005: кхм, комментарий-то мы в файл пишем, а вот как насчет того, чтобы этот файл был еще и "опознавабелен", от какой он подсистемы, и НЕ ТОЛЬКО по имени?

    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 также можно, в качестве ссылки на того, кто сделал файл.)

  • 12.01.2005: стоит сделать еще один тип -- 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: надо б ввести еще одно цвето-состояние -- "данные еще не были получены".

    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: и еще одно состояние, уж совсем на будущее -- "disabled", или "readonly": когда некоему клиенту уставляется "ты -- щенок, имеешь только readonly-доступ", чтобы все ручки-жертвы приобретали "нередактировабельный" вид.

    24.01.2005: вопросов только два:

    1. Как факт readonly'вости будет добываться от сервера? Задействовать какой-нибудь флажок в rflags? (Лучше бы всего -- старший, который сейчас -- CXRF_NO_DRV/"Driver loading problem".
    2. Как отображать сие состояние? Ведь оно несколько ортогонально всем остальным -- просто должно отключаться редактирование.

      У text-widget'а можно ставить обычно-серый фон вместо белого (хотя где ж его брать? :-).

      У разных объемообразных -- можно делать XmNsensitive:=False всяким стрелочкам (в т.ч. и стрелочкам [v\^] у text'а).

      А вообще -- ЦВЕТОВОЕ ли это состояние? Или стоит ввести дополнительное поле в knobinfo_t?

  • 19.04.2005: придумал, как реализовать "colformula" прямо сейчас, в нынешнем CX, не дожидаясь CXv4.
    • Вводим еще один опкод -- OP_RETSEP, который для cda_execformula() работает в точности как OP_RET, а остальные части cda обращают на него внимания не больше, чем на NOP.
    • Плюс изготавливаем "cda_временный_хак()", позволяющий добыть указатель на команду, следующую ЗА OP_RETSEP. И если она будет возвращать не-NULL, то Cdr будет считать результат за colformula,
    • А в knobinfo_t поле вставить можно, это никакого интерфейса не нарушит.
    • И, естественно, никакого free(ki->colformula) делать не надо.

    (Реально идея эта пришла в голову еще где-то во время поездки в Японию -- в каком-то аэропорту...)

    Что касается обращения с ki->colformula:

    • Если она есть -- то для принятия решения о расцветке используется она, независимо от LOGK_DIRECT/LOGK_CALCED.
    • Да, и надо ее добывать при конверсии logchannet_t->knobinfo_t ВСЕГДА!
    • Можно также и в окне "Knob properties" дополнительно выводить второй "источник данных".

    Да, и 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":

    1. Ввел поле knobinfo_t.cur_cfv.
    2. Сохранение в него результата вычисления colformula в CdrProcessKnobs().
    3. Отображение (через запятую) в поле "Value" еще и второго числа.

    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: Бубен потребовал, чтобы в phm-tsyline.c в протокол НЕ валились бы каналы КШД. И как сие сделать, спрашивается?

    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'ы -- в т.ч. и элементы.

    Так что --

    • Ввел в Knobs_typesP.h static-inline-функцию IdentifierIsSensible().
    • Вставил проверки в CdrSaveGrouplistMode()+RecSaveElement().
    • Аналогично в CdrLogGrouplistMode()+RecLogElement().
    • Плюс -- в descr2html.c::WriteFile(); именно в ОДНО место -- только на верхнем уровне, чтоб пропускались "служебные" элементы лишь группировки, а вот содержимое элементов пропускать нельзя -- иначе все там поедет.

    И, наконец -- в db_linmag.h элемент федорообразных вычислений теперь именуется "-fedcalc".

    istc/xmclients/ трогать не стал -- во-первых, фиг уж с ними, а во-вторых, там СТОЛЬКО ссылок на элемент "kshd" по имени -- у-у-у!)

    Короче -- на сейчас считаем "done".

    06.04.2006: блин, там в IdentifierIsSensible() проверка была некорректной -- оно сразу проверяло первый символ, а ведь поле ident может быть не только пустым, но и просто NULL (имеет полное право, и так и делается в impacis10.c!).

    Вставил проверку.

  • 17.05.2005: обнаружил, что при сохранении в файл -- ныне это RecSaveElement() -- НЕ обрезались пробелы слева при fprintf()'ах диапазонов. Причем не обрезались еще издавна, с самого появления этой фичи -- еще задолго до введения fprintf_f_rtrim().

    В принципе-то это несмертельно -- парсер в ParseChanVals() лидирующие пробелы допускает, но -- некрасиво.

    17.05.2005: решение просилось само -- легкая модификация fprintf_f_rtrim() для пропуска пробелов, что и было сделано.

  • 23.05.2005: для "полноты" следует иметь в элементе его собственные rflags -- кумулятивные rflags его содержимого.

    Кстати, при желании можно будет в 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: неудобно, что у нас на ВСЕ attn-состояния ОДНА длительность. А хотелось бы -- OTHEROP по 5 секунд, а RELAX -- 10.

    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: вчера (см. "CFG_PROBL") появилась идея, что надо ввести отдельное цветовое состояние для "софтверных ошибок", для отличения их от аппаратных.

    16.06.2005: йоу!!! Насчет "другого цвета" -- придумал: это должно быть нечто коричнево-болотное. Первое же, что было подобрано в xpaint'е -- #B09422.

    Йоу#2: помнится, забавные, примерно болотные цвета получаются, если xwd-дамп экрана с гусиными программами показать в xv -- xv почему-то меняет местами R и B. Так вот -- "цвет гусиной кожи" превращается в #a09b5f. А FVWM/AnotherLevel'овские заголовки активных окон -- в более ядовито-яркий #8b8b00 (fvwm'овская topShadow от него -- #c3c300).

    Остается только вопрос выбора: какой из категорий ошибок {железной,программной} отдать какой из цветов {бордовый,болотный}.

    28.06.2005: Мысли по теме:

    1. Насчет "кому что": очевидно, что АППАРАТНОЙ ошибке надо отдать бордовый, а ПРОГРАММНОЙ -- болотный.
    2. Флаг "calcerr" относится к программным.
    3. HWerr -- приоритетнее SWerr.
    4. Надо заводить в cx_types.h отдельные маски "SERVER_HWERR_MASK" и "SERVER_SWERR_MASK".

    28.06.2005: а сам проект -- реализуем. Поехали:

    • Во-первых, для уменьшения вероятности ошибок/опечаток, не "swerr", а "sferr".
    • Введены CXRF_SERVER_HWERR_MASK и CXRF_SERVER_SFERR_MASK -- пока халявно, как 0-й и 1-й байты.
    • Сделан отдельный CDR_FLAG_SFERR_MASK (куда теперь отнесен и CALCERR); он добавлен и к CDR_FLAG_SYSERR_MASK.
    • Вводим Knobs_types.h::colalarm_t::COLALARM_SFERR.
    • Добавлена альтернатива в ChooseColorState() (приоритет -- у HWerr).
    • Введен XH_COLOR_BG_SFERR плюс строчка в Xh_colors.c. Выбран fvwm'ный болотно-зеленый #8b8b00.
    • Добавлена альтернатива в ChooseKnobColors().
    • Вставлен "интеллектуальный" выбор цвета подсветки в Chk_knobprops.c::DisplayCurVals().
    • Вставлена альтернатива и в cx-starter.c::EventProc(), с соблюдением надлежащего приоритета.
    • А также -- в ipp.c::DecodeData().

    Проверил -- все работает. Итого -- "done".

  • 05.11.2005: обнаружилось, что CdrCvtLogchannets2Knobs(), при count==0 возвращающий NULL, не позволяет создавать пустые элементы, что есть плохо.

    05.11.2005: поправил -- теперь CdrCvtElemnet2Eleminfo() просто пропускает и вызов, и проверку результата CdrCvtLogchannets2Knobs() при count==0.

    (Правда, почему-то это не помогло, что-то продолжило глючить, ну что ж -- будем разбираться :-) Как установлено давным-давно, глючил, как водится, антоновский виджет XmSepGridLayout, так что к Cdr продолжение отношения не имело.

  • 08.12.2005: возникло нечто по поводу загрузки режимов: что делать, когда в режиме встречается значение, выходящее за пределы разрешенного диапазона? Вопрос возник при общении с Гусевым, который сейчас реализует у себя чтение режимов (по формату файлов похожих на мои). И:
    • Гу-гу в таких случаях хочет отвергать весь файл целиком, как неправильный.
    • У меня же чтение файла приравнивается к забивке полей с клавиатуры -- посему значение просто вгоняется в разрешенный диапазон.
    • Старостенко же предлагает -- именно мой вариант, но чтобы об этом как-то сообщалось.
    • (Понятно, конечно, что в идеале надо иметь "типа менеджер режимов", и в случае выхода чего-то за границы выдавать окошко, в котором "выходящие" помечены, и юзер сам решает, что с ними делать. Но это шибко жирно :-)

    Так вот -- можно ж просто ввести еще какое-нибудь цветосостояние, этакое "при-чтении-вышло-за-пределы", так что пользователь сразу будет видеть проблему.

    Некоторые "детали":

    • Состояние "при-чтении-вышло-за-пределы" должно висеть, пока юзер там сам что-то не исправит либо до следующего чтения режима (т.е. -- попросту до SetControlValue(,,fromlocal:=1), к которому оба варианта и сводятся).
    • Как-то надо выбирать приоритет этого цветосостояния -- кто его перекрывает, а кого -- оно?
    • Тогда надо б иметь еще одно поле в knobinfo_t -- "прошенное" значение, которое отображать в "knob properties".
  • 31.01.2006: Малютин попробовал импортировать log-файл в Excel. И -- этот "умник", в отличие от Gnuplot и MathCAD, НЕ пропускает строки, начинающиеся с'#', а позволяет иметь их как комментарии вверху колонок. Но -- из-за "# Time..." (пробел!) заголовки едут на 1 колонку вправо.

    31.01.2006: потому -- пробел убран :-).

    03.06.2010: Карнаев попросил сделать, чтобы в log-файле кроме колонок "time()" и "strcurtime()" была также колонка "время с начала записи log-файла" -- так удобнее строить графики, а то "глобальное время" -- малополезно.

    Вечером: да, сделал. Для этого пришлось добавить к ChlLogWindowMode() и CdrLogGrouplistMode() параметр struct timeval *start, плюс, естественно, в юзерах -- Chl_app.c и cdrclient.c -- инициализацию этого "времени начала логгинга" при включении протоколирования.

  • 02.03.2006: сегодня два часа потратил на поиск некоей якобы имевшейся ошибки в связке Cdr+Chl, успев почувствовать себя как тот географ из "Золотого теленка", который не нашел на карте Берингова пролива -- вроде как все на вид правильно, а чего надо -- не происходило!

    Выяснилось в конце концов, что в widgettest'е было ДВА канала с именем "IlimHW"#n, и оно просто при загрузке режима всегда находила лишь первый, а второго не замечала.

    Вывод: вообще-то надо б такие очевидные вещи проверять -- нет ли дублирования имен!

    02.03.2006: вариантов действий тут видется три:

    • 1. "Заранее" -- в случае использования СУБД/редактора просто выбирать некую "точку", когда элемент уже точно в готовом, а не редактируемом состоянии (например, сохранение), и там проверять.
    • 2а. Проверять при "рождении" массива knobinfo_t[] -- т.е., в CdrCvtLogchannets2Knobs() -- и если идентификатор "осмыслен" (т.е., непуст и не "-"), то проверять, нету ли уже такого, и если есть -- то ругаться на stderr.
    • 2б. Все аналогично 2а, но только не ругаться на stderr, а запоминать в некую строку, каковая должна храниться в элементе и мочь быть считана, и выставлять некий флажок, чтоб элемент подсвечивался.

    12.08.2010: ну вот -- опять наткнулся на такое: в liucc "почему-то не грузились режимы". Потому, что опять были дубликаты -- имена "_".

    Вводим диагностику -- при $CDR_DEBUG_DUPS=1 оно выполняет проверку по варианту 2а.

    Работает -- СТО-О-ОЛЬКО поналовило... Так что, подумал-подумал -- и сделал проверку принудительной, ВСЕГДА.

    Засим, по прошествии почти четырёх с половиной лет, можно раздел считать за "done".

    01.03.2013: а ведь теперь, с введением "прозрачных" контейнеров (с ident=":") проблема может вернуться. Проверять "параллельные вселенные" довольно нетривиально, и делать этого не хочется.

    Просто пока закроем глаза на потенциальную проблему.

  • 29.05.2006: практически случайно заметил, что поля ident (eleminfo_t и knobinfo_t) используются сразу -- БЕЗ проверок, что они не NULL. Нехорошо!!!

    29.05.2006: прошелся по всему тексту, попроставлял проверок на NULL. С остальными текстовыми "свойствами" проблем нет.

    Еще раз убедился -- ну и угребищна же нынешняя хак-архитектура вложенных элементов!!!

  • 20.07.2006: да, пора вводить для элемента метод "пришли новые данные", вызываемый Cdr'ом из 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) вызывалась только про приходу конкретно ЕГО данных.

  • 23.07.2006: уже давно хотелось иметь поле 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).

  • 07.08.2006: проблема: пока что у нас не все значения проходят через Cdr, а флажок DEFUNCT уставляется именно там -- в ChooseStateRflags()...

    07.08.2006: ну и что -- ChooseStateRflags() публиковать, что ли? Но тогда придется дозволять ki==NULL, и всю функцию придется напичкать if()'ами...

  • 07.08.2006: и давно грызет мысль -- а не ввести ли все-таки "отдельное" цветовое состояние "UNDERFLOW"? Оно бывает нужно в некоторых "экзотических" случаях, типа BPM'ов.

    07.08.2006: ммм... Соображения:

    • Нужно это бывает нечасто -- пока что именно только для linbpm'а.
    • Как именно отображать -- сильно зависит от конкретного случая: для ручек -- хз; для графиков-гистограмм имеется четкая "таблица": зеленый #00FF00|#00C080=>#004000, синий #0000FF=>#000080, красный #FF0000=>#600000).
    • "Способ выбора" -- если значение равно 0, и rflags содержит 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: есть еще пара вариантов цветов, помимо более темных "базовых":

    1. Смесь базового с серым либо с фоном.
    2. Штриховка.
  • 07.08.2006: писец -- у нас ОТСУТСТВОВАЛО поле типа "eleminfo_t.something_private"!

    07.08.2006: ввел, долго ли -- eleminfo_t.elem_private, сразу за wdci.

    (Ну да, как бы я без него linipp-то делал! :-)

  • 02.01.2007: в программульке ringcamsel возникла необходимость в такой фиче: в зависимости от текущего значения ручки менять ее цвет (0-7 -- зеленый, 8-15 -- желтый, 16-23 -- красный).

    В данном-то случае все отлично ложится на обычное расцвечивание (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".

  • 19.03.2007: несколько поднадоело, что числовые параметры (номера каналов/bigc) нашим заменителям user-type knob-plugin'ов -- element-plugin'ам -- приходится указывать в options в СТРОКОВОМ виде. А они ведь частенько ВЫЧИСЛЯЮТСЯ -- т.е., формулы, но результат вычисления формулы в строку не засунешь...

    Так что -- добавим к elemnet_t и eleminfo_t целочисленное поле intparam?

    Вот только из-за этого опять придется CXSS_SUBSYS_VERSION_MAJOR двигать, а неохота...

    20.03.2007: в принципе-то можно использовать поле ncols, но как-то это неправильно...

  • 14.05.2007: хреновенько, что ругательства при загрузке режима ("channel/element not found" и "unknown element") безадресны -- имя ненайденного канала/элемента не пишется, и приходится искать по номеру строки. Видимо, из-за того, что это имя -- просто кусочек из входного буфера, и не-(NULL-terminated).

    А писать бы надо! Благо, есть хоть и не-очень-портабельная, но используемая во всяких psp спецификация "%.*s".

    23.05.2007: сделал, и все проверил.

  • 23.05.2007: было крупное ляпище -- в CdrProcessKnobs() при count<=o делался просто return, а rflags оно прописать забывало, так что там оставался мусор.

    Заменил return на goto в конец, туда, где делается возврат.

  • 20.11.2008: shame on me -- в CdrCvtGroupunits2Grouplist() отсутствовала проверка, что grouping!=NULL. Спасибо Роговскому -- случайно натолкнул на этот аспект.

    20.11.2008: вставил проверку -- при NULL возвращается NULL и errno=EINVAL. Заодно подобные же проверки добавлены и в CdrCvtElemnet2Eleminfo() и в CdrCvtLogchannets2Knobs(). А вот в троицы CdrDestroy*() и CdrProcess*() пока вставлять не стал -- надо подумать.

  • 27.03.2009: а не ввести ли нам под шумок -- пока затишье -- поле style для ручек и элементов (и в *net_t, и в *info_t), аналогично тому, как 3 года назад добавили placement?

    Тогда можно будет, отработав стили в 4cx/, быстро back-портировать их и сюда...

    28.03.2009: вставил пока что в knobinfo_t и eleminfo_t -- при этом бинарная совместимость остаётся. А как комплекс отключат -- добавлю и в *net_t.

    21.10.2013: да сделано, сделано, в конце сентября.

  • 18.11.2009: давно мелькает потребность уметь в под-элементах указывать СВОЙ defserver.

    Это изначально предусмотрено в 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), по первоначальному проекту:

    • Вставлена проверка на "defserver=", с выкусыванием его из options и созданием доп.соединения, для чего...
    • ...в cda создание auxsid'а вытащено в публичную функцию cda_add_auxsid().
    • Кстати, раньше там был не вполне корректный подход -- в конце cda_register_formula() чохом ВСЕМ auxsid'ам данного соединения делался cda_add_evproc(). Несмертельно, конечно -- в нём стоит проверка для недобавления уже имеющихся нотификаторов, но всё же.
    • Кроме того, поскольку для aux-серверов правильным (если не единственно-разумным) действием по приходу данных является вызвать нотификатор базового сервера с причиной 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, но вряд ли оправданно переходить на него (просто трата времени и ломание работающего кода).

  • 01.12.2009: кстати, а не провести ли СЕЙЧАС upgrade формата subsysdescr?

    Из актуальных потребностей:

    • поля logchannet_t.style и elemnet_t.style (см. 27-03-2009);
    • elemnet_t.defserver (см. предыдущий пункт -- чтоб с options не маяться);
    • subsysdescr_t.plugfiles (чтоб a-la-v4 в описании подсистемы указывать, какие ей нужны файлы-реализаторы-плагинов -- тогда можно б было все программы свести к chlclient'у плюс набор плагинов).
    И это -- если будет сделано -- станет ПОСЛЕДНЕЙ крупной модификацией в CXv2.

    21.10.2013: да сделано, сделано, в конце сентября. Кроме plugfiles, которая ТУТ не к месту (уж в v4 будет).

  • 28.12.2009: еще энное время назад Батраков и Жора Фатькин нудели, что им надо уметь менять подписи к строкам timer16/dl200 -- потому что эти хмыри постоянно перетыкают кабеля между выходами (в первую очередь -- потому, что выходы постоянно выжигают).

    (А еще раньше Малютин со Старостенкой приставали с аналогичной просьбой про вакуум -- они там тоже имеют привычку кабеля перетыкать.)

    28.12.2009: естественно, стоит также и вопрос СОХРАНЕНИЯ этого барахла в файл и восстановления оттуда. То, как оно будет попадать в defaults-файл и когда оттуда считываться -- обсуждается в разделе по Chl_app за сегодня.

    А вот КАК оно туда будет сохраняться и восстанавливаться обратно в ручки -- вопрос уже для Cdr. Идея такая:

    • Вводим LOGK_USERTEXT -- по аналогии с LOGK_NOP, так что оно будет и не DIRECT, и не CALCED. И все проверки будут рядышком, там же, где и на NOP.
    • В файл для таких ручек сохраняем их поле label.
    • Чтоб не добавлять специальный эндемичный метод типа "SetLabel()" или "SetText()", будем при загрузке просто менять label и вызывать метод PropsChg().

    31.12.2009: вроде сделал.

    1. В собственно хождениях по дереву (кроме сохранения/загрузки) нигде отдельно проверять на LOGK_USRTXT не пришлось -- там код уже достаточно правильно организован, так что просто пропускает всё, что не LOGK_DIRECT/LOGK_CALCED (и при этом не LOGT_SUBELEM).
    2. Сохранение/загрузка: сохранение сделать оказалось тривиально. В загрузке же -- слегка перетряхнул логику, сделав её более "правильной", типа приблизив к будущей 4cx/...Cdr/'ной (заменил булевскую is_subelement на перечисление line_type, плюс, за компанию, и массив-имён-типов -- names[] -- теперь ссылается на nnn_s, а не дублирует их).

      Собственно парсинг строк .usrtxt делает функция ParseUsrTxt() -- оказавшаяся тривиальнейшей.

    Проверил, работает -- "done".

  • 11.05.2010: в интересах cx-starter'а надо уметь обрубать формулам возможность записи, чтоб группировка работала в режиме "readonly". Для этого, очевидно, надо б передавать в 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".
    • При наличии этого флага
      1. UpdateChannels() включает CDR_OPT_READONLY (тем самым отсекается запись из формул).
      2. Chl_E_SetPhysValue_m() просто делает return (так отключается запись от ручек).

    29.10.2011@Снежинск-каземат-11: а вот пункт "Chl_E_SetPhysValue_m() просто делает return" оказался лишним: тем самым мы отключаем не только "запись от ручек", но и локальную активность программы (через регистры).

    • Хотя отключать надо именно ТОЛЬКО в Cdr/cda -- поскольку всё равно запись что в каналы, что в регистры может быть исполнена в ЧИТАЮЩИХ формулах, для которых ЭТО отключение вообще побоку.
    • Тут же пришлось пресекать в _SetPhysValue_m() из-за неполноты поддержки CDR_OPT_READONLY в Cdr: в CdrProcessGrouplist() она есть, а в CdrSetKnobValue() -- нету.
    • Вывод: надо делать!
    • Вводим:
      1. В CdrSetKnobValue() добавлено поле options (причём не в конец, а ПЕРЕД localreginfo).
      2. Его поведение: из него добывается cda_opts, и при LOGK_DIRECT при нём делается просто return 0, а при LOGK_CALCED -- оно OR'ом передаётся в cda_execformula().
      3. Chl_E_SetPhysValue_m(), соответственно, передаёт ему CDR_OPT_READONLY при di->is_readonly.
      4. В единственного оставшегося пользователя -- cdrclient.c вставлена просто передача 0.

    Надо будет и в v4 реализовать всю алхимию с readonly.

  • 05.08.2010: надо бы прямо сюда, в CXv2, ввести придуманную для v4 фичу оптимизации обхода дерева при обновлениях по "точкам изменений" (см. bigfile-0002.html раздел "К вопросу об обновлении ручек, зависящих от более чем одного канала" за 01-08-2007).

    Побудительный мотив -- liucc: там сервера работают на 5Гц, и уже сейчас, при 4 рабочих стойках, там 5 серверов, вследствие чего оно обходит всё дерево по 25 раз в секунду -- в 5 раз чаще, чем нужно. А при 8 стойках будет 9 серверов (и 8 "лишних" обходов из 9). Содержимое там очень-очень развесистое -- и собственно knob'ов много, да и формул неслабое количество, так что на viper@PIII-1GHz оно жрет ~50% процессора (на wolf@Core2Duo-3GHz -- ~25% одного). И это у нас пока еще графиков нет, которым процессор будет сильно нужен.

    05.08.2010: эта штука затронет аж 3 библиотеки:

    1. Cdr -- главная, на которую и ложится основная работа.
    2. cda -- от неё требуется сотрудничество в определении задействованных в канале/формуле sid'ов.

      Плюс -- надо бы в cda_eventp_t вместе с reason передавать и номер сервера. (А может, передавать оный sid как раз в параметре sid? Неа, не стоит -- надо добавлять еще один параметр.)

    3. Chl -- должна, получая от cda, передавать этот sid дальше в Cdr.
    4. ...и присоседившиеся к ним -- в роли Chl'я -- cx-starter, cdrclient и descr2html.

    Несколько соображений:

    • Что касается обновлений по OP_REFRESH: при них в качестве sid-number передавать -1, что будет означать "ничего не проверять, а честно обойти всё дерево".
    • И отдельная деталь: если при обходе ветка не затрагивается, то надо не забыть использовать её ТЕКУЩИЕ rflags (т.е., оставшиеся от предыдущего обхода). Сейчас это cmlrflags (переименовать бы их в просто currflags, а? 06.08.2010: переименовал.).
    • И: element-метод NewData в этом случае, конечно, НЕ вызывать.

    Проект получается масштабным, но вроде реализуемым. Заодно, кстати, и потренируемся перед "кошерной" реализацией в 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.

    • Вводим в Chl_gui.c::UpdateChannels() правило, что 2-м параметром туда теперь передаётся не synthetic, а sid_n (по OP_REFRESH -- -1).
    • Насчёт дальнейшего использования былого synthetic:
      1. Параметр "synthetic" передаётся в NewData. Там всё можно оставлено -- передаётся sid_n!=CDA_R_MAINDATA.

        (Вообще, конечно, бибиканье+морганье по alarm'ам надо бы перевести на просто 1Гц-основу, но пока и так вполне катит.)

      2. Аналогично с теми, кто регистрируется по ChlSetDataArrivedCallback().

    06.08.2010: продолжаем:

    • В CdrProcess*() добавлен параметр cause_conn_n -- по аналогии с 4cx/, перед options (в результате тут он получился первым).
    • "Точкой изменения", в отличие от v4, у нас может быть только элемент (а не любая ручка), так что:
      1. Добавлено поле eleminfo_t.conns_u тот самый указатель на массив булевских флагов. И safe_free() его в CdrDestroyEleminfo() не забыто.
      2. Проверка "обрабатывать ли элемент?" вставлена в CdrProcessEleminfo(), если "не обрабатывать" -- то хватаются старые флаги.

        Кстати, подхват старых флагов добавлен и в Cdr_treeproc.c::CdrProcessKnobs() -- раньше отсутствовал.

    07.08.2010: майстрячим дальше -- теперь уже основное "мясо":

    • В cda вставлена передача event-processer'ам cause_sid_n'ов. Для этого -- чтобы не вести в InternalAuxsrvNotifier()'е поиск по auxsidslist[] -- добавлено поле serverinfo_t.sid_n, заполняемое в cda_add_auxsid().

      Замечание: это всё касается только стандартного использования aux-серверов в Cdr и cda, когда работает InternalAuxsrvNotifier(). Если же клиентская программа укажет свой processer, то, естественно, никакого reason>0 ей прислано не будет -- она сможет получать уведомления о событиях данного sid'а напрямую, в указанный processer (хотя, там есть свои потенциальные косяки -- что если заказывать один и тот же auxspec несколько раз с разными processer'ами, то реально зарегистрируется только первый).

    • Теперь добавляем в cda API для получения списка sid_n'ов, используемых каналами/формулами.

      Функции -- cda_cnsof_physchan() и cda_cnsof_formula().

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

      В дополнение к serverinfo_t.sid_n добавлена также "ссылка на parent-сервер" -- serverinfo_t.main_sid. Так что cda_cnsof_physchan() тоже не занимается никакими поисками "принадлежит ли вообще этот канал к указанному main-серверу", а просто сравнивает.

    09.08.2010: добиваем (в первоначальном варианте) -- собственно основная работа по определению "точек изменений":

    • Выполнение этой работы возлагаем на здесь-внутреннюю CdrRealizeGrouplist(), вызываемую из CdrCvtGroupunits2Grouplist().
    • Алгоритм действий таков:
      1. Если приложение односерверное -- то вообще ничего не делается.
      2. В противном же случае работает 2-проходная схема:
        1. Для каждого из элементов в дереве malloc()'ится булевский массив, затем он заполняется флагами.
        2. Делается обход всего дерева, и -- ПОСЛЕ вызова такого же обхода для подветок -- проверяется: если массив пуст (это -- при чисто-регистровых ручках), либо если он идентичен массиву parent'а, то он выкидывается и делается conns_u=NULL -- так что подветка будет ВСЕГДА обрабатываться при обработке parent'а.
    • Основная работа делается парой функций FillConnsOfElemInfo() и FillConnsOfKnobs() -- чья логика обхода дерева скопирована из CdrProcessKnobs(), с оговоркой в виде следующего пункта:
    • поскольку на 2-м проходе заполнять булевские массивы с помощью cda уже не требуется, то после первой проверки -- на LOGT_SUBELEM -- вставлено условие
      else if (pass_n == REALIZE_PASS_2){НИЧЕГО-НЕ-ДЕЛАТЬ}

    Собственно -- проверено, работает, потребление процессора viper'а снизилось с >70% до ~25%.

    Имеет место, конечно, некоторая неоптимальность:

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

      И пока в голову элегантного алгоритма не приходит -- лишь некое странное "аллокирование по мере надобности", когда malloc() делается на верхнем уровне, потом оно передаётся вниз, а далее буфер может "оставляться" там, и malloc()'иться новый. Запутанновато.

    09.08.2010: P.S. При кодировании умудрился допустить пару дичайших ляпов:

    1. В cda_add_auxsid() присвоения полей sid_n и main_sid делались в si-> вместо servers[auxsid]..

      В результате оно везде отдавало только 0-й (main) sid.

    2. После 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: кое-что всё-таки не работало.

    Вылезшие симптомы и протокол расследования:

    • Переключатель режима работы УБСа в liucc в какой-то момент переставал отображать текущее состояние (которое может меняться само, хоть это и w-ручка).
    • Расследование показало, что перестаёт оно отображать, как только разок сменишь режим вручную.
    • Первой мыслью было -- что проблема в кванте, который, возможно, определён для этого канала по ошибке (в смысле -- значение считалось бы "почти тем же").

      Однако, это явно было не так -- и ругани никакой при CDA_DEBUG_PHYSINFO=1 не выдавалось, да и оранжевела ручка исправно.

    • Следующей гипотезой было, что умничает choicebs (там достаточно "мозгов" касательно расцвечивания).

      Однако отладочная печать, вставленная в тамошний 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*.

  • 22.12.2010@Снежинск-каземат-11: пара моментов:
    1. В RecLoadLevel() отсутствовала проверка
      ki->subelem->ident != NULL
      перед
      cx_strmemcasecmp(ki->subelem->ident, ...
      в ветке "...or subelement's name".
    2. В 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(). Выглядеть стало намного лучше.

  • 21.01.2011: пришлось сделать функцию-обёртку _CdrSetControlValue(), предоставляющую со-библиотечникам доступ к SetControlValue(). Объявлена она в созданном для этого Cdr_private.h, лежащем тут же.

    21.01.2011: причина -- потребности Cdr_script.c, а из-за отсутствия у нас тут отдельной libdatatree таковой ПУБЛИЧНОЙ функции нету, как это ни глупо (отсюда постоянная маета с поддержанием двух почти идентичных копий в Cdr и Knobs).

    29.08.2011: в связи с введением datatree_SetControlValue() сделанное тогда убрано, и раздел "withdrawn".

  • 22.01.2011: Роговский написал, что при kind=LOGK_CALCED,formula=NULL бывает SIGSEGV; также некрасиво при kind=LOGK_DIRECT,phys=-1 (такое случается при переделке канала из одного типа в другой).

    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 единой...).

  • 29.03.2011@Снежинск-каземат-11: вводим CdrFindKnobFrom(), ведущую поиск не от верхушки (grouplist'а), а ОТНОСИТЕЛЬНО -- от указанного элемента. Включая адресацию a-la "../".

    Побудительные мотивы:

    1. Для liucc, чтоб adc200me_calc мог адресоваться к своему "source" относительно, а не искать того от верхушки.
    2. Полезно будет и для alarmonoffled'овского victim -- тогда он сможет управлять ручками не только на своём уровне, но и вглубь элемента, и даже в соседних.
    3. И для LOGD_SCENARIO/Cdr_script'а это тоже пригодится -- чтоб можно было работать с "соседними" ручками.

      (Правда, там сложнее -- нет возможности указывать ДВА privptr'а (grouplist и "self-location"), но с этим разберёмся.)

    29.03.2011@Снежинск-каземат-11: соображения:

    • Вопросы относительной адресации у нас имеются в двух аспектах -- ручки и каналы -- и рассматриваются в двух местах:
      1. Про ручки -- наиболее последние соображения на эту тему в bigfile-0002.html за 22-12-2010 ("нужен развитый механизм поиска ручек"), касательно "прозрачности" и "какой-то способ указывать как ".", так и ".."".
      2. Про каналы -- наиболее полные соображения имеются в bigfile-0002.html за 10-07-2007.

      Схема именования и для каналов, и для ручек должна функционировать ОДИНАКОВО. Посему -- используем идеи и описания из обоих источников.

    • Стараемся делать всё СОВМЕСТИМО со всеми вариантами спецификаций в обоих тех случаях.
    • ОБЯЗАТЕЛЬНО нужна возможность "двигаться" не только вглубь от указанной точки, но и "вверх".
    • КАК такое мочь указывать -- у нас пока явно не было постулировано
    • В общем случае могут быть 3 варианта:
      1. Абсолютная (глобальная) ссылка: начинается с '.'.
      2. Относительная ссылка: начинается с [a-z0-9_].

        (Случай "/[a-z]+:/i=>глобальная" для ручек неактуален.)

      3. Относительная ссылка "вверх": самое хитро-неприятное.
        • С одной стороны, на роль способа указать "вверх" подходит только '.' в начале ссылки -- сколько точек, столько раз подняться уровнем выше.
        • С другой стороны, с '.' начинаются абсолютные ссылки. Печа-а-аль!
        • Но в bigfile-0002.html за 07-08-2007 предложен "легкий хак -- например, если ref[0]==':', то это первое двоеточие отбрасывается, а далее идущее считается относительной ссылкой".

        Итого -- относительные ссылки вверх будут иметь вид ":..." (двоеточие, за которым следует нужное количество точек).

        Вариант ":ИМЯ.ПОД-ИМЯ..." для просто относительных ссылок также легитимен.

    Новая функция универсальнее CdrFindKnob() -- с ТРЕМЯ параметрами:

    Knob         CdrFindKnobFrom(groupelem_t *list, const char *name,
                                 ElemInfo     start_point);
    
    • При start_point==NULL она НЕ воспринимает относительные ссылки (всегда возвращает NULL), превращаясь в только-глобальную.
    • При list==NULL она НЕ воспринимает глобальные ссылки.

    Заметки с реализации:

    • RecFindKnob() переименована в RecFindGlobalKnob() -- чтоб не смущала своим названием.
    • У DoFindKnob() убран параметр namelen, который всё равно не использовался и только вызывал gcc'шный warning.

      (Почему он вообще был введён в 2004-м -- теперь уже точно и не помню. Кажется, для упрощения жизни RecLoadLevel()'у -- чтоб тот напрямую использовал содержимое line[], не суя туда '\0'; но это так и не было доделано.)

    • Кусок DoFindKnob(), отвечающий за поиск ручки в элементе и его подэлементах, был вытащен в отдельную FindKnobInElem().
    • alarmonoffled_widget.c переведён на сию вещь.

      ...неприятность в том, что теперь появилась зависимость 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.

  • 24.10.2012@Снежинск-каземат-11: очень уж хочется уже тут и сейчас мочь «делать имена элементов "прозрачными"» (см. bigfile-0002.html за 22-12-2010).

    Причина -- надо перегруппировать содержимое 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: идеологический вопрос -- а не считать ли прозрачными все имена, НАЧИНАЮЩИЕСЯ с ':', а не только равные ":"?

    • Смысл -- чтоб такие прозрачные элементы оставались адресовабельными для прямых операций (типа будущей свернуть/развернуть и выбора закладки в tabber'е) из самого клиента.
    • И, замечание -- это касается именно самих ИМЁН элементов, в ССЫЛКАХ же синтаксис ":..." означает совсем другое.

      Собственно, вот этот факт и может представлять проблему -- ведь смысл в том, чтоб даже на "прозрачные" элементы можно было сослаться, а если механизм поиска по ссылкам на таких именах обломится (на относительных ссылках в текущий элемент -- точно да, т.к. оне выглядят ровно как ссылки вверх; использовать двойное двоеточие -- "::ИМЯ"?), то и весь смысл пропадает.

    Конкретно в Cdr.c проблем нет -- там всё просто и очевидно (аж в целых двух точках).

    Вот в datatree.c, конкретно в datatree_FindNodeInside(), похитрее...

    16.04.2014: ПО-ХОРОШЕМУ, надо делать еще другое изменение: считать прозрачными и ПУСТЫЕ имена элементов, а не пропускать их вовсе, как сейчас. Такой подход выглядит логичнее.

    А пропускать при сохранении -- только те, которые начинаются с '-'; это определяется функцией IdentifierIsSensible().

    Но это уж в 4cx (аналогично пустым меткам строк/колонок, вместо "?", приводящим к копированию из ручек, и "!-!" в качестве указателя пустоты).

    28.07.2014: да, НАДО считать прозрачными имена, НАЧИНАЮЩИЕСЯ с ':'.

    (29.07.2014) сделано, проверки были в 4 точках. Теперь гонять и проверять, не вылезет ли чего.

    28.07.2014: а можно ль при поиске мочь считать прозрачными корневые groupelem'ы с ':'?

    (У Феди такая потребность возникла -- элементы верхнего уровня являются средством снижения бардака, а смысловой нагрузки их имена не несут.)

  • 01.03.2013: уже очень сильно достала архитектура, когда верхний уровень дерева радикально отличается -- groupunit_t/groupelem_t. Из-за этого не только лишние сложности с изготовлением группировок, но еще и дико неудобно с ними обращаться -- и "вверх" не прогуляешься, и "контекст" не сделаешь (для решения проблемы задействуются 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, из которого нужное значение добываемо.)
    • И в собственно юзере -- alarmonoffled -- вставлена передача свежевведённого линка в 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.

  • 16.05.2013: все ругательства в Cdr.c переведены с fprintf(stderr,...) на reporterror() (скопированный из cda.c).
  • 27.08.2013: поскольку опять затишье, то есть еще более сильное, чем четыре года назад (27-03-2009 и 01-12-2009) желание проапгрейдить формат subsysdescr. Ибо очень уж сильно желание поиметь возможность ссылаться на каналы по имени еще ДО перехода на v4'шный стек KnobsCore/Cdr/datatree.

    27.08.2013: собственно, основной мотив -- есть возможность (слегка напрягшись) перевести сервер на v4'шную архитектуру под libsrv (на основе ppf4td), а тогда появятся server-side имена каналов, на которые можно ссылаться из клиентов, и занадобится таковая возможность в них.

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

    1. Поля logchannet_t.style и elemnet_t.style -- в концы структур. Никаких проблем не составит, да и нужно скорее для галочки.
    2. Поле elemnet_t.defserver -- чуток посложнее, и нужно в основном для уменьшения кривизны, а не необходимо. Просто раз всё равно формат менять -- то чё б не сделать.
    3. Главное: поле logchannet_t.phys сменить на более хитрую конструкцию.
      • Само поле останется в этой же позиции, но превратится из int в хитрую структурку с union внутри.

        Это гарантирует генерацию ошибки при попытке откомпилировать старые (неисправленные) DB/*.h-файлы.

      • Собственно структурка будет состоять из селектора варианта и union'а, содержащего 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.
    • В logchannet_t поле phys_rec pr вместо phys.
    • Продвинутое на единицу CXSS_SUBSYS_VERSION_MAJOR=5.

    25.09.2013: продолжаем:

    • Вводим (пока #if'ную) обработку phys_rec в CdrCvtLogchannets2Knobs(), с проверками.
    • Переделываем все DB/db_*.h=>_db.so-файлы, добавляя PHYS_ID(). Адская работёнка :)
    • Кстати, компилятор ПРЕДСКАЗАННУЮ ошибку не выдавал -- лишь "warning: missing braces around initializer". Т.е., просто строки с явным указанием phys в конце не вызывают ошибок.

      А ошибки генерятся совсем другими вещами -- например, указанием .minnorm=...; ведь такого поля в phys_rec нету.

      Так что -- анализ логов с поиском "missing braces"...

    Сделано, всё собирается и работает в обоих вариантах.

    01.10.2013: на пульту поменяно на прошлой неделе -- вроде работает; в остальных местах поменяем позже, потихоньку, а раздел числим за "done" (хоть поля style и defserver, как и предсказывалось, просто не используются).

    20.10.2013: состояние на текущий момент:

    • Оно уже больше полумесяца работает, особых проблем не вызывая.
    • Поскольку "в остальных местах" не сменено, то PHYS_REC пока присутствует, что неприятно.
    • Но одна феерическая история всё же случилась, хотя и НЕ из-за смены формата, а из-за произведённого заодно перехода с canadc40/candac16 на xcanadc40/xcdndac16.

      Почему-то у cangw-magsys постоянно была загрузка процессора под завязку. Настолько, что оно дико тормозило, каналы постоянно синели, а плавное изменение работало с дикими тормозами (а ИСТы не функционировали вовсе, из-за таймаутов по включению).

      Стал разбираться.

      1. Первой проверилась гипотезы проблем на линии -- последовательно выткнуты все устройства. Не помогло. И даже вынимание CAN-кабеля не помогло.
      2. Следующей гипотеза о проблемах SJA1000. Смена контроллера не помогла.
      3. Затем идея, что это БП так дурит, из-за чего SJA1000 может глючить. Тоже не помогло.
      4. Но, поскольку дурь повторялась стабильно, то напрашивалась профилировка -- с откинутым CAN'ом, чтоб полностью исключить внешние события.
        • Профилирование показало высокую активность ProcessPacket()'а и, самое странное -- ReturnChanGroup()'а, чего вообще при отсутствии железа ну никак не должно было присутствовать.
        • Запущенный сниффер показал какое-то дикое количество пакетов "Read" ОТ контроллера (но НЕ ему -- вот это так и не ясно).
        • Ручное разбирательство на тему "а кто же это?!", с добавлением отладочного printf()'а в remcxsd_driver.c::ReturnChanGroup() показало, что каналы -- НЕзадействованные у xcandac16 и xcdac20.

          Т.е., кто-то их постоянно опрашивал, а раз они несуществующие, то драйвер просто тут же отдаёт CXRF_UNSUPPORTED, и может это делать много.

          Причём вовлечены были каналы НЕименованных устройств.

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

          Расследование нашло виновника -- 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()).
    • Добавлено копирование и высвобождение в CdrCvt*() и CdrDestroy*() соответственно.

    05.03.2014: очень, ОЧЕНЬ зря не приурочил тогда к несовместимому изменению формата subsysdescr еще и смену архитектуры дерева -- с NULL-terminated массива groupunit'ов на "общий корень". Не так уж много тогда было проблем внести это несложное исправление в несколько десятков db_*.h-файлов. (Хотя сейчас посмотрел -- нифига, около сотни. Тогда было напряжновато и те-то исправления вводить, а еще это -- хбз, как бы получилось. Но всё равно жаль...)

    Сейчас же возникла потребность, а выпендриваться придётся по-другому (видимо, через ссылку на первый groupelem у каждого верхнего eleminfo (т.е., с uplink==NULL (Вечером: сделано, у ВСЕХ, а не только у верхнего.)).

    15.06.2014: еще вопрос -- а чё тогда заодно не сделал 4-й/0-й диапазон -- ALWD? Уж оно совсем бы проблемы не составило...

fqkn:
  • 16.01.2011: новый модуль, для махинаций с именами ручек. Тут он относится к уровню Cdr, и живёт в ней, а в 4cx/ сразу пойдёт в libdatatree.

    Заголовочный файл -- fqkn.h, реализация -- fqkn.c.

    16.01.2011: к его созданию привели следующие соображения:

    • 3-я "сущность" в Chl_scenario, кроме собственно knob'а и script-engine -- это API расширения имён (который в globact'е интегрирован). Ему передаётся функция-callback, которую надлежит вызывать по концу расширения.
    • Соответственно, добыча списка ручек-"клиентов" в liuadcs становится тривиальна -- это, аналогично scenario'вскому, также psp-плагинчик, "парсящий" шаблон имени через вышеозначенный API и складывающий результирующий список к себе в массив.

    Первоначальная мысль была назвать API "nameexp" или "knobname_exp" (префикс -- "kne_"), но потом подумалось, что сие действие явно будет не единственным по манипулированию с именами, так что -- пусть будет хоть и не совсем точное, но зато достаточно зонтичное "fqkn" (Fully Qualified Knob Name). Вероятно, что в v4 именно этот модуль будет отвечать за всякие относительности и вверховости (аналог "./" и "../" в файловой системе).

    Замечание: API изначально делается просто для имён, никак не привязанный к семантике, так что должен подходить и для v2, и для v4.

    21.01.2011: а ведь применимость этой вещи -- ШИРЕ, чем просто knob'ы. Еще и, как минимум, имена каналов в сервере.

    Так что -- не переименовать ли сие в "fqcn" и не перенести ли в lib/misc/?

  • 17.01.2011: приступаем.

    17.01.2011: вроде сделано -- с теми же способностями, что в globact, по тому же алгоритму, только проще и элегантнее.

    21.01.2011: да, проверил -- пашет.

    Конечно, сейчас оно совсем простенькое, ни ?/*/[], ни даже {X..Y}. Но нынешние задачи решает на все 100%, так что -- "done".

    02.02.2011@Снежинск-каземат-11: "пашет", ну как же!

    Видать, я потом пооптимизировал, уже после проверки, так что было всё плохо:

    1. В основном цикле do_one_stage() условие "обломимся по !isletnumdot()" стояло ДО условия на '{', так что по оной оно вместо обработки {}-списка считала имя добытым и заканчивала.
    2. В FindCloseBr() оно умудрялось отлетать по ',', считая, что '}' не нашлось.

    Исправил, конечно, но отсутствие хоть какой-то внятной диагностики ошибок изряднейше напрягает.

  • 22.01.2011: кстати, а ведь zsh'ный формат {X..Y} намного удобнее {X-Y} (про который я почему-то думал в Снежинске).

    Потому, что '-' -- вполне может быть просто куском имени, а вот последовательность ".." -- бессмысленна.

Cdr_script:
  • 20.01.2011: создаём такой модуль -- мотивы описаны в разделе Chl_scenario за вчера.

    Вкратце -- это будет реализация скриптинга на базе lightscript_engine с добавлением команды set (позже -- возможно, и других, типа записи/чтения регистров).

  • 20.01.2011: изготавливаем.

    21.01.2011: готово, работает. Фактически это просто мини-надстройка над LSE, реализующая свой набор команд, содержащий пока что одну-единственную команду "set".

    У него даже никакого своего privrec'а нет, а в качестве privptr'а использует grouplist.

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

datatree:
  • 31.03.2011@Снежинск-каземат-11: создаём модуль. Основной смысл -- держать в нём функционал, необходимый как libKnobs, так и libCdr; т.е. -- относящийся к "работе с деревом вообще", и с некоторыми свойствами узлов (не относящимися к деталям отображения) в частности; как это уже сделано в 4cx/.

    Побудительный мотив -- чтоб избавиться от появившейся позавчера зависимости Knobs-программ от libCdr.

    Жить библиотека будет в lib/Cdr/, файлы -- datatree.[ch], libdatatree.a.

  • 31.03.2011@Снежинск-каземат-11: делаем.

    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 убран.
    • В Knobs'ову HookPropsWindow() добавлена регистрация editstate_hook'а KnobsChangeHiliteState().
    • Реализация Knobs_internals.c::SetControlValue() заменена на эмуляцию -- вызов datatree_SetControlValue() с бибиканьем при >0.

      Проверено -- hilite работает.

      Выкидывать в пользу прямого использования datatree не стали:

      1. Тут одно место для бибиканья, а иначе пришлось бы ВЕЗДЕ вставлять if().
      2. Есть навалом вызовов в *_widget.c, и еще их трогать -- ну уж совсем вломы б.

      Кстати, по этому опыту ясно, что надо и в 4cx/ делать MotifKnobs_SetControlValue(). Часом позже -- сделано.

    • Реализация Cdr.c::SetControlValue() вообще убрана, и везде вставлены прямые вызовы datatree_SetControlValue().
    • Соответственно, _CdrSetControlValue() выкинута вместе с Cdr_private.h, а Cdr_script.c::set_exp() также переведена на datatree.
    • Cdr: избавляемся от
      • 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".

  • 30.08.2011: продолжаем населять -- теперь сервисные функции для добычи текстовых строк.
    30.08.2011: эту толпу делаем без префикса 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: битым текстом: имена ручек, начинающиеся с '-', НЕ ПРЕПЯТСТВУЮТ поиску этих ручек средствами datatree.

    08.12.2011: собственно вопрос "да или нет" встал, когда захотелось в liucc сделать, чтобы ручки [Выкл]/[Вкл] у УБС'ов и/или БЗ1/2 при загрузке режимов НЕ меняли бы своего состояния. Это делается просто -- начать имя с '-', но ведь эти же ручки должны быть адресуемы из скриптов в элементе "Общее управление". Тут и встал тот вопрос. (Потом оказалось, что незагружаемость требуется не в УБС'ах, а в зарядных, но это уже не принципиально.)

    Так вот: проверкой на этот префикс занимается IdentifierIsSensible(), применяемый только в Cdr.c -- там оно влияет в основном на участие тестируемого в сохранении и логгинге (даже на загрузку уже НЕ влияет).

  • 20.12.2011@Снежинск-каземат-11: заметил престраннейшую вещь -- при загрузке режима на мгновение меняются значения даже в readonly-каналах!
    20.12.2011@Снежинск-каземат-11: полез разбираться -- и-и-и...
    • Странно, что ничего такого не замечалось раньше.
    • Закавыка в том, что 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. Кстати, почему "такого не замечалось раньше" -- а на живой и нормально работающей системе этого обычно и не заметишь. Лучше всего видно на клиенте, у которого отсутствует сервер.

  • 24.10.2012@Снежинск-каземат-11: введена datatree_FindNodeInside() для поиска имени внутри указанного элемента с возможностью углубления в "прозрачные" (":") подэлементы.
  • 01.11.2013: такая неприятность -- ручки с флагами CXRF_OFFLINE+CXRF_REM_C_PROBL выглядят так же, как и CXRF_OVERLOAD.

    Сегодня неудобство вылезло на ВЭПП-4 в v4gid25x4n4. Там после включения каналы от vsdc2 оставались бордовыми из-за OVERLOAD (какой-то заскок коммутации), что смутило Утюпина, справедливо принявшего это за неработающесть устройства вообще.

    01.11.2013: источников проблемы несколько, и фиг знает, кто из них первичнее и с кем воевать:

    • REM_C_PROBL отнесено к АППАРАТНЫМ флагам.

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

    • У флагов HW приоритет перед SF при отображении.

      Первоначально это определялось SF-флагом CALCERR, который лишен практического смысла при бредовом из-за аппаратной проблемы значении.

    • Сам приоритет -- что отображается лишь одно состояние, считающееся более значимым. Но в реальности всё не так просто и линейно/монотонно.

    Потенциальные варианты решения:

    1. Перераспределить принадлежность флагов к группам HW/SF. Например, считать REM_C_PROBL за SFERR.
    2. Реализовать попеременное отображение цветов SF/HW при наличии флагов из обеих групп.

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

      И с флажком есть сложности -- КТО и КОГДА должен его переключать? Раньше хоть привязка к циклам была, причём они шли посекундно. А сейчас -- и циклы у разных серверов разные, и обновление частичное через conns_u. Разве что просто глобальный метроном в Chl, дрыгающий этот флаг раз в секунду.

      Но моргание доставать будет преизрядно...

    Предпочтительнее выглядит всё же вариант (а) -- он и проще в реализации, и менее раздражителен.

    03.11.2013: из любопытства всё же реализовал вариант (b):

    • Выбор состояния в зависимости от flipflop'а -- в 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 утекание есть и без этого перещелкивания, просто медленное -- по мере обычно индикации желтым/розовым.

  • 06.08.2013: сменено поведение внутренней FindKnobInList(), используемой datatree_FindNode() и datatree_FindNodeFrom().

    06.08.2014: убрано правило "a global name may include only ONE component", и заменено на "если глобальное имя (начинающееся на '.') содержит более 1 компонента, то оно считается за FQKN -- т.е., полным именем ручки от корня".

    Понадобилось для возможности адресоваться "к ручкам из других groupelem'ов" -- иначе никак (т.к. НЕглобальные имена считались от start_point, а глобальные отказывались быть многокомпонентными -- что и исправлено).

simpleaccess:
  • 17.12.2012: вводим новый модуль -- "простой" доступ к каналам, для возможности делать простейшие биндинги к python/tcl/...

    17.12.2012: технология проста:

    • "Клиент" сначала регистрирует канал, запрашивая его у CdrRegisterSimpleChan() в формате SUBSYSTEM.CHANNEL.NAME, а модуль по мере надобности грузит подсистемы (через descraccess), затем ища в них указанные ручки (по имени CHANNEL.NAME).

      Клиенту в качестве "handle" возвращается прямо указатель на ручку (Knob).

    • По мере надобности "клиент" может дёргать CdrGetSimpleChanVal() и CdrSetSimpleChanVal() для чтения/записи.

    Неясности/несделанности на текущий момент:

    1. Как делать уведомления клиента?
    2. Как работать с большими каналами?

    18.12.2012: по минимуму сделано -- надо проверять.

    ...чуть попозже -- проверил, чтение работает.

    А вот запись работать не будет -- она СЕЙЧАС сделана через datatree_SetControlValue() (кое в v2 функционально только для Chl-клиентов, из-за использования uplink->emlink->SetPhysValue), а надо -- через CdrSetKnobValue(), что потребует чуть большей маеты.

    19.12.2012: внутренности и концепция сильно переделаны.

    • При регистрации возвращается не сама ручка, а целочисленный "идентификатор канала".
    • Этот идентификатор -- индекс в GROWING-SLOTARRAY'е, где хранятся описатели всех зарегистрированных simple-каналов.
    • Описатель содержит в себе также идентификатор подсистемы -- так что теперь и запись сделана как надо.
    • Сделаны и уведомления.
      • При регистрации канала может указываться callback+privptr.
      • Все зарегистрированные каналы подсистемы организованы в список: добавлены 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.

  • 11.06.2013: пришла пора делать поддержку и больших/векторных каналов, по проекту из предыдущего раздела (bigc_n=ki->color, datasize указывают клиенты).

    19.06.2013: за прошедшие пару дней основа сделана. Для этого имевшаяся схема слегка переработана, так что поддержка "Sbigch" пользуется той же инфраструктурой "Subsys", что и обычные "Smplch" (но список ячеек свой). Так что сейчас и регистрация и callback'и работают.

    Вопрос же дальше в выборе подхода общения с данными:

    1. Либо совсем "просто" -- только callback'у передавать данные. Но тогда надо иметь свой буфер, да и нет в cda возможности узнать "статистику" -- retdatasize, retdataunits, rninfo.
    2. Либо отражать интерфейс самой cda -- чтоб клиентщина могла спрашивать выжимки данных и параметров.

    24.06.2013: всё больше склоняюсь к варианту (b) -- как в cda.

    25.06.2013: вроде сделано (причём age и rflags при отдаче конвертируются в int'ы, с параметрами аналогично (вместо int32)).

    Теперь вопрос делания соответствующего python'овского биндинга. В частности, там надо будет аккуратно обходиться с типом данных (byte/short/int).

    26.06.2013: за компанию добавил функцию для ОТПРАВКИ данных (переходник к сделанному вчера в cda).

???:
cda:
  • 08.12.2003: добавить дополнительное состояние 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: уже сделана :-)*/

  • 09.01.2004: все к вопросу о том, как переводить обычные human-readable/writable формулы в поддерживаемую cda польскую нотацию: у Таненбаума в книжке (кажется) по компьютерам описан алгоритм Дийкстры для перевода из обычной (инфиксной) нотации в польскую (постфиксную) -- тот, что с "вагонами едущими в Калифорнию или в Техас". Алгоритм чрезвычайно простой, и безо всякой рекурсии -- нужны только несколько буферов.

    В принципе, можно вполне эту радость реализовать, как в рамках 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 ("локальные" для окна/группировки/...).
    • Внутри выражений ссылки на регистры будут иметь префикс "%" (по аналогии с ассемблером AT&T), ссылки на каналы -- "$" (по аналогии с shell/perl). Префиксирование позволит легко различать регистры, каналы и функции.

      15.06.2007: а может, все-таки обойдемся без префиксирования каналов символом '$', оставив его для макрорасширений, а?

    • Все операторы, помимо +,-,*,/ будут записываться функциями. Например, "%" -- mod(x,y), "^" -- pow(x,y), "^2" -- pow2(x), etc.

      В принципе, можно было бы расширить дейкстровскую матрицу и на остальные операторы, в соответствии и их приоритетами в Си, но зачем? Это дало бы, во-первых, бардак с неочевидными приоритетами (как это и есть в Си), а во-вторых -- ненужное усложнение кода.

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

    • При дийкстровском алгоритме понадобится дополнительная оптимизирующая логика, чтобы фрагменты типа "1+2" переводились не в "push_i(1),push_i(2),add", а в "push_i(1),add_i(2)".

    06.08.2006: насчет появившихся с тех пор префиксов (условного исполнения и т.п.):

    • Префикс "ALLOW_W" так и делается -- перед командой ставится allow_w.
    • Префикс "PRGLY" -- отражается дополнительной командой prglyput.
    • А условное исполнение -- делается префиксом "if": if (expr) COMMAND.

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

    • Все локальные переменные должны ОБЪЯВЛЯТЬСЯ в начале "программы", например -- фразочкой
      var ИМЯ
      или
      var ИМЯ[РАЗМЕР]
      -- так объявляются массивы.

      Соглашение "l0..l99" нафиг не нужно. А вот временные (если они вообще нужны?) -- надо думать, как объявлять или именовать.

    • Адресоваться и локальные переменные, и врЕменные (регистры) будут по именам, а уж компилятор пусть назначает им номера.
    • Различать после этого переменные и регистры глубокого смысла нету.

    29.01.2007: как можно бы прямо в нынешнюю архитектуру впихнуть just-in-time-транслятор с нормального языка: ввести дополнительный опкод OP_COMPILE_I, которому параметром-строкой указывать "программу".

    Опкод уже ввел (='c'), он сейчас приводит к ошибке -- возвращает NULL и описание "фича пока не поддерживается".

    Нерешенные пока проблемы:

    • А ГДЕ хранить список имен уже "набранных" предыдущими формулами переменных и массивов?
    • Как эмулировать ныне существующий хак с OP_RETSEP?

    01.02.2007: ответы на вопросы предыдущего раздела:

    • Таблицу имен хранить в localreginfo, сделав оное закрытым типом, и чтоб не "юзеры" отводили место под регистры, складывая туда лишь указатели, а cda делала бы массивы там сама. (Да -- и имена должны храниться с префиксами подсистем, чтобы можно было в одно окно намешивать ручки из разных группировок; например -- "подсистема$имя".)
    • В случае формул, долженствующих компилироваться из двух с OP_RETSEP'ом между ними, исходник должен содержать ДВЕ штуки OP_COMPILE_I.

    09.06.2007: в свете желаемого введения TCL'я, и "множественных" формульных языков -- а что, если параметр OP_COMPILE_I сделать префиксируемым типом языка? Т.е.:

    1. #!tcl
      ТЕКСТ-НА-TCL'е

      - TCL'ный код.

    2. #!fla
      ТЕКСТ-НА-ФОРМУЛЬНОМ-ЯЗЫКЕ

      - обрабатывается по вышеприведенному проекту от 12-01-2004.

    3. =КАНАЛ

      - просто берется значение канала (это будущий синтаксис для CXv4, где НЕ будет никакого phys, а пока что оно будет компилироваться просто в CMD_GETP_???, CMD_RET).

    4. =ВЫРАЖЕНИЕ

      - упрощенный вариант синтаксиса "#!fla".

    И, кстати, ежу ж понятно -- формулы МОЖНО и НУЖНО уносить в отдельную библиотеку. Причем -- опять же с плагин-архитектурой, чтобы эти "#!ЯЗЫК" можно было добавлять по мере надобности.

    А вообще -- поддержку TCL'я-то (пока что НЕ для формул) можно будет худо-бедно ввести хоть прямо сейчас, в Chl.

    15.06.2007: а лучше -- для простоты и однозначности -- вариант =КАНАЛ сделать так: у нас ведь в исходных-формулах предполагается имена/номера каналов префиксировать каким-нибудь символом, типа '$', так? Ну вот пусть прямая-ссылка-на-канал и будет иметь вид $КАНАЛ. (А если имена каналов будут ка-есть, без префиксов -- то ==КАНАЛ; мнемоника -- что "==" это как бы "тождественно равно", т.е. "связать с каналом".)

    P.S. А еще можно тогда заодно упростить создание "синтетических" ручек -- отображаемых на регистр: ввести синтаксис %РЕГИСТР.

  • 19.01.2004: давно пора все-таки подшаманить Cda так, чтобы оно могло функционировать в паре с cxscheduler'ом, т.е., чтобы имело режим SELECT, в котором обходилось бы БЕЗ "сигналов", а только таймаутами.

    22.01.2004: точнее, таймаутами и слежением за дескрипторами.

    А собственно режим SELECT будет прост -- при этом понадобится всего навсего

    1. Перевести cxlib в режим SELECT.
    2. Не пользоваться методами NnnnSignal(), а прямо сразу в точке, где обычно вызывается NoticeSignal(), напрямую дергать cda_signal_handler(). Наверное, более правильным/простым будет заменить все обращения к NoticeSignal() на вызов внутренней функции, которая в зависимости от режима выполняет либо то, либо другое.
    3. Аналогично cxlib, вызов cda_setcdamode() можно произвести только один раз, ДО первого cda_new_server(); cda_set_environment -- аналогично.
  • 19.01.2004: по мыслям о cx-starter/staromakh возникла необходимость в еще одной фиче/опкоде.

    Проблема: загрузка таблиц для 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_CALCERR", который берет слово из стека, и если оно !=0 (как OP_REFRESH), то выставляется флажок CALCERR. Тогда можно при ошибке загрузки прописывать не NOOP, а именно CALCERR, и "кривой" канал будет светиться. Идеология с "!=0" позволит, в паре с OP_CASE, делать условное выполнение этой команды.

      Замечание: похоже, надо все-таки в Chl обрабатывать флаг CALCERR отдельно от CXRF_SERVER_MASK -- т.е., не включать в биты в окошке "Свойства", а демонстрировать еще каким-нибудь фоном (коричневым? али чего?).

    • В случае, если имеется десяток каналов с разными, но сходными таблицами, то как-то мрачно писать на каждый ОТДЕЛЬНУЮ функцию. Лучше бы иметь возможность передать функции параметр. Сейчас это как бы не лезет в "идеологию", поскольку в excmd_content_t есть место лишь под одно поле (это ж union), но физически память под 2 указателя там есть -- sizeof(double)==sizeof(void*)*2. Так что... Делаем полем excmd_content_t структуру с двумя членами, а макрос CMD_LINTRP_BYFUNC_I() становится двухпараметрическим?
    • Название "BYFUNC" навеяло мысль: а что, если сделать именно "рабочий" опкод, который не берет указатель на таблицу, а вызывает функцию? При первом вызове (который станет делаться в качестве "предвыборки" из 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% случаев нам нафиг не нужна никакая "особая" функция загрузки таблицы, а достаточно считать ее из файла (как в будущем она будет браться из БД).

    Посему стоит ввести еще один "преобразуемый" опкод --

    OP_LINTRP_FROM(char *tableref)
    где "tableref" было бы неким "абстрактным" именем, которое могло бы сейчас являться компонентом имени файла (~/cx/settings/APPNAME/APPNAME_TABLEREF.lst), а в будущем -- именем-ссылкой в БД.

    Проблема 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:

    • НЕОБХОДИМО ввести макрос "LAPPROX_FROM".
    • Надо заменить "excmd_lapproxld_func" на структуру, и там указывать ДВА указателя -- на собственно функцию, плюс параметр.
    • При этом проблема "где взять третий регистр для опкода FROM" решается тривиально: если func==NULL, значит -- надо читать из файла, имя которого присутствует в операнде.
    • Для унификации надо ВСЕГДА копировать выданную таблицу в свой собственный malloc'нутый буфер.
    • На знание APPNAME можно полностью забить -- читать файл из текущей директории, с именем "./%s.dat", где "%s" -- переданное нам имя. Такая схема (указывается имя, но без расширения) позволит в будущем прозрачно перейти на чтение из БД.

    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(). При использовании стандартного загрузчика -- он это будет делать сам, а в остальных случаях -- можно копировать результат.
    • Надо б как-нибудь избегать повторных загрузок одной и той же таблицы... Ну что -- сохранять вместе с таблицей также и имя и номер колонки? И, соответственно, вести reference counting. Но -- придется ввести понятие "список таблиц", чтобы было по чему искать "загружена ли уже такая".

    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: раскомментировал, проверил -- вроде работает.

  • 21.01.2004: а ведь в 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 -- копирование-то там осталось, хотя оно едва ли теперь нужно...

  • 21.01.2004: в 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).

  • 08.04.2004: занятная обнаружилась "хохма": регистры-то инициализируются в 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()'а, т.е., производилось только при неинициализированности. А в противном случае это значение оставалось лежать в стеке, засоряя/портя его...

    Пофиксено.

  • 08.04.2004: при осмотре NewDataProc() обнаружилась такая последовательность:
        DecodeData(si);
        RequestData(si);
        NotifyClients(si, CDA_R_MAINDATA);
    
    То есть сразу после расшифровки данных отправляется следующий запрос, и лишь потом дается знать клиенту. Это "не есть гуд" в случае, когда на основе полученных данных клиент принимает какие-то решения с записью в каналы (т.е., в случае обратной связи) -- при вышеприведенной схеме будет отсрочка реакции на один цикл. Лучше вставить уведомление клиента ДО отправки запроса, чтобы дать ему возможность реагировать.

    08.04.2004: перенес, делов-то. Теперь надо следить за возможными отрицательными последствиями.

    01.03.2007: да, возможные последствия -- пропуск "кадров". См. раздел о "cda_continue()".

  • 17.04.2004: возникла необходимость снаружи иметь функцию, делающую то же самое, что DoLinearInterpolation(). Очевидное решение -- экспортировать ее.

    19.04.2004: экспортировал -- переименовал в cda_do_linear_interpolation() и объявил в cda.h.

    Часом позже: переименовал в cda_do_linear_approximation().

  • 27.04.2004: Федя-jr еще раз поинтересовался, когда ж будет возможность грохать cda-соединения. После расспросов выяснилось, что реально ему нужен не 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".

  • 24.08.2004: еще давно заметил какие-то странности с "оранжевением не к месту" в thermosm. А теперь -- еще аналогичные приколы с новым ipp при использовании олеговского драйвера. Вроде как в cda приходит уведомление, что "число свежее", но реально это неправильно... Надо б разобраться!

    27.04.2005: да, и еще аналогичное оранжевение в phm-tsyline.c с полем "ЦАП мотора"...

  • 22.09.2004: понадобилось иметь операцию "получить текущее значение таймера".

    Это требуется для замены былой олеговой программы "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: собственно -- а почему она отдает только секунды? У нас ведь есть потребность и в большей точности, а результат все равно вещественный -- так пусть она дробное число и отдает.

    Так и сделал -- работы было на минуту.

  • 24.09.2004: уже давно заметил какие-то странности с реконнектами в cda: во-первых, staromakh через какое-то время "умирает"; во-вторых, подобным же страдают и Chl-клиенты.

    Надо б разобраться!!! Вот только как...

    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".

  • 06.10.2004: обнаружилось, что если в OP_{GETP_BYNAME,SETP_BYNAME,ADDSERVER}_I указывается тот же сервер, что является и базовым для этой формулы, то он зачем-то все равно разок добавится в auxsidslist.

    06.10.2004: все оказалось просто -- оно, найдя запрошенный сервер в servers[], проверяло, не добавлен ли он уже в auxsidslist. И если нет -- то добавляло. А проверки на тему, что он ==defsid -- не делалось!!! Вставил проверку.

  • 30.10.2004: почему-то в cx-starter'е дико множатся LED'ы серверов, указанных через "chaninfo=".

    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 РЕАЛЬНО РАБОТАЮЩУЮ поддержку больших каналов.

    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);
    

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

    • исходящие от клиента к серверу данные (data,datasize,dataunits) не используются вовсе;
    • исходящие (args) и возвращаемые (info) параметры -- суть одно и то же, просто неуказанные в запросе args никак не меняют текущих уставок большого канала.

    Символы "???" означают некоторые доп. параметры -- типа флагов 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.
    • Т.е., достаточно в этом месте передавать "что-то".
    • Это "что-то" надо делать один раз, сразу после запроса клиентом.
    • Для чего
      • ввести вызов типа "cda_setbigcdata()",
      • который бы складировал указанное во внутренний буфер (обычно у всех ==NULL), взводил бы флаг "а у нас есть данные!";
      • в момент исполнения cx_bigcreq() флаг бы проверялся, и если выставлен -- то данные передаются, а флаг сбрасывается.
      • Да, вещь однократная -- как бы "всегда shy".
    • Может понадобиться вместе загнать данные+параметры -- "атомарно" -- но для уставки параметров уже всё давно есть, и если просто вызвать подряд уставку параметров и данных, то оно как раз атомарно и уйдёт.

    Главный вопрос -- насколько подобная реализация адекватна нашей задаче, подойдёт ли оно для козачиных таблиц.

    ...После обеда: вроде сделано. В основном по вышеприведённому проекту, с несколькими деталями:

    • Дадена возможность указывать не весь буфер целиком, а кусками -- для этого в cda_setbigcdata() есть параметр ofs; т.е. -- всё симметрично чтению.
    • ...но, кстати, общий размер буфера оно ограничивает объёмом, указанным в качестве "retbufsize" при вызове cda_add_bigc() (иначе -- E2BIG).
    • При увеличении "использованного объёма" буфера она нулит добавленные участки (а после отсылки запроса sndbufused обнуляется).
    • Дополнительно указывается bufunits -- ведь что-то cxlib'у надо передавать. Тут уж на совести вызывальщика указывать что-то одно и осмысленное (в дело пойдёт последнее).

    08.09.2013: проверено, cda_setbigcdata() работает, причём совместно с cda_setbigcparams() вроде бы даже удовлетворяет требованиям weldclient'а.

    15.03.2014: в cda_getbigcparams() и cda_setbigcparams() НЕ проверялось на count!=0, и всегда вслепую делалось memcpy(); а формально не определено, как она должна действовать в данном случае. Так что добавлена проверка.

    Кстати, в cda_getbigcdata() и cda_setbigcdata() с параметром size аналогично.

  • 26.04.2005: хочется иметь возможность "приостанавливать действие" больших каналов.

    Это нужно, например, в phm-tsyline.c, чтобы там не посылались зря запросы, результаты которых все равно не устроят. А главное -- чтобы запросы не посылались "раньше времени" (т.е., чтобы не слался запрос на измерение осциллограммы раньше момента, когда будет подведен нужный фильтр).

    27.04.2005: с одной стороны, некая подготовка к этому уже имеется -- там предусмотрено (зачем? не ради этого ли самого?) поле bigcinfo_t.active.

    С другой стороны, даже "деактивация" канала не дает полной гарантии -- ведь может быть такой сценарий: (1) отсылка запроса; (2) деактивация; (3) активация; (4) приход ответа на запрос шага 1. И тогда в момент 4 НЕЛЬЗЯ будет понять, что это "не тот" запрос.

    Так что, может быть, реальное решение проблемы "гарантированной очередности" лежит в timestamp'ах запросов?

    26.02.2007: кстати, пока что потребность в "приостановке действия" больших каналов реализуется просто через приостанов их серверов -- в ndbp сейчас сделано именно так. ПОКА хватает, но вот в будущей версии надо будет реализовать так, как предложено в разделе "Отключаемость каналов в cda".

  • 26.04.2005: хотелось бы уметь узнать "локальный timestamp запроса" -- т.е., момент (timeval от gettimeofday()), когда был отправлен запрос.

    Это позволит иметь полную гарантию, что мы получаем ответ на запрос, отправленный ПОСЛЕ некоторого момента, а не фиг-знает-как-давно.

    27.04.2005: делов-то -- ввел поле serverinfo_t.req_timestamp, заполняемое перед вызовом cx_run(). И функцию cda_get_reqtimestamp(), позволяющую этот timestamp узнать.

  • 27.04.2005: решено -- надо сделать публичный интерфейс к 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: по опыту ругани с Лебедевым по поводу термоизмерений, которые сводятся к формульным из НЕСКОЛЬКИХ каналов: а не ввести ли интерфейс, позволяющий узнавать "srcof" ВСЕХ каналов формулы? Чтобы их всех можно было показывать в "Knob properties" (и -- вместе со значениями!).

    14.05.2005: в cda-то вставить проще простого -- хотя проще дополнительным интерфейсом, принимающим те же параметры, что и cda_srcof_formula(), плюс номер интересующего нас "исходного"; и чтоб оно кроме имени возвращало и текущие значения -- и double, и сырое. Естественно, нужнО и "сколько исходников у формулы?". Короче -- получается что-то очень похожее на интерфейс "cda_status_NNN()".

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

  • 20.05.2005: сегодня стало окончательно очевидно, что "формулы" -- это КОНЦЕПЦИЯ, играющая очень важную роль. И они могут надобиться в большом количестве разных качеств, ролей.

    И стоило бы, для удобства использования, несколько обобщить интерфейс, а именно (список почти копируем из раздела "Групповой инкремент каналов" за сегодня):

    • Уметь передавать формулам более одного параметра.
    • Уметь считывать не только значение physval, но и "userval" -- то, что уставлено пользователем, пусть еще не вернуто обратно сервером.
    • Уметь возвращать БОЛЕЕ одного значения.

    20.05.2005: идеи по ходу дела:

    • В принципе-то параметры можно передавать через регистры -- localregs. Также и множественные результаты можно возвращать через них же.
    • В чтении параметров можно было бы использовать тот же принцип, что в групповом инкременте -- если известно, что нечто было уставлено юзером, но еще не подтверждено сервером, то автоматом использовать именно "userval".

      Но это НЕПРАВИЛЬНО -- подобное может вылезти еще в куче мест. Так что -- лучше уж добавить еще API, типа "cda_getphyschanuserval()", который при вышеуказанной ситуации возвращает юзерское значение, а обычно -- просто вызывает cda_getphyschanval().

    • По-хорошему, надо б уметь передавать cda_execfurmula() некоторые флаги-опции. Вообще-то уже есть параметр is_write, который можно превратить в набор битовых флагов (как два года назад flags (нынешнее CDA_FLAG_OTHEROP) превратилось в rflags).

    Есть также пара "глобальных" замечаний:

    1. Если уж "омножествовлять" параметры, то надо это делать так: передавать пару
      double *args, int nargs
      и дополнить троицу {result,tag,rflags}_p параметром nret.
    2. Вообще-то нынешняя архитектура рассчитана на возврат ОДНОГО значения -- именно для него собираются rflags, для него выбирается наибольший из tag'ов. Как переделывать на множественность -- вопрос.

    А вообще, все, что тут понаписано -- это переход от ВЫЧИСЛИТЕЛЯ ФОРМУЛ к ПРОГРАММЕ, с входными и выходными параметрами.

    Вопрос только -- а реально ли мы сего хотим? Или стоит остаться при простом и элегантном -- как есть сейчас?

    Если хотим, то надо разрабатывать ДРУГОЙ API.

    06.06.2005: да, а еще -- если мы все-таки как-нибудь разрешим "команды записи в read-формулах", то тем самым получим работающий механизм ОБРАТНОЙ СВЯЗИ.

    12.06.2005: угу, мало того, уже СЕЙЧАС формулы используются (будут использоваться :-) для неслабого программирования -- camsel.

    13.03.2006: ага, а по сравнению с тем, что уже есть, плюс делается (reset) и планируется (demag) в linmag'е, camsel -- просто детский лепет :-).

  • 25.08.2005: почему-то многосерверные программы (например, ipp) при натравливании их на недоступные места (тот же отключенный hedgehog) начинают делать connect() МНОГОКРАТНО, и, в результате, стартуют ОЧЕНЬ долго.

    13.09.2005: выяснил, почему. Сначала делается первый connect, который обламывается. Он регистрирует таймаут на 1с. А потом делается второй, который тоже обламывается, но тоже после некоторого висения, в течение нескольких секунд. А когда дело доходит до "основного цикла", то Xt обнаруживает истекшие таймауты и дергает их, и так они по очереди истекают в течение висения друг друга.

    Попахивает мисдизайном, не правда ли? Или, как минимум, недодизайном...

    P.S. А все, кстати, оттого, что Xt пытается "слишком правильно" реализовывать таймауты. Вот cxscheduler -- тот бы просто прошелся по имеющимся на момент НАЧАЛА цикла, а потом в безусловном порядке прошелся бы по дескрипторам, выполнив их требования, и лишь затем устроил бы следующую проверку таймаутов. А Xt -- циклит, собака...

    14.09.2005: соп-пственно -- проблема-то исчезнет при переходе на неблокирующийся connect(), но то ж когда будет!

    01.06.2010: эта фича начинает уже ОЧЕНЬ сильно мешать: на ЛИУ, если какой-то из cPCI-крейтов отключен, то cx-starter подвисает, считай, почти до второго пришествия (а если там, не дай бог, нажать кнопку мыши -- то еще и мышь грабит (а если правую -- то и клавиатуру...)).

    Идея проекта временного решения:

    1. Пусть cda пытается подключиться к серверу не через 1с=1000000мкс, а, например, через 5с.
    2. А cxlib пусть вместо блокирующегося connect() пользуется функцией timed_connect() (она есть в архиве), с таймаутом 2с=2000000мкс.

    Обсуждение:

    1. Неприятная мелочь: при оживании сервера программы будут к нему подключаться не практически сразу (в среднем 0.5с), а с изрядной задержкой (в среднем 2.5с).
    2. Главная проблема: а при БОЛЬШОМ количестве таких не-срабатывающих соединений, не вылезет ли всё равно длинный зависон -- из-за того, что их таймауты по 2с, отрабатывающиеся ПОСЛЕДОВАТЕЛЬНО, перекроют тот диапазон 5с?
    3. И всё это из-за чёртова блокирующегося connect()'а, блин...

    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: вторая попытка:

    1. Можно же сделать параметр "таймаут для подключения" настраиваемым -- для локального случая, с ЛИУ, достаточно будет и 0.2-0.5 секунды вместо 2. Например, ввести cx_setconnecttimeout() (который бы при <0 ставил default-значение).

      (Если из-за тормозов на удаленной стороне (что возможно) она и не успеет уложиться в этот таймаут -- то через 5 секунд уже будет готова. Хотя и так тормоза бывают на уровне userspace, а уж ядро-то 3-way-handshake выполнит резво.)

    2. Для уставки из chl/Chl_app-клиентов ввести в options параметр "connect_timeout".
    3. А в cx-starter, которому это options до лампочки, ввести config-директиву ".connect_timeout USECS".

    Сделано по этому проекту. Но общее впечатление -- уродство, конечно, поскольку Chl_app и cx-starter вынуждены напрямую обращаться к cxlib'у, минуя стек библиотек, что есть однозначно неправильно.

    18.08.2010: и еще одно неприятное последствие вылезло, из-за того, что теперь оно пытается реконнектиться не через 1 секунду, а через 5 секунд (определяется значением DEFAULT_RECONNECT_TIME_USECS=5000000 -- кстати, это-то я нигде не записал), с cx-starter'ом.

    А именно: cx-starter определяет "запущенность" сервера через cda, и возможна ситуация, что по нажатию на кнопку "Старт (программы|сервера)" он вызовет запуск, и, хотя сервер уже запустится, но еще в течение 5 секунд это не будет известно, и если в течение этого времени опять нажать "Старт", то он попробует запустить сервер ПОВТОРНО. Реально от этого спасает то, что:

    1. на повторный запуск программ стоит guard как раз в 5 секунд, так что кнопкой запуска программы такое не проделаешь;
    2. просто запуск сервера с его лампочки делается через меню, так что там уложиться в 5 секунд -- надо специально стараться (хотя и можно -- проверено);
    3. сам сервер не позволит повторный запуск -- там все действия производятся в очень продуманном порядке, так что даже испортить ничего уже запущенному серверу оно не сможет (ни /tmp/cx-N-socket, ни /var/tmp/cxd-N.pid);
    так что вроде бы можно спать спокойно, но всё равно неприятно. Но тут уж ничего не поделаешь -- выход только в будущей правильной работе всего по select()'у.

    И, кстати, помечаем раздел как "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: а ведь возраст-то каналов надо отдавать с учетом "возраста" последнего пакета -- т.е., приплюсовывать количество_секунд-1.

    14.01.2006: реализация может выглядеть так:

    • В момент, когда программа спрашивает статус сервера, результат вычисления возраста складывается в некую переменную (это в качестве оптимизации -- ибо вызывать time() при каждом getphyschan*() -- шибко уж накладно). Халтурновато, конечно, но пойдет.
    • Для вящей крастоты сохраняется не количество_секунд, а количество_секунд-1 (если и так 0, то 0 и останется).
    • В момент прихода пакета с данными эта переменная сбрасывается в 0.
    • И -- она просто приплюсовывается в cda_getphyschanvnr() к значению phystag (только аккуратно, избегая переполнения, с максимумом в 255!).

    Конечно, несколько некорректно суммировать возраста, подсчитанные (для серверов с cycle_size!=1000000) "в разных временных потоках", но пока что вполне подойдет. Ведь цикл БОЛЬШЕ 1с у нас вроде не используется.

    А в идеале, конечно, надо уметь спрашивать у сервера период его цикла.

    14.06.2007: да, именно так!!! Надо переходить от нынешнего понятия tag_t/"возраст" к нормальным временнЫм интервалам, примерно следующим образом:

    • Сервер продолжает отдавать возрасты в своих циклах.
    • Клиент может запрашивать у сервера период его цикла -- в микросекундах.
    • Хранит же клиент возрасты уже в физических временах, и лучше в МИЛЛИсекундах (чтоб в int32 вместилось до 49 дней),
      ВозрастКанала(мс)=Возраст(цикл)*Период(мкс/цикл)/1000(мкс/мс)
    • Прибавляется же к возрастам каналов время с момента прихода последнего пакета в миллисекундах.
    Вот и решение проблемы "суммирования возрастов из разных временнЫх потоков".

    И еще -- надо каждому физическому каналу дать (в дополнение к r,d и q) свойство "период свежести". Детали:

    • Указывать и отдавать -- также в микросекундах, также пересчитывая в клиенте в МИЛЛИсекунды.
    • Это даст возможность иметь не единый для всех "OLDTAGCYCLES", а per-channel.
    • Период свежести имеет смысл указывать равным удвоенному (или упятеренному?) периоду измерения. Отдельный вопрос -- что делать, если этот период меньше, чем период сервера (хотя, это уже зависит от способа получения данных -- cached, once-per-cycle, on-measurement).
    • А периодим-свежести формулы можно считать наименьший из периодов вовлеченных в нее каналов.
    • Период-свежести по умолчанию (по аналогии с возрастом-по-умолчанию, равным 0) можно брать либо бесконечность (((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), предварительно обновив то поле "давность последнего прихода данных от сервера", которое будет прибавляться ко всем возрастам.

    Соображения по поводу:

    • Сам способ, конечно, слегка некорректен -- будет дёргаться "данные пришли", в то время как реально ничего не приходило.

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

    • Начинать генерить такие фальшивые "приходы" можно ТОЛЬКО ПОСЛЕ первого настоящего. Чтоб если каналы в состоянии JUSTCREATED -- то они б в нём и оставались.

    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, то:
        1. Всем каналам проставляется phystag=MAX_TAG_T (т.е., безо всякого хитрого сложения -- просто сразу "бесконечно старый").
        2. Сбрасывается was_data_reply.
        3. Вызывается NotifyClients(,CDA_R_MAINDATA).
    • По-хорошему -- еще бы делать то же самое при CAR_KILLED/CAR_ERRCLOSE, чтоб при потере соединения синело сразу, а не через 5-10 секунд.

      Но это -- потом.

    • Замечание: данная штука делает далеко не совсем то, что обозначено в первоначальной цели от 14-01-2006 ("возраст-то каналов надо отдавать с учетом "возраста" последнего пакета"). Но -- и этот способ даёт нужный результат.

    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'а), и тогда оно всё усиневает.

  • 20.01.2006: есть потребность как-то уметь производить операции типа AVG прямо в cda, БЕЗ использования Cdr. Например -- в формулах (как? например, по аналогии с lapprox иметь "подчиненную структуру", в которой и держать кольцевой буфер последних N данных. В любом случае -- имеется проблема: ЧТО считать "шагом", когда производить сдвиг? Ведь вызовы формулы могут быть и синтетическими, и по приходу данных от другого сервера...).

    Это -- по результатам беседы с Гусевым, рассказавшим, что от него на сварке (Медведко, Купер, Логачев, и прочие заказчики) надо иметь дело именно со СРЕДНИМ значением. И именно среднее значение должно потом участвовать во всяких вычислениях.

    Подобные слова уже проскакивали и у Лебедева -- см. секцию 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-формулами, и вообще много чем.

  • 23.01.2006: при анализе содержимого окошка "CX-starter logs" возникло желание разобраться -- какое cda-сообщение от какой программы. А хренушки! Cda-то про имя своей программы нифига не знает!

    Так что -- надо, по образу и подобию 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: на случай, если захочется-таки иметь в формулах математические функции, изобретен некоторый проект "стройной" реализации.

    20.02.2006: проект:

    • Все функции делаются ОДНИМ опкодом -- например, 'f'.
    • В исходных представлениях (logchannet, DB, etc.) конкретные функции указываются СТРОКАМИ -- т.е., попросту названиями ("sin", "atanh", etc.).
    • При cda_register_formula() строка переводится в "индекс" -- условный номер функции, являющийся именно индексом во внутренней таблице.
    • Функции реализуются не каким-нибудь монструозным if'ом или switch'ем внутри cda_exec_formula(), а вызываются ею по указателям, которые и записаны в той самой таблице.
    • Собственно содержимое таблицы таково:
      const char *name;
      fmlfunc_t   func;
      int         nargs;
      int         nresults;
      
      (последние два поля позволяют иметь централизованный контроль за stack underflow/overflow).
    • А для доступа функций к dbl-стеку им передаются указатель на dblstk[] и на dblidx.
  • 02.03.2006: достало, что для отладки формул и прочих странностей приходится вставлять отладочную печать в cda.c::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: надо, чтобы cda писала не только о пропадании соединения, но и о восстановлении (как это уже сделано в cm5307_drv.c).

    11.04.2006: сделано, практически по образу и подобию cm5307_drv.c, с использованием переменной was_suffering.

    Можно бы, конечно, и покрасивше -- чтобы при ПЕРВОМ установлении соединения оно писало "successfully connected to SRVRSPEC", а при ВОССТАНОВЛЕНИЯХ -- "restored connection to SRVRSPEC"; для этого лишь пришлось бы завести еще один флажок -- "was_connected", который уставлять в 0 изначально, и в 1 при соединении (и никогда не сбрасывать)... Но сие уже слабоважно -- так что "done".

  • 16.05.2006: возникло желание иметь из формул доступ не только к значениям каналов, но и к флагам...

    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: вылезла любопытная потребность: уметь указывать некий "базовый сдвиг" для всех номеров каналов (реально -- даже не в cda, а в группировке, для Cdr'а).

    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: вообще-то ответ -- в использовании БД и службы имен!

    А именно:

    • Во-первых, указанная выше проблема с D16 уже в основном решилась при использовании задуманной еще для ucam схемы адресации -- "имя_блока.имя_канала". Тогда можно было б просто указывать программе "имя блока", а уж при использовании runtime-макрорасширений с '$' или '%', задуманных 28-04-2005, все становится совсем просто.
    • Во-вторых, если принять схему, используемую в DOOCS/ddd -- что в описании "элемента/компонента" указываются ОТНОСИТЕЛЬНЫЕ короткие имена -- внутри некоего аппаратного куска, а при использовании компонента в обязательном порядке к ним prepend'ится базовое имя.

    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 от psp. Но по факту -- все юзеры первой являются и юзерами второй.
    • И еще -- при ошибке в этом случае cda_new_server() формирует _cda_lasterr_str, так что:
      1. Надо и остальные его отваливания по ошибкам перевести на этот же механизм.
      2. В его юзерах -- в частности, ChlRunApp()/ChlSetServer() потребуется это учесть.

      Пока же он для совместимости возвращает errno=EINVAL.

    Проверено -- работает, так что раздел наконец-то "done".

  • 14.06.2006: вторая "непристойная любопытная потребность": нужна также трансляция имен серверов. Т.е., если указывают "ring1:5", то реально коннектиться к "localhost:-8105".

    14.06.2006: нужно это будет для работы через файрволл: если в "БД" указана ссылка на некий сервер, то ссылка-то эта наверняка будет локальной, предназначенной для пультовой сети, а из-за файрволла надо будет, например, транслировать "linac1:59" в "localhost:-8159".

    Как бы это можно было делать?

    1. Например, при запуске программы, еще ДО установления соединения, регистрировать некую таблицу соответствия -- набор дуплетов {УказываемыйServerref, КудаРеальноКоннектиться}.

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

    2. Идея о clientside-макрорасширениях за 28-04-2005 -- тоже в эту струю: тогда бы трансляция заключалась в иных значениях макросов типа $LINVAC.

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

    Так что, этот пункт вкупе с предыдущим -- "базовым сдвигом" -- складывается в некую общую картину: этакая "автоматическая трансформация" ссылок в cda...

    21.01.2007: да-да-да, здесь тоже, как и в предыдущем пункте, ответ ТОЧНО в clientside/runtime-макрорасширений вкупе с БД!

    04.01.2010: тут, как и в предыдущем пункте -- потребность по-прежнему есть (и даже усилилась!), а макрорасширений и службы имён -- по-прежнему нет.

    Так что вводим трансляцию по идее за 21-11-2009 (пункт про "defserver" из Cdr): при создании нового сервера ищется переменная окружения с именем CX_TRANSLATE_nnn, где nnn -- указанный spec. Пара замечаний:

    1. Тонкость (скорее даже толстость): с одной стороны, надо бы делать проверку/трансляцию в cda_add_auxsid()'е как можно раньше -- чтобы сравнивать со списком уже приконнекченных серверов. С другой же, physinfo-то в программе имеется с оригинальными, НЕтранслированными именами, так что надо в cda_new_server() иметь и оригинальное имя. Так что там введена уродливая схема -- транслируется во 2-ю переменную, tr_auxsid, и сверка со списком-приконнекченных ведется по ней (если же трансляций нет, то в делается tr_auxsid=auxsid), а собственно cda_new_server()'у передаётся обычный auxsid.
    2. Поскольку zsh не позволяет в именах переменных использовать '.' и ':' (да и '-' тоже), то все !isalnum() заменяются на '_'. А всему остальному делается toupper().

    За саму трансляцию отвечает функциечка find_srvrspec_tr().

    Еще раз замечание: фичи "базовый сдвиг" и "трансляция имён серверов" не очень хорошо стыкуются, но это и не требуется. В основном потому, что обе они -- временный хак (и дико уродливый при том!).

    Поскольку проверено и работает (пусть и с ограничением -- в трансляции нельзя указывать base_*, по global_physinfo_db[] не найдёт), то наконец-то "done".

  • 03.01.2007: оказалось, что у нас нету команды CMD_SETLCLREG, без _I(). Резоном было, видимо, то, что переменные (локальные регистры), мол, должны адресоваться "статически", а динамическому выбору номера там делать нечего.

    Это неправильно -- так мы лишаемся возможности делать "массивы", индексируемые вычисляемым формулой значением (это понадобилось в ringcamsel'е для уставки значений регистров концевиков, в зависимости от выбранной в данный момент камеры).

    Надо сделать!

    03.01.2007: а вот и хрен-то сделаешь!!! По одной простой причине (и никаких "видимо было", "мол, должны" и т.д.): флажок "локальный регистр" (OP_ARG_REG_TYPE_LCL) указывается вместе с номером регистра. Упс...

    Так что единственный вариант -- это прямо в формуле прибавлять этот флажок к номеру отдельной CMD_ADD_I(OP_ARG_REG_TYPE_LCL). Так в ringcamsel'е и сделал.

    Выводы на будущее (в CXv4):

    1. Постараться все-таки целиком кодировать команду прямо в ней самой -- в опкоде, а не в операнде.

      Кстати, реально-то у нас опкод занимает 4 байта, а не 1 char. Вот и сделать там оставшиеся 3 байта "модификаторами"/"дополнительными параметрами".

    2. Если уж делать как сейчас, то лучше бы обычные переменные ("локальные регистры") были по умолчанию, а TMP-регистры (собственно регистры) -- с дополнительным флажком.

      Резон -- TMP-регистры адресовать динамически просто нет смысла, ибо они именно временные; переменные же более долгоживущи, так что имеет смысл пользоваться ими как массивами.

      (А то, как есть -- исторически сложилось: регистры-то вводились именно как врЕменные переменные, и лишь потом (в июне-июле 2003г.) были добавлены локальные регистры/переменные. И там даже есть комментарий -- "Такой подход удобен, но имеет недостаток: становится затруднительно обращение с регистрами как с массивом -- поскольку придется прибавлять к индексу еще и селектор".)

    3. И вообще, стОит сменить названия: врЕменные регистры пусть так и именуются -- регистрами, а "локальные" надо нызывать переменными.
    4. И, на будущее, когда будет транслятор с формульного языка в нынешнюю польскую запись: надо там иметь именно отдельно переменные (они ж будут по именам, так?), и отдельно массивы -- которые надлежить объявлять, с размером.

      И, кстати, поскольку в самой формуле объявлять нельзя -- ибо локальные переменные делятся между всеми формулами -- стОит объявлять все переменные сразу для всей группировки. Причем и массивы, и просто переменные.

  • 28.02.2007: сейчас модель работы 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".

  • 16.03.2007: обнаружилось некоторое, мягко говоря, неудобство того, что в CMD_{GET,SET}P_BYNAME_I() номер канала указывается вместе с вервером СТРОКОЙ. В исходниках-то он частенько вычисляется, типа "DEV_BASE+chan", а такие формулы в строку не загонишь...

    Так что надо бы уметь указывать ДВУМЯ параметрами -- (char *server, int chan). Вопрос лишь, как водится, в наполнении excmd_content_t (введем еще структурку? :) и в символе под опкод.

  • 19.04.2007: а все ж таки возникла надобность из формул иметь доступ (на чтение!) к АТРИБУТАМ knob'ов, которые имеются уже у Cdr -- к ДИАПАЗОНАМ. Причем даже не к своим, а к ЧУЖИМ!!! Нужно это -- для корректного подсвечивания красным значений в linmag'е, чтобы колоризация отключалась, если уставка совсем мелкая -- по минимуму 0, а в идеале -- при уставка<максимум/1000. И максимум-то хочется брать не зашитый в группировке, а ТЕКУЩИЙ...

    Потребность совсем не столь бредовая, как к 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. Нужно для всяких программ с обратной связью, типа demag'а, чтобы в случае, если кто-то другой начинает трогать ключевые управляющие каналы, то программа могла бы гневно бибикнуть и умыть руки.

    27.06.2007: да, видимо, надо завести некий опкод, который бы складывал в стек значение флажка OTHEROP -- 0.0/1.0.

    Только вопрос: а с КАКИХ флагов она должна брать этот бит -- с кумулятивных, или с флагов от последнего канала? Лучше, конечно, от последнего -- они и так уже имеются в phys_rflags, только надобно ее инициализировать нулем.

    Чуть позже: сделал, было бы о чем говорить.

    18.04.2008: так с тех пор нигде и не воспользовался, но все равно давно пора -- "done".

  • 18.04.2008: надо иметь API для уставки/чтения локальных переменных группировки -- функции cda_setlclreg() и cda_getlclreg(), чтобы не-chlclients могли бы прямо из своего кода иметь доступ к значениям регистров, НЕ выпендриваясь с имением/ведением для этого "формул". Конкретные потребности:
    1. (@10.04.2008) Суханов жаждет в следующей версии sukhphase'а мочь указывать разные параметры вычислений, как собственно коэффициенты егойных обсчетов, так и количество итераций для измерения "базы".

      Это явно надо делать локальными регистрами -- чтобы можно было отображать их на панели программы (и/или для модификации "на лету").

    2. (@17.04.2008)В программе для махинаций с ВЧ300 по проекту "температурные измерения тантала" (проект "tantal"?) в блоке Кузнецова -- там надо указывать начальный ток, конечный ток, сколько итераций, и время между изменениями уставки. Поскольку собственно отработка этих параметров будет осуществляться в не-chl-части программы, а ввод -- именно обычными chl-knob'ами, то надо маппировать эти ручки на локальные регистры, доступные программе.

    19.04.2008: собственно cda_-функции изготовил -- там делов-то. Они возвращают 0 при успехе, -1 при ошибке (например, плохой номер регистра), и +1 при чтении неинициализированного регистра.

    Другой вопрос, что информация о локальных регистрах скрыта внутри Chl'я, так что программам нужен API-"переходник", по аналогии с ChlGetChanVal()/ChlSetChanVal(). Так что -- сделаны, ChlSetLclReg() и ChlGetLclReg().

    Теперь осталось эту шоблу только проверить -- сие будет скоро.

    06.05.2008: да, проверил на тантальной программе -- работает. "done".

  • 01.05.2008: в продолжение предыдущего пункта: программам-то надо мочь реагировать на какие-то действия интерфейсных ручек, так? А это можно сделать вроде бы двумя способами:
    1. либо вводить свои типы ручек -- чтобы нечто, выглядящее кнопкой, было не LOGD_BUTTON'ом, а реально дергало бы какую-нить функцию-callback, либо --
    2. уставлять локальные регистры, а потом программа (или центровая "ручка") будет смотреть их значения -- либо по приходу данных, либо сразу (по 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".

  • 19.11.2008: изготовил функцию cda_strserverstatus_short(), возвращающая по коду статуса сервера краткое описание этого статуса. Реально -- нафиг не нужно, сделал исключительно для Роговского.
  • 19.11.2008: обратил внимание на странное поведение: ведь при закрытии соединения по ошибке -- например, 1006 (CXT_EINVCHAN), cda просто ругается что "Connection was closed on error: Request was successfully sent", вместо того, чтобы выдать описание реально принятой ошибки (тут -- CEINVCHAN). Почему? Кто тут виновник -- cda или cxlib, и где именно?

    (Кстати, это продолжается ОЧЕНЬ давно, практически с самого начала -- и я ни разу не обращал на это внимания, пока сейчас Роговский не спросил?!)

    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.
    • Радует, что в данном случае страдала только диагностика писавшая хрень, на собственно программном функционировании оно не сказывалось.
    • В cxlib'е имелся недогляд/misdesign, что перед передачей управления "клиенту" -- а вызов нотификатора именно оным и является -- ему не передавалась часть информации.

    И явно наипростейшим на настоящий момент является сделать errno=cp->errcode в MarkAsClosed(). Что и было проделано, после чего диагностика стала осмысленной.

  • 21.06.2009: вылезла дурацкая потребность -- мочь узнавать 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: понадобилось сегодня, для отображения вакуума в weldcc::"Blockings", иметь операцию "экспонента". А вот нету у нас в формулах возможности считать ни экспоненту, ни логарифм. Экспоненту-то можно посчитать как 2.7182818284590452354^x (хотя это и халтура), а вот с логарифмом -- никак...

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

    25.06.2009: так что -- введены пара опкодов, рядышком с PWR'ом, OP_EXP='e' и OP_LOG='E' ('l' и 'g' заняты под LABEL и GOTO, а 'n' -- под POLY). Ну и обработка сделана.

  • 22.12.2009: недавно вылезла неприятная особенность при округлении: если диапазон кодов устройства 0-255 отображается на экранный диапазон 0-100, что даёт r=255./100, то при вводе 100 получается 100*(255./100)=254.99999999999997, и при переводе в int32 округляется до 254 (это в программе balkon для ЛИУ, с пановским УБСом; и еще аналогичные же приколы были с DL200slow).

    Видимо, надо перед переводом в 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: в интересах cx-starter'а надо было иметь возможность обрабатывать формулу в режиме "readonly".

    11.05.2010: задача состояла из 2 частей:

    1. Возможность указать.

      Для этого былой параметр-флаг is_write переделан в options (аналогично Cdr'ову synthetic), и введены флажки CDA_OPT_IS_WRITE=1 (реализующий функции былого параметра) и CDA_OPT_READONLY=2.

      (Да, комбинация флагов IS_WRITE плюс READONLY будет выглядеть диковато.)

    2. Собственно реализация запрета записи.

      Сие решилось просто -- перед проверкой "а находимся ли мы в пишущей формуле, или был префикс разрешить-запись" в случае CDA_OPT_READONLY делается break.

    Теперь надо проверить.

    03.06.2010: проверено, работает -- "done".

  • 27.07.2010: "о сохранении и восстановлении rflags": надо добавить опкоды для сохранения и считывания rflag'ов. Назначение -- для случаев, когда флаги формируются где-то (например, в под-окне), а отображаться должны в другом месте (на индикаторе в основном окне).

    27.07.2010: идея проста:

    1. CMD_SVFLAGS='['-64 (Ctrl+[) складывает текущие флаги на верхушку стека.
    2. CMD_LDFLAGS=']'-64 (Ctrl+]) считывает флаги из стека, и ДОБАВЛЯЕТ (or'ом) их к текущим.

    Естественно, _I-версии отсутствуют по определению.

    В общем -- сделано, хотя пока не используется.

  • 28.07.2010: еще одна "прикольная" потребность: надобно в chlclients уметь "персистентно" хранить некоторые данные -- конкретно всякие там счетчики выстрелов.

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

    Напрашивается такое решение: сделать опкоды "записать число с верхушки стека в указанный файл" и "прочитать число из указанного файла на верхушку стека". Соответственно --

    1. запись в файл делать одновременно с записью в регистр;
    2. чтение -- исполнять одноразово при помощи окружения его конструкцией с дополнительным регистром с defval=0.

    29.07.2010: делаем... Конкретика:

    1. Добавлены опкоды:
      • OP_SAVEVAL_I='{'|OP_imm,
      • OP_LOADVAL_I='}'|OP_imm.

      Вообще, конечно, уже реально кончаются символы...

    2. Поскольку файла при LOADVAL может и не быть, то в стеке также указывается и значение-по-умолчанию, используемое при проблеме чтения.
    3. Для хранения выбран бинарный формат -- поскольку непонятно, с каким %-форматом писать число при использовании строк. А чтобы файл был также и человекочитабельным, в конец дописывается "\n%-8.3f\n".

      Если будем делать такую фичу для настоящего формульного языка, парсимого-из-строк, то надо будет добавить параметр "формат", и тогда проблема исчезнет.

    4. Файл пишется максимально секьюрным способом:
      1. при помощи mkstemp() создаётся временный файл;
      2. в него пишется число+строка (при nbytes_written!=nbytes_required выдаётся ошибка);
      3. делается rename() временного файла в запрошенное имя; при ошибке -- unlink().
      4. делается 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: да, сделал. Та еще "радость" была, конечно, но сделал -- так что и для подобных вещей эта фича тоже годится.

  • 07.08.2010: случайно заметил -- а ведь в cda_new_server() ВСЕГДА делается увеличение servers[] на 1 элемент -- и НИКОГДА не делается поиск свободной ячейки. Это следствие недееспособности cda_del_server() -- реально никакая программа никогда не тасует набор серверов, а только добавляет их.

    09.06.2014: уже давно не так -- со времени перехода на v4-alike-схему со SLOTARRAY в 2012-м.

  • 20.12.2010@Снежинск-каземат-11: давно уже нужны были функции округления.

    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'е) -- работает.

  • 03.02.2011@Снежинск-каземат-11: при запуске в Снежинске программы linipp выдавалась куцая диагностика, НЕ содержащая формулировки проблемы. А проблема заключалась в несуществовании тут хоста linac1, на который оно ссылалось по 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'ей.)

  • 15.02.2011@Снежинск-каземат-11: а почему, аобственно, у нас 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).

  • 05.03.2011: сходу -- сделана функция cda_sidof_physchan(), отдающая sid указанного physhandle (для нужд liu/plot/fastadc_knobplugin.c -- чтоб можно было делать еще ссылки на каналы, соседние с указанным в ki "базовым").
  • 02.08.2011: надо б превратить CENOHOST также во ВРЕМЕННУЮ проблему, НЕ препятствующую созданию соединения.

    Резон: оно, конечно, действительно есть серьёзная и, скорее всего, НЕвременная ошибка, но -- ведь иногда надо запускать программы для одного места (Снежинска) в другом (ИЯФ), где нету того набора хостов. И каждый раз ваять длинный список трансляции -- дюже муторно.

    02.08.2011: делаем:

    1. В cda_new_server() оно добавлено к списку "исключений", при которых просто запланировывается повтор.

      Также добавлено в 4cx/.../cda_d_cx.c::IsATemporaryCxError().

    2. В ScheduleReconnect() сделано, что при (ec=ERRNO)==CENOHOST оно увеличивает время до повтора в 10 раз. Причём -- "творчески", так, чтоб оно не превысило 2 миллиарда микросекунд.

      И в 4cx/.../cda_d_cx.c::FailureProc().

    Работает, "done".

  • 26.10.2011@Снежинск-каземат-11: (реально -- еще в конце прошлой поездки) возможно, поведение cda "после присоединения к серверу отправляем в него все уставки, что были сделаны за время его оторванности" далеко не всегда полезно.

    По-хорошему -- надо б уметь такое поведение включать/выключать per-server. Например, при помощи параметров, что идут в srvrspec после ',': persistent_data -- нынешнее поведение, shy_data -- НЕнастойчивость (т.е., забытие по соединению).

    26.10.2011@Снежинск-каземат-11: делаем:

    • Введен флаг serverinfo_t.shy_data, ...
    • ...и psp-ключики persistent_data (default) и shy_data.
    • В ConnectProc() при включенном shy_data для всех каналов делается .physmodified=MODIFIED_NOT.

    Проверить бы...

    27.10.2011@Снежинск-каземат-11: проверил (на медленном dl200me@rack0:44) -- работает.

  • 10.02.2012@Снежинск-каземат-11: в продолжение withdrawn-раздела за 16.05.2006 о желании "иметь из формул доступ не только к значениям каналов, но и к флагам": к одному конкретному флагу доступ иметь всё-таки стоит -- к DEFUNCT.

    10.02.2012@Снежинск-каземат-11: желание такое возникло при организации подсчёта "времени наработки" накала катода на ЛИУ. В момент реализации оного как-то получилось, что канал состояния горел "Вкл", но был посиневшим. Т.е., по-хорошему, программа должна иметь возможность в такой ситуации решать по своему усмотрению -- например, что НЕ засчитывать это во время наработки.

  • 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().
    • Реализация LOGSTR -- аналогична DEBUGP, условная. При неуставленном протоколяторе сообщение выдаётся на stderr.
    • Протоколятор -- 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:

    • Проверил -- работает; "done".
    • А ЧИСЛО в лог выдавать точно не захочется? Придётся еще один опкод искать...
    • Парой часов позже: добавил, что при флаге READONLY оно ничего не логгирует -- в основном, в интересах cx-starter'а.
  • 10.02.2012@Снежинск-каземат-11: а что у нас с детектированием stack underflow? При реализации в liucc/elem_rack.h по-WEIRD'ения при всех нулевых уставках вроде была ошибка -- вместо CMD_WEIRD_I(1.0) стояло просто CMD_WEIRD, вычерпывавшее значение из стека (которое как раз было 0 и потому ничего не синело), и, соответственно, CMD_RET в read-формуле должно было пытаться вычитать значение из пустого стека. Но НЕ РУГАЛОСЬ!!!

    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, хотя надо б в любом случае выборку из стека делать, а уж потом проверять "исполнять ли команду".
    • Вылезло на коде для linthermcan'а: там в стек помещалось некое число (результат вычисления), потом шел условный BREAK_I(0), и при невыполнении его в стеке оказывалось число 0 вместо вычисленного.
    • Просмотр всего показал, что больше нигде такого использования нет, так что исправление бага вреда не нанесёт.
  • 22.02.2012: сходу -- слегка поменял функционал cda_status_of(), волюнтаристски заложенный 22-07-2003 (см. bigfile.html): закомментировано приравнивание !is_running к DISCONNECTED, так что теперь она по нему стандартно смотрит время последнего прихода данных и возвращает NORMAL/FROZEN.
  • 30.08.2012@Снежинск-каземат-11: вылезла странность: в liu отображение тех стоек, которые вообще выключены, в какой-то момент почему-то становилось розовым (RED) вместо надлежащего JUSTCREATED. Чем немало смутило юзеров, ожидающих хотя бы DEFUNCT.

    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].
  • 18.10.2012@Снежинск-каземат-11: избавляемся в cda_new_server() от параметра rcn_on_ifail, полагая его всегда ==1 (как реально везде и есть). Сделано быстро и легко.
  • 19.10.2012@Снежинск-каземат-11: вводим поддержку режима работы через cxscheduler.

    19.10.2012@Снежинск-каземат-11: все изменения идут в "#if USE_NCXLIB". Собственно:

    • Весь для-внешний API касательно "ENVIRONMENT" делаем пустым.
    • Вместо дерганья "сигналов" функции для реакции на них вызываются сразу.
    • Таймауты -- уже напрямую на cxscheduler'е, для чего в serverinfo_t добавлена троица trec'ов.
    • ...и вот из-за них становится неадекватной старая схема с самостоятельно разрастаемым realloc-массивом servers[] -- поскольку trec'и должны располагаться по фиксированным (в момент enqueue'нья) адресам, и никуда уже не бегать.

      (Попробовал сначала запустить со старым вариантом, ага... Знатные получились глюки -- фиг поймёшь, почему segfault'ится или зависает, никакой gdb не помощник.)

    • Потому -- переводим менеджмент таблицы соединений на GROWFIXELEM.

    Вроде работает.

    08.11.2012: да, уж точно работает, "done".

  • 26.02.2013: а что мешает ПРЯМО СЕЙЧАС добавить именованые регистры/переменные, вместо номеров? Примерно по проекту от 01-02-2007, просто команды INIT/SET/GET сделать пока отдельными. Ни на какую совместимость это особо не повлияет, поскольку всё использование сосредоточено внутри библиотек -- ну да, API слегка изменится, т.к. добавится закрытый тип на замену нынешнему cda_localreginfo_t.
  • 12.09.2013: замечены странные косяки в нескольких программах на пульту с ring1:32 (в т.ч. ringmag) -- почему-то они НЕ реконнектятся после рестарта серверов. Сходу воспроизвести не удаётся, но глюк был и с самой распоследней версией.

    Единственная гипотеза -- глюк замечен в момент падения сервера по 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), хоть там менее актуально.

    Что странно:

    • Натравленный на живой процесс gdb показал, что cxlib'овских соединений аллокировано аж 4 (?!), при этом живо лишь [1],type=CT_DATA. Из оставшихся:
      • [2] имеет errcode=-16=CESOCKCLOSED, а type=CT_BIGC.
      • [3] вообще in_use=0, state=CS_OPEN_FRESH -- никогда не использовался. [0] аналогично.

      Вывод по разблюдовке: [0] принципиально не занимается, [3] появился из-за ALLOC_INC=2 (при затребовании второго соединения аллокировалась вторая пара); а [1] и [2] реально рабочие, но [1] достался chan-соединению и потом ожил, а [2] -- bigc и почему-то после облома более не задействовалось. Косяк, видимо, у cda?

    • У cda:
      • аллокировано двое (там FIRST_INDEX=0);
      • [0] -- живое, ST_DATA.
      • [1] -- какой-то мусор: там даже in_use=289, чего быть вообще никак не может.
    • Вывод: заскоки действительно у cda. Как так происходит, что (*servers_list)[1] вообще ничего вменяемого (т.е., это НЕ соединение почему-то фатально-невосстановимо закрылось)?

      Не в SLOTARRAY ли типа GROWFIXELEM проблема (тут -- ЕДИНСТВЕННАЯ точка его использования, даже в 4cx не оно)? Или -- верно ли определён тип для servers_list? А то как-то странно gdb показывает по print'у -- сразу содержимое, а указатели почему-то нет.

    24.11.2013: безотносительно возможных странностей с cda и показом в gdb: в cxlib'е имелся глючок, внесённый 18-09-2013 -- сюрприз!!! -- как раз во время борьбы с титульной проблемой, при добавлении WipeUnconnectedTimed(). Суть -- там НЕ сбрасывалось clp_tid=-1 при снятии таймаута по ненужности.

    В результате мог бы быть такой сценарий:

    1. Для подчистки выдавался некий таймаут номер N, который в момент успешного соединения снимался.
    2. Но в поле clp_tid он оставался.
    3. Затем при закрытии соеденения по обрыву делалось sq_deq_tout() еще раз.
    4. ...но таймаут N уже использовался кем-то другим -- например, как раз cda, для заказа реконнекта.
    5. В результате реконнект никогда не происходил.

    Диаграмма исполнения такое вроде бы не подтверждает -- sl_deq_tout() вызывается ДО CallNotifier()'а. НО: такое поведение наблюдалось практически всегда в программах с несколькими соединениями (пример -- fastadcclient-based), типа того v5adc200_grp2. А там как раз глюк cxlib'а с одним соединением мог подгадить второму.

    ...а не такая же ли причина у аналогичной проблемы с не-реконнектами у remdrv?

    А в сервере-то (cxsd_fe_cxv2.c) аналогичный ляп был!

  • 07.03.2014@лыжи-16:00: надо бы добавить троицу опкодов для БУЛЕВСКИХ операций: BOOLEANIZE, AND, OR. Нужно для реализации логики работы дверных блокировок в db_liu.h: булеанизация -- для симуляции (когда "биты" входного регистра не только 0/1), AND/OR -- для собственно логики и возможности маскировать блокировки.

    11.03.2014: сделано. OP_BOOLEANIZE='b', OP_BOOL_OR='O', OP_BOOL_AND='&'.

    12.03.2014: проверено, работает.

  • 17.03.2014: производим разделение cda.c на "ядро" (поддержание соединений) и "сервис" (формулы, lapprox). Делается это для возможности использования cda+cxlib в драйверах, чтоб не тянуть туда лишнее.

    17.03.2014: типа протокола:

    • "Сервис" уводим в cda_math.c. Он вообще почти никак не завязан на "ядро" (за одним-единственным исключением, о котором ниже).

      Но при этом становятся недоступными некоторые служебные внутренности cda.c. Поэтому:

    • ...инфраструктуру "lasterr" (last_err) унесены в cda_err.[ch], почти скопированную с 4cx'ной, ...
    • ...а config_debug_* переименованы в _cda_debug_* и опубликованы в cda_internals.h, ...
    • ...вместе с reporterror(), превратившейся в _cda_reporterror().
    • Поскольку CheckSid() используется в начале cda_register_formula(), то он переименован в _CdaCheckSid() и аналогично опубликован.

    Вроде всё работает, так что "done".

cxlib:
  • 10.01.2004: поразмыслить о возможности "расшить" 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'а этот шаг будет, как и предсказано выше, просто пропускаться.

  • 02.08.2004: имеются какие-то странности с поведением соединения, когда хост на том конце паркуют (т.е. shutdown -h).

    В разных программах по-разному: 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: еще небольшая серия разбирательств:

    • Причина/место -- когда в 3-м здании пришлось (из-за утренней просадки питания) выключить/включить hedgehog, а программа ausacc на viper'е энное время продолжала показывать синюю лампочку. Причем другие, запущенные позже экземпляры -- отлично связывались.
    • Программа висела так около 2 часов, а потом -- взяла да и прочухалась. Казалось странным, но...
    • "find /proc -iname '*keep*'" нашел 3 файла-параметра в /proc/sys/net/ipv4: tcp_keepalive_time=7200, tcp_keepalive_probes=9, tcp_keepalive_intvl=75.
    • Итак -- со временем ожидания в 2 часа все стало ясно: 2*3600=7200. Странно, почему РАНЬШЕ, при летних испытаниях связь восстанавливалась раньше. Видимо, оттого, что шли запросы.
    • Что же до общей картины: первые два параметра документированы, а вот третий -- крайне слабо, только комментариями в коде. Но удалось (из тех комментариев и ethereal'овского дампа) составить следующую картину:
      • При отсутствии какого-либа обмена по сокету ядро шлет через tcp_keepalive_time секунд пакет ACK.

        Если все окей -- то получает ответ и успокаевается еще на tcp_keepalive_time секунд.

        Если та машина как раз была перезагружена -- то она отвечает пакетом RST ("не знаю я такого соединения"), что отдает наверх "ready for read" и ECONNRESET, и, в случае cda, соединение устанавливается заново.

      • А вот если ответа не приходит -- то система продолжает слать пробные пакеты раз в tcp_keepalive_intvl секунд, и tcp_keepalive_probes раз. И по истечении этих попыток -- объявляет соединение порванным (реально не проверял, но, судя по исходникам ядра -- net/ipv4/tcp_timer.c -- отдает ETIMEDOUT).
      • BTW, в самом ядре времена хранятся не в секундах, а в "квантах", которых HZ в секунду (при В/В через /proc делается деление/умножение на HZ).

    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'ах вываливуемую туда информацию можно б было прочитать. (Собственно, это просто надо было выполнить при старте крейта, и сей файл просто удачно попался под руку. Никакого глубокого смысла тут нет.)

  • 30.10.2004: похоже, скоро в cx-starter'е перестанет хватать предусмотренных в cxlib'е 30 соединений (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".

  • 29.11.2004: надо бы при сообщении "connection closed" вначале писать имя программы. Лучше бы всего -- вообще при ВСЕХ reporterror()'ах.

    29.11.2004: сделал -- завел статический буфер progname[40], в который в ConnectTo() копируется известное там имя -- будь то указанное пользователем argv0, либо из program_invocation_short_name. А reporterror(), соответственно, если в буфере непусто, префиксирует сообщения строкой "program: ".

    29.11.2004: кстати, наконец-то заметил, что в конце сообщения о "connection closed" отсутствовал '\n' -- вставил.

  • 05.03.2005: ох и неудобен же символ '!' в качестве разделителя номера/имени канала! Проблема в том, что shell использует его в качестве префикса истории, и с радостью расширяет, так что приходится строки типа hedgehog:9!1 брать в апострофы (даже обычные двойные кавычки не годятся).

    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" в ссылке на канал также используется '.' вместо былого '!'.

  • 20.05.2007: придумал, как правильно избавиться от кучи проблем с cxlib'ом -- и от нерасшитого connect()'а, и от неадекватности для графических программ (в т.ч. -- невозможности использования fdiolib'а), и от слишком "завязанности в себе", и как следствие -- непортабельности и, самое страшное, от негодящести под Win32:

    Надо разделить 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.

    • БД -- как ее реализация, вместо/дополнительно-к нынешним blklist*.lst, так и добавление ее в протокол, чтобы клиенты могли и получать описания экранов, и добывать калибровочные параметры.
    • Научиться-таки пересылать по сети вещественные данные.
    • Нормальный формульный язык!

    15.01.2009: еще вариант реализации "SIGIO mode" в CXv4'ном cxlib'е -- а что если сделать его не РЕЖИМОМ работы, а опциональным доп.модулем, который бы сам следил за SIGIO, и дергал бы callback'и (либо cxlib'овские, либо fdiolib'овские)?

    Т.е., не "полнофункциональная" реализация транспорта, а именно "способ узнавания и готовности дескриптора", а собственно транспорт -- на некоем общем модуле.

    11.05.2013: сделано по развитию этого проекта больше полугода назад, так что тут "done".

  • 05.08.2010: узрел неприятность: на машине wolf у каждой из запущенных cx-starter'ом программ в /proc/PID/fd/ висит МОРЕ открытых дескрипторов.

    Причина очевидна -- это сокеты соединений с серверами, фиг знает почему остающиеся незакрытыми.

    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 аналогично.

  • 19.04.2011@Снежинск-каземат-11: сходу -- в cxlib добавлено сохранение переданного функциям открытия соединений host.

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

    19.04.2011@Снежинск-каземат-11: детали:

    • Для хранения введено поле CxConnection.srvrspec[64].
    • Сохранение производится в ConnectTo().
    • Во все вызовы reporterror(), относящиеся к соединениям, добавлена и выдача этого имени.

      Формат теперь почти везде -- "%s[%d/\"%s\"]".

  • 01.10.2012: сходу: CopyReceivedBigcs() при dataunits==0 радостно делила на 0 (в count=datasize/dataunits) -- позор! Исправлено, в стиле проверок в ReturnBigc():
    1. if (retdatasize == 0) retdataunits = 1;
    2. Проверка на корретность самого dataunits -- !=1,2,4, retdatasize%retdataunits!=0; в этом случае кроме ругательства делается datasize=0, dataunits=1.

    02.10.2012: заодно проверяем прочие места -- где б еще могли быть такие косяки.

    • remdrv_drv.c::remdrv_fd_p(), REMDRVP_BIGC. Туда скопирована проверка из cxlib'а.
    • Также обнаружилась пара мест "в другую сторону", в которых надо бы, но вломы тратить время:
      1. cxsd_driver.c::inserver_req_bigc(): там аж комментарий был, что надо проверять параметры.
      2. cx-server_bigc.c::ServeBigcRequest() -- это вообще тихий ужас какой-то...
  • 09.10.2012: еще некоторое время назад возникло стойкое желание сделать новый вариант cxlib'а -- на основе 4cx/'ного, т.е., на cxscheduler+fdiolib (следовательно -- без SIGIO и с расшитым 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():
      1. cda -- для не-rcn_on_ifail-соединений. Таковых реально нет (все передают 1), так что можно вообще параметр rcn_on_ifail и всё с ним связанное убрать.

        18.10.2012@Снежинск-каземат-11: убрано.

      2. tsycam.c в режиме cxlib -- а не забить ли на это уже?

      Вывод: сделать пустым.

    • cx_isready() -- только самим старым cxlib'ом.
    • cx_result() -- помимо самого cxlib'а, еще
      1. cx-rdt_wrt.h для cx_sub -- используется в непонятных целях (для отваливания НЕ из обработчика, что ли?).
      2. cx-console.c -- для добычи кода ошибки перед самозастреливанием.

      Вывод: возвращать в errno код ошибки и делать

      return errno == 0? 0 : -1
    • cx_entercritical()/cx_leavecritical() -- cda, и более нигде. Точнее, в QSGN'е, но с libcx_async это всё станет неактуально.
    • cx_setnotifier():
      1. cx_console.c, cx_rdt_wrt.h для cx-sub.
      2. tsycam.c.

    17.10.2012@Снежинск-каземат-11: продолжаем наполнение.

    • Критичная штука -- открытие соединения. Реальная работа делается в do_open() -- копии 4'шной cx_open(), а cx_{connect,openbigc,consolelogin}_n() просто вызывают её.
    • Вместо былой архитектуры "finalizer'ов" для CT_CONS нужное делается прямо в async_CS_OPEN_LOGIN().
    • Старый reporterror() заменен на v4'шный cxlib_report() 09.10.2013: в который сейчас добавлена выдача cd.

    18.10.2012@Снежинск-каземат-11: продолжаем.

    • Сварганены мелкие служебные функции, нужные для старых программ -- см. вчерашний анализ.
    • Скопировано (с мелкими переделками) основное "мясо" -- блоки DATA, BIGC, CONS.
    • Единственная серьёзная НЕсделанность -- это отсутствие реальной работы 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).
    • Большие каналы в GUI (v2hw/fastadc/) работают.
    • Максимальный тест был проведён на клиенте liu -- тоже пашет. Фантастика!

    Делаем возможность использования нового варианта библиотеки:

    1. Создаём ссылки LIBCX_ASYNC и LIBCX_SYNC в TopRules.mk.
    2. Указание на использование делается уставкой USE_NCXLIB=YES
    3. ...кстати, cdrclient, staromakh и descr2html с ними собираться не будут -- поскольку они без Xh, а напрямую на Xt. Но невелика проблема -- после перевода cda с сигналов на cxscheduler их также перевести на cxscheduler.
    4. ...и chaincx_drv тоже никак и никуда. Впрочем, от него давно пора избавляться -- бесполезен, и никогда полезен не был.

    Кстати, отдельный вопрос, мучающий меня уже несколько дней: как конкретно надо будет переделать внутренности 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%.

    • Можно лишь предположить, что Xt не очень эффективно работает с большим количеством файловых дескрипторов (там их 80 штук). Но провести профилирование -- придётся.
    • Парой часов позже: попробовал отпрофилировать -- фиг, получил статистику только по самой программе (CdrProcessKnobs() -- 24%). Ведь Xt-то у нас собрана БЕЗ "-pg". (А как вообще называются profile-capable версии библиотек, и из каких .rpm они берутся?)
    • Еще чуть позже: неа, это тут ни при чём. Проблема явно та же, почему не отрабатывает reconnect -- что-то не так с fdiolib/cda; при ECONNREFUSED или чём-то подобном сокет всегда готов на запись, Xt это честно сообщает, но никто не чешется -- вот программа и жрёт сколько может CPU.
    • ...когда всё приконнекчено -- ничего лишнего программа не жрёт, всё как у старой. Так что обвинение Xt насчёт неэффективности при большом числе дескрипторов -- снимается.
    • И еще чуть позже: похоже, проблема в Xt. Я проверил -- и fdiolib, и Xh_cxscheduler ведут себя нормально, а вот Xt... 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
      
    • Видимо, Xt, спросивши про дескриптор "events=POLLOUT", а в ответ получив revents=POLLERR|POLLHUP, офигевает (в ответе отсутствуют биты запроса?), и вместо того, чтобы просто вызвать зарегистрированный обработчик -- не делает ВООБЩЕ НИЧЕГО.
    • Вопрос -- что можно сделать? Можно ли как-то объяснить Xt, что надо ВСЕГДА вызывать обработчика? Или объяснить ядру, чтоб НЕ возвращало "не то" (эт вряд ли, "готовность на запись при connect()" -- фишка select()'а)...

      ...попытка сбагрить XtInputWriteMask|0x08 -- программа сразу вылетела с диагностикой

      Error: invalid condition passed to XtAppAddInput
    • вечер-Снежинка-холл в интернетах советуют использовать в дополнение к XtInputWriteMask еще XtInputExceptMask -- непонятно, с чего бы; но можно попробовать.

    21.20.2012@Снежинск-каземат-11: продолжаем:

    • 21.20.2012@Снежинск-каземат-11: попробовал -- фиг.
      • strace показывает, что оно пытается спрашивать events=0.
      • Анализ исходников (libXt-1.0.2-3.1.fc6.src.rpm -- взято из SRPM'ов CentOS-5.2 на dist) показал, что там есть явное определение
        #define XPOLL_EXCEPT 0

        Это уже преконкретнейший баг, после этого нет никакого смысла что-то рыть -- всё равно такой Xt нормально работать не будет.

        Разве только прошариться по багрепортам и проверить вариант в RHEL6...

        14.01.2015: реальная проблема из-за неподдержки exception-fd в Xt (предсказанная еще тогда): НЕ работают USPCI'ные драйверы в 4cx'ных GUI+server-сборках (stand).

    • Думал уже написать "всё, засим пока прекращаю работу с ncxlib и поиски решения проблемы", но попробовал еще один вариант -- вместе с XtInputExceptMask указывать XtInputReadMask. Бинго!!! Всё заработало -- оно ЛОВИТ ошибку!
    • Итого:
      • Ловит оно из-за особого устройства fdiolib'а -- там сначала проверяется тип (FDIO_CONNECTING), и более ничего. Вот потому оно и обрабатывает ошибку, полученную формально другим (не-WR) путём.
      • Конкретно в Xh_cxscheduler'е надо просить не просто InputWrite, а плюс InputRead (и плюс Except?).
      • Но просить это для ВСЕХ спрашивающих о готовности-на-запись -- некорректно.
      • Посему введём дополнительный флажок -- SL_CE ("Connect Error"), который актуален только для Xh_cxscheduler'а, и тот станет при наличии этого бита жаждать слегка другую маску.
      • Ну а fdiolib для FDIO_CONNECTING-дескрипторов этот флаг укажет. Никому другому он помешать (вроде бы) не должен.

    Сделано -- вроде пашет.

    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).
    • Выкидываем "environment" -- cda_set_environment(), cda_signal_handler(), cda_timeout_handler(), CDA_DECLARE_XT_ENVIRONMENT(), CDA_REGISTER_XT_ENVIRONMENT().
    • Удаляем старую lib/cxlib/, а ncxlib/ переименовываем в просто cxlib/.
    • ...заодно и в 4cx/ переходим от связки libcx.a+libcx_sync.a к libcx_async.a+libcx_sync.a.

    28.11.2012: подпиленная вчера версия проверена на x86 и на x86_64 -- работает, так что считаем за "done".

    25.01.2013: присутствовал баг, унаследованный от 4cx/: при отведении слота НЕ делалось fd=-1, что приводило к фееричным глюкам при не-резолвинге сервер-хостов. Детали в bigfile-0002.html за сегодня.

    09.04.2014: обнаружился дурацкий ляп: в do_open() присутствовало сравнение spechost!=loclhost ДО получения spechost.

    • Обнаружено было valgrind'ом (при поиске причины падения liu).
    • В какой момент появилось -- фиг знает (и лень разбираться); возможно, еще в 4cx/.
    • Самое смешное, что на функционировании это вообще никак не сказывалось -- условие отвечает за мелкую оптимизацию (чтоб не вызывать лишний раз gethostbyname() при ссылке на localhost).
    • Исправлено синхронно в обеих системах.

    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.

    Вроде работает...

  • 07.05.2013: замечена странность: при Ctrl+C серверу клиент закрытия соединения не замечает, а при отправке следующей команды вякает "Broken pipe", а при попытке еще более следующей -- "Connection is busy".

    11.05.2013: разборки показали причину -- различие в схеме работы старого и нового cxlib'ов:

    1. Старый ловил события сразу при появлении -- в обработчике SIGIO, там же пытался делать read() и реагировал на результат/ошибку.
    2. Новый реагирует только синхронно, и в случае с cx-console сначала пытается отправить и обламывается, так и не доходя до чтения.

      (Был проведён тест: в cx-rdt добавлен sleep(10) перед cx_run(), во время которого сделано Ctrl+C -- результат вышел ровно тем же.)

    Т.е., схема работы В/В изменилась, а схема реакции (построенная на соответствующих допущениях, верных для SIGIO) осталась старой (хотя допущения уже не верны).

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

    1. Общее: в самом cxlib'е надо бы везде при ошибке отправки грохать соединение. Конкретно -- SendRequest() может вернуть -1 ТОЛЬКО при ошибке отправки, так что не только возвращать клиенту -1, но и сразу MarkAsClosed().
    2. Конкретно в cx-console: оно при cx_execcmd()<0 лишь вякает ошибку, но НЕ отрубается.

      Видимо, изначально был расчёт на то, что кроме "транспортных" могут возвращаться и "логические" ошибки (результат выполнения команд -- типа "неверный идентификатор для kill"). Но по факту СЕЙЧАС этого нет, и ВСЕ ошибки -- транспортные.

    Сделано -- в обеих точках. Проблема решается в обеих местах (проверено поотдельности). Единственная неприятность -- kill'нутые клиенты вместо "killed" тоже ловят "Broken pipe", но это исправится при реализации следующего пункта (асинхронная ловля по SIGIO).

    23.03.2015: конкретно ТЕПЕРЬ, при чтении stdin'а через FDIO_STRING, всё уже работает прекрасно -- при обрыве соединения (в т.ч. по Ctrl+C) ругается и отваливает.

    Так что закрываем случай.

  • 11.05.2013: надо б в конкретно cx-console добавить АСИНХРОННУЮ реакцию на события -- чтоб они ловились даже во время висения в fgets()'е. По идее за 07-05-2013 -- надо просто ловить SIGPIPE, аналогично тому, как было в старом cxlib'е.

    Т.е. -- некоторый "откат назад", для одного конкретного клиента (хотя, если в v4 появится интерактивный вариант cxclient'а -- то в нём придётся делать то же самое).

    Весьма вероятно, для реализации потребуется какое-то сотрудничество от cxlib'а.

    06.02.2015@лыжи-5-ка: есть вариант проще: учитывая существование FDIO_STRING, можно перевести cx-console с fgets() на fdiolib.

    • Т.е., регистрировать fd=0, чтоб он вычитывался как строка.
    • Возможности "редактирования" при этом останутся, поскольку за них отвечает драйвер терминала, а не stdio.
    • А проблема синхронности/асинхронности уйдёт -- поскольку управление постоянно будет у cxscheduler'а, и он в любой момент обеспечит реакцию и на обрыв соединения тоже.

    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: работать-то работает, но с косяками -- ведь при переходе на асинхронную архитектуру модификации требовались более серьёзные.

    1. Отсутствовала ловля CAR_CONNFAIL -- ошибок соединения. Так что оно дальше висело до бесконечности на stdin'е.

      Это исправлено тривиально.

    2. "Проблема telnet'а в пакетном режиме": при запуске с перенаправленным stdin'ом -- из файла или pipe'а -- оно делает exit() сразу же по EOF'у там. А оное случается практически сразу, так что до ответа (и даже до соединения!) дело и не доходит.

      Как подобное лечить "правильно" (инфраструктуро-индифферентно) -- вопрос.

      Но при внедрении вышеупомянутого 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(), он пофиксен. Но проблема "обрезания" осталась.

  • 18.09.2013: в преддверии следующего пункта слегка сменены строки, касающиеся CETIMEDOUT: теперь это будет "Handshake timed out" вместо "Connection timed out before authorization".
  • 18.09.2013: вводим clientside handshake-таймауты, для борьбы с ситуациями, когда непосредственно после отработки 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 за сегодня.

  • 09.04.2015: обнаружилась позорная проблема в ExtractPrintable(): там значилось
    const char empty[] = "";
    и указатель на это empty передавался наружу -- в заказанный программой в cx_execcmd() reply.

    Косяк очень древний (он есть в ucamlib.c от 28-05-1998), но успешно скрывался, а вылез только при переводе cx-console на FDIO_STRING (наверное, раньше так совпадало, что это место в стеке прописывалось нулями, а сейчас практически сразу после ExtractPrintable() вызывается нотификатор, и место оказывается уже в его собственном стековом кадре).

    Поправлено тривиально добавлением static.

CX-протокол:
  • 15.02.2004: пришла в голову мысль, что недурственно бы завести rflag "проблема с удаленным/подчиненным драйвером" -- например, когда нет связи с мотороллером.

    16.02.2004: ввел флажок CXRF_REM_C_PROBL/"Remote controller problem", следующий за CXRF_IO_TIMEOUT. В cxlib.c::_cx_rflagslist[] изменения также отражены.

  • 23.05.2004: надо бы ввести еще один rflag -- overflow/overload.

    23.05.2004: ввел флаг CXRF_OVERLOAD/"Input channel overload", следующий за CXRF_REM_C_PROBL. И в cxlib.c::_cx_rflagslist[] это не забыто.

  • 10.08.2004: наконец-то удалил старые, за-#if-0'енные определения из cx_proto.h -- CXC_* и CXB_BIGCREQ.
  • 10.08.2004: кстати, в преддверии введения data-forks: стоит попереименовывать коды из CXC_* и CXB_* в что-то унифицированное -- типа CXF{C,B,D}_*, а то появится префикс CXD_*.
  • 16.04.2005: потребен еще один rflag -- "Wrong device". Чтобы драйвер, способный проверять тип устройства (CAN-bus, adc333, "S" -- АЦП-101*/850*), при неопознании мог бы сообщить, что это НЕ ТО устройство, которого он ожидает.

    Или -- ввести соглашение, что в такой ситуации отдается 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".

  • 15.06.2005: нужен еще флаг -- "Configuration problem", который бы можно было возвращать при ошибке конфигурации вместо нынешнего CXRF_DRV_PROBL.

    15.06.2005: ввел этот флаг, и надлежащее описание для него в _cx_rflagslist[]. Глядя на этот список вылезают две мысли:

    • Надо будет в CXv4 флаги перегруппировать -- чтобы они шли по "тематике". В частности, CFG_PROBL явно следует поставить сразу (считая снизу) за DRV_PROBL.

      И 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 бит, а именно список флагов.

    • Надо разбить нынешнее цветовое состояние HWERR на два: одно -- как сейчас, "железячная" проблема, а второе, куда войдут "те, что идут снизу" -- {NO_DRV,DRV_PROBL}.

      И эти, "что снизу" -- ОТОБРАЖАТЬ ДРУГИМ ЦВЕТОМ!!! 30.08.2005: да отображаются, отображаются они болотным, давно уже :-)

      Кстати, тут отдельный вопрос -- к какой категории относить CXRF_OFFLINE? Наверное, ко второй, где оно сейчас и пребывает.

    Акромя же того -- прошелся по имеющимся драйверам, повставляв сей флаг где надо. "Жертвы" -- new_cm5307_drv.c, canbus/canadc40_src.c с ныне пишущимся canbus/marcankoz_pre_pyr.c.

  • 15.06.2005: да, надо будет в CXv4 реализовать давнюю идею -- чтобы драйвер при фатальной ошибке (ошибка инициализации, DRVS_OFFLINE/DRVS_NOTREADY) мог бы указывать и текстовую строку, характеризующую проблему, а не только валить в лог.

    Дальше будет вопрос о том, как это передавать клиентам. Ясно одно -- передавать надо будет строку плюс диапазон номеров каналов, к которым она относится. А в cxlib'е будет "репозитарий"/"база данных", по которым можно будет делать lookup -- типа

    char *cx_geterrdescr(int cd, chanaddr_t addr)
  • 07.07.2005: насчет того, что флажки 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.

  • 08.02.2006: надо б завести флаг "указан invalid параметр" -- для того, чтобы большие каналы могли отвечать на некорректные параметры (когда нельзя просто молча взять сколько-то бит (как для селектора чувствительности) -- например, когда указывается номер канала для осциллографических измерений в CANADC40 вне диапазона [0..39]).

    Напрашивается название CXRF_INVAL. А вот по смыслу -- как-нибудь бы его отнести к SFERR (=1<<10, прямо перед WRONG_DEV/CFG_PROBL (в CXv2/CXv4 порядок отличается)?).

    28.02.2006: ввел CXRF_INVAL=1<<10. Также отразил сие в _cx_rflagslist[].

    В 4cx/.../cx.h тоже не забыто.

  • 27.02.2006: а как еще насчет флажка "UNSUPPORTED"? Потребность возникла в драйвере CGVI8 -- там есть некий канал "база", имеющийся токмо начиная с некоей версии устройства/прошивки.

    (Сейчас пришлось выкручиваться сочетанием 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: давно напрашивалась доформализация протокола "команд" от кнопок -- чтобы было не просто "магическое число 1.0", а нечто унифицированное.

    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 возникла потребность как-то сообщать "наверх" не только об overflow, но и об underflow -- когда входные сигналы слишком малы, чтобы давать полезную информацию.

    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() и так передается текущее значение.

  • 17.06.2009: перенес символ CX_MAX_BIGC_PARAMS из cx_proto.h в cx_types.h. Объяснение -- в разделе по CANGW за вчера и сегодня.
  • 09.08.2010: надо бы и в ветке клиенты<->сервер вводить application-pings.

    Это стало необходимым вследствие использования 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" -- с пустой реакцией (поскольку нам ответы не требуются, важен лишь сам факт пересылки пакета по сокету).
    • Собственно обеспечение периодичности повешено на cda: функция HeartbeatProc() дергается раз в 5 минут, постоянно и unconditionally, полностью аналогично cm5307'ной HeartBeat(), а уж собственно cx_ping() вызывает только при полной функционирующести соединения.
    • В v4, конечно, надо будет делать в самом cx_client.c -- там доступен API cxscheduler'а.

    Проверено -- работает. Конечно, можно бы заморочиться еще с некоторыми фичами:

    • Настраиваемый интервал пингования.
    • Не пинговать при протоколе UNIX.

    Но просто остановимся на сделанном -- "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 при больших интервалах между выстрелами стали появляться сообщения типа

    2010-10-27-14:01:55 liucc: cxlib: async_CS_BIGC_SENT(17): reply Seq/87/ != syncSeq/84/
    -- т.е., расхождение по 1 на каждый PING, успевший проскочить между запросом и ответом.

    Стал рыться -- и нашел!!! Причём зацепкой послужило как раз то, что проблема ТОЛЬКО с большими каналами -- именно в cx-server_bigc.c и нашлось. Итак:

    • Дело оказалось в том, что InitReplyPacket() (каковой и выполняет копирование cp->replybuf->Seq=cp->reqbuf->Seq) делается не при получении запроса -- как у обычных каналов -- а непосредственно уже в FillBigcReplyPacket(), вызываемом при получении ответа от драйвера.
    • Но ведь за это время могли приходить иные запросы, так что ежу понятно, что ни в коем случае нельзя лезть в reqbuf, а надо туда лезть
    • Причём -- эта проблема явно осознавалась в момент реализации больших каналов, поскольку там есть lastreqbuf, в который и копируется запрос.
    • И, кстати, потенциально аналогичная проблема есть в консольном интерфейсе: для команд, пересылаемых на исполнение серверу, может возникнуть ситуация, что между отправкой серверу и получением от него ответа успеет пробежать PING -- и тогда reqbuf уже не будет отражать состояние ЗАПРОСА.

    Возможные варианты решений:

    1. Правильное решение самоочевидно -- делать InitReplyPacket() в момент получения пакета.
    2. А простое и максимально-non-intrusive -- после InitReplyPacket() делать cp->replybuf->Seq=cp->lastreqbuf->Seq.

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

    Так что делаем по второму.

  • 29.05.2014@Снежинск-каземат-11Сейчас новые fdiolib/cxscheduler-based cxlib и cxsd, так что надо убирать искусственное ограничение количества точек у adc200me/adc812me, для чего повысить лимиты в протоколе (и, возможно, в сервере?).

    03.06.2014: делаем. Увеличено с 2<21 (4М) до 1<24 (16M).

    В сервере ограничений нет (более того -- там в конфиге можно указать отрицательный размер... :().

    В драйверах 200/812 имевшееся ограничение также убрано. Но проверить пока не на чем :)

cxsd:
CORE:
  • 10.01.2004: приличия ради позаменять префиксы: чтобы все свойства блоков были d_NNN (Device, вместо нынешнего b_ -- Block), а больших каналов -- b_NNN (Bigc, вместо нынешнего артефакта d_ -- Dirc).

    20.01.2004: переименовал. Пока не проверял, но, учитывая, что "d_" там был только d_data[], превратившийся в b_data[], и все откомпилировалось, проблем быть не должно.

  • 04.06.2005: О! Стало ясно, что надо вводить отдельный тип cycle_t, которым и объявлять все вещи, связанные с "внутренним временем" -- все "*time*", "*cycle*" и параметры соответствующих функций.

    04.06.2005: да, ввел cx-server_data.h::typedef int32 cycle_t. И попереводил на него все переменные и параметры функций.

    Единственное место, где "торчит" просто int, это Drivers-API-функция GetCurrentCycle().

  • 04.06.2005: кстати, а вот концепция "cycle step" себя, похоже, давно изжила (точнее, ее изжило превращение 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 удалено.
    • Из cx-server_channels.c::ServeDataRequest() убрано его заполнение.
    • В cx-server.c:
      • Убрана проверка 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".

  • 21.06.2005: надо будет для ВСЕХ level'ов для logline() сделать enum'ы -- чтобы не было зашито всяких магических чисел.

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

  • 21.11.2012: директория daemon/ отправлена на пенсию -- в ARCHIVE/work/c-s-p-daemon.20120722.

    Актуальность свою (даже потенциальную) она давно потеряла, а поддерживать её в собирабельном состоянии стало окончательно лень в момент обновления API cxscheduler'а.

  • 21.05.2013: в начальных числах месяца (на праздниках, где-то 3-4-5) была инициирована работа по однопроцессному серверу -- cxsd, деятельность ведётся в programs/zzz/.

    21.05.2013: детали:

    • Основа -- cxscheduler+fdiolib.
    • "Каркас" взят из 4cx/'ного, а...
    • ...начинка делается в основном копированием крупными кусками из programs/server/ и адаптацией.
    • В частности, ВСЁ взаимодействие с клиентами ушло в cxsd_fe_cxv2.c -- и былой cx-porter.c, и cx-server_{channels,bigc}.c.
    • Дополнительно был сделан драйверный API "DevIO".

    За следующую неделю оно было допилено до запускабельного состояния, потестировано и обезжучено, еще через неделю втихушку запущен сервер linac1:31 (linmagx), а еще через неделю сделан симлинк pult/sbin/cxd->cxsd, и при рестартах серверов идёт миграция.

    21.05.2013: да, имелись препозорнейшие ляпы:

    1. stop_dev() при де-регистрации файловых дескрипторов (а там схема отличается, из-за cxscheduler'а) не имел "break"ов, и вякалось о попытке повторной дерегистрации.
    2. Там же было забыто делать close() этим дескрипторам, так что они оставались висеть и, как следствие, "sato" remdrv'шным приводило к "device (L,K) already in use" -- на той стороне оно было не-освобождённым.
    3. При старте сервера не делалось RequestReadOfWriteChannels() -- с вытекающими последствиями.

    15.09.2013: добавлена cxsd_uniq_checker() -- аналогично remcxsd'шной. Ради чего пришлось

    • Завести cxsd_driver_c_int.h -- исключительно для её декларации.

      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/ проблемы с открытием логов также никак не решаются!

  • 26.09.2013: при увольнении старой programs/server/ (12-08-2013) вместе с ней улетели и cxd.conf с cxd-onelog.conf. Восстановлены в zzz/.
  • 13.12.2013: тогда при сведении всего в один процесс в "сценарии" старта в 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", но ухо надо держать востро -- вдруг где изменение последовательности аукнется.

  • 23.03.2015: был ляп в ServeConsoleRequest() в парсинге параметров: оно умудрялось вылазить за терминирующий '\0' у команд без параметров, потому, что безусловно делало *p++='\0' после пропуска whitespace после команды (которого там не было) -- в результате и вылазило за пределы строки.

    Проявлялось в nargs>1 ВСЕГДА -- в т.ч. в команде "scan" (на чём и обнаружилось -- лезло всегда как от "scan w").

    Теперь запись-с-инкрементом условная, если !='\0'.

Drivers' API:
  • 10.01.2004: а как насчет того, чтобы позволить СЕРВЕРУ заниматься отведением памяти под приватные данные драйверов? Для этого понадобится:
    • Ввести в DriverRec'е еще одно поле -- size_t privdatasize.
    • Добавить параметр privptr к CxsrvBlkInitFunc.
    • Поведение drvrmgr'а: если driverrec.privdatasize!=0, то перед инициализацией блока он сам делает malloc() и RegisterDriverPtr(), а после term_blk сам делает free().

      Если же драйвер хочет сам возиться с динамической памятью (например, если у него нефиксированный размер данных), то он указывает privdatasize=0, и drvmgr туда не суется (и даже free не пытается делать).

    Стандартное замечание: не забыть продвинуть CXSRV_DRIVERREC_VERSION.

    20.01.2004: угу, сделал все, что прописал выше. Место под privdatasize выбрал между описанием фич драйвера и таблицей методов, так что при промахе компилятор сразу даст warning.

    Заодно три вещи, которые не предусмотрел ранее:

    1. Надо было "расширить" под новый параметр определение AddBlock()::stub.
    2. malloc()'нутую область надо перед вызовом init_blk забить нулями.
    3. В DeregisterDriverFD() отсутствовала проверка, что мы удаляем "основной" дескриптор драйвера -- т.е., что d_fd[magicid]==fd, и, как следствие, d_fd[] не подчищался, и в будущем драйверу мог бы передаваться "протухший" номер дескриптора.

      Обнаружил это при разбирательстве, "когда же очищается default'ный дескриптор при reset'е драйвера" (никогда это поле там не очищалось ;-).

    Теперь надо все это протестировать. Мда, хорошенько-прехорошенько подумать над тестами :-).

    21.01.2004: в процессе переделки самих драйверов, чтоб не занимались malloc()'ом, обнаружил еще недоделку (хоть и не фатальную): забыл всем функциям _init_b подобавлять параметр privinfo.

    А собственно переделка драйвера включает такие действия:

    1. Объявление вида "privrec_t *info" дополняется до "privrec_t *info=(privrec_t *) privptr".
    2. Выкидываются info=malloc(), if(info==NULL) и bzero(info,...).
    3. Выкидывается вызов RegisterDriverPtr().

    Естественно, в драйверах, не использующих privptr, переделка ограничивается добавлением параметра "void *privptr __attribute__((unused))".

    28.06.2005: эх! Ведь уже почти полтора года, как это работает! Баги были только в самих драйверах -- типа sim_drv, где забывал указывать sizeof() в driverrec'е. А так -- все окей.

    Помечаем как "done".

  • 10.01.2004: еще мысли насчет layer'ов: вариант цели:
    1. чтоб можно было иметь "абстрактный" CAN-драйвер, знающий только о логике работы блока, но не имеющий понятия о способе отправки ему пакета и приема оного;
    2. и этот "слепоглухонемой" драйвер "на лету соединяется" с layer'ами для Marathon/Kozak или Mamkin/Kozak. Причем один физический .so-драйвер в разных экземплярах одновременно соединяется с разными layer'ами -- т.е., это связывание производится на уровне БЛОКА, а не драйвера.

    26.01.2004: стукнула мысль: а ведь решение-то очевидно (вот что значит сформулировать цель!)!

    Итак:

    • Во-первых, надо отказаться от "прямого" связывания (когда функции из layer'а вызываются напрямую), поскольку тогда само связывание оказывается вне нашего контроля, и совсем никак нельзя одновременно загрузить 2 layer'а с одинаковым интерфейсом. То есть -- надо вызывать методы по указателям.
    • Во-вторых, вводим понятие "интерфейс layer'а", очень похожее на "метрику драйвера". Для любого layer'а интерфейс будет вначале содержать некоторые стандартные поля:
      magicnumber
      сигнатура "описания интерфейса".
      ifacename
      название интерфейса -- строка (типа "kozak-can").
      version
      версия интерфейса: при добавлении методов или исправлении багов увеличивается minor, и разрешено связывание при
      drv.iface_major==lyr.iface_major && drv.iface_minor<=lyr.iface_minor

      Дальше идет таблица указателей на функции/методы интерфейса, которые драйвер-клиент может вызывать. Они будут определяться в некоей структуре, которую #include'ат и драйвер, и layer. (Очень напоминает VMT, не правда ли?)

      Похоже, один из методов также стоит стандартизовать -- "DisconnectDriverFromLayer()", чтобы его мог принудительно вызывать TermBlock().

      17.11.2014: о чём я тогда думал, какое "стандартизовать" -- ведь метод "disconnect" должен быть не в VMT, а в описателе самого layer'а. Где он по определению будет стандартизован. Так что тут и обсуждать нечего.

    • В-третьих, поскольку связывание с layer'ом производится на уровне блока, а не драйвера, то нельзя будет просто прописывать некую переменную "layer_vmt" у драйвера, а надо в каждом вызове функции типа 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 пихать неохота, но и в драйверах дублировать -- изврат.

    Так что надо ввести понятие "просто модуль", который:

    1. Можно было бы загрузить просто при старте сервера.
    2. Можно было бы указывать как "зависимость" в описании драйвера, чтобы оно хваталось перед загрузкой оного.

    Вопрос только -- а как именно проводить связывание и давать ли возможность иметь несколько разных модулей с одинаковой функциональностью -- как layer'ы, или же связывание будет лишь статическим? Хбз... И вообще -- отложим пока это, а? :-)

    (Надо было это всё ввести отдельной секцией, но оно столь сильно переплетено с layer'ами, что пусть уж живет здесь.)

  • 20.01.2004: кстати, а мы собираемся все-таки проверять код возврата от 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 оно уже используется.

    Вообще-то стоит получше подумать об именовании и об описании -- что-то мне современный вариант кажется неадекватным...

  • 31.03.2004: по результатам писания canadc40_drv.c: нужен какой-нибудь простенький интерфейс для парсинга auxinfo, a-la getopt()/Xrm*(). Проект такой:

    Строка auxinfo имеет вид

    param=value {param=value...}
    а для API может быть два варианта:
    1. Функции типа GetParamInt() и GetParamStr(), которым указывается "ключ" -- имя интересующего параметра, и если он присутствует, то они возвращают его, иначе -- указанное умолчательное значение.
    2. Создается таблица с указанием "ключей", типов (int/string) и умолчательных значений, а также мест, куда складировать результаты, а потом ОДИН раз вызывается функция типа "ParseAuxinfo()", которая и разбирает строку.

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

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

    19.04.2004: видимо, решение будет в рамках библиотечного модуля paramstr_parser.[ch].

    15.06.2004: поскольку уже проверил, что canadc40_drv прекрасно живет на paramstr_parser'е, помечаем раздел как "done".

  • 04.04.2004: еще мысль насчет logging'а от драйверов: обычно драйверы (особенно для незнакомого железа) начиняются в отладочных целях большим количеством DoDriverLog'ов -- например, ведется протокол отправляемых/принимаемых пакетов. По мере доводки этот логгинг удаляется, но временами, при возникновении проблем, он надобится снова. Приходится долго и мучительно разбираться в исходниках, куда б это засунуть, напихивается в кучу и лишних тоже мест, потом забывается -- в общем, маета.

    А хочется иметь стандартное, унифицированное решение, причем позволяющее "играть" такими деталями логгинга без лазанья в исходники и перекомпиляции. Проект таков:

    1. Вводится некоторое количество "стандартных категорий", первоначальная прикидка:
      • точки входа/выхода в функции,
      • получение/отправка пакетов (прямо с побайтными дампами),
      • отдельной категорией -- результаты "разбора" входных пакетов (получен пакет код такой-то, канал номер такой-то...)
      • ...
    2. Все эти категории получают короткие имена, чтобы можно было на них ссылаться из драйверов и в config'ах (например -- entrypoint, pktdump, pktinfo).
    3. В вызове DoDriverLog() ответственно подходящий к делу драйверописатель сможет в level передавать и "категорию" (по умолчанию -- 0). Например, это будут константы DRIVERLOG_C_NNN. (Замечание: категория здесь -- это номер, а не бит маски, как в серверном logline()!)
    4. Такие DoDriverLog()'и будут оставаться в коде драйверов навсегда -- если они хоть сколько-то полезны, то выкидывать их по мере отладки не надо.
    5. В blklist'е можно будет при каждом драйвере указать, какие именно категории сообщений от него стоит разрешить. Поскольку заново менять формат blklist.lst не хочется, то можно расширить поле file с
      file[@layer]
      до
      file[@layer][;log=category,category...]

      (В более широком варианте можно будет указывать после спецификации файла/layer'а еще список опций через точку запятой, вида "option=value". Это идеологически сильно смахивает на поле options в /etc/fstab и ключ mount'а "-o", а также на параметры командной строки ядра Linux; разве что разделители из-за ограничений синтаксиса другие. Какие могут еще понадобиться опции -- пока неясно, но возможность для их введения есть.)

      Для удобства вводится макрокатегория "all".

    6. По умолчанию логгинг всех категорий, кроме "обычной" 0 отключен. Его можно включить либо для отдельного драйвера (log=...), либо для всех сразу -- например, каким-нибудь ключиком командной строки cxsd, в котором так же через запятую перечисляются интересующие категории (парсер формата "category{,category}" надо будет сделать общим для обоих случаев).

      Можно синтаксис и чуть расширить -- чтобы в конфиге можно было для "особо болтливых" драйверов отключать некоторые категории, когда они включаются для всех. Например, log=-category1,+category2, а по умолчанию считается "+". (Унифицированный парсер будет возвращать две маски -- того, что "+", и того, что "-".)

    7. Поскольку при всех входах в код драйверов/layer'ов будет использоваться ENTER_DRIVER(), то в каждый конкретный момент времени DoDriverLog() сможет определить, какой именно драйвер его дернул, и проверить по маске категорий -- надо ли исполнять этот запрос.

    07.05.2004: кстати, а ведь захочется менять драйверный логгинг "на лету" -- так что придется изготовить консольную (cx-console->cxd_manager->cx-server_manager) команду "setdrvlog". Ей стОит назначить синтаксис как у chmod:

    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: так и делаем --

    1. Вводим эту парочку в cxsd_driver.h (в CXv4 надо будет список переупорядочить).
    2. Также -- в cm5307_dbody.h и в ppc_canserver/cxsd_driver.h (для общности).
    3. Добавлены в cxsd_drvmgr.c::catlist[].
    4. И собственно в cm5307_drv.c и cangw_drv.c внесены соответствующие дополнения.

    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: логгинг с префиксом "magicid!driverid".

    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: эх, была не была -- решился, надо:

    1. вводить driverrec.name (чтобы можно было потом сделать и layerrec.name с тем же оффсетом!);
    2. DoDriverLog() -- добавить параметр magicid.
    3. и, естественно, продвинуть 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

    Не мега-красиво, конечно, но полезно.

  • 07.04.2004: изоляция драйверов друг от друга, предварительные действия.

    Это понадобилось, чтобы в каждый конкретный момент нахождения в драйверах было известно, в ком из них -- т.е., его 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".

    В общем -- исправлено, "скобки" поставил.

  • 15.06.2004: еще на тему изоляции драйверов: в cxsd_driver.c кое-где стоят проверки на корректность передаваемого драйвером magicid'а. Вчерашние разбирательства с сошедшим с ума odr_rfgen.c показали, что надо при выдаче в log сообщений о "странностях" писать не только переданный 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: короче --

    • во-первых, сделал внутренний для cxsd_driver.c макрос CHECK_SANITY_OF_MAGICID() и почти везде перешел на него -- кроме RegisterDriverFD(), где оно пока может быть -1; там собственный логгинг;
    • а во-вторых -- во всех сообщениях, где пишется magicid, выдается также и active_magic.

    Так что -- считаем что "done".

    24.05.2005: дополненьице: подобавлял выдачу active_magic также в cxsd_bigc.c -- в ReturnBigc().

  • 02.07.2004: добавление к 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().
    • То же самое (исключительно для компилируемости) в его предка -- m5200_drv.c.
    • Плюс -- в istc/drivers/kshd485_drv.c.

    03.07.2004: проверил -- работает. Так что -- помечаем как "done".

  • 26.11.2004: давно назрело -- надо иметь возможность указывать умолчательное значение DefaultDrvlogMask из командной строки при запуске сервера.

    26.11.2004: ввел дополнительный ключик -- "-l".

    Плюс, естественно, передачу указанного значения через environment -- шифровку и дешифровку. Еще раз убедился, насколько муторна сия процедура...

    Можно указывать как "+category", так и "-category" -- оно будет корректно убираться из умолчательного списка.

    28.02.2005: олух царя небесного! В парсинге ключиков после "case 'l':" забыл поставить break. А следующей альтернативой шло 'n' -- norun. Вот оно и отваливало сразу же :-)

    В остальном же -- оно работает, так что помечаем раздел как "done".

  • 25.02.2005: обнаружил в cxsd_bigc.c::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: надо иметь в API функцию "ReRequestChannels()".

    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".

  • 17.04.2005: надо также иметь и функцию "SetBlockStatus(magicid,status, rflags)", которая меняет "состояние" между [offline,operating,not-ready]. "Offline" -- то же самое, что просто DisconnectBlock(). "Not ready" -- устройство вроде бы живо, но с запросами к нему лучше не приставать.

    17.04.2005: в данный момент, фактически, есть только "operating" и "offline", в который можно уйти только безвозвратно. А хочется -- чтобы вполне "живой" драйвер мог бы сигнализировать (для ВСЕХ своих каналов, а не только для тех, которые спрашивают), что что-то не слава богу (например -- потеряна связь с контроллером -- CXRF_REM_C_PROBL); и для этого можно будет вызвать

    SetBlockStatus(magicid, CDRVS_NOTREADY, CXRF_REM_C_PROBL);
    А когда связь восстановится -- то, соответственно, перейти в состояние "OPERATING".

    Highlights проекта:

    • Собственно сделать функцию. "Алгоритм" ее работы:
      • Ничего не делать, если реально новый статус совпадает со старым.
      • Переход из состояния "offline" запрещен и считается ошибкой (да и кто б мог легитимно его вызвать -- сам-то блок "мертв", только кто-то "за него".
      • При переходе в состояние "operating" автоматически вызвать "ReRequestChannels()".
    • "Дергать" методы драйвера ТОЛЬКО при состоянии OPERATING.
    • Слегка перетрясти имена переменных: нынешний 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.

    • Фабула: после аппаратного перезапуска CAN-устройств (выключение питания, кнопка reset) каналы, связанные с регистрами ввода/вывода, оставались в состоянии "offline" -- т.е., флаг CXRF_OFFLINE продолжал гореть.

      Чего я только ни думал и ни делал: и полный дамп пакетов после reset'а просматривал, и чуть ли не "даиграммы хождения данных" в мозгу прокручивал -- все без толку.

    • Причина же оказалась в том, что в marcankoz_pre_lyr.c::marcankoz_fd_p() по получению от блока значений регистров (пакет CANKOZ_DESC_READREGS) делалось ReturnChanGroup(..., rflags:=NULL). А при rflags==NULL там флаги каналов вообще не трогаются -- это было введено для упрощения жизни О.Токареву (запись о сим имеется в bigfile.html за ??.10.2003).

      Т.е. -- при переходе в DRVS_NOTREADY всем каналам уставлялось CXRF_OFFLINE, которое на регистровых каналах никогда не сбрасывалось.

    • В принципе, как вариант -- возможно, проблему можно было бы решить путем выполнения в ReturnChan*() обнуления соответствующих флагов в случае, если rflags==NULL, но не хочется, и вот почему...
    • Надо прошерстить все драйверы на тему -- не используется ли где-нибудь эта "фича" с RCG(...,rflags:=NULL), и если где найдется -- поправить. 01.12.2005: Прошерстил -- результат см. ниже.
    • А в CXv4 надо вообще ЗАПРЕТИТЬ возможность не-указывать флаги.

    01.12.2005: прошелся по всем драйверам на тему "где ReturnChan*(...,rflags:=NULL)". Результат:

    • noop_drv.c, sim_drv.c -- тяжелый случай. Оне ж там не знают, сколько у них будет каналов, и как раз для них был бы идеален вариант, когда rflags==NULL=>bzero(...).

      Но -- настоящие самураи так не поступают :-)

      В них (код _rw_p() у них, по причине простого копирования, идентичный) все равно имелся цикл на случай !=DRVA_WRITE, так что достаточно было сделать его всегдашним и ввести пустую флаговую переменную.

    • istc/drivers/kshd485_drv.c: в одной-единственной точке -- прямо в init_b()! -- был NULL при "уставке/выдаче" искусственного канала KSHD485_C_NUM_STEPS. Вставил указание нулевого значения.
    • drivers/istc/canadc40_drv.c (т.е., ВСЕ старые мои CAN-драйверы) -- навалом этих NULL'ов (точнее -- не-NULL всего в 1 случае из 7 :-), но, поскольку оно уже полгода как отправлено в отставку, то не трогаем -- пусть покоится с миром.
    • otokarev'ские драйверы -- CAN-bus и одреночные: в них лезть также смысла нету, ибо CAN-bus уволены без выходного пособия еще летом, а одреночные -- ну нафиг...

    Все остальные драйверы на вид в порядке. Засим считаем инцидент расследованным, проверенным, исправленным и исчерпанным.

    01.12.2005: Ха, а "scan z"-то показывает время смены состояния -- "01.01.1970,07:00:00"! Причина очевидна -- d_stattime[] никогда не инициализируется, а только bzero()'ится.

    Чуть позже: странно! Посмотрел -- в InitBlock() как раз очень даже делается gettimeofday()!

    Блин, дятел -- так было только в режиме "simulate" -- с ключом -s! Ну так и правильно -- пусть так и будет.

  • 25.05.2005: сходу: в RegisterDriverFD() почему-то отсутствовала проверка на fd<0 -- просто скопировал оную из DeregisterDriverFD().

    25.05.2005: эх, по-хорошему все-таки надо бы подобные проверки делать #define'ами, чтобы не было множественных, потенциально отличающихся экземпляров.

  • 25.05.2005: КСТАТИ!!! А не может ли у нас все-таки быть проблемы, когда сервер попутает-таки дескрипторы, и какому-то свежерожденному клиенту отдаст то, что предназначалось для совсем другого, только что скопытившегося, но с тем же fd?

    С каким сценарием?

  • 25.05.2005: хмык... Обнаружилось, что флажок CXRF_OFFLINE НИКТО и НИКОГДА не проставлял. Хотя очевидно, что это должно делаться АВТОМАТИЧЕСКИ при самоубийстве блока.

    25.05.2005: да -- просто вставил в stop_block() и FreezeBlock() принудительное |CXRF_OFFLINE.

  • 27.05.2005: имеется недочет: при "сбросе" блока (ResetBlock() которое) необходимо делать то же самое, что и RequestReadOfWriteChannels() -- но только среди каналов этого блока.

    Можно сделать аналогично новому варианту MarkRangeForIO(), точнее -- ReRequestChans().

    А правильнее -- просто изготовить "мясо", ReqRofWC(), которая будет принимать параметры "from" и "until", и уж ее услугами будут пользоваться обе API-функции.

    27.05.2005: угу -- создана внутренняя ReqRofWrChs(), в которую НЕ попали артефакты, так что она маленькая и элегантная. И новый вызов -- RequestReadOfWrChsOf(), заказывающий чтение только для wr-каналов указанного блока.

    24.05.2006: поскольку почти год как работает -- "done".

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

    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 останутся. Тут есть несколько аспектов:

    • А что ДОЛЖНО происходить по RemitBigcRequestsOf()? Вылазит две разных вещи:
      1. Должен удаляться ПЕРВЫЙ запрос из очереди -- который, собственно, сейчас числится отправленным.
      2. Должны удаляться ВСЕ запросы -- блок-то сканчивается. Это -- ради inserver.

      Ну так и:

      1. А почему вообще запрос должен удаляться -- зачем? Ведь достаточно потом, когда (если) драйвер "оживёт", пере-запросить. Что НАДО будет делать при переходе на "полную" схему CX_CACHECTL_*.
      2. По второму же пункту -- ну так и сделать такое же поле on_stop, чтоб cxsd_driver.c мог "забывать" свои запросы (они-то, в отличие от клиентских, никому не нужны).
    • Как бы то ни было, однозначного понимания -- как поступить с имеющейся очередью запросов к большому каналу при stop'е блока -- всё же нет.
    • В частности, как клиенты отреагируют на приход им полностью пустого пакета данных -- ни данных, ни параметров?
    • Чем "делать ReturnBigc() от имени драйвера" -- можно ж просто сделать ровно те же самые действия для всех элементов в очереди.
    • А вообще, чем та алхимия, может, один из таких вариантов:
      1. А может, именно просто сбрасывать req_sent=0, и тогда по ResetBlock() отправлять первый запрос из очереди?
      2. ...или вообще ничего не сбрасывать, но в 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.
    • Происходит это по "рестарту" remdrv, когда тот делает OPERATING после NOTREADY (там драйвлет u0632 падал).
    • Конкретный сценарий понять не удалось -- шибко там много навёрнуто условий и наслоено вызовов/действий.
      • Похоже, "сломалось" при введении для больших каналов парадигмы Remit/ReRequest, ...
      • ...либо при добавлении on_stop-callback'ов.
      • Поскольку раньше в ReturnBigc() был просто цикл "возврата клиентам", и потом уже ПОСЛЕ него сбрасывалось req_sent=0 (там на эту тему туча комментариев), и всё работало.

        Потом же добавились новые обстоятельства, на которые та модель не была рассчитана.

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

      В v4 аналогичный функционал (параметризованные запросы, их очередь) надо будет делать в парадигме "сервер отводит структуры", с реализацией "fast-SLOTARRAY" от cxscheduler'а.

    • Пока сделано симптоматическое лечение -- вставлена проверка на curitem!=NULL, падать перестало (хотя и раньше сценарий был неочевиден, НЕ с первого раза), но чем еще аукнется -- фиг знает.
  • 27.05.2005: обнаружил, что параметр values в типе CxsrvBlkRWProc почему-то был int*, вместо int32*.

    27.05.2005: мда... В 99% случаев это, конечно, несмертельно, но...

    Короче -- в самой декларации исправил.

    А потом пошел наводить аудит везде, где оные функции определяются... Оказалось немного -- в server/drivers/*_drv.c и в istc/drivers/*_drv.c. Все оказалось просто и несложно -- реально ВЕЗДЕ и так предполагалось int32*, а ляпсус был ТОЛЬКО в декларации.

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

  • 06.06.2005: надо делать RegisterDriverWrFD()! (И, кстати, не забыть с write-дескрипторами повторить те же махинации по cleanup'у, что с обычными).

    06.06.2005: приступаем. Highlights:

    • Изготовлена "симметричная" пара вызовов -- RegisterDriverWrFD() и DeregisterDriverWrFD().
    • Их содержимое практически скопировано с не-Wr-версий. Отличие -- что есть отдельный массив wr-callback'ов, driverwrcallbks[].
    • И, чтобы можно было независимо включать/выключать маски R и W -- d_fd[magicid]=-1 уставляется только если в "другом" наборе этот дескриптор отсутствует. (Главная кривизна!!!)
    • За компанию вытащил все проверки из FD-вызовов в макросы: CHECK_IF_FDS_MAGICID_IS_SANE() -- отличается от CHECK_SANITY_OF_MAGICID() тем, что допускает отрицательный magicid; а также CHECK_FD_SANITY() и CHECK_FD_AFFILIATION().
    • В TermBlock() Wr-дескрипторы прикрываются так же, как и обычные.
    • Вставил в cx-server_fdio.[ch] набор-комплект maxdriverwr::driverwr и стандартные функции манипуляции с ним.
    • А в cx-server.c -- надлежащие махинации с оным.

    Общее впечатление -- как-то кривовато это все выглядит, поскольку одни и те же дескрипторы на чтение и на запись рассматриваются как разные объекты (а "принадлежности"-то -- 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) -- так что доделал.

  • 09.06.2005: хо-хо! А не понадобится ль нам метод блока "BeginOfCycle()"? Для frontend'ов-то в cxsd такой метод уже есть -- CxsdFrontendRec.begin_cycle(), и даже .end_cycle() имеется.

    12.06.2005: да, в CXv4 сделаем.

    Кстати, затрат-то процессора на это не будет никаких -- один экземпляр сервера поддерживает у нас обычно ну, максимум, пару десятков блоков.

  • 09.06.2005: сейчас, пока поддержка magicid'ов от layer'ов отсутствует, кое-кто (конкретно -- canadc40_drv.c вынужден пользоваться "-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: идея: а что, если в CXv4 сделать "динамические методы драйверов"? Т.е., чтобы драйвер мог определять некие нестандартизованные функции, которые потом можно будет вызывать с консоли.

    11.06.2005: детали идеи: в DriverRec'е указывается (опционально; ессно -- можно и NULL) некая NULL-terminated таблица, со строками вида

    {"имя", proc, <some-param-spec>}
    Из консоли такие команды вызываются в стиле
    call MAGICID COMMAND [PARAMS...]

    Обработку параметров можно делать двумя способами:

    1. Как в Xt -- т.е., просто
      char *params[], int num_params
      Это проще, но менее удобно для драйверов, ибо тогда ОНИ должны проверять корректность.
    2. Как в Perl -- в <some-param-spec> описывать параметры, а уж консольный интерфейс сам убедится, что все правильно и при надобности сконвертирует строки в числа. При этом
      • Передавать параметры можно массивами структур вида
        {int type; union {char *s; int i; double d;} v;}
      • Описывать параметры -- тоже как в Perl'е, строками типа "isi" (3 штуки -- int, string, int).

    Пара замечаний под конец:

    1. Реализовать такую фичу -- крайне просто, как со стороны сервера, так и со стороны драйверов.
    2. Оным можно заместить даже и планировавшийся когда-то (для reset'а блокам) "ioctl".

    13.01.2007: тогда-то эта мысля возникла, помнится, то ли на какой-то предзащите, то ли на каком-то семинаре (надо было записывать!!!).

    И -- подобное есть в EPICS'е (Гусев пользуется); и в TINE также можно зарегистрировать "команду", которая исполняется из консоли сервера (там команды НЕ привязываются к устройствам, а просто расширение shell'а; может, такое также позволять для всяких frontend'ов и иных модулей?).

  • 24.09.2005: надо в дополнение к d_stattime иметь и нечто, где записывается "текущее состояние устройства" -- то, что он передал при смене статуса. А при возврате в Online совать туда принудительный 0. И, соответственно, по "scan" показывать и этот статус тоже.

    11.02.2009: да, введено d_statflags[], прописывающееся одновременно с d_stattime. Кстати, всё-таки устройство всех этих *block() -- особенно TermBlock()! -- довольно бардачно, в частности, из-за MustSimulateHardware, так что пришлось подвыпендриться.

    И в cx-server_execcmd.c::ExecScan() также добавлен вывод "%4x" младшего слова флагов. (Выглядит результат такого вывода теперь, конечно, жутковато, но для разборок при отладке -- пойдет.)

  • 23.05.2006: в продолжение махинаций с 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 за указанную дату), ибо у него что-то глючило.

      Но она в любом случае разумна.

    • Про необходимость при reset'е драйверов/блоков поле c_time[] затронутых каналов сбрасывать в 0 говорится в том же bigfile.html за 15.06.2003. Но сделано этого так и не было!!!
    • Потребность же занулять при переходе в состояние OPERATING явнейше вылезла вчера.
    • И, кстати, как показал grep, поле c_time[] вообще НИКОГДА не занулялось/не инициализировалось, даже при инициализации блока!
    • Итак, теоретически надо бы нулить в ReqRofWrChs(), НО: в ReviveBlock() вначале вызывается ReRequestChans() (каковой может сразу привести к возврату данных и заполнению c_time[]), а уж затем -- RequestReadOfWrChsOf(). Так что такое "решение в лоб" было б неудобно/нерационально.

      По-хорошему, надо нулить в начале "транзакции"/"действия", каковым и являются "Reset" и "Revive"...

    Итак, что же было сделано:

    • В FillChanProps() "приличия ради" добавлено зануление c_time[].
    • Что до исходной проблемы -- вставил зануление c_time[] всех каналов блока "в начале транзакции", но с комментариями "/*!!! Temporary measure -- should be somewhere else */"

    26.05.2006: наконец-то представилась возможность проверить (пока было тепло, блок почему-то не глючил, а как похолодало (!) -- стал глючить опять).

    В общем -- да, ляп исправился, теперь все работает корректно.

  • 13.01.2007: не вполне корректно реализована поддержка регистрации дескрипторов не-драйверами -- с magicid=MAGIC_NOT_IN_DRIVER=-1.

    В собственно регистрации-то все окей -- там стоят хитрые проверки, так что НИКОГДА -1 не используется как индекс в массиве.

    А вот при вызове -- в cx-server.c::new_Run() -- внахаловку стоИт передача d_privptr[magic] (с d_busid[] аналогично), что приводит к взятию -1-го элемента массива. Нехорошо!!!

    16.02.2009: вставил guard'ы. Странно, что не нашел на это времени раньше -- там работы-то было на пару минут...

  • 06.02.2009: занадобилось уже ОБЯЗАТЕЛЬНО иметь возможность завести >1 таймаута на драйвер -- для плавного изменения значений в ЦАПах. Да и для "ping'ов" в CANADC40 & Co. они тоже нужны -- см. "еще о таймаутах" за 08-06-2006.

    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-указатели иначе хранить было б негде.
    • И, чтобы не заниматься всякой адресной арифметикой trec-..., в качестве privptr'а прописывается именно ссылка на описание, а уж там сохраняются 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):

    1. Перво-наперво -- удаляем дикое дублирование кода между RegisterDevTout() и RegisterDevToutAt(): "мясо" переехало в DoRegisterDevToutAt(), а те двое лишь проверяют параметры usecs/when.
    2. Проверка devid сменена на свежевведённую CHECK_IF_TOUTS_DEVID_IS_SANE() (копия FD'шной).
    3. Еще особенности работы с devid<0:
      1. Добавлены:
        • l_devtinfo[CX_MAX_TOUTS_PER_DEV] (сервер),
        • lyr_tinfo[CX_MAX_TOUTS_PER_DEV] (remsrv),
        ...используемые при devid<0.

        В результате, увы, на ВСЕ layer'ы таймаутов столько же, сколько на ОДИН драйвер.

      2. DevToutCallback() вместо d_devptr[devid] передаёт NULL (в 4cx devptr заделан сразу в cxsd_hw_toutinfo_t).

    А вообще -- пора просто переходить на унифицированную libcxsd/libremcxsd.

    30.12.2012: проверено -- NOT_IN_DRIVER-таймауты работают, в обеих средах.

  • 09.02.2009: наблюдена странность: имелось 2 CAN-устройства с отключенным питанием, каналы которых в программе linmag посинели -- CANDAC16 и CANADC40. После reset'а драйверов (sato) CANADC40 остался синим, а вот CANDAC16 -- синева исчезла, как будто блок ожил. Но реально нет -- достаточно было записать что-то в канал, как через 5 секунд он опять посинел...

    10.02.2009: понятно, в чем дело.

    1. CANADC40 -- каналы чтения -- вел себя абсолютно предсказуемо: ведь данные так и не обновились, и их c_time[] были далеко в прошлом.
    2. CANDAC16 же -- каналы ЗАПИСИ -- и по команде "sato" 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"!

  • 23.08.2010: занадобилась возможность одним драйверам уставлять callback'и на возврат значений каналов другими драйверами.

    Конкретно нужно для стенда у Ращенко:

    • чтобы некий драйвер (назовём его "triggered") мог бы возвращать значение некоего канала (от ceac124), измеренного не ранее, чем пришло измерение от biip/vsdc2 (это измерение работает как "триггер" -- т.е., что-то типа имитации внешнего запуска для козачиных АЦП, которые такой способностью не обладают).

      И даже чуть хитрее: triggered должен уметь ПЕРВОЕ измерение "после" выкидывать, а отдавать ВТОРОЕ -- поскольку реально то первое могло быть

      1. сделано ДО дёрга от БИИПа, но доставлено уже после (например, вследствие приоритетности БИИПа)
      2. начато ДО "момента X", а закончено уже после.

    23.08.2010: работа состоит из 3 частей:

    1. Ввести возможность в blklist-файлах поименовывать устройства -- чтоб драйверы-юзеры ссылались не на зашиный канал, а на ИМЯ_УСТРОЙСТВА.НОМЕР_КАНАЛА
    2. Сделать API для этих callback'ов, что включает в себя
      1. собственно callback-list'ы per-channel, их регистрация и вызов;
      2. списки зарегистрированных callback'ов per-device, чтоб они грохались при гроханьи устройства.
    3. Реализовать сам triggered_drv.c.

    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).

    • Применяемая в реализации этого API технология -- аналогична cxsd_timeout_t и tout'ам:
      1. Все данные для "низкоуровневого" вызова хранятся в структуре chan_cbitem_t:
        • отведение её -- задача "юзера";
        • в ней же хранятся поля next и prev для помещения в список;
        • её поле callback заполняется также "юзером", и для него же есть поле privptr, только им и используемое (а в вызове никакого private-pointer'а нету).

        (1) Соответственно, реализация скопирована с RegisterDevTout()/DeregisterDevTout().

      2. На каждый драйвер есть массив d_ievents[] в 10 структур inserver_evprocinfo_t, хранящих всю "высокоуровневую" информацию (т.е. -- о вызове уже драйверова callback'а). И в ней же, отдельным полем -- "низкоуровневая" структура chan_cbitem_t.
    • По-канальные списки зарегистрированных "низкоуровневых" callback'ов хранятся в 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 позаботиться об унификации:

    1. то ли вообще делать одинаковые вещи "объектами" или хотя бы макросами,
    2. то ли просто именовать одинаковые вещи везде одинаково ("item", а не "cbrec").

    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'ного набора в сокращенном варианте). По крайней мере, для начала, для наших текущих потребностей (хотя можно обойтись для сварки и одним специализированным драйвером).

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

    Так что реализовать надо будет примерно следующее:

    1. Аналогичный API для больших каналов (чтоб на драйвер "сварочник" можно было бы переложить ВСЮ работу, и чтоб у него была ВСЯ информация).
    2. Что-то типа "языка формул" от cda, но желательно с примесью seqexecauto. Или, возможно, наоборот -- seqexecauto на основе формул; короче -- то, что было в идеях еще N лет назад.

      ТАКАЯ штука позволит унифицированно исполнять всякие хитрые вещи, безо всяких спец.драйверов "условие".

      Хотя -- понятно, что всё это мечты, мечты...

    11.10.2010: на всякий случай, если этого ранее не было отмечено -- битым текстом: этого механизма НЕ БУДЕТ в прочих реализациях API cxsd_driver -- т.е., в cm5307_dbody и canserver_drvmgr. Потому как:

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

    Но в 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, в сервере нет, то получается гибридное решение:

    • ЗАПРОС обработки больших каналов делается так же, как в cxlib'е: функции inserver_req_bigc() указываются args[nargs] плюс data[datasize/dataunits].
    • А вот ЧТЕНИЕ текущих данных -- как в cda: троица 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.

    • Смысл -- для определённости, чтоб драйвер-клиент мог не маяться фигнёй типа "r<0" или "r<=0", а просто делал бы r==INSERVER_nnnREF_ERROR" (аналогично тому, как сделано в нынешней cda).
    • Дополнительный смысл -- в v4 конкретные значения могут и поменяться (devid=-1 там принадлежит первому layer'у), так что прямые сравнения при переносе кода были бы чреваты трудноуловимыми ошибками.

    Так что --

    1. В сервере теперь в таких местах "return -1" сменено на возврат соответствующей INSERVER_nnnREF_ERROR.
    2. В имеющихся драйверах-клиентах (аж трёх! :-)) проверки также оправильнены.
    3. НО: это касается только функций, возвращающих ИДЕНТИФИКАТОРЫ. Прочие же, возвращающие успех/ошибку (типа регистрации evproc'а) оставлены на 0/-1.

    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, хоть внутрисерверно:

    Когда "клиент" заказывает "серверу" канал, то его надо поставить в список читаемых (*), но также надо ОДНО значение прислать сразу -- чтоб у клиента появилось текущее значение.

    • "Сразу" может быть и разным:
      1. Для rw-каналов -- именно сразу (но с оговоркой про свежесть -- если значение несвежее (с точки зрения FreshFlag()), то слать надо не сейчас, а дождавшись прихода ответа; т.е., как в пункте (b)).
      2. Для ro-каналов -- сделать запрос, и по приходу данных слать. Но если данные уже свежие -- то как бы уже всё и готово.

      ...короче --

      1. Если этот канал свеж, то слать сразу, иначе -- сделать запрос, и по приходу ответа слать, на ПЕРВЫЙ раз плюя на "условия чтения" (типа частоты/отклонения).
      2. Отдельный вопрос -- что делать с "неживыми" каналами (от не-DRVS_OPERATING-устройств)? Видимо, тоже слать сразу, ибо ждать там нечего; а уж клиент сам по возрасту увидит "неадекватность", и сам примет решение, что делать.
    • (*) "Список читаемых" -- это либо периодически, либо по изменению, либо по изменению более чем на сколько-то.

    И вот нынешние ритуальные танцы с бубном "заказать evproc, но также и сделать чтение (для ro -- req_cval(); для rw -- сразу get_cval(), с проверкой возраста)" (ist_xcdac20_bind_hbt(), +req_chan()) как раз и есть реализация того поведения.

    28.03.2012: некоторые уроки по результатам реализации драйвера ist_xcdac20.

    • В первоначальном варианте, начинавшемся с tube-каналов, была сделана ошибка в выборе "системы координат".

      Поскольку большинство ist-каналов связаны с cdac20-каналами, то напросилось использовать для них ЕДИНУЮ нумерацию. Вопрос, что считать "первичнее" и на чём основывать таблицу связей/маппирования.

      1. Сначала всё нумеровалось по ist-каналам -- в т.ч. в отсылке канала SndCVal(). Но это оказалось ИСКЛЮЧИТЕЛЬНО неудобно, поскольку драйверу "командовать" надо в терминах CDAC20, и получаемое оттуда воспринимать в них же. А и соответствие имеем не 1-к-1 -- в обоих наборах есть каналы, не имеющие "отражений".
      2. Потому затем схема была "перевернута с головы на ноги" -- основная нумерация идёт по CDAC20, а перевод в/из IST_XCDAC20 делается только в обращении с tube-каналами (для этого при загрузке драйвера заполняется обратная таблица ourc2sodc[]).

      В общем, это естественно: основная ВНУТРЕННЯЯ работа драйвера -- по CDAC20, _rw_p() воспринимает адреса по IST_XCDAC20, а там, где эти вещи взаимодействуют/пересекаются -- производится преобразование.

      Но сколько же было диких неудобств из-за первоначальной неправильной схемы!

    • В конечном итоге выбрана также независимая от названия устройства схема именования.

      Переменные, касающиеся каналов самого драйвера (в его нумерации) нехай имеют имена/префиксы ourc (OUR Channels); касающиеся каналов "ведомого" устройства -- sodc (SubOrdinate Device Channels).

    • По ходу дела была изготовлена еще одна машина состояний. И видно, что она преизрядно ограниченна -- т.к. сделана "на коленке", то не позволяет делать некоторых вроде бы естественных вещей. Например, прямо из определителя состояния (SwchTo...) переходить к другому состоянию; как следствие, начальное определение (текущего) состояния сделано не в SwchToDETERMINE(), а отдельно; "сваливание" в INTERLOCK там сделано сильно отдельно, с ручным отсечением состояний, отвечающих за сброс блокировок; автоматический переход к следующему состоянию работает, только если указана "задержка"...

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

      И отдельно -- лёгкая неясность, а как же всё-таки лучше делать "проверку разрешения перехода":

      1. Как в sea -- функция "можно считать это состояние отработавшим и переходить к СЛЕДУЮЩЕМУ"; она является свойством ТЕКУЩЕГО состояния.
      2. Или как тут -- функция "можно переходить в ЭТО состояние"; вызывается она при текущем "другом".

    04.06.2014: в связи с необходимостью изготовить multiplexer_drv.c делаем переименование, для большей адекватности имён:

    • triggered -- в trig_read,
    • multwrite -- в trig_write.

    Хоть эта пара и не совсем симметрична (как их имена), но новые имена отражают суть получше.

    06.06.2014: ну и сам multiplexer сделан, только проверить надо.

  • 20.12.2011@Снежинск-каземат-11: реализовываем возможность одному драйверу (блоку) следить за состоянием другого -- идею см. в разделе по CANGW за 08-11-2011.

    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'ов -- резолвинг по имени и получение текущего статуса.

    Продолжаем.

    • Сначала делаем реализацию API inserver_*devstat -- пока примитивным копированием из bigcref, а не на SLOTARRAY.
    • "Мясо" реализуется функциями AddDevsOnchgCallback() и DelDevsOnchgCallback(), объявленными традиционно в cxsd_module.h, а живущими в cxsd_drvmgr.c.

      Они скопированы с минимальными изменениями с *ChanOnret*(); по-устройственные списки зарегистрированных "низкоуровневых" callback'ов хранятся в d_cblist[]/d_cblist_end[].

    • Непосредственно вызов всех зарегистрировавшихся производит CallDevsOnchgCallbacks(), дёргаемая из stop_block(), FreezeBlock(), ReviveBlock().
    • В cxsd_channels.c поменяно с модели
      cbp->callback(...);
      cbp=cbp->next;
      
      на
      next_cbp=cbp->next;
      cbp->callback(...);
      cbp=next_cbp;
      

      Причина -- оно могло сбрендить, если бы callback сам себя снимал (что и делает devs_onchg): при этом Del*Callback() сделает bzero() структурке, и next пропадёт.

      А в cxsd_bigc.c УЖЕ было примерно так!

    • ЗАМЕЧАНИЕ: автоматическое снятие callback'ов для уходящих в OFFLINE сделано не в CallDevsOnchgCallbacks(), как изначально хотелось, а в inserver_devs_callback().

      Причина -- что DelDevsOnchgCallback() делает bzero() переданной ему структуре, а она еще нужна самому inserver_devs_callback()'у. Посему -- именно он и выполняет авто-диссоциацию.

    • В stop_block() в секцию подчистки добавлено.

    Традиционно -- надо проверять.

    P.S. И не перенести ли реализацию *devstat* вверх -- ПЕРЕД dataref_evproc? Ведь оно как бы "основнее".

    Парой часов позже: да, перенёс. Итого, план дальнейших действий по этим фичам:

    1. Проверить, что работает. 22.02.2012: проверено Роговским, работает.
    2. Перевести на SLOTARRAY -- при этом вся адресация сменится с явной на Access().
    3. Ввести во всю троицу функции поиска по {ref,evproc,privptr}: оно используется дважды, в _add_ -- для проверки, не зарегистрировано ли уже (и щас -- БЕЗ проверки ref!), а в _del_ -- для нахождения, кого удалить.
    4. Превратить FIXED в GROWFIXELEM.
    5. Постараться устранить дублированности/скопированности, помеченные выше как (1)-(5).
    6. Реализовать и в remsrv.

    20.02.2012: а ведь всё-таки НАДО для скопытившегося девайса снимать все поставленные на него chan/bigc-evproc'ы. Ровно по той же причине, по которой автоматом снимается и devstat-evproc -- в основном ради remsrv-драйвлетов.

    Подчищать надо сразу ПОСЛЕ уведомления юзеров, чтобы затронутые могли спокойно подчистить свои запросы сами. Т.е., реально подчищаться будет только за "неряхами".

    Есть технологическая проблема, аналогичная devstat'овой:

    • По-хорошему, это удаление должен бы производить модуль drvmgr, конкретно stop_block().
    • НО: у него информация только о низкоуровневых callback'ах, уставленных уже cxsd_driver'ом, а у того есть еще своя кухня, и удалять надо именно егойным inserver_del_chanref_evproc()'ем.
    • Т.о., полностью НОРМАЛЬНО удалить уставленный callback, выходит, СЕЙЧАС -- невозможно.
    • С одной стороны -- значит, архитектура кривовата, не должно такого быть.
    • С другой -- сейчас можно решить проблему добавлением во все *_cbitem_t поля "деструктор" -- какую функцию вызвать для разрегистрации (а оно уж там пусть само разбирается).

    21.02.2012: да, так и делаем.

    1. К {chan,bigc}_cbitem_t добавлены поля on_stop devs_cbitem_t -- нет, ибо незачем).

      "Юзеры" в cxsd_driver.c заполняют их.

    2. Создаём "подчищаторы" ClnChanOnRetCbList() и ClnBigcOnRetCbList(). Они проходятся по списку callback'ов для указанного канала и вызывают у всех on_stop(), а если оный ==NULL или всё равно ничего не сделал, то делают DelXXXXOnretCallback() сами.
    3. 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() -- слежение за соседскими каналами, с автоматическим привязыванием (см. предыдущую запись за прошлый март).

    Некоторые дополнительные подробности:

    • Сделано в интересах простых драйверов типа triggered и multwrite, реализация скопирована из них. В обоих случаях используется тип inserver_refmetric_t. Для оптимизации под слежение может указываться не один канал, а массив (потому и "_list") -- количество определяется полем m_count в 0-м элементе.
    • "Последней каплей" послужило создание драйвера s2v (scalar-to-vector), в котором не захотелось в очередной раз повторять все хитрости.

      (s2v -- v2hw'шный драйвер, унифицированный с остальными fastadc по набору параметров.)

    • Однако, сделать единый API, подходящий как для "простых" драйверов (работающих с дуплетами {УСТРОЙСТВО,КАНАЛ}), так и для более навороченных, типа ist_xcdac20 (множественные каналы ОДНОГО устройства -- {УТРОЙСТВО,КАНАЛЫ}) пока не вышло.

      Для последних (там должны добавиться как минимум v3h_xa40d16 для ВЧ300 и v1k_xcdac20 для В1000) будем делать свой, отдельный модуль, включающий и машину состояний.

    • "Целью" могут быть не только обычные, но и большие каналы -- для этого надо указать поле chan_is_bigc!=0 (и callback тогда будет считаться bigc_evproc'ом -- из-за их одинаковости даже warning'а не будет).

      12.03.2014: ага, а в собственно ДОБЫЧЕ ссылки оно не проверялось. Результат должен был получаться совершенно бредовый (спасло лишь то, что до сегодняшнего дня не использовалось). Пофиксено.

    • Подытожим: по факту это есть движение в сторону линков а-ля epics.

    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. Там потребуется как-то понаперемешанные НЕСКОЛЬКО разных сущностей:

    1. Собственно биндинг множества каналов одного target-девайса.
    2. ...в том числе -- НЕСКОЛЬКИХ девайсов (нужно для v3h_xa40d16).
    3. Машина состояний.

    ...а вот ПАРСИНГ там не требуется, поскольку просто имя устройства и так несложно добывается.

    Работы ведём в 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) -- повеситься, поэтому разделение следующее:

    1. "Нижний" слой -- observer, занимается слежением за target-устройством.

      ...или несколькими -- технология та же с "m_count".

    2. "Верхний" слой -- машина состояний, пользуется услугами observer'а.

    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: вылезло некоторое неудобство:

    • Из-за ошибки в коде v3h_xa40d16.c конец target-имени обрезался, так что устройство не могло найтись, но НИКАКОГО сообщения об этом не было.
    • А надо б бы: ненайденное имя легитимно (будет) только в среде remsrv (где inserver пока не поддерживается), а в рамках обычного сервера это ВСЕГДА ОШИБКА.
    • Так что, по образу прочего reconnect-кода (cda, remdrv) надо вводить "is_suffering" в vdev/observer.

    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: близкая тема: Федя время от времени возникает, что надо бы мочь ограничивать СУММАРНЫЙ ток, уставляемый в несколько разных каналов ЦАПа, мотивируя желание тем, что управляемые этими каналами источники питаются от единого БП, имеющего ограничение по току.

    На уровне САМОГО драйвера (да и сервера) такое не реализовать никак -- не то место. Но не есть проблема сделать драйвер-прокладку, чтоб всё шло через него, а он уж может считать сумму и ограничивать запись, приводящую к её превышению. Проблем только две:

    1. При записи нескольких уставок сразу есть шанс превысить сумму -- если драйвер сравнивает со СТАРЫМИ значениями, а "в полёте" уже новое. Самый частый частный случай -- загрузка режима.

      Но это решается просто: надо запросы на запись запоминать, вместе со значениями, и при обработке следующих к сумме прибавлять не "последнее известное записанное", а с "запрошенное на запись". И флаг "запрошено" сбрасывать по возврату актуального значения.

    2. Как организовывать список каналов -- ЧТО суммировать/лимитировать.
      1. Общий случай -- просто произвольный список каналов; вероятно, от разных устройств. Указывать дуплетами dev.chan в auxinfo (ограничить, например, 98 штуками, 98-й канал сделать суммой (управляемым) а 99-й канал текущей суммой).

        Максимально общо, но громоздко и неудобно в случае, когда все каналы в ОДНОМ ЦАПе (что чаще всего).

      2. Более частный: считать, что target-устройство -- kozdev_common-compatible, так что:
        • Из auxinfo брать только его имя;
        • "карту" каналов наверх отдавать того же формата, ...
        • ..., остальные каналы (вроде регистров) просто туннелируя (только отмаппировав специфичности (лимит и текущую сумму) в какие-нибудь незанятые места).
        • Таким образом, можно будет подсовывать этот драйвер-прослойку вместо настоящих устройств -- например, другим vdev-based-драйверам, типа v3h_xa40d16 (вместо настоящего xcandac16).

      Какой из вариантов выбрать -- надо смотреть по конкретной задаче. Сделать любой из них несложно.

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

      12.05.2014: отдельное замечание: а что, если понадобится так ограничивать сумму уставок источников, являющихся разными vdev-драйверами, работающими через разные устройства (пример -- ИСТы)? Ответ: видимо, ограничение надо будет ставить "выше" -- чтоб клиенты писали в каналы драйвера-сумматора, а тот уж переадресовывал бы в _CHAN_OUT'ы тех vdev-драйверов. Да, тогда получится уже 3-уровневый бутерброд.

    07.05.2014: битым текстом: устройство vdev_set_state() таково, что его МОЖНО вызывать из swch_to-методов, и всё отработается корректно (только вычитывание state-related channels будет сделано несколько раз).

    08.05.2014: и даже более того:

    1. Реализация "реверсивного" ИСТа сделана целиком на основе возможности смены состояния прямо из swch_to-методов: в SwchToON_UP() (перед началом поднятия тока; и также в ist_xcdac20_rw_p() при записи уставки) оно проверяет, что если полярность надо менять, то отправляется в состояние IST_STATE_RV_ZERO, начинающее цепочку смену полярности, которая заканчивается переходом обратно в IST_STATE_SW_ON_UP (так что всё корректно отработается даже если посреди этой цепочки юзер передумает и опять сменит полярность -- при повторном входе в ON_UP оно еще раз проверит и опять уйдёт на переполяризацию).
    2. И реализация состояния DETERMINE переехала именно в SwchToDETERMINE(), и прекрасно там работает, переключаясь в определяемые там состояния (вместо былого хака внутри ist_xcdac20_sodc_cb(), из-за якобы невозможности переключения в другое состояние из swch_to).

    15.06.2014: в vdev'е надо бы кое-что добавить:

    1. Заполнение таблиц state_important_channels[] и ourc2sodc[] -- каждый драйвер делает сам, а оно длинное.
    2. find_sdp() -- почему у каждого драйвера свой экземпляр?
    3. Требуется поддержка больших каналов. Пока примитивная, чтоб просто включался опрос -- надо для bpmd_cfg_drv.c. В bpmd_cfg пока обошлись inserver2'шной поддержкой больших каналов.
    4. (17.06.2014) надо чтоб 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: теперь он будет жить в include/.

    24.04.2012: смысл -- ибо достало. Еще что у разных сред исполнения свои cxsd_driver.h, и приходилось химичить и надеяться на включение нужного.

    • Сейчас введено, что при #ifdef TRUE_CXSD_DRIVER_H делается #include TRUE_CXSD_DRIVER_H, а собственное содержимое пропускается.
    • Сейчас единственным таким "true" является cangw_cxsd_driver.h, в который переименован былой nppc_canserver/cxsd_driver.h.
    • В будущем предполагается сделать более "гранулярное" отключение частей стандартного cxsd_driver.h, так что:
      1. стандартный API будет описываться непосредственно в нём
      2. (возможно, с отключением вещей типа inserver),
      3. а заменяться будут специфичные вещи, в основном -- DEFINE_CXSD_DRIVER.
    • Соответственно, нынешний cm5307_dbody.h/cm5307_sl_dbody.h превратится в какой-нибудь "cm5307_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: при сегодняшней разборке с глюком remdrv (когда он пытался еще что-то делать после застреливания) стало ясно, что следует ввести защиту от таких случаев -- чтоб OFFLINE-устройства вообще ничего не могли делать.

    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*().
      • Ironically, также и в SetDevState().
      • И в ReRequestDevData().

      Добавлены.

    • ...за компанию и серверовы проверки запиханы в do{...}while(0).

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

  • 13.09.2012: после введения (в предыдущем пункте) ограничения "мёртвые устройства не могут ничего делать" вылезло, что sato перестало работать, и приводило к SIGSEGV'у.

    13.09.2012: симптомы оказались знакомыми по предшествующей разборке с падением по таймауту -- вызов метода с devptr==NULL.

    Обусловлено такое изменение оказалось как раз предыдущим пунктом -- что теперь мёртвые устройства не могут и RegisterDevPtr() делать, а в InitDev() ОТСУТСТВОВАЛО заполнение d_state[devid] ПЕРЕД вызовом init_dev(). Исправлено.

  • 02.10.2012: делаем, чтобы в do_big() при datasize==0 передавалось бы data=NULL, и аналогично при nargs==0 чтоб args=NULL.

    02.10.2012: это в двух местах -- в сервере и в remsrv.

    1. В сервере производим оное в AddBigcRequest().
    2. В remsrv -- в ProcessPacket(), прямо в самом вызове.

    29.12.2012: да вроде всё нормально, так что "done".

  • 23.11.2012: создаём возможность драйверу сделать "ping" своему хозяину (в основном -- для remsrv). Это достигается путём указания ReturnChanGroup()count=0 (см. идею за 21-11-2012).

    23.11.2012: собственно:

    • Во-первых, вместо просто "0" введена константа RETURNCHAN_COUNT_PONG. Это и наглядность/искабельность повышает, и позволит в будущем при надобности добавлять еще специфичности (выходит этакий "обратный ioctl").

      Из-за до сих пор недоунифицированного API добавлять пришлось аж в 3 места: cxsd_driver.h, remdrvlet_c.h, cm5307_dbody.h.

    • Дальше, во все реализации ReturnChanGroup()'а вставлена обработка оной константы:
      1. remsrv_drvmgr.c -- отсылка пакета "PING".
      2. cxsd_channels.c, remdrvlet_c.h, cm5307_dbody.c -- игнорирование (а раньше ругнулось бы в log).
    • Воспользование новой фичей: cankoz_pre_lyr.c::cankoz_add() и nkshd485_drv_common.c::piv485_open().

    03.12.2012: да, в canserver'е это работает, REMDRVP_PING отсылается. Так что "done".

  • 02.06.2016: какая-то странность в ottcam'е: на экране всегда показывается ПРЕДЫДУЩИЙ кадр, а не текущий.

    Такое впечатление, что "предыдущий" присылается не устройством, а софтом (сервер? драйвер?).

    02.06.2016: протокол:

    • Предыдущесть обнаружена была так: ставится частота кадров ~10с, включается пучок, после получения кадра выключается, но на следующем кадре он есть (хотя не должно быть).
    • Дополнительная странность: по нажатию кнопки [Stop] вместо пустого кадра присылается как-то заполненный. А по следующему [Shot] -- наоборот, пустой, и только по еще следующему -- уже с картинкой.

      03.06.2016: Хотя конкретно этот эффект, возможно, как-то и вызван тем, что pzframe_gui::RgParKCB() уставляла параметр большого канала.

    • Оттмар рылся-рылся, но так и не нашел у себя косяков.
    • И сниффер никакого криминала не показал -- всё как положено (а так-то наблюдаемый эффект мог бы объясняться, если б камера сразу по NEW_FRAME присылала бы все скан-линии, кроме, например, одной).
    • Вывод: косяк где-то в софте. Но где -- совершенно неясно, надо всё тестировать с толпами отладочной печати.
Drivers:
  • 08.05.2004: некоторые правки в m5200_drv.c, при добавлении параметра magicid к DoDriverLog().

    08.05.2004: во-первых, там делались попытки максимально подходящим образом имитировать нынешнее поведение DoDriverLog()'а -- печатались magicid и __FILE__. Это убрано, код упростился и украсивился.

    Во-вторых, поскольку уже давно реализован вызов DisconnectBlock(), а в сим драйвере было несколько мест, требующих его в комментариях (собственно, ради этого драйвера вызов и родился), то вставил его.

  • 25.05.2004: случайно обнаружилось, что ссылка на std_abstract_iface.h как на "экспортируемый" файл стоит в LocalRules.mk вместо Makefile (видимо, при изготовлении отдельного LocalRules.mk забыл правильно разделить).

    11.06.2004: исправил -- перенес.

  • 20.06.2004: подготовка ppc-slaves:

    20.06.2004: для начала, имеется несколько мыслей на тему "как это надо сделать":

    • Во-первых, надо б избавиться от "неполиткорректного" словца "slaves". На что его заменить? По смыслу это -- "драйверята". Ну что -- "drivelets" (по аналогии с "servlets"), сокращенно -- "drvlets"? (Менять надо будет 1.Имена директорий в src/ и exports/, 2.*.mk, 3.переменная окружения $UCLINUX_SLAVES_PATH.)
    • Во-вторых, для cm5307-uclinux и cm5307-ppc-linux явно надо использовать одинаковый протокол и даже одинаковый серверный драйвер. Отсюда -- еще более глобальное переименование -- "m5200" в, например, "cm5307".
    • Следующее следствие -- cm5307_drv должен понимать "тип подчиненного контроллера" -- например, в auxinfo указывается "ppc/DRIVER" либо "uclinux/DRIVER".
    • Надо "складировать" все общие для контроллеров семейства cm5307 исходники в одно место, а из cm5307_uclinux_drvlets/ и cm5307_ppc_drvlets/ делать туда симлинки. К таковым исходникам относятся rrund.c, cm5307_alfo.c, и нынешние uclinux_{camac,driverbody}.[ch] (будущие cm5307_{camac,drvletbody}.[ch]).

      (Угу -- структура директорий: drivers/cm5307/, в которой есть common, содержащая "общие" вещи, и поддиректории uclinux_drvlets/ и ppc_drvlets/.)

    20.06.2004: начинаем изготавливать cx/tools/c5307-ppc/. Сценарий:

    • Копируем туда из (cm5307ppc-linux-sdk-linux-v1.tgz)/ppc860/linux/ подветки tools/lib/gcc-lib (целиком), tools/bin (за вычетом огромных gdb (10M), run (5M) и непонятно зачем нужного make).
    • Поскольку у gcc глюки с поиском "as" (этот кретин вместо "ppc-linux-as" ищет просто "as", и к тому же в не тех где надо директориях), а у collect2 аналогичные проблемы с ld, то делаем tools/ppc-linux->. плюс tools/bin/as->ppc-linux-as и tools/bin/ld->ppc-linux-ld.
    • Населяем tools/lib/ нужными файлами из ppc860/linux/initrd_big/{lib,usr/lib}/:
      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*
      
    • Командная строка для ".c->.exe":
      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: что сделано за прошедшее время:

    • Создана поддиректория drivers/cm5307/, в которой будет размещаться ВСЕ, касающееся cm5307. В т.ч., серверный драйвер, переименованный теперь в cm5307_drv.c.
    • cm5307_drv.c: перекопан под "cm5307" вместо былого "uclinux"/"m5200". В т.ч., auxinfo теперь имеет формат "arch/host:driver[slaveinfo]", где обязательный параметр "arch" -- либо "uclinux", либо "ppc". Соответственно, в зависимости от оного испльзуются разные переменные окружения для определения пути.
    • В drivers/cm5307/ созданы 3 поддиректории: uclinux_drvlets, ppc_drvlets и common. В последней содержатся "общие" файлы -- cm5307_{camac.[ch],drvletbody.[ch],i_cm5307.c,alfo.c,rrund.c} плюс CommonRules.mk, содержащий правила для симлинкования этих общих файлов.
    • В uclinux_drvlets/ воссоздано все, делавшееся в drivers/uclinux_slaves/.

    Из общих вещей: вместо суффикса ".driver" теперь используется ".drvlet".

    27.06.2004: полностью запинал "прототип" компилируемости ppc-drvlets.

    Пришлось еще повозиться с include-директориями, плюс доделать правила для перевода %_drv.o->%.drvlet.

    Пока что include берутся из /tmp/mamkin -- надо будет корректненько поразбираться, чего ж подобавлять в cx/tools/cm5307-ppc/include/, а то все копировать -- жаба душит, там 9М.

    И -- сделан симлинк cm5307-ppc/include/camac->../../cm5307/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.

    Что еще надо будет сделать:

    • Донаселить include/.
    • Доразобраться с таким понятием, как "DIRECT_DRVLETSOURCES" (ex-DIRECT_DRIVERSOURCES, вроде было введено для Чугуева).
    • Сделать автоматический запуск rrund на cm5307-ppc.
    • Сделать один BACKUP/ в такой "гибридной" комплектации -- uclinux_slaves+cm5307, и после него uclinux_slaves и m5200*.* истребить.

    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:

    • Надо чтобы rrund при проблеме с запуском попрошенной программы возвращал бы драйверу ответ об этом -- по протоколу cm5307_drvlet.
    • Недурственно бы проверку совместимости версий делать не только на стороне драйвлетов, но и в cm5307_drv. Вводим ответный пакет на 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 контроллера -- в строке

    ifconfig eth0 192.168.2.100 netmask 255.255.255.0
    
    (все ppc идут с адресами вида 192.168.{2,8}.1NN -- адреса с сотни, подсеть 2 на linac1 и 8 на ring1).

    02.07.2004: сделал, что rrund возвращает ошибку при ненахождении файла для exec()'а. Для этого:

    1. Ввел новый код -- CM5307DLP_RRUNDP/*'RrdP'*/.
    2. cm5307_drv по нему выдает в лог присланную строку и застреливается.
    3. Вставил поддержку этого в rrund.c -- во-первых, собственно #include"cm5307_drvlet_proto.h" (так что теперь rrund НЕ независимая/самостоятельная программа), а во-вторых -- сделал там ReportProblem() по образу и подобию cm5307_drvletbody.c::vDoDriverDebug().

    Заодно обнаружил, что rrund почему-то при проблемах при чтении имени программы для запуска сразу делает exit() -- тем самым отваливаясь целиком. Поправил.

    15.07.2004: еще махинации с *_drvlets/LocalRules.mk (необходимость в них вылезла при написании CAMAC-benchmark'а на основе cm5307-camac, во внешней директории):

    • Стоял какой-то странный if, делавший include ../common/CommonRules.mk при "$(OUTOFTREE)"=="". И -- это было ДО Rules.mk. Естественно, при первом же внедеревном использовании эта конструкция глюканула.

      Решение: перенес сей include в ПОСЛЕ Rules.mk -- и вообще в раздел "Out-of-tree usage support", в ветку if'а "$(OUTOFTREE)"=="" и он теперь выглядит как

      include $(D_CM5307_PATH)/common/CommonRules.mk
    • Поскольку в *_drvlets/Makefile отсутствовало определение DRVSRCDIR, а вместо того оно было в LocalRules.mk, где заключалось в if (это -- еще наследство uclinux_slaves), то НИКАК нельзя было делать директории БЕЗ абстрактных драйверов, только с утилитами.

      Решение: DRVSRCDIR определяется теперь в Makefile, а if из LocalRules.mk убран. Т.о., поскольку в AbstractCamacRules.mk есть соответствующая проверка, можно просто присваивать DRVSRCDIR'у пустоту.

    • Поскольку в LocalRules.mk в определении LOCAL_INCLUDES стояло "-I$(DRVSRCDIR)" (что после предыдущего пункта стало приводить к просто "-I" в командной строке, поглощающей следующий ключик), то пришлось вынести в отдельный ifneq "$(DRVSRCDIR)" "" строчку LOCAL_INCLUDES+=-I$(DRVSRCDIR).
    • Нужен был какой-то способ, как внешним программам нормально ссылаться на *_drvlets/cm5307_camac.o.

      Решение: сделал "выходной" параметр 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.)

  • 19.07.2004: can*_drv: надо, чтобы все драйверы, имеющие УПРАВЛЯЮЩИЕ каналы по получению от блока пакета "инициализация" (хоть по reset'у, хоть по питанию, хоть от чего еще) выполняли бы чтение этих каналов.

    Например, если мы жмем кнопку [Reset] у козачиного CANDAC16, то -- в сервере (при -wY) будут помниться уставленные (или -- считанные при запуске) значения, а в блоке -- будут стоять нули.

    Если же по получению пакета DESC_GETDEVATTRS=0xFF выполнять пересчитывание, с дальнейшим ReturnChan*(), то все автоматически скогерентится.

    01.02.2006: хо, так именно эта технология и реализована еще летом 2005-го в новом can-драйвере! Так что -- "done".

  • 07.08.2004: sim_drv.c: надо сделать, чтобы он умел делать симуляцию не только для больших, но и для обычных каналов.

    Вот понадобилось отладить ipp в отсутствие живой установки -- а хрен. А так бы, раз -- и готово.

    И еще -- пора перевести sim на psp.

    02.02.2008: поскольку давным-давно (уже два года) вместо "собственных мозгов" там используется StdSimulated_rw_p(), то -- withdrawn. Но это "пока" -- в будущем какая-то "умная" симуляция может понадобиться.

    P.S. А sim-то на psp перевел, да.

  • 21.02.2005: sim_drv.c: обнаружил, что там все было рассчитано на отведение места под privrec сервером, но в DEFINE_DRIVER_REC()'е размер оного указать забыл, там стояло 0, и cx-server великолепно segfault'ился. Пофиксил :-).
  • 26.01.2006: noop_drv.c (да и sim_drv.c): выявилась неприятная особенность: если даже каналы определены как "w", то из них читаются не нули, а -- как и для "r" -- номер канала в блоке.

    Причина -- драйвер-то не знает, какой канал у него что, и всегда возвращает этот номер, в т.ч. -- и при "инициализационном чтении" от 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".

  • 19.01.2007: недавно на CANGW при использовании той же инфраструктуры сборки вылез прикол -- сильно подглючивал realloc(), вместо отведения нового блока дававший ссылку на другой, ранее отведенный, блок такого же размера.

    Выяснилось -- дело было в том, как указывались параметры для линковки: в конце командной строки была прямая ссылка на $(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/).

  • 21.01.2007: кстати, а не свести ли все специфические для PowerPC/m860 определения, общие для cm5307-ppc и CANGW, в отдельный файл в tools/cm5307-ppc/? И пусть его и делают include те двое -- ppc_drvlets/LocalRules.mk и gw/Makefile -- вместо прямой ссылки на Rules.mk, а уж он пусть все такое исполняет.

    А то надоело уже править в обоих местах одинаково.

    13.02.2007: да, сделано, все вытащено в drivers/ppc860_rules.mk.

  • 02.02.2008: новый sim_drv.c.

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

    • Главное отличие -- он хранит массив параметров большого канала, имитируя настоящие драйверы (точнее, "честно" поддерживает нынешнюю семантику параметров). Так что если одна программа что-то уставила, то и в другой это отражается; плюс, одныжды уставленный параметр сохраняется все время жизни сервера.

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

    • Вместо былого позиционного '/'-синтаксиса используется PSP.
    • "Модуляция" rand()'ом выкинута за ненадобностью.
    • Массивы как параметров, так и данных отводятся malloc()'ом -- так что теперь снято дурное ограничение в 10000 единиц, а поставлено (от фонаря) 1000000.
    • Оно работает в двух режимах: либо немедленный ответ-"измерение" -- если параметр msecs не указан, либо оно само, независимо от запросов, генерит данные с периодом msecs.
    • ЗАМЕЧАНИЕ: многоканальность не поддерживается -- считается, что всегда есть один-единственный канал.

    Новый вариант сделан практически с нуля, а не на основе старого. Старый, если что, сохранился в BACKUP и STABLE (его дата была 01-02-2006).

  • 19.05.2008: еще прикольчик на тему PPC -- cm5307-ppc и CANGW: если контроллер включается ДО своего host-компьютера, то он не может смонтировать свой /mnt/host, и уже никогда не сможет ни это, ни запустить rrund.

    А что -- если сделать так: вытащить монтирование и запуск в отдельный процесс в background'е, который при обломе монтирования засыпает на 60 секунд и повторяет попытку снова, а при успехе -- запускает rrund и отваливает?

    20.05.2008: да, сделал такое -- вытащил соответствующие команды в отдельный файл /etc/mounthome.sh, имеющий такой вид (на CANGW):

    #!/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/rc.sh теперь осталась строка
    /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: насчет конфигурации контроллеров -- "что делать"...

    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 имеется строчка вида

    /export/swan            /export/HOST            none    bind            0 0
    в /etc/exports -- соответственно,
    /export/HOST    192.168.8.*(ro)
    а в контроллере в конце /etc/rc.sh указываем
    /etc/mounthome.sh $MY_HOST:/export/HOST &

    Ключевые моменты в /etc/fstab: тип файловой системы -- none (хотя, по слухам, и bind тоже катит), fs_freq и fs_passno -- нули (не дампить и не проверять).

  • 01.11.2008: недавно Юра Роговский натолкнул на мысль: вообще-то нехорошо, что все драйверы на CM5307, CM5307/PPC и CANGW работают под root'ом. По-правильному -- делать в rrund и в canserver setuid() подо что-нибудь другое, например, daemon или лучше вообще nobody.

    Эта фича должна быть опциональной -- например, по ключику "-uUSER". Замечание: на CM5307, под uClinux, файл /etc/passwd ОТСУТСТВУЕТ, так что там проканает только "-uUID". Да и с getpwnam() & Co. не удивлюсь, если будут "неудобства".

    Вопрос в том, какие могут возникнуть сложности -- например, по доступу к устройствам (/dev/camac и /dev/can*).

    16.03.2017: чтоб не гадать:

    • /dev/can* (cangw-v1000):
      # 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
      
    • /dev/camac (ppc-blm):
      # ls -l /dev/camac 
      crw-rw-r--    1 root     1000      21,   0 Mar 11  2004 /dev/camac
      

    Т.е., с CAN -- нет проблем, с CAMAC -- будут (там открывается с O_RDWR).

  • 13.03.2009: еще идея от Роговского: надо бы во все _drv_i.h-файлы быстрых АЦП включать какие-нибудь константы для указания МАКСИМАЛЬНОГО количества данных (либо на канал, либо на все каналы). Иначе мы имеем магические числа в драйверах и в клиентах.

    А по-хорошему и dataunits тоже надо бы там указывать.

    13.03.2009: да, сделал "по максимуму".

    1. У всех nadc* теперь есть константы:
      • NADCxxx_MAXNUMPTS -- максимально допустимое значение параметра NUMPTS.
      • NADCxxx_NUM_LINES -- "канальность" блока -- обычно 2 или 4.
      • NADCxxx_DATAUNITS =sizeof(NADCxxx_DATUM_T) -- 1, 2 или 4.
    2. Введен typedef-тип 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: занадобилось -- для candump_HHH и cansend_HHH -- иметь в drivers/LocalRules.mk поддержку сборки также просто-бинарников (не-драйверов).

    11.08.2009: да, теперь Makefile'ы могут указывать UTILSSOURCES (список .c-файлов, из которых будут сделаны бинарники) плюс LIBSSOURCES (список .c-файлов, для которых надо генерить зависимости). (Да-да-да, с человеческой системой сборки эта проблема даже и не встала бы :-))

    Кстати, предыдущая дата на файле была 14-06-2005.

  • 27.01.2012: до чёртиков уже надоела uclinux_drvlets & Co. Надо избавляться!

    27.01.2012: юзеров её практически изначально было всего 3:

    1. linac1:9,192.168.2.2 -- термостабилизация. Она уже переводится с CAMAC на CAN.
    2. linac1:10,ucl-sukhanov -- фазовые измерения. Сейчас апгрейдится, и сразу поставим CM5307/PPC.
    3. linac1:57,l5-spectre -- сей момент не работает, но там тоже надо просто поменять контроллер.

    Так что -- аминь!

    • Выкошены директории uclinux_drvlets/ из cx/src/, qult/, weld/, и ссылки на них в Makefile/4Makefile.
    • И programs/server/drivers/m68k_rules.mk изначально пустой.
    • Удалена lib/server/drivers/cm5307_uclinux_drvlets из EXPORTSTREE.
    • А вот кросс-среду -- cx/tools/cm5307/ и x-compile/mcf5200-linux/ -- пока оставим.
  • 10.08.2013: пора переводить драйверы в programs/drivers/ из былого programs/server/, в связи со скорым закрытием последней.

    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: продолжаем:

    • Неофиты -- triggered и multwrite -- проверены, работают.
    • Переводим все Makefile'ы на programs/drivers/DirRules.mk -- легко и просто.

    12.08.2013: добиваем:

    • Повсеместно устранена специфика "SUBFLSSOURCES" и "UTILSSOURCES". В основном все Makefile'ы переведены на прямое указание MONO_C_FILES или SOLIBS, а где лениво -- заиспользовано свежевведённое LOCAL_DEPENDS.
    • Удаляем старое:
      • Старые work/yweld/ и work/istc3682/ отправлены в ARCHIVE/. Первый так никогда не вышел из стадии тестового проекта, а второй давно стал не нужен (грант закрыт, драйвер kshd485 есть в v2hw/, а impacis10 вряд ли понадобится (если что -- пересделаем на sendqlib'е)).
      • Туда же уехал и cx/src/programs/server/ со всеми поддиректриями (в old-cx_n_v2hw-sd.20130813/).

        А за ним и старые драйверные директории из 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/.

      • За компанию убрано ставшее ненужным:
        • Старая директория lib/rem/, туда переехала gem/.
        • Старые API Dev**FD, DevTout и DevIO.

          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@лыжи-10:00-5км: надо реализовывать pzframe_gw_drv.

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

    • Мысль такая возникла несколько месяцев назад -- в связи со всеобщим переходом на cxscheduler+fdiolib, так что cda+cxlib стали годны для использования в среде исполнения сервера.
    • Конкретная сиюминутная потребность -- для драйвера fadiffer, коий сейчас просто не на чем испытать: единственные в хозяйстве adc200me на foxy, где стоит мега-древняя версия сервера, так что туда не подселить (а обновлять там софт неохота -- "не чини то, что работает").
    • Но контраргумент: у нынешних cda/cxlib ведь нет параметра uniq, так что НЕ будет работать авто-подчищение ресурсов при убиении драйвера.
    • Контр-контраргумент: сделаем это в term_d().
    • ...и, собственно -- понадобится реально наполнить 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/.

  • 27.05.2014@Снежинск-Снежинка-206-вечер: занадобилось на сварке (точнее, на "3D-принтере") мочь менять уставку в ЦАПе по некоему закону (квадратично). Козачиная таблица тут не помощник.

    А чё б не сделать СОФТОВУЮ реализацию обычной табличной waveform -- специальный драйвер, часто-часто сбагривающий данные из записанной в него таблицы в ЦАП?

    28.05.2014@Снежинск-каземат-11: приступаем, драйвер назовём softwform.

    ...кстати, потом можно расширить функционал double-buffer'ингом: иметь второй буфер, в который писать во время исполнения первого, чтоб по окончанию первого драйвер бы "менял их местами", переходя к исполнению второго. Так можно реализовывать длинные непрерывные процессы.

    После обеда: вроде первоначальный вариант доделан.

    • Период -- 100мс (10 записей в секунду). В принципе, можно поробовать сделать 10мс/100Гц...
    • Блюсти его она старается точно -- через sl_enq_tout_at(). Исходя из сути задачи.
    • Единственное что -- цепочка HeartBeat()'ов начинается при старте драйвера, и НЕ привязывается к сигналу START. А надо бы -- тогда куча процессов может запускаться скопом через multwrite.

      Чуть позже: да ну нафиг -- сделано. Оно ре-стартует цепочку прямо в момент START.

    • Принимает оно данные просто вектором, беря количество из datasize, а возвращает с info[0]=numpts, чтоб можно было отрисовывать.
    • Запрос без данных считается чтением.

    Теперь -- проверять.

    17.06.2014: проверено, после исправления мелкого глюка (не было bzero(opts), и считалось target.chan_is_bigc) работает.

    Теперь надо будет еще добавить конфигурябельность периода.

    23.06.2014: конфигурябельность тоже добавлена -- параметр period. Работает.

  • 24.09.2014: в дополнение к softwform надо б заиметь еще скалярный драйвер "плавное изменение" -- чтоб позволял для любого канала менять значение со скоростью не выше некоторой, как это делают CAN-драйверы ЦАПов.

    Т.е., такому драйверу указывается целевой УСТРОЙСТВО.КАНАЛ, а он имеет 3 канала -- OUT, OUT_RATE, OUT_CUR (уставка, скорость, текущее), и при записи в OUT сбагривает значения в "жертву" со скоростью не выше OUT_RATE.

    Потребность -- на сварке, там канал "ток" в weld02 надо б мочь изменять плавно.

cm5307_drv:
  • 07.06.2005: cm5307_drv.c -- новый вариант, с авто-reconnect'ами.

    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: продолжаем тестирование.

    • Наткнулся на забавный финт: первые пять драйверов в мотороллере запускаются резво, а 6-й -- с задержкой в 3 секунды. Думал -- отчего бы? А потом дошло -- ведь в rrund.c стоит 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: да, проверил аж три "жестких" случая:

    1. Reset (восстановилось).
    2. Вытыкание кабеля и втыкание его через 5 минут (восстановилось, без побордовения).
    3. Вытыкание надолго (через 15-20 минут побордовело, после втыкания -- восстановилось).

    Все испытания прошли успешно.

    "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. Смысла его править нету никакого, так что -- удаляем из дерева!

  • 28.06.2005: давно надо было включить на ветке cm5307_drv<->драйвлеты SO_KEEPALIVE.

    28.06.2005: да, вставил:

    1. В cm5307_drv.c::FdWrReady_p().
    2. В cm5307_drvletbody.c::DriverBody() -- на всякий случай в ДВУХ экземплярах: для дескрипторов 0 и 1.

    25.01.2006: "done" -- это надо было сделать еще в прошлом июне :-).

  • 25.02.2006: еще несколько дней назад вылезло -- Федя сказал -- что в новой магнитной системе (linmag) время от времени сами по себе "синеют" каналы, связанные с ppc/v1000. И -- хрен его знает почему...

    Так что -- вставил при получении пакетов и при их отправке в _fd_p() и _big_p() диагностические сообщения с пометкой DRIVERLOG_C_PKTINFO -- чтобы можно было включать при надобности.

  • 11.05.2006: заметил, что разбивка auxinfo в cm5307_init_b халтурновата -- оно просто ищет пробел, и все с него считает за drvletinfo. И -- ни '\t' не поддерживается, ни пробелы в начале drvletinfo не пропускаются...

    12.05.2006: полностью переделал тот кусочек -- теперь оно ищет первый isspace(), засчитывая его за конец drvname, потом пропускает все isspace(), и уж результат сих действий считает за начало drvletinfo.

  • 15.05.2006: еще прикол: было объявлено дюже коротким поле 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_drvlet_proto (возникло в его близнеце cangw_cansrv_proto) относительно отладочной печати: надо, чтобы DoDriverLog() от удаленных драйверов УЧИТЫВАЛ текущую d_logmask[] этого драйвера и текущий уровень "болтливости" (verbosity) --
    • и чтобы зря по сети не гонялись не-должные-видеться-сообщения,
    • и даже если прилетело, то чтоб по _DEBUGP оно просто игнорировалось.

    02.01.2007: реализация может состоять из двух частей, по частям задачи:

    • Добавить к cm5307_pkt_header_t.var.debugp поле "level", в котором просто передавать параметр DoDriverLog().level -- это будет сразу и level, и mask.

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

    • Ввести пакет host->drivelet, в котором бы указывались level+mask.

      Когда его передавать: в точке хост-драйвера, реагирующей на пакет _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, а максимальной -- -1.
    • И, с другой стороны, чтобы драйвер почем зря не гонял при этом информацию в случае с actual_mask==0, надо его "предыдущую" также ставить в -1 -- тогда любая другая будет отличаться и вызовет посылку маски, а при actual_mask==-1 и так уже все будет окей.

    (Уровень же 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: раз уж движемся к полной поддержке удаленных драйверов, то надо реализовывать и удаленные SetBlockStatus() и ReRequestData().

    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. Не то чтобы прям щас сильно требовались, но -- пусть будут. Делаем:

    • В lib/rem/remsrv_drvmgr.c.
    • В remdrvlet_c.h (будущий lib/rem/remdrvlet.c) -- почти копия.
    • В cm5307/common/cm5307_dbody.c (это уже ненадолго).
  • 04.01.2007: вообще по результатам реализации ВТОРОГО протокола подчиненных драйверов и более полной реализации удаленного API стали очевиды некоторые соображения (которые надо реализовать в CXv4):
    1. Явно надо иметь просто ЕДИНЫЙ протокол удаленных/подчиненных драйверов.
    2. И удаленные драйверы должны включать тот же самый cxsd_driver.h, чтобы всякие числа (коды состояний драйвера, log-маски и т.д.) были унифицированы -- в этом случае их можно просто не задумываясь передавать по сети от подчиненного драйвера к "хозяину".
    3. А драйвер-хозяин -- тоже ЕДИНЫЙ ("drivelet"?).

      Различаться же должны именно и только конечные серверы/исполнители: если сейчас бедный cm5307 начинен кодом работы с разными архитектурами, то вообще-то надо просто перед запуском rrund делать cd в соответствующую директорию, а он уж пусть запускает "./DRIVER.drvlet".

    11.01.2007: и еще --

    1. Надо этому драйверу как-то указывать endianness подчиненной стороны. Это сейчас оно там безусловно big-endian, а в принципе-то может быть любым.
  • 01.02.2007: исправил давний ляп, почему-то никогда не проявлявшийся -- что в вычислении длины пакета CONFIG делалось лишнее "&~3U". Детали см. в разделе CANGW за сегодняшнее число.

    01.02.2007: ну так понятно, почему этот ляп больше пяти лет не проявлялся -- в драйвлетах всегда были статические буферы (изначально, естественно, забитые нулями), а пакет CONFIG всегда приходил первым. Ну а slaveinfo/drvletinfo использовалось крайне редко, поэтому отбрасывание последних не-нулевых байтов и не замечалось.

  • 25.08.2008: в a_c0609.h зашито чтение в диапазоне 0 (+-8V), что хорошо, и с кодом времени 2 (20ms), что плохо.

    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.

  • 25.08.2010: уже несколько раз за последний месяц на сварке замечалась странная диагностика:
    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.

    Выявление проблемы осножнилось тем, что

    1. более нигде dbody-драйверы не обновлялись (ВЭПП-5 стоит);
    2. на сварке dds300 -- ЕДИНСТВЕННЫЙ dbody-драйвер (frolov_bfp -- на drvletbody, а остальное -- CAN; ошибка же была ТОЛЬКО в dbody);
    3. по веселому совпадению именно у DDS300 и раньше подглючивала электроника, вот и возникло подозрение, что проблема в железке.

    Еще задача усложнялась тем, что в cm5307_dbody.[ch], в отличие от cm5307_drvletbody.[ch] и canserver*, напрочь отсутствует какая-либо печать на stderr, так что даже ручной запуск rrund из консоли ситуацию не прояснил.

    Поэтому добавлена выдача на stderr при запуске и при останове драйвлета, а также при получении неизвестной команды.

  • 15.01.2013: несколько вещей касательно remdrv_drv.c.

    15.01.2013: собственно:

    • чисто для информации: remdrv_drv.c при практически ЛЮБОЙ отправке пакета -- в т.ч. REMDRVP_PING и REMDRVP_READ/REMDRVP_WRITE -- сбрасывает O_NONBLOCK:=0, и лишь после отправки ставит обратно O_NONBLOCK:=0.

      Вот именно по этой причине на прошлой неделе были "чудеса", когда сервер linac1:57 (ipp) время от времени "подвисал", так что его лампочка посиневала -- это просто отправной буфер в сторону контроллера забивался.

    • Имелась пара косметических косячков, затруднявших отладку:
      1. В remdrv_rw_p() код команды вместо "0x%08x" выдавался простым "%d".
      2. В remdrv_fd_p() код команды выдавался бездумно через ntohl() (хотя /*!!!*/ там и было). Добавлено условие.
    • При переходе на новый API "fd" -- с cxsd_fdh_t -- добавился глючок: оно пыталось в ScheduleReconnect() DeregisterDevRdFD того, что еще не было зарегистрировано (fdh=-1) -- на стадии "connecting" (когда зарегистрирован только ). Всё результат кривоватой архитектуры -- в 4cx'ном варианте уже "прямее" (и вследствие лучшего API, и изначально устроено поправильней).
  • 04.03.2013: в remdrv_drv.c вылез косячок на IPP'шном сервере (linac1:57): на высокой частоте комплекса (12.5/25/50Hz) иногда сервер надолго подвисает, и, как ни странно, очухивается по strace.

    04.03.2013: разборки:

    • Точку зависона нашел, при помощи gdb -- write() в remdrv_rw_p(). Оно после развисания выдавало в лог ругательство, но БЕЗ кода результата и errno.

      Посему -- теперь после ошибок перед почти всеми ScheduleReconnect()'ами в диагностику добавлены r и errno.

    • Ключевая же причина ошибки -- явно отсутствие fdiolib'а: из-за этого оно вместо правильной неблокирующейся работы запись делает БЛОКИРУЮЩУЮСЯ, и на ней-то и подвисает.
    • Вероятно, возникает deadlock из-за переполнения входных буферов:
      1. Контроллер присылает много данных, которые еще к этому моменту не вычитаны remdrv'ом, и висит в их отправке (ведь в cm5307_dbody.c -- тоже n_write() вместо fdiolib'а!),
      2. а remdrv пытается что-то отправить "туда", но там уже тоже входной буфер переполнен.
    • И понятно, почему по strace оно очухивается: ловит EINTR, после которого саморестартуется по ошибке. (А если б еще до чтения доходило -- то и проблема б сама исчезала...)
    • Поскольку сейчас на fdiolib фиг перейдёшь, то можно временно пойти экстенсивным путём: увеличивать SO_RCVBUF/SO_SNDBUF. А чтоб не всем вообще -- то можно ввести remdrvopts-параметры rcvbuf и sndbuf, по умолчанию равные -1 ("не менять").

    05.03.2013: да, делаем возможность менять размеры буферов, и перед этим вначале читаются текущие размеры.

    11.03.2013: да, попробовал. Результаты:

    • Попытка увидеть на модифицированном драйвере ПРИЧИНУ "ошибки" при отвешивании обломилась -- оно просто перестало отвешиваться.

      Только по kill драйвлета -- там заявляет errno=104 (Connection reset by peer).

      Так что остальные эксперименты шли уже с модификацией буферов.

    • Умолчательные размеры -- RCVBUF=87380, SNDBUF=16384.
    • В конфиг добавлено "sndbuf=100000,rcvbuf=100000".

      На 12.5Hz почти не виснет (ПОЧТИ!).

      А вот на 50Hz -- продолжает. Не очень понятно, почему -- ведь 100кб на отправку должно хватить (и даже если драйвлет зависнет, то сервер синеть не должен; а синеет!). Или что-то с окном?

      Кстати, иногда синеет на секунду, но потом восстанавливается (врЕменное подтормаживание?).

    Общее впечатление -- надо всё же ускоренными темпами переходить на унифицированное ядро сервера/драйвлетов, на fdiolib+cxscheduler, тогда проблема сама исчезнет.

    12.06.2013@вечер-дома-в-ванне: а ведь скорее всего всё намного проще и СЕРВЕРНАЯ сторона вообще почти ни при чём! Поскольку cm5307_u0632 работает на dbody (и, что обидно, sl_dbody не поддерживает), то затык именно в ней -- это ТАМ оно успевает накидать в сокет тучу данных, так что забивает ядровый буфер отправки. И именно ТАМ надо увеличивать SNDBUF!

    13.06.2013: типа пробуем. Причиной (и вчерашнего размышления) послужило то, что при смене уставок ИПП (чувствительность) они почему-то отрабатываются секунд через 10, а до того просто синеют. Т.е. -- на вид какие-то приколы с буферами. Итак:

    • Увеличение SO_SNDBUF в cm5307_u0632.c вставлено, до 100000 (показывает, что изначально стоит 16384).

      Результат: без толку. Ничего не изменилось.

    • Следующая мысль: а может, там просто CPU слишком загружен (с cangw-chanvac так было)?

      После некоторой маеты удалось запустить top (hint: положить рядышком libproc*so (и указать туда LD_LIBRARY_PATH), TERM=linux и сделать симлинк /.terminfo->etc/terminfo), и оказалось, что ~75% idle.

    • Дополнительно доставляет тот факт, что глюкотормоза -- на ppc-espectre, в котором ТОЛЬКО У0632. А в ppc-ipp, где еще пара adc200, несколько коммутаторов и В06rr -- никаких проблем, всё летает...

    14.06.2013: идея:

    • а ведь u0632'шный собственный цикл ЧЕРЕДУЕТ
      1. проверку на готовность данных
      2. с select()'ом, по результатам которого делается HandleDriveletInput(), вычитывающий ОДИН пакет; и всё это с таймаутом 100ms.
    • И -- не получается ли, что оно вычитывает запросы не чаще, чем 10 раз в секунду?

      Нет, по идее -- не должно: оно ж на следующем же цикле тут же увидит готовность сокета и вычитает еще.

      Да и непонятно, почему такая радикальная разница между поведением на двух контроллерах.

    • ВЕЧЕРОМ: а вот и да!!! После замены однократного вызова HandleDriveletInput() на цикл по
      while (check_fd_state(DRIVELET_INPUT_FD, O_RDONLY) > 0)
      проблема ушла -- всё стало реактивно!

      А до того наблюдалась дичайшая картина: после гроханья сервера драйвлет еще секунд 10 продолжал жить своей жизнью, черпая из stdin'а команды и ловя запуски (видно по отладочной печати).

      Т.е. -- надо в HandleDriveletInput() вставлять цикленье "пока есть что (до 30 раз)".

      Но всё равно неясно: во-первых, почему такое странное поведение (чего сразу несколькими подряд select()'ами не вытащит всё? Планировщик дурит? Процессор-то ~80% idle...), а во-вторых, почему на втором контроллере проблем нет...

    • (17.06.2013) поставил счётчик числа повторов вычитывания: характерные числа 32, 128, 160 запросов в пачке. Мда, преизрядно -- немудрено, что такое обрабатывается долго.

      Но почему вариант с "полным" select() тормозит, а с "подсматривающим" -- нет? Или scheduler ядра ведёт себя по разному при нулевом и не-нулевом таймауте? Несколькими часами позже: попробовал посмотреть в 2.4.18-5'шном fs/select.c::do_select() -- ничего не понял; вроде, на вид, какое-то отличие при __timeout==0 есть, но даст ли оно такой радикальный эффект -- неясно.

  • 18.09.2013: вводим "clientside handshake-таймауты" -- аналогично тем, что только что сделаны в cxlib'е.

    18.09.2013: а вот и нет, не надо! Ведь тут сразу после успешного connect()'а делается отправка конфига драйверу (т.е., безо всякого многоуровневого договаривания) и включается в работу HeartBeat() с периодом 5 минут.

  • 16.10.2013: еще глючок вылез: в магнитной системе в какой-то момент remdrv[4], глядящий на cangw-magsys (с которым имелись странности иного характера), вдруг перестал восстанавливать соединение.

    16.10.2013: подсмотр за его privrec'ом с помощью gdb показал, что:

    • fd=11, и при этом
    • is_ready=0, is_suffering=0, а
    • rd_fdhandle=-1, wr_fdhandle=-1
    • fd_uniqs[11]=8 !!!

    Т.е., дескриптор в cxscheduler'е зарегистрирован за другим devid. Возможно, ДРУГОЙ экземпляр remdrv сделал close(), но НЕ sl_del_fd(); хотя как бы такое возможно -- из кода неясно (что-то вызванное кривизной "ScheduleReconnect()'у передаётся fd"?).

    А еще --

    • rcn_tid=68 (НЕ -1!), но
    • tout_list[68].is_used=0, да и
    • used={{fds_bits = {-248, 8095, 0 x 30} -- это long'и, т.е. по 32 бита, следовательно 68-й бит сброшен.

    Как такое возможно -- тоже загадка загадочная.

    Подсмотр -- само по себе нетривиальное занятие, поскольку:

    • Объяснить gdb, что надо преобразовать 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'и с проверками (или, как минимум, отладочной руганью).

  • 21.10.2013: нынешний remdrv будет утекать память при завершении/рестарте. Ведь у него аллокирование privrec'а собственное, а освобождение отсутствует.

    Жить ему осталось недолго, но всё же...

    22.10.2013: сделано просто: добавлена remdrv_term_d() с safe_free().

CAN:
  • 08.06.2005: приступаем к созданию CAN-драйвера второго поколения. Он будет жить уже не в istc/, а в cx/src/programs/server/drivers/canbus/. И, в преддверии CXv4'шных "правильных" layer'ов будет сделан уже по той layer'ной модели. И состоять из ОТДЕЛЬНЫХ файлов.

    08.06.2005: директория создана.

    14.06.2005: структура также создана. Драйвер сейчас состоит из:

    • marcankoz_drv.c -- пустой файл, нужный лишь для системы сборки.
    • marcankoz_pre_lyr.h -- определение интерфейса (будущий cankoz_layer.h).
    • marcankoz_pre_lyr.c -- реализация интерфейса (будущий marcankoz_layer.c). Пока почти пуст.
    • canadc40_src.c -- драйвер CANADC40 (будущий canadc40_drv.c). Готов.
    • candac16_src.c -- аналогично для CANDAC16. Пока пуст.

    15.06.2005: сделал (точнее -- перевел из canadc40.c) и работу с очередью, включая таймауты. В т.ч. теперь ВСЕ "заинтересованные", включая do_send() функции возвращают коды QFE_nnn (но НЕ sendframe() -- та, как и положено, 0/-1).

    Кроме того, в marcankoz_fd_p() вставлена "интеллектуальная" обработка пакета 0xFF:

    1. При несовпадении присланного устройством кода с тем, что был заявлен драйвером, автоматически производится переход в состояние DRVS_OFFLINE с кодом CXRF_WRONG_DEV.
    2. При "причине", указывающей на сброс блока, автоматически очищается очередь и щелкается состоянием ->NOTREADY->OPERATING (что вызывает, во-первых, повтор всех запросов чтения/записи, а во-вторых -- автоматическое вычитывание каналов записи).

    16.06.2005: О! Так надо сделать, чтобы прямо NNNcankoz_add() посылал бы unicast-запрос 0xFF. Тогда, даже если блок будет добавляться уже потом, через большой интервал времени ПОСЛЕ инициализации порта, от него все равно будет приходить идентификационный пакет, по которому драйвер и станет запрашивать периодические измерения. А из init_b() этого делать совсем не надо.

    Вставил такую посылку, а RequestPeriodic() из canadc40_init_b() убрал.

    16.06.2005: интересный идеологический вопрос: как реализовывать команды, имеющие вид "1) Команда записи, без ответа; 2) Команда чтения (контрольная)". Просматриваются 2 варианта:

    1. "Простой": sendframe(WRITE); enqueue(READ, QFE_IF_NONEORFIRST);.
    2. "Кошерный": 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.
    • Всю обработку ioregs, кроме ->regs_rw() -- в самом layer'е.
    • Следовательно, то, что было в cankoz_ioregsinfo_t -- переезжает (в сокращенном виде) в kozdevinfo_t.
    • А указание наличия ioregs -- если параметр regs_base>=0.
    • Отдельный callback "ondevattrs()", с параметром "is_reset".

    Таким образом, мы унесем ВЕСЬ "стандартный" интеллект (0xFF и ioregs) в layer, но сами пакеты БУДУТ отдаваться _in()-callback'ам, так что желающие смогут производить нестандартные действия.

    Часом позже: перетряска произведена. Заодно отделил собственно отправку фрейма (ныне writeframe()) от API-вызова. Общее впечатление: все стало красивее и правильнее.

    07.07.2005: еще вчера начал наконец-то реально делать поддержку ioregs. Тезисы (ажно в блокнотике их сначала написал :-) таковы:

    1. Чтение inreg/wrreg: if(absent(READREGS)) enqueue(READREGS).
    2. По команде записи:
      1. reqd_val,mask=...
      2. if (!were_read) enq(READREGS,IF_ABSENT);
        else {
        if (enq_or_repl(WRITEREGS,ons)) enq(READREGS);
        }
    3. Собственно "отправка запроса на запись" -- отдельной функцией.
    4. При "реализации" запроса на запись (хоть через очередь -- 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.)

  • 16.06.2005: пара идеологически-филологических вещей общего характера:
    1. Переделать везде имя "info" в качестве (privrec_t*)privptr на что-нибудь.
    2. Переделать "iface" в качестве "layer interface" на что-нибудь -- а то конфликтует с "iface" в смысле /dev/NNN.

    09.09.2015: первое сделано повсеместно еще давным-давно (в основном me). Второе -- сейчас в v4'шных во всех "iface" заменено на lvmt.

  • 26.07.2005: (записано 04.08.2005) добавлен драйвер caniva. Проверен -- работает.

    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 штук старых вызовов.

    • Фиг -- по top'у различий вообще не видно, всё в пределах колебаний. Один блок потребляет ~26-32% CPU; два -- ~49-62%; а вот три уже почему-то непропорционально меньше -- не выше 85%, а чаще 77% и ниже, причём новый вариант жрёт больше!!!
    • Да, проверял ethereal'ом -- по сети гуляют пакеты "RCSt".
    • Чуть позже -- сравнение-то некорректное, поскольку "старый" вариант еще до-libremcxsd'шный. С другой стороны -- а чё это вдруг влияет так сильно, там ведь инфраструктура непринципиально поменялась?!
    • Еще чуть позже -- сравнено со "старым" вариантом на современной инфраструктуре. Вот теперь различия есть: один блок сразу берёт 51-67%, два -- 83-94%, а три вообще зашкаливают сразу (96% и idle=0%).

    Выводы:

    1. Один вызов возврата данных действительно эффективнее четырёх.
    2. Что-то с обновлённой инфраструктурой не то, надо разбираться.

      30.10.2013: давно разобрано -- множественные fdio_send()'ы тормозили, с lock'ингом проблема исчезла.

  • 04.08.2005: (12:48) есть желание иметь драйвер cpks8.

    04.08.2005 (13:09) прошло меньше получаса -- а уже написал. Точнее, практически скопировал с candac16, удалив работу с регистрами.

    Пока не проверено.

    31.01.2007: уже полтора года прошло, а сами-то блоки так в В1000 и не пошли -- ломает наших боймельштейнов там что-то переделывать. Так что -- отдал я все четыре блока Володе Реве, им понадобятся.

  • 06.09.2005: насчет candac16 -- сегодня выяснилось (см. записки в конце файла), что при попытке дать драйверу +10V он реально пишет -10V -- происходит целочисленное переполнение и закольцовывание (число -- в дополнительном коде).

    Явно надо ставить ограничение, чтобы не происходило таких уродских неприятностей.

    06.09.2005: вроде сделал -- число вгоняется в безопасный диапазон [-10000305,9999999].

    24.05.2006: давно проверено и работает -- "done".

  • COMMENT: 28.09.2005 Vyyasnilos', chto iz-za togo, chto v _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.

    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.

    Поправил -- теперь все окей...

  • 30.01.2006: йоу-йоу -- а ведь в marcankoz_fd_p() НЕ проверялось, какой именно ТИП пакета нам пришел, т.е. -- поле "приоритет".

    Даже если забыть об "общекорректности", можно считать, что нам просто везло, что никаких иных, помимо "reply" (7) не приходило. Не говоря уж о том, что в multi-host environment (например, при отладке) оно б точно сдурело.

    30.01.2006: вставил проверку, сразу после log_frame() -- т.е., прямо перед разбирательством, что за пакет.

  • 03.02.2006: возникла потребность (для ausacc/building3) иметь драйвер CGVI8.

    03.02.2006: посмотрел систему команд -- ну, блин! Есть как плюсы (удобства), так и минусы (неприятности).

    • Удобство: программирование собственно 8 каналов ГВИ -- в точности как у любого ЦАПа (CANDAC16,CPKS8).
    • Неудобство: там есть еще дополнительные каналы, mask+prescaler и limit, которые пишутся отдельными командами F0 и F1 (хотя mask+prescaler -- вместе), а читаются -- все вместе, по FE.

      Имеем ту же проблему, что с IS10, где команда "pa" скопом вычитывала море разных параметров, писимых (пишимых?) разными командами, из-за чего мог выходить "сбой синхронизации выдачи подтверждений" (обсуждение см. за 27-02-2005).

    • Вопрос: поскольку, в отличие от ГВИ-8 (G0401) у CGVI8 выдаваемый интервал зависит не только от кода, а еще от прескалера, то -- как драйверу воспринимать числа для каналов 0-7? Как 16-битовые коды? Или как "сотни наносекунд"? В разных случаях может быть полезно разное...
    • И -- регистр "маски" должен позволять как побитовую запись (один бит -- один канал), так и оптовую (все 8 бит одним каналом). В точности как работа с регистрами.
    • И -- нужет отдельный командный канал "принудительный старт" (отображается на команду F7).

    09.02.2006: насчет дилеммы "16-битные коды или сотни наносекунд" -- и то, и другое! Т.е., каналы 0-7 -- коды, а 8-15 -- сотни наносекунд. Просто драйвер перед отправкой в устройство в случае прихода через каналы 8-15 будет пересчитывать наносекунды в код с учетом прескалера.

    10.02.2006: итак, карта каналов:

    • 0-7 -- коды;
    • 8-15 -- сотни наносекунд;
    • 16-23 -- побитовый доступ к маске;
    • 24 -- маска оптом;
    • 25 -- принудительный старт;
    • 26 -- прескалер;
    • 27 -- база;
    • 28-45 -- регистры.

    14.02.2006: изготовил начальную версию cgvi8_drv_i.h, где эта карта и определяется.

    15.02.2006: дополнил карту определениями регистровых каналов, на основе свежевведенного cankoz_common_drv_i.h.

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

    Хуже оказалось другое: эта "концепция" с "aliasing'ом" управляющих каналов-кодов на "100ns-каналы" -- это просто полная задница! Это СПЛОШНЫЕ НЕУДОБСТВА!!!

    • Во-первых, само то, что, теоретически, может придти запрос на запись в 100ns-канал в тот момент, когда значение prescaler'а еще не считано. Или -- сразу после записи в канал prescaler'а, когда еще не вернулся ответ. (А уж при записи в момент отсутствия связи -- тут о-о-очень много сценариев можно напридумать.)

      Во всех этих случаях будет выполняться некорректный перевод.

    • Во-вторых, чтобы при смене значения prescaler'а сразу же "менялись" значения 100ns-каналов (они ведь каналы ЗАПИСИ, сами по себе сервером не опрашиваются), приходится помнить значения кодов -- значения запоминаются в момент прихода пакетов. А еще -- помнится "факт" запомненности каждого значения, и в _rst() эти флаги сбрасываются. Мутотень в общем!
    • В результате код сильно забардачивается и ухудшается.
    • Что еще вылезет -- не возьмусь даже предполагать.

    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) не было на экране управления маской ГВИ, то с новым драйвером у них просто нифига не работало (маска после включения питания была нулевой).

    Напрашиваются две вещи:

    1. Возможность указывать желаемую маску прямо в auxinfo.

      Поскольку маска может быть любой, включая 0, то можно использовать такой приёмчик: значением-по-умолчанию сделать -1, а разрешенный диапазон -- 0...255, и "понимать" указанность маски по обнуленности битов вне 0-7.

    2. Возможно -- стоит также и "следить" за маской: засылать её в устройство в _rst@is_a_reset?

    Учитывая подлянскость системы команд -- придётся "следить" И за маской, И за прескалером (и указывать в 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(). И тут всё стало ясно:
    • В обычных-то случаях -- при отсылке через очередь -- ошибки отправки нефатальны, поскольку отправка будет повторена.
    • Но в cgvi_src.c для записи практически повсеместно используется именно send_frame().
    • А при загрузке режима драйверу скопом приходит толпа команд записи (да еще и не ему одному), так что крохотное FIFO из 10 пакетов мгновенно переполняется.

    Исправляем на q_enq_ons():

    1. send_wrmode_cmd().
      • Эффект вышел преинтереснейший -- сообщения о переполнении сыпаться перестали, зато глючить стали уже ДВА бита!
      • НО: ручная отправка пакета 0xFE (GETDEVSTAT) показала, что битики-то выставились, а проблема -- исключительно в том, что оно не добавляет в очередь эти GETDEVSTAT'ы, если они там уже есть (QFE_IF_NONEORFIRST; правильно -- раньше-то прямые посылки шли вперёд очереди); в результате оно читает только после первых 1-2 записей, а следующие проходят "молча".
      • Так что -- обе отправки чтения-после-записи переведены на QFE_ALWAYS, и проблема исчезла.
    2. Отправка данных в обычные каналы (DESC_WRITE_n_BASE+n).

    Использование очереди в любом из сих двух мест сразу убирает проблемы "незаписи" в этом месте (причём 2-е -- в том числе и проблемы в 1-м; загадка... -- видимо, чисто по вероятности).

    Резюме:

    1. Вообще надо избавляться от прямых посылок ВСЮДУ, за исключением пары вещей:
      1. Бродкасты -- в частности, 0xFF.
      2. Высылка DESC_STOP в методах _term_b().
    2. В v4 (при работе через sendqlib) схема будет чуток сложнее -- вместо принудительной установки пакета в очередь надо использовать SQ_REPLACE_NOTFIRST, и только если оный вернёт ==SQ_NOTFOUND, то добавлять последующее вычитывание.

      И, кстати, чтения-после-записи во ВСЕХ каналах надо делать так же -- не как сейчас

      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.
      1. Это, конечно, некрасиво.
      2. Хбз пока, где это может аукнуться.
      3. Реально непонятно, как же можно сделать 100%-корректно?
        1. Вот в нынешней модели очереди, поддерживаемой cankoz_pre_lyr'ом -- как? Есть какие-нибудь варианты, как/когда/куда надо запоминать текущее-известное и запрошенное-записать значения, и как какие флаги выставлять/сбрасывать? (Что, диаграмму хождения запросов и пакетов нарисовать?)
        2. На основе sendqlib:
          1. Частично жизнь упростится наличием REPLACE_NOTFIRST, но в случаях, когда первый запрос уже всё равно ушел в линию -- оно нас не спасёт;
          2. наверное, еще поможет наличие on_send.
          Но полного понимания -- пока нет.
      4. Теперь оно стало аналогично работе со вторым членом WRITE_MODE -- маски: там тоже биты в req_m_msk только взводятся, но никогда не сбрасываются (кроме как в _rst()).
      5. А ведь работа с ioregs сделана совершенно аналогично, со сбросом req_o_mask=0 после отправки пакета, и оно так же делается через прямую отправку с последующим чтением по IFNONEORFIRST, и при переводе на q_enq_ons() там вылезет такое же "залипание старого значения". Чё, тоже убирать обнуление req_o_mask после отправки?
      6. Если наконец-то сподобимся сделать унифицированный "template" от 14-01-2011 для таких случаев -- надо будет крепко продумать его поведение, а то ошибка вылезет во всех местах сразу (и, возможно, по-разному).

    Резюме:

    1. Ну и гадкая ж система команд, когда ОДНИМ кодом пишется ДВЕ разные сущности!!!
    2. Оказывается, у нас до сих пор используется кривоватая модель работы с ioregs. Именно оно было самой главной неприятностью еще лет 6-7 назад, и, похоже, так нормального решения и не сделано. А я-то считал, что эта гадкая проблема давно позади... :-(

    17.04.2012: к вопросу о "работе с ioregs" -- да, проблема вылезла, безо всякого "q_enq_ons()". См. в разделе о баге в выходном регистре.

    11.12.2012: краткая история добавления поддержки быковского варианта CGVI8 (код 32):

    • Что разные коды -- ладно, при регистрации указывается -1, а в cgvi8_rst() проверяется "либо/либо" (см. за 02-10-2012).
    • Оказалось, что регистров (F8/F9) у него нет, и команды такие он не поддерживает.
    • ...возникла мысль сделать возможность переключения поддержки регистров на ходу, для чего даже введён метод ->set_regs_base().
    • Однако РЕАЛИЗАЦИЯ его совсем нетривиальна -- при переходе от "есть регистры" к "нету" надо также вычистить из очереди все пакеты F8/F9 (долженствующие туда попасть сразу при запуске драйвера, еще до ответа на FF), что в нынешней модели (да и вообще) несколько замутно.
    • Так что была даже выработана стратегия -- при таком переключении делать "рестарт" устройству (->NOTREADY->OPERATING) и полный сброс очереди.
    • Но в конце концов удалось уговорить Быкова вставить нулёвую поддержку 0xF8 (в ответ отдаются значения 0), так что проблема снята (пока) и результат попыток её обхода удалён.
  • 08.02.2006: еще 16-06-2005 на предзащите Славы Середнякова услышал, что его программа работы с источниками умеет переводить CANADC40 в одноканальный осциллографический режим, для измерения пульсаций.

    А сегодня Федя Еманов заявил, что такое же нужно и у нас, в тех же самых целях. Вопрос -- а как?

    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), но лишь частично.

  • 14.02.2006: а ведь надо и для CAN-драйверов иметь _drv_i.h-файлы, и чтобы все определения каналов были ТАМ, а не в коде драйверов (как всякие nnn_BASE)!

    (Тогда и проблема CAC208 никогда бы не возникла -- ибо там везде бы стояли оффсеты каналов.)

    15.02.2006: поскольку в определениях каналов ПОЧТИ ВСЕГДА участвуют смещения регистров (18-канальный блок), то нужны ОБЩИЕ определения для них.

    Вот и создал include/drv_i/cankoz_common_drv_i.h, включабельный прочими c*_drv_i.h-файлами. Содержимое таково:

        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
    
    т.е. то, что раньше было "*8_BASE", теперь стало "*8_BOFS" -- чтобы не возникало путаницы, поскольку это все оффсеты. Плюс -- добавлено стандартное определение количества каналов.

    Далее (завершалось сие уже 16.02.2006):

    • Из marcankoz_pre_lyr.c удалены старые определения REGS_*, и все переведено на новые.
    • Для смещений многоканальных сегментов принят суффикс "_N_BASE".

      А для "размеров" таких сегментов -- суффикс "_N_COUNT" (хоть он по большей части и не используется).

    • Создан canadc40_drv_i.h и canadc40_src.c переведен на него. (Заодно в ReturnChanGroup() к номеру, полученному от устройства, добавляется CANADC40_CHAN_READ_N_BASE.)
    • Создан candac16_drv_i.h и candac16_src.c переведен на него. (И добавлено вычитание/прибавление CANDAC16_CHAN_WRITE_N_BASE при отправке/получении пакета.)
    • Создан cac208_drv_i.h и cac208_src.c переведен на него. (И с ним проделаны оптом все те махинации, что с can{adc40,dac16}_src.c.)
    • Создан cpks8_drv_i.h и cpks8_src.c переведен на него.

      Елы -- мало того, что самый простой и короткий драйвер, так еще и, пожалуй, самый короткий и простой из возможных! (А, не -- может быть еще драйвер на ОДИН-единственный канал :-)

    • Создан caniva_drv_i.h и caniva_src.c переведен на него. Это просто полный писец! Самый муторный в этом смысле драйвер!
    • Для корректности все определения *_main_info[] переведены на константы *_N_COUNT.
    • А за компанию, уж для полноты корректности, введен макрос
      #define CANKOZ_REGS_MAIN_INFO {8, 1}, {8, 0}, {1, 1}, {1, 0}
      и везде старая цепочка цифр заменена на него.

    Короче -- считаем, что "done".

  • 16.02.2006: возникла потребность в драйвере (CGVI8) знать значения hw_version и sw_version устройства: в CGVI8 фича "база" реализована только при hw_ver>=1&&sw_ver>=5.

    (Конечно, драйвер может и сам реагировать на такие пакеты и запоминать, но -- зачем?)

    Так что -- потребно, 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 кода.

  • 19.02.2006: очевидно -- надобно иметь готовую функцию "erase_and_send_next()", которой бы прямо напрямую передавались байты удаляемого пакета, vararg'ами (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!).

    И еще две вещи:

    1. Сейчас метод явным образом проверяет исключительно на пакеты с prio=CANKOZ_PRIO_UNICAST. С другой стороны -- никакие иные вроде бы и не надо поддерживать.
    2. Там сейчас в обязательном порядке надобно указывать пакет целиком, а... это предполагает, что мы всегда знаем содержимое пакетов... В принципе-то это вроде бы пока верно -- ведь НЕ знать мы можем только содержимое пакетов ЗАПИСИ, а они отсылаются напрямую, минуя очередь.

      НО: на будущее, возможно, стоит расширить семантику 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: да, а вот сегодня уже начало требоваться "...НАЧИНАЮЩИЕСЯ с указанных данных" -- для пановского блока: у него там:

    1. На пакеты ЗАПИСИ предусмотрены автоматические ответы (что правильно), а при этом уже НЕ надо включать в сравнение числа-значения из пакета, используя ТОЛЬКО начальные байты "расширители кода команды".
    2. (Это уже следствие, так что не влияет, но для протокола) пакет с одним и тем же дескриптором может делать изрядно разные вещи -- конкретное действие определяется последующими байтами.

    Так что -- сделал. Состояло из 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() -- там тоже поправил.

  • 27.02.2006: а может, сделать и методы ОТСЫЛКИ и ПОСТАНОВКИ В ОЧЕРЕДЬ тоже с va-параметрами, чтоб не нужно было заполнять структуру, а?

    21.12.2011@Снежинск-каземат-11: в v4'шном оно так сделано, а тут уже едва ли будет. Так что -- "obsolete".

  • 27.03.2006: вылезла некая проблема -- оказывается, при нынешней инфраструктуре реагирования на пакеты 0xFF драйверы НЕ МОГУТ корректно выполнять те же действия, что делает marcankoz_pre_lyr с регистрами.

    27.03.2006: А все оттого, как делается по is_a_reset "перезапрос" данных (точнее, как сервер заставляется перезапросить): это "лерганье" статуса ->NOTREADY->OPERATING. Дело в том, что сброс своих флагов оно делает уже определив проблему, но ДО дерганья статуса, а вот _rst() вызывает ПОСЛЕ.

    Так что _rst() не может спокойно брать да сбрасывать у себя какие-нибудь флаги -- просто потому, что запросы, потенциально приводящие к чтению каналов, к которым относятся эти флаги, уже могли быть отданы сервером. А надо дергаться ДО...

    31.03.2006: ну и что делать будем? Из "простых" просматриваются 2 варианта:

    1. Переставить вызов _rst() ПЕРЕД дерганьем статуса.
    2. Вставить вызов _rst() ВНУТРЬ -- между NOTREADY и OPERATING. (Но тогда придется иметь ДВА вызова -- при is_a_reset и при else.)

    Пока склоняюсь ко 2-му, но надо отследить все "пути данных"...

    03.04.2006: вообще -- какое, нафиг, "отследить"?! По самой очевиднейшей логике -- надо именно просто вставить ПЕРЕД дерганьем, да и все.

    Короче -- переставил, а дальше "будем посмотреть", когда допишем поддержку регистра маски у cgvi8.

    05.05.2009: поскольку сделано еще три года назад (!!!), и вроде должно было заработать (хотя хбз, вроде маску у cgvi8 так и не доделали), то помечаем как "done".

    25.06.2010: дя-дя-дя!!! Вот только сегодня вылез другой прикол:

    • "правильные" _rst() теперь посылают DESC_MULTICHAN не send_frame()'ом, а enq_ons()'ом.
    • НО: ведь _rst() вызывается ПЕРЕД действиями, предпринимаемыми pre_lyr'ом, так что он пакет в очеред вставит -- а pre_lyr её тут же и грохает!

    Переставил очистку очереди при is_a_reset ДО вызова _rst(), так что у нас теперь такая цепочка:

    1. cankoz_q_clear()
    2. вызов rst()
    3. забывание регистров и дерганье статуса.

    И в cankoz_lyr_common.c::cankoz_fd_p() сделал такое же изменение.

  • 08.06.2006: еще о таймаутах -- сегодня возникла "забавная" ситуация:
    1. Запускаем сервер, который программирует CANADC40 на измерения с высылкой в линию.
    2. Отрываем линию от компьютера.
    3. Дергаем питание блоков (не всех! чтобы пакеты в линии бегали).
    4. Втыкаем линию обратно в компьютер.
    5. Вуаля -- данные на каналы тех "передернутых" ADC не приходят! Ведь сервер-то считает, что он всех запрограммировал, и как померяют -- так пришлют. А не шлют -- ну, наверное, линия вынута, чего суетиться-то.

    Вопрос -- и что делать?

    (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: а ведь всё ОЧЕНЬ просто, с учётом

    1. пакет 0xFF теперь и так шлётся из очереди;

      13.02.2011: почему-то тогда не записал, пишу сейчас: и "метроном" в xcac208/xceac124 также сделан -- поле privrec_t.rd_rcvd плюс функции _alv()@60секунд, при rd_rcvd==0 посылающие 0xFF; а уж _rst() по ЛЮБОМУ 0xFF вызывает SendMULTICHAN().

    2. в прочих аналогичных местах -- cxlib и cm5307_drv/cangw_drv -- используется просто метроном, который всё сильно упрощает (по сравнению с отсчетом таймаута от последнего прихода).

    Итого: просто заводим метроном с периодом 60 секунд, и при определении зависания -- ставим в очередь пакет 0xFF с how=QFE_IF_ABSENT.

    И еще, насчёт определения зависания: нафиг не нужны ни gettimeofday(), ни GetCurrentCycle(). Всё намного проще -- достаточно иметь булевский флажок, который по приходу данных от АЦП взводить в 1, а в конце обработчика метронома сбрасывать в 0. Вот автоматом и получится ровно то, что нужно.

    Сделано по вышеприведённому проекту в xcac208_drv.c и xceac124_drv.c. Традиционно -- надо проверять.

    07.03.2013: традиционно -- не первый год работает, "done".

  • 26.12.2006: вытащил из canbus/Makefile список поддерживаемых устройств в отдельный файл ListOfCanDrivers.mk, где уставляется переменная CANDRIVERSSOURCES.

    Смысл -- чтобы при сборке canserver'а (для CANGW) список драйверов брался сразу из этого файла.

  • 28.12.2006: идейка: а ведь у нас иногда бывает, что CAN-АЦП меряет один-единственный канал (как на лебедином селекторе камер на накопителе).

    Так вот: ведь в таком случае можно включать не многоканальные измерения (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).

  • 04.01.2007: IMHO, надо бы избавляться от прямых посылок и переходить на использование q_enq_ons().

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

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

  • 18.01.2007: почему-то в marcankoz_add() отсутствовала проверка на занятость запрашиваемого устройства.

    18.01.2007: вставил, точную копию из c4lcankoz_add().

  • 09.02.2007: после создания поддержки для CANGW стало очевидно, что надо переходить на унифицированную архитектуру -- чтобы *cankoz_pre_lyr реализовывался одним и тем же файлом, независимым от конкретного драйвера CAN-интерфейса, а специфику этого драйвера отражать в некоем файле, который бы подключался к собственно _pre_lyr'у.

    09.02.2007: сделано, детали таковы:

    • Сами файлы marcankoz_pre_lyr.[ch] переименованы в просто cankoz_pre_lyr.[ch], и во всех драйверах префиксы "Mar" и "mar" удалены.
    • В cankoz_pre_lyr.c делается #include CANKOZ_HAL_H, а уж указать CPPFLAGS+=-DCANKOZ_HAL_H='"???cankoz_hal.h"' -- задача Makefile'а.
    • Файл же ???cankoz_hal.h содержит три функции:
      1. canhal_open_and_setup_device() -- теперь она именно только открывает устройство и уставляет baud rate, а уж регистрацией файлового дескриптора и отправкой бродкастного 0xFF занимается cankoz_add().
      2. canhal_writeframe() -- почти не отличается от того, что было раньше.
      3. canhal_readframe() -- сюда из cankoz_fd_p() вынесен read() с проверкой.

    В общем -- стало очень красиво: работа с адаптером -- отдельно от собственно мозгов, заведующих очередью.

    10.02.2007: и c4lcankoz_hal.h также сделал. Проверил -- работает.

  • 09.02.2007: возникла потребность сготовить драйвер для CAN-PCSC8 by Вадим Забродин.

    16.02.2011: давно очевидно, что этот проект давным-давно прочно и основательно накрылся -- из-за ухода Вадима. Так что 'obsolete".

  • 21.04.2008: возникла потребность в драйвере CDAC20 (для проекта tantal/ВЧ300 в блоке Кузнецова).

    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: и вообще выкошена нафиг, чтоб не маячила.

    1. Там почему-то (осталось от 208/124?) при вычислении this_targ используется kozdac_val_to_c16(), вместо вроде бы положенной cdac20_val_to_daccode().
    2. И формат данных там тоже никак не учтён. Хотя у CDAC20 в таблице по 6 байт на каждый инкремент, а не по 4, как у прочих.

    08.09.2011: тэк-с, а поддержку калибровки (CALIBRATE) и цифровой коррекции (NUMCORR), присутствующих в этом девайсе, мы делать будем?

    Там, кстати, будет иметь место некоторая сложность: канал NUMCORR_V расположен в config-блоке, объявленном как w20 -- так что СЕРВЕР потребует его чтения только один раз. А надо б как-то вычитывать АКТУАЛЬНОЕ значение.

    28.02.2012: да, делаем.

    Для начала -- везде (глобально!) меняем фиг знает как родившийся термин "NUMCORR" на "DIGCORR".

    • Реализовано управление цифровой коррекцией.
    • Текущее её значение -- халтурновато, ибо W-канал, само пока не вычитывается.
    • Команда отправки калибровки сделана.
    • Также добавлен канал XCDAC20_CHAN_AUTOCALB_PERIOD (на место RESERVED19). В него указывается период, если он 0 -- то "отключено" (отдельного канала на вкл/выкл нету...). Реализовано оно прямо в _hbt().

    Стандартно -- осталось проверить :-)

    28.02.2012: проверяем.

    • Для начала был скотский баг -- оно во всех отправляемых пакетах забывало проставить prio=CANKOZ_PRIO_UNICAST и слало prio=0.

      Всё из-за дурной модели с необходимостью заполнять item вместо нормального набора отправляющих функций (которые, кстати, ВСЕГДА шлют UNICAST -- в очереди остальное бессмысленно).

    • Также была дурь с не-чтением этой четвёрки каналов -- всё потому, что они расположены в блоке CONFIG, а оттуда все чтения кэшируются отдельным if()'ом в начале. Пришлось переставить эти 4 вперёд.

      Опять же -- ведь это уже вовсе НЕ config-каналы в том смысле, что остальные (adc_mode, do_reset, do_tab*), а вполне нормальные. Но на них иного места нету. Всё-таки надо расширять с 20:30 до 50:50 (см. за 06-09-2011) -- тогда эта проблема уйдёт.

    • Авто-калибровка -- работает, хотя и с погрешностью около 0.1с, что приемлемо.

    02.03.2012: переделываем по схеме "60:40".

    • Делаем это на основе kozdev_common_drv_i.h. Почти всё теперешнее содержимое _drv_i.h-файла -- ссылки вида XCDAC20_CHAN_zzz=KOZDEV_CHAN_zzz. Неприятный процесс, конечно.
    • Введён отдельный канал XCDAC20_CHAN_AUTOCALB_ONOFF, "период" переименован в XCDAC20_CHAN_AUTOCALB_SECS, а команда калибровки -- в XCDAC20_CHAN_DO_CALB_DAC.
    • Канал XCDAC20_CHAN_DIGCORR_FACTOR переехал в RD-область и теперь читается сам по себе.
    • Заведены и каналы HW_VER и SW_VER.
    • Поскольку добавлена туча дырок, то в конец switch()'а добавлен по умолчанию возврат 0/CXRF_UNSUPPORTED.

    05.03.2012: проверяем.

    • Поскольку каналы АЦП предполагалось, что будут возвращаться автоматом, то в основном switch()'е были пропущены (со специальным комментарием), и теперь стали возвращаться как 0/UNSUPPORTED.

      Так что вставлена отдельная альтернатива с "пустой" реакцией.

    • Каналы HW_VER/SW_VER были забыты -- теперь реакция на них вставлена.
    • В xcdac20_in() была недопеределана реакция на DESC_DIGCORR_STAT -- оно пыталось возвращать при помощи ReturnCtlCh(); пофиксено.
    • Обработка всех не-MODE (ex-(не-)CONFIG) каналов переехала обратно вниз, в после блока MODE.

    Итого -- тот функционал доделан.

    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.

  • 15.10.2008: неудобно, что cankoz_pre_lyr.c::cankoz_fd_p() при получении пакета GETDEVATTRS не упоминает magicid устройства, к которому этот пакет относится, а всегда делает логгинг как MAGIC_NOT_IN_DRIVER.

    15.10.2008: есть два варианта решения:

    1. Всегда указывать dp->magicid в выдаче.
    2. При dp->magicid>0 использовать его для передачи DoDriverLog().

      И -- это надо делать с учетом предстоящего перехода на layer'ы.

    16.10.2008: сделал по 2-му варианту.

  • 06.04.2009: понадобилось изготовить поддержку для доступа к CAN через интерфейс SocketCAN -- в первую очередь для Tews'овского CAN-мезанина в cPCI.

    08.04.2009: задача состояла из двух частей:

    1. Собственно cankoz_hal.h-файл-адаптер.
    2. Поскольку настоящих layer'ов пока нету, потребна инфраструктура для сборки единого _drv.so-модуля, содержащего все стандартные CAN-драйверы.

    Сделано:

    • Размещено это покамест в директории work/liu/drivers/sktcan/, пока не включенной в Makefile уровня выше (поскольку требует ядра >=2.6.25, да и еще и /usr/include/linux/ соответствующих).
    • Файл-адаптер назван cankoz_hal.h, и его содержимое сделано в соответствии с описанием в http://svn.berlios.de/svnroot/repos/socketcan/trunk/kernel/2.6/Documentation/networking/can.txt. Единственное -- пока что скорость 125 не ставит (её можно настроить через /sys/).

      А сам драйвер-пустышка -- соответственно, sktcankoz_drv.c.

    • Инфраструктура очевидным образом симлинкует исходники драйверов и cankoz_pre_lyr.c из основного дерева. Несложно, конечно, но слегка муторно.

    09.04.2009: попробовали на foxy.inp.nsk.su с Tews 1498:032a -- работает!

  • 09.04.2009: понадобилось иметь драйвер CEAC124, для испытаний свежеизготовленного sktcankoz_hal.h.

    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: а вот наконец-то понадобилось уметь уставлять скорость работы CAN-линии -- для электронной сварки, где используется 250кбод. А возможности это сделать -- тю-тю...

    28.05.2009: да, реализовано -- пока скорее в виде хака:

    • Идея: указывать скорость работы (точнее, её номер -- 0(def):125, 1:250, 2:500, 3:1M) третьим параметром в 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: несколько замечаний по опыту работы/использования:

    1. Указывать ",1" надо не первому, а ВСЕМ драйверам, использующим эту линию, поскольку неизвестно, какой из них окажется первым -- далеко не факт, что первый по конфиг-списку: в отличие от локальных в-сервере, тут еще сеть играет роль, плюс это идет не в один пакет, а в два (1 -- запуск драйвера, 2 -- инициализация).
    2. В отличие от в-сервере, в коробочке линия ОТКРЫВАЕТСЯ только при первом использовании, а при следующих запусках сервера -- уже нет, так что скан линии делается только при первом запуске сервера, а при дальнейших -- нет.
    3. И вообще -- эта модель с "адаптивным изменением logmask" замечательно умудряется терять кучу сообщений в ситуации, когда уже надо что-то вякнуть наверх, а команда SETDBG еще не успела придти... :-(

    По теме же -- да, работает, так что "done".

  • 02.06.2009: по-хорошему НЕ надо передавать драйверам пакеты 0xFF с reason=3 (who are here). Просто потому, что драйверы в _rst() выполняют программирование устройства, а reason=3 -- после реализации "консольного интерфейса в коробочках" -- может быть и просто из-за команды "canlist".

    Да и из общих соображений: ведь "who are here" -- идеологически совсем не то же самое, что все остальные коды, это именно скан шины, а не результат запроса от драйвера или оживания блока, так что драйверу этот пакет видеть совершенно незачем.

    02.06.2009: да, сделал -- при CANKOZ_IAMR_WHOAREHERE метод _rst() НЕ вызывается.

    Теперь надо смотреть на последствия -- не вылезет ли где.

    02.12.2010: да вроде за полтора года нигде не аукнулось. Так что -- "done".

  • 11.08.2009: для разбирательства с cPCI-CAN/SocketCAN очень захотелось иметь тестовые утилиты -- монитор и посылальщик пакетов.

    12.08.2009: за эти два дня сделал.

    • Готовые утилиты называются candump_HAL и cansend_HAL, где HAL -- это имя HAL-модуля (mar, c4l, skt).
    • Собственно "мозги" расположены в файлах candump_common.c и cansend_common.c, плюс инкапсуляция работы с интерфейсом -- в canutil_common.[ch].
    • Файлы candump_HAL.c и cansend_HAL.c присутствуют в каждой CAN-директории -- это пустышки, необходимые для правильной работы Make.
    • В директории gw/ppc_canserver/ и sktcan/ файлы can*_common.[ch] симлинкуются, плюс делается вся надлежащая алхимия. ДА, ОХРЕНЕТЬ ТАМ МОЖНО!!!
    • Обеим программам первым параметром обязательно является номер CAN-линии, который можно указывать либо как N (0, 1, 2, ...), так и как canN (can0, can1, can2, ...). Скорость можно указывать через ключ -bSPEED, где SPEED -- 125, 250, 500 или 1000.
    • Представление данных выбрано так, чтобы быть одинаковым -- то, что печатает candump, можно подавать на вход cansend'у. Пакет выглядит как
      ID:b0,b1,b2,...bN
      либо
      kKID/PRIO[.RESV]:b0,b1,b2,...bN
      т.е. --
      1. идентификатор от данных отделяется двоеточием;
      2. данные идут через запятую;
      3. идентификатор может быть либо готовым числом, либо префиксироваться буквой 'k', за которой идёт адрес, потом через '/' "приоритет" (цифра, плюс вместо 5,6,7 -- буквы b,u,r), затем, опционально, через '.' -- цифра 0-3 "резерв".
    • candump печатает на stdout все принимаемые пакеты.
      • Ключ -cCOUNT позволяет ограничить их количество.
      • Печать времен прихода пакетов: -t -- СЕКУНДЫ.МИКРОСЕКУНДЫ считая от первого; -T -- в ISO8601. Все -- с префиксом "@".
      • Ключи -x и -X переключают на шестнадцатиричную выдачу данных и идентификаторов, соответственно. С префиксами "0x".
      • Ключ -k переключает на печать идентификаторов в kozak-формате.
      • Никакой ФИЛЬТРАЦИИ НЕ ПРЕДУСМОТРЕНО -- предполагается, что можно фильтровать через egrep (_IOLBF-буферизация stdout'а там делается).
    • cansend:
      • Отправляет пакеты, указанные в argv[] -- идя от optind до argc. Формат указания пакетов -- вышеописанный.
      • Если в качестве "пакета" указано "-", то он читает stdin -- по одному пакету на строку.
      • Для лучшей поддержки такой файловости: если спецификация начинается с '@' -- то пометка времени скипается; пустые строки, а также начинающиеся с '#' -- игнорируются.
    • Во всех HAL-модулях пришлось ввести дополнительную функцию canhal_writeraw(), принимающую на вход готовый ID -- а то canhal_writeframe() берет отдельно kid,prio.

    Некоторые выводы по опыту создания этих утилит:

    • Надо HAL-модули делать УНИВЕРСАЛЬНЫМИ, а не привязанными к API сервера. Т.е., как-то отделить печать отладочной информации.
    • Совсем-то по-хорошему -- это должно быть ЕДИНОЙ утилитой, которая могла бы одновременно и слать пакеты, и принимать их, a-la NetCat. Просто потому, что только SocketCAN позволяет нескольким программам работать с одной линией, а Marathon и CAN4Linux позволяют только одной.

      Но это слишком муторно, так что пока будем обходиться парой утилит -- поскольку у каждой карточки по 2 линии, то можно вешать candump на 0-ю, а cansend на 1-ю. Собственно, сейчас так и делал. (А уж если ну совсем-совсем припрёт -- ну тогда можно и подумать об универсальной утилите "canmon_HAL".)

    • И -- было бы очень полезно иметь стандартный API для "CAN-монитора" как-то прямо в сервере, чтобы некая программа могла бы обращаться к CAN-линии по сети и независимо от типа используемого HAL'а.

      В принципе -- это очень похоже на то, что требовал Тарарышкин&Co. для ТНК.

      И как сие можно сделать? Нечто похожее на механизм для получения доступа к логгингу всех пакетов -- специальным как-бы-драйвером (без аппаратуры). И -- 1) а не понадобится ли в сервере иметь директиву "загрузи такой-то просто-модуль"; 2) а как это будет работать с CAN-сервером в КОРОБОЧКЕ?

    • Нынешняя система сборки -- конкретнейше достала!

    19.10.2009@ТНК: в дополнение к cansend+candump создан также canmon -- необходимый для cangw, где только один процесс может держать CAN-линию открытой.

    Команда ":[msecs]" работает как "ждать [некоторое время] принимаемые пакеты". Когда пакеты-для-отправки в командной строке указаны, оно работает как cansend; когда нет -- неявно добавляет команду ":", эффективно работая как candump.

    Фактически, canmon обладает ВСЕМИ фичами candump+cansend, и может заменить обоих.

    20.10.2009@ТНК:

    • Добавлено дополнительное декодирование пакетов 0xFF, если находимся в режиме -k; результат печатается через '#'. Логика декодирования скопирована из cankoz_pre_lyr.c.
    • Также сие скопировано и в candump_common.c.
    • Для этого всего devtypes[] и GETDEVATTRS_reasons[] перемещены из cankoz_pre_lyr.c в свежесозданный kozak_strdb.c.
    • Дополнительно, len и data в cankoz_pre_lyr.c переименованы в can_len и can_data -- для унификации.

    21.10.2009@ТНК:

    • kozak_strdb.h: поменял "CAC208" на "CAC208/CEAC208".
    • Перетащил все CANKOZ_-enum'ы из cankoz_pre_lyr.h в новый cankoz_numbers.h, доступный для can*_common.
    • Переименовал kozak_strdb.h в cankoz_strdb.h.
    P.S. Также в эти два дня было море "веселухи", чтоб эти kozak_/cankoz_*.h правильно симлинковались в gw/ppc_canserver/ и в liu/drivers/sktcan/.

    22.10.2009@ТНК: надо чтобы canutil_common.c::CanutilOpenDev() КОРРЕКТНО ПРОВЕРЯЛ бы правильность указания CAN-линии, а то сейчас оно просто берется за 0. Короче -- надо atoi() заменить на strtol() с проверкой.

    04.11.2009: да, сделал.

    16.02.2011@Снежинск-каземат-11: оказывается, canmon НЕ воспринимал с stdin'а команду :МИЛЛИСЕКУНДЫ -- просто не стояла проверка (а оно потребовалось Панову для игрищ с выводом на восьмисегментниках УБСов всяких надписей).

    Исправил.

  • 22.10.2009@ТНК: стоит сделать чтобы canserver отпускал бы /dev/can* когда последний драйвер отпускает её.

    (Селивановский 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(),
    • но тут пришлось бы делать иначе: именно запускать таймаут (вместо close() линии на месте), чтобы close() сделалось в нём.
    • Но, поскольку в CANGW (точнее, в can4linux) доступ к линии монопольный, то нельзя просто "разрегистрировать дескриптор у cxscheduler'а, а через секунду сделать ему close()".

      Вместо этого придётся помнить о "почти-закрытости" линии, и в случае прихода запроса на её открытие отменять закрытие.

    Очень уж это всё хлопотно -- игра не стоит свеч, выигрыш (сейчас?) скорее академического характера. Так что ничего предпринимать не будем -- фиг с ним, пусть один-два девайса продолжают сыпать в линию.

  • 24.11.2009: как сказал Козак, ВСЕ его блоки, кроме CANADC40, дают достаточную точность уже на кванте 20мс, и при его увеличении шумы уже не уменьшаются. БОльшие же кванты нужны только для бОльшего времени усреднения.

    Так что, свободно можно везде ставить по умолчанию квант 4 (20мс) вместо нынешнего 5 (40мс). Что и сделано в имеющихся драйверах.

  • 07.12.2009: надо б всё-таки уметь ловить ошибки CAN-линии -- хотя бы для того, чтобы их печатать. И SocketCAN, и can4linux умеют при bus-off'е генерить т.н. "error frame" (марафонский candrv -- нет, только сигнал шлет, даун), и задача *_hal.h'а -- уметь с этим работать.

    07.12.2009: highlights:

    • Перво-наперво, надо при открытии линии разрешать отдачу этих "error frames".
    • Затем, HAL-модули должны быть обучены их понимать, и при получении нехай canhal_readframe() возвращают некое отрицательное значение. А уж конкретным отрицательным числом и будет кодироваться причина ошибки. Например, -2 -- bus-off. (Естественно, -1 не трогаем.)
    • И, наконец, cankoz_pre_lyr.c и компашка *common*.[ch] должны быть обучены обработке этих кодов -- выдаче диагностических сообщений.

    Следствие: очевидно, что протокольчик "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 оно бы и не пыталось заниматься лишней активностью.

    Тут не всё так гладко, есть сложности:

    1. Надо еще отдельно проверить, с какими низкоуровневыми драйверами это работает -- т.е., кто поддерживает select() на POLLOUT.
    2. Информация о "предыдущем" результате записи в N-тый порт (last_wr_r) есть только у "юзера", у *canhal.h же её нет, и тому остаётся только ВСЕГДА перед write() делать и check_fd_state().
    3. Теоретически, можно было бы вообще использовать такую "готовность на запись" регулярным образом, в стиле RegisterWrFD() (как это делается в remdrv и fdiolib), но... в общем, тут всё слабопонятно.
    4. Может, правда проще тупо понаставить check_fd_state() во все canhal_send_frame()'ы, чьи подстилающие драйверы это поддерживают, да и всё?
  • 13.05.2010: обнаружилась потенциальная проблема: после ошибки отправки пакета в линию драйвер больше НЕ БУДЕТ пытаться отправить пакет снова и снова через 100мс.

    13.05.2010: итак:

    1. История:
      • При разборках с плохо-работающей линией заинтересовался -- а почему это в debug.log сообщения "write=-1: No buffer space available" бывают только по 1 штуке на драйвер, хотя, казалось бы, должны б лезть по 10 раз в секунду при перепосылках.
      • Вроде бы никаких проверок "мы это уже логгили, больше не надо" тут пока нет (в отличие от v4'шного layer'а).
      • Разобрался -- в do_sendnext() таймаут на 100ms уставляется только при УСПЕШНОЙ отправке, а при r!=0 она возвращает QFE_ERROR и до уставления не доходит.

        Интересно, когда и ПОЧЕМУ было сделано именно так?

    2. Положительная сторона: собственно -- отсутствие заталпливания логов одинаковыми сообщениями об ошибке -- несомненный плюс.
    3. Потенциальная проблема: получается -- после ошибки отправки пакета не будет НИЧЕГО, что бы протолкнуло очередь:
      1. Авто-пере-посылка -- отключена.
      2. Добавление нового пакета в очередь (читай -- запрос на канал) -- нет, оно пытается отправить только при постановке пакета в пустую очередь.
      Остаются только
      1. erase_and_send_next: но он вызывается только как реакция на приход некоего пакета. А если связь оборвалась -- то будут ли вообще приходить пакеты? (Да-да-да, вот тут-то как раз и были б полезны heartbeat/keepalive-pings.)
      2. По приходу пакета 0xFF:
        1. Программирование блоков на циклическую работу СЕЙЧАС делается прямой посылкой -- send_frame(), в обход очереди. Это должно вызвать постоянный поток измеряемых данных, НО: данные от АЦП идут в обход очереди, и НЕ вызовут посылку очередного пакета.
        2. А вот если пакет -- "is_a_reset", то и очередь будет сброшена (причем -- даже не драйвером, а layer'ом), и все запросы переповторены. Тут, вроде, оживёт.
      Достаточно ли этого для стабильной живучести драйвера (с восстановлениями при восстановлении связи и оживании блока)?

      Одно ясно -- если реализуем keepalive-pings, то ситуацию "блок казался мертвым в течение некоторого времени; на какой-то ping вдруг ответил" надо приравнивать к "0xFF,is_a_reset".

    4. На будущее: а в v4 -- drivers/can/common_sources/cankoz_lyr_common.c -- как? Там архитектура в этом плане сильно отличается:
      • Сейчас-то в cankoz_sender()'е результат SendFrame() и не проверяется -- всегда возвращается 0. Так что подобного поведения там не будет.
      • С другой стороны, и толп сообщений об ошибках там не будет -- только на первый результат некоего типа (ERR/BUSOFF/ZERO), а следующие точно такие же пропустятся по last_wr_r.
      • Однако -- как всё-таки ЛУЧШЕ сделать, и чем чреват каждый из вариантов?
      • И -- не забыть реализовать описанное выше "ответ на ping после долгого молчания приравнивается к 0xFF,is_a_reset".

    28.05.2010: вот и наткнулись на ситуацию, когда такое поведение -- с прекращением повторов при -1/"No buffer space available" -- доставляет реальную проблему:

    • На живой системе выдергиваем из карточки/cangw CAN-кабель.
    • Вуаля: получаем -1, и авто-пере-посылка прекращается.
    • А блоки-то при этом остаются живыми, но вплоть до следующего "серьёзного" пакета 0xFF их больше никто ни о чём не спросит.
    • Самое интересное, вылезло это в двух местах сразу, с разницей в несколько часов:
      1. На стенде у Ращенко: там после дерганья питания от 124-го прилетал пакет 0xFF, но как-то непонятно мешался постоянно-посылаемый пакет 0xF8 (READREGS), и оно так и не слало DESC_MULTICHAN -- так что измерительные каналы навечно оставались болотными.
      2. На ЛИУ: Леша Панов во время разборок с его контроллером УБСа выдернул кабели из всех 6 блоков, так что комп остался на линии один -- и тоже более никакие пакеты не ходили, кроме как к этому оставшемуся, который только-включился (а остальные-то оставались живыми!).

    Короче -- надо переходить на более свежую схему с last_wr_r, при которой:

    1. НЕ прекращать пере-посылку при ошибке записи.
    2. НЕ выдавать прямо из HAL-модуля в лог сообщение об ошибке, а делегировать это уже HAL-юзеру (т.е., layer'у; а canmon по любой ошибке просто вылетает).

    Приступаем. Анализ:

    • Собственно canhal_writeframe() дергается в нескольких местах:
      1. broadcast-0xFF;
      2. send_wrreg_cmd();
      3. cankoz_sendframe() -- метод send_frame() для драйверов;
      4. do_sendnext() -- собственно, наш пациент;
      5. 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/. Протокол:

    • Перво-наперво -- собственно переход на ту архитектуру:
      1. Скопированы тамошние файлы -- canhal.h, canmon_common.c и marcanhal.h (аналогично -- sktcanhal.h и c4lcanhal.h).

        Замечание: мастер-копии -- живут именно в drivers/can/common_sources/.

        Соответственно, имя макроса сменено с CANKOZ_HAL_H на CANHAL_FILE_H.

      2. Удалены устаревшие исходники утилит -- canmon_mar.c cansend_mar.c cansend_common.c candump_mar.c candump_common.c canutil_common.*. Аналогично этим *mar* -- *skt* и *c4l*.
      3. Также удалены старые marcankoz_hal.h, sktcankoz_hal.h и c4lcankoz_hal.h.
      4. Файлы marcankoz_drv.c и sktcankoz_drv.c сделаны автогенерируемыми (и, соответственно, удаляемыми).
    • В lineinfo_t добавлены поля last_wr_r и (за компанию) last_rd_r.
    • Введена функция-wrapper 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). Исправлено.

    • Также чтение переведено на новый механизм, и умеет корректно ругаться на ошибки (фактически -- back-port из cankoz_lyr_common.c).

    01.06.2010: проверил на sktcankoz'е -- вроде, работает.

    Так что теперь надо реализовывать НЕ-прекращение пере-посылок и НЕ-выкидывание из очереди oneshot-пакетов в случае ошибки отправки. Плюс -- собственно отсылку MULTICHAN делать не прямой посылкой, а _ons()'ом.

    10.06.2010: завершаем -- изготавливаем НЕ-прекращение:

    1. В cankoz_sendframe() еще тогда вставлен безусловный return 0.
    2. Метод q_sendnext убран из свободного доступа -- им всё равно напрямую давно никто не пользуется.
    3. Создана функция perform_sendnext(), пытающаяся отправлять пакеты "по-умному":
      1. Циклит, пока очередь не опустеет -- отправляет очередной пакет.
      2. При ошибке отправки или при не-oneshot регистрирует таймаут.
      3. Далее, если ошибка -- отваливает. Т.о., oneshot-пакеты остаются в очереди, и попытка отправить повторится через 100мс.
      4. Затем, если oneshot-пакет -- то удаляет его из очереди и переходит к следующему, иначе же (т.е., пакет требующий ответа) -- заканчивает цикл.
    4. Соответственно -- вся инфраструктура переведена на вызов оной функции.
    5. А 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() тоже.

  • 13.05.2010: существенное изменение: теперь unicast-пакет 0xFF отправляется не напрямую -- 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: наблюлся баг в работе cankoz_pre_lyr.c с выходным регистром: если устройство перегружалось (reset/питание), то при первой записи какого-либо битика остальные битики портились.

    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() (что давно уже является моим стыдом).
    • Отличие же в том, что это происходит при РУЧНОМ переключении, безо всякого режима. Но, зато -- в этой стойке 12 этажей (хоть из них лишь 5 включены), и просто в начале цикла количество пакетов (уже ради чтения) сразу зашкаливает.

    17.04.2012: в продолжение темы -- баг вылез посерьёзнее.

    • В subharmonic просто отправка нескольких битиков подряд ВРАЗБРОС приводила к "забыванию" изменения всех, кроме первого записанного.
    • Причём -- в log никаких ошибок типа "ZERO" не падает.
    • И после закомментировывания ioregs.req_o_mask=0 проблема ушла.
    • Аналогичная проблема еще раньше проявлялась на управлении ВИП-27 (newvac) -- там дерганье нескольких подряд битов включения/выключения приводило к неотработке изменения последних нескольких.

    Теперь вопросы:

    1. Почему эта проблема появилась -- ведь раньше её не было, subharmonic прекрасно работала.

      Когда и какое изменение привело к этим глюкам?

    2. Ну закомментировано сейчас req_o_mask=0 -- где и как это может аукнуться?

    Расследование быстро нашло ответ на 1-й вопрос:

    • См. 01-06-2010,"наблюлся баг", и тогда эта строчка была раскомментирована.
    • Соответственно -- вот потому subharmonic и работало, что раньше, до 2010 года, обнуление и было закомментировано. А субгармонику последние 2-3 года и не включали.
    • Вопрос 1: а когда всё-таки строчка была ЗАкомментирована, и почему?
    • Вопрос 2: исправлена ли сейчас проблема, заставившая тогда закомментировать? В cankoz_fd_p()::0xFF::is_a_reset что-то не вполне понятное, в т.ч. комментарий "Do we need to send a write request?", после которого ничего нет.

    18.04.2012: попробовал разобраться. Результаты:

    1. О той строчке: хбз -- в начальной версии marcankoz_pre_lyr.c она была, вплоть до как минимум BACKUP/cx_src.20050912_regular, а в STABLE/cx.20051231 уже закомментирована.

      Это всё уходит корнями еще в старую версию, жившую в istc/drivers/canadc40_drv.c (доступна в BACKUP/istc.20060228). Обсуждение имеется в разделе "CAN:", начиная с 16-06-2005, особенно за 07-07-2005.

      ПОЧЕМУ было закомментировано -- стопроцентно неясно, но есть подозрение, что это произошло при разборках с неработающестью регистров 20-09-2005--28-09-2005. Причина-то оказалась в другом, но, возможно, как закомментировали в процессе отладки -- так и забыли, а оно относительно работало.

    2. О проблеме: учитывая написанное выше -- вопрос становится иррелевантным.

      Но: ведь самое правильное -- делать 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: проверяем:

    • cgvi8: mode.req_m_msk и так НИГДЕ не сбрасывалось, кроме is_a_reset; у mode.wrt_prescaler (выполняющий аналогичные функции) -- также обнуление было закомментировано, т.е., уже "нынешний" подход.
    • curvv: использовался вариант с req_o_mask:=0 по send_wr*_cmd(); плюс по is_a_reset нулилось не всё ptregs, а were_read и pending.

      Исправлено -- но еще не проверено.

    • panov_ubs: с одной стороны, в send_wrmsks_cmd() ничего не нулилось; с другой стороны -- по is_a_reset также нулилось не всё msks, а were_read и pending.

      Также исправлено, но не проверено.

    • tvac_320: ровно то же самое -- похоже, там просто копия кого-то из двух предыдущих.

    Кстати, нынешняя модель ХОРОША, но с одним ИСКЛЮЧЕНИЕМ:

    • Если блок после команды записи (или просто по очередному чтению) пришлёт какое-то ДРУГОЕ значение, то выйдет хреновенько -- драйвер упорно продолжит пытаться писать именно то, что "req".
    • А ведь по-хорошему программа должна считать именно БЛОК авторитетом, а не себя...
    • Так что -- по-прежнему надо обдумывать "правильную" и "естественную" реализацию этого механизма...

      (Вопрос тут -- в какой момент можно НАДЁЖНО "сложить с себя ответственность", обнулив req_*_mask, и начать считать уже блок авторитетом?)

    24.05.2012@дорога-на-работу;около-ИХБФМ: ага, опять "передача ответственности" -- это и есть КЛЮЧЕВОЙ термин.

    Когда -- наверное,

    1. Когда блок вернёт значение (по тестирующему чтению), при условии, что отсутствует запрос на еще запись. Т.е., в обработке пакета чтения:
      if (pending==0) req_o_mask=0

      Там как раз везде есть if(pending!=0){send_wr();SEND_RD()} -- вот к нему else и приделать.

    2. По-прежнему, по is_a_reset.

    С таким "правильным" изменением начинает представляться подозрительным нынешний подход к использованию "were_read" в regs_rw() и подобных: там тупо стоит if(were_read){send_wr();SEND_RD()}. А как это будет себя вести при НЕСКОЛЬКИХ последовательных записях, когда чтение после одной еще не успеет придти перед следующей?

    26.05.2012: в продолжение:

    1. У нас send_wr*() до сих пор пользуются send_frame().
    2. Отправка читающего пакета после него сделана по QFE_IF_NONEORFIRST (в обоих местах -- и по получению CANKOZ_DESC_READREGS при наличии pending, и по команде записи), а...
    3. ...по команде записи при !were_read -- с QFE_IF_ABSENT.

    Насколько это всё нормально?

    16.08.2013: в ту же степь -- делаем унификацию по именам полей везде, где используем эту концепцию:

    • Бывшее were_read превратилось в rcvd.
    • pending сократилось до pend.
    • У ioregs реально слабонужное cur_i стало cur_inp, чтоб проще, однозначнее и красивее сделать следующий пункт -- ...
    • ...троица cur_o, req_o_val, req_o_mask переименована в более однородную cur_val, req_val, req_msk (оно так было названо в новеньком (вчера начатом) smc8_drv.c, и это красивше, ибо речь о ЗАПИСЫВАЕМОСТИ, а заодно-читаемость (столь бесящая у Козака) -- побочна).
  • 01.06.2010: надо бы подпеределать cankoz_fd_p(): сейчас он считывает по ОДНОМУ пакету, а их может накопиться несколько, и будет лишний раз дергаться серверов основной цикл.

    01.06.2010: да, надо бы пытаться читать пакеты, пока они есть (т.е. -- пока не вернут CANHAL_ZERO). Но, на всякий случай, например, не более 10 пакетов за раз.

  • 01.12.2010: написан драйвер CURVV.

    История: на прошлой неделе Федя Еманов поинтересовался, как у меня с поддержкой 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, в виде пары пановских девайсиков, и в очень ограниченном применении:

    1. Для управление коллиматором и цилиндром Фарадея (PANOV_FROLOV_BASE) -- там 2 бита, но они всегда используются только раздельно, а чтоб 2 сразу ехали (и, соответственно, записывались бы 2 бита быстро друг за дружкой) -- такого нету.
    2. Для управления переключателем камер (PANOV_CAMSEL_BASE) -- там всего 1 бит.

    Так что если и присутствует ошибка, то у неё не было шанса проявиться.

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

    03.03.2019: тут-то, в v2hw/, ничего исправлять уже не будем. Продолжение разбирательств см. в bigfile-0002.html за сегодня.

  • 01.12.2010: начат драйвер CIR8. Скорее -- за компанию с CURVV.

    16.08.2013: ага, начат -- как же! Только cir8_drv_i.h тогда и был сделан, и с тех пор всё.

  • 14.01.2011: учитывая количество мест, где применяется механизм a-la kozak-ioregs (с отложенной записью, send_wr*_cmd() и т.п.), может -- пора уже сделать готовую инфраструктурку (типа template), на которую перевести весь этот карнавал вариантов? Ведь механизм не самый тривиальный, и легко допустить ошибку.

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

    • А мест-то уже выше крыши -- собственно ioregs, cgvi8, curvv, panov_ubs, tvac320, будущий cir8 (два раза).
    • Основной вопрос -- КАК оное реализовать, с точки зрения Си.
    • Мелкий вопрос -- делать ли различие для двух вариантов системы команд: козачиного, с записью-без-ответа, и пановского, с записью-с-ответом.
    • Более гадкий аспект -- что существует аж 3 разновидности:
      1. Пановская -- самая простая: одна команда пишет маску, другая её читает. Необходимость в алхимии -- исключительно ради "а прочитано ли текущее состояние в момент записи одного бита?".
      2. Классическая козачиная (регистры) -- чуть померзее: читающий пакет возвращает не только выходной регистр, но, за компанию, и входной.
      3. Козачиная в CGVI8 -- существенно мерзее: в пакете записи 0xF0 идут ДВА независимых параметра (Mask и Prescaler). Плюс, пакет чтения 0xFE возвращает аж ТРИ параметра (Mask+Prescaler, писимые по 0xF0, и Limit, записываемый по 0xF1).

      Что успокаивает -- общая схема действий пока вроде бы остаётся единой (то, что базируется на were_read+pending).

    • И уж в drivers/can/common_sources/ -- точно надо так сделать, чтоб не повторять местный нудный труд.

    BTW, а ведь эта фигня -- потенциально не только CAN-specific, но sendq-specific. Ведь у RS232/485-девайсов могут быть аналогичные заскоки.

  • 24.01.2011: наткнулся на кошмарнейший ляп: в cankoz_pre_lyr.c::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().

  • 20.04.2011@Снежинск-каземат-11: крупное изменение в cankoz_q_erase_and_send_next() -- теперь и отправка очередного пакета, и даже удаление указанного делаются только в случае, если указанный найден в голове очереди.

    20.04.2011@Снежинск-каземат-11: ситуация:

    • Первоначальная причина -- при разборках с некоей проблемой с пановским УБСом увидели, что почему-то там часто прут подряд по несколько одинаковых .u-запросов (вовсе НЕ через 100мс).
    • Разборки нашли причину (по крайней мере, одну из): если в девайс присылал некий пакет, НЕ с тем кодом, что стоит в голове очереди (а это нормально для асинхронной связи) -- то оно просто заново слало пакет из головы, поскольку он и оставался на своём месте.
    • По здравому размышлению стало ясно, что:
      1. НЕ НАДО пытаться слать пакет из головы очереди, если он там уже был (т.е., если удалялся не другой пакет из головы, его "предшественник") -- поскольку он УЖЕ отправлен, и его пере-отправкой уже озабочен таймаут.
      2. И даже более того -- не надо пытаться УДАЛЯТЬ пакет, если он НЕ первый.
        1. В первую очередь -- потому, что это бессмысленно: ведь операция предназначена именно для удаления "предыдущего" пакета, а если только-что пришедший -- не он, то и действие будет бессмысленным.
        2. Кроме того, потенциально могла бы возникнуть кривая ситуация: если в куда-то в серединку очереди попала пара пакетов "W:запись" с последующим "R:проверочное чтение", и тут вдруг блок по своей инициативе присылает пакет R, то в очереди останется только запись БЕЗ чтения.

          Т.е., сервер/клиенты получат в качестве актуального некое "старое" значение, затем выполнится запись, но её результата никто не узнает.

    • Короче: сейчас непосредственно в cankoz_q_erase_and_send_next() добавлена проверка «если очередь не-пуста, и пакет в голове -- "не наш", то return».

    21.04.2011@Снежинск-каземат-11: проверено, вроде работает нормально, так что "done".

    22.04.2011@Снежинск-каземат-11: даже более того -- пановским УБСам резко похорошело (раньше они захлёбывались в этих лишних пакетах, и на стойке с 6 УБСами при загрузке режима или перезагрузке "высокие номера" стабильно посиневали).

    06.07.2015: вот только само условие там сделано халтурно:

    if (ring_used!=0 && elem_is_equal(ring+ring_start)) return NOTFOUND;
    
    (условно-сокращённое описание), что в sendqlib-вариантах в ottcam_drv.c и cankoz_lyr_common.c превратилось в
    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 пустой очереди -- но просто дурной.

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

  • 29.10.2011@Снежинск-каземат-11: наткнулся на проблему из-за того, что драйверы в _in() НЕ проверяют размер пакета (хотя даже комментарии /*!!! Check datasize? */ частенько есть): Панов в эмулируемом CANADC40 присылает "ACK"'и длиной 1 байт (просто код команды, в подтверждение, что эта команда им получена); а драйвер, не глядя на размер, тупо смотрел следующие байты и брал оттуда мусор.

    Вывод: надо это исправлять.

    Заодно: везде проверять номера каналов, присылаемые в пакетах.

    30.10.2011@Снежинск-каземат-11: делаем.
    • canadc40_in(): вставлено сравнение как на длину пакета, так и на номер канала (оное стоит ПОСЛЕ логгинга PKTINFO, но перед возвращением данных, тем самым обеспечивая возможность разборок без дополнительных логгингов и приседаний).
    • candac16_in(): проверка просто на размер. Заодно ввёл там chan.
    • cac208_in(), ceac124_in(): комбинация предыдущих двух.
    • xcac208_in(), xceac124_in(), xcdac20_in(): аналогично не-x-версиям.
    • В tvac320_in() шибко мутно -- так что временно на него забито.
    • vsdc2_in() -- тоже, от него просто тошнит.
    • В panov_ubs_in(), естественно, ничего не потребовалось -- там давно уже везде стояли проверки.
    • Прочие отдельных замечаний не заслуживают.
    • Нетронутыми остались canpcsc8_src.c, cgti_src.c, k5045_bbs.c -- по причине пустоты их _in()'ов.
    • Подумывал еще ввести в cankoz_fd_p() доп.условие, чтобы оно НЕ проверяло код устройства, если пакет содержит меньше 2 байт (тогда считается devcode=-1).

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

  • 30.10.2011@Снежинск-каземат-11: нужен драйвер для пановского встраиваемого блочка -- который маскировался под CURVV и CANADC40, а сейчас получил идентификатор 38 и название VV2222.

    30.10.2011@Снежинск-каземат-11: приступаем.

    • В cankoz_strdb.h код внесён (кстати, кто такой 37?!).
    • Изготавливаем скелет vv2222_src.c -- на основе xcac208_src.c.

    30.10.2011@Снежинск-каземат-11: доделываем:

    • Создаём vv2222_drv_i.h.
    • Продолжаем набивать "мясо" копированием из 208-го.
    • Изготовлен chlfaces/.../db_v2222.h.

    01.11.2011@Снежинск-каземат-11: проверяем. По несколько багцов обнаружилось и у меня в драйвере (неудивительно -- копировалось нахрапом с 208-го, у которого всё-таки другая идеология), и в пановском коде.

    Драйвер вроде причесали, так что "done".

  • 19.11.2012: добавлен новый вызов -- CanKozSetLogTo(), для управления включенностью log_frame() (чтоб разгрузить слабенький процессор CANGW).

    19.11.2012: и в canserver.c добавлен консольный интерфейс для её вызова -- функции disable_log_frame() и enable_log_frame().

  • 24.01.2013: результаты разборок с заглючившим на новой сварке (в 14-м здании, на Romeo) блоком weld02 -- опять в сторону проблемностей с send_frame().
    • Выглядело просто страно:
      1. Иногда (чуть ли не половина) уставок не отрабатывалась, так что поле ввода подсвечивалось оранжевым.
      2. А несколько раз вылезало запредельно-бредовое значение -- 2000 (==4095 raw?), и поле желтело.
    • Размышления и допрос Жарикова натолкнули на ответ: пакеты блоку приходят слишком быстро! У него всего ОДНА входная ячейка ("mailbox"?), и она читается один раз за цикл, причём НЕ по прерыванию. А я ему отправляю сразу два пакета -- запись и чтение. Раньше, при CANGW, тамошний проц был медленным, плюс еще и логгинг (который, как выяснилось в октябре-ноябре 2012, очень прожорлив) давал желаемую паузу), а на Romeo, с Adlink PCI-7841, всё мега-резво. Так что:
      1. При отправке двух пакетов сразу первый часто перетирался вторым (странно, что не всегда -- видать, 125кбод спасали), и запись просто не отрабатывалась.
      2. Изредка срабатывал race condition: поскольку Жариков вычитывает буфер неатомарно, то начальные байты данных были от первого пакета (записи), а следующие -- от успевшего как раз прилететь второго (чтения). В результате на запись принималось бредовое число.
    • Сейчас вышли из положения просто -- у Жарикова кроме "слепой" записи (0x80+n) предусмотрена также с автоответом (0x70+n), на неё и перешли.
    • В случае же отсутствия таковой в "правильном" драйвере (при sendqlib'е) надо отправлять пишущий пакет с tries=SQ_TRIES_ONS и timeout_us, уставленным в разумное значение.
  • 17.05.2013: возникла потребность в обновлённых драйверах CANDAC16 и CANADC40 (в X-варианте, со свойствами последних поколений драйверов). В основном в первом -- для плавного подъёма тока в источниках ВЧ-400 на накопителе.

    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...

    • За основу взят исходник xcdac20 (свежеобкорнанный сегодня от таблиц), из которого удалена работа с АЦП и вёрнут единственный DEVTYPE.
    • Затем имевшийся развесистый набор команд сменен на коротенький от старого драйвера.
    • Также из оного взята кодировка данных.
    • Существенное дополнение: auxinfo-параметр spd, маппирующийся на ch_spd[0], оно в init_d() размножает на все остальные. Это добавлено и в xcac208/xceac124 (раньше в них вообще нельзя было указывать скорость).

    24.05.2013: и db_xcandac16.h изготовлен.

    Теперь -- проверять надо обоих.

    19.09.2013: да, проверено (причём ещё и в связке с v3h_xa40d16) -- оба работают.

  • 23.05.2013: драйвер CEAD20. Записываем задним числом.

    23.05.2013: оно начато было давно, как минимум 11-04-2012. Зачем -- пёс его знает, следов использования нигде не видно (как и записей о нём), "db_xcead20.h" отсутствует. Сделано явно на основе canadc40 (это его евромеханический аналог), но на карте kozdev_common.

  • 14.10.2013: слегка бесит то, что при получении пакета 0xFF с id_devcode!=dp->devcode устройство застреливается напрочь.

    14.10.2013: вылезло это на gid25@ВЭПП4, где Карнаев что-то напортачил с джамперами, так что по адресу 63 в дополнение к CEAC124 водился также CGVI8M. Проблема в том, что после вроде бы исправления проблемы (в железе) на экране это никак не отражается.

    По-хорошему, надо б таковое считать не фатальной проблемой, а рассматривать как частный/хитрый случай того, что требует "reconnect'а". Т.е., например:

    • При обнаружении такой ситуации делать
      SetDevState(, DEVSTATE_NOTREADY, CXRF_WRONG_DEV, )
    • А вовсе НЕ застреливаться.
    • Но помечать в описателе устройства (или в privrec'е -- для драйверов CGVI* и CDAC20/CEAC51) "чего-то не того", чтоб не делать более никаких действий. И/или, как минимум, очищать выходную очередь.
    • По получении пакета 0xFF с правильным типом (а пакет ДОЛЖЕН придти при исправлении проблемы, ибо устройство придётся перезагрузить) просто спокойно начинать работать (ну, флаг "не того" сбросить, если будет).
    • Чуть позже: а вот фиг, ведь дёрнуть питание придётся "не тому" устройству, которого мы ждём, а совершенно постороннему.

      Или требовать, чтоб "этому" reset нажали? Тоже не айс...

      Или всё-таки продолжать раз в N секунд слать пакеты 0xFF?

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

      А это может быть решено "режимом недоверия" с повторами:

      • сначала слать 0xFF с таймаутом как изначально (1 минута?);
      • потом, после получения правильного ответа при горящем флаге "wrong_dev" -- чаще, с интервалом 1-2 секунды, и так несколько раз;
      • и после получения, например, 5 "тех" ответов уже начинать "верить", сбросив флаг "wrong_dev".
      • Можно вообще флаг и сделать счётчиком или "индексом шага машины состояний", так что при любом неправильном пакете оно будет автоматом сбрасываться на начало недоверия.

    Одна неприятность: мозни получаются мега-навороченными, и надо б их тогда как-то сделать доступными для "множественных" устройств, с разными 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 могут быть уже запросы -- которые драйвером будут приняты к исполнению и приведут к отправке пакетов.

    И еще, насчёт того как/когда:

    • Раз уж "счётчики доверия", то проблема паузы перед отправкой ping'а отпадает: просто ставить в очередь сразу 5 пакетов

      26.12.2016: ... неа, нифига не отпадает!

    • В счётчик писать число 5, и:
      1. По приходу правильного пакета уменьшать его на 1.
      2. По приходу неправильного -- повторять всё сначала, сбрасывая счётчик в 5, чистя очередь и ставя в неё 5 пакетов.

    26.12.2016: тот (последний) 0xFF, что вызвал обнуление счётчика и "переход в доверие", надо для драйвера переделывать в is_a_reset.

    26.12.2016: пытаемся делать.

    • Счётчик введён -- devcode_chk_ctr.
    • И при его !=0 в cankoz_q_enqueue() не пропускаются никакие пакеты, кроме 0xFF.
    • И даже зачатки проверки "если в режиме недоверия пришёл правильный пакет" были сделаны, но тут вылезли...
    • ...сложности с таймаутами:
      • Во-первых, даже в ситуации "ставим 5 штук FF'ов в очередь" перед первым НАДО сделать таймаут. Иначе он тут же будет отослан, тут же придёт ответ (правильный/неправильный -- пофиг), и тут же будет отправлен следующий FF (при правильном ответе -- просто следующий; при неправильном -- опять тот же "первый").
      • Во-вторых, в sendqlib'е НЕТУ возможности указать "вот сделай это, и потом столько-то микросекунд не делай больше ничего" (в отличие от vdev'а с его принудительно-соблюдаемыми паузами после переходов): по приходу ответа пакет будет из головы очереди убран а его таймаут деактивирован.
    • @по-пути-на-обед-в-ТП,-по-Будкера: мысли:
      1. По-хорошему, при devcode_chk_ctr!=0 надо бы и пакеты от устройства НЕ передавать драйверу. @после-обеда: сделано, тривиально.

        @по-пути-обратно-после-проходной-ОК: совсем по-хорошему -- надо бы вообще иметь флаг "was_good_ff_rcvd", и пропускать пакеты в любую сторону только при нём !=0. И изначально делать его =0, сбрасывать по чужим пакетам, и ставить =1 по приходу хороших (точнее -- в точке перед передачей 0xFF'а драйверу: чтобы и в режиме недоверия оно б срабатывало как надо). Но, учитывая, что у нас устройства сейчас рождаются НЕ в NOTREADY (коему бы "was_good_ff_rcvd=0" отлично соответствовало), с этим будут проблемы.

      2. Всё же как-то некрасиво сделано определение "верное/неверное устройство": там переплетено само определение и действия по результатам.

        "Красиво" -- было бы просто делать определение, и результат помещать в булевскую devcode_is_correct, с которой потом уже что-то делать.

        Так было бы легче модифицировать код "решения" -- а там нужно что-то менять в логике.

    • @по-пути-с-обеда-в-ТП,-по-Будкера: а можно выпендриться так: в режиме "недоверия" НЕ удалять 0xFF из очереди -- пусть он там продолжает оставаться и просто посылается 5 раз, а уж после получения последнего ответа -- удалим.

      Проблем 2:

      1. Удаление производится в самом начале, ДО каких-либо проверок.
      2. В оригинальном-то варианте предполагалось перед первым пакетом ставить задержку 60с, а между следующими -- всего по 1-2с. Но тут будет ОДИН таймаут на всё.
    • Как можно решить проблемы:
      1. Если, как хочется, выделить определение в отдельный кусок с помещением результата в devcode_is_correct, то всё переделывается тривиально.
      2. При первом верном ответе удалять пакет вручную и тут же ставить его уже с таймаутом в 1с.

        ...но:

        • Проблему "как сделать паузу ПЕРЕД отправкой первого пакета" это ж не решает?
        • А, вот как надо: удалять после получения ВТОРОГО верного ответа -- тогда перед ним как раз будет 60с.
        • ...неа, нифига это не решит: если придёт "плохой" ответ, то СРАЗУ ЖЕ будет отправлен очередной 0xFF, на него тут же придёт опять плохой ответ, и т.д. -- пакеты польются как из ведра.
    • @лыжи-после-обеда: раз проблема в невозможности сделать паузу в нужный момент (перед отправкой следующего пакета) -- значит, надо добавить в sendqlib возможность делать паузу! Например, sq_pause().

      Некоторые замечания:

      1. Поскольку паузу в ДАННОМ случае надо делать только после очистки очереди, то первая идея была «добавить в sq_clear() аргумент "сколько выдержать паузу после очистки"». Но это а) менее гибко; б) сломает совместимость.
      2. Как бы то ни было, предполагается, что sq_pause() можно использовать:
        1. не когда попало, а только после sq_clear();
        2. только с одиночными очередями, БЕЗ портов (т.к. у портов таймауты свои, делимые с другими устройствами на том же порте).

          27.12.2016: теоретически, можно было бы задействовать очередевы таймауты -- которые при работе через sq_port не используются. Но это именно если припрёт, ибо получится нехилое усложнение (там и так приподзапутано).

          27.12.2016, получасом позже: что интересно, в работе с портами очередевы tout_set тоже проверяются. Видимо, наследие от до-портовых времён; в любом случае, "если припрёт" -- код к такому дополнению (заюзывание очередевых таймаутов) уже готов.

        3. Была мысль сделать его "полноценным управлением таймаутами" -- чтоб значения <=0 могли убирать текущий таймаут. Но ЗАЧЕМ? Лучше сделать только то, что реально нужно, а не добавлять избыточные инструменты (лишь усложняющие ситуацию).
      3. Также, ABI несколько изменится (а sendqlib является частью публичного API сервера), но лишь в сторону совместимого расширения. А поскольку локальные CAN-драйверы сей момент нигде не используются (LIU -- эксперимент; b14 -- еще не внедрены; сварка -- тоже), то изменение вообще нигде проблем не вызовет (нет места, где б новый cankoz_lyr* столкнулся со старым сервером).
      4. Касательно самого "пингования N раз": несколько криво, что, хоть оно будет само пытаться слать пакет 5 раз, но в качестве "ответов" примет также и пакеты по инициативе блока (reset, poweron, whoarehere, ...). Ну да фиг с ним -- на корректность работы алгоритма это влияет непринципиально.

    27.12.2016: делаем.

    • sq_pause() сделана.
    • Определение корректности переведено в отдельный кусок кода, складирующий результат в devcode_is_correct.
    • Использование devcode_is_correct -- также отдельным if(){}else{}.

      В нём и делается вся работа по "восстановлению доверия":

      • По некорректному devcode -- очистка очереди, заказ паузы 60с, установка в очередь 0xFF с таймаутом 2с, установка devcode_chk_ctr=5.
      • При devcode_chk_ctr!=0 --
        1. декремент,
        2. если всё ещё !=0 -- возврат;
        3. иначе взведение is_a_reset=1 и проваливание ниже в обычную обработку 0xFF.

        Замечание: пакеты типа CANKOZ_IAMR_WHOAREHERE игнорируются, т.к. они ниже зафильтрованы от передачи драйверовым ffproc(), а нам НЕОБХОДИМО, чтобы после прихода последнего пакета -- когда происходит "восстановление доверия" -- драйвер был дёрнут. Ну фильтрация нестрашна -- дождёмся ответа на очередной unicast.

      • Конкретно erase_and_send_next делается только при корректном devcode и нулевом счётчике -- т.е., когда всё в порядке.
    • Взведение 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 месяца показывает, что всё окей. Итого -- цель достигнута, раздел закрываем.

cm5307 drivers:
  • 06.09.2004: надо-таки расшивать cm5307_drvletbody.c::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: плохая была идея, ох плохая...

    • Отправку они производят из буфера sndbuf, указываемого вместе с размером 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/.

    Некоторые замечания:

    • То, "старое" -- work/karpov/ и work/new_rfsyn/ теперь становятся deprecated. Более того -- они толком и компилироваться-то не смогут, из-за конфликта -- разные варианты файлов с совпадающими именами cm5307_dbody.*.
    • Ныне все pult-specific, что было в тех директориях, переехало в work/qult/ -- новая версия work/pult/ (который по мере ее заселения также станет deprecated). Название "qult" выбрано потому, что "q" -- едва ли не единственная буква, с которой в work/ ничего не начиналось.
    • API сделан ПОЛНОСТЬЮ совпадающим с тем, который будет у обычных драйверов в CXv4:
      • Префиксы методов теперь -- Cxsd*.
      • Несколько расплывчатое (по историческим соображениям) слово "magicid" теперь сменено на "devid".
      • Метод init_b вместо bid (который раскодируй-как-хочешь) теперь принимает int *businfo,int businfocount; в "описании" же драйвера указывается (min_busid_n,max_busid_n).
      • Возвращает он теперь не fd, а статус -- один из DRVS_OFFLINE=-1, DRVS_NOTREADY=0, DRVS_OPERATING=+1, либо -rflags. Не 0!
      • Кроме него НИКОМУ более ни bid, ни fd не передаются -- пусть складируют в privrec'е, именуемом "me". Только devid и privptr.
      • В "описании" кроме privrecsize также указывается и paramtable, в которую "хозяин" сможет pre-parse'ить содержимое auxinfo. Если NULL -- ничего не делается.
      • Само описание делается макросом DEFINE_DRIVER(), и должно стоять в конце файла (резоны -- см. ниже).
      • Информация о layer'ах сделана по проекту за 26-01-2004 -- char *layer,int layerver (пока -- под хвост).
      • Недоросли {,v}DoDriveletDebug() заменены на целиком унифицированные {,v}DoDriverLog().

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

    • Насчет "возможной унификации": cm5307_dbody.h::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, который бы позволил одному драйверу обращаться к каналам другого.

    • Что же пока мутно -- это инициализация доступа к CAMAC'у. Она пока весьма неявная.

    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 для этого все есть -- надо лишь:

    1. Задействовать методы init_b() и term_b().
    2. Реально задействовать privrec.
    3. ...а вот что делать с типа-методом "таймаут select()'а", который используется некоторыми драйверами для проверки таймаутов? Вводить реально работающий timeout-API, пусть даже пока всего на ОДИН таймаут?
    4. Получше определить в dbody-API интерфейс CAMAC и, в частности, LAM'ов.

      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 (а вот это -- да-а-а... Как оно вообще работало и не падало постоянно...).

    Исправлено...

  • 18.10.2004: порядком задрало, что на каждом конкретном контроллере надо
    1. указывать его IP;
    2. прописывать /export/NNN, где NNN зависит от того, кто у него хостом -- то linac1, то ring1, то hedgehog... Вот если бы -- было унифицированно, типа /export/localhost/, но с таким вариантом свои проблемы -- на собственно хосте.

    Ну и че -- обе проблемы решит DHCP?

  • 20.12.2004: было замечено, что иногда uclinux-драйверы тихо мрут -- это выражается во внезапном закрытии соединения. Диагностика --
    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: некоторое время назад я обнаружил, что кросс-компилятор под uClinux (m68k-elf-gcc, egcs-2.91.66 19990314) под Fedora3 падает по SIGSEGV на rrund.c.

    31.01.2005: Виталий Мамкин посоветовал -- что у него эта хрень любит так падать при re-#define, и что стоит окружать все такие места условиями. Угу, вставил на каждый из htonl()/htons() по #ifndef'у.

  • 07.04.2005: пришел Андрей Чугуев с вопросом -- какого максимального объема могут быть большие каналы при Мотороллерах. Выяснилось, что в нескольких местах зашиты числа, сводящиеся к 32000байт на содержимое пакета данных.

    Надо это свести в одно место и сделать 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.

  • 07.04.2005: сделал сходу: раз уж rrund.c теперь все равно #include'ит cm5307_drvlet_proto.h, то для корректности перенес туда определение номера порта -- RRUND_DEFAULT_PORT=8000, избавив rrund.c и cm5307_drv.c от раздельных констант.
  • 10.06.2005: наблюдается некая неясность с порядком действий в rrund.c::Run(): сейчас там порядок действий следующий:
    1. fd=accept(...);
    2. Прочитать из fd имя запрашиваемой программы;
    3. vfork(), exec().

    Имеем следствие: rrund DDoS'ится тривиально -- первым же connect()'ом. Это вроде как нехорошо, и, по логике, надо СРАЗУ уфорковываться и уже там читать имя программы (хотя в случае с vfork() это без разницы).

    С другой стороны, нынешняя последовательность обеспечивает сериализацию -- что rrund НИЧЕГО не вякнет (типа "can't fork()"), пока ему не отправят имя программы целиком.

    Я влез в эти дебри сегодня, при написании новой, не-блокирующейся-на-connect()'е версии cm5307_drv.c. И там нынешнее поведение представляется явным благом.

    30.04.2010: в can/gw/ppc_canserver/ и в 4cx'ной версии rrund (точнее -- в remdrvlet_listener'е) используется такая же последовательность, только чтение делается неблокирующимся и отваливающимся по таймауту, что и решает все проблемы. Более того -- именно такой подход и является максимально адекватным (всё централизованно и управляемо).

    Так что -- считаем вопрос закрытым как "obsolete".

  • 05.05.2006: наблюл какую-то странность: если в abstract_camac/ListOfDrivers.mk указано некое имя, а файла abstract_camac/a_ИМЯ.h нету, то ничего и не происходит -- make не ругается, а просто мирно говорит, что "Nothing to be done for `firsttarget'". Чего-то тут не то -- это неправильно!!!

    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 куска знаний:

    1. Автопеременная $* принимает своё значение ОТ ШАБЛОНА ТОГО ПРАВИЛА, КОТОРОЕ ОПРЕДЕЛЯЕТ СОЗДАНИЕ 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 -- это неочевидно.

    2. Надо быть аккуратнее с автогенерацией: 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 вывода:

    1. Косвенная компиляция -- ЖУТКОЕ ЗЛО, маеты с ней -- опухнуть сколько, как на стадии использования (всё криво), так и на стадии избавления от неё.

      Но и автогенерация "частей makefile" -- тоже не сахар.

    2. Избавляться надо к чертовой матери от "коцаных" и "генеримых" вещей -- как от chlclients (в CXv4 уже нету), так и от нынешней навороченной концепции "абстрактные драйверы с множественными интерфейсами".

    Первоначальная же проблема за 05-2006 решена, на отсутствующие a_ИМЯ.h оно ругается, да и от косвенной компиляции наконец-то вроде бы как ушли (аллилуя!!!), так что -- "done".

    P.S. Да, и .d-зависимости оно теперь для абстрактных драйверов генерит.

    05.05.2010: насчет абстрактных драйверов в v4: совсем от них избавляться не надо -- концепция-то удобная. Просто надо упростить ситуацию: в каждой директории поддерживать ТОЛЬКО ОДИН ИНТЕРФЕЙС. При этом *Rules.mk значительно упростятся, и не понадобится вообще никакое генерение i_IFACE_rules.mk -- достаточно будет иметь прямо в AbstractCamacRules.mk соответствующие строчки с $(ABSTRACT_IFACE) в именах target'ов.

  • 11.05.2006: (не совсем сюда, но отдельной секции для протокола нету) заметил в cm5307_drvlet_proto.h поле cm5307_pkt_header_t.var.debugp.length, которое не используется и никогда не использовалось -- для строк отладочной печати используется терминатор '\0'.

    12.05.2006: удалил это поле.

  • 18.05.2006: уже неоднократно требовалось преобразование из CAMAC-статуса в rflags. В старых драйверах это делается вручную, конструкцией
    (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. Так они доступны и абстрактным, и нормальным драйверам.

  • 23.05.2006: раз уж ABSTRACT_INIT_FUNC() возвращает int (сейчас не использующийся), то почему бы не использовать то же соглашение, что и для обычных драйверов -- что когда функция инициализации возвращает >=0 -- это нормально, <0 -- проблема, блок надлежит пристрелить, причем !=-1 -- значит, это -rflags_to_set.

    Для сего понадобится:

    • Сделать, чтобы cm5307_dbody.h::cm5307d_cfig_p также стала int.
    • Естественно, ввести в drvlet-протокол пакет CM5307_DLP_HALT с полем "rflags_to_set".
    • И, конечно, его обработку в cm5307_drv.c::cm5307_fd_p().
    • Плюс, уж для полного комплекта -- не помешает и cm5307_dbody.[ch]::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: поднадоело, что абстрактные драйверы в значительной своей части (в основном старые) напрочь игнорируют понятие "rflags". (Ну да, некоторые из них даже дату модификации имеют еще до того, как появилось само слово "rflags".)

    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: понадобилось слегка подпричесать cm5307_alfo.c -- не хватало его способностей для разборок с У0632.

    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", по идее, быть не должно :-)

  • 15.07.2006: наткнулся на редкую дурь в a_c0602.h: каналы пределов там только ПИСАЛИСЬ, без КОНТРОЛЬНОГО ЧТЕНИЯ. В результате и имелись те глупости, что "не реализовывалась обязательная кратность 256" -- бибикало, например, при попытке уставить предел 50.0, ибо он воспринимался как 25.6.

    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: еще давным-давно надлежало изготовить файл include/drv_i/adc200_drv_i.h.

    30.08.2006: "последней каплей" послужила потребность программы adc200 -- потому сделал, включая определения таймингов и диапазонов.

  • 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: драйвер a_c0602.c игнорировал принцип "уставка предела в 0 отключает соответствующий alarm".

    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: из опыта canserver'а увидено, что SIGPIPE иногда все-таки возникает. Так что его надо перехватывать -- и в rrund, и в cm5307_dbody.

    18.01.2007: да, вставил, в:

    • rrund.c::main().
    • cm5307_dbody.c::InitDrivelet() -- чтоб во все программы не пихать отдельно.
    • i_cm5307.c::main() -- для старых, cm5307_drvletbody-based драйверов (так ведь их на dbody и не перевели!!!).
  • 20.02.2008: постановка проблемы: достает, что при перезагрузке драйверов быстрых АЦП (ADC200 и ADC333) старые параметры забываются.

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

    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вских драйверах -- пока всё печально...

    • adc4: в память писать вообще никак нельзя.
    • adc200: работает, но как-то странновато: если писать сначала в 1-й банк/канал, а потом в 0-й -- то всё нормально, а вот если по-человечески, сначала в 0-й, потом в 1-й -- то почему-то каждое старшее полуслово читается из ПРЕДЫДУЩЕГО. Сазанский озадачен, пока ничего не сказал.
    • adc502: похоже, пишется вообще непонятно что (если вообще пишется). На вид -- читаемое никак не зависит от записанного, хотя NAF'ы официально описаны в документации. Сазанский также озадачен.
    • adc333: после предыдущей троицы даже не стал пробовать (а, наверное, зря...).
  • 18.02.2009: давно напрашивалась поддержка операции "блочный NAF".

    Главная потребность -- у драйверов быстрых АЦП (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: давно пора было изготовить вариант dbody на базе cxscheduler+fdiolib -- как remdrv* в v4. Основной смысл -- чтоб драйвлеты также могли регистрировать таймауты и файловые дескрипторы.

    22.07.2011: за последние несколько дней вроде сделал, но еще не проверял. Ключевое слово -- cm5307_sl_dbody.[ch].

    • Технология безболезненного перехода на новую реализацию:
      • Собственно новый код живёт в отдельных файлах cm5307_sl_dbody.[ch].
      • В старые -- cm5307_dbody.[ch] добавлено:
        #if CM5307_SL_DBODY
          #include "cm5307_sl_dbody.c" // или .h
        #else
        ...тут старое содержимое файла
        #endif
        
      • А в CommonRules.mk --
        CM5307_SL_DBODY=	0
        DEFINES+=	-DCM5307_SL_DBODY=$(CM5307_SL_DBODY)
        
      • Соответственно, можно управлять используемым вариантом даже просто указанием make'у в командной строке "CM5307_SL_DBODY=0/1".
    • В cm5307_sl_dbody.h добавлен макрос DEFINE_CXSD_DRIVER(), со списком параметров из 4cx/src/include/cxsd_driver.hDEFINE_DRIVER() просто вызывает его).

    • Начинка cm5307_sl_dbody.c -- гремучая смесь из содержимого тучи компонентов из разных мест разных версий системы:
      • Из 4cx/src/lib/rem/ не сгодилось вообще ничего -- ибо там пока почти ничего нет (только listener да srvmain, тут не нужные).
      • Из 4cx/src/include/{cx_module.h,cxsd_driver.h}, напоминаю, взято море определений в cm5307_sl_dbody.h касательно модулей (cx_module*) и CxsdDriverModRec.
      • Из ppc_canserver/canserver_drvmgr.c -- заклинания по работе с fdiolib'ом касательно хостового дескриптора; причём, поскольку в canserver'е драйверный API "методов драйвера" совпадает с в-серверным от v2, то...
      • из cm5307_dbody.c взяты v4-образные вызовы методов.
      • Из 4cx/src/lib/srv/cxsd_driver.c взята реализация API Tout, FD, IO (пока еще, кстати, ведь не проверенная...).
    • И, конечно же, cm5307_fastadc_common.h в таком варианте функционировать не станет -- он плотно заточен под обычный _dbody, сам делает основной цикл по 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.

    • Поскольку требуются libuseful.a и libcxscheduler.a, то пришлось добавить в CommonRules.mk их собирание, а в LocalRules.mk -- прилинковывание.

    Некоторые размышления "на будущее" о потенциальной унификации, по результатам изготовления:

    1. То, что сейчас находится в cm5307_sl_dbody.c, очень желательно как-нибудь вынести в отдельный файл, чтоб он был единым для всей толпы cm5307/bivme2/moxa. Главное при сём -- убрать оттуда код касательно CAMAC.

      Видимо, надо все "стандартные" вещи унести в общие 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: почему-то тогда

    2. Код, реализующий API Tout, FD и IO фактически совпадает с оным из 4cx/src/lib/cxsd_driver.c -- отличаются лишь прологовые макросы CHECK_SANITY_OF_MODID() и SELECT_EVENT_TARGET(). Очевидно, и с API каналов будет аналогично -- с отличием в том, что делать с полученными данными (в сервере -- отдать в БД, в драйвлетах -- отправить "наверх").

      Напрашивается -- как-то попробовать и это вынести в некий общий файл.

      10.12.2012: поскольку в "унифицированном" интерфейсе v2/v4 для v2 был вариант файлового API {Rd,Wr,Ex}FD, а не просто FD (этот -- для v4), и реально оно не использовалось нигде, то оно выкинуто.

    3. ...а вообще, конечно, да -- объектная архитектура так и прёт, наследование тут было бы весьма к месту... :-(

    27.07.2011: расширил Tout-API: добавлена RegisterDevToutAt().

    • Сделана тупым копированием RegisterDevTout() с тривиальной модификацией.

      Вообще-то это дюже некрасиво (там ОГРОМНОЕ количество кода скопировано), но пока пойдёт и так.

    • В 4cx/ покамест обратно не портируем -- потренируемся здесь.

    27.07.2011: Роговский успел попроверять -- на "sl_dbody" всё работает, и оба варианта Tout'ов в том числе.

    29.07.2011: приподупорядочен API работы с CAMAC: теперь не каждый драйвер содержит кусочек вида

    #include "cm5307_camac.h"
    #define DO_NAF  do_naf
    #define DO_NAFB do_nafb
    
    а это вынесено в c5307_{,sl_}dbody.h.

    Также, для изоляции специфики, туда добавлена строка

    #define CAMAC_REF camac_fd
    
    и во всех исходниках direct-drivelet'ов позаменены "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" в разных местах использовались разные имена:

    1. privptr -- это в "Driver's methods definition, унаследованное от "продвинутого" API нынешней версии (т.е., уже не 2.0, как в сервере, а то ли 3, то ли 2.5);
    2. devptr -- во всех остальных, взятых уже из 4cx/; а privptr там уже "для callback'ов".

    Правильное решение -- везде devptr.

    09.08.2011: имела место забавно-постыдная история:

    • При копировании из 4cx/ определения CxsdDriverModRec там остались и поля chan_nsegs, chan_info, chan_namespace, cmd_table, а их типы были, за ненадобностью, тихо с-typedef'лены на void.
    • Через некоторое время возник вопрос -- а нафига эти типы там вообще нужны, "они ж нигде не используются".
    • Окей -- выкинуты (использования почему-то не заметил).
    • Через пару дней заметилось, что оно не компилируется.
    • Ладно -- были выкинуты и поля, как никому нафиг не нужные.
    • Еще через некоторое время обнаружилось, что собранные с sl драйверы просто не работают.
    • Оказалось, что поля-то выкинуты, но в инициализации структуры в 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, состоящий из двух аспектов:

    1. В remdrvlet_c.h::ProcessPacket() делается приведение busid'а к массиву businfo -- оно бъётся на байты. А для VME это вредно -- поскольку адрес на шине указывается 12/16-битным числом.
    2. И там же стоит проверка, что если получившийся байт за пределами диапазона [1,24], то он ставится в -1.

    Можно обходиться полумерами -- типа восстановления 16-битного числа из пары 8-битных конкретно в VME-драйверах, плюс устранения проверки на [1,24] вообще для всех.

    Но раз уж всё равно remdrvlet_c.h #include'ится в конкретные .c-файлы, то лучше сделать РАЗНЫЕ случаи для разных видов драйверов, и переключать их #if'ами. Так что теперь:

    • Тип раскладки busid по businfo[] определяется символом REMDRVLET_C_H_BUSINFO_MODE, могущим принимать значения
      1. REMDRVLET_C_H_BUSINFO_CAMAC -- раскладка по 4 байтам, с проверкой на [1,24].
      2. REMDRVLET_C_H_BUSINFO_VME -- байты 0,1 идут в [0], байт 2 -- в [1], байт 3 -- в [2] (т.е., это триплет {address,irq,vector}).
      3. Иначе -- копируется в [0] как есть.
    • Соответственно, конкретный _dbody.c файл уставляет требуемый ему режим перед включением remdrvlet_c.h.
    P.S. В v4 вся эта страхомуть неактуальна -- там изначально нормальная схема с businfo[].

    30.07.2013: всё вышеописанное не только было сделано и использовалось, но уже и де-факто устарело (заменено новым libremdrvlet'ом, сделанным на основе полученного тут опыта) и отправлено в отставку. Так что даже не "done", а сразу "obsolete".

    И, кстати, многое из описанного и обсуждённого РЕАЛЬНО устарело -- и махинации с businfo, и "API дескрипторов и таймаутов" -- в связи с существенной переработкой драйверного API.

  • 22.07.2011: за компанию -- приподдостало наличие ДВУХ описаний протокола удалённых драйверов: cm5307_drvlet_proto.h и cangw_cansrv_proto.h. А еще ведь будут BIVME2 и MOXA.

    Пора переходить на ЕДИНОЕ определение -- очевидно, с префиксом 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: неа, учитывая необходимость в ближайшем будущем поддерживать также BIVME2 и MOXA -- всё-таки надо заводить и единый remdrv_drv.c.

    04.08.2011: Делаем:

    • Поселяем его в drivers/remdrv_drv.c.
    • За основу взят cm5307_drv.c -- поскольку именно в нём имеется дополнительный код а) парсинга архитектуры и передачи префиксов/суффиксов; б) парсинга set1 и set2.
    • Общая концепция -- архитектура будет указываться ВСЕГДА, и в зависимости от неё будут использоваться разные префиксы/суффиксы (для мультиплексоров cangw и moxa -- пустые).

      Возможные архитектуры -- 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*()).

    • Считаем, что сам remdrv_drv работает АПРИОРИ И ТОЛЬКО на little-endian.
    • Добавляем arch_info_t.is_be -- сразу после имени.
    • Код повсюду начиняем проверками на него. Сейчас практикуется 2 варианта:
      1. if (...is_be) {делаем с конверсией} else {просто копируем}
      2. просто копируем; if (...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), но глубокого смысла не видно. А главное --

    В будущем надо бы эту схему распространить и на

    • CAN.
    • fdiolib.
    • serial. //02.01.2013
    • cm5307_dbody. // 14.06.2013 // 18.06.2013: неа, забъём -- скоро исчезнет.

    13.09.2012: в результате того усовершенствования появился феерический глюк: remdrv стал регистрировать таймаут уже ПОСЛЕ ухода в OFFLINE. В результате сервер вызывал TryToReconnect_p() с devptr==NULL и всё благополучно SIGSEGV'илось.

    Технически причина проста -- по сдыханию драйвера дескриптор закрывался, дальнейшая проверка check_fd_state() давала -1/EBADF, и оно ставило RECONNECT.

    Выводы:

    • После застреливания надо сразу делать return -- это в remdrv реализовано.
    • Поскольку аналогичное поведение требуется и в fdiolib, то там надо менять API -- чтоб 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:

    1. Портабельность операции "вычитывание пачки".
    2. Логика обработки "ошибок" -- CANHAL_R_*/last_rd_r.

    Так что по возможности надо стараться всё же воздерживаться от внедрения данной "оптимизации".

  • 13.09.2011: случайно (на vadc16) заметил, что rrund НЕ закрывает перед exec()'ом драйвлета слушающий сокет, и драйвлеты его наследуют -- дескриптор 3. Это совершенно излишне.

    13.09.2011: да, вставил close(lsock).

    (В принципе, можно было бы уставлять lsock'у close-on-exec:=1 -- fcntl(lsock,F_SETFD,1) -- но close() кажется симпатичней.)

  • 13.09.2011: заодно сделал давно напрашивающуюся вещь -- ключик "-v", при наличии которого rrund выдаёт на stderr сообщение о том, что его попросили запустить.
  • 16.04.2013: новая утилитка -- cm5307_test.c, на замену кривому старичку cm5307_alfo.c. Живёт покамест там же, в основном дереве cx/.

    16.04.2013: концептуальности:

    • Общий подход взят от canmon'а -- в argv[] указывается список "команд", среди которых могут быть как В/В, так и ожидание. Ожидание кодируется аналогично -- ":[микросекунды]".
    • Формат команд В/В -- N,A,F[=DATA].
    • Кроме того, предусмотрена специальная команда "z", для выполнения Z-цикла (но пока не реализована, по причине несделанности в cm5307_camac.h).
    • Поддержка LAM'ов пока также отсутствует -- отложим до перехода на новую архитектуру.

    31.05.2013: проверено; в реализации DoIO() были критичные ляпы -- очень кривой парсинг. Исправлено, NAF'ы исполняются (так что старый cm5307_alfo можно уже и не использовать).

    30.07.2013: поддержка LAM'ов тоже сделана (уже в новом месте -- v2hw/driver/camac/cm5307_ppc_drvlets/, но опишем за компанию тут). Интересности:

    • Поскольку лень было реализовывать на cxscheduler'е, то пришлось сделать собственную копию работы с LAM'ами, вместо использования готового API WATCH_FOR_LAM(). Стыд и позор, плюс непортабельность (оно живёт именно в cm5307_ppc_drvlets/ вместо src/).
    • Общая схема содержимого DoWait() взята с uspci_test.c, в т.ч. ":[ТАЙМАУТ]" означает "ждать первого LAM", а ".ТАЙМАУТ" -- "выждать всё время".
    • И возникает мысль и синтаксис тоже приблизить к uspci_test: NAF'ы писать не через запятую -- N,A,F[=V], а через двоеточие -- N:A:F[=V], чтоб можно было пачку NAF'ов указывать, например, для -i.

      Для будущего vme_test это тоже может быть полезно.

    • Никак не мог определиться, в каких же единицах указывать возможную задержку -- в МИЛЛИсекундах (как в canmon) или в МИКРОсекундах (как в uspci_test).

      В результате сделано "всё" -- можно указывать число и суффикс единиц -- us/ms/s/m/h, по умолчанию ms.

      Код с одной стороны громоздковатый, а с другой -- напрашивается на внедрение и в прочие утилиты mon/_test. Из чего вытекает идея о попытке унификации оных -- но это скорее маниловщина, поскольку точно потребует перевода их всех на cxscheduler.

    • И вообще вся нынешняя реализация выглядит ОЧЕНЬ громоздко и бардачно.
    • С LAM'ами вылез косячок: если УЖЕ был готовый выставленный LAM, то сразу в обработке -i делалось "LAMENABLE" и сигнал начинал прилетать бесконечно, блокируя работу. Поэтому вызов camac_setlam() переставлен уже в "после синхронной реакции на LAM" (в для-драйверной реализации тоже).

    Вообще вроде бы пашет.

CANGW:
  • 07.06.2006: пришла в мозги мысля, как можно просто и по-быстрому реализовать поддержку "коробочек" CAN-GW.

    Основой послужило то, как реализована последняя (на данный момент :-) инфраструктура "cm5307 drivers": там просто среда исполнения, поддерживающая/реализующая API драйверов.

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

    07.06.2006: детали:

    • Запускаемый в "коробочке" процесс -- ЕДИНСТВЕННЫЙ (это следует из монопольности доступа к /dev/can*). Он и будет запускаться из /etc/rc.sh, и слушать какой-нибудь порт.
    • В сервере же будет работать драйвер cangw_drv.so, представляющий из себя просто мультиплексор-шлюз.
    • Отдельный вопрос, сколько иметь сокетов: по одному на ДРАЙВЕР, или же по одному на ПРОЦЕСС в коробочке? Наверное, лучше по одному на процесс. (Но мультиплексор-шлюз нехай пытается в цикле считать БОЛЕЕ одного пакета, например, не более 10.)
    • Делаем просто "тупую" директорию drivers/canbus/cangw_ppc/, без поддержки "внешней сборки".
    • В нее симлинкуем файлы из drivers/canbus/, а вот "layer" там имеем свой -- переделанный под специфику CANGW вариант marcankoz_pre_lyr.c. И плюс -- отдельным файлом "среда исполнения" -- которая имитирует сервер.
    • Среда исполнения работает на основе cxscheduler'а.
    • Некоторый вопрос -- в "адресации" и "magicid'ах": 1. Как адресоваться в протоколе, если на каждую "коробочку" -- по одному сокету? 2. Кто назначает magicid'ы для драйверов в коробочке? (Лучше бы, конечно, из соображений разделения -- пусть назначает сама коробочка).
    • Отдельный вопрос: а ДВА разных сервера к ОДНОЙ коробочке обращаться смогут (к разным ветвям)? И если да, то КАК? И надо ли это?
    • И еще вопрос: в cm5307-то за запуск драйверов отвечает отдельный, ма-а-ахонький процесс rrund, который хрен упадет. А тут -- монстровый монолитный драйвер...

      Стоит сделать ДВА процесса: собственно драйвер, и запускальщик-сторож, имитирующий 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: наблюдены какие-то странности:

    • Во-первых, иногда драйвер сразу при запуске валится с диагностикой
      DEBUGP: psp_parse(auxinfo): The '0ЪЪЪЪ' is an invalid value for 'magn=' (integer expected)
      -- странно, поскольку везде auxinfo сохраняется и передается корректно, не забывая о +1 на trailing NUL.
    • Во-вторых, после добавления забивки всего пакета FF'ами перед его заполнением -- пару раз с-segfault'ится сам сервер. Но -- тоже невоспроизводимо, так что и gdb фиг натравишь...

    Концов так и не нашел, но на всякий случай (если это НЕ битая память у ring1) вставил перед инициализацией драйвера побайтовый дамп пакета на stderr.

    01.02.2007: с первой странностью (0ЪЪЪЪ) разобрался -- это был ляп в InitializeDrivelet(), пришедший еще из cm5307_drv.c (а туда оно попало из old_cm5307_drv.c, куда оно перекочевало из m5200_drv.c -- итого, ошибка больше пяти лет). Там размер пакета считался так:

    sizeof(pkt.hdr) + ((strlen(rec->drvletinfo) + 1) &~ 3U)
    Либо надо было "+1+3)&~3U" -- добивка до кратного 4, либо вообще безо всякой кратности -- тут она незачем (ибо строка, в конце пакета, и размер пакета -- в байтах).

    В результате же там просто ОТБРАСЫВАЛИСЬ последние 1-3 байта, в т.ч. и trailing NUL. А уж в контроллере-то обычно на этом месте просто случайно оказывался нолик -- но не всегда.

    Пофиксил, просто убрав "&~3U".

    16.02.2010: замечание: написанное выше «Либо надо было "+1+3)&~3U"» -- явная чушь, добивкой "до кратного 4" является просто "+3", а не "+1+3".

    В общем, уже давно работает, так что -- "done".

  • 10.02.2007: архитектура с c4lcankoz_drv.c переведена, вместе с обычным marcankoz, на унифицированные cankoz_pre_lyr.[ch]. HAL-модуль называется c4lcankoz_hal.h.
  • 10.02.2007: вся поддержка CANGW переехала из ~/work/cangw/, где она создавалась, в cx/src/progrsms/server/drivers/canbus/gw/.
  • 13.02.2007: вытащил все специфичные для ppc860 махинации в drivers/ppc860_rules.mk, который может использоваться ВСЕМИ подсистемами на основе этого процессора; сейчас это CANGW и CM5307/PPC, а может еще добавиться BIVME2.
  • 01.06.2009: Надо бы завести "консольный интерфейс" в canserver'е -- например, чтобы можно было посмотреть, какие драйверы сейчас активированы, или послать broadcast-пакет 0xFF.

    Самое простое -- слушать еще один порт (8081?), и с него принимать просто текстовые строки-команды, и обратно отправлять тоже текст. Так что можно будет "заходить" при помощи "telnet ppc-... 8001". Реализация -- прямо на cxscheduler'е, читать хоть по-байтно.

    02.06.2009: да, сделал.

    • Добавил менеджмент "консолей", практически копированием из менеджмента устройств.
    • Неудобством является отсутствие функции "fdprintf()" (нестандартизованная glibc'шная dprintf() не в счет), так что пришлось слабать халтурненькую Limited_fdprintf(), с буфером на 1000 символов.
    • Слушает оно тот же порт, что и основной canserver'ов, +1.
    • Сам парсинг сделан тривиально, "на коленке" -- полученная строка тупо strcmp()'ится с возможными командами в одном if()/elseif()/else. Так что ни "?" не поддерживается, ни параметры у команд.
    • Предусмотрены также команды выхода -- чтобы не приходилось прибегать к telnet'овому Ctrl+]. Это bye, quit, logout и Ctrl+D.

    16.06.2009: слегка поменял парсинг -- оно пропускает isspace() в начале строки, и ничего не делает с пустыми строками и строками, начинающимися с '#' (это чтобы можно было рисовать целые "сценарии" для скармливания консоли, с комментариями внутри).

    Команда list стала чуток умнее: она для каждого драйвера выдает также информацию от cankoz_pre_lyr'а -- line,kid,devcode, для чего в дополнение к CanKozSend0xFF() создан еще один публичный вызов CanKozGetDevInfo(). И, кстати, она проверяет код возврата, так что для не-CAN-драйверов печатает только "magicid busid drvname".

    Также добавлена команда date, отдающая strcurtime().

    16.06.2009: еще мыслишка: а не сделать ли аналогичный интерфейс в обычном rrund?

    Нет, не сделать, ибо:

    • Сильное усложнение кода -- придется иметь таблицу запущенных-сейчас драйверов, да еще и помнить IP запустившего, плюс в 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'.)

    Идея, конечно, красивая, но использовать её скорее не стоит:

    • Во-первых, получается смешение разных сущностей, и появляется возможность ошибок (если remdrv вдруг пришлёт пустую строку, то попадёт в консольный интерфейс, к которому он никак не готов).
    • Во-вторых, при этом юзер сразу по запуску telnet'а НЕ будет попадать в консоль, пока не нажмёт Enter, что неприятно.
  • 01.06.2009: Также надо бы иметь и "обратный" канал -- чтобы туда могли лететь все сообщения типа NOT_IN_DRIVER.

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

    Реализовать это можно довольно просто: специальный "псевдодрайвер" (реально -- пустышка!), который не привязывается ни к какому адресу на CAN-линии, а просто при его наличии менеджер коробочки гонит ему все сообщения, которые драйвером на стороне сервера просто отдаются -- стандартным образом! -- системе протоколирования. И таких "протоколяторов" одновременно может быть активировано НЕСКОЛЬКО -- чтобы каждый из серверов, пользующихся услугами данного CANGW, получал бы всю инфу.

    Это автоматом решит и проблему "кому отдаётся NOT_IN_DRIVER-сообщение в-сервере" -- там можно активировать этот же драйвер-пустышку, и все такие сообщения будут идти ему.

    02.06.2009: замечание -- ежу понятно, что вместо DoDriverLog() надо будет в cankoz_pre_lyr.c вызывать какую-нибудь свою функцию, которая при magicid>0 вызовет обычный логгинг, а при NOT_IN_DRIVER -- "отдаст" всем псевдодрайверам, если таковые имеются.

    В canserver_drvmgr.c уже и так имеется "интеллектуальный" выбор файлового дескриптора -- вот будет нечто как бы похожее. Плюс -- при таких отдачах НЕ НАДО проверять по logmask.

  • 16.06.2009: делаючи поддержку табличной работы в cac208, обнаружил отсутствие реализации 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".

  • 17.06.2009: и, кстати, точно так же отсутствует и реализация GetCurrentCycle().

    Её при надобности сделать будет несложно, но слегка муторно: при входе в ЛЮБОЙ метод драйвера надо будет запоминать его magicid в некоей глобальной переменной active_magic, и в функции брать devinfo[active_magic].curcycle -- благо, получение номеров от сервера и сохранение их per-device уже есть.

  • 23.04.2012: поскольку уже давно готов remsrv-based наследник в nppc_canserver/, то директорию ppc_canserver/ ликвидируем (чтоб не путала).

    Кроме того, поскольку драйвер cangw давно изведён в пользу remdrv, да и вообще это всё скоро перебазируется в v2hw/, то в этот раздел более ничего писаться не будет.

    (Возможно, единственный пункт, который продолжит существование -- про «"обратный" канал» от 01-06-2009.)

    29.04.2012: да, и вся cx/src/programs/server/drivers/canbus/gw/ также грохнута вместе с gw/.

bivme2:
  • 02.09.2011: поддержка bivme2 будет жить прямо в основном дереве, в drivers/bivme2/, чтоб, аналогично CAMAC-драйверам, отдельные проекты могли пользоваться функционалом из центрального репозитария.

    Технологически это должно быть похоже на драйверы cm5307_ppc, и содержать оно будет следующее:

    • Инфраструктура сборки -- вкл. LocalRules.mk.

      Тут мы никаких абстрактных драйверов поддерживать (пока?) не будем, так что всё сравнительно просто.

    • Модуль доступа к VME-шине bivme2_io.[ch] (аналог cm5307_camac.[ch]).
    • Реализация основного цикла, bivme2_dbody.[ch] -- на основе remdrvlet_[ch].h.

      Ситуация облегчается тем, что прерывания видны по select(), а не сигналами.

    • Утилита bivme2_test.

    19.12.2011@Снежинск-каземат-11: кстати, в drivers/bivme2/Makefile отсутствовала проверка на тему CPU_X86_COMPAT. Добавил.

  • 02.09.2011: приступаем к реализации.

    02.09.2011:

    • Сделаны LocalRules.mk и традиционно крохотный Makefile.
    • Чтобы прекратить плодить make-код для локальной сборки библиотек из misc/ и useful/ (с симлинкованием их исходников), помещаем его в drivers/x_libs_rules.mk.

      Помимо использования тут, на него уже переведены cm5307/common/CommonRules.mk и can/gw/ppc_canserver/Makefile.

    • Начат модуль доступа к VME-шине -- bivme2_io:
      • Из мамкинского powerpc-sdk-linux-v4.tgz, директория ppc860/linux/apps/bivme2_direct_lib/, скопированы libvmedirect.h и libvmedirect.a.
      • Также из SDK взят требующийся для них и ранее отсутствовавший sched.h -- добавлен в tools/cm5307-ppc/include/ (и в work/x-compile/ppc860-linux/ тоже).
    • На скорую руку сварганены 4-строчный bivme2_dbody.c и bivme2_dbody.h -- этот сделан практически копированием cm5307_sl_dbody.h.

    02.09.2011: замечание насчёт bivme2_io: базовая идея -- сделать портабельный API доступа к VME, который бы можно было реализовать и для других контроллеров; в идеале -- чтоб могло работать по технологии layer'ов, с несколькими контроллерами сразу. Пока же даём этому API имя/префикс bivme2_io чисто для простоты, чтобы не заморачиваться.

    06.09.2011: приступаем к начинению bivme2_io.

    Выбрана общая концепция -- работа аналогично pci4624, т.е.:

    1. Драйвер регистрирует себя через bivme2_io_open(), получая в ответ handle, который потом указывается всем операциям В/В.
    2. Соответственно, bivme2_io хранит в себе массив структур (адресуемых по handle), и сможет работать с несколькими устройствами параллельно.

      Эта возможность понадобится, если придётся вместо нынешней cm5307-подобной архитектуры "каждый драйвер живёт в отдельном процессе" использовать cangw/ppc_canserver-подобную "все драйверы живут в одном процессе-сервере". А понадобится сие в случае необходимости вешать на одно IRQ несколько устройств (с разделением по векторам) -- поскольку мамкинский интерфейс /dev/vmeiN поддерживает не более одного процесса на каждую линию IRQ.

    3. Для РАЗрегистрации служит bivme2_io_close().
    4. При регистрации указывается базовый адрес, и всем последующим операциям чтения/записи VME-шины указывается уже лишь смещение, а базу они прибавляют сами (это как бы аналог PCI'ных BAR'ов).
    5. Плюс -- указывается "address modifier" (am).
    6. Также указывается номер IRQ, за которым надлежит следить, и обработчик, который надлежит вызвать.

      В целях упрощения обработчику передаётся ТОЛЬКО devptr, безо всякого privptr'а. Тут этого будет вполне достаточно (драйвер, работающий с несколькими IRQ -- просто зарегистрирует несколько разных обработчиков).

    7. В/В осуществляется функциями-переходниками к libvmedirect'овским libvme_{read,write}_{a16,a24,a32}_{byte_word,dword}() -- bivme2_io_{a16,a24,a32}{rd,wr}{8,16,32}().

    Отличие -- что для упрощения делается покамест БЕЗ layer-alike технологии, на прямых вызовах.

    Пока что там будет очень ограниченный функционал:

    • Вместо массива структур пока одна-единственная -- так мы опускаем махинации с поиском/выделением, а также со сложными разборками с возможными множественными IRQ (в т.ч. разделяемыми).
    • Операции В/В -- только bivme2_io_a16wr16() и bivme2_io_a16rd16() (поскольку для козачиных ничего другого не требуется).
    • Работу с дескрипторами -- для IRQ требуется слушать /dev/vmeiN на чтение -- оно пока делает напрямую через cxscheduler.

    13.09.2011: попробовал/проверил на vadc16: всё и хорошо, и плохо:

    1. В/В -- работает, драйвер прекрасно задаёт режимы работы и читает измерения.
    2. IRQ же -- НЕ пашут. Тут была целая история:
      1. Сначала выяснилось, что у Мамкина в bivme2_to.pdf нигде не сказано, что select()'ом надо слушать дескриптор на EXCEPTIONS (3-й набор), а не на чтение (1-й).
      2. Потом анализ исходника мамкинского же драйвера прерываний -- vmei.c -- показал, что он некорректно обращается с концепцией "poll": в самом начале vme_poll() СБРАСЫВАЕТ "признак irq" -- .vector=-2. Таким образом, select() для некоего дескриптора можно вызвать лишь единожды -- второй подряд вызов ВСЕГДА вернёт "таймаут".
      3. Затем прогон драйвера под strace показал, что даже и не в этом дело -- vadc16, запрограммированный на генерирование irq, реально НИКОГДА прерывания не генерит:
        1. select() НЕ вызывается "по несколько раз", но он никогда НЕ возвращает готовности vmei'шного дескриптора.
        2. Просмотр /proc/interrupts показывает, что никаких прерываний НЕ происходит -- кроме тех, что связаны с Ethernet.
        3. Блок начинает дико тормозить -- печать карты каналов идёт с видимой задержкой, и, похоже, тормозит просто обращение к блоку по шине.

    P.S. Проверить удалось далеко не сразу -- блок никак не запихивался, имелся дребезг контактов, происходили чудеса.

    14.09.2011: сделал ВСЕ функции В/В. Для краткости и простоты они определяются макросом DEFINE_IO().

    15.09.2011: в bivme2_io_open() добавлен параметр space_size -- размер занимаемого адресного пространства. Это на будущее -- когда будет layer, чтоб он контролировал неперекрытие.

    20.10.2011: после поднятой мною бучи (послал письмо с результатами Козаку и Мамкину, Козак передал Куперу, тот скомандовал Мамкину разобраться) история с IRQ счастливо разрешилась. Мамкин провёл расследование с осциллографом, и оказалось, что были 2 проблемы:

    1. Некорректная работа с прерываниями в vmei.c -- он сам затирал свою же информацию.
    2. Баг в козачиной документации -- бит b2 не разрешает генерить "прерывания после каждого измерения (1)", а разрешает(1)/запрещает(0) прерывания вообще.

    Итого:

    1. С модифицированным мамкинским vmei.o и с взведённым b2 (KCMD_START_FLAG_EACH_IRQ) всё работает как надо.
    2. Надо будет переделать vmei.c по-человечески -- он в принципе некорректно работает с точки зрения poll()/select(), чтобы Мамкин выпустил модифицированную прошивку.

      Заодно надо будет и в CAMAC-драйвер ввести поддержку poll/select.

    20.10.2011: дополнительная "радость": поскольку libvmedirect работает через mutex в ОЗУ, то при асинхронном гроханьи драйвера (хоть по kill, хоть по Ctrl+C -- если rrund запущен руками) mutex оказывается заблокирован навечно, и другие программы лишь до второго пришествия вызывают sched_yield().

    Т.к. сделать с этим ничего нельзя -- ни atexit(), ни даже ловля SIGINT нас не спасут -- то остаётся только НЕ пользоваться kill/Ctrl+C, а грохать соединение.

  • 02.09.2011: для проверки работы создаваемой инфраструктуры делаем пару драйверов -- для козачиных VADC16 и VDAC20. Жить они будут в qult/drivers/bivme2/.

    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 можно считать рабочим.

moxa:
  • 08.12.2011: несколько дней назад начал реализацию.
    • Делается оно сейчас исключительно в интересах КШД485, ...
    • ...потому живёт пока в qult/drivers/moxa/.
    • Сама софтина-сервер названа cxv2moxaserver.
    • Специфика сборки определяется в moxa_rules.mk. Он вышел сравнительно маленьким -- в этой системе кросс-сборки разных глюков немного, они касаются почти исключительно линкера.
    • К сожалению, под RedHat-7.3 оно уже не работает. Точнее, работает почти всё -- кроме collect2...

    08.12.2011: да, ТУТ всё сделано, на Star@CentOS-5.7 оно собирается, проверено также на самой Moxa -- работает, драйвера и правильно подхватываются, и функционируют (вопрос уже в самой работе с RS485/КШД485).

Access-modules API:
Access modules:
Старый сервер -- cxd+cx-porter+cx-server:
Общее:
  • 21.02.2005: столкнулся сегодня с потребностью иметь ключик, аналогичный X-серверову флагу "NoTrapSignals". Понадобилось, когда cx-server повадился (из-за sim_drv) вылетать по SIGSEGV, а, как оказалось, натравить gdb на core-файл не удастся -- оно само перехватывает сигналы и в core не падает...

    Кстати -- такой же ключик понадобится и в cxsd! 15.06.2014: да, давно сделано.

    24.06.2005: окончательно достало -- вводим такой ключ. Хроника:

    • Ключ "-D" -- "Don't trap signals", cxd-параметр -- option_donttrapsignals, environment -- $DONTTRAP.
    • Поскольку в cxd.c первой же строчкой перехватывались сигналы -- перенес ParseCommandLine() в начало.
    • Вставил передачу child'ам через environment (как задрало!!!).
    • Оба вызова InterceptSignals() -- теперь условные.

    Короче -- "done".

  • 07.07.2005: задолбали валящиеся в DBG1 сообщения, связанные с передачей дескриптора от привратника к серверу и менеджеру ("").

    07.07.2005: Закомментированы -- шестью слэшами.

  • 27.01.2006: узрел пренеприятнейшую вещь: если указывается log-файл, который невозможно открыть на запись (примеры -- /dev/pts/30, /zzz), то имеем SIGSEGV (причем хитрый -- но об этом следующий пункт).

    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".

    Так, некоторые комментарии:

    • BTW, для справки: вплоть до недавнего введения 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.

  • 31.01.2006: что еще неприятно -- cx-porter-то сразу падает, поскольку у него перехват сигналов (PrepareClean()) стоит ПОСЛЕ StartPart(), приводящей к падению, а вот cxd -- улетает в бесконечный цикл, когда он, например, получает SIGINT, пытается о нем сообщить, в результате имеет SIGSEGV, о котором также пытается сообщить, и т.д.

    Вопрос: а почему появление SIGSEGV не блокирует дальнейшие его проявления? Что -- надо SA_ONESHOT указывать?

    25.11.2013: в связи с переходом на однопроцессность (да и вообще сильной модификацией потрохов за эти почти 8 лет) ставим "obsolete", хотя де-юре проблема так и не была исследована.

  • 14.02.2012: ох до чего ж достала эта трёхпроцессная архитектура!!! Может, как-нибудь всё-таки изготовить ОДНОпроцессный сервер -- с использованием всех компонентов обычного cx-server, но заменив взаимодействие с cxd и cx-porter на локальный аналогичный функционал? (Ага, еще и на cxscheduler с fdiolib перейти -- тогда точно проще будет населить daemon/.)

    16.02.2012: если это и делать -- то только после перехода на GeneralRules.mk, CX_CACHECTL* и SLOTARRAY сотоварищи; просто чтоб копировать в новый сервер уже полностью отлаженные куски, а не дублировать сырость.

    И, строго говоря -- не так это много работы, несколько дней (при отключенных телефонах :-D).

    25.11.2013: ну уже полгода как на нём всё и пашет.

cxd:
  • 05.04.2004: так, замечание на будущее: обнаружил, что почему-то в cx-console отображается странный prompt -- точнее, вместо него отображается строка "(hostname)\n\n". Быстро разобрался...

    05.04.2004: дело в том, что там формат ответа на consolelogin-запрос -- "issue\0prompt". А когда я вставлял приколы с отдачей в issue версии, то "по привычке" оставил все добавления текста как "p+=sprintf(...)+1". Вот оно и вставляло лишние '\0'. Правильная же реализация (как я и сделал) -- "+1" только в ПОСЛЕДНЕМ sprintf'е, ПЕРЕД prompt'ом.

  • 31.10.2004: надоело, что pid-файлы (cxd-*.pid) из /var/tmp/ исчезают. Переходить на /var/run/, что ли?

    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: да, давно сделано.

  • 28.02.2005: обнаружился мелкий глюк: при запуске с ключом -n -- norun -- в syslog падает строчка на тему "cx-master: exit(0) errno=...", что, вообще-то, не к месту.

    28.02.2005: делов-то было -- уставить normal_exit=1.

  • 23.01.2006: достало, что долго-не-просматриваемые .log-файлы в /var/tmp/ так же исчезают, как раньше .pid-файлы.

    (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().

    • Плюс -- в cxlogger нужен будет вызов "переоткрой все log-файлы" -- тогда легко можно станет и logrotate поддерживать.

      Вопрос только -- по какому сигналу это делать? Явно напрашивается 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'а прошелся по /var/tmp/ почти сразу после запуска сервера.
    • Поскольку чтение выполняется в cxd_manager'е, который обычно никому не нужен, то тот и висел целый час (3600 секунд), прежде чем обновить atime.

      За этот час tmpwatch успел сделать свою грязную работу:

      • server#45 запустился в 11:12:03 (добыто из deleted-файла /proc/PID/fd/7), а
      • "anacron[2572]: Job `Cron.daily' terminated" в 12:09:32 (из /var/log/cron).
    • Поскольку log-файлы прошлый раз читались аж в мае, то они оказались слишком старыми и стёрлись.
    • А .pid-файлы были свежесозданы, поэтому у них atime был свежим (равным create-time).

    Решение простО -- чтение перетащено из конца цикла в его начало, так что теперь atime будет обновляться прямо сразу при запуске.

    07.11.2013: а в v4 вместо регистрации таймаута просто сразу вызывается PerformAccess() (который сам себя зарегистрирует).

    08.04.2019: в продолжение вопроса: ведь сейчас в Linux повсеместно применяется опция монтирования "relatime", при которой atime обновляется лишь в случае, если предыдущее значение atime было давнЕе mtime/ctime -- это сделано в интересах почтовиков (чтобы видеть, что запись в mailbox произошла позже последнего чтения -- значит, новые письма). И как при этом работает наш механизм "периодически читать, чтобы atime обновлялось"?

    Ответы нашлись в документации:

    • Man-страница mount(8):
      ...since Linux 2.6.30, the file's last access time is always updated if it is more than 1 day old.
    • Дополнительная информация у RedHat (по-русски!): "3.7. Оптимизация доступа к дискам relatime":
      ...Наконец, с помощью параметра загрузки relatime_interval= можно изменить время, по истечении которого система обновит данные atime файлов. Значение по умолчанию -- 86400.

    Смысл ясен, и он совпадает с нашими потребностями: сделать так, чтобы tmpwatch имел смысл (при "простом" варианте relatime оно работать не будет -- после первого чтения-после-изменения atime так бы и оставалось на месте, и через 10 суток файлы бы становились "старыми" и стирались).

  • 16.12.2007: еще исправление в cxdlib: надо использовать 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".

  • 08.06.2010: поднадоела ситуация, когда cx-server может скопытиться, оставив висеть живые cxd с cx-porter'ом -- получается, что вроде сервера и нету (так что cx-starter не позволяет его убить), но и перезапустить его не удаётся -- порт-то занят.

    Пожаловался на это Роговский -- у них почему-то из-за отключения электричества что-то глюкануло.

    Понятно, что с однопроцессным сервером такого не произойдёт, а пока можно в 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.

cx-porter:
cx-server:
  • 06.06.2004: за-#if-0'ил все содержимое usefullib.[ch], поскольку теперь в сервер линкуется libuseful.a, включающая идентичную функциональность. Соответственно, в файлах-жертвах добавлена строка #include "timeval_utils.h".

    Через один BACKUP/STABLE надо будет usefullib.[ch] вообще удалить.

    23.08.2004: удалил, ежели что -- STABLE/cx.20040802.

  • 13.06.2004: случайно (sic!) обнаружил, что при приходе двух (или более) подряд запросов на запись в канал (в течение одного цикла) не-первые просто отбрасываются -- т.е., самый первый запрос отправляется драйверу, а потом, покуда 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".

  • 09.07.2004: полуслучайно обнаружил, что в cx-server_channels.c::CheckFork() была халтурная проверка на номера каналов в CXC_{GET,SET}VSET -- реально проверялся только первый канал в наборе, поскольку отстутвовало "p++".

    09.07.2004: пофиксено -- засунул и p тоже внутрь цикла.

  • 03.08.2004: сходу: по просьбе Феди-jr. вставил, чтобы можно было заставить сервер валить в лог запросы на запись.

    03.08.2004: для начала сбросил умолчательное значение verbosity с 9 до 5. Затем добавил в cx-server_channels.c вызовы logline() с уровнем 9.

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

    Видимо, самое для начала простое -- это ввести ключик типа "-Vcategory=N", где "category" -- это некое название предопределенной категории (аналогично драйверным), а N -- значение для нее. И соответствующие вызовы logline*() будут указывать что-то типа "loglevel_category" вместо предопределенного числа.

  • 30.10.2004: ой-е... Обнаружился старый (надо узнать, с какого времени) ляп с большими каналами: в ответ на запрос большого канала ВСЕГДА возвращался пакет с NumData=0.

    30.10.2004: как выяснилось, в cx-server_bigc.c::FillBigcReplyPacket() после инициализации ответного пакета -- InitReplyPacket() -- НЕ уставлялось в нем поле NumData, как это делается для обычных каналов.

    Сделал -- теперь оно уставляется в то, что лежит в cp->lastreqbuf->NumData.

    Вообще-то достало -- надо переходить на унифицированную, СОВМЕСТНУЮ обработку нормальных и bigc-fork'ов. Вот только -- хлопотно это в современной архитектуре, нужна fdiolib, следовательно -- cxsd...

    И еще, возникли некоторые вопросы по ходу разбирательства:

    1. Что это за имечко -- "Zdata"? Видимо, когда-то оно было предназначено под снос, но в bigfile*.html никаких упоминаний о том нет.
    2. Что за концепция с "copy_src" и "lastreqbuf"? Где это еще используется, и насколько это осмысленно?
    3. Поле CxHeader.Status -- оно где-нибудь для чего-нибудь применяется? Вроде в bigfile.html упоминается про некое CXS_ENDIANID, но -- корректно ли делать ТАК? Или лучше использовать Res1/Res2?

    25.05.2005: комментарии насчет вышеупомянутых вопросов, по тем же пунктам:

    1. Черт его знает, зачем я обозвал поле "Zdata" -- оно явно было предназначено для того, чтобы легко доступаться к точке, с которой начинаются данные. Что, собственно, и делается в cxlib.c и cx-server_bigc.c.

      Похоже, это было зачем-то сделано при унификации DataFork и BigcFork -- видимо, чтобы были отличающиеся имена. Ну и фиг с ним :-).

    2. Скорее -- "что за тупой вопрос про концепции copy_src и lastreqbuf" :-)

      "lastreqbuf" используется cx-server_bigc.c для хранения последнего полученного запроса. Чтобы

      1. Хранить там данные, на которые ссылается его bigc-request (args и data).
      2. Брать оттуда параметры для заполнения ответного пакета -- в частности, количество fork'ов в запросе (сейчас -- реально всегда 1) и номер (в будущем -- номерА) больших каналов.

      А "copy_src" -- всего лишь используется для более удобной адресации внутрь этого сохраненного пакета.

    3. Насчет CxHeader.Status -- все очевидно, кроме вышеупомянутого CXS_ENDIANID оно используется еще и в console-протоколе, флажки CXS_PRINT и CXS_PROMPT. Всего-то надо было заглянуть в cx_proto.h :-) И -- ответ "да", это поле применяется, и оно применяется абсолютно правильно, не надо ничего никуда менять. (Ну -- согласен, что для endian-id несколько кривовато, но зато просто.)

    Короче -- поскольку и заявленная в заголовке проблема разрешена, и на вопросы отвечено, то -- "done".

  • 07.11.2004: кстати, еще один прикол с "записью": ведь при "-wY" на каналы записи всегда отдается age=0 (этим занимается FreshFlag()). Но если в канал была произведена запись, а значение еще не вернулось -- то надо отдавать Tcurrent-Twrreq.

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

    07.11.2004: вообще-то не вполне понятно, как именно надо делать корректнее. Некоторые соображения:

    • Вроде бы -- надо вводить поле "c_wr_time[]"...
    • А с другой стороны, уж шибко красиво (кажется, но это опасно и подозрительно) легло бы все, если б время записи проставлять прямо в c_time[].
    • И вообще -- а какой, собственно, возраст надлежит возвращать, когда канал помечен как c_wr_req[]=1?
      1. С момента команды записи? -- Криво, будет выглядеть, словно оно отработало, а это еще не так.
      2. С момента последнего реального чтения -- c_time[]? -- Тоже криво, ибо значение будет "вдруг", с 0, становиться очень-очень старым.
      3. С момента команды-записи минус 1? -- Это, вроде, выглядит наиболее безопасным. "Выглядит"...

    Замечание 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. Это плохо!

    Есть две альтернативы:

    1. Оставить все как есть -- тогда такой канал посинеет после первой же попытки записи.
    2. Придумать-таки, как же умудряться "усинить" такой канал... Как? 25.05.2005: никак не надо!

      (Чем-то мне это напоминает игрища с 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(), но "поумнее":
      1. Она будет возвращать код -- ЧТО делать (ничего/read/write); тем самым мы решим проблему "что делать при попытке писать в канал чтения".
      2. И именно ОНА будет прописывать 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: да, сделано -- см. замечание за сегодня.
    • А в ReqReadOfWrChans ввести также прописывание в 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:

    • Дело было в nkshd485_drv_common.c -- реакция на запись 1 в канал "DO_RESET" (попытка сделать сброс и реинициализацию) -- при глюках с линией (кстати, откуда они там берутся -- загадка).
    • При записи 1 оно почему-то падало -- причём молча, в логи ничего не попадало.
    • Под gdb стало видно, что бесконечно вложенно вызывается цепочка SetDevState(), ReviveDev(), ReRequestDevChans(), SendChanRequest(), nkshd485_rw_p() -- и далее по кольцу.
    • Видимо, происходило следующее:
      1. При переходе в OPERATING оно пыталось повторить все запросы, что приводило к повторному вызову этой же записи, и так до бесконечности, пока стек не заканчивался.
      2. После окончания стека программа снималась уже молча, БЕЗ уведомления её.
    • Решение -- ReturnChanGroup() переставлено в начало обработки, ДО щелканья состояниями.
    • Но вообще, конечно, ситуация неприятноватая, общеидеологически.

      Где бы и как такие заскоки разводить?

      • В самом драйвере -- можно (выставлять в privrec'е флажок-блокировку, при взведённости которого ничего не делать); но нет смысла, и нынешнее решение работает.
      • А на уровне cxsd_channels -- фиг его знает, как (флаггировать каналы, на которые сейчас вызвано do_io -- избыточное усложнение).
      • Видимо, проще оставить нынешнее решение.
  • 05.01.2005: сходу -- удалены старые, за-#if 0'енные куски кода:
    • cx-server.c::Run() -- древний вариант, не поддерживавший драфверных таймаутов.
    • Из cx-server_dbase.c::SimulateDatabase() -- первый, не-psp'шный вариант парсинга ";log=", плюс старую локальную переменную cur_magic (ныне это имя напрямую смотрит на numblocks).
    • cxsd_driver.[ch]::TimedConnect() -- оно никогда не использовалось, и полагалось на ныне удаленную функцию timed_connect(). (В будущем-то эта работа вообще будет выполняться при помощи cxscheduler'а.)
  • 06.02.2005: имеется давняя проблема, что при кэшировании чтения из каналов записи НЕ производилось первоначальное вычитывание при старте сервера. В свое время была сделана функция RequestReadOfWriteChannels(), выполняющая предвыборку, но при этом начинают сходить с ума глючные отокаревские драйвера (детали см. в bigfile.html за {13,14,16}.06.2003). Так что ее пришлось отключить.

    А что, если ввести еще один ключик, указывающий наличие (по умолчанию -- отсутствие) олеговых багливых драйверов? Например, -T{y|n}?

    06.02.2005: да, ввел парсинг ключика "-T" (скопировав из парсинга "-w") в option_otokarevs, с последующей передачей ее через $OTOKAREVS в cx-server'ову OtokarevsBuggyDrivers.

    И теперь "return" в начале функции предвыборки выполняется не безусловно, а только если указан флаг "-T1".

    Что теперь надлежит сделать:

    1. Прошерстить все blklist-файлы и понять, в каких серверах присутствуют олеговы олигофрены. И для них в srvparams.conf указать "-TY". Заодно -- пересмотреть необходимость наличия флагов "-wN".
    2. Проверить бы содержимое 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".

  • 08.04.2005: мелкие укорректнивания в cx-server_dbase.c::CheckBlockInfo():
    1. Заменил условие проверки с *_nsegs!=-1 на *_nsegs>=0.
    2. Вставил перед memcmp() "ограду" *_nsegs!=0 -- а то раньше было опасно в драйвере указывать "нет таких каналов" как просто *_nsegs=0.

    16.06.2005: это где это там была ограда?! А вот и фиг -- забыл я тогда ее там изготовить!

    Вставил.

  • 08.07.2005: вставил в AddBlock()'ов логгинг "добавляю то-то с такими-то параметрами" еще и blk_name.
  • 22.08.2005: старые драйверы -- например, отокаревские одреночные -- в случае своего незапуска возвращают проосто -1, и фиг что поймешь -- каналы болотные, и все.

    Надо иметь хоть какой-то логгинг этого события.

    22.08.2005: сделал. Валит короткое сообщение.

    А вообще -- в будущем, когда драйверы будут возвращать состояние (offline/notready/operating), такое сообщение надо будет валить ВСЕГДА. Причем, будущий-в-CXv4 аналог InitBlock()'а должен будет этот код возвращать "вверх".

  • 22.08.2005: надо в момент "застреливания" драйверов все-таки хоть что-то в log выводить -- а то драйверы в системе subharmonic повадились копытиться, а отчего, когда -- нифига не поймешь.

    22.08.2005: да -- теперь TermBlock() печатает сообщение о себе. (Хотя у can-драйверов subharmonic'а проблема была в другом. :-)

  • 22.08.2005: хочется все-таки иметь в выводе "scan"'а время последнего изменения состояния драйвера.

    22.08.2005: вставил -- теперь, если "scan" вызывается с параметрами (без разницы -- просто nargs>1), то оно сразу за символом статуса пишет еще и время в формате "@DD.MM.YYYY,HH:MM:SS".

    30.01.2006: собственно -- теперь, когда есть strcurtime(), сам Б-г велел перевести и "scan" на нее.

    Э-э-э... Только не strcurtime() -- та-то отдает ТЕКУЩЕЕ время... Так что -- в первую очередь в этих целях ввел stroftime(). Вот на нее и перевел -- с разделителем "_".

  • 31.03.2006: ну вот, доигрался -- у CGVI8 количество сегментов main-каналов достигло 11, превысив былой лимит в 10...

    31.03.2006: проблем-то особых нет -- ну увеличил до MAX_MAIN_SEGS=20. Но на будущее надо б это запомнить (а еще когда обычные и большие каналы будут перемешаны...).

  • 26.02.2007: еще некоторые мысли-потребности по симуляции данных, по результатам изготовления ndbp:
    1. Стоит упростить процедуру перевода ОДНОГО устройства в симулируемый режим:
      • Ввести per-device-флаг d_simulate[], работающий через "||" с MustSimulateHardware.
      • При указании '-' перед file в .lst-файле включать этот флажок, НЕ пытаясь грузить драйвер. Варианты реализации:
        1. Отдельная проверка d_simulate[] в SendChanRequest() и SendBigcForExecution().
        2. Просто использовать некую builtin-метрику "simulator_driverrec" (тогда и d_simulate[] не нужен).
    2. Для симулируемых таким образом устройств надо хранить массив параметров для каждого bigc, куда помещать args[] и откуда возвращать info[].

      Хранить, видимо, в privrec'е, в котором первые bigc_count int'ов будут содержать смещения, а далее -- просто последовательно массивы параметров для каждого bigc.

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

    Конкретно -- чтобы осциллограммы не присылались чаще 5 раз в секунду. Реже, если частота запусков меньше -- пожалуйста, а чаще -- не надо: человеку и 5Гц хватит, а вот лишнее мерцание и напряг процессора -- ни к чему.

    И делать это ограничение надо именно в сервере, поскольку клиенту такие времена считать -- очень не с руки.

    07.05.2007: сходу приходит в голову одна идея: переделать флаг immediate в параметр, и в нем передавать желаемые герцы. Возникающая проблема -- что 1 должно означать, как и сейчас, "столь быстро, сколь возможно", а не 1Гц.

    И еще вопрос, похитрее -- а КАК именно серверу делать этот лимит? Ведь клиенты-то не подписываются, а именно запросы присылают. И если ответ некоторое время не отправляется, то и очередной запрос не придет, и измерение проделано не будет. Так что -- при freq!=fastest просто ИМИТИРОВАТЬ холостой запрос от клиента?

    10.05.2007: подумал-подумал -- неа, как-то хреновато вышла бы такая реализация, поскольку серверу не очень-то понятно, пропустивши отсылку результата клиенту -- КОГДА все-таки данные отправить? А что, если один раз пропустили -- а потом-то откуда возьмется "импульс", который бы вызвал проверку слать/не слать?

    И опять же -- КАКИЕ данные надо отправлять? И с КАКИМИ параметрами померянные?

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

    Пока же -- пусть этими махинациями с ограничением частоты занимается клиент, а эту секцию -- делаем "withdrawn".

  • 15.10.2008: при очередном анализе логов сервера (system.log), при очередной проблеме на linac1, вылезло, что все-таки ОЧЕНЬ неудобно выдается там информация. А конкретно:
    1. Отсутствует magicid блока -- потом узрел, что он пишется при FillChanProps(), но там неудобно.
    2. BusID отображается просто как int, и для CAN-устройств (где реальный формат -- "line,dev") это дико неприятно -- приходится вручную делить на составляющие байты.
    3. Для cm5307-устройств просто имени драйвера -- это всегда "cm5307" совершенно недостаточно, нужно еще и auxinfo.

    15.10.2008: фиксим проблемы...

    1. Теперь magicid выдается в квадратных скобках перед остальными параметрами. Из FillChanProps() и FillBigcProps() убрано.
    2. BusID теперь выдается "интеллектуально" -- в предположении, что это всегда именно от 1 до 4 байт. Сам алгоритм выдачи, конечно, диковатый -- для соответствия диковатому способу парсенья и упаковки этих байтов.
    3. Добавлена в конец печать auxinfo, если оно не пустое.

    И, кстати, строчка logline() в AddBlock() была с половинным отступом -- типа временный логгинг. Теперь она переставлена в нормальное положение.

  • 19.08.2009: добавлена дополнительная удаленная/протокольная диагностика на отлуп CXT_EINVCHAN.

    19.08.2009: исходная проблема -- при CXT_EINVCHAN обычно просто выдаются ругательства "reply is 1006..." (cxlib) и "Invalid channel number" (cda). Но вот как понять, КАКОЙ канал её смутил -- хбз (и в данном конкретном случае эту -1 долго бы искал).

    Поэтому введено усовершенствование: сервер при ошибке также передаёт и НОМЕР плохого канала.

    • В протоколе под это использовано поле CxHeader.Res2
    • Для позможности передавать и параметр в connlib'е создана доп. функция -- TellClientWParam() ("WParam" -- "with parameter").
    • В сервер покамест вставлена такая расширенная диагностика в cx-server_channels.c и cx-server_bigc.c.
    • А cxlib при печати сообщений о неожиданном коде пакета также выдаёт и значение этого параметра -- в обработчиках DATA_SENT, SUBS_SENT и BIGC_SENT.

    С этой инфраструктурой ошибка была найдена очень быстро.

    А на будущее -- надо предусматривать (и в протоколе, и в библиотеках), чтобы кроме кода ошибки могло также передаваться и некое "описание" -- либо текстовое (как сделано в psp-плагинах), либо числовое (как тут). И это должно быть НА ВСЕХ УРОВНЯХ -- дабы именно сама программа могла получать эту инфу (а не как сейчас, когда cxlib печатает ругань).
  • 02.09.2009: была идиотская проблема: блоки больших каналов считались неправильно. Например, при указании 8*...,1*... она прописывала второй блок со смещения 1 вместо 8.

    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: Сделать бы всё-таки, чтоб cx-server КОРРЕКТНО реагировал на номера каналов вне [0...numchans), например, отдавая значение 0x7FFFFFFF и rflags=0xFFFFFFFF. А то нынешнее CXT_EINVCHAN уж больно неудобно -- сделал ошибку в одном месте, и оно считай что не коннектится к серверу вовсе, и НИФИГА не понятно.

    13.02.2011@Снежинск-каземат-11: вылезло, когда переделывал ручку в CALCED, phys=-1 уже сделал, а DIRECT по невнимательности оставил. И -- хрен чё поймёшь...

    Конечно, реализовать сие будет не так тривиально, поскольку придётся вводить лишние условия/сложности в MarkRangeForIO() и FillDataPacket() (зато CheckFork() радикально упростится -- останутся только проверки на OpCode и Count).

    14.06.2014: да, надо, ибо реально поддостало -- малейшее добавление на стороне клиента, еще не отраженное в сервере, делает клиента нерабочим...

    Муторно, но оно того стоит.

    И -- надо постараться возвращать на такие каналы флаг CXCF_FLAG_NOTFOUND. Он хоть и cda'шный, и будет срезаться, но можно прям в cda его отдельно забирать в ci->rflags. Бонус -- такие каналы будут правильно раскрашиваться, поскольку уже давно вся поддержка сделана.

  • 09.09.2011: оказалось, что в blklist'ах нельзя в bus_id указывать шестнадцатеричные числа с префиксом 0x.

    Как выяснилось, потому, что считывание их -- в GetInt() -- идёт вручную, по циферке, безо всяких strtol(). Это -- тяжкое наследие давних времён, было уже во времена oldcx (дата на самом старом cx-server_dbase_new.c -- 10-12-1999).

    14.09.2011: перевёл на strtol(). "Хитрость" в том, что там везде адресация по позиции -- linepos в буфере line, вот и сделал тривиальный переход к указателям и обратно. "done".

  • 10.02.2012@Снежинск-каземат-11: сходу -- добавлен API-вызов GetBlockLabel(), в интересах remdrv, чтоб он мог передавать имя/метку экземпляра устройства подчинённому контроллеру (для реализации в нём интерфейса inserver).
  • 16.02.2012: имеется потребность слегка дополнить поведение флага cachectl: чтобы помимо вариантов "можно возвращать результат чужого, но совместимого измерения" (сейчас ==0) и "обязательно провести своё измерение" (сейчас !=0), также был бы вариант "вообще не проводить своё, а только вернуть чужое".

    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_types.h добавлены константы CX_CACHECTL_* -- пока просто 0,1,2,3. Жаль, конечно, что так -- напрашивается-то "по возрастанию активности", FROMCACHE=0, SNIFF=1, SHARABLE=2, FORCE=3; но не получится из-за требования совместимости.
    • Также за компанию введена парочка CX_BIGC_IMMED_NO=0 и CX_BIGC_IMMED_YES=1.
    • Всё, использующее cxlib (в данном случае это cx-bigc.c и tsycam.c -- причём оба на cx_bigcmsg() вместо cx_bigcreq()), переведено с констант 0/1 на свежевведённые.
    • Аналогично все cda-программы.
    • Добавлен вызов cda_setbigc_cachectl() -- для возможности менять cachectl по ходу работы программы.

    В КОДЕ/поведении пока ничего не менялось.

    23.10.2012@Снежинск-каземат-11: делаем.

    • cx-server_bigc.c::ServeBigcRequest(): CX_CACHECTL_FROMCACHE влечёт:
      1. сброс immediate=0; После обеда: поразмыслив -- убрал.
      2. вместо запроса -- AddBigcRequest() -- сразу делается подготовка ответа, сводящаяся к FillBigcReplyPacket(), только для "простоты" она впрямую вызывает bigc_callback().
        1. Это может иметь нежелательный эффект -- если к большому каналу еще вообще не было обращений, то отдастся ninfo=0 и retdatasize=0.
        2. Ну и еще -- в отличие от фейральского проекта, оно заполняет пакет в момент получения запроса, а не перед отправкой. Это даёт временУю неопределённость в размере цикла сервера.

        Впрочем, это уже тонкости, имеющие исчезающе малое значение.

    • AddBigcRequest() в проверке "а не отправить ли свежедобавленный запрос" также НЕ отправляет SNIFF и FROMCACHE.
    • ReturnBigc() в конце вместо былой отправки первого в списке теперь ищет в нём первый не-SNIFF и не-FROMCACHE, и если найдёт -- то отправляет.
    • may_return_data() считает "да" при SNIFF и FROMCACHE.
    • В cx-bigc добавлен флажок -c, принимающий параметром значение флага cachectl -- 0...3.

    По первоначальной проверке -- вроде работает.

    03.12.2012: Роговским широко используется, жалоб не было -- следовательно, считаем, что всё окей, "done".

  • 21.03.2012: при запинывании ist_xcdac20 полезли warning'и "attempt to move from DRVS_OFFLINE to 2". Причина -- драйвер прямо из init_b() делает SetBlockStatus(,DRVS_NOTREADY), а ведь d_status[] по умолчанию 0, т.е. DRVS_OFFLINE.

    21.03.2012: решение -- прямо перед вызовом init_blk() вставлено d_status[magicid]=DRVS_NOTREADY.

    Вылезут ли побочные эффекты?

    21.03.2012: да, вылезли -- почему-то конкретно на самом ist_xcdac20 (и ТОЛЬКО на нём!), перестало производиться вычитывание каналов записи. СОВСЕМ.

    Хбз почему. Так что пока ту строчку отключаем.

  • 22.03.2012: аукнулся старый хак (14-06-2003, bigfile.html), что на время вычитывания каналов записи делается CacheReadsFromWriteChannels=0.

    Раньше это было безопасно (хоть и некрасиво). Сейчас же, с введением inserver, вышло, что на время вычитывания каналов некоего драйвера MASTER (ist_xcdac20) сброшенный флаг влияет также и на возрасты, возвращаемые inserver_get_cval()'ом на каналы другого драйвера (TARGET -- xcdac20): всё потому, что этот же флаг используется и в FreshFlag().

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

    22.03.2012: исправлено, по очевидному "правильному" пути:

    • Введён еще один флаг, внутренний для cxsd_channels.c -- IsReqRofWrChsIng, по умолчанию он =0.
    • В ConsiderRequest() он добавлен в пару к CacheReadsFromWriteChannels.
    • А ReqRofWrChs() теперь махинирует именно с ним, не трогая флаг кэширования.
    • ...причём махинирует -- так же, с СОХРАНЕНИЕМ старого, что позволит и "вложенные" вызовы (они потенциально возможны по цепочке уведомлений/вызовов вследствие inserver).

    Естественно, проблема решилась :-)

  • 08.12.2012@МЕГА-М.Видео: возникло лёгкое желание сделать возможность менять размер цикла сервера -- BaseCycleSize -- на лету через консольный интерфейс.

    11.12.2012: сделать-то несложно, но только клиенты не будут знать об изменении, и продолжат использовать (при рисовании графиков) старое значение.

    Вот если б было уведомление...

  • 29.05.2013: в cx-server'е имелась позорнейшая утечка памяти -- причём бесконечно давно: оно не делало free(lastreqbuf) при отключении клиентов.

    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 было аналогично (но оно не влияло из-за полной неиспользуемости).

Иные programs/:
utils/:
  • 06.01.2004: Отображение результатов в консольных утилитах (cx_rdt_wrt_sub.h):
    • При печати прибавлять "from" к номеру канала.

      08.01.2004: сделал. Работы-то...

    • Как-нибудь уметь выводить форматированными столбцами -- чтобы колонки не плыли при добавлении цифр к результату (99->100). Может, ключик -ddT, где T -- тип форматирования?
    • Отображение тэга: -dt- -- всегда ':'; -dtf -- ':'/'*' (как сейчас); -dtv -- отображать <tag>.
    • Отображение флагов: -df- -- не печатать вовсе; -dfd, -dfx -- печатать {%d} или {%x}; -dfs, -dfl -- печатать {shortname,...} или {longname,...}

    И аналогично в 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.

  • 25.04.2005: потребно перевести programs/utils/ на архитектуру с LocalRules.mk. И чтоб $(LOCALLIBDEPS) поддерживалось.

    04.05.2005: перевел. И $(LOCALLIBDEPS) поддерживается.

  • 20.06.2005: наблюдена неприятная особенность: если stdout от cx-console отправлять через pipe другой программе (grep, например), то ничего на терминале не появляется. Точнее -- появляется, но намного позже.

    20.06.2005: вообще-то подобное я наблюдал еще два года назад, на staromakh'е, когда пытался пользоваться им в связке с tee, но тогда в причину так и не въехал.

    А вообще-то все просто -- ведь stdio по умолчанию делает line-buffered только те потоки, что ассоциированы с tty, а остальные (за исключением unconditionally-unbuffered stderr) -- block-buffered. Вот оно и копит по BUFSIZ байт.

    Так что решение крайне простое:

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

    1. Либо делать fflush() после печати каждого блока данных (как printable в cx-console);
    2. либо в самом начале программы делать 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.

  • 10.07.2005: cx-console давно пора бы научиться ловить разрыв соединения и отваливать.

    30.07.2005: сделал -- в самом-то cx-console.c для этого нужно было всего лишь добавить обработку CAR_ERRCLOSE в нотификатор.

    Но хуже оказалось другое: обрыв соединения --- это CAR_ERRCLOSE, который в cxlib.c::CallNotifier() рассматривается как не-требующий-вызова-нотификатора при синхронном режиме.

    Так что пришлось добавить CAR_ERRCLOSE в список дергаемых-всегда.

  • 16.01.2007: надо бы уметь во всех утилитах измерять время отклика. Для этого можно ввести стандартный флажок -- например, -T.

    21.01.2007: в cx-bigc сделал. Результаты тестирования: локально (viper-viper) -- 150-170мкс, по сети (viper-linac1) -- ~500мкс.

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

    22.03.2010: ключик переименован в -R -- для возможности задействовать -T под "print ISO8601 timestamps".

  • 24.12.2008: а как бы этак научить cx-bigc выдавать данные от осциллографических блоков в виде, пригодном для всяких gnuplot'ов -- аналогично тому, как это умеют делать сами программы nadc*?

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

    24.12.2008: для этого надо:

    1. Ввести какой-нибудь ключик, типа "распечатай данные в столько-то колонок" (или, может, лучше -- "длина колонки столько-то" -- а то и вовсе "используй в качестве длины колонки такой-то параметр").

      Во -- если указано число >0, то это высота колонки, а если <0 -- то номер параметра, из которого брать высоту! При этом оно будет автоматом отлично адаптироваться к изменениям параметров по ходу работы.

      И, в любом случае, при указании этого параметра оно автоматом переключается в режим "выдача для текстового файла".

    2. А полученные с пакетом параметры, видимо, выдавать просто в за-'#'нных строках как parN=VALUE.

    P.S. И, кстати, давно пора научить cx-bigc УСТАВЛЯТЬ параметры. Причем в ДВУХ вариантах -- уставлять каждый раз, или же только первый раз.

    P.P.S. А вообще -- сильно тянет поменять интерфейс этой утилиты, поскольку указание в стиле "-f КАНАЛ" является извращением.

    27.12.2008: сделано, именно по вышеприведенному проекту. Формат вывода "для gnuplot'а" включается ключом -g, а высота колонки -- -H. Уставка единичного параметра также реализована -- теперь за это отвечает ключ -aARGN=VALUE; неработающие же пока уставки параметров и данных списками -- повешены на -A и -D.

    Вообще-то явно напрашивается некоторая переделка утилитки, а именно:

    1. Чтоб можно было указывать формат вывода -- Dec/Oct/heX, как в прочих утилитах.
    2. Аналогично, формат вывода rflags.
    3. Указание "вид вывода" -- list/gnuplot/binary -- стоит сделать ОДНИМ ключом с параметром.
    4. Вместе с данными в вариантах list и gnuplot печатать timestamp.
    5. Надо б уметь как-то указывать, что выводить надо В ФАЙЛ.
    6. А вот как при этом делать МНОЖЕСТВЕННЫЙ вывод -- хбз.

    04.06.2008: поскольку давно работает и используется Роговским -- помечаем как "done". А "переделка" -- на потом, если РЕАЛЬНО и СИЛЬНО потребуется.

  • 17.02.2009: по настойчивой просьбе Роговского сделал, чтобы в cx-bigc ключик -q (quiet) гасил ЛЮБОЙ вывод.

    Для этого в "главный if() выбора формата" вставлено отдельной альтернативой -- на первое место --

    if (flag_quiet){}

    14.12.2013: добавлен ключик -Q (quiet_data), гасящий лишь вывод данных. Потребно для разборок с видеокамерами -- иначе печать их многосоткилобайтных данных занимает море места и времени. При нём вместо данных печатается троеточие "...".

  • 04.06.2009: надо бы мочь указывать 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), которая сама разбирается и в размере значения, и в его знаковости.

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

  • 04.06.2009: вылезло, что, оказывается, cx-setchan странновато реагирует на ключик -h: вместо того, чтобы показать help/usage, оно пытается разрезолвить его как имя хоста.

    04.06.2009: да, сделал, что оно показывает usage не только при argc<2, но и при argv[1][0]=='-' -- так что теперь достаточно попробовать указать ЛЮБОЙ ключик.

  • 01.09.2009: еще некоторое количество усовершенствований в cx-bigc -- вызваны потребностью отлаживать табличную работу ЦАПа в драйвере xcac208.

    01.09.2009: итак:

    • Сделан парсинг параметра ключика -A -- прямо из optarg (сам парсинг сделан по образу парсера данных CAN-пакета в cansend_common.c).
    • Сделан парсинг параметра ключика -D -- аналогично. Список чисел префиксируется буквой b, s или i, указывающей dataunits.
    • Добавлен ключик -dDPYOPT -- код просто скопирован из cx-rdt_wrt.h, вместе с функциями Print*(). Понадобилось в основном для расшифровки rflags.
    • Добавлен ключик -Wlinewidth, определяющий ширину строки для выдачи в режиме Gnuplot. Значение указывается совершенно аналогично ключу -H (>0 -- ширина, <0 -- номер параметра, в котором лежит ширина), с той же проблемой -- что 0-й параметр не может использоваться (-0==0). -W приоритетнее -H.

    Вообще, конечно, интересно -- во что вся эта шобла (cx-rdt, cx-wrt, cx-bigc, cx-setchan) превратится в CXv4. Явно это будет ОДНА утилитка, могущая и обычные скалярные запросы исполнять, и векторные, и агрегированные. И эта утилитка потребуется ОЧЕНЬ СКОРО -- ведь она и есть главный инструмент при начальной отладке!

  • 19.03.2010: в cx-rdt_wrt.h добавлен ключик -T, копирующий оный у canmon'а и uspci_test'а, т.е., печатающий перед строкой с данными еще и timestamp в формате ISO8601.

    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() (вместо имевшейся странной самодеятельности).

  • 05.04.2011: в cx-setchan.c изрядно переделан парсинг командной строки.
    05.04.2011: теперь он вместо старого "абы как" сделан на getopt(). Смысл -- чтоб поддерживать ключик '-q'.

    Ну и сам ключик также сделан.

  • 23.11.2012: Роговский просит сделать, чтоб cx-bigc можно б было указать "воспринимай int32 как float32" (он пользуется совпадением размеров и отдаёт из драйвера сразу real).

    14.03.2013: вроде сделано.

    • Добавлен ключ "-F", коему указывается желаемый формат для вещественного вывода.
    • Собственно printf(dformat,...) вытащено из трёх точек в отдельную функцию DoPrintDatum().
    • И она, если размер 4 байта и указан ключ -Fформат, выполняет соответствующий другой prinntf().
    • Преобразование int32->float32 сделано через union.

    15.03.2013: вроде работает, закрываем.

  • 14.03.2013: сходу -- сделана реальная обработка flag_binary. Оно делает write() сначала "свойств" (rflags и tag (оный продвинут до 4 байт)), потом счётчиков (rninfo, retbufused, retbufunits), затем info и retbuf.
cdrclient:
  • 29.03.2007: возникло желание завести еще одну утилиту командной строки, названную "cdrclient". Для разных "манипуляций" с группировками, но не графически (как chlclient), а из скриптов.

    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".

    Некоторые тонкости:

    • Технологию придется использовать как в descr2html и staromakh -- Xt без дисплейных соединений.
    • Поскольку реально данные каналов имеются далеко не сразу, то как-то надо дожидаться, когда ВСЕ соединения ожили. Проверять cda_status_of()? И в течение какого времени? Наверное, ввести таймаут 10с. И -- тогда уж сделать флажок "-k", чтобы даже если НЕ все соединения успели ожить, то все равно продолжать.
    • Понадобится ли вводить команду "quit", чтобы прерывать периодический режим?
    • Каждое действие должно логгиться на stdout, если только не указан ключ "-q".
    • Можно ввести специальную команду -- "interactive", которая включала бы считывание команд с stdin.
    • Команда "save" должна уметь писАть как в точно указанный файл, так и в стиле Chl/XhSaveDlg -- сама выбирать очередное имя файла.

    Итак, планируемые команды:

    • load FILESPEC
    • save FILESPEC // FILESPEC=="." => autoselect name
    • log FILESPEC // "."=>auto
    • html FILESPEC // "."=>auto
    • set KNOBREF=VALUE
    • print KNOBREF
    • pause SECONDS
    • quit
    Командам сохранения и печати может указываться суффикс "/период" -- например, "log/10 ." включит протоколирование с периодом 10 секунд.

    P.S. Кстати, с появлением "cdrclient" переименование "chlclient" в "pult" кажется намного менее осмысленным.

    06.04.2007: программа cdrclient.c была начата позавчера, интенсивно делалась вчера, и доведена до первоначально работающего состояния сегодня. Highlights интересностей:

    • Описания всех команд хранятся в таблице (из которой также автоматом печатается help), в частности -- имеет ли команда параметр и может ли быть периодической. И там же хранятся указатели на "методы" исполнения этой команды --
    • ...которыми являются две функции: "check" (проверка корректности параметра -- вызывается ПОСЛЕ загрузки группировки) и "proc" (исполнение).
    • "Периодичность" реализуется не конкретными командами, а основной программой. Периодичность может иметь два варианта:
      • период >0 указывает именно период между вызовами, в секундах;
      • период <0 означает, что надо исполнять эту команду по каждому приходу данных от сервера; в частности, период <=-2 будет игнорировать CDA_R_AUXDATA, реагируя только на данные от основного соединения.

      Поскольку периодичность делается Xt-таймаутами и DataProc()'ем, то будущая команда pause на периодические команды никак не влияет -- они являются как бы "фоновыми задачами", а все не периодические -- "консольными" и последовательно-исполняемыми.

    • Вначале программа дожидается, пока будут установлены все соединения -- пока для всех соединений их cda_status_of() не побывает хоть раз CDA_SERVERSTATUS_NORMAL. Ожидание длится 10 секунд.
    • Периодические команды запускаются именно ПОСЛЕ того, как установлены все соединения.

    Пока есть только инфраструктура/планировщик, а собственно ТЕЛА команд пока не сделаны. Также пока не реализованы "интересности" -- интерактивный режим и команда pause.

    08.04.2007: сделаны тела базовых команд -- load, save, log, set, print. Пара замечаний:

    1. Для автоподбора имен файлов по "." пришлось скопировать Xh_fdlg.c::XhFindFilename(), слегка ее изменив и обозвав ChooseFilename(). Халтурноватенько!
    2. После команд ИЗМЕНЕНИЯ (load, set) надо перед выходом обязательно дождаться ОТСЫЛКИ данных, в противном случае ничего не происходит -- данные не уходят дальше cda.

      Вопрос -- как? Видимо, эти команды должны уставлять некий флажок "modified", и по окончанию обработки очереди надо (при отсутствии периодических команд и интерактивности) запускать процесс, аналогичный первоначальному ожиданию -- чтоб она дожидалась прихода данных ото всех серверов; плюс, поскольку отправка запросов сейчас делается ПОСЛЕ уведомления клиентов, то надо перед exit() сделать cda_continue(mainsid).

    11.09.2014: изменена кривость с реализацией команды set, имевшаяся изначально -- что строка-параметр моодифицировалась (заменой '=' на '\0') для простоты.

    Теперь строка остаётся как есть, а имя ручки копируется в отдельный буфер.

    Бонус -- команды при исполнении печатаются целиком, без обрезания по бывшему '='.

  • 17.03.2009: надо переносить из utils/ в другое место -- поскольку cdrclient зависит от libXt, а тут должны быть ТОЛЬКО чисто консольные программы (которые должны собираться и при NOX!="").

    17.03.2009: понятно куда переносить -- в runner/.

    Перенес, избавив заодно utils/LocalRules.mk от "CDRCLIENTS", каковая существовала единственно для этой утилитки.

    А секцию эту оставим уж здесь.

  • 28.11.2012: переводим его на cxscheduler2. Собирается; но работает ли -- не проверено (а надо?).

    23.06.2014: проверено, работает. И -- да, надо.

  • 23.06.2014: надо реализовывать работу команды pause (команда была изначально, но без наполнения). Для техпроцесса на сварке ("3D-принтер", для изготовления цилиндра).

    23.06.2014: делаем:

    1. Периоды теперь в МИЛЛИсекундах (просто множитель сменен с 1000000 на 1000).
    2. Переименовываем StartOperation() в ExecCommandsInQueue(). Поскольку теперь она будет не "начинать исполнение", а исполнять очередной последовательный кусок очереди.
    3. Собственно реализация паузы:
      • ЛЮБАЯ proc может уставить значение глобальной переменной pause_ms (перед вызовами proc'ев ей делается =0).
      • ExecCommandsInQueue() при обнаружении после исполнения наличия pause_ms>0 прерывает цикл и перед return'ом заказывает таймаут -- через указанный промежуток вызвать PauseEnd().
      • А PauseEnd(), в свою очередь, вызывает ExecCommandsInQueue(), и всё продолжается.
      • Основа была заложена еще изначально, тем, что очередь исполняется не с 0-го элемента, а с "текущего" cmd_queue_ex_idx.

    Судя по тестам -- пашет. Реализация, конечно, кривенькая, ну да фиг с ней, на сейчас сойдёт.

    26.06.2014: да, проверено на "сварке" -- работает.

    Дальнейшие усовершенствования:

    • Команды печатаются на экран не сразу вся очередь перед исполнением, а каждая команда перед её исполнением -- это позволяет контролировать ход процесса.

      Причём -- с префиксом времени.

    • В дополнение к "pause" сделана пара команд для "точной" синхронизации, с привязкой к абсолютному времени, а не "дельта от текущего":
      1. timestamp, параметр -- буква-"метка". Сохраняет текущее время в указанную ячейку (A-Z).
      2. waituntil, параметр имеет вид L+MILLISECONDS, где L -- метка, к которой привязываться. Делает паузу до момента "того" времени плюс указанный интервал.
    • Механизм ожидания для поддержки такого был чуток сменён -- теперь вместо pause_ms используется pause_tv, куда должно складываться точное время окончания.
~/work/tmp/descr2html:
  • 25.04.2005: (записано 22.05.2005) Зачатие.

    Еще энное время назад понадобилось уметь показывать текущее состояние комплекса по 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().

  • 12.11.2012: лень дальше с ней возиться -- ушла в ARCHIVE/work/tmp.20121031/.
~/work/staromakh/staromakh:
  • 05.09.2005: мда, решился-таки изготовить здесь отдельную секцию и под многострадальный staromakh.
  • 05.09.2005: обнаружил, что перед 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.

  • 12.11.2012: лень дальше с ней возиться -- ушла в ARCHIVE/work/staromakh.20121031/.
~/work/alien2cx:
  • 07.11.2008: с полмесяца как стало ясно, что надо создавать новый вариант программы "staromakh". В первую очередь, для Старостенки. Плюс, давно назрела надобность иметь gateway для доступа EPICS'ных тулзов -- и это явно должно делаться программой с совершенно аналогичной функциональностью; в идеале -- просто той же самой. Потому она получила название alien2cx. Фичи проекта:
    • Программа будет пользоваться OpenDescription(), подгружая описания, а не линковаться с ними. Список обслуживаемых подсистем будет указываться в командной строке.
    • Реализации "протоколов доступа" будут не вшиты намертво, а, в стиле модульного сервера следующего поколения, сделаны в виде отдельных модулей, через фиксированный API получающих доступ к "ядру" утилиты.

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

    • Адресация каналов будет по именам, имена вида "ПОДСИСТЕМА.КАНАЛ", где компонент "ПОДСИСТЕМА" является фиксированным, а "КАНАЛ" может иметь синтаксис, понимаемый CdrFindKnob()'ом.
    • Клиенту будут отдаваться не все каналы, а лишь те, которые он попросит -- ибо так правильнее.

    08.11.2008: во-первых, этот alien2cx -- отличный кандидат для использования давно планируемого компонента "Module Loader". Вот на нем, как на кошечке, этот модуль и отладим.

    Во-вторых, а КОГДА клиенту отдавать значения каналов, и КАК? Варианты:

    1. Все скопом, с какой-то частотой (например, 1Гц, или как клиент попросит).
    2. Каждый канал -- по приходу данных на него (реально -- по callback'у от соответствующего sid'а).
    3. Все каналы по приходу данных от любого sid'а. Некрасиво! Даже если "право голоса" давать только тем sid'ам, которые задействованы для данного клиента -- все равно некрасиво.

    Тут есть еще вопрос -- а в каком виде отправлять ответные пакеты?

    • Если давать все каналы скопом -- то можно отправлять просто вектор double'ов.
    • А вот если индивидуально -- то тогда уж надо слать парами (КАНАЛ,ЗНАЧЕНИЕ); и как указывать КАНАЛ -- отдельный вопрос:
      • то ли номер канала в списке прошенных,
      • то ли имя, спрошенное клиентом (это имя тогда надо помнить, а не только результирующий knob-reference).
      • (Полное имя -- нельзя: клиент его может не знать, если спрашивал по короткому шаблону; плюс, это полное имя получить -- отдельная залачка...)

    Впрочем, это вопросы уже, пожалуй, не к ядру программы, а к реализации конкретного протокола -- в данном случае, "staromakh v.2". Хотя потребности "CA server library" также надо изучить, поскольку ядро должно удовлетворять им.

    26.11.2008: кстати, одним из модулей протокола доступа может стать оный для DataSocket.

  • 21.01.2015: сделана утилитка starogate -- в качестве врЕменного (до полного перехода на v4) решения для потребностей Старостенки на сварке: там Алексею Медведеву надо из-под Форточек на LabWindows/CVI доступаться до железа, управляемого CX@bike.

    21.01.2015: higglights:

    • Идеологически -- именно замена staromakh.
    • Слушает тот же самый порт 8008.
    • Но протокол -- ТЕКСТОВЫЙ.

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

      Поддерживаются 2 команды:

      1. monitor ИМЯ.КАНАЛА

        Помимо ответа "200" затем будут приходить строчки вида

        100 value ИМЯ.КАНАЛА ЗНАЧЕНИЕ
        (код 100 выбран от балды).
      2. write ИМЯ.КАНАЛА ЗНАЧЕНИЕ

      Определения лежат в starogate_proto.h.

    • Внутренности работают на simpleaccess'е. Соответственно, имена каналов -- в его формате: SUBSYS.GLOBELEM{.SUBELEM}.CHAN.
    • Сделано всё на основе SLOTARRAY'ев, 4 штук. Делалось по образу и подобию v4'шных cda_core и cxsd_fe_cx, с некоторыми упрощениями.
xutils/:
  • 18.06.2004: решил удалить пока эту директорию -- все равно пустая, только глаза мозолит и <Tab>'у мешает.
xmclients/:
tsycam:
  • 07.04.2004: tsycam: возникла необходимость отключать "нормализацию" данных.
    07.04.2004: ну -- взял да сделал еще один чекбокс.
  • 18.04.2004: tsycam: возникла необходимость, по образу и подобию istcc, выводить "статистику" по картинке -- максимальное значение и сумму всех пикселов.
    19.04.2004: сделал. Во-первых, включил StatusLine, на который и производится вывод, а во-вторых -- FindMinMax() теперь вызывается в любом случае, отдельно от NormalizeCurdata(), перед оным.
  • 27.06.2005: обнаружилось, что при fbbpp=24 (VMWare) программа падает по SIGSEGV -- GetGray2ImageConverter() возвращает ошибку, а она ее не проверяет.

    27.06.2005: вставил проверку и печать сообщения о проблеме, с перечислением параметров.

  • 04.04.2007: надо добавить управление параметрами "перезапрашивать при недоприеме" и "всегда брать только из памяти".

    10.04.2007: добавил -- просто еще парочка чекбоксов, совершенно аналогичных SYNC'у.

  • 10.04.2007: а вообще пора нафиг переделать по крайней мере пользовательский интерфейс tsycam'а -- сейчас он построен "руками" на обычных виджетах, которые выстраиваются в линию при помощи Gridbox'а.

    А надо -- сделать simple-Knob'ами, уложив их поразумнее. А "Loop" и "Get picture" переделать на кнопочки [>] и [|>].

    11.04.2007: да, переделал внешний вид. Стало суш-чественно симпатичнее!

  • 11.04.2007: да и вообще -- пора подпеределать и внутренности этой утилиты, чтобы они были аналогичны содержимому ndbp_image_elemplugin.c. Свалить собственно алгоритмы обработки/обсчета в отдельный файл, включив его вторым компонентом в библиотеку tsycamlib? И уж общие для клиентов и драйвера вещи, типа конверсии 10->16/32 точно должны быть там.

    А возможно, завести также и третий компонент библиотеки -- собственно реализацию протокола (включая описание его форматов и констант). Ибо нефиг в tsycamlib.c и tsycamv_drv.c иметь дублирующийся код.

  • 11.04.2007: а также надо перевести утилиту на протокол tsycamv, чтоб она поддерживала варианты 10bit-packed и 16bit.

    И, естественно, перейти с cxlib на cda!

ipp:
  • 18.05.2004: ipp: пришел Федя-jr. с кучей претензий, причем обоснованных. Они таковы:
    1. Писались какие-то странные числа в окне ИПП-2-10 (гистограмма).
    2. Был странный масштаб в том же окне.
    3. Надо КОРРЕКТНО показывать перегрузку входа -- а то она незаметна, а из-за вычитания фона еще и программа ее не замечает.
    4. Достало, что 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: уж и не знаю когда, но решил сделать совсем другой, "новый" вариант клиента ipp (сегодня дата на файле -- 20.06.2004, а начало, похоже, около 01.06.2004).

    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".

  • 28.10.2004: обнаружился-таки прикол в вычислениях энергий: они почему-то получались мелковатыми. Разобрались -- там стояло "L=36.0", а, как выяснилось, должно было быть "L=44.8". Поправили. Заодно ввели чуть более другой обсчет, который, впрочем, дает практически те же результаты.
  • 09.11.2004: Федя-jr. объявил, что хочется иметь возможность по некоторым "палкам" выводить некоторые данные на дополнительную панель.

    Это похоже на 1) sukhphase, где надлежало отображать данные с нескольких точек графика в числовом виде; 2) отображение (в будущем множественное) "крупногабаритных" значений каналов. Так что, наверное, стоит и подумать об унификации интерфейса этих трех случаев.

    Во-первых, отображать сии текстовые данные следует, видимо, на дополнительной панельке в том же окне (в случае с ipp -- справа от графиков; пусть появляется только когда отображается хоть что-то, а в остальное время -- пусть будет unmanaged).

    Во-вторых, вопрос, каким именно способом юзеру надлежит эти параметры "вызывать" и "удалять". Закладываться на Ctrl+Btn3, как с bigval'ом, не стоит -- там ситуация отдельная. А вот унифицировать ipp с sukhphase и возможными будущими клиентами -- стоит.

    И, кстати, недурственно бы те "палки", для которых выводятся данные, как-нибудь помечать.

    Сейчас в sukhphase принята такая схема: Btn1 ставит репер, Btn2 позволяет его двигать, Btn3 -- убирает.

  • 12.11.2005: после того, как в ipp появился help по цветам, встал вопрос: а почему это, хоть колоризация k_sens[] и k_time[] и выполняется, но неполностью -- в самом начале не ставится COLALARM_JUSTCREATED?

    12.11.2005: вставил -- там делов-то было. Это наметка на будущее -- когда у "сложных" plugin-knob'ов будут внутренние "под-knob"'ы, надо будет исполнять аналогичные телодвижения.

  • 26.06.2006: так, и ipp также надо будет пересделать уже в work/qult/. Во-первых, под новый драйвер ipp, а во-вторых -- с обновленным интерфейсом.

    26.06.2006: чуть более детально:

    • Во-первых, в архитектуре knob-plugins. Виды knob'ов -- "обычный", "спектрометр", "цилиндр Фарадея", ... Раскладку же на экране -- оставляем на Chl. Номер же коробочки -- пока что придется указывать параметром в widdepinfo.
    • Во-вторых, делаем коробочки-элементы "не жмотясь" -- с нормальным заголовком, вместо дурацкого "2-10".
    • В-третьих, реализовать ПОЛНУЮ колоризацию всех столбцов.

    31.07.2006: да, проект создан некоторое время назад. Highlights его таковы:

    1. Программа теперь называется linipp.
    2. Содержится весь код в одном файле linipp.c -- поскольку теперь значительная часть "стандартностей" (т.е. -- содержимое PROGRAM_gui.c) ушли в Chl_app.c, то уж main+meat+knobplugins+db/vars пока что вполне помещаются в один файл.
    3. Мало того, что оно сделано на Chl'е, так...
    4. ...используются element-plugin'ы. Управляющие каналы (S, T, ...) теперь создаются не "коробочкой", а указываются просто обычными ручками в БД, так что все махинации с ними -- уставка, чтение, колоризация -- переложены на Cdr+Knobs+Chl.

      Элемент-"коробочка" же создает только собственно график и ручку "коэффициент отображения".

    5. Кроме S и T добавлен выключатель -- который раньше "жил" в селекторе камер (candid, планировавшийся к замене camsel'ом). Он во включенном состоянии зеленый.

      И он использует опцию panel, которая два года назад и создавалась для ipp, хотя и в несколько ином варианте :-).

    6. Ко всем управляющим ручкам добавлены tooltip'ы.

    01.08.2006: добавлена поддержка "методов" -- с использованием свежеопубликованных Chl_E_Nnn_m(); естественно, alarm'ы не поддерживаются.

    16.04.2007: поскольку программа работает уже чуть ли не полгода (хоть и не до конца, в смысле мелочей, доделана -- но это больше по милости Феди-jr, да и не принципиально), то считаем за "done".

  • 14.04.2007: а вообще -- почему бы, собственно, не отделить ОПИСАНИЕ конкретной аппаратуры (экрана программы) от собственно КОДА?

    Мало того, что тогда программу можно будет использовать для разных приложений (хоть для ВЭПП-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-файла -- "описания" карпиных каналов.

  • 02.07.2012: по результатам недавних разборок и осознания, что модель "на элементах" неразумна, приступаем к созданию варианта "на ручках".

    Т.е., каждая "коробочка" делается knobplugin'ом, вместо elemplugin'а, а раскладкой "оформления" занимается обычный элемент, в котором оно всё содержится.

    02.07.2012: детали:

    • "Реализация" состоит из двух частей:
      1. Другая структура группировки. Практикуемся на соседнем db_zlinipp.h, потом скопируем/переименуем.

        Это сделано быстро и просто.

      2. Собственно реализация knobplugin'ов в ippclient'е.

        Данной части и посвящено всё дальнейшее изложение.

    • Нынешний вариант ippclient поддерживает ОБА типа -- и на элементах, и на ручках.
    • Сделано всё на той же инфраструктуре, с теми же функциями отрисовки, вот только обсчёт приходящих данных скопирован в свою функцию, дергаемую из ручковых SetValue_m(), безо всяких synthetic.
    • Поскольку ручка-IPP в группировке помещается в свой отдельный элемент, в котором ТОЛЬКО ОНА, то многосерверность программы никак не мешает -- благодаря conns_u (уведомление о новых данных происходит только по приходу от "нашего" сервера).
    • Так что вёрнуто разрешение использовать backgroundPixmap. Проверено -- оно работает нормально; хотя при высокой частоте, возможно, породит проблемы -- поэтому пока опять отключено.
    • Для без-pixmap'ного же варианта раньше был недостаток: программа изначально полагалась на pixmap'ы и не имела реакции на ExposeEvent. В результате, например, при нажатой кнопке [пауза] всё переставало отрисовываться. Сейчас добавлено, что при pix==0 навешивается exposeCallback.
    • Из IPP_ESPECTR_Draw() убраны прямые обращения к me->e->content[] (которые б просто не проканали). Для этого введены поля i_ki и r_ki, для старой версии заполняемые как ссылки на content[], а для новой получаемые через datatree_FindNodeFrom() по именам, указываемым в widdepinfo.

    04.07.2012: добиваем:

    • В privrec добавлено поле ident (заполняемое создавателями), для отвязки кода сохранения данных от элементов. А ipp_elems_array[] переименован в ipp_recs_array[].
    • Добавлена поддержка magn. Довольно извратным образом -- оно реализуется отдельным knobplugin'ом, имя этой ручки указывается её "клиенту" в widdepinfo, и потом производится ручная "привязка".

      Заодно possible_scales[] переименовано в magn_factors[].

    В общем, оно работает, так что "done".

    10.12.2012: угу, тогда zlinipp сделал, а переименовать (и, соответственно, выложить на пульт) забыл.

    В результате вылезло, что "старая" версия, не ловя ExposeEvent, также не перерисовывалась в режиме [пауза]. Сделано.

    Ну и сама группировка переименована и проапдейчена.

    13.01.2013: и старый код наконец-то выкинут и из ippclient.c, и из db_linipp.h.

  • 03.06.2013: что неприятно -- оно ж до сих пор работает на СКАЛЯРНЫХ каналах, вместо больших (которые бы обновлялись ровно по приходу измерений).
vacclient:
  • 08.07.2004: окончательно созрело понимание, как стоит сделать "вакуумного клиента следующего поколения". Итак:
    • Понятно, что оно должно вместо этой "матрицы" чисел отображать гистограмму, показывающую состояние вакуума по следованию канала (это было ясно еще года два назад).
    • Подписи к каналам имеет смысл делать вертикальными -- т.е., писать на некоем большом pixmap'е, а потом транспонировать на маленький pixmap, которым уже и делать XCopyArea() (шрифт, по-хорошему, надо добывать ресурсом -- через Xrm-чего-нибудь())
    • Как можно делать tooltip'ы на столбцах: либо сделать все столбцы отдельными виджетами (благо, у нас будет своя drawing area, а не готовый виджет-гистограмма), либо -- положить поверх гистограммы кучу специальных виджетов с input-only окнами: они станут ловить перемещения и щелчки мышью, и будут при том прозрачными.
    • Шкалу имеет смысл делать логарифмической -- поскольку значения тока тым колеблются от единиц (норма) до тысяч.
    • На тему шкалы поговорил со Старостенкой. Результаты:
      • Напряжения, по его мнению, вообще показывать не надо.
      • Он хочет иметь возможность переключаться между нормальной и логарифмической шкалами.
      • Кроме нынешних (реально хардверных) лимитов, хочется еще иметь "разноцветность" для облегчения восприятия логарифмической шкалы: например, 0-10 -- столбец зеленый, 10-100 -- желтый, выше -- красный.
      • Хочется еще как-нибудь учитывать производную: если значение начинает резво расти, то надо сигнализировать -- да хоть бибикать.
    • Поскольку у нас уже как минимум 2 окна вакуума (условно назовем их linvac и ringvac), то имеет смысл сделать ОДНОГО клиента, который бы, аналогично chlclient, грузил описание из *_db.so-файла (за этим -- надо сделать descraccess.*::OpenDescription() общедоступным).

    25.07.2004: еще насчет транспонированного текста: роясь в архивах fvwm'а, наткнулся на упоминание программки "xvertext". Найдя ее исходники (компилируются хреново, а потом падает по SIGSEGV) узнал, что она делает "дело" путем загрузки шрифта и выкладывания его на ximage'и. А потом еще наткнулся на упоминание, что нечто подобное имеется в XFig'е -- точнее, что у XFig'а есть какая-то связь с xvertext.

    06.07.2005: "транспонированный текст" мы делать уже давным-давно умеем, так что эта проблема снята. А насчет того, как компоновать собственно гистограмму с метками подписями -- да в общем-то довольно просто. Итак:

    • Делаем все столбцы отдельными виджетами, а не единой drawing area. Подписи, соответственно, тоже.
    • Помещаем всю эту шоблу в таблицу -- 16 (32?) столбца, 2 строки: в верхней строке -- столбец графика, в нижней -- метка.
    • Ширину столбцу графика -- СТАВЯТ (например, таблица, halign=fill), а он уж при рисовании узнает свои ширину/высоту и из них считает, сколько рисовать.

    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: приведение в соответствие с современной парадигмой использования меток/тултипов.

    01.03.2006: отразил в vacclient'овском PopulateWindow недавно введенную n-этажность/многоподъездность -- теперь от поля ncols учитываются только младшие 16 бит.

    03.03.2006: а также обновил get_label_and_tip() (м-мать!!!).

  • 26.06.2006: ясно, что vacclient'а надо будет переписать в work/qult/, сделав его уже "с правильным" интерфейсом.

    Вопрос же -- КАКОЙ интерфейс делать.

    26.06.2006: замечания по теме:

    1. Вариантов интерфейса, собственно, видится несколько.
      1. Первое, что приходило в голову -- это по нажатию правой кнопки мыши выдавать окошко с теми 6 параметрами.
      2. Второе, "попродвинутей", придуманное 22.06.2006 -- иметь формочку справа от гистограммы, которой делать manage по нажатию на правую кнопку мыши, и в этой форме пусть будут 6 "настоящих" knob'ов, линкуемых на соответствующие "логические каналы" (хотя и вопрос, конечно, КАК). И "крестик", для убирания этой формочки.
      3. И третий вариант, за 24.06.2006 -- это прямо в ОДНОЙ программе иметь ОБЕ инкарнации вакуумной системы: в тулбаре кнопочка [гистограммы/числа], и, как у Чугуева было в bpm'е, делать managed то сетку с числами, то гистограмму.

        И по правой кнопке мыши просто переключаться на сетку с числами и фокусировать нужный knob.

        Вот этот 3-й вариант и реализовываем.

        И еще -- надо кнопку "linear/logarithmic" также иметь на тулбаре.

    2. И еще несколько пунктов общего характера, которые надлежит не забыть:
      • На тулбаре надо иметь ВСЕ стандартные кнопки -- включая Help.
      • Все столбцы должны ВСЕГДА расцвечиваться. Также, как и у ipp. В частности, при DEFUNCT/HWERR/SFERR они должны рисовать закрашенный столбец ПОЛНОЙ высоты (а не на размер текущего значения).
      • Вследствие существования n-этажности теперь, казалось бы, совершенно нефиг "умно" поддерживать упихивание многих элементов в одну гистограмму.

        Но реально возникает контрвопрос -- а как? Видимо, каждый такой элемент делать именно "элементом" -- подчиняясь флагам "notitle"/"noshadow" и fromnewline.

    3. И пара замечаний насчет общей инфраструктуры Cdr/Chl:
      • Возникает проблем -- как делать двойное отображение одной и той же группировки? Как вариант -- изготовить в Cdr функции "клонирования": CdrDup{Groupunits,Eleminfo,Knobs}().
      • И, коли уж нету пока "отдельного отображатора «группировка»", надо б тот кусок ChlPopulateWorkspace(), что занимается раскладыванием готовой группировки, вытащить в отдельную функцию -- например, "ChlLayOutGrouping()".

      Так, немного подумав (после обеда): а нафига иметь "двойное отображение"? Просто нехай существует "application-level callback" (который ставится через ChlSetDataArrivedCallback()), и уж в нем пусть пробегает по НОРМАЛЬНОЙ группировке и на основе ее данных отрисовывает те каналы.

      А чтоб особо не маяться -- в каждом "столбике гистограммы" заимеваем указатель на "base knob" (первый из тех 6).

      Зато плюс -- что иерархия ОДНА, и модификация желтого диапазона в сетке чисел отразится и на гистограмме.

      Так что -- пока что "CdrDup{Groupunits,Eleminfo,Knobs}()" в баню. А вот "ChlLayOutGrouping()" -- по-прежнему нужен, хотя бы для ipp.

    07.07.2006: еще неделю назад (не записывал, потому как ваял презентацию на предзащиту) сделал более-менее рабочий вариант. Highlights:

    • Сделано переключение "Числа/Гистограмма" кнопочкой на тулбаре.
    • Также есть переключение "Линейный/Логарифмический" режим отображения.
    • Логарифмический режим - изрядная задница, ведь измерения-то у нас НЕ логарифмические, а log(x) при x<1 -- отрицательный. Поэтому мы рисуем не log(x), а log(x+1).

      И -- "правильная" формула вгоняния значения в шкалу выглядит как

      (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)
    • От отображения напряжений решили отказаться -- мало того, что не нужны, так еще и не ложатся на один график с токами, все поганят -- уж слишком несвязанные значения.
    • Зато -- наконец-то заиспользовал (первый раз после xruconsole, т.е., после восьмилетнего перерыва :-) XhMakeTempMessage(); в соответствии с идеей после обсуждения с Гусевым за 30-06-2004: при входе курсора в столбец в statusline отображаются числами текущие ток и напряжение. И даже обновляются по приходу данных.

      Возможно, стоит туда же сваливать и флаги, при !=0.

    • ChlLayOutGrouping() -- очень даже понадобился, именно он выкладывает все в числовой панели.
    • Но вот с "многоэлементностью" -- мутно: она как бы полу-поддерживается (в раскладке -- да, но формальный список для дальнейших проходов -- НЕ создается).
    • И еще: Малютин очень жаждет иметь именно графики-самописцы. Сделать ТРИ кнопки на тулбаре?

    Что еще осталось сделать -- до полной готовности:

    1. Реальную поддержку кнопок Load/Save/SwitchLog (все, достало -- надо эту функциональность в Chl_simple.c делать публичной!).
    2. Расцвечивание столбцов в зависимости от флагов.
    3. Мерцание красным при бибиканьи (аналог DisplayAlarm()'а & Co.).

    13.07.2006: пункт 1 исполнен -- через ныне опубликованный интерфейс ChlHandleStdCommand().

    14.07.2006: пункт 2 также исполнен -- через CdrChooseColorState(). Замечания:

    1. Поскольку "ALARM_ALARM" цветовым состоянием НЕ является, проверка этого флага делается отдельно и имеет наивысший приоритет. Отображается красным (обычное COLOR_RED/COLALARM_RED -- "розовым").
    2. Алармовость по НАПРЯЖЕНИЮ проверяется отдельно, имеет наименьший приоритет, и отображается синим.

    16.07.2006: и пункт 3 также реализован. Очень просто -- в NewDataArrived() узнаем новое состояние alarm-или-нет, при надобности щелкаем переменной alarm_flipflop либо скидываем ее в 0, а при отображении -- выбираем один из двух GC, используя alarm_flipflop как индекс в массиве.

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

    1. Бледно-желтые и розовые столбцы на фоне зеленых слабо заметны.
    2. Состояние "RELAX", которое также зеленое, никак не видно...

    Использовать более бледно-зеленый? Или -- менее яркий, типа 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. Он вроде более-менее нормально сочетается и различается со всеми остальными.

    "Процесс":

    • "ТЗ" было следующим: цвет должен быть и спокойным, приятным для глаза (т.е -- что-то зеленое), и при этом хорошо отличаться как от чисто зеленого "relax" #00FF00, так и от болотного "sferr" #8b8b00.
    • Идея: берем цветовой круг, ставим точки теми двумя цветами, и потом находим максимально отстоящую от них, но все еще зеленую точку.
    • Совсем-то так не получилось -- поскольку круг двумерен, а параметров ТРИ. Поэтому -- и по кругам гонял (при помощи GIMP'овского окна "Color selection"), и в PowerPoint'е смотрел, и рядышком на экране все варианты плюс окно "Help" ставил, так что в конце концов скорее уже интуитивно нашел подходящий цвет.

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

    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: хо -- а не отображать ли "тени" ушедших alarm'ов "в полный рост"? Т.е., чтобы кроме самого столбца нужной высоты, также и "фон палки" также рисовался бы GC'ом "тени" (естественно, когда он не красный-и-так от alarm'а). А то свежепогасший аларм на столбце небольшой высоты слабозаметен.

    02.08.2006: сделал.

  • 02.08.2006: кря-кря! Очень, ОЧЕНЬ неудобно читать названия каналов, написанные вертикально. А что, если все-таки "транспонировать" картинку: слева -- нормальные метки, правее -- собственно "столбцы", а еще правее тогда можно и просто числа рисовать, а?

    03.08.2006: хрю-хрю!!! А по вертикали -- не влезет! С 12-м шрифтом нынешнее окно linvac с 48 столбцами имеет ширину 861 пиксел; даже при небольшом скоращении (за счет "уплощения" шкалы) вместе с тулбаром и заголовком оно все равно будет вылезать за нынешнее пультовое ограничение в 864 пиксела. Вот только после перехода на TFT с 1280*1024 -- станет влазить...

    И еще минус: при повороте наверняка возникнет желание удлинить столбцы. И это съест экономию, которая вышла бы за счет сокращения пустой площади тулбара. (Реально-то НАВЕРНЯКА придется удлинить -- иначе, даже с отображаторами текущих значений тока, тулбар будет шире.)

  • 01.09.2006: короче -- рано или поздно будет нужен "vacclient следующего поколения" (2-го -- сейчас 1-е, а было еще 0-е -- прототип). Фичи его бдут следующие:
    • Вместо ГЛОБАЛЬНЫХ данных заводится список структур ПО ЭЛЕМЕНТАМ.
    • Флаги alarm_* используются от "настоящих" элементов.
    • Возможность вертикального отображения.
  • 10.11.2006: давно напрашивалось, чтоб при Ctrl+MB3 на столбце гистограммы также б вызывалось окно bigval. А сегодня еще и Пирогов этого запросил.

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

    06.02.2007: приравнял в смысле мыши метки к столбцам -- теперь и Ctrl+MB3, и курсоренье поверх меток имеют тот же эффект, что и на столбцах.

oscilloscope:
  • 06.11.2004: давным-давно напрашивалось, что стоит ("будет стоить" :-) сделать программу "осциллограф вообще" -- которая будет способна отображать данные от любого быстрого осциллографа, получая эти данные через большие каналы.

    06.11.2004: Несколько замечаний:

    • Во-первых, ТОЧНО надо будет все "описания параметров драйверов" вытряхивать в отдельные .h-файлы.
    • Во-вторых, а не предусмотреть ли какой-нибудь способ вытрясти описание параметров большого канала прямо из него самого, "на лету"? Как -- хоть идея?
    • В-третьих, чтобы оно могло на лету хотя бы опознавать тип прибора -- стоит сделать так, чтобы, например, 0-е слово в info[] возвращало бы тип прибора. Тогда можно будет при старте выполнить одих "холостой" запрос, с retbufsize=0 и immediate=1. Плюс, естественно, надо будет завести и "реестр кодов-типов осциллографов". 07.07.2008: неа, не надо этого делать -- это задача другого "уровня", информация о типах должна браться из "БД конфигурации". Gо-хорошему, и "описание параметров" также должно предоставляться БД. Тут, в CXv2, мы этого делать уже точно не будем, так что см. раздел "Насчет БД конфигурации" в bigfile-0002.html.

    07.04.2005: ага! Мыслишки: в частности, "описание на лету" должно отдавать параметры: минимальное возможное значение, максимальное, макс. количество точек.

  • ??.??.????: Кстати... А как насчет поддерживать тут же и как-бы-не-осциллографические большие каналы? Типа того же ИПП, лебединого варианта CAC208, etc. Или считать их также осциллографическими?
  • 04.02.2006: удобно было б мочь указывать прямо в командной строке и настраиваемые-из-GUI-параметры: T, range0, range1, ..., scale (x1, x2, ...), а также и numpts.

    "Задвигов" только два:

    1. То, что параметры бывают как "настоящие", так и "искусственные" -- как тот же scale. Но это несложно.
    2. То, КАК указывать значения параметров-селекторов: ведь удобнее-то не "0", "1", etc., а именно реальными значениями -- "500us", "2.048V", etc.

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

      (А в CXv4, когда получится отдельное поле knobinfo'ы "список для селектора", сия проблема исчезнет.)

  • 23.01.2008: сюда пойдут общие надобности для осциллографообразных программ.

    23.01.2008: списочек на сегодня:

    1. Уметь рисовать ТОЛСТЫМИ линиями не 0 пикселов, а, например, 2.
    2. Мочь указывать для каждого канала не только коэффициент, но и сдвиг нуля.
    3. Для каждого канала должны быть индивидуальные "ручки" [xN] (увеличение/масштаб) и вкл/выкл (отображения).
    4. А главное -- надо уметь менять шкалы, сдвиги нулей, и имена at run-time, как у обычных осциллографов; и уметь эти настройки сохранять и считывать (сделать для этого отдельное окошко, вызываемое с тулбара?).
    5. Возможность переключаться на показ "сырых" значений, в вольтах. Это нужно при перекалибровке.

    03.04.2008: продолжение:

    1. Кнопки "Get Background" и "Reset background" -- для сохранения текущих значений в качестве фоновых, чтобы они в дальнейшем вычитались из текущего при отображении.
    2. Кнопки "Get good" и "Forget good" -- для сохранения текущих значений в качестве "хороших", чтобы они в дальнейшем отображались для ориентировки (см. раздел по adc200 за 18...19-12-2007).
  • 08.05.2009: общая потребность для осциллографоподобных и для CCD-программ: показывать fps'ы (в дополнение к бегунку "/-\\|").

    08.05.2009: "показывать"-то несложно, вопрос -- КАК СЧИТАТЬ. Т.к., возможные варианты:

    1. Около 1 ("1.2fps").
    2. Меньше 1 ("0.3fps").
    3. Существенно больше 1 ("23.4fps")

    Для больших чисел просто -- раз в секунду показывать некую переменную, инкрементируемую при каждом кадре, и сбрасывать её потом в 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'ов, чтоб упростить жизнь программе-юзеру.

  • 13.05.2012@Снежинск-каземат-11: цели/компетенция этого раздела в огромной тепени перекрываются с тем, что сделано и сейчас живёт в lib/fastadc/ (многое из заявленного здесь там реализовано). Но -- НЕ полностью. Так что этот раздел пока оставляем жить.
adc333:
  • 05.03.2005: рождение xmclients/adc333. Хотя правильнее было бы иметь унифицированную программу "oscilloscope", для этого придется много возиться, а тест для ADC333 нужен уже сейчас. Так что -- изготавливаем таковое приложение.

    07.04.2005: Губин потребовал иметь возможность менять пределы отображения. В идеале, наверное, именно указывать пределы, а для начала хватит и селектора "увеличение".

    Сделал -- с коэффициентами (как у ИПП) 1, 2, 4, 10, 20, 50, 100.

    07.04.2005: и еще он возжелал сеточку.

    Что ж -- тоже сделано. Выглядит, признаюсь, препоганенько, но уж пусть так.

    06.08.2006: какого черта?! Давным-давно готова программа -- так что "done".

  • 06.02.2006: надо в adc333 сделать хотя бы предварительный, "хакообразный" вариант возможности указывать значения параметров в командной строке.

    22.02.2008: поскольку уже есть более свежая и продвинутая nadc333, которая имеет данную фичу от рождения, то -- "obsolete".

  • 18.12.2007: начал делать новый вариант программы -- пока под названием nadc333. Делается по образцу adc200, но с дополнениями/исправлениями, учитывающими полученный там опыт.

    21.02.2008: сделано в первом приближении еще чуть ли не месяц назад, протестировано на симуляторе. Сегодня -- проверено на реальном устройстве.

    Из ляпов -- в text2show[]-то не было параметров SHOT/ISTART/WAITTIME/STOP, так что значения оставались 0, и, следовательно, отправлялись в параметрах при первом запросе -- так что программа при запуске гасила ISTART. Теперь после bzero() и перед psp_parse_as() все параметры циклом уставляются в -1.

    Что еще осталось -- запись/чтение файла и рюшечки типа включателя "IStart".

    13.05.2012@Снежинск-каземат-11: всё проверено, еще энное время назад; и даже уже исчезло в пользу v2hw/fastadc/. Так что с чистой совестью "done".

adc200:
  • 28.08.2006: захотелось иметь также и программу adc200 -- чтоб можно было работать с adc200 напрямую, безо всяких sukhphase'ов.

    28.08.2006: чего хочется иметь (типа план работ):

    • Окно состоит из панели управления -- слева, и графика -- справа.
    • Разрешается уставлять количество точек для измерения/отображения.
    • Должно быть можно указывать в командной строке начальные значения параметров -- причем как параметров самого adc200, так и некоторых "управляющих": например, отображать ли выключатели "1" и "2", отображать ли параметры "количество точек" и "смещение".
    • (Тогда уж должно быть можно указывать и заголовок -- ключами -T или -title. Но их, наверное, съест Xt...)
    • Хотелось бы разрешить изменение размера окна по горизонтали -- чтобы размер графика юзер мог ставить по вкусу.
    • Надо отображать подписи к графикам -- снизу, справа (канал 1) и слева (канал 2).

      Горизонтальный же scrollbar должен быть ПОД горизонтальными подписями, но МЕЖДУ вертикальными -- те должны быть "по бокам" от него.

    • А главное -- надо мало того, что отображать подписи к графикам, так еще и иметь возможность как-то в командной строке указывать "масштабы" -- т.е., откуда и докуда идут "физические" диапазоны (а не [-128..+127]).

    31.08.2006: Ну -- некий первоначальный "скелет" сделан.

    01.09.2006: пообщался с Гусевым на тему, как у него сделано отображение в adc200 (выбор пределов для графика, etc.). Ой, хи-хи-хи. ы-гы-гы!

    Он всегда отображает просто 8-битовые числа -- [0..255], не задумываясь о смысле, а ПОДПИСИ рисует уже с учетом и диапазонов, и сдвигов нулей.

    Итого, вариантов получается два:

    1. "По-правильному" -- с пересчетом значений в настоящие вольты, выбором границ по максимальному из пределов, и отображение этих настоящих вольтов (и учитывая при пересчете и выборе пределов еще и сдвиги нулей).
    2. "По-простому" -- с учетом 8-битовой специфики adc200.

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

    01.09.2006: еще результат: я-то вчера пытался управляющую панель оформить в стиле "a-la Goussef" -- с XmFrame, с метками-подписями. Вышло -- хреновастенько, ибо рамки эти никак не уравниваются по вертикали с графиком, и вставленные в сетку они сдуревают при filling:=1... Короче -- надо продолжать идти СВОИМ путем, ценить СВОЮ самобытность, и пользоваться ЭЛЕМЕНТАМИ.. Вот с элементами, являющимися формами, всех вышеперечисленных проблем не возникает.

    12.09.2006: за прошедшие пару дней практически реализовал внутренности adc200. Highlights таковы:

    • График рисуется, по вертикали -- 256 пикселов.
    • Scrollbar -- это вообще засада: ему надо ОЧЕНЬ согласованно указывать minimum, maximum, sliderSize, и, как ни забавно -- value; это дурилово хоть и вгоняет value в разумный диапазон, но печатает на stderr ругательство.

    Что осталось сделать:

    • Корректная отработка кнопок "Loop" и "Once". 13.09.2006: сделано.
    • Команды "Сохранить измерение" и "Прочитать измерение". (При чтении оно нехай переходит в режим "пауза", и делает для всех считанных параметров SetKnobValue(), дважды. При отпускании же паузы оно автоматом пересчитает то, что есть реально.) 22.09.2006: сделано.
    • Рисование сетки. 13.09.2006: сделано.
    • Поддержка реперов.
    • Отображение статуса канала (по rflags+tag).
    • Поддержка параметров "то показывай, это не показывай" (на psp, собирая из argv[]). 22.09.2006: сделано, на psp_parse_as(), безо всякого собирания -- прямо поштучно из argv[].
    • Возможность также указывать начальные значения для параметров измерения. 22.09.2006: сделано. Но надо тогда указывать ВСЕ, а то, из-за невозможности cda/cxlib/cx-proto указать индивидуальный параметр, предшествующие ставятся в 0...
    • Плюс, возможно, указание масштаба по вертикали, и, возможно, указание "масштаба" по горизонтали.

      И, как минимум, отображение в горизонтальном масштабе не только номера отсчета, но и времени.

    • [Опционально] добавить возможность менять вертикальный масштаб. (Это -- very low priority.)
    • Возможность указывать из командной строки файл для загрузки. (Например, если argv[n] начинается со '/', НЕ содержит '=', НЕ начинается с "no", либо заканчивается на ".dat" -- это явно имя файла.) 22.09.2006: сделано, хотя и с иной эвристикой.

    21.09.2006: реализовал сохранение данных в файл. Детали:

    • Формат данных в каждой строке -- "N ns c1 c2 U1 U2".
    • Файл начинается с "сигнатуры" -- строчки "# Data adc200", что позволяет отбрасывать заведомо левые файлы.
    • Строчки "#!SUBSYSTEM:", "#!CREATE-TIME:" и "#!COMMENT:" унифицированы с форматом файлов Cdr, так что даже код парсинга в stat-функции просто скопирован из CdrStatGrouplistMode().

      Единственное что -- подсистема указывается как "adc200,ПОДСИСТЕМА" (по аналогии со всякими text/plain, text/html etc.). "ПОДСИСТЕМА" -- на будущее, когда можно будет app_name из командной строки указывать.

    • Параметры указываются в строках вида "#.PARAM ИМЯ ЗНАЧЕНИЕ".

    Заодно уж сделал, что и на горизонтальной оси отображаются не номера отсчетов, а время в наносекундах (для timing=TIMER считается, что 1ns/отсчет).

    22.09.2006: реализовал отображение "того, что под мышью" в statusline. Отображается, только если в этой точке есть данные -- иначе не пишет ничего. По приходу новых данных обновляется. Экспериментально получил знание:

    Перехватывать надо не только LeaveNotify (для убирания) и MotionNotify (для отображения), но и EnterNotify тоже! Дело в том, что при входе курсора в окно присылается только EnterNotify, так что корректно бы и на него тоже правильно реагировать, а не дожидаться последующих шевелений мыши.
    Так что во всяких istcc.c/phm-tsyline.c/lebed_meat.c используется недоделанный подход.

    Доделано наконец-то и чтение из файла, примерно по означенному в за 12-09-2006 проекту. Унификация -- код "условного обновления" шкал вытащен в отдельную функцию DisplayNewData(), и вызывается как по приходу данных, так и при загрузке.

    И уж, наконец-то, реализован "умный" разбор argv[], так что имя файла можно указать и из командной строки.

    Замечание: похоже, adc200 становится этаким reference-приложением для осциллографоподобных программ (за вычетом раздельных шкал, вместо будущих двух вложенных DrawingArea). И формат файла вполне можно брать за стандартный -- хоть для того же sukhphase.

    28.03.2007: поскольку программа уже давно вполне функциональна, то помечаем раздел как "done". Дальнейшие же усовершенствования пойдут отдельными разделами.

  • 10.11.2006: еще давно была мысля сделать поддержку варианта "светлые линии на темном фоне" (см. детали в секции Xh_colors за сегодня).

    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-цветов синхронизованы).
  • 27.02.2007: сходу -- добавил кнопочку "Int Start"/[Start], привязанную к свежевведенному параметру ADC200_PARAM_ISTART (одноразово-срабатывающий программный запуск).

    28.02.2007: поскольку одноразово-срабатывающесть в драйвере была переделана на нормальный булевский флаг, то и тут оно сменилось на onoff-panel "Int. Start", в нажатом состоянии зеленую.

    26.04.2007: ага, а упоминание show.params[ADC200_PARAM_ISTART] в text2show[] добавить забыл, и в результате ладно, что этот параметр нельзя было уставить из командной строки, но оно оставалось равным 0, из-за этого уставлялось в первом же запросе, и, как следствие, уставлялись в 0 и все остальные параметры. Исправил.

  • 28.03.2007: некоторые дальнейшие усовершенствования программы.

    28.03.2007: смысл -- как усовершенствование вообще, так и приведение к годности для дополнения/замены гусиных adc200 на linac1. Общие пожелания следующие:

    1. Свести все элементы управления в одну форму-элемент, которая бы по double-click'у сворачивалась/разворачивалась.
    2. Ввести-таки реперы, причем -- в том же окне. Не более 10.
    3. Изготовить возможность делать окошко максимально компактным -- чтобы, как у Гу, их несколько штук в один экран влазило.
    4. Сделать "альтернативный тулбар", который появлять при опции "notoolbar", чтобы управление оставалось.
    5. Отображать статус канала цветом (прямо на метке-заголовке?).
    6. Мочь указывать коэффициенты, чтобы отображались не вольты, а некие "научные" величины. Попросту -- множитель. (Или, для полного соответствия physprops_t.r, делитель?)

      (А по максимуму Старостенко согласен на некий "формульный язык" -- чтобы можно было организовывать зависимость от некоего "внешнего" параметра (частота комплекса, ток в некоем магните, etc.).

    Что уже сделано:

    • Старая панель управления (два "элемента" в одной сетке) переделана на ОДИН элемент, который может сворачиваться. Общие настройки отделяются от поканальных сепаратором.

      Чтоб размеры у тянутых окон при сворачивании/разворачивании не прыгали, пришлось AdjustPreferredSizeInForm() делать всей шобле: графику, scale_bot'у, а главное -- parent'у parent'а (frame) -- т.е., первому "сверху" child'у формы workspace. И оказалось, что последнего достаточно, а остальное вообще не нужно! Т.е., делать AdjustPreferredSizeInForm() надо только самому верхнему уровню, "непосредственно" управляемому бордюром окна. Почему так -- хрен его знает, в иной ситуации может оказаться и по-другому...

      17.03.2010: ага -- как раз НЕ так!!! И совсем НЕ так оно стало явно после введения Xh_hilite: размер прыгает обратно либо при его появлении, либо при исчезновении (в зависимости от того, какой части виджетов БЫЛО сделано XhAdjustPreferredSizeInForm()). Более детально -- см. в разделе по Xh_utils.c за сегодня.

    • Введен флажок nostatusline -- смысл ясен.
    • Введен флажок compact -- приводит к XhWindowCompactMask, плюс у всех элементов (Controls, frame графика) убирается тень.
    • Не оправдавшие себя vslider'ы в управлении сдвигами нуля заменены обычными текстовыми полями -- намного компактнее.

    26.04.2007:

    • Добавлен ключ "foldctls", сворачивающий элемент Controls сразу при запуске.

    29.04.2007: с реперами -- все маялся и маялся, куда же их помещать, как их ставить/удалять, и т.д. и т.п. И справа от графика -- фигово и будут проблемы с растягиванием графика. И слева от графика -- тоже хреново, много места занимают. И общий менеджмент -- тоже маета: держать их отсортированными, "не более чем n", всякое таскание мышью -- бе-е-е... Промаялся целый день.

    А потом, уже по пути на ужин -- снизошло:

    • У нас же уже есть табличка с двумя колонками -- настройки каналов. Ну так и пустить реперы дальше в этой табличке! А времена -- в колонке меток.
    • И количество реперов -- ТРИ штуки. И позиционировать их -- просто тремя кнопками мыши. Никакого хитрого менеджмента, типа добавления/удаления.
    • Попробовал сделать автосортировку, выполняемую по ButtonRelease. Фигово -- попытка поставить мышью слева направо 1-й, 2-й, 3-й приводит к тому, что все время трогается 3-й. Так что -- отключил сортировку, хотя временами ее отсутствие тоже путает (сходу, не глядя на табличку, фиг поймешь, какой из реперов какой). Чтоб упростить жизнь, около каждого репера ставится его номер.
    • При сворачивании управляющего элемента он превращается не в вертикальную надпись, а в жирный пунктирный вертикальный XmSeparator высотой с график. Идею подали "таскатели/сворачиватели" тулбаров в Netscape4/Mozilla. Получилось -- и места мало занимает, и хорошо заметно.
    • А "альтернативный тулбар" подселил прямо в строчку надписи "Controls", справа от нее. Так лишнего места по вертикали не занимает.

    Дальнейшие действия:

    • Надпись "controls" вообще-то нафиг не нужна.
    • Зато нужен меняющийся индикатор прихода данных -- например, просто "крутящесть" -- символы /-\|. И можно на них и навесить функцию сворачивания по double-click'у.
    • Горизонтальные сепараторы едва ли нужны -- только место жрут.

    03.05.2007: далее:

    • Добавил все "умные" махинации с событиями, ныне поддерживаемые Xh'ем -- XhProcessPendingXEvents(), () и ().
    • Слайдеры при Numpoints и Offset были нафиг не нужны, так что вместо двух отдельных строк сделал одну -- [Numpts]@[Offset].
    • Выкинул нафиг надпись "Controls" и сделал крутящесть /-\|.
    • Поскольку крутящесть расположена в дополнительном контейнере-форме, то имеет не того parent'а, что compact, так что теперь обработчику ButtonPress'а в closure передается "жертва", к которой и надо применять SwitchContentFoldedness().
    • Реализовал более умный, stateful, алгоритм обработки мыши для реперов -- теперь оно по ButtonPress выставляет флажок, что этот репер находится "в прооцессе перемещения", и далее уже просто по ЛЮБОМУ событию, дергающему PointerHandler(), меняет координату этого репера на координату события. (Раньше же оно смотрело на само событие -- есть ли ButtonNMask в поле state, не является ли событие ButtonPress'ом с button==N, etc.)

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

    19.12.2007: реализовал возможность указывать "коэффициент"-множитель для каждого канала -- параметры coeff1 и coeff2. Это (пока) влияет только на шкалы слева/справа, числа в statusline и реперах, и сохранение в файл. Плюс, введены строковые параметры units1 и units2, призванные заменить умолчательные единицы "V" (но это пока сделано не до конца).

    Доп. результаты/выводы по опыту изготовления сего:

    1. Менябельность коэффициента приносит проблему с определением ширин боковых шкал -- ибо at-compile-time неизвестно, сколько там будет символов.

      А основополагающая проблема -- неизвестно, какой ФОРМАТ использовать. Откуда его брать? Десятичным логарифмом от минимума и максимума (а если кроме множителя еще и базу указывать можно будет -- т.е., сдвиг нуля...)?

    2. Надо (по крайней мере, в общем случае) забивать на эту специфику adc200 -- диапазон в 256 значений -- и ВСЕ махинации производить уже в готовых "конечных" значениях (как это уже частично делается в adc333). "Махинации" -- включают в себя в первую очередь отрисовку, т.е., все заклинания вокруг графиков. При этом можно разные fastadc еще и унифицировать.

    27.12.2007: был ляп -- у параметра PTSOFS стояло minnorm=1, вместо надлежащего 0. Исправил.

  • 07.05.2007: надо бы в дополнение к программе adc200 сделать также и "стандартный" plugin adc200, чтоб можно было изготавливать экраны, в которых бы стояли рядышком осциллограмма и переключатель каналов.

    29.12.2007: да, плагины такие будут требоваться в нескольких разных программах -- на данный момент видны управление пучковым датчиком и стендом для Снежинска. И -- также будет требоваться плагин для adc333. Так что -- впору изготавливать "общую инфраструктуру" для графиковых плагинов; Xh_graph?

    Чтобы оно само создавало виджеты graph и axis, само бы все отрисовывало, реагировало бы на resize, etc. Для этого, видимо, надо будет указывать N штук (N<=5) буферов, пар пределов, и т.д. Короче -- некий "graphrec". И еще -- чтобы некие "user"-supplied hooks вызывались при прорисовке (для рисования реперов, средних, ...) и при обработке событий от мыши (для установки реперов и "резиночки"). Общая "техника" -- программа указывает "маску событий" (до-тени; до-сетки; до-графика; после-графика), а hook'у передается "момент" -- для какого из этих событий он вызван. В общем -- "объект"/"класс" для рисования графиков.

  • 07.05.2007: надобно, как и в Chl_histplot'е, переходить на XDrawLines().

    10.05.2007: готово.

  • 10.05.2007: понадобилось уметь ограничивать частоту обновления данных -- чтобы не с той скоростью, с которой сервер присылает, а не-более-чем-N-раз-в-секунду.

    Вначале-то хотел это повесить на сервер, указывая частоту в параметре immediate, но, поразмыслив, пришел к выводу, что проще это делать в клиенте.

    10.05.2007: сама идея очень проста -- указывать "максимальные герцы" (0 -- фича отключена, и обновлять со скоростью отдачи данных сервером), и при получении данных проверять, прошло ли с момента предыдущего обновления времени более, чем 1000000us/maxfrq.

    Так что -- ввели еще один параметр, maxfrq (умолчание=0).

    Guard же для сравнения времен имеет такую логику -- "если oneshot==0 и show.maxfrq!=0", т.е., по нажатию кнопки [|>] она результат показывает сразу.

    Точность отработки заданной частоты, конечно, далеко не выдающаяся: во-первых, потому, что частота прихода данных и заданная частота не обязательно кратны, а во-вторых (и это главное!) время-то отсчитывается от ПРОШЛОГО отображения, что приводит к охренительной погрешности. Впрочем -- это уже неважно.

    Вообще-то, конечно, стоит подумать о более корректной реализации ("привязывающейся" к "абсолютному" времени), но пока что и так пойдет. Так что -- "done".

  • 18.12.2007: Бак заикнулся о том, что очень хочется уметь видеть вместе с текущим сигналом также некий "сохраненный хороший", чтобы сравнивать с ним.

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

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

    • Некий вопрос, ОТКУДА брать "хороший" режим? Вначале думал добавить лишние кнопки сохранить/считать "хороший".

      Но, подумав, сделал проще: есть кнопка "скопировать текущий в хороший", копирующая mes_info[]+mes_data[] в svd_info[]+svd_data[] и выставляющая флажок "отображать хороший" (use_svd), плюс кнопка "забыть хороший", просто сбрасывающая use_svd.

      Так что реализация ИСКЛЮЧИТЕЛЬНО простая.

    • В качестве цветов пока используется для обоих каналов GRAPH_PREV, но GC 2 штуки, так что при желании можно и поменять.
    • А вот с пиктограммами кнопок -- некий вопрос: пока что используются getbkgd и rstbkgd, но это нехорошо -- юзеры думают, что это фон (для последующего вычитания); надо придумывать другие.

    Про всякие усреднения -- надо думать (как минимум, ГДЕ это рисовать; прямо на графике?). А диапазон можно задавать 1-м и 3-м реперами.

    29.12.2007: с пиктограммой возникла идея: ведь "хороший" режим это что? -- правильно, это "good", "fine", "wonderful", в общем -- положительные эмоции. А оные обозначаются рукой с поднятым большим пальцем. Так что -- надо как-то творчески переработать пиктограммы рук из progman_icons.bmp (изобразить с другой стороны и поднять палец).

    03.04.2008: да, сделал такие пиктограммы -- по большому счету, с нуля, на progman'овские только ориентируясь. Они названы setgood и rstgood, и уже задействованы в обеих (на настоящий момент) осциллографных программах -- adc200 и nadc333.

  • 19.12.2007: необходимо добавить еще фичу: переключатели J5=1,2 и J6=1,2, которые во включенном состоянии меняли бы селекторы диапазонов на +-1,2,4,8, и влияли бы на вычисления напряжений -- как в ndbp_adc200_plugin. И, естественно, возможность указывать их состояния из командной строки.
  • 19.12.2007: и еще -- Батраков высказал резонное пожелание более толстых линий. Видимо, также надо делать параметр командной строки.

    13.05.2012@Снежинск-каземат-11: да, фича давно реализована.

  • 20.12.2007: такая полукрамольная мысля -- как дорисую новый вариант adc333 -- надо бы общую для них обоих функциональность вынуть в некий общий же модуль, типа fastadc_common.[ch].

    20.12.2007: собственно -- практически сразу же стал ваять этот fastadc_common.[ch], складывая в него стандартную для быстрых АЦП начинку, так, чтобы программа конкретного АЦП указывала только некие специфические вещи (плюс -- населяла бы окно (в т.ч. ctl_form), делала бы отрисовку графиков и чтение/запись данных.

    Первый прицел -- и adc200 потом перевести на этот модуль.

    А потом -- чем черт не шутит, может, еще более стандартизируем все это как-нибудь, так что можно будет обходиться просто неким текстовым описанием (будет тогда та самая программа "осциллограф вообще").

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

    Вот когда потребуется ТРЕТЬЯ программа -- тогда реализую, на новом уровне понимания. Опять же, к тому времени будет "правильно" сделанная adc333, с человеческим представлением данных, а не только узкоспециализированная под 256 значений adc200.

    03.03.2010: в связи с потребностью в программе для ADC200ME возвращаемся к этой теме. Итак:

    • Свежую версию пока девелопим в liu/xmclients/, в интересах adc200me.c.
    • Концепция несколько поменялась: теперь МАКСИМУМ действий пытаемся спихнуть именно на fastadc_common.c, а объём кода конкретного клиента стараемся свести к минимуму -- в идеале просто к описанию параметров конкретного устройства (чему всемерно помогает наличие у всех таких АЦП констант *_MAX_NUMPTS, *_NUM_LINES, *_DATAUNITS; надо бы еще *_LO_VAL и *_HI_VAL ввести).

      А именно:

      1. Отрисовкой графиков будет заниматься общий код, и все переменные на эту тему из .h-файла унесены.
      2. Вместо былой двух-уровневой структуры "type-specific rec, включающая стандартный fastadc_stdparams" теперь есть стандартизованный fastadc_show_t, уже включающий массивы data[], coeff[], zero[], units[], descr[] размером в MAX_LINES.
      3. Теперь psp-таблица создаётся в RunFastADC() на лету:
        • СЕЙЧАС она комбинируется из стандартных параметров и предоставленных программой, плюс PSP_END().
        • Надо также сделать, чтобы программа могла указывать PSP-таблицу с привязкой только к НОМЕРАМ параметров, независимо от типа fastadc_show_t.
          1. Видимо, надо просто определить некий тип "структура, содержащая массив", и программы-юзеры будут указывать PSP_xxx() как бы в ней, а уж при слиянии таблиц оно будет добавлять смещение массива параметров в fastadc_show_t к psp_paramdescr_t.offset.
          2. После этого fastadc_show_t окончательно оприватится из .h-файла в .c.
          3. А в CXv4, с адресацией параметров обычными каналами и по именам, эта проблема и вовсе не будет стоять
        • И psp-параметры chanN/nochanN, coeffN, zeroN, unitsN, descrN также надо прилеплять к слитой-вместе таблице at-run-time -- в нужном количестве.
      4. На долю же самой программы остаётся только создание панельки управления.

        (В идеале -- и это бы как-нибудь упростить, например, сделав текстовым описанием. Видимо, в v4 так и будет, там есть все ингредиенты: 1) параметры адресуются именами; 2) имеется парсер из текста в группировки/элементы; 3)  ??? )

      5. И даже сохранение и чтение файлов будет делать fastadc_common.c:
        • Собственно данные сохраняются без проблем -- N колонок сырых данных, а за ними -- N колонок пересчитанных.
        • А уж параметры -- тем более: просто номер параметра и его значение.

          С учетом наличия psp-таблицы с описанием параметров, можно дальше -- через "#" -- писать осмысленное вещи: имя параметра плюс, ориентируясь на psp_paramdescr_t.t, текстовое значение; в т.ч. -- lookup-имена.

          Замечание: имена-то она станет брать первые найденные, так что надо будет более человекочитаемые пускать раньше (в случае с adc333 -- в range_lkp[] должно сначала идти "4096", а уж потом -- "4".

    P.S. А вообще, какого лешего это всё здесь, а не в разделе oscilloscope? Видимо, потому, что это пока не совсем "общий" осциллограф...

    12.03.2010: продолжаем:

    • Все заявленные выше махинации по созданию PSP-таблицы теперь исполняются. Изрядно монструозный код!
    • В RunFastADC() добавлен параметр line_num_base -- указывающий, откуда нумеровать каналы (с 0 или с 1).
    • Плюс параметры min_val и max_val, описывающие возможный "разумный" диапазон входных данных.
    • А главное -- в список параметров понатыканы комментарии, разделяющие разные группы, а то уж совсем невозможно стало читать.

    17.03.2010: в основном доведено до юзабельного состояния:

    • К line_num_base добавлен также line_def_name -- как именовать канал (у ADC200ME -- никак, просто номер).
    • В параметре info2on передаётся функция, по текущему mes_info[] определяющая, какие из каналов "активны", т.е., а) присутствуют в данных, и, следовательно б) могут быть отображены.
    • Добавлено создание панели управления. В нужный момент вызывается указанная юзером функция-создатель, которой передаются функции
      1. mkstdctl -- создаёт "стандартный" элемент управления, такой, как метка, управление масштабом, атрибуты реперов, и т.д. Тип элемента указывается параметром kind (один из FASTADC_CTL_nnn), а параметры a и b дают "уточняющую" информацию (к какой линии относится элемент, номер репера).
      2. mkparknob -- создаёт по указанной simple-Knob-спецификации ручку, регистрируя её у себя в k_params[] и ссылая её на ParamKCB().
    • В дополнение к панели управления оно также делает и индикатор "/-\\|", плюс -- в случае notoolbar -- лампочку сервера. Программа также может разместить эту штуку сама, при помощи mkstdctl(,FASTADC_CTL_COMMONS,,).
    • Сделано (скопировано из adc200/nadc333) отображение значений в реперах и под курсором мыши в statusline.

    Дальнейшие действия:

    1. Почему-то оно дурит при указании параметра show=WIDTH при VAL<700 -- график "схлопывается" и оно начинает ругаться. Похоже, как-то связано с только-что-"управильненным" использованием XhAdjustPreferredSizeInForm().
    2. Сохранение/считывание файлов пока не сделано, а оно понадобится.
    3. Надо менять парадигму диапазона отображения, чтоб было как у обычных осциллографов: при x1 текущий диапазон должен занимать ВСЮ шкалу.

      Видимо, надо переходить от всегда-определенных min_val/max_val к per-line-диапазонам, высчитываемым программой-юзером по текущему mes_info[].

    4. И, кстати, насчет экранного увеличения: Мелкостенко рассказал, что у LeCroy'ев есть удобная ручка "Offset" -- когда сигнал по абсолютному значению большой, но хочется детально посмотреть форму "полочки", то можно включить "крупный зум", и Offset'ом пододвинуть сигнал в экран.

      У нас же сейчас -- 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: продолжение:

    • поменял парадигму диапазона отображения, как и планировалось: теперь при x1 текущий диапазон занимает всю шкалу. Для этого программа-юзер предоставляет функцию info2ranges(), заполняющую mes_mins[] и mes_maxs[], передаваемые ей параметрами, по текущему mes_info[].

      Параметры min_val/max_val оставлены -- они используются для расчета полей под подписи, плюс, если info2ranges==NULL (как будет для ADC4 и нынешнего ADC200/NADC200), то и для самостоятельного заполнения mes_mins[]/mes_maxs[].

    • Также добавлен параметр-функция datum2dsp -- она пересчитывает указанное значение (обычно микровольты) для указанной линии и при указанном info[] в отображаемое на экране число. В adc200me оно просто делит на 1000000., а в nadc200 -- требуется уже реально хитрый пересчет с учетом диапазона и сдвига нуля.
    • С использованием вышеописанного теперь размер полей для подписей слева/справа вычисляется более интеллектуально: берется максимум среди всех линий, причём берется максимальная ширина от min_val и max_val.
    • И вместо фиксированного использования LFRT_TICK_FMT теперь передаётся dpyfmt, интеллектуально модифицируемый для каждой линии с учетом её zero и coeff.
    • Начато изготовление второго клиента -- nadc200.

    25.03.2010: nadc200.c доделан!

    25.03.2010: а-а-а!!! Идиот!!! Фтопку все FASTADC_LINE_BASE_xxx -- надо просто передавать "базовый символ"!

    Сделал. Кстати, это даёт вообще полную свободу -- при надобности можно будет использовать и заглавные буквы, и маленькие.

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

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

    Несколько замечаний по теме:

    1. Рисовать будут хотеть просто ЛЕВОЙ кнопкой мыши.
    2. Посему -- пусть установка реперов делается Ctrl+ЛеваяКнопкаМыши.
    3. И еще: а ведь полезно бы кроме вертикальных иметь также и горизонтальные реперы. Хотя бы парочку, а не тройку -- чтоб какой-нить-ам диапазон устанавливать...

    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: за прошедшую неделю сделано много-много -- в основном с подачи Роговского:

    • Кнопка однократного запуска -- [|>] -- теперь "залипает" (остаётся нажатой) вплоть до прихода данных.
    • Рядом с бегунком добавлено отображение FPS'ов и времени с момента последнего прихода данных; при отключенном running оба поля пусты.
    • У поля PTSOFS теперь во всех *adc* также есть стрелочки [v\^].
    • В RunFastADC() введён параметр param_phys_rs -- указатель на int-массив штучек, аналогичных phys_r у обычных каналов. Если N-й элемент !=0, то для N-го параметра получаемое от сервера значение перед отображением делится на это число.

      Понадобилось для того, чтоб параметры WAITTIME отображать не в МИКРОсекундах (как их понимают драйверы), а в МИЛЛИсекундах.

    • Также добавлен параметр-функция info2exttim, возвращаущая, не включено ли внешнее таймирование.

      При внешнем таймировании единицы времени нигде не подписываются.

    • И -- наконец-то сделано окончательно-правильное решение проблемы с именованием каналов: вместо всяких "базвых символов" просто передаётся массив, в котором перечислены имена. Естественно, пришлось чуть усложнить код генерации таблицы параметров (per-channel-параметрам типа chan/magn/coeff/... теперь не просто заменяется '0' на другой символ, а добавляется строка.

      Зато теперь у ADC8-12 значатся каналы 0A, 0B, 1A, ...

    • ДО первого прихода данных ручки каналов, не-инициализированных из командной строки, ставятся в состояние JUSTCREATED -- так что голубеньки.

    31.05.2010: давно мешает то, что из GUI-клиентов (вследствие API больших каналов) проблемы с "залипанием" измерений:

    1. В момент ожидания включить ISTART никак нельзя.
    2. И ни SHOT, ни STOP не сделаешь -- оно ведь только через обычные каналы. Которые, увы, НИКАК не определяемы по ссылке на большой канал.

    И вот, в беседах с Роговским, родилась мысль: а что, если дополнительным параметром указывать программе еще и ссылки на обычные каналы SHOT и STOP? А потом -- мысль еще получше: указывать этим дополнительным параметром номер базового ОБЫЧНОГО канала для данного устройства. Тогда там можно будет любую функциональность добавить.

    Конкретно функциональность такая:

    • Во-первых, конечно, кнопки, "Импульс" и "Стоп" -- маппирующиеся на эти два обычных канала.
    • А во-вторых, Роговский придумал такой алгоритм, как можно приблизить поведение к поведению обычных осциллографов:
      1. При переходе от ISTART=0 к ISTART=1 делать SHOT=1 (в принципе подойдет и STOP=1).
      2. При переходе от ISTART=1 к ISTART=0 делать SHOT=1 (в принципе подойдет и STOP=1).

      Суммируя вышесказанное -- можно высылать 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, и для того, чтобы это работало, нужно лишь пара косметических изменений:

    1. Добавить ко ВСЕМ константам и _lkp[] префиксы типа-осциллографа -- чтобы программа могла внаглую #include'ить соответствующие *adc*.c по несколько сразу, не опасаясь коллизий имён.
    2. Чтобы не возникало проблем с множественными 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() с диким списком параметров есть ДВЕ:

    1. Маленькая RunFastADC(), принимающая лишь минимально необходимые параметры (argc/argv, def_app_name/def_app_class) плюс указатель на "функцию-описыватель".

      А вот последней передаётся указатель на структуру fastadc_dscr_t, которую надлежит начинить информацией. Начинение же делается...

    2. ...функцией FillFastADCDscr() -- принимающей ту тучу параметров.

    Это позволило заиспользовать в liuclient'е тот же самый код.

  • 24.12.2007: публика -- в лице Гусева -- хочет мочь убирать реперы, а не только перемещать. В принципе, резонное желание. Чтобы не целиться -- видимо, убирание должно делаться каким-то нажатием не на графике, а на поле отображения чисел.

    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.

  • 13.05.2012@Снежинск-каземат-11: поскольку всё касательно клиента adc200 переехало в v2hw/fastadc/, то этот раздел считаем выполнившим своё предназначение и впредь замороженным.
*sukhphase*:
  • 16.03.2009: поскольку sukhphase.c/old_sukhphase.c не менялись с осени 2006г., а сейчас они уже устарели и вместо них используется ромкин SAFI2, то пора их наконец-то из дерева удалять.

    Сделано... ("В де-етском саду-у-у скоро бу-у-удет Новый го-о-од..." :-)).

:
cx-plotter:
  • 20.04.2009: нужна тулза, которая бы позволяла:
    1. Рисовать на самописце любые каналы -- аналог EPICS'ного striptool'а.
    2. Рисовать графики "зависимость канала Y от канала X" -- это Старостенко возжелал.

    20.04.2009: Старостенко-то захотел иметь возможность просто как-то щелкнуть на канале -- и чтоб он попал на график, как-то еще щелкнуть на другом -- и чтоб он попал на другую ось.

    Понятно, что упихивать такую функциональность в обычные программы -- будет уже жирновато, так что надо заводить отдельную утилитку. Которая и умела бы рисовать что угодно от чего угодно -- включая каналы от РАЗНЫХ подсистем.

    Как это можно/нужно реализовать -- в принципе понятно:

    • Во-первых, это всё будет наиболее осмысленно при переходе на систему ИМЁН, но даже и сейчас уже можно сделать.
    • Удобнее всего -- drag-and-drop'ать, как это сделано в MEDM+StripTool.
    • В Motif'е уже есть drag-and-drop по средней кнопке мыши, так что уже сейчас добыть ссылку на канал можно, вызвав окно "Knob properties" и взяв поле Source.
    • А можно попробовать выпендриться сильнее -- навесить обработчик на Ctrl+MB2, чтобы оно при этом отдавало в DnD сразу ссылку, без вызова окошка.
    • В любом случае -- надо уметь в cx-plotter'е реагировать на DnD на него.
    • И еще следствие -- в окошке "Knob properties" при показе ссылки и разрезолвленного номера надо их отображать не через "\n", как предложено в "CXv2+" за 19-10-2008, а двумя РАЗНЫМИ XmLabel'ами.
chlclients/:
  • 12.06.2004: разборки со скриптами-генераторами:

    12.06.2004:

    1. Во-первых, случайно заметил, что все chlclients перекомпилируются при внесении изменений, например, в Xh.h, хотя их это (со времени введения схемы "один бинарник и множество .so-шек") касаться ну никак не должно. Их вообще должен интересовать только cxdata.h.

      Оказалось, что там в mkclient.sh оставался просто закомментированный старый код (который main(){...ChlRunSimpleApp(...);}), и, соответственно, незакомментированные старые #include.

      Выкинул все, оставив только misc_macros.h и добавив cxdata.h. Заодно пришлось в mkphysdb.sh вставить #include "cda.h" -- там-то оно актуально, из-за cda_physinfodb_rec_t.

    2. Во-вторых, полезши разбираться с пердыдущей проблемой, узрел в mkphysdb.sh артефакт тех же до-.so-шных времен -- вписывание в allsystems.h "полной базы данных" groupings_database[].

      Выкинуто нафиг.

    3. В-третьих, со стародавних времен (точнее, с 11.08.2003 :-) оставался "зародыш" единого скрипта -- ProcessList.pl. Со введением схемы с подгружаемыми модулями надобность в нем почти отпала, а как сделаем настоящую СУБД -- вообще вся директория chlclients исчезнет.

      Так что -- файл стерт.

  • 08.07.2004: как насчет ввести в формат SimpleClients.lst флажок "no binary/symlink" -- т.е., чтобы оно только создавало файл SYSTEM_db.so, но НЕ создавало симлинк SYSTEM->chlclient?

    Это понадобится для не-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=-".

  • 25.09.2004: а однако, все ж таки стОит перейти от {mkclient,mkphysdb}.sh к ProcessClients.pl: во-первых, на Perl'е парсинг всяких "option=value" проще, а во-вторых, чтобы все было в ОДНОМ скрипте.
  • 10.12.2005: еще позавчера обнаружил "страшное": mkclient.sh НЕ делал клиентам #include "cx_types.h". И-за этого formula-программы не могли пользоваться константами CX_VALUE_{LIT,DISABLED}_MASK.

    10.12.2005: делов-то -- ну добавил :-).

  • 24.06.2009: обнаружилась в mkclient.sh существенная дурь: он не позволяет указывать после DEFSERVER'а еще НЕСКОЛЬКО параметров -- только один. Вот просто отделение "всего после" сделано уродски -- через
    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. И, возможно, лучше и нагляднее было бы вернуть ту былую конструкцию, что "...должна была выкусить всё вплоть до первой ';'". включительно

  • 02.01.2010: ввёл в mkclient.sh новую фичу -- теперь если перед app_name'ом указан '+', то это означает, что вместо обычного physinfo там присутствует physinfo-database под именем ${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 *).
knobtest:
  • 07.04.2008: Создал новую "программку" -- chlclients/knobtest, который должен будет содержать в себе "демо" всех возможных ручек.

    07.04.2008: она будет состоять из нескольких закладок:

    • "Basic" -- основные виды ручек (ВСЕ типы).
    • "Advanced" -- картинчатые ручки и варианты расцветок (LOGC_*).
    • "Canvas" -- содержимое canvastest'а.
    • "Tree" -- древесный виджет (?).
    • "Interactions" -- испытания всяких эффектов взаимодействия (CMD_REFRESH, CMD_CALCERR, CMD_PRGLY, ...).
    • "Tests" -- а вот тут химичить, как сейчас над widgettest'ом, для испытаний новых ручек.

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

:
libs:
misc:
  • 19.04.2004: исправлено сходу: обнаружил, что при misc_macros.h::check_snprintf() отсутствовал __attribute__((format)). Добавил.
  • 24.10.2004: вообще-то злобная мысль: а не стоит ли объединить libmisc и libuseful? Т.е., присоединить маленькую libmisc к солидной libuseful?

    С одной стороны -- хочется, поскольку "*_memcasecmp()" по статусу лезет именно в libmisc, а нужна она -- в libuseful, для psp. С другой стороны -- душа не лежит, уж шибко эти библиотеки разные по "классу"...

    12.05.2007: нет, НЕ СТОИТ, и даже НЕЛЬЗЯ объединять эти две библиотеки -- они должны быть раздельными. Это стало ясно сегодня, по результатам вытаскивания cxscheduler.o из libuseful.a в отдельную библиотеку.

    Повторю здесь резоны и критерии:

    • В libmisc идут маленькие, самодостаточные и не от кого более не зависящие модули -- "расширения libc" в смысле базовых функций/примитивов.
    • В libuseful же помещаются уже более навороченные вещи, которые, по аналогии с POSIX, были бы не в libc, а в других, отдельных библиотеках.
    • В результате -- мы имеем корректный стек библиотек, где зависимости только в одну сторону (в частности, libuseful зависит от libmisc).
    • Так что в командной строке линкера можно спокойно указывать вначале libuseful, а libmisc после него, и не беспокоиться о циклических зависимостях и о потребности в "-(" и "-)".

    Посему -- "withdrawn".

    14.05.2007: произвел обоснованное выше (необходимое для Xh_cxscheduler'а) переселение memcasecmp.c и timeval_utils.c из useful/ в misc/.

  • 21.01.2005: обнаружил занятный прикол с парсингом double-форматов: я-то пытался указать через "%-0.0f", что НЕ НАДО делать никакой ширины поля, и выравнивать по левому краю (чтобы писалось, например, "23C" ("C" -- units), по левому краю). А оно -- лидирующий нолик просто проигнорировало: поняло как флаг "zeropad", да еще и срезало его из-за более приоритетного "leftadjust"; в результате вышло "%-.0f".

    Но тем не менее -- оно сделало "что надо", просто выделив на текстовый виджет соответствующее дефолтное количество колонок -- в данном случае 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.
    • В Cdr используется функция fprintf_f_rtrim(), пишущая во внутренний буфер, обрезающая хвостовые пробелы, а потом буфер -- в указанный fp. По параметрам совместима с просто fprintf()'ом одного числа.
    • В Chl_knobprops.c оказалось вообще просто -- там во всех местах, кроме одного, используется функция Ltsv(), делающая left-trim. Вот в нее вставил за компанию и right-trim. А в единственном оставшемся месте -- "ручное" отсечение.

    Вообще-то кое-где там стоИт p=buf+snprintf()..., что может вылезти боком.

  • 21.01.2005: кстати, а как насчет ввести формат "%d", который бы сводился к "%.0f"? Наглядности для, а? (Чтобы не нужно было грузиться вопросами типа того, что в предыдущем пункте.)

    29.04.2005: думаю -- не стоит. Ведь в случае, если такой формат пройдет мимо Cdr, то он останется целочисленным, а применен будет -- к double-значению, что сходу будет ошибкой. Так что -- неа, и раздел помечаем как "done" и <S>.

  • 09.06.2005: в misc_macros.h был древний-древний макрос OUT_OF_RANGE(), который использовался только в {oldcx,curcx}/server/cx-server_link_new.c.

    09.06.2005: Поскольку оно уж три года как вышло из употребления, да и корректность (с точки зрения возможных side-effects -- см. "info gcc 'c extensions' 'naming types'") сомнительна, то я этот макрос грохнул.

  • 09.06.2005: а вот неплохо бы, по аналогии с 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_drv.c::bridge_init() -- забывало делать ++ при разборе @-префикса).
    • А на этот сервер натравливалась свора cdaclient'ов с таймаутом в 3 секунды.
    • В результате после 5-го недо-коннектившегося cdaclient стал отваливать с ошибкой "Resource temporarily unavailable" от cx_open().

      Всё потому, что в проверке -- IsATemporaryCxError() -- используется IS_A_TEMPORARY_CONNECT_ERROR(), НЕ считавшая EAGAIN/EWOULDBLOCK временной ошибкой.

    После добавления всё исправилось -- теперь оно говорит "will reconnect".

  • 10.06.2005: насчет менеджмента fd_set'ов, с точки зрения производительности, security и портабельности на Win32: похоже, надо будет слегка под-усложнить эту подсистему и, возможно, даже частично вынести в отдельный файл, и кабы не унести из libmisc в libuseful. Детали -- в комментарии.

    10.06.2005: по порядку:

    • Идея раз: кроме maxSET иметь еще и minSET -- чтобы не тратить в циклах переборки/поиска время на точно не относящиеся к делу дескрипторы. (Это особо актуально для Win32.)
    • Насчет security -- очень пользительно почитать "SECURITY.NNOV: Multiple applications fd_set structure bitmap array index overflow" и followup на него.
    • В Win32 сокет-дескрипторы НЕ являются небольшими положительными числами и fd_set устроен совсем по-другому. Так что, видимо, под Win32 наши GENERIC_FD_NNN должны выглядеть весьма иным образом (в частности, поиск максимального/минимального).
    • И вообще -- возможно, даже сами "итераторы по fd_set'у" должны выглядеть совсем другим образом -- это ж просто тупые циклы переборки массива, а то итераторы в POSIX'ном стиле будут занимать вместо n аж n^2 времени!

      (При введении таких итераторов, впрочем, надобность в maxSET (а особенно -- в minSET) становится весьма невеликой.)

    • В свете того, что "в Win32 сокет-дескрипторы НЕ являются небольшими положительными числами и fd_set устроен совсем по-другому", то:
      1. Проверка "можно ли поместить данный fd в fd_set" НЕ делается при помощи "fd<FD_SETSIZE", и должна выполняться ПРИНЦИПИАЛЬНО ПО-ДРУГОМУ -- причем, в задачу начинает входить еще и сам "целевой" fd_set...

        (А ведь в задачу может входить и не один fd_set -- тогда вообще зад... Или не может?)

        В общем, сию проверку надо, как минимум, вынести в отдельный макрос.

      2. Становится принципиально кривой наша реализация "слияния" двух fd_set'ов -- FD_ADD(src,dst), делающая просто побитовый OR.

        С другой стороны -- оно сейчас используется скорее оттого, что нынешние cxd+cx-server+cx-porter НЕ используют cxscheduler+fdiolib, а в будущем cxsd такая функция станет ненужна. Так что на эту проблему можно бы и забить.

    11.05.2007: да, надо забить, и НИЧЕГО с этой "подсистемой" делать не надо -- вот почему:

    • Специфика махинаций с файловыми дескрипторами -- для "select() & Co." -- должна быть ТОЛЬКО в cxscheduler'е, который под Win32 будет все равно свой. Видимо, на основе Xh_cxscheduler'а.
    • Единственное затрагиваемое место в fdiolib'е -- функция CheckFD(), но она по смыслу может быть перенесена в cxscheduler, где и будет специфична для платформы.
    • В cxlogger'е использовать fd_set для левых целей совершенно нефиг -- но этот вопрос обсосан еще в конце 2003г.
    • Естественно, исполняющие среды для контроллеров -- также будут пользоваться этими специфичностями, но для них вопрос портабельности неактуален.

    Так что -- "забиваем", помечая как "withdrawn".

  • 23.01.2006: (поскольку у misc_macros.h нету своего раздела, то тут) давно пора заиметь стандартную, доступную всем клиентам функцию "получить текущее время в текстовом виде", для выдачи его во всяких сообщениях. Общепринятый в CX формат -- DD/MM/YYYY-HH:MM:SS.

    Реально оная функциональность есть в 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: продолжение в ту же степь:

    • Во-первых, нужна функция, которая бы стандартным образом выдавала не только текущее, а УКАЗАННОЕ время.
    • Во-вторых, наткнулся я (в первую очередь в описании date(1)) на упоминание стандарта ISO-8601,описывающего международно-портабельный способ представления даты-времени. В нем дату+время надлежит представлять как "YYYY-MM-DDThh:mm:ss" (например, 2006-01-30T20:43:15).

      Так вот -- надо бы все-таки переходить на такой формат, тогда и о совместимости со всякими MathCAD/Matlab можно не волноваться.

      (Вопрос только в human-readability строк с символом "T".)

    В общем -- сделано:

    • Введена функция
      static char *stroftime(time_t when, char *dts)
      где параметром "dts" передается строка-разделитель для даты и времени (Date-Time Separator).

      И теперь strcurtime() сводится к вызову stroftime.

    • А вот собственно формат сменен с нашего волюнтаристского DD/MM/YYYY на YYYY-MM-DD.

      Но -- для читабельности -- strcurtime() использует "-" вместо "T".

    • На новую функцию переведена Cdr, с соответствующей модификацией комментария-заголовка.
    • ...а также XhLoadDlgShow().

    На будущее:

    • В CXv4 надо будет перевести на такой стандарт и cxlogger.
    • Общепринятым расширением ISO-8601 (реально это -- часть стандарта) является добавление ".SSS" (миллисекунды) либо ".SSSSSS" (микросекунды). Этим можно также пользоваться (да хоть в тех же логах) -- "...%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: в продолжение унификации:

    1. Поскольку 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 этот код также скопирован.

    2. cx-starter, во-первых, при выдаче от 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() из них выкинуто и осталось теперь лишь в клиентах.

  • 10.04.2006: ч-черт! Обнаружилось, что, оказывается, ParseDoubleFormat() НЕ поддерживает флаг апостроф -- "'" -- "группирователь тысяч".

    Почему -- фиг его знает: что мешало сделать это весной 2003-го ()? Казалось бы, ничего сложного нет, ибо он и не интерферирует с другими флагами (как "+", и никак не влияет на длину... Просто лень было, что ли?!

    А сейчас вот -- захотелось...

    10.04.2006: вроде сделал.

    1. Добавил FMT_GROUPTHNDS=1<<5.
    2. Детектирование апострофа в ParseDoubleFormat().
    3. Добавление апострофа в CdrCvtLogchannets2Knobs().

    Так, а что у нас с размером knobinfo_t.dpyfmt? "Максимальный" формат -- "%#0+'20.10f", 11 символов плюс NUL, так что в 16 все влезает.

    А вот дальше... Вроде как все сделано, не ругается, но толку-то -- не нашлось НИ ОДНОЙ локали с символом-группирователем :-)

    И, теоретически, имеется вопрос -- а поддерживается ли обратное преобразование с символом-группирователем функцией strtod()? Судя по найденным исходникам -- вроде как да, хотя в man-странице ничего не сказано.

    Ну да ладно -- "done". (Да, в 4cx/ скопировано.)

  • 19.01.2007: давно уже пора в дополнение к 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> меня как-то не тянет.
  • 24.02.2007: добавил к errno-проверяющим макросам -- SHOULD_RESTART_SYSCALL() и IS_A_TEMPORARY_CONNECT_ERROR() в misc_macros.h еще и ERRNO_WOULDBLOCK() -- проверку на EWOULDBLOCK/EAGAIN. Взял из fdiolib, а используется аналогичная проверка также в tsycamlib.c и в connlib.c.
  • 27.02.2007: за последние полторы недели при изготовлении ndbp выкристаллизовалась общеполезная функция-макрос, которая и переехала в misc_macros.h: 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)
  • 04.12.2008: обратил я внимание, что в misc_printffmt.c имеется аж четыре штуки fprintf(stderr,...) -- непорядок!!! Надо переводить оное на архитектуру "_lasterr"!

    04.12.2008: да, сделал. Публичный API на эту тему -- printffmt_lasterr().

    05.12.2008: и в 4cx/ тоже портировал -- misc_printffmt.c просто скопировал, а в misclib.h, поскольку он в 4cx чуток отличается, просто добавил строчку.

  • 05.12.2008: странно, что у нас с давних пор сборка строки формата, ранее распарсенной при помощи ParseDoubleFormat(), делается прямо в Cdr.c::CdrCvtLogchannets2Knobs(), и только там -- это ж неправильно!

    13.12.2008: да, сделал новую функцию -- CreateDoubleFormat(), и перетащил сборку туда. Теперь эту функцию можно использовать и в других местах.

    Как водится, и в 4cx/ также портировал; заодно адаптировал и единственного пока пользователя -- DPYFMT_fparser().

  • 15.01.2009: крупная перетряска парочки. misclib+misc_macros.h. Обусловлена она двумя причинами:
    1. Надо наконец унифицировать все варианты "check_fd_state()" и "set_fd_flags()", сделав централизованную реализацию.
    2. Файл misc_macros.h должен бы содержать именно МАКРОСЫ, а не навороченные вещи типа n_read()/n_write()/uintr_read()/uintr_write() и ISO-8601.

    Так что -- населяем 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 и:

    1. Функции n_read() и n_write() -- в misc_n_read.c и misc_n_write.c.
    2. Функции 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), массивчик указателей следующего вида:

    void *public_funcs_list [] =
    {
        set_fd_flags,
    };
    
    Поскольку массив НЕ-static, то компилятор с линкером его оставляют, а он тянет с собою и функции, на которые ссылается.

    Замечание: по-хорошему надо бы как-то уметь объяснять ЛИНКЕРУ, что нам надо иметь для экспорта такой-то набор символов (составляющий 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: изведение некоторых потенциальных проблем с misc_printffmt.c.

    18.02.2009: проблем как бы 2:

    1. Обычно (реально -- в 2 точках, в cda и в Cdr) используется конструкция вида
      if (ParseDoubleFormat(GetTextFormat(...
      а ведь GetTextFormat() вполне может при ошибке вернуть NULL, и будет SIGSEGV (проверено!).
    2. В 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: и увидел еще одну "будущую" проблему: ведь у нас для "стандартных форматов" используется префикс '$', который преочевиднейшим образом станет конфликтовать с префиксом макросов.

    19.02.2009: так что -- заменил на '='.

    Конечно, "стандартные форматы" за прошедшее с 2003г. время так и не получили вообще никакого распространения, но мало ли -- вдруг понадобятся. И если вдруг '=' чем-то не устроит, всегда можно будет поменять.

  • 22.02.2009: новый модуль -- misc_set_signal.c.

    22.02.2009: смысл его появления -- предоставлять функцию set_signal(), являющуюся аналогом signal(), но функционирующим одинаково на всех платформах, BSD и SysV, без этих дурацких отличий по самосбросу обработчика и т.п.

    Сделано оно, конечно, на основе sigaction(), а точнее -- взято почти один-в-один из cxdlib.c. Отличия от обычного signal():

    • Оно возвращает не previuos-handler, а, как sigaction, int-код результата, 0 или -1.
    • Определение, ради портабельности, "дикое", как в старых Юниксах, БЕЗ 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: еще новый модуль -- misc_urls.c.

    08.09.2009: нужен он скорее для 4cx/, но поскольку библиотека у нас в обеих ветках синхронизована, то пишем сюда.

    Итак: назначение -- парсинг/разбивка URL'ов вида [СХЕМА:]ПУТЬ на компоненты. Реализуется сие действие функцией split_url(), позволяющей:

    • Указать "default scheme", которая будет вёрнута при неуказании схемы в 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: дополнение в модуль misc_growbuf.c -- работа с буферами, чей объем хранится не в байтах, а в неких иных единицах.

    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: И еще новый модуль -- fps_counter.c. Служит для подсчёта FPS'ов в программах типа осциллографов и камер.

    08.04.2010: сделано по проекту от 14.05.2009. Данные держатся в объектах типа fps_ctr_t, и предоставляется следующий API:

    fps_init()
    Инициализация объекта.
    fps_beat()
    "Heartbeat" -- надлежит вызывать с частотой 1Hz.
    fps_frme()
    Вызывается по приходу очередного кадра.
    fps_dfps()
    Возвращает текущее значение FPS -- в 1/10 долях (деци-fps -- dfps).
  • 07.02.2012@Снежинск-каземат-11: добавляем в misc_macros.h функционал работы с "растущими массивами слотов".

    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. А вот уставление остальных "начальных" значений -- забота клиента.
    • Аналогично, занулить всё в Rls и не забыть уставить in_use_field=in_use_val_unused -- тоже забота клиента.

    И fastadc_data.c вместе с fastadc_gui.c уже переведены на него.

    08.02.2012@Снежинск-каземат-11: делаем дальше -- в соответствии с конкретикой из раздела "О работе с памятью" за сегодня.

    • Переименовываем конкретно вчерашний макрос в GENERIC_SLOTARRAY_DECLARE_GROWING().
    • Переводим возвращаемое Get() с указателя на int.
    • Вводим Access().

      Параметр "obj_arg" идёт не в начале, а в конце -- чтобы избежать сложностей с запятой в списке параметров (которая для статичиских должна отсутствовать, а в объектах с полями -- присутствовать), и воспользоваться gcc/C99-фичей "swallow the comma preceding ##".

    • В DEFINE добавлен параметр MAX_COUNT -- верхнее ограничение на размер массива.
    • Вводим Destroy().
    • Сделана и GENERIC_SLOTARRAY_DEFINE_FIXED() -- копированием и "удалением лишнего".
    • И GENERIC_SLOTARRAY_DEFINE_GROWFIXELEM() -- также изначально копированием, а затем
      1. malloc()'ирование ячейки возложено на FindFreeZZZSlot().
        • Там слегка извратноватый код -- оно смотрит, что если [i]==NULL, то делает malloc(); иначе если !=NULL, то проверяет in_use_field, и если !=in_use_val_unused, то переходит к следующему элементу. Иначе же -- если либо ==NULL и аллокировалось, либо !=NULL и unused, то инициализирует слот и возвращает его.
        • Если malloc() обламывается, то оно возвращает -1. И если это был ПЕРВЫЙ вызов FindFree() из Get()'а, то Get() попробует сделать realloc(). Тут есть некоторая некорректность: если malloc() будет обламываться, а realloc() -- нет (например, если размер слота сильно превосходит размер приращения массива), то оно станет при каждом вызове Get() вхолостую увеличивать массив.

          29.01.2025: был ещё один косяк, оставшийся при копировании: неверное приведение типа -- (slot_type *)safe_realloc(...) вместо (slot_type **)safe_realloc(...); некритично, ошибочного кода не генерило (ибо в C++ не использовалось), но warning при компиляции выдавался.

      2. В Destroy() добавлено safe_free() всех слотов массива.

    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, то надо будет сделать следующее:

    1. Ввести в ..._DECLARE() также объявление Rls##cname##Slot().
    2. Сделать ..._DECLARE() обязательным к использованию (иначе в Destroy() будет ссылка на Rls(), определяемый позже, что приведёт к проблемам компиляции).
    3. Сам Rls() также сделать обязательным к определению юзером.
    4. В Destroy() проходить по всем используемым элементам и делать им Rls().

    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".

  • 15.05.2014: в misc_macros.h сделана функция snprintf_dbl_trim() -- по результатам вчерашнего исправления бага в Xh_plot.c с отображением вещественных чисел через буфер.

    15.05.2014: показание -- что во всех таких местах делалось примерно одно и то же:

    1. Острочивание числа в буфер -- и с защитой через snprintf(), и с проверкой на переполнение (с форсением '\0' в конце).
    2. Обрезание пробелов в конце -- "rtrim".
    3. Опциональное обрезание (пропуск) пробелов в начале -- "ltrim".

    Так что создана такая функция.

    • Она внутри одного #if с check_snprintf().
    • Помимо прочих получает параметр-флаг "ltrim" -- надо ли делать "3-й шаг".
    • Возвращает указатель на начало результата (именно так и реализуется ltrim).

    21.05.2014@Снежинск-каземат-11: на неё, помимо Xh_plot.c тогда, сейчас переведены еще:

    • Chl_knobprops.c -- на замену Ltsv() ("LeftTrimmedSnprintfV", теперь уволенной).
    • 4cx/'ная Chl_knobprops.c -- аналогично.
    • ...и еще в паре мест в Xh_plot.c (подписи по бокам).
    • Chl_histplot.c -- аналогично Xh_plot'у (вычисление ширины боковых полей и их отрисовка).
    • Cdr.c::fprintf_f_rtrim() -- почти совсем опустела.
    • vacclient_meat.c::put_trimmed_val() -- больше на будущее, для следующего поколения vacclient'а.
    • *Knobs_internals.c::*SetTextString() пока НЕ тронуты. 22.05.2014: тронуты.
    • За компанию в Xh_statusline.c сомнительные vsprintf() превращены в vsnprintf().

    Замечание: старый код в тех местах зачастую за-#if'лен, потом его надо будет совсем удалить.

    22.05.2014@Снежинск-каземат-11: продолжение/окончание:

    • Переделаны также Knobs_internals.c::SetTextString() и MotifKnobs_internals.c::MotifKnobs_SetTextString. Но БЕЗ snprintf_dbl_trim() -- в них свой код, в целях оптимизации (функции очень часто вызываемые): избегаем лишнего strlen()'а, обходясь результатом snprintf()'а/укорачивания.
    • Плюс v2'шная/v4'шная 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".

  • 21.04.2015: вводим в misc_printffmt.c инфраструктуру работы с int-форматами -- она очень похожа на double'ную, но чуть меньше эвристики (нет хитростей форматов "%e" и "%f"). Ключевое слово -- 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".

  • 22.02.2017: поддерживать бы в misc_printffmt формат "%a" -- чтоб шестнадцатиричкой можно было пользоваться (понадобилось для triadatv_um, там тип устройства удобнее 0xNN).

    13.07.2017: а получится ли? Ведь, судя по man 3 printf, оно выводит в формате "[-]0xh.hhhhp?", и до десятичной точки всегда только ОДИН знак.

    Сейчас вот проверил -- да, так и есть.

    Причина-то понятна: это сделано для вывода данных без потерь при конверсии в десятичку (полезно при сериализации/десериализации). (И цифра перед ".", скорее всего, почти всегда будет "1": по определению, это нехранимая единица при нормализации.)

    Но от понимания не легче -- очень уж была бы шестнадцатиричка полезна. Но это НЕ ТА шестнадцатиричка, и единственное, что остаётся -- "withdrawn"...

  • 04.02.2019: а вот всё-таки НУЖНА поддержка формата "%a" -- именно для возможности вывода без потерь.

    Потребовалось для Д16 -- там отличие даже в дальнем знаке значения в наносекундах (канал Vn) может приводить к отличию на единицу в значении канала аналоговой задержки (Bn).

    04.02.2019: делаем. Собственно изменений всего 2, крохотных:

    1. В ParseDoubleFormat() символы 'a' и 'A' добавлены к списку валидных.
    2. В GetTextColumns() добавлена эвристика угадывания ширины при неуказанности.

      Вся "эвристика" сводится к числу 24 -- это длина максимально возможного числа -0x1.fffffffffffffp+1023.

      Значение precision тут в задачу не входит (да и угадать его всё равно нетривиально).

    05.02.2019: проверил на cdaclient -- вроде работает.

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

    Кстати, в datatree.c::get_knob_items_count() УЖЕ есть поддержка формата %a; точнее, "понимание" его -- эта буква в спецификаторе #FXXf классифицируется как LIST_FMT_FLOAT.

    Короче -- считаем за "done"; если потом что-то вылезет, будем разбираться.

useful:
cxlogger:
  • 16.09.2003@Bled: заметки на тему того, как вынести logging в отдельную библиотеку, и что оно должно уметь:
    • logline(): в строчках, включающих клиента, передавать "объект-клиент", чтобы в log падала запись "IP:username", а в случае драйверов -- ID девайса ("magicid:driverid"), причем сделать это все совместимым:
      1. промеж себя;
      2. с 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)).

    Почему пока неюзабельно -- какие проблемы:

    1. ll_addfile() пока пуста. А точнее -- я пока не вполне уверен, как же именно ее реализовать. И частично это зависит от следующей проблемы.
    2. Наша старая фича "логгинг нескольких категорий может идти в один файл, и если одновременно в маске выставлены несколько из них, библиотека запишет строчку лишь один раз". Дело в том, что ll_vlogline() помещает уже тронутые текущим сообщением дескрипторы в fd_set, и затем проверяет их наличие.

      НО! Если мы захотим-таки в будущем реализовать по образу и подобию Apache "HIGH_SLACK_LINE", то логгерные дескрипторы-то как раз лучше выносить ЗА пределы, поддерживаемые fd_set'ом/select()'ом, так что наш способ проверки перестанет работать.

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

    А что уже сделано хорошо: защита в ll_vlogline() от переполнения буфера строки -- там используются snprintf()/snprintf(), и постоянно "поддерживается" знание текущей длины. Это важно, поскольку библиотека теперь годна не только для cx-сервера, а другие программы не смогут так "железно" обеспечить непереполнение (да и он-то ничего не гарантировал :-).

  • 06.11.2013: надо менять древнюю концепцию логгинга (еще с ucam), при которой источник логгинга -- который type, аналог syslog'овской facility -- это МАСКА.

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

    06.11.2013: так что переделываем.

    • Для начала -- переделываем этот странный "mask" в int category (да, с ней МОЖЕТ or'иться LOGF_ERRNO).
    • Затем убираем хитромудрость в конце ll_vlogline(), касающуюся распределения сообщения по файлАМ.
    • Общая парадигма:
      1. Категории указываются номерами.
      2. Функции ll_init() указываются пара массивов:
        1. categories[] ((.name==NULL)-terminated) -- список категорий, каждая из которых ссылается полем fref на строчку из массива...
        2. files[] ((.path==NULL)-terminated) -- список файлов для логгинга.
      3. Никакой "ll_addfile()" более нет за ненадобностью.
      4. Менеджмент таблиц категорий и файлов (со ссылками первых на вторых) возложен на клиента. Это включает и всякие проверки "какую категорию не сконфигурили -- смаппим на [0]". А cxlogger занимается только логгингом.

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

    • ll_reopen() делает открытие -- и любое, и ПЕРЕоткрытие (для возможности logrotate). Она получает доп.параметр "initial", чтоб ругнуться при проблеме во время первого открытия.
    • ll_access() производит "доступ" с чтением. Скопировано из старосерверовой accesslogs(), с дополнительной проверкой на можность чтения.
    • ...и они обе уже использованы в cxsd.c.
    • При несконфигурированности категории либо при ошибке записи сообщение сбагривается syslog'у по LOG_LOCAL7. Правда, пока вместе с собственным префиксом "DATE TIME HOST". И в RH-7.3 почему-то опция LOG_PID не отрабатывается...
    • Модификации в юзерах:
      1. libcxsd::cxsd_logger: он выполняет промежуточную обвязку между программой-юзером и cxlogger'ом; в т.ч. то, что изначально имело шанс быть сваленным на последнего. Он реализует все хитрости жонглирования с массивами описателей, оставляя программе лишь само указывание "что куда".
        • cxsd_log_setf() теперь вместо почившей-в-бозе-никогда-не-наполнившись "ll_addfile()". Она получает маску категорий (по 1 биту на категорию -- благо, их сейчас всего 5) и имя файла.

          И создаёт правильные ссылки (при изменении целевого файла правильно выполняет освобождение неиспользуемых).

        • Для чего внутри cxsd_logger.c имеются приватные cats_info[] и file_info[] (которыми и жонглируется).
        • cxsd_log_init() является добрым феем. Немаппированные категории он сам маппирует на первую из указанных. В случае отсутствия оной -- ничего, и тогда это отдаётся на откуп cxlogger'у, который сбагрит syslog'у.
        • Кроме того, она теперь получает argv0, вместо былого вшитого (в daemon/cxsd_logger.c) "cxsd". Правда, в cxsd.c используется "cxsd#N", без оглядки на argv[0].

        Кстати, бестолковое logmask_t переименовано в logtype_t.

      2. cxsd_config:
        • Этот просто принимает команды из конфига/-e, получая пары {Категории,Файл}, и сбагривает их библиотеке.
        • Для удобства добавлена категория all, выставляющая сразу все биты в маске.
        • Благодаря интеллектуальному жонглированию, можно сначала указать "all", а потом отдельно выделить интересующие категории в свои файлы.

    08.11.2013: потестировано, всё работает -- "done".

  • 07.02.2014: проблема -- никак не обрабатываются ошибки открытия лог-файлов.

    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 так и не понадобилось.
  • 28.10.2014: мелкая модификация: теперь при unconfigured сообщение сбагривается syslog()'у только при !is_on_console.

    Смысл -- чтобы простые программы, совчетающие GUI и libcxsd, не заморачивались с конфигурированием категорий логгинга, и тогда всё просто пойдёт на stderr.

  • 08.02.2017: есть проблема с параметрами вызова syslog().

    Когда /var переполняется, то логи сервера начинают сыпаться на ВСЕ терминалы этой машины (включая ssh'ные и xterm'ы).

    В результате и сделать-то что-то невозможно -- экраны зафлуживаются. Только вслепую набрать

    echo -n >/var/tmp/4access.log

    Проблема вылезала за последние месяцы пару раз на vepp4-pult6, а сегодня и на ring1.

    10.02.2017: механизм, когда начинает использоваться syslog -- понятен:

    • Беркаевские/карнаевские/ойдиновские скрипты ломятся по десять раз в секунду, коннектясь cdaclient'ом. Сервер каждую попытку логгирует, и при такой интенсивности файл быстро растёт и /var резво переполняется.
    • Когда место заканчивается, то запись обламывается, при этом взводится unconfigured=1 и оно выполняет syslog() -- который и валится всем невинным.

    Технический же вопрос состоит в том, ПОЧЕМУ это валится на все терминалы, а не только в /var/log/messages?

    • Для логгинга используется priority, равное LOG_LOCAL0. С чего вдруг оно валится всем -- неясно.

      Анализ /etc/rsyslog.conf сходу ответа не даёт. Там "всех залогиненных" касается лишь строчка

      *.emerg *
      ...которая к LOCAL0 вроде бы относиться не должна.
    • Есть еще одно подозрение: в openlog() указано LOG_PID в параметре facility, а не в option -- это оно так с давних пор, еще с пре-v2'шного сервера.

      И вот вопрос: не OR'ится ли это значение с указываемым syslog()'ом priority? А ведь LOG_PID=0x01, что равно LOG_ALERT.

  • 03.06.2024@~09:45, дорога в ИЯФ на планёрку, между мышью и аркой ИЦиГ: (после обдумывания "а не внедрить ли в cxscheduler kqueue вместо select(), чтобы не страдать от проблемы дескрипторов выше FD_SETSIZE?") а нет ли проблемы, что в ll_reopen() при переоткрытии новый дескриптор будет другим (и не факт, что новый не окажется за пределами FD_SETSIZE)?
    Не делать ли вместо нынешнего "close();open();" цепочку вроде
    newfd=open();
    if (oldfd<0) oldfd=newfd;
    else            {dup2(newfd,oldfd); close(newfd);}
    
    а?
    03.06.2024@планёрка, ~10:01: а не пофиг ли? Ведь эти дескрипторы только пишутся, они ни в какой select() не попадают, так что могут быть хоть выше 1000000.

    Так что "withdrawn".

cxnetacl:
  • 17.09.2003@Bled: заметки на тему access control в CX, насчет вынесения его в отдельную библиотеку, и о том, что оно должно уметь:
    • А все-таки, как насчет strong authorization в CX? Сертификаты, SSL, etc., или просто забить на это, оставив для будущих access modules типа CORBA -- если потребуется, то пользоваться их access control'ом.
    • Подумать-таки о .cxhosts -- надо:
      1. Все же начать использовать хоть текущую версию;
      2. Разработать более пристойный формат -- типа
        allow|deny PROTO:addresses
      3. Вынести это вообще в отдельную lib -- типа
        int IsAllowed(const char *file, ???)
        returns -1 if no file, 0 if denied, +1 if allowed.
    • // Итого: 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: так, о птичках: наверное, раз уж так все затянулось, имеет смысл ввести "расширябельный" формат и API?

    30.01.2005: видимо -- как в socket API: т.е., указывать addrlen::addrbuf. А в текстовом файле -- "family/address" (unix/*, tcp/host, tcp6/host),

cxscheduler:
  • 11.11.2003: Дозрел -- надо делать отдельную библиотеку-scheduler, заточенную для работы в тандеме с fdiolib. Итого -- libuseful+=cxscheduler.o, все функции оттуда -- 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: Насчет "под Win32".

    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", и тогда уж оно наконец-то даст посмотреть нормальную страницу.

  • 11.01.2007: как-то все-таки немного кривовато получается, что timeout-callback'ам НЕ передается никакого privptr, а только trec.

    Т.е., с точки зрения функциональности-то -- все окей, поскольку есть trec->privptr, но криво то, что fd- и timeout-callback'и выглядят сильно по-разному.

    (А идет это из планировщика cx-server'а -- cxsd_events.[ch] -- там так было удобнее.)

    02.05.2013 да, пофиксено еще почти полгода назад при переходе на cxscheduler2.

  • 11.01.2007: учитывая, что fdiolib пользуется cxscheduler'ом явно, стоит перейти от числовых констант 1, 2, 4 к символьным -- SL_RD, SL_WR, SL_EX.

    30.01.2007: да, сделал такие константы и перевел внутренности cxscheduler'а и fdiolib'а на них.

    07.12.2007: ага, перевел fdiolib -- как же! Там оставались константы 1 и 2.

    Исправил, и в 4cx/ тоже скопировал.

  • 19.01.2007: плохо, что поле 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, который пришлось обновить, четко видно, что это намного более натуральный вариант.

  • 22.04.2007: наличие cxscheduler'а прямо в libuseful.a -- это неправильно: ведь fdiolib-то может понадобиться и в Xt-программах, ДРУГИМ scheduler'ом. А cxscheduler, намертво вбитый в libuseful, будет этому мешать из-за дубликатных имен.

    Так что -- надо вытаскивать его в отдельную .a, хоть и в той же директории. А по-хорошему и fdiolib тоже надо вытащить, чтобы в libuseful остались только "общеполезности", "расширения для POSIX", индифферентные к тулкиту.

    12.05.2007: да, выселил cxscheduler.o в libcxscheduler.a. При этом создал в Rules.mk макрос LIBCXSCHEDULER, и добавил его в список зависимостей

    ЗАМЕЧАНИЯ:

    1. В 4cx/ этого пока не сделано. 31.05.2007: уже сделано, в рамках переноса основного упора уже на следующую версию системы.
    2. Что неприятно, мы в результате получили взаимозависимость библиотек: libuseful.a(fdiolib.o) "ссылается" на cxscheduler.o, а libcxscheduler.a ссылается на libuseful.a(timeval_utils.o).

      Видимо, правильным будет все-таки корректно разделить все компоненты библиотек по уровням, между которыми зависимости только в одну сторону -- в данном случае, надо унести timeval_utils в libmisc.

      (BTW, модули в libmisc совершенно самодостаточны и уже ни от каких других не зависят -- таков же и timeval_utils, и, кстати, memcasecmp. Вот и обоснование для существования и libuseful, и libmisc, и критерий для определения, в какую же из библиотек какой модуль определить.)

      А вот бОльшая проблема -- как будет выглядеть граф зависимостей касательно Xh_cxscheduler'а (ради которого это все и затеялось), и не будет ли там неразрешимого цикла.

    Поскольку готово, то "done".

    14.05.2007: угу, с Xh_cxscheduler'ом предсказанная проблема, естественно, возникла! Как точно я все предсказал! :-)

  • 23.04.2010: небольшое нововведение в интересах драйвлетов: функция sl_set_select_behaviour(). Она позволяет указать функции-hook'и, должные быть вызванными ПЕРЕД и ПОСЛЕ select()'а.

    23.04.2010: причины следующие:

    1. В main()'ах драйвлетов, работающих с LAM'ами через SIGUSR1, имеется собственный select(), перед которым делается EnableSIGUSR1(), а после -- DisableSIGUSR1(). Т.е., SIGUSR1 разрешается ровно на время select()'а.

      И чтоб драйвлеты можно было перевести на cxscheduler -- необходима эта пара hook'ов.

    2. Интервал времени, проводимый в select()'е при отсутствии таймаутов, у таких драйвлетов разный -- совсем не cxscheduler'овы 10s: у большинства -- 1s, у cm5307_u0632 -- 100ms (он вынужден постоянно делать поллинг, из-за отсутствия LAM'а).

      Теперь же можно указывать этот интервал -- в микросекундах (более 2000s все равно не понадобится).

    Возможно, при собственно переводе драйвлетов понадобится еще какой-нить лёгкий тюнинг -- посмотрим.

    P.S. И, естественно -- этот механизм реализован только в "классическом" cxscheduler.c -- ни в Xh_cxscheduler, ни в Qcxscheduler оно никак.

    P.P.S. Old cxscheduler.c -- 30.01.2007...

  • 21.10.2012@Снежинск-каземат-11: добавлена SL_CE=8, в интересах Xh_cxscheduler'а (основной же реализацией оно никак не используется).
  • 18.09.2013: just for record: все дальнейшие добавления описываются уже в bigfile-0002.html.
fdiolib:
  • 19.08.2005: так, мысли по реализации: при регистрации -- fdio_register_fd() -- должны указываться только "обязательные", "жизненно важные" параметры (hdr_size, endianness, len_{offset,size}). А остальные (типа len_{addition,multiplier}) -- уставляться доп. вызовами.
  • 12.05.2006: поскольку fdiolib полтора месяца назад была реализована в 4cx, то этот раздел целиком становится "done", а все комментарии -- в bigfile-0002.html.
  • 25.12.2006: сделал back-port (точнее -- просто копирование) fdiolib.[ch] из 4cx/.

    Причина -- оно требовалось живое и рабочее для canserver'а, а попытка компилиться с базовой системой CXv3, но с fdiolib'ом из CXv4 обламывалась, поскольку include-файлы брались из "базовой" include/, и получался конфликт.

seqexecauto:
  • 09.04.2004: (записано 14.04.2004): новый модуль -- seqexecauto.[ch].

    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".

  • 17.04.2004: еще мысль: а ведь мы легко можем сделать ветвления -- собственно, проверка условий не есть проблема, поскольку реально все исполняется на Си, а сам переход... Ведь в нашей архитектуре GOTO реализуется тривиально (еще бы :-) -- вводится дополнительная функция 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.

  • 22.04.2004: давно сомневался -- стоит ли делать метод sea_message_t с "...", или лучше с va_list?

    22.04.2004: вообще-то, из простейшей логики: при "..." перейти к va_list можно, а наоборот -- нет. Так что, дабы внутри seqexecauto тоже можно было использовать "свой MakeMessage", надо делать именно на va_list.

    Кстати, последней каплей стал ляп, когда istcc отображал какую-то ахинею. Как выяснилось, из-за того, что я там в make_message_method() вместо XhVMakeMessage() вызывал не-V-вариант.

    (BTW, заодно избегаем проблемы "как указать __attribute__((format)) типу-указателю на функцию, имеющую "..." в списке параметров".)

    В общем -- сделал.

  • 27.04.2004: давно была мысль: надо сделать "внутренний" аналог sea_stop(), которому бы передавалась еще и причина (строка), чтобы он мог различать как бы "код завершения". И аналогично с sea_run() -- чтобы оно при вызове из sea_next() и sea_check() не трогало некоторые внутренние переменные в контексте.

    06.05.2005: сделано, сегодня. Только "причина завершения" -- это не строка, а именно код.

  • 25.07.2004: посмотрел на старую отокаревскую программу C13. У него была таблица-столбец, в которой помечались выполненные действия, так что "прогресс" наблюдался воочию (это похоже на инсталлятор Mandrake, который слева отмечает пройденные шаги).

    А что, если в дополнение к "make_message" ввести еще какую-нибудь функцию, которой бы передавался еще указатель на "шаг" (для получения его названия либо номера) и некий "код причины" -- о чем это сообщение (начало исполнения шага, некий облом, или что -- SEA_MMC_NNN, "MakeMessage Code").

    А разумнее всего -- модифицировать таким способом имеющийся интерфейс make_message.

  • 25.04.2005: хочется все-таки под-об-удобить sea, чтобы:
    1. При sea_stop() выдавалось бы какое-нибудь осмысленное сообщение.
    2. Для этого требуется ввести в "контекст" "имя" программы.
    И заодно с этим -- провернуть улучшения, упомянутые выше, за 2004г.

    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".

  • 28.04.2005: сегодня, роясь на тему "что есть в EPICS'е", наткнулся на такую вещь: у них есть некий "State Notation Language" (SNL), предназначенный примерно для тех же целей, что и SeqExecAuto, но реализующий именно "настоящую" машину состояний.

    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/.

  • 07.11.2005: столкнулся с неприятной особенностью -- если захочется прямо из action'а/checker'а сделать "stop", то имеем SIGSEGV, и вот почему: по логике, возвращать надо SEA_RSLT_NONE, а по нему ExecuteFrom() начинает проверять, не надо ль еще и задержку сделать -- и, естественно, падает, поскольку ctx->curcmd==NULL.

    07.11.2005: первая мысль -- перед обращением вставить проверку на !=NULL. Но это некорректно: тот же контекст мог быть запущен уже заново, и в данной ситуации туда лучше вообще не соваться.

    Поэтому наиболее корректно -- ввести еще один код результата, типа "отвали немедленно, ничего не делая".

    Так и поступил -- ввел еще код SEA_RSLT_GOAWAY. Вроде оно нас спасло. Некрасиво, конечно, но -- работает.

  • 12.11.2005: йоу! Нашел решение вышеупомянутой проблемы, при котором этот уродливый 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] не стал -- мож где и пригодится.

  • 28.12.2011: при реализации "циклирования" с КШД485 вылезли некоторые неудобства с реализацией пары "вложенных" сценариев, подчинённый+хозяин. Заключается неудобство в том, что:
    1. Есть метод cleanup, а вот ХОЗЯИНУ-вызывальщику никакого уведомления о завершении не выдаётся.
    2. И даже в cleanup'е нельзя понять причину завершения -- нормальное ли окончание или какая-то ошибка.

    В результате пришлось выпендриваться -- во-первых, делать вызов хозяйского sea_check() из подчинённого процесса, а во-вторых -- вводить глобальный флажок для передачи информации о статусе. А всё потому, что у нас куцеватая модель: ведь cleanup -- это свойство ПРОЦЕССА, и оно действительно должно быть принадлежностью контекста, а для его ХОЗЯИНА нужен отдельный механизм.

    А ведь модель с "вложенными" sea -- очень похожа на модель процессов в *nix. Ну так и надо сделать аналогично:

    1. Ввести кроме cleanup'а -- аналога atexit() -- еще ВТОРОГО уведомляемого, аналог wait*().
    2. При уведомлениях передавать еще и "причину" -- аналог exitcode.

    28.12.2011: меняем -- первый раз за 6 лет!

    1. Второй уведомляемый -- вводим:
      • Тип sea_on_term_t().
      • Поле sea_context_t.on_term (в приватной части!).
      • Вызов sea_run4(), коему передаётся указатель на функцию, которую вызвать по окончанию. В неё же перенесены все мозги, а sea_run() сделана wrapper'ом.
      • Поскольку теперь у нас не атомарный вызов cleanup, и, теоретически, из него этот же контекст может быть перезапущен, то теперь StopBecause() сначала копирует оба указателя в локальные переменные, а уж потом вызывает.
    2. Теперь обоим уведомляемым передаётся параметр 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.
  • 28.12.2011: добавлен макрос SEA_END() -- он давно напрашивался.
timeval_utils:
  • 18.04.2004: еще новый модуль -- timeval_utils.[ch].

    Новый именно только модуль -- в него ушли функции манипуляции со struct timeval из cxscheduler.[ch].

    Резон -- оне понадобились в seqexecauto.c, а приделывать к пользующимся оным программам еще и cxscheduler было б абсолютно бессмысленно -- например, к Chl/Xh/Xm/Xt-программам он вообще никак, ибо Xt и cxscheduler взаимоисключающи.

    А в libs/useful (т.е., в libuseful.a) он будет в самый раз -- рядышком с cxscheduler, но не обязательно вместе. Ирония в том, что первоначально timeval_NNN() появились в programs/server/usefullib.[ch] :-).

    Помимо создания этого модуля, провернуты следующие манипуляции:

    1. Из cxscheduler.[ch] весь timeval-API удален.
  • 14.05.2007: модуль переехал из useful/ в misc/.
  • 13.08.2009: A BIG FAT NOTE: timeval_subtract() портит параметр y -- вычитаемое.

    Как следствие -- переменная, передаваемая в качестве вычитаемого -- ОДНОРАЗОВА.

    В частности:

    1. Использовать в варианте subtract(xxx,somewhen,now) -- можно.
    2. В cxscheduler'е сейчас всё корректно, сим "приколом" не затрагивается. Так же как и в SleepBySelect().

    Выводы:

    1. Если значение y нужно в дальнейшем -- то стоит передавать его копию.
    2. Радостно, что она хоть уменьшаемое она не трогает.
    3. Лучше использовать не
      subtract(diff,now,prev)
      а
      moment=start+delay; IS_AFTER(now,moment)
      В *adc* -- сейчас сделано плохо и ненадежно!

    Так что вставил в .c и .h комментарий-предупреждение (кстати, timeval_utils.c был за 18-04-2004).

  • 25.10.2010: собственно, а какого черта?!?!?! Почему КЛИЕНТЫ должны делать копию?! Нефиг -- это timeval_subtract() должна делать копию и извращаться с ней.

    25.10.2010: Так и сделал -- yc=*y.

    Кстати, что странно:

    • В fastadc_common время-с-последнего-прихода-данных отображалось корректно, а вот в liuclient -- тот же код! -- нет, там оно уплывало в минус.
    • Интересно, а как было с cm5307_fastadc_common.h и cpci_fastadc_common.h? Теперь уже вряд ли узнаем...

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

    • canmon_common.c -- при TIMES_REL.
    • В cx-bigc.c -- копия НЕ делалась, и оно должно было начать дурить.
    • В cx/programs/xmclients/*adc*.c -- должна была дурить функциональность maxfrq, так же, как и её копия в fastadc_common.c
    • А в uspci_test.c вообще своя копия, но там проблем бы не было. Копия -- проапдейчена.

    08.08.2011: кстати, и из timeval_utils.h предупреждение тоже убрал.

  • 12.10.2012: а не расширить ли (на будущее для v4) интерфейс printffmt, чтобы
    1. поддерживал форматы %d, %o, %x -- выдавая в коде возврата "это тип int" (стандартный парсер (который превратится в обёртку) будет это считать ошибкой);
    2. допускал бы префикс (до % и суффикс (после символа формата).

    Тогда:

    1. ручки так смогут определять свою целочисленность или вещественность;
    2. RW-поля будут префикс и суффикс отбрасывать.
paramstr_parser:
  • 19.04.2004: и еще новый модуль -- paramstr_parser.[ch].

    19.04.2004: в настоящий момент это даже еще не модуль, а просто идея.

    ИДЕЯ же заключается в том, что часто возникает потребность в разборке строки описаний вида

    param=value {param=value}
    
    либо
    param=value,{param=value}
    

    На настоящий момент в числе потенциальных потребителей выступают:

    • Драйверы -- проектируемый интерфейс ParseAuxinfo().
    • Knobs -- проектируемый интерфейс CreateSimpleKnob().
    • cx-starter -- разборщик cx-starter.conf.
    • (возможно) cxsd_dbase -- для разбора опций для драйверов (сейчас предусмотрена только log=).

    Очевидно, что надо изготовить единую библиотеку, работающую по варианту 2 проекта парсера AuxInfo. Проект надо дополнить понятием "квазибулевских опций" -- которые просто при наличии некоего ключевого слова прописывают в указанное место указанное число (как mount'овы опции sync/async, etc.).

    Символ-разделитель будет передаваться отдельным параметром -- это может быть ',', ';', либо пробел (в последнем случае будет использоваться isspace()).

    Да, и еще тонкость: для возможности "кавычить" символ-разделитель просмотр всегда будет выполняться последовательно, БЕЗ предварительного поиска этого разделителя strchr()'ом. А чтобы избавить вызывающую программу от подобной привычки, стоит, видимо, предусмотреть еще передачу параметром "символа-терминатора" (который если '\0' -- то просматривать всю строку до конца).

    20.04.2004: префикс этой библиотеки будет "psp_".

    20.04.2004: еще мысли про функционирование либы:

    • К типам параметров стоит добавить "селектор" -- когда в определении указывается набор вариантов строк ("9600", "19200", ..., NULL-terminated), а оно прописывает int (как LOGD_SELECTOR).
    • Можно "обобщить" поле "размер" со строки на все типы параметров -- тогда оно сможет напрямую прописывать в структуры int8, int16, а не только int32.
    • Еще тип параметра, самый навороченный -- вложенный PSP! Указывается указатель на описание, плюс символ-разделитель.
    • Как следствие из предыдущего -- "символ-терминатор" заменяем на "строку-набор-терминаторов", так что при вложенных PSP-параметрах она будет передавать свою строку с добавленным к ней своим символом-разделителем. Соответственно, про isspace забываем, и надо указывать отдельно пробел и '\t'.
    • Надо ввести макрос 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)
      для выполнения "интеллектуальные" действий по прописыванию значения как при вычитывании его из строки, так и при инициализации.
    • Поскольку потенциально возможны ошибки при parse'ньи, то надо завести внутренний буфер char psp_errdescr[100], в который при обломах будет складываться сообщение об ошибке, и функцию psp_error(), возвращающую указатель на этот буфер (эта функция должна работать как dlerror() -- быть однократно вызываемой).
    • Надо ввести enum кодов результата: 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".

  • 27.04.2004: подумал, а как с нашими макросами "PSP_P_NNN()" клиент сможет вычитывать конфигурацию не в структуру, а в глобальные переменные? Ведь в принципе ничто не мешает в поля 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".

  • 20.05.2004: прибежала мысля: а как насчет сделать "parser plugins"? Т.е., чтобы, если распарсить надо что-либо, не лезущее в стандартные типы (например, driverlog-mask specification), клиентская программа могла бы указать свой собственный парсер. (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).

    • В ТОЙ архитектуре это нормально: там всё устроено так, что plugin-parser только выполняет работу, а ругательствами заведует сам PSP, и уж он добавляет и имя, и строку-объяснение, полученную от plugin'а.
    • Неудобство же возникает тогда, когда плагин не сам парсит, а вызывает другую функцию -- возможно, имеющую свой СОБСТВЕННЫЙ логгинг. Как это есть в ситуации с inserver_d_c_ref_plugin_parser(), вызывающим inserver_parse_d_c_ref(), коий содержит собственный параметр what.
  • 04.06.2004: надо иметь список клиентов (как текущих, так и потенциальных) модуля paramstr_parser.

    08.06.2004: что ж, вот он:

    • Драйверы -- парсинг auxinfo (первый -- can_*.so, будет -- sim_drv.so 02.02.2008: да. обновленный sim_drv уже использует PSP.).
    • CreateSimpleKnob() -- парсинг описания ручки.
    • cx-starter -- парсинг опций для не-cx-подсистем.
    • SimulateDatabase() -- парсинг опций драйвера.
    • Knobs_selector_widget.c (потенциально) -- можно ли перевести тамошний парсер с экзотических #N на psp?
    • CdrLoadGrouplistMode() (потенциально) -- парсинг доп.свойств каналов (norm_range/yelw_range/disp_range).
  • 25.07.2004: кстати... А не стоит ли ввести еще понятие символ-начало-комментария (обычно -- '#', если не надо -- то '\0')? Ведь поиск таких вещей лучше всего ложится именно на psp, ибо если его caller начнет искать такие вещи сам, то будет иметь проблемы типа "символ комментария внутри кавычек".

    Замечание: если вводить, то, аналогично separators/terminators, не символ, а строку -- чтобы можно было передавать вложенным вызовам, дополняя их символами.

    27.04.2005: а нафиг?! Ведь достаточно эти символы-начала-комментария добавить к terminators, а уж вызывальщик пусть по *endptr разбирается, что послужило причиной завершения -- не комментарий ли.

    Так что помещаем идею в <S>, одновременно помечая как "done".

  • 02.08.2004: как насчет ввести "массивовые" параметры (хотя бы для чисел)? Чтобы можно было не делать каждое из чисел отдельным параметром, а указывать скопом -- например, через запятую.

    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
    
  • 19.08.2004: кстати, вспомнилось: аналогичный формат использовался для параметра record-format под VMS -- например, в fopen().
  • 19.08.2004: кстати, как насчет ввести флаг "не ругаться на неизвестные параметры"?

    Это нужно, когда из ЕДИНОЙ строки параметров несколько подсистем выкусывают СВОИ опции.

    Не очень корректно, конечно, но может понадобиться...

    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".

  • 06.05.2005: О! А тип "PSP_T_INCLUDE" нам сильно повредит? Чтобы можно было иметь некоторое количество стандартных опций, и не дублировать их в куче мест, а include'ить.

    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, с МНОЖЕСТВЕННЫМИ парами таблица/структура? Чтобы оно пыталось найти указанный параметр в ЛЮБОЙ из указанных таблиц?

    • Реализовать это очень просто, и это может быть альтернативой для PSP_INCLUDE.
    • Однако это НЕ альтернатива для include конкретно для CXv4'шного семейства архитектур, когда парсинг выполняется не компонентом, а стандартным уровнем (KnobsCore, сервер) -- поскольку там в описании компонента указывается ЕДИНСТВЕННАЯ пара таблица/privrec.
    • А собственно реализация тривиальна:
      1. Вместо пары rec+table будут указываться массивы recs[]+tables[] и количество элементов -- recs_tables_count.
      2. Оно же будет во всех местах, где идет перебор по таблице (инициализация, поиск), перебирать по ВСЕМ таблицам.
      3. И, естественно, если одно и то же имя есть в нескольких таблицах, то приоритет у более ранней.
    • Видимо, лучше все-таки оставить это до "PSP 2.0".

      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(), и выкидываем весь подсчёт-количества (который и не использовался; а зачем он вообще был сделан?).
    • Собственно реализация INCLUDE -- по проекту от 16-09-2009:
      • Инициализация -- тривиальна: просто вызываем 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 для парсинга стандартных опций:

    • Заводим для стандартной таблицы свой struct-тип, помещаем поле этого типа в начало конкретной privrec-структуры (так что offset'ы полей в стандартной и в конкретной будут совпадать); а специфические/конкретные поля идут после неё.
    • И так, в принципе, можно делать даже многоуровневое наследование -- принцип тот же самый (предок идёт в начале, так что указатель на предкову структуру совпадает с указателем на структуру наследника, и, соответственно, все оффсеты также совпадают).

    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, с таблицами, возвращаемыми аксессорами, не попользуешься -- поскольку там парсинг делается базовой инфраструктурой. Выводы-следствия:

    1. С функций-аксессоров придётся переходить на явную декларацию вида
      extern psp_paramdescr_t *zzzzzopts[]
    2. Все "наследовабельные" ручки, типа v2'шной MULTICOL, должны будут предоставлять функции создания _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'ом.
  • 23.05.2005: сходу -- обнаружил, что отсутствует проверка на бэкслэш в конце строки. Вставил, теперь оно на такое ругается.
  • 04.07.2005: а как насчет типа "bitfield"? Аналогичного int'у, но чтоб еще указывалась маска -- которая должна быть непрерывной.

    Проблема лишь в том, что оно может захотеться для ВСЕХ числовых типов -- INT, SELECTOR, LOOKUP, FLAG.

    Впрочем -- все они в PspSetParameter() отрабатываются скопом, так что реально деятельность сведется к:

    • Добавлению к var.p_{int,selector,lookup,flag} поля "mask".
    • Созданию лишних четырех макросов -- PSP_P_{INT,SELECTOR,LOOKUP,FLAG}_BITFIELD.
    • Лишнего if'а в PspSetParameter(), который бы (также тремя вариантами) вместо простого присвоения делал бы присвоение битового поля, предварительно сдвинув значение на базовый сдвиг маски.
  • 04.07.2005: итак, если мы захотим внести в paramstr_parser усовершенствования, "доводящие его до абсолюта", то это будут:
    • Массивовые параметры.
    • PSP_T_INCLUDE. (сделана в 12-2010)
    • Множественные таблицы. (@01.03.2009)
    • Битовые поля.
  • 11.09.2005: Во!!! А если мы собираемся использовать в будущем psp-таблицы для создания графического интерфейса для ввода параметров, то, может -- добавить к psp_paramdescr_t еще и поле "comment"? Чтобы в нем было некое описание. Макросы же PSP_P_NNN(n,sname,field,...) оставим старые, они будут проставлять NULL, а для тех случаев, когда хочется указать комментарий, введем доп. комплект -- PSP_CP_NNN(n,comment,sname,field,...).
  • 02.11.2005: сходу - обнаружилось, что на спецификацию "title=\"Lebedev's line\"" оно ругается "Unterminated <'>-string in 'title' value".

    02.11.2005: все оказалось просто -- там при проверке, не является ли очередной символ открывающей кавычкой, не стояло дополнительное условие -- что мы сейчас уже внутри кавычек.

    Условие вставлено -- проблема исчезла.

    Заодно: там стояли уж больно дебильные комментарии не тему "open quote" и "closing quote" -- исправил на более адекватные.

  • 26.12.2005: по опыту реализации Knobs_simple.c::LookPluginParser() (см. замечание за сегодня): похоже, надо б иметь интерфейс для плагинов, облегчающий парсинг. Как вариант -- специальный подвид плагина -- "конвертер", которому передается уже отпарсенная строка (попросту -- valbuf[]).

    27.07.2006: да, надо. Другой опыт -- парсинг параметров у декоративных knob'ов при ELEM_CANVAS'е: там несколько своих форматов -- так их приходится объявлять как PSP_*_STRING, чтоб складировались в строчки, а потом эти строчки парсить; делать же плагин-парсеры -- просто сил нету, слишком мерзко. И "доморощенные" парсеры крайне примитивны, нифига не проверяют на ошибки (ну а как бы -- опять наворачивать море кода).

    Так что -- НАДО!

  • 14.03.2006: попался еще один аналог getopt()'а -- popt.
    • Это целая библиотека (psp отдыхает :-), родившаяся, по-видимому, для нужд RPM'а -- по крайней мере, popt-*.rpm исходником имеет rpm-*.rpm.
    • Заточена она, естественно, именно под нужды разнообразнейшего и подзапутаннейшего rpm'овского парсинга ключей командной строки.
    • Так что они с psp -- не конкуренты :-). Хотя popt и "умеет" парсить строки (разбивая их предварительно на argv[]-списки), но сие крайне слабо настраивается -- т.е., наши фокусы с вложенными psp там едва ли возможны.
    • А что приятно/радует -- в popt имеется понятие "include-таблицы". А еще там есть понятие "callbacks" -- они per-table, и вызываются, когда найден параметр из этой таблицы; дальний-дальний аналог наших плагинов.
    • И -- psp рядом с ней прямо верх простоты! Ее .h-файл втрое больше!
  • 27.07.2006: давно возникала мысля -- а не допускать ли, в стиле VMS, "частичное" указание как имен параметров, так и значений селекторов/lookup'ов? Т.е., если ключевое слово -- "color", то позволять его указывать как "col", и даже "c"? Естественно -- в стиле VMS же, требуется однозначность: либо указанное слово является полным соответствием, либо -- с таким началом нашелся ЕДИНСТВЕННЫЙ кандидат.

    27.07.2006: да, выглядит-то это соблазнительно, но -- НЕФИГ!!!

    1. Во-первых, незачем делать такие дурацкие поблажки -- напишут слово полностью и правильно, ручонки не отвалятся. Здесь педантизм к месту.
    2. Во-вторых, для "сторонних автоматов" -- таких, как визуальный редактор группировок -- лучше иметь точную спецификацию, чтоб им не маяться с парсингом заранее-точно-непонятно-чего.
  • 13.09.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(), и флаги во вложенные не передавались. Исправлено.

  • 31.01.2007: обнаружилось, что в PSP_P_STRING нельзя было указывать def=NULL -- оно падало по SIGSEGV. Оказалось -- стоял вызов strncpy() вслепую. Вставил проверку -- теперь NULL равносилен "".
  • 04.08.2008: был ляп -- в ругательстве "Junk at position..." стояло просто srcp-str, так что нумеровалось с 0, вместо принятого с 1. Добавил +1. В 4cx/ скопировано.
  • 28.11.2008: попался еще один условный аналог -- BSD'шный getcap().
    • В отличие от Линукса, в BSD-системах для чтения /etc/termcap используется не tgetent() из curses (специфически заточенная только под termcap), а более навороченный интерфейс getcap(3).
    • Он позволяет указывать, из каких файлов читать информацию.
    • Но -- именно что только из файлов, хотя для конфигов всяких программ и демонов этого более чем достаточно.
    • Посему этот API и используется, кроме termcap'а и похожего на него printcap'а еще, например, для /etc/mail/spamd.conf.
  • 25.03.2010: введен еще один тип -- PSP_T_NOP=0.

    25.03.2010: смысл -- чтобы программа могла at-runtime деактивировать некоторые элементы таблицы. Конкретно потребовалось в fastadc_common-based nadc200 в режиме эмуляции (oadc200).

    Для полноты сделан и PSP_P_NOP() -- помимо типа он заполняет только поле name, просто чтоб можно было использовать как placeholder.

    31.03.2010: ма-ла-дец!!! Вставив код 0 и сдвинув все остальные -- нарушил бинарную совместимость сервера/драйверов!!! Теперь надо везде обновлять, блин!!!

  • 08.12.2010@Снежинск-каземат-11: может, имеет смысл добавить "paramstr_parser_utils.c", предоставляющий функции для манипуляций с таблицами? А то сейчас монструозный FastadcCreateMergedTable() выполняет всё самостоятельно.
  • 18.01.2011: заметил такую неприятную/некрасивую штуку: 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".

  • 29.01.2011@поезд-туда: а не завести ли еще доп.тип "VOID" (в чём-то аналог NOP) -- "съедатель" ключиков, чтоб им можно было "затенять" стандартные ключи? Т.е., выпарсиватель строки "в никуда".

    01.02.2011@Снежинск-каземат-11: пара замечаний:

    1. Сам NOP использовать нельзя, поскольку он FindItem()'ом игнорируется, но сделать парсимый-в-никуда аналог -- раз плюнуть.
    2. Процесс "затенения" (при INCLUDE, естественно) будет выглядеть кривовато:
      1. ПЕРЕД INCLUDE-таблицей -- этот PSP_?_VOID, для затенения имени.
      2. ПОСЛЕ INCLUDE-таблицы -- уже конкретный PSP-параметр, но с недоступным именем, для отмены действия include'нутого при инициализации.

    02.02.2011@Снежинск-каземат-11: да, делаем, для liu/plot/*knobplugin*'ов оно точно понадобится.

    Сделал за несколько минут -- там всё тривиально:

    1. Декларация PSP_P_VOID() заполняет только имя и тип.
    2. В PspSetParameter() -- рядышком с NOP'ом, просто чтоб не ругалось.
    3. Ну и в "уставке" распарсенного значения -- тоже просто пустое действие.

    Считаем за "done" сразу, проверим позже.

  • 10.03.2011: проводим большую перетряску:
    1. Вводим PSP_T_NULL=0, а NOP и VOID ставим сразу после него.
    2. Добавляем пару общих полей: rsrvd1 и rsrvd1. Это запас на будущее -- например, для массивов и/или битовых полей.

    Мотивировка: всё равно на пульту сейчас придётся всё обновлять (поскольку там старый API/ABI, уже не совместимый из-за отсутствия PSP_T_NOP, а надо будет ставить туда драйвер xcdac20 -- для управления чернякинским ИСТом).

    16.03.2011: и чтоб уж решить проблему с добавлением новых PSP_T-типов раз и навсегда -- перетряхнуто намного сильнее (коль всё равно не успели на пульте обновить ;-)):

    1. Теперь не просто "автоматический" enum, а нумерованный -- ВСЕ типы имеют =ЗНАЧЕНИЕ.
    2. Значения разбиты по группам, с наличием пустых мест про запас:
      • "Пустые" типы (NULL, NOP, VOID) идут с 0.
      • "Структурные конструкции" (INCLUDE, PSP, PLUGIN) -- с 10.
      • Собственно "парсеры значений" -- со 100.

    "done".

    31.03.2011@Снежинск-каземат-11: еще до-делка в ту же степь: дополнительно сегментируем разные "подвиды" типов, заодно меняя базу со 100 на 50:

    • Целые -- с 50.
    • PSP_T_REAL=70.
    • STRING и MSTRING -- с 80.
  • 28.06.2011: возникло желание сделать PSP юзабельным и для C++ (по просьбе Роговского, высказанной на прошлой неделе).

    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: немного в продолжение вопроса:

    1. Гугление показало, что корень проблемы -- именно в стандарте C++: он позволяет инициализировать только ПЕРВОЕ "поле" union'а (при присвоении-инициализации оно подразумевается принудительно).

      (Сейчас и не помню, где точно нашёл с месяц назад -- что-то типа stackoverflow.)

    2. Как вариант, можно попробовать использовать конструкторы. Поскольку в C++ конструкторы могут быть и у struct, и даже у union.

      Вопрос лишь в том, получится ли результирующий тип, сгенерённый C++'ом, совпадающим с C'шным.

  • 09.08.2012: переносим paramstr_parser из useful/ в misc/ -- как в 4cx/.
  • 30.12.2013: обнаружился крупный ляп, сделанный изначально: поле psp_paramdescr_t.offset имеет тип int, а должно б быть ptrdiff_t. На 64-битных архитектурах это не одно и то же (32 и 64 бита соответственно).

    30.12.2013: обсуждение:

    • В принципе, это не особо страшно: вряд ли будут очень актуальны именно структуры объёмом больше 4Гб.
    • А вот для фичи "парсим в разрозненные/глобальные структуры" (см. 27-04-2004) это очень даже критично.

      02.01.2014: но в свете "rec==NULL => выкинуть результат" та фича всё равно не жилец.

    • Но просто так это пофиксить уже нельзя -- на 64-битных платформах съедет совместимость.

      Придётся отложить до очередного глобального апгрейда.

  • 02.01.2014: отсутствовали проверки на тему "rec==NULL" -- для "при dst==NULL результат парсинга никуда не складывается, а просто «выкидывается»", как было сказано в кандидатской, защищённой аж в 2007-м.

    02.01.2014: вставлены проверки в 3 места:

    1. В PspDoInit().
    2. В PspSetParameter().
    3. В psp_free().
    4. Дополнительно везде, где делается прибавление offset/aux_offset к base_addr (поиск среди таблиц, PSP_T_PSP, PSP_T_PLUGIN), проверяется на ==NULL и если да, то оставляется NULL.

    Проверить затруднительно (не на чем), так что верим в "done".

  • 08.04.2016: обнаружилось, что paramstr_parser non-64bit-safe. Вылезло на pzframe_gw_drv.c, где парсится поле типа size_t -- оно на x86_64 является 64-битным (т.е., 8-байтным), а в PspSetParameter() максимально поддерживаемая альтернатива -- 4 байта.

    10.04.2016: добавлена альтернатива "8:",

    • включаемая условно по "#if MAY_USE_INT64";
    • MAY_USE_INT64 же вначале уставляется в 1, если не определена.
    • Т.е., поскольку извне (из Makefile'ов) нет определения нигде, то по факту на всех платформах оно будет включено.
    • Операция расширения со знаком довольно проста и, скорее всего, не доставит проблем нигде вообще, даже на ныне отсутствующей MCF5200.
    • ...если же вдруг где-то понадобится отключить, то достаточно в x_rules.mk соответствующей директории вставить строчку
      ARCH_CPPFLAGS=-DMAY_USE_INT64=0

    Несколько слов об аспектах реализации:

    • По факту БАЗОВЫМ является тип int:
      • поле psp_var_p_int_t.defval (и minval/maxval; и defval'ы у selector, lookup и flag) являются int'ами;
      • и парсинг в psp_parse_v() делается в int,
      • и проверки на попадание в [minval,maxval] тоже.
    • Поэтому реально корректно обработать оно сможет только значения в пределах, представимых в int.

      (Хотя если где-то простой int будет 64-битным, то и парситься будут более объёмные значения; но таких платформ вроде не видно, а если проявятся, то там вылезут иные проблемы, в misc_types.h с определением типа int32.)

    • Т.е., реально сложности будут за пределами 2147483647 -- поскольку +2147483648 уже превратится в -2147483648; почему -- кстати, не совсем ясно: конверсия там стоит
      *((uint64 *)dst) = *((int *)vp);
      но парсинг числа 2147483648 в size_t на 64-битной платформе даёт результат 0xffffffff80000000 -- проверено (код утилитки тут внутри, закомментирован).
    • Т.о., paramstr_parser годится для парсенья размеров не более 2ГБ.
    • Для текущих потребностей и этого предостаточно, и уж задачу указания "units" (как в pzframe_gw_drv.c) решает точно.
    • Но если вдруг понадобится поддерживать что-то большее -- то хбз, как это вводить.
      • Бинарно-совместимо -- никак совсем, т.к. размер psp_var_t изменится. Хотя -- ведь в psp_var_p_real_t же как раз 3 штуки 64-битных поля?
      • Отдельно же -- можно ввести дополнительные PSP_T_NNN-коды (плюс соответствующие psp_var_p_NNN_t и PSP_P_NNN()-макросы). Хотя выглядит сие криво -- столь же криво, как отдельные NNN32-вызовы в Win32.
cx_memcasecmp:
  • 24.10.2004: новый "микромодуль" -- memcasecmp.[ch].

    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: понадобилась еще одна "близкая" вещь: strmemcasecmp() -- сравнить строку с не-NULL-terminated строкой. Требуется при парсинге файла, когда токен из входного потока совсем не-NULL-terminated. В данном случае потребовалось для считывания режима в Cdr'е.

    03.12.2004: для начала сделал

    static int cx_strmemcasecmp(const char *str, const void *mem, size_t memlen)
    {
        return !(cx_memcasecmp(str, mem, memlen) == 0  &&  strlen(str) == memlen);
    }
    
    в самом Cdr.c. Идея реализации понятна :-). Она несколько халтурновата тем, что возвращает не -1/0/+1, а ==0/!=0.

    Весьма вероятно, понадобится также и не-'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.

  • 14.05.2007: модуль переехал из useful/ в misc/.
  • 05.05.2008: да, не-'case'-версия -- cx_strmemcmp() -- понадобилась, для ChlRunMulticlientApp(). Добавил ее практически копированием из cx_strmemcasecmp(). В 4cx/ также скопировано.
findfilein:
  • 20.04.2007: захотелось наконец-то, в реализацию еще давнего пожелания за 18-05-2004, завести функцию, которая бы искала файл с указанным именем по нескольким директориям -- в текущей, в home, и т.д.

    20.04.2007: создаем модуль с именем findfilein, и функцию с таким же именем. Смысл такого именования -- просто "findfile" уже широко используется разными вариантами, водящимися в сети (чаще всего -- рекурсивный поиск), "findafile" -- как-то подозрительно (что-то связанное с malware), а вот "findfilein" и "findfileinpath" -- обычно используют примерно в тех же целях, что и у нас.

    Проект таков:

    • Чтобы дозволялись РАЗНЫЕ типы файлов, часть ответственности придется перекладывать на клиента -- в стиле client-supplied checker. Так что прототип таков:
      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,
                       ...);
      
    • Функция-проверяльщик (открывальщик?) сама решает, что именно ей делать -- fopen(), opendir(), dlopen() или что -- и при успехе возвращает не-NULL, а при обломе -- NULL.

      Параметр privptr просто передается ей как есть -- на случай, если клиенту понадобится передать функции какую-то информацию, либо получить от функции что-то важное.

    • Если checker==NULL, то используется встроенный проверяльщик, который сводится просто к fopen(). (Просто имя или директорию вернуть не удастся, поскольку они лежат в автоматических переменных в стеке.)
    • Функция findfilein() идет по списку из ..., пока тот не закончится NULL'ом (тогда она вернет NULL), либо пока проверяльщик не вернет не-NULL (тогда этот результат и будет вернут).

      Естественно, нужна и va_list-версия -- vfindfilein().

    • Функция понимает специальные префиксы:
      1. ~/ -- заменяется на $HOME/.
      2. $ENVNAME/ -- заменяется на значение этой переменной. Если переменная не определена -- то вариант пропускается.
      3. !/ -- заменяется на argv0. 08.08.2007: поправка: не на argv0, а на dirname(argv0).
    • Если некий компонент из списка -- "", то он просто пропускается. Это чтобы программа-клиент могла в зависимости от каких-то своих условий варьировать список, вместо выпавших вариантов указывая "".
    • Как вариант -- можно использовать специальное значение (void*)1, чтобы проверяльщик мог сообщить "прерви цикл и верни облом/ошибку" (например, если мы нашли .so-файл, но у него не та версия.

    Замечание: с таким вариантом 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".

  • 05.08.2008: черт возьми, пора-таки перевести на 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: напрашивалось дополнение: чтобы можно было указывать стандартному fopen()-проверяльщику еще и суффикс -- конкретно в 4cx/.../Cdr_file_ldr.c надо добавлять расширение ".subsys" (а то приходилось использовать собственную функцию, отличающуюся только добавлением суффикса).

    05.07.2009: да -- теперь если privptr!=NULL, то builtin_fopen_checker() считает его за суффикс.

  • 08.12.2011: пришлось добавить #include<sys/param.h> -- ради PATH_MAX. Детали см. в разделе про систему сборки.
  • 29.03.2013: а почему была выбрана модель "NULL-terminated массив строк", вместо общепринятого ":-separated path list"?

    29.03.2013: "общепринятый" вариант позволил бы указывать такие пути в конфигах (т.е., он более the Unix way). А сейчас, например, cxldr'у приходится обходиться ЕДИНСТВЕННЫМ вариантом пути.

    Потенциальные сложности такого варианта (помимо просто перетряски API):

    1. Вопреки задекларированному 20-04-2007, компоненты "" вроде бы НЕ пропускаются (судя по коду).
    2. Конкретно ':' проблемен с точки зрения Форточек -- там это буква диска, так что придётся вводить поддержку ';'.

      Наверное, для определённости -- в *nix надо ':', в Win* -- ';'; БЕЗ вариантов.

    09.08.2013: функция с одним параметром -- const char *search_path -- реализована:

    • Названа пока findfilein2().
    • Символ-разделитель определяется #define-макросами FINDFILEIN_SEP и FINDFILEIN_SEP_CHAR, сейчас это всегда ":" и ':'.
    • ТЕПЕРЬ пустые элементы пропускаются.
    • Предварительная проверка проведена на тестике programs/tests/findfilein-test.c -- вроде работает всё как надо.

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

    10.08.2013: переделываем проверяя (это, кстати, список текущих применений):

    • cda.c::lapprox_table_ldr()
    • descraccess.c::CdrOpenDescription()
    • KnobsInternals.c::MaybeAssignPixmap()
    • cxldr.c::cxldr_get_module() (этого проверять особо не на чем)
    • cx-starter.c::ReadConfig(()
    • Cdr_file_ldr.c::Cdr_file_subsys_ldr()
    • Cdr_newf_ldr.c::file_opener()
    (последняя пара -- из 4cx/).

    Поскольку всё вроде пашет -- удаляем старые определения и переименовываем обновлённую функцию из findfilein2() в просто findfilein().

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

  • 03.06.2014: изменения в типах переменных slen и plen в findfilein(): теперь они size_t вместо int.

    Смысл -- избавление от warning'а; чисто по способу вычисления они не могут быть <0. Хотя и немного ссыкотно -- мало ли, какое изменение взбредёт в голову в дальнейшем, а там права на ошибку не осталось...

  • 23.01.2018: имена cx-starter-HOST.conf и devlist-HOST-N.conf привязаны к имени хоста. А что, если он конфигурится по DHCP и имеет какие-нибудь проблемы с сетью? Тогда будет ай-яй-яй: имя превратится в "localhost", и подхвачен не будет.

    Отсюда возникает вопрос: а как бы этак сделать, чтобы программа могла пытаться взять одно из НЕСКОЛЬКИХ имён файлов? Т.е., не удалось взять по имени хоста -- берём по localhost; не удалось и его -- берём просто без имени хоста (т.е., cx-starter.conf или devlist-N.conf). Соответственно, чтоб и снаружи эти "множественные" имена могли бы указываться (серверам -- стартером).

    Напрашивается идея, что множественные имена должны указываться в том же стиле, что и список путей для поиска -- через ':'; и чтоб уж findfilein() и пытался оное искать.

    Соответственно, в findfilein() получится ДВА вложенных цикла -- по путям и по именам. Вопрос только, какой из них должен быть наружным, а какой внутренним (это определяет порядок перебора).

    P.S. А вообще примерно то же самое можно б было сделать какой-нибудь shell'овской командой -- типа "взять первое из такого-то списка имён", где "список" получается как бы расширением "списка шаблонов".

    25.01.2018: а вообще проблема ep1-berezin2 (на котором "потребность" и встала в реальный рост) решается проще -- пусть у него будет static IP (и hostname тоже). 29.01.2018: так и сделано.

lightscript_engine:
  • 20.01.2011: создаём модуль, резоны описаны в разделе Chl_scenario за вчера. Название концепции -- "Lightweight Script Engine", файлы -- lightscript_engine.[ch], префиксы -- lse_/LSE_, местожительство -- lib/useful/ (по применимости он чем-то близок к seqexecauto).

    Коротко -- это модуль простейшего текстового скриптинга, с расширяемым набором команд. Основное предназначение -- база для Cdr_script, в свою очередь, созданного в интересах Chl_scenario.

    20.01.2011: тут следует сказать, что в его нынешнем виде этот модуль скорее временный, в основном для обкатки технологии скриптинга. Уже сейчас понятно, что такой вариант технологии очень примитивен и подходит лишь для тривиальных применений. В будущем наверняка будет какое-то иное решение, объединяющее скриптинг и формулы.

    Кроме того -- само название уже занято, хренью для мобильников: http://www.lightscript.net/. Так что у будущего развития технологии еще и название будет иное.

  • 20.01.2011: приступаем к изготовлению.

    20.01.2011: по сравнению с сырым Chl_scenario'вским прототипом схема улучшена и украсивлена: базовые концепции содержимого context-структуры делаем по принципам, аналогичным sea:

    • В контексте при инициализации указываются "имя" скрипта и собственно текст.
    • Соответственно, скрипт получается как бы готовым объектом, и может в любой момент запускаться/тормозиться, а значение "current pointer" теперь лишь просто указатель на текущую команду, а не вообще единственная привязка к тексту скрипта.
    • Команды возвращают:
      • LSE_OK=0 -- всё окей, переходить к следующей команде.
      • LSE_ERR=-1 -- ошибка, тормозить и отваливать. В будущем, возможно, появятся еще отлицательные коды, конкретизирующие тип ошибки.
      • >0 -- число микросекунд, сколько надо выдержать паузу.

    21.01.2011: да, в первом приближении работает. Что еще надо сделать:

    1. Чтобы прямо в lse_init() автоматически делалась проверка с LSE_PARSE_ONLY.
    2. Ввести еще отрицательные коды ошибок, из которых часть может быть фатальной, а часть -- не очень :-).

    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.

???:
tsycamlib:
  • 18.04.2004: явно нужно иметь метод типа tc_stop(), который тормозил бы прием текущего кадра, если таковой происходит.

    Понадобилось это для istcc.c, чтобы там по кнопке [STOP] можно было корректно тормозить sea-программу.

  • 04.04.2007: пришла пора делать поддержку перезапросов -- сегодня заглянул цыгановский студент и сообщил, что у него это реализовано, и надо уже иметь тестовую программу. Причем он хочет, чтобы:
    1. В программе эта фича была бы отключаема (отладки ради) -- это не проблема.
    2. Чтобы был параметр "читать сразу из памяти" -- как при перезапросе.

    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 изменения заключаются в следующем:

    • Начальный таймаут теперь не 1с, а 1/10с, но -- по начальному таймауту оно пытается повторно до 10 раз послать команду, так что для не-12345-enabled-камеры будет тот же самый таймаут 1с.
    • При получении пакета с line_n==12345 таймаут увеличивается до 10с -- это решает проблему работы с частотой ниже 1Гц (было у Гусева). (И там стоит условие -- это делается ТОЛЬКО в состоянии INITIAL, т.к. при REGET камера тоже присылает подтверждение, а там долго ждать уже не надо.)
    • А при получении первой сканлинии таймаут сбрасывается в 0.1с -- чтобы досылки всего кадра не пытаться ждать 10 секунд (он, по идее, должен дойти вообще за 0.04с).

    Теперь осталось все эти новации внедрить и в tsycamv_drv.c (а поддерживать ли перезапросы -- указывать в auxinfo).

  • 26.05.2008: отражение новых фич в tsycamv_drv -- пишем сюда, для близости.

    26.05.2008: подготовка к отражению -- введены параметры TSYCAMV_PARAM_WAITTIME (def=1000ms), TSYCAMV_PARAM_WAITTIME_ACKD (def=10*1000ms, пока не используется) и TSYCAMV_PARAM_STOP.

  • 02.05.2012: библиотека переехала в v2hw/lib/.
miscXutils:
gray2image:
  • 27.06.2005: обнаружилось, что под VMWare, если в host-системе стоит 24bpp, а не 32, то X-сервер пускается только с fbbpp=24, на который gray2image не рассчитана.

    Так что -- в принципе, было бы полезным наличие конвертера в 3-байтный формат...

  • 02.05.2012: и эта библиотека тоже переехала в v2hw/lib/.

    В связи с чем и сама miscXutils/ прекратила существование.

fastadc:
  • 19.12.2011@Снежинск-каземат-11: заводим новую директорию-библиотек -- lib/fastadc/, где публикуем созревший в liu/y/ код для взаимодействия с быстрыми АЦП.

    Код в настоящее время состоит из 4 компонентов:

    1. fastadc_data -- корневой. Отвечает за получение и обработку данных от быстрых АЦП.
    2. fastadc_gui -- для отображения панелей осциллографов на экране.
    3. fastadc_knobplugin -- содержит стандартный код для создания на основе GUI конкретного осциллографа knobplugin'а для встраивания в Chl-программы.
    4. fastadc_main -- содержит стандартный код для оборачивания GUI конкретного осциллографа в отдельную программу для него.

    Первый компонент складывается в библиотеку libfastadc_data.a, а остальные три -- в libfastadc_gui.aMakefile её НЕ собирает при наличии NOX).

    28.06.2012: по опыту [начала] создания 2-й аналогичной инфраструктуры (для CCD-камер -- vcamimg) стало ясно, что между ними ОЧЕНЬ много общего: vcamimg делается копированием огромных кусков fastadc, с удалением лишнего.

    Явно надо выделять общую часть, и называть её parframe. А специфичности (для fastadc -- раздербанивание общего массива (и вообще многострочность), пересчёт raw/pvl/dsp, ...) выносить в "наследников"/"extension'ы".

    02.07.2012: аналогичные мысли при реализации обновлённого ippclient (с knobplugin'ами вместо elemplugin'ов): там тоже была бы полезна эта инфраструктура. И для DIMEX тоже.

    Итого, потенциальный список девайсов, требующих как parframe-драйверов, так и parframe-отображаторов (точнее, клиентской части):

    1. Быстрые осциллографы -- fastadc.
    2. CCD (и прочие?) камеры -- vcamimg.
    3. Одномерные (и квази-одномерные) детекторы, типа ИПП (только отображение, но НЕ драйвер) и DIMEX -- onedfrm.

    ...что бы еще -- неясно: сходу в голову ничего не приходит, Гусев ничего добавить не смог, Чеблаков+Роговский просто молчат.

    Как бы то ни было: надо сделать "общий кусок" -- parframe, а поверх него "отнаследованные" классы -- fastadc, vcamimg, onedfrm, от которых генерить поддержку конкретных устройств.

  • 26.01.2012: в fastadc_data.c::DoConnect() был ляп: там сначала делалось cda_run_server(), а ПОТОМ при надобности уставлялись параметры [из командной строки]. Пофиксено.
  • 10.02.2012@Снежинск-каземат-11: в fastadc_knobplugin оставалась давно (никогда за последний год?) не использовавшаяся поддержка "единичного" callback'а -- fastadc_knobplugin_evproc_t cb, в дополнение к стандартному "списочному" ndcb.

    10.02.2012@Снежинск-каземат-11: как-то там мутновато: в _data и _gui как раз СЕЙЧАС есть evproc, а не ndcb. Надо бы разобраться и устранить лишнее.

    13.05.2012@Снежинск-каземат-11: разбираемся.

    1. FastadcKnobpluginInit(): параметр "cb" действительно никем не использовался. Так что -- убираем:
      • параметр;
      • поле fastadc_knobplugin_t.cb;
      • тип fastadc_knobplugin_evproc_t;
      • и всё их использование в FastadcKnobpluginInit() и FastadcKnobpluginEventProc() тоже.
      • И плюс поле fastadc_knobplugin_t.ki, также требовавшееся только этому механизму.

        Господи, ну и brain-damaged же модель там использовалась -- каким местом я думал, вводя весь этот кошмар (в конце 2010-го?)?

    2. А в fastadc_data этой хрени (уже?) не было.

    Засим проблему считаем за "done".

    Единственная оставшаяся не-вполне-адекватность -- что здесь у нас именно ndcb, а не evproc; т.е. -- тупая, без параметра reason, нерасширябельная.

    14.05.2012@Снежинск-каземат-11: радикально решаем проблему "нерасширяемости":

    • Постулируем, что код reason будет единым/сквозным для всех уровней. Для data -- 0-999, gui -- 1000-1999, knobplugin -- 2000-1999.

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

    • И везде вводим правило, что при cb==NULL ничего не делаем и возвращаем "success".
    • По knobplugin'у:
      • Переименовываем fastadc_knobplugin_ndcb_t в fastadc_knobplugin_evproc_t -- теперь вся шобла имеет унифицированные названия вида fastadc_LEVEL_evproc_t.
      • Приводим его список параметров к стандартному виду -- (object,reason,info_changed,privptr).

        (В начале "клиентов" manyadcs и two812ch теперь стоят проверки -- if(reason!=FASTADC_REASON_BIGC_DATA)return;.)

      • Убираем компоненты имён "ex_" и "ext_" -- оставшиеся от момента, когда в дополнение к "одиночному" вводились также "дополнительные" callaback'и.
      • А также -- префикс "newdata_".
    • Приводим к общей схеме fastadc_gui:
      1. Модифицируем FastadcGuiEventProc() для передачи "наверх" РАЗНЫХ типов событий, а не только FASTADC_REASON_BIGC_DATA. Причём и тип события он теперь передаёт, а не тупо 0.
      2. Вытаскиваем вызов в отдельную CallCBs().
      3. Меняем архитектуру с "одиночных" на "множественные".
      4. А поле privptr использовалось также для передачи в mkctls. Теперь же, поскольку принят принцип "предок идёт в начале структуры", на это забито и указатель на свой privrec все берут, просто кастя к нему gui.

      (Да, это делается в рамках подготовки к возможности уведомлять об изменениях в реперах.)

    14.05.2012@Снежинск-каземат-11: вводим уведомления об изменении состояния реперов.

    • Код FASTADC_REASON_REPER_CHANGE.
    • Вызывается из 2 точек -- gui_set_rpr() (сдвиг) и RprSwchKCB().

      Модель с "методом" set_rpr() выказалась очень удобной.

    06.10.2012@Снежинск-каземат-11: в fastadc_gui.c::set_rpr() -- в нынешней инкарнации -- был ляп: после действия при x<0 отсутствовал "else" и оно оставляло репер включенным, да еще и со значением -1.

  • 15.02.2012: в программе управления клистронами/модуляторами на ВЭПП-5, возможно, будет потребность в ДВУХ инкарнациях окошек с осциллограммами: в основном окне -- просто отображающие, а в subwin'е -- еще и с управлением параметрами.

    И вот тут вопрос: чтоб не плодить два формально РАЗДЕЛЬНЫХ knobplugin'а (каждый со своим sid'ом) -- может, как-нибудь стандартизовать прямо на уровне fastadc_data специальный "фэйковый" способ добычи данных, сосанием их с другого knobplugin'а (как это делает twi812ch)? Ключевым словом может быть "mirror". Вопрос лишь, как бы сие реализовать.

  • 17.02.2012: давно было прошено сделать так, чтобы при ОТСУТСТВИИ необходимости в данных -- т.е., при отключенном running и неуставленном oneshot -- cda-соединение переводилось бы в "неактивный" режим, т.е., делалось бы cda_hlt_server().

    17.02.2012: сейчас есть недоинкапсулированность: используются running/oneshot уровнем data, а меняет их -- main, причем напрямую; ну и FastadcBigcEventProc().

    Так что --

    1. Введена функция-установщик FastadcDataSetRunMode(). Она принимает на вход и running, и oneshot, но значение <0 означает "оставь текущее".

      Оно делает либо run, либо hlt sid'у.

      ЗАМЕЧАНИЕ: оно влияет ТОЛЬКО на bigc_sid, chan_sid оно НЕ трогает.

    2. В FastadcDataInit() добавлено уставление обоих sid'ов в CDA_SERVERID_ERROR -- чтоб не пытаться сделать run еще несуществующему sid'у.
    3. FastadcBigcEventProc() переведена с прямого уставления oneshot=0.
    4. Соответственно, и в fastadc_main.c прямые махинации также заменены.
    5. Кроме того, в DoConnect() теперь run делается также условно.

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

    18.02.2012: формально, кривизна есть и как бы полезно бы замораживать обновление и скалярных ручек тоже. Но:

    1. Сделать сие нетривиально: данные большого канала могут придти раньше, и скалярные потом не обновятся. Так что для корректности надо заводить им СВОЙ экземпляр флага (oneshot) -- та еще радость.
    2. Но: в обязательном порядке на скалярные каналы маппируются только SHOT и STOP (они по-другому и работать не смогут), а опционально -- ISTART, WAITTIME, SERIAL и калибровочные, т.е., те, которые касаются ПОВЕДЕНИЯ. Параметры же, неразрывно связанные с ДАННЫМИ (в т.ч. текущие значения калибровок у adc200/812me) -- всегда только как параметры больших каналов.

    Так что -- фиг с ними, оставим как есть сейчас.

    18.02.2012: проверено Роговским, работает как положено. Был прикол с краснеющей лампочкой соединения, но он пофиксен, так что "done".

  • 21.03.2012: Федя высказал пожелание, чтоб можно было осциллографам (конкретно v5h1adc200s) на лету менять сдвиг нуля, как у обычного осциллографа.

    21.03.2012: собственно, на уровне Xh_plot это уже возможно, а вот в fastadc_gui -- пока никак, так что делается "вручную", см. конец manyadcs_knobplugin.c::SetLineKind().

    Реально, ГЛАВНЫЙ вопрос -- как оформить GUI для изменения этих параметров:

    • В основную панель управления -- некуда, да и нефиг им там постоянно болтаться.
    • Доп. панелькой, с противоположной стороны графика от cpanel? Неа, фигово -- будет запутывать, да и неочевидно.
    • Кнопочкой [^v] в правом нижнем углу графика (туда НИКОГДА не ставится [+])?
    • ...или оной же кнопочкой рядом с [Calibr...] (или на её месте, для не-калибруемых CAMAC-устройств)?
    • ...и не забываем, что должен будет существовать еще вертикальный scrollbar!
  • 15.08.2012: надо отображать статус/rflags большого канала. Хоть как-нибудь, хоть где-нибудь.

    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: сделано, хотя и несколько отлично от проекта.

    • Никакого массива GC и AllocStateGCs() не используется.
    • Работает схема, аналогичная раскрашиванию обычных ручек: оно (fastadc_gui) постоянно определяет статус, и при его несовпадении с предыдущим обновляет и вызывает пере-раскрашивание.
    • Пере-раскрашивание реализовано методом XhPlotSetBG(), являющимся просто переходником к XhViewportSetBG().
    • Последний же заменяет текущий bgkdGC (аллокацией нового и release старого) и уставляет XmNbackground всем затронутым виджетам.
    • Изначально ставится COLALARM_JUSTCREATED, так что оно всё бирюзовенькое, а потом по приходу данных перекрашивается как надо.

      Но для синтетических (FASTADC_B_ARTIFICIAL) сразу ставится COLALARM_NONE, т.к. их обновление идёт в обход обычной цепочки EventProc'ев, да и понятия "приход данных" у них формально нет.

    Реализация несколько неполная, т.к. ни расшифровка статуса, ни список флагов (строки State, Flags) никак не отображаются. Однако текущая базовая проблема решена, так что помечаем за "done" и далее будем допиливать по мере надобности.

  • 26.10.2012@Снежинск-каземат-11: делаем режим "readonly" -- с помощью CACHECTL_SNIFF.

    26.10.2012@Снежинск-каземат-11: деяния (синхронно в fastadc/, pzframe/ и yzframe/):

    1. Добыча:
      • Поле readonly добавлено перед maxfrq.
      • И в psp-таблицу.
      • В Main() "-readonly" превращается в просто "readonly" -- причём халявно: argv[n]++.
      • К сожалению, непонятно, как бы этак добывать readonly от Chl'я...
    2. Использование -- что делается или не делается при уставленном readonly:
      • При открытии соединения уставляется SNIFF вместо SHARABLE.
      • Там же НЕ делается отправка init-параметров.
      • ParamKCB()/RgParKCB() просто ничего не делают.
      • mkparknob(): не уставляет в ручке значение из init.
      • К сожалению, нет возможности всем par-knob'ам уставлять "ro" -- поскольку spec-строку получаем от конкретного _gui.c. Только если их всех приучить -- но це дюже муторно.

        Прям хоть в 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".

  • 13.11.2012: сделано, чтоб обычными каналами запрашивались только те параметры, что не-BIGC.

    Некрасивенько сделано, но работает.

  • 02.05.2013: обнаружилось, что в заголовках окон save/load значится {save,load}DataDialog_popup. Исправлено добавлением в Xh_fallbacks.h соответствующей пары ресурсов (аналогично ModeDialog).
  • 21.05.2013: еще небольшие дополнения "не сюда", но в интересах сего: для "miniToolButton" в Xh_fallbacks.h добавлены размеры полей, а в Xh_colors.c -- armColor:XH_COLOR_BG_TOOL_ARMED.
  • 21.05.2013: еще с неделю назад начато расширение единственной кнопочки [>F] в полноценный мини-тулбар, с кнопками [>] и [|>].

    21.05.2013: протокол:

    • Тогда одна кнопочка была заменена на контейнер, в который допихнуты также еще две кнопки (пока не работающие).
    • Сейчас (см. предыдущий пункт) эти кнопульки сделаны попрезентабельней. А дальше...
    • Копируем в fastadc_gui.c куски из fastadc_main.c, касающиеся менеджмента -- функции ShowRunMode() и SetRunMode(), плюс их использование (всё адаптировано к параметру gui вместо глобальности).
    • ...заодно введён helper SetMiniToolButtonOnOff(), для отражения текущего статуса "нажатости" кнопочки.

    Итого -- работает! "done".

    Единственная некрасивость -- при несколько-раз-подряд кликаньи по [|>] у неё перепутываются bg/armed. Причина -- данные приходят в момент нажатости мышью, и перепутывается инвертирование программой и Motif'ом (кстати, на обычном тулбаре в _main.c тоже). Посему пока попытка отражать состояние "ждём один приход данных..." убрана (в обоих местах). 10.06.2013: ага, только в тулбаре еще надо было и тип кнопки сменить с TOOLCHK на TOOLCMD, а то её сам тулбар инвертировал.

  • 04.10.2013: касательно отображения времён:
    1. Есть потребность в осциллографах в режиме внешнего тактирования уметь вместо номеров отсчётов рисовать реальные времена, как-то добытые снаружи (например, от системы синхронизации).
    2. Давно пора что-то делать с отображением больших времён -- всякие "450000ns" слегка напрягают (450us было б адекватней)

    04.10.2013: а вот как бы сие можно сделать:

    1. Тут хитро, несколько аспектов.
      • Получать значение единиц "с точки зрения математики" -- видимо, пропусканием номера такта через пару {множитель,делитель} (а еще сдвиг?).
      • Выдавать значение для отображения (скрывая детали его добычи) -- возможно, это в компетенции x2xs(). Но тут, скорее, еще войдёт в игру п.2.
      • Добывать основания для пересчёта значения -- как-то каналом из сервера, который самим fastadc-plugin'ам хбз как указывать (и в каких единицах там всё должно отдаваться?).

        Либо же принудительно требовать наличия рядом с плагином invisible-ручки-"дирижёра", которая б любым удобным способом добывала информацию, и сбагривала б её плагину уже по стандартному внешнему API. Но нет, это плохой способ -- даже standalone-утилиты *adc* должны быть способны к такому частото-учитыванию.

    2. Видать, нужна библиотечка более общего плана. И можно ль туда как-то уместить сантиметры, дециметры и прочие не-кратные-3 порядки?
  • 14.05.2014: есть некоторые сомнения в корректности работы FastadcDataSave(): все ли числа там разделяются пробелами? А строка "0"?

    21.05.2014@Снежинск-каземат-11: мда, там

    1. Ветвь "сырых значений" была сделана ляповато -- без предваряющих пробелов, так что "короткие" столбцы (с numpts<=x) гарантированно бы сливались.
    2. А ветвь "печатных данных", наоборот, не содержала проверки numpts>x, и пыталась бы вылазить за границы буферов.

    Когда это (1) появилось -- вопрос. Возможно, код еще от старых клиентов вроде nadc200.

    Исправлено уже в pzframe/fastadc_data.c, тут оставим как есть -- zastadc/ ныне на увольнение.

    22.05.2014@Снежинск-каземат-11: проверено (на тестовом примере: сохранение искусственного manyadcs, с одной линией в 1024, а второй в 500) -- теперь пишет корректно.

  • 28.05.2014@Снежинск-каземат-11: замечены какие-то странности с отображением репера "d" у adc812me: число совсем бредовое, НЕ равное разности времён.

    Неужто где-то по ходу вычислений происходит переполнение в 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/ забъём.

???:
pzframe:
  • 11.07.2012: по опыту начала возни с оттмаровской CCD-камерой стало очевидно, что надо выдёргивать из lib/fastadc/ функционал, общий для всех подвидов больших каналов вообще, держать его в lib/parframe/, а оттуда уже наследовать конкретные реализации для разных видов.

    ЗАМЕЧАНИЕ: в новом fastadc_type_dscr_t теперь вместо УКАЗАТЕЛЯ на parframe_type_dscr_t (как было в старом fastadc_gui_dscr_t) лежит сама структура (ибо "наследование").

    А вот всякие *gui должны будут содержать именно УКАЗАТЕЛЬ -- поскольку там другая, ПАРАЛЛЕЛЬНАЯ иерархия.

  • 30.07.2012: переименовываем "parframe" в "pzframe" (ParametriZed FRAME). Резоны:
    1. Так длина префикса выходит одинаковая у всех -- fastadc, vcamimg, pzframe.
    2. Первые буквы префикса теперь уникальны, а не перекрываются с paramstr_parser.
  • 03.09.2012@Снежинск-каземат-11: имеется техническая сложность: у нас есть ДВЕ параллельные иерархии -- например, pzframe и fastadc:
    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)
    

    Ссылки в левой и правой колонках -- это одно и то же, поскольку они есть ссылки из объектов разной степени наследования на "параллельные" (сопутствующие) объекты соответствующей степени наследования.

    Замечания/комментарии:

    1. Плюсы:
      • Объекты более низких уровней могут существовать
        1. как в виде полей объектов более высоких уровней (например, fastadc_data_t -- поле в fastadc_gui_t, а fastadc_gui_t -- поле в fastadc_knobplugin_t),
        2. так и в виде реально отдельных деревьев: fastadc_data_t -- в дереве "данных" (в будущем в Cdr), а прочие на неё лишь ссылаются (причём -- могут ссылаться МНОЖЕСТВЕННЫЕ: два экранных графика на один объект "данные графика").
      • Кстати, при повсеместном соблюдении правил наследования -- например, при расположении структуры pzframe_type_dscr_t первым полем в fastadc_type_dscr_t -- существенно сокращается количество указателей, поскольку ссылки на "предка" и "потомка" становятся взаимозаменяемыми.
    2. Сложности:
      • Недостатоком этой модели будет затруднённость парсинга параметров в параллельные структуры.
      • Некоторые вопросы будут в организации "виртуальных методов" -- чтоб предок вызывал функционал, реализованный в потомке.

        Делаем поля-методы прямо в структуре.

      • Неоднозначность есть также в том, куда привязывать EventProc'ы: к узлу "ниже", или же "справа".
      • Множественное наследование исчезает! Bingo!!!

    Делаем. Работы временно ведём в соведней директории yzframe/, файлы получают названия yzframe_*.[ch] и xastadc_*.[ch].

    18.12.2013: давно пора доделывать новую версию pzframe, так что уже несколько дней как анализируем имеющийся результат разделения (более чем год назад).

    • Концепция "svd" отдана в конкретности (fastadc), на уровне 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 ставить

      1. Всяким диапазонам, сдвигам нуля -- влияет на вертикальные оси (кстати, у всех нынешних *adc* сдвиги почему-то вообще не упомянуты в *_param_infos[]).
      2. Таймингам, FRQDIV'ам, PTSOFS -- влияет на горизонтальную ось. А NUMPTS -- вроде не влияет (он влияет на horzbar, но там свои мозги обновления).
    • И само поле 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).

    • _cpanel_decor.h-файлов теперь двое -- общий yzframe_cpanel_decor.h и специфичный xastadc_cpanel_decor.h, содержащий лишь 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:

    • Некоторое движение в сторону обобщения mkctls: теперь туда и gui передаётся pzframe'ный.
    • И evproc'ы -- не хочется ли их объединить (в рамках наследования -- т.е., все _data вместе (и обеспечивались в pzframe_data), все _gui вместе, все _knobplugin).
    • Кстати: была ж мысль рассматривать pzframe как часть общего Cdr-функционала больших каналов (201110-SNEZHINSK-ACTIVITY.txt, 26-10-2011).

      Значит, надо делать всё исходя именно из этой парадигмы: чтобы _data были "для Cdr" (а _gui/_knobplugin -- для Chl/Knobs).

      Тогда, кстати, pzframe_data_t/fastadc_data_t будут жить не внутри fastadc_gui_t, а прям в Cdr/datatree-дереве, что снимет вопрос "где физически должны жить какие поля?!" и сделает общую архитектуру стройной.

      Итого, строим всю структуру исходя из двух базовых принципов:

      1. Максимально похоже на ООП -- с наследованием.
      2. Оно должно легко переделаться под datatree.

    25.12.2013:

    • По части наследников fastadc_type_dscr_t и fastadc_gui_dscr_t имелась изрядная неясность -- как это вообще всё организовывать, в свете разделения на компоненты pzframe<-fastadc/vcamimg. Так что в 09-2012 ничего сделано не было, и даже параметры именовались "void *zzz".

      А сейчас, поразмысливши -- да так же с ООП-наследованием!

      1. Переименовать для симметричности *_type_dscr_t в *_data_dscr_t

        Подумавши, всё взвесивши -- неа, не стоит. Переименовать было бы симметричнее, но ТАК оно уникальнее, да и сокращается до "ftd" (atd,vtd), а то что будет -- "fdd" (add,vdd)?

      2. !!!НАСЛЕДОВАНИЕ? Вызывать "конструктор" _потомка_ со ссылкой на переданный свыше dscr _предка_, являющийся по факту полем в экземпляре dscr'а потомка, и потомчатый dscr_filler чтоб заполнил всё сразу (вызвав (после bzero()?) и dscr предка).

        Вопрос только, как с ВЕРТИКАЛЬНЫМИ связями (кто когда кого будет вызывать) -- ведь dscr'ы есть как у data, так и у gui.

    26.12.2013:

    • Еще несколько замечаний на тему ООП и наследования:
      1. Еще как бы обоснование варианта с наследованием по горизонтали и ссылками по вертикали: количество вертикальных уровней ограничено, всего 3 (data, gui, knobplugin/main), по горизонтали же их может быть и больше нынешних 2 -- наследовать можно сколь угодно далеко.
      2. Эти _type_dscr и _gui_dscr по смыслу -- не что иное, как VMT (хоть и с примесью не-методов, (статических "свойств")). Тогда если б переименовывать в "_data_vmt" и "_gui_vmt", то сокращения будут fdv (adv,vdv) и fgv (agv,vgv) -- тоже, кстати, менее уникально/произносимо.
      3. Следовательно, само существование "dscr_filler'ов" -- извращение: по идее, VMT должна существовать в ЕДИНСТВЕННОМ экземпляре и заполняться статически. И потом уже она может быть передана всяким FastadcMain()'ам.

        Поскольку у нас ООП лишь имитируется, то придётся как-то и заполнение тоже имитировать? Макросами, "возвращающими" структуры (точнее, списки инициализации)? Парой часов позже: нет, так нельзя -- для доступа макросами придётся всем потрохам торчать наружу. Скорее тогда уж методы с именами вроде get_*_dscr(), возвращающие указатели на статические переменные. А как они у себя внутри проинициализируют -- их личное дело.

    • Делаем:
      1. Вся концепция "dscr_filler'ов" выкинута, а вместо неё теперь "создавателям" передаются сразу сами указатели на dscr'ы-"VMT", добываемые от...
      2. ...функций-заполнятелей.
      3. Замечание: повсюду возвращаются/передаются всегда самые "низкоуровневые"/"родовые" указатели на pzframe_*_dscr_t, и где надо они преобразуются в "высокоуровневые" -- функциями pzframe2fastadc_type_dscr() и pzframe2fastadc_gui_dscr().
      4. Вёрнута FastadcFillDscr(), pzframe-часть работы которой выделена в PzframeFillDscr(), вызываемую точно как "конструктор предка" -- в самом начале.
      5. С _gui подобного (пока?) создавать не потребовалось -- там заполнение метрики простое, так что всё делается по месту.
    • Некоторые размышления на будущее касательно knobplugin'ов:
      • Надо постараться всё максимально унифицировать, минимизировав содержимое конкретных TYPENAME_knobplugin.c. Для чего...
      • ...почти всехние privrec_t станут просто fastadc_knobplugin_t, ...
      • ...кроме liu/y/'шных manyadcs_knobplugin и two812ch_knobplugin, которые являются ТРЕТЬИМ уровнем наследования, после fastadc(pzframe). Точнее, третьимИ уровнЯМИ, поскольку они параллельны и независимы; ну и наследуют напрямую, без дополнительных "обобщающих" инфраструктур (и без data/gui/main).

    27.12.2013:

    • (Штука общего плана) из-за наследования возникает проблема аллокации: если раньше поле типа fastadc_gui_t можно было разместить прямо в fastadc_knobplugin_t/fastadc_main_t, то теперь -- никак. Возможные варианты решения:
      1. "ООП-правильный": завести в VMT (dscr) поле "size", и аллокировать объекты malloc()'ом. Но сие геморройно.
      2. Аллокировать где-то полями в структурах более высокого уровня, а "создавателям" (main, knobplugin) передавать уже указатель на готовенькое.

      По соображениям простоты выбран вариант 2.

    • Make Realize() and UpdateView() dscr-methods and move GuiUpdate to PzframeGuiUpdate(), calling UpdateView() at the end. 04.01.2014: сделано.

    28.12.2013:

    • Сделан метод pzframe_gui_dscr_t.update и временно реализован в тестовом nadc200.c.
    • После еще небольшого шаманства (типа отлова непрописывания adc->atd) программа запустилась. Пока, правда, без обновлений (ибо инфраструктуры evproc'ев пока не задействованы).
    • Сколько ж усилий потрачено на совершенно дурную работу -- воспроизведение на обычном C базового ООП-функционала с наследованием и виртуальными методами (дурацкие приведения типа либо местами дублирования полей), хотя в ООП оно бы всё было готовенькое "как надо".

      Шоб я еще раз возился с такими вещами вручную, вместо реализации на адекватном задаче языке (уж GUI-то -- самая то задача для ООП!).

    30.12.2013:

    • Еще на тему наследования:
      • Сейчас наследование+экземпляризование у нас сделано слегка не классично-по-ООП, а сепаратно, по двум группам (или "двухуровневым образом"?):
        1. "наследование" ТИПОВ (в смысле языка) делается только по "классам" устройств (корень -- pzframe, от него fastadc и vcamimg), а...
        2. конкретные типы (вроде nadc200) делаются как бы тем же классом, в тех же структурах данных, просто меняются значения полей.

          Т.е. -- некоторый винегрет.

      • Тогда, 09-2012, в pzframe_gui_t были "для Inheritance" введены callback'и child_evproc, child_newstate, child_do_renew, плюс pzframe_data_t.child_evproc.

        Вот не факт, что это правильный путь: ведь оные callback'и определяются на уровне конкретного ЭКЗЕМПЛЯРА, а должны бы на уровне КЛАССА (пожалуй, именно "класса" в вышеупомянутом смысле, т.е., fastadc/vcamimg).

      • Конкретно на сейчас: для упрощения жизни и скорости реализации начальной версии сделано (временно?) как тогда 09-2012, что методы "класса" живут в самих экземплярах, а заполняются в функциях Init() -- например, FastadcGuiInit(). Потом, возможно, сделаем "правильнее".
    • Теперь главной задачей осталось сделать psp-парсинг в разные места (в отличие от былого, тут всё НЕ в одном месте.

      Как? Наверное, по аналогии с идейкой от 27-04-2004 о вычитывании конфигурации в глобальные переменные: указывать rec=NULL, а в качестве offset'ов указывать прямо указатели на под-структуры. Но проблема с 64-битностью... (Вот уж где б пригодился парсинг в разрозненные дуплеты таблица/структура!)

    31.12.2013:

    • Разрозненный парсинг в PSP реализован, и теперь PzframeMain() его использует -- ура!

      Кстати, возможно, пора избавиться от pzframe_main_t myrec -- раз уж всё равно единая структура более не требуется -- и парсить main_opts сразу в переменную такого типа. 02.01.2014: сделано.

    02.01.2014: возимся с парсингом:

    • FastadcDataCreateText2DcnvTable() практически скопирована из старой (с минимальными изменениями -- теперь сама fastadc_dcnv_t стала парсимой единицей).
    • FastadcGuiCreateText2LookTable(), с некоторой оптимизацией, и из неё...
    • ...часть параметров, которые теперь в компетенции pzframe_gui, ушли в pzframe_gui_text2look[].

    04.01.2014: возимся с knobplugin (начато еще вчера):

    • opts и bigc_n убраны из структуры, став временными переменными при создании.
    • PzframeKnobpluginInit() переименована в PzframeKnobpluginDoCreate(), поскольку это НЕ "init" в том смысле, как у data/gui, а именно "create" в смысле knobs.
    • Управильнена реализация наследования касательно виртуальных методов в gui: в pzframe_gui все такие методы собраны в единый тип, поле этого типа помещено в gui_dscr_t, а заполнение для fastadc'шных возложено на FastadcGuiFillStdDscr() (своего рода "конструктор").

      Так что те наследники, которым потребуется что-то менять (как manyadcs_knobplugin в отношении realize), запросят заполнение стандартной VMT и поменяют в своём экземпляре что надо (при надобности куда-нибудь сохранив указатель на inherited-метод).

    • !!!Make MOST knobplugins use fastadc_knobplugin_t as privrec_t

    06.01.2014:

    • Возимся с evproc'ами.

      В порядке проекта-идеологии:

      1. Ясно, что раздельных evproc'ев должно быть ТРИ набора/"вида": у data, gui и knobplugin.
      2. ???Механизм вызова ВНУТРИ "вида" -- желательно с тем же наследованием (inherited/offspring).
      3. А вот КОДЫ ПРИЧИНЫ (reason) -- желательно у всех унифицировать, чтобы код от data прозрачно доходил до юзеров knobplugin'а.

      Делаем:

      • Из всех Realize() параметры-evproc'ы убираем -- они уже и так не используются.
      • Собственная fastadc'шные реализации evproc'ев убраны.
      • Зато pzframe'ные CallCBs опубликованы (с комментарием "protected") с префиксами PzframeData и PzframeGui.
      • А в "ветви" _knobplugin это пока и не было сделано (оно нужно для "соседей", типа liu/y/), когда оных будем делать, то и дореализуем.
      • Ну и на корректное распределение REASON_-кодов пока забито.

      25.05.2014@Снежинск-каземат-11: добавлена PzframeGuiAddEvproc() -- собственно PzframeGuiCallCBs() была и раньше, но никакой возможности уставить callback не было. По идее, там как-то должно было вызываться "через другую ветвь" -- то ли через _data, то ли через _knobplugin (но у этого что-то поназапутано -- реально pzframe_knobplugin_evproc_t содержит pzframe_data_t *pfr -- идентично pzframe_data_evproc_t; хбз как это всё работает...), но нет -- так что фиг с ним, сделаем просто чтоб давало результат, лень разбираться со всеми хитросплетениями этой хрени.

    • В _data делаем такого же вида VMT, как в GUI. Основной смысл -- реализация методов "save_data" и "load_data".

      Заодно FastadcFillDscr() переименована в FastadcDataFillStdDscr() и переведена из inline в обычную функцию, чтоб правильно производить заполнение VMT.

      07.01.2013: и порядок параметров слегка поменян -- чтоб dtype и num_params были вместе сразу после behaviour, для унификации с vcamimg.

    • Сохранение/загрузка для fastadc тоже сделаны, работают.

      Кстати, метод "filter" отвязан от специфики Xh_fdlg, через функцию-адаптер PzframeDataFdlgFilter(), и теперь получает сразу ссылку на pfr.

    07.01.2014:

    • Делаем vcamimg (начато еще вчера). Соответствие названий: adc->vci (VCamImg), atd->vtd. 30.07.2015: был нефатальный ляп: в vcamimg_{data,gui}.h оставалось #include"plotdata.h".
    • На нём сделан ottcam (при запинывании которого vcamimg и доведён до рабочего состояния).
    • Переводим на новое и по именам:
      1. Удаляем полусделанные pzframe_{data,gui}* и zastadc_* как из include/, так и из pzframe/.
      2. Переименовываем старые fastadc в zastadc -- как файлы, так и директории в lib/ и v2hw/.
      3. Переименовываем свежесделанные yzframe_* и xastadc_* в pzframe_* и .c-файлы переселяем в lib/pzframe/ (и новорожденные vcamimg_*.c), опустевшую yzframe/ удаляем.
      4. Директорию v2hw/nfastadc/ переименовываем в pzframes/.
    • Кстати, поддержку козачиных таблиц -- если вдруг понадобится -- можно реализовать под именем "koztdac" (Table-DAC), с внутренними названиями kdc (KozakDaC) и ktd (KozakTypeDescr).

    08.01.2014:

    • Мелкие допиливания pzframe_gui: добавлены кнопочки [>][|>], и они теперь появляются по тому же флагу, что local_leds -- (т.е., фактически вследствие отсутствия тулбара).
    • В ottcam добавлены ручки-параметры -- всё пашет.
    • Заодно изготовлена tsycamv, копированием с мелкими модификациями. Правда, проверить пока не на чем (надо пучковый датчик включать).

    10.01.2014: доводим _knobplugin -- внешний сервис, для "соседей".

    • Отлаживаемся на "новом" sukhclient, названном zsukhclient (manyadcs и two812ch и проверить не на чем, и поддержка PCI'ных еще не сделана).
    • Чуть сменена парадигма по сравнению со старой: теперь callback'ам передаётся не kpn (и даже не gui), а сразу pfr -- как и возвращается из функции-регистратора.
    • И тут вылезла проблема изначально 3-летней давности (из-за которой в своё время и был введён "constr"): ведь в realize() от gui у нас нет никакой ссылки на knobplugin.
    • Что делать? Может, всё-таки неправильно совсем устранять "constr", а считать его отдельным методом, но уже уровня knobplugin'а (3-го, т.е.) -- как раз за realize?

      (И незачем тогда маяться с подменой gui'шного метода.)

    • ...но:
      • Это будет совершенно излишний напряг: ВСЕМ knobplugin'ам придётся иметь vmt, некоторым еще делать там realize.
      • Намного проще прямо в DoCreate() этот realize передавать параметром...
      • ...что и было сделано в первой версии с constr :)
    • Так что -- вертаем параметр, называя его "kp_realize".
    • И zsukhclient работает!

    16.01.2014: запинываем под новую архитектуру содержимое liu/y/, теперь вернувшееся (через 3 года!) в liu/xmclients/.

    • Первый -- two812ch -- прошел без особых проблем.
    • Второй -- manyadcs -- помуторнее, поскольку там сильно поперемешан функционал разных уровней, да и само наполнение навёрнутое.
    • Сделан (вёрнут) вызов PzframeKnobpluginGetGui() -- в интересах manyadcs, который показывает микропервеанс с учётом положения реперов в другом GUI.
    • Модифицирован в PzframeKnobpluginDoCreate() порядок вызовов разных "realize": теперь сначала вызывается kp_realize, а потом уж gui'шный. На такое уж был рассчитан manyadcs -- иначе давал SIGSEGV вследствие неинициализированности ссылок на "источники".
    • Ну и с "определением местоположения" -- получением ссылки на MANYADCS_privrec_t из прочих, типа gui -- пришлось помучиться. Но вроде с нынешней архитектурой (раскладкой полей по структурам) всё неплохо получается, хотя и громоздко и неочевидно.
    • В конечном итоге клиент liu запустился. Но в отсутствие данных говорить о полной готовности некорректно -- вдруг еще где сковырнётся; так что старая архитектура под названием zastadc пока будет сохранена.

    Но вот с этого момента новую "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.

    • Смысл -- knobplugin'ы должны мочь ловить события не только от данных, но и от GUI, поскольку являются следующим уровнем: knobplugin(gui(data)).
    • Тогда, изначально, было сделано иначе из-за отсутствия PzframeGuiAddEvproc() -- появившейся лишь 25-05-2014.
    • Конкретная потребность вылезла (традиционно ;)) в manyadcs_knobplugin.c -- ловля реперов.

      (В предыдущем юзере аналогичного функционала -- beamprof_knobplugin.c -- самостоятельно делалось PzframeGuiAddEvproc() отдельно добытому gui, что криво.)

    • В получившейся ныне архитектуре есть некоторая кривизна:
      1. Назвать функцию для стройности/унификации обозначений следовало бы PzframeKnobpluginAddEvproc(), а не "RegisterCB".

        Но оставим как есть из-за следующего пункта...

      2. Возвращает она target'ов pfr, хотя по логике надо бы kpn (из которого остальное добывабельно).

        Но именно pfr уже используется практически во ВСЕХ местах её вызова, так что оставим как есть.

  • 20.02.2013: мыслишка на будущее: было б полезно вытащить всё, касающееся преобразования данных, из *adc*_data.c в отдельные .c-файлы, чтоб они могли быть доступны и клиентам, и драйверам.

    Смысл -- чтоб драйвер сразу отдавал отконвертированные данные min/max/avg/sum. Хотя, это скорее уже для v4, тут-то вряд ли выйдет...

  • 10.04.2013: замечание: в нынешнем/старом fastadc_*.c не всегда перед вызовом x2xs стоит проверка на exttim. А надо бы -- чтоб x2xs() вызывалось ТОЛЬКО при exttim. 14.04.2014: может, "ТОЛЬКО при НЕ exttim"?

    (Сейчас это компенсируется мозгами *adc*_data.c::x2xs(); но, например, для nadc333 всё равно отдаётся *1000.)

  • 14.04.2014: (давно уже Федя требует) надо бы уметь как-то СНАРУЖИ мочь указывать размер 1 кванта xs при exttim.

    Смысл -- в некоторых случаях ставится внешнее таймирование, и размер такта добывабелен (снаружи через какой-нибудь канал СУ). В таком случае грех показывать просто "x".

  • 26.05.2014@Снежинск-каземат-11: сделана FastadcGuiSetReper(), в интересах liu'шного beamprof_knobplugin.c, чтоб синхронизировать реперы двух gui, не лазя в их внутренности.

    Семантика параметров идентична оной у XhPlotSetReper().

  • 15.04.2015@Снежинск-каземат-11: коль у нас такие "весёлости" с NUMPTS, который не-change_important (см. 201504-SNEZHINSK-ACTIVITY.txt за 15-04-2015), то что -- вводить еще ТРЕТЬЕ возможное значение этого поля ("numpts_important"), и всю соответствующую инфраструктуру?

    15.04.2015@Снежинск-каземат-11: ...вообще-то корректнее, наверное, конкретно в manyadcs_knobplugin.c отдельно проверять изменение NUMPTS, и если изменилось, то ставить info_changed:=1; а можно так проверить? Похоже, так проверять нельзя -- т.к. info_changed вычисляется в pzframe_data.c::PzframeBigcEventProc(), и там делается копирование prv_info[]:=mes.info[] еще ДО вызова вышестоящих callback'ов.

    ...в принципе, копирование можно переставить в ПОСЛЕ вызова. Стоит ли?

  • 02.06.2016: неприятная особенность: в ottcam'е, если он запущен не один, нажатие кнопки [Stop] работает "странно" -- следующий кадр пропускается.

    Причина очевидна: в 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().

    Проверено -- помогло.

  • 22.06.2016: в vcamimg_gui.c был косяк: ВСЕГДА отображался предыдущий кадр.

    Понять причину удалось на v4, но, т.к. там архитектура в точности такая же, а ошибка появилась в v2, то записываем здесь.

    22.06.2016: по порядку:

    • Поиски:
      • "Прошлый" кадр был замечен (в v2) в ottcam на ВЭПП-5 в режиме редких запусков, когда пучок бывал раз в 12.5с, между запусками его успевали убрать, но на картинке он почему-то был.
      • И даже больше: по нажатию на [Stop] вместо нулей показывалась некая картинка, а по первому [Shot] после этого показывались нули!
      • Было подозрение, что глючит что-то у Оттмара. Даже wireshark'ом смотрелось -- фиг, всё корректно!

        И, собственно -- у Оттмара внутри просто нет памяти, в которой мог бы храниться предыдущий кадр.

        А главное -- на [Stop]-то Оттмар как бы мог влиять?

        Значит, косяк явно у меня.

    • Он нашелся: в vcamimg_gui.c копирование данных в буфер XhMonoImg'а делалось в VcamimgEvproc(), а обновление экрана -- в DoRenew().

      Но из них сначала вызывался DoRenew() (показывая предыдущий кадр), а уже потом VcamimgEvproc() (как раз и "готовя" данные для отображения на следующем цикле -- когда они и станут "предыдущими").

    • Исправление:
      • По логике -- копирование данных в буфер XhMonoImg'а должно делаться непосредственно перед 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: тогда 2.5 года назад при внедрении новой архитектуры было забыто перенести управление стилем линии (ключевое слово -- "LineStyle" или "LineMode").

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

    1.07.2016: и в v4 перенесено (где оно отсутствовало из-за отсутствия в v2'шной, откуда копировалась). Только тут уже потребовались модификации -- API-то приподизменились.

pzframe_drv:
  • 28.01.2012: рождаем будущий "общий и формализованный кусок для fastadc-драйверов" (ex-xapi_fastadc_common.h/parframe) -- он будет жить в libfastadc_common_drv.a, реализация -- fastadc_common_drv.c, описание -- include/fastadc_common_drv.h.

    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().
    • Тестировать будем на cpci, во временной директории zcpci/, где всё сразу адаптируется под pzframe_drv-based вариант.
    • ...а также сразу можно сделать и ottcam_drv.c, ...
    • ...и, за компанию с cPCI-, также и CAMAC-fast-ADC под новую модель.

    30.08.2012@Снежинск-каземат-11: по результатам прикидочной реализации первого не-локального драйвера -- ottcam:

    1. Похоже, метод "ReadMeasurements()" всегда будет вызываться после того, как все данные уже пришли (или стало ясно, что уже не придут) -- именно в этот момент будет дёрнут _drdy_p(). Так что, выходит, что сама по себе функция "ReadMeasurements()" никогда не будет протяжённой во времени, а всегда лишь просто подготовит имеющиеся данные к предстоящему ReturnBigc() (формально её можно вообще переименовать во что-то типа "PrepareRetdata").
    2. Следствия:
      1. Сама функция становится void -- без кода возврата.
      2. То, КАК обращаться с её результатом -- должен указывать вызывальщик _drdy_p() -- просто булевским флагом "do_return" (если нет -- то рестартовать измерения (например, при калибровке adc{200,812}me)).
      3. Вызываться функция должна непосредственно из ReturnMeasurements() -- чтоб уж во всех случаях (включая немедленную готовность и abort) подготовить данные.
    3. Кроме того, тот, кто вызывает drdy_p(), может знать, насколько данные полны или нет, и для возможности ему сигнализировать о проблемах пусть передаёт еще rflags.

    Сделано:

    • По вышеописанному проекту.
    • Кроме того, если раньше cpci-драйверы определяли return/ignore именно в ReturnMeasurements(), то теперь введено поле privrec.do_return, заполняемое сразу в StartMeasurements() -- дабы быть доступным для FASTADC_IRQ_P().

    Строго говоря, с отъёмом у ReadMeasurements() способности к прерывности модель несколько упрощается: в принципе, с некоторыми натяжками оно способно работать и так, БЕЗО ВСЯКОЙ МАШИНЫ СОСТОЯНИЙ. Теряем мы всякие тонкости: отложенное вычитывание текущих параметров и не-мгновенный abort.

    07.12.2020: касательно наличия параметра "do_return" у _drdy_p() -- он пригодился ещё, уже не для калибровок, а для "искусственного таймаута". (Это уже в CXv4, но неважно.)

    • У карповских BPM'ов накопителя, драйверы для которых делает Роговский, есть пакостная особенность: если в течение 80 секунд не придёт внешний запуск, то они всё забывают -- как будто их и не программировали. И Юра вынужден через некоторое время по таймауту повторять программирование; дополнительный бонус -- если юзер крутил параметры, то в этот момент они вступают в силу.
    • Исходно это делалось уставкой значения WAITTIME, так что генерился pzframe_drv'шный таймаут и наверх отдавались нулевые данные с флагом CXRF_IO_TIMEOUT, что нервировало Виталю Балакина.

      И нервировало оправданно: карповское забывание -- специфика конкретной железки, и по-хорошему должна обрабатываться на уровне драйвера, а не выноситься юзеру.

    • После консультаций очевидным решением выглядело, что драйвер должен САМ иметь таймаут (к WAITTIME никак не относящийся) и в нём исполнять "PerformTimeoutActions(,do_return=0)".
    • Но это внутренняя, недоступная юзеру функция, поэтому появилась мысль её опубликовать -- что-то типа "pzframe_drv_perform_timeout_actions()", и даже начал было это делать под именем pzframe_drv_do_stop(), ...
    • ...но тут обратил внимание, что внутренности PerformTimeoutActions() и pzframe_drdy_p() почти идентичны -- в последней только отсутствует вызов abort_measurements().

      Так что не надо ничего добавлять, а достаточно в драйвере вызывать pzframe_drdy_p(,0,) (при надобности тормознув устройство), что и было рекомендовано Роговскому.

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

    09.12.2020: поправка от Роговского: "ибо ПОКА ЕЩЁ покрывает все возникающие потребности". :D

    30.08.2012@Снежинск-Снежинка-307-вечер: мысли:

    • Основной функционал *_fastadc_common.h перешел в pzframe_drv,
    • ...а сами они остались скорее адаптерами, с весьма небольшим объёмом кода.
    • Объём в драйверах-юзерах, требующийся для описания параметров этим адаптерам, примерно сопоставим.
    • И учитывая, что технология, когда файл-юзер в конце делает #include куска реального кода, всё же кривовата и некрасива,
    • ...пора избавляться от *_fastadc_common.h, перенеся этот код в сами драйверы.

    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: в сторону обобщения функционала -- некоторого "разнообразия" возможностей: Роговский хочет, чтоб можно было "делать запуск измерения без вычитывания и возврата всего массива", а лишь пары чисел.

    18.01.2013: побудительный мотив: а что, если некий fast-АЦП может после измерения отдать не только весь массив, но и просто некие характерные числа (например, среднее значение, минимум и максимум). Тогда при высоких частотах (>10-100Гц) вычитывание массива сильно бы тормозило, а вот просто пара-тройка чисел -- прекрасно дадут сколь угодно большие Гц (сам-то прибор всё успеет). Для CCD-камеры аналогичным функционалом может быть, например, отдача самой железкой координат "центра тяжести" пятна.

    Идея в том, чтоб пойти по стопам tsycamv_drv (имевшего 3 больших-канала-alias'а, и после измерения возвращавшего лишь те, что были запрошены). А именно:

    1. Завести специальные readonly-параметры, которые самим запросом себя на чтение будут запускать измерение (если оно еще не запущено), с ТЕКУЩИМИ параметрами.
    2. Собственно вычитывание и возврат большого канала должны происходить лишь если был реальный запрос.

    Потенциальные сложности:

    • В нынешней модели pzframe_drv -- а именно, при нынешних взаимоотношениях между ReadMeasurements() и вызовом drdy_p() -- вылезет проблема: если в локальных устройствах (типа *adc*) ReadMeasurements() РЕАЛЬНО производит вычитывание, и его невызов даст желаемый эффект, то в удалённых (та же ottcam) он лишь подбивает статистику -- какие скан-линии не дошли и нулит их.
    • Приподсломается механизм "сравнения" запросов больших каналов по параметрам. Сценарий:
      1. Приходит запрос на "статистический параметр" ("среднее значение").
      2. По нему запускается измерение.
      3. Пока оно еще идёт, прилетает запрос на собственно большой канал -- с какими-то параметрами.
      4. Измерение заканчивается, данные готовы.
      5. ...И что делать? Просто отдать и большой канал тоже (раз запрос на него пришел и сей факт был запомнен) нельзя -- параметры могут отличаться.

        Может, по приходу такого запроса сохранять параметры в "nxt_args", а при окончании измерения молча запускать измерение заново, но уже с флагом "запрошено большим каналом, а не стат.параметром"?

        Всё равно как-то мутновато. И еще -- надо сразу рассчитывать на будущую схему работы со СВЯЗАННЫМИ каналами, чтоб и в ней тоже всё было корректно.

???:
rem:
  • 30.12.2011: вытаскиваем все специфичности работы на стороне удалённых контроллеров сюда. Всё по аналогии с 4cx. Состоять оно будет из двух частей:
    1. libremsrv -- для многодрайверных серверов; полная среда исполнения.
    2. libremdrvlet -- для самостоятельных драйверов, на основе fdiolib+cxscheduler; то, что сейчас живёт в server/drivers/remdrvlet_[ch].h.

    Только эти части никак не интегрированы -- в отличие от 4cx, где они (предположительно?) будут пользоваться одним и тем же модулем разбора пакетов и вообще взаимодействия с хостом.

  • 21-10-2012@Снежинск-каземат-11: Приступаем к новой реализации remdrvlet/remsrv.

    Делаем в lib/gem, include-файлы -- gem*.h ('g' -- картавящее 'r'; и ни include/g* других нет, ни lib/g*).

    21-10-2012@Снежинск-каземат-11: Общая идеология:

    • remsrv и remdrvlet делят общие модули В/В и driver-API.
    • "Общее" содержится в файлах remdev_* (СЕЙЧАС - gemdev_*).
    • Конкретных использований этого общего функционала 2:
      1. remsrv (rrund как бы тоже тут, слегка сбоку).
      2. remdrvlet.
    • собственно "модуль запуска" у каждого свой:
      • remsrv слушающий и порождающий драйверы по мере надобности,
      • у remdrv просто сразу исполняющийся.
    • также у каждого свой модуль "drvmgr", включающий аллокирование/деаллокирование devid:
      • у remsrv -- большая таблица (100? 1000?) и FreeDevID(), просто прибивающий драйвер.
      • У remdrvlet -- таблица из 2 элементов ([0] -- пустой, [1] -- сам) и FreeDevID(), делающий sl_break().

    Мысли по теме:

    • как-то это по именам слегка корявовато. Например, перекрываются префиксы remdev и remdrvlet. А хочется -- чтобы "базовая" сущность имела префикс, аналогичный cxsd_ (чьим удалённым отражением она является), а "сервер" и "драйвлет" имели бы похожие префиксы.
    • очень уж напрашивается мысль сделать внутренности унифицированными с v4'шным cxsd (вплоть до имён структур).

      ...а может и правда поступить как с cxsd_driver.h -- иметь ОДНО определение (cxsd_*.h) и РАЗНЫЕ реализации?

    22.10.2012@Снежинск-утро-в-Газели-на-полигон: О -- remcxsd?

    22.10.2012@Снежинск-каземат-11: да, так и поступим. Краткое резюме проекта:

    • Определения используем общие для библиотек обычного сервера (srv/) и удалённого сервиса (rem/); именуются они cxsd_*.h.
    • Именования:
      • cxsd_*.c -- сервер.
      • remcxsd_*.c -- общая инфраструктура удалённых.
      • remsrv_*.c -- специфичности libremsrv.a.
      • remdrvlet_*.c -- специфичности libremdrvlet.a.

    Доп. соображения:

    • Определённо есть желание "похожий" код максимально использовать просто один и тот же. В частности, весь менеджмент Tout, FD, IO -- чтоб он физически содержался в ОДНОМ месте (на настоящий момент модельная реализация -- в 4cx/src/lib/srv/cxsd_driver.c).
    • ...временами даже напрашивается мысль "не слить ли в одну директорию srv/ и rem/?". Потому, что между ними есть общий код. Или пока обойтись симлинками?

    @вечер: да, надо всё делать в одной директории. Ибо всё это -- СРЕДЫ ИСПОЛНЕНИЯ драйверов, просто чуть разные их варианты. 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) также предварительно "припилена":

    • В основном скопирована из старых remsrv_main.c и remsrv_drvmgr.c (это касается в первую очередь "listener"'а и консольного интерфейса, основной же интерфейс сильно перепилен под libremcxsd). Для простоты оно всё слито в единый .c-файл.
    • Существенное отличие от старого -- организация таблицы драйверов. В примерном соответствии с идеей от 28-04-2010 (см. bigfile-0002.html) вместо drivermapping'а используется NULL-terminated таблица указателей на CxsdDriverModRec'и, и при поиске драйвера используются прямо поля .mr.name. "Примерное" соответствие потому, что таблица простая, и предоставить её должна непосредственно программа-юзер (т.е., никакого сотрудничества от чего-нибудь в стиле "plugmgr" нету).
    • Теперь надо
      1. Сделать версии canserver'а и cxv2moxaserver'а под неё, плюс проверить их.
      2. Поразмыслить, не подпортировать ли сюда всё-таки некоторые концепции из так-и-недоведённой 4cx'ной rem/.

        15.04.2015@Снежинск-каземат-11: вопрос -- а КАКИЕ концепции? Давно об этой записи помню, но вот сейчас совсем неясно, что же такого ценного было в 4cx'ной rem/, чего нету в нынешней v2-cx'ной (разве что rrund.c на основе remdrvlet_listener, а не своей реализации всего).

        16.04.2015@Снежинск-каземат-11: да, поанализировал исходники -- стало ясно, что

        • Речь действительно о более элегантной реализации remsrv, поддерживающей и rrund.
        • И еще в 4'шном есть setvbuf().
        • И поддержка prefix+suffix для rrund там есть (см. bigfile-0002.html, раздел "libremdrvlet" за 29-04-2010). И remdrv_drv.c тамошний на это рассчитан.
        • А модуль remdrvlet_hostio.c там пустой -- содержимое не было сделано.

        19.06.2015@вечер-ванна: с учётом поддержки prefix+suffix, конечно, хотелось бы перейти на тамошнюю версию. Но трудоёмко, а хочется иметь в v4 работающую rem-инфраструктуру "прям щас" -- скопировав всё из v2.

        Но можно ж просто тупо добавить поддержку prefix+suffix, это почти ничего не стоит!

        21.06.2015: добавляем (идеологически -- максимально притягиваем к "той" v4'шной версии).

        • Во-первых, добавлен setvbuf().
        • Указание порта (а им кто-то когда-то пользовался?) теперь через ключ -p.
        • Не-начинающиеся-с-'-' параметры складываются в argv_params[].
        • Собственно Run() теперь использует эти параметры.

          Что странно -- gcc-2.96 выдаёт warning'и

          warning: variable `prefix' might be clobbered by `longjmp' or `vfork'
          warning: variable `suffix' might be clobbered by `longjmp' or `vfork'
          
          -- в отличие от v4'шного кода. Хотя на вид они аналогичны.

          И мелкая модификация: если запрошенное имя абсолютное -- начинается с '/', то prefix+suffix игнорируются.

        • Из оставшихся минусов -- то, что считывание ОДНОГО имени драйвера напрочь блокирует всю прочую работу. Криво, конечно.

        В общем, теперь плюсом старой v4'шной осталась та "элегантность реализации". Пока постараемся пережить...

      3. 10.08.2013: и подумать, не заиспользовать ли и тут cxldr.

        Сейчас-то неохота, ибо даст вроде лишнее усложнение, но мало ли, что потребуется в будущем...

      4. 15.08.2013: ...а еще поддержки layer'ов нету, хотя будет нужна; как и поддержка инициализации модулей -- вот тут и понадобится cxldr.

    06.08.2013: более интенсифицирован логгинг на консоль -- и содержимое businfo теперь выдаётся, и по максимуму всё префиксируется [DEVID].

    13.08.2013: сюда же переселён rrund.c -- из предназначенной под снос старой rem/.

    (Его б надо еще как-нибудь в кросс-директориях собирать...)

    13.08.2013: старая rem/ отправлена в ARCHIVE/, а gem/ переименована в rem/.

    06.08.2013: проверяем на живой аппаратуре. Результаты:

    • Был ляп: remsrv_drvmgr.c::FreeDevID() НЕ вызывало term_dev(); в результате повторный (после рестарта сервера) запрос драйвера приводил к ошибке "device (0,2) already in use".

      Это просто тупо было забыто при копировании из старого кода.

    • Следствие: почему-то сервер СРАЗУ делает TermDev(), и remdrv'ом не делается попытки еще 2 раза запросить. Надо разобраться.
    • И вообще бывают какие-то странности с "inpktsize=0", а на стороне remsrv при этом ругань на какой-то незарегистрированный devid...
    • Отдельно: почему YMDRV при втыкании CAN-кабеля ПОСЛЕ запуска сервера присылает тучу ответов на GetAttrs? Раньше было 400 (похоже на размер queue), но сегодня вообще >800...

      04.09.2013: выяснилось, FIFO+ответы-на-всё, см. другой комментарий за сегодня.

    17.08.2013: процедурное: в ЭТОМ "общем" разделе дообсуждаем только те проблемы, что уже затронуты, всё же касательно новых фич и развития -- будет в подразделах remsrv/remdrvlet/remcxsd.

    25.09.2014: было забыто сделать LIBREMSRV и LIBREMDRVLET в TopRules.mk. А понадобилось для v2sktcanserver'а. Кстати, оный работает -- т.е., remsrv под обычной x86_64 пашет.

    Поскольку всё хорошо, то "done".

remsrv:
  • 30.12.2011: следующая пара пунктов перемещена сюда из раздела "cxsd:"/"CANGW:" (где они жили изначально с сентября/ноября-2011), чтоб всё было рядышком -- всё равно ТАМ они были постольку поскольку.
  • 30.09.2011@Снежинск-каземат-11: перерабатываем исходники canserver'а, так, чтоб они годились для использования в других местах (bivme2, moxa).

    30.09.2011@Снежинск-каземат-11:

    • Технология -- разделение функционала:
      1. Специфичный для CANGW остаётся в canserver.c. Его довольно мало, фактически только названия ("CANGW", "canserver", ...) да консольная команда "ff".
      2. Остальной же, НЕспецифичный функционал, переименовываем в модули c префиксом remsrv_:
        • remsrv_drvmgr.c -- drvmgr () плюс driver-API.
        • remsrv_main.c -- собственно "main".

        Первоначально была мысль сделать аналогично v4'шной lib/rem/, но там уже слегка сильно совсем по-другому, так что обойдёмся без излишней работы -- простейшим разделением с переименованием.

      3. "Промежуточное", "симбиоз" -- remsrv_drvtable.h, чьи определения используются генеримым canserver_drvtable.c.
      4. А вот от remsrv_includes.h надо избавляться...
    • Работы ведём в "соседней" директории nppc_canserver/.

    07.10.2011: продолжаем.

    • Наконец-то проверено, что оно собирается.
    • remsrv_includes.h, плюс еще некоторые декоративные подчистки кода.

    12.10.2011: добиваем.

    • Обнаружилось, что интерфейс разборки команд крайне примитивен -- оно сравнивает с образцом ВСЮ СТРОКУ ЦЕЛИКОМ, а вовсе не пытается вычленить первое слово.

      Ну и фиг с ним -- в ЭТОЙ версии не станем улучшать.

    • Введён файл remsrv_internals.h, содержащий некоторые typedef'ы, используемые в _drvmgr, значения для которых передаются от клиента через _main:
      • 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().
    • В canserver.c сделана реализация единственной команды exec_ff() плюс описатель canserver_commands[], а также canserver_clninfo().
    • В remsrv_drvmgr.c:
      • Реализация "ff" убрана (вместе с пустой "map", фиг знает для чего предназначавшейся), зато ...
      • добавлен поиск команды по внешней таблице, при помощи сервисной 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 без кросс-компиляции -- ох что там полезло!!!

    • Почему-то при попытке подконнектиться к консоли (8001) telnet'ом нифига толком не работает -- в качестве Enter вместо положенного \n telnet отправляет \r\n. Это поведение я могу понять, но почему с CANGW такой сложности не возникало? Вроде никакого договаривания со стороны telnet'а там видно не было -- по каким-то особенностям функционирования IP-стека собеседника определял, что ли?

      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'е исчезло.

    • Далее, команды почему-то принимались на 1 символ короче.

      Оказалось, что в 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.
    • ...из другой оперы: в kshd485_drv.c и nkshd485_drv.c "использовалась" 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*.

    И оно, по этой же аналогии, должно состоять из ДВУХ частей:

    1. libremsrv.a -- вышеописанное.
    2. libremdrvlet.a -- то, что сейчас обитает в programs/server/remdrvlet_[ch].h.

      Конечно, ту парочку и её пользователей надо будет подпеределать:

      1. Перевести на lib-архитектуру с того #include-шаманства.
      2. Избавиться от зашитости FDIO_LEN_BIG_ENDIAN в пользу определения endianness.
      3. . . .

      Короче -- все работы с 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

  • 08.11.2011: всё-таки понадобится в remsrv (и в его аналоге в 4cx/) какой-то вариант API "inserver" -- пусть и сильно обкорнанный.

    Смысл -- для "быстрых" синхронных действий:

    • В CANGW -- типа одновременного пинка драйверам xcac208 (исполнение таблицы) и weld02 (подъём тока).
    • Да и в BIVME2 (в случае использования там связки на remsrv) это тоже будет полезно.
    • И, что греха таить, в CM5307, при переходе на LAM-via-select(), такое тоже станет возможно.

      А вот не факт: оно ведь тоже использует SIGUSR1, и работать с МНОЖЕСТВЕННЫМИ позициями так просто не удастся, т.к. не различить, от кого пришёл LAM (или маску читать?).

    08.11.2011: о степени "обкорнанности":

    • Она должна быть такой, чтобы допустима была ТОЛЬКО отсылка (запись) данных -- inserver_snd_cval().
    • Просто потому, что с нынешней моделью драйвлетов (в т.ч. и внутри remsrv) никакое "get_cval" решительно невозможно -- по причине отсутствия там кэша текущих значений каналов.
    • Соответственно, и req_cval становится бессмысленным, как и все evproc'ы.
    • Единственное что -- в принципе, можно сделать и inserver_req_bigc().
    • Замечание: похоже, нужен всё-таки отдельный API для уведомления о смене статуса блока-жертвы -- чтоб при подыхании манипуляторщик не пытался продолжать что-то делать (а в рамках remsrv слот (devid) через некоторое время может достаться совсем другому блоку, от другого сервера).

      И это нужно даже в СЕРВЕРОВОМ inserver.

    Сами дескрипторы -- inserver_dataref_t -- можно реализовать как target_devid*65536+chan_n. Разве что проверки на выход за допустимый диапазон [пока] не будет.

    Помимо технических проблем реализации собственно API есть еще вопросы, отсутствующие в настоящем сервере и обусловленные "сборищной" моделью группировки драйверов:

    1. Где брать имена блоков?

      Добавить команду отсылки имени в протокол remdrv?

      А сами имена -- (a) брать принадлежащее самому remdrv или (b) мочь указывать в auxinfo вместе с set1/set2? Наверное, для унификации лучше (a) (хотя (b) был бы гибче).

    2. КОГДА делать биндинг (get_dataref)? Ведь тут нет порядка "загрузили всю БД, потом запускаем".

      Или повесить это на драйвер -- чтоб, в стиле remdrv/cda, пытался сбиндиться раз в секунду? Тогда автоматом получится и ре-биндинг (восстановление при умирании и последующем рестарте блока-мишени), и управление блоками от иных серверов (появляющихся существенно до или после этого).

    12.05.2012@Снежинск-каземат-11: в СЕРВЕРОВОМ-то реализовано, еще энное время назад и "КОГДА" в драйверах делается именно по вышеприведённому принципу.

    НО: а тут-то как делать -- ведь это означает всё же наличие неких callback'ов, чего как раз очень не хотелось. Хотя, раз уж никакой кэш не нужен, то особых проблем не будет -- при условии НЕиспользования inserver_get_devstat() (впрочем, при АККУРАТНОМ использовании -- со свежеполученным devref'ом -- и его легко сделать).

  • 30.12.2011: оное перетаскивается из nppc_canserver/. Остаются компоненты remsrv_drvmgr.c и remsrv_main.c, а все .h собираются в include/remsrv.h.

    30.12.2011: делаем, с легкими модификациями. Модификации заключаются в добавлении к именам публичных вызовов префикса RemSrv и аналогичном переименовании drivermapping_t в remsrv_drivermapping_t.

    Оно собирается, хотя толку от этого немного (чисто для проверки кода), т.к. применение библиотеки -- только для кросс-сборки.

    26.01.2012: да, теперь libremsrv используется уже и в кросс-компилируемостях. Соответственно, старая, локальная версия из nppc_canserver/ удалена.

  • 27.01.2012: а не пора ль вводить в remsrv фичу "если имя НЕ содержит '/', то это имя внутреннего драйвера, если содержит -- то сделать fork()+exec(), как rrund" (см. раздел CANGW за 25-12-2006/09-02-2009)?
  • 15.06.2012: обнаружилась неприятность -- НЕ вызывается метод init_drv() (он же init_m). В результате не может нормально работать init_lyr() в pre-lyr'ах; в cangw изначально сделано через layer_initialized.

    16.06.2012: и вообще отсутствует поддержка layer'ов как класс -- а надо! Для начала, хоть в протокол добавить (уже в v4, видимо...).

  • 10.10.2012: как бы этак при попытке попросить уже занятое устройство можно б было первоначальному занявшему послать тестовый PING?

    (Первый раз эта мысля возникала еще черт-те когда -- кабы не в Зеленограде...)

    10.10.2012: смысл -- что если это просто клиент (в смысле -- cx-server) молча сдох (например, у него моргнуло питание), то пройдёт аж до 7200 секунд, прежде чем будет обнаружена мёртвость соединения (ровно это произошло сегодня, когда Хамёл дёрнул питание linac1 в целях тренировки Детокоса). По PING'у же произойдёт отправка пакета, и если "тот конец" просто перегрузился, то его ядро немедленно пришлёт RST, и фиктивная занятость сбросится.

    1. Правда, полным решением это не будет -- remdrv при этом всё равно уйдёт в OFFLINE (и НЕ станет пытаться повторно приконнектиться, увы). Но тут уж методологически-идеологическая проблема. Но хоть рестарт сервера прям тут же уже поможет.

      20.11.2012: вариант решения: чтоб remdrv уходил в offline после НЕСКОЛЬКИХ таких попыток -- тогда через 10 секунд remsrv уже точно обнаружит скопытившесть сокета и закроет соединение, освободив позицию, и вторая попытка станет успешной. Но тут встаёт следующий вопрос -- а КАК remdrv должен понять, что тут надо включить счетчик?

      26.11.2012: как-как -- да просто по всем отлупам делать несколько попыток.

    2. А технологическая проблема конкретно в отправке PING'а -- то, что занятость обнаруживает конкретный layer (can, piv485), а у них никакого доступа к файловым дескрипторам "клиентов" remsrv нету (и оных может и не быть -- при работе в сервере). Решабельно ли это?

      21.11.2012: есть идейка, как решить проблему, не вводя всяких лишних сущностей (и не "протыкая" слои): считать, что при передаче ReturnChanGroup() значения count=0 надо сделать "понг".

    26.11.2012: часть 2 вроде сделана, осталась часть 1 -- повтор попыток коннекченья.

    Тут есть тонкость: поскольку всё асинхронно, то нет возможности "сразу" понять неудачу CONFIG'а, ибо ответный CHSTAT придёт очень далеко не сразу. Так что:

    1. Вводим ДВА поля:
      • fail_count -- для подсчёта ошибок.
      • config_timestamp -- момент отправки пакета CONFIG.
    2. Работа с ними:
      • По приходу CHSTAT в OFFLINE делается проверка: если флаги CXRF_CFG_PROBL, время с CONFIG укладывается в разрешенное, количество попыток еще не превышено -- то делает попыткам ++ и заряжает ScheduleReconnect().
      • Вторая, симметричная "проблема" -- определение реально-успешности запуска драйвлета. Сброс fail_count=0 делается
        1. По остальным вариантам CHSTAT (если OFFLINE -- то пофиг, а OPERATING или NOTREADY -- значит, драйвлет запустился).
        2. По любому возврату данных.

        ...в идеале -- драйвлет должен ВСЕГДА при запуске присылать CHSTAT, пусть даже в OPERATING. ...Несколькими минутами позже -- сделано во все, так что по возврату данных убрано.

    3. Числовые параметры таковы:
      • число retry-попыток -- 2;
      • интервал между CONFIG и OFFLINE, в течение которого тот считается ответом -- 3 секунды.

    Надо признать, что все эти махинации -- сплошной хак. Часть проблем была бы решена при наличии отдельного ответа "потенциально врЕменная проблема" (как коды 5xx в ftp/smtp/http), но лишь часть. Основная сложность -- то, что данные фортели сильно выпадают из общей модели работы сервера/драйверов. То ли из-за не совсем адекватности модели для таких перепусков, то ли потому, что вопросы уведомления о перезагрузках должны бы решаться на совсем других уровнях.

    03.12.2012: да, проверено -- remdrv при CXRF_CFG_PROBL повторяет попытки коннекченья еще 2 раза.

    Проверялось просто запуском двух серверов с одним blklist-файлом на разных :N, с последующим прибиванием первого "захватившего" -- тогда оно переходит второму. Также проверялось, что после успешного "захвата" и последующего обрыва оно будет повторяться.

    На совсем живой системе после reset'а хоста проверить пока не было возможности, но будем надеяться и ставим "done".

  • 19.10.2012: усовершенствование в консольный интерфейс: добавляем возможность исполнять консольные команды при запуске, указывая их из командной строки ключом -e КОМАНДА.

    19.10.2012: делаем для команд логгинга

    1. Собственно обработка команды вытащена в публичную функцию RemSrvExecConsCommand().
    2. В remsrv_main.c::ParseCommandLine() добавлена обработка ключика -e.

    Заодно:

    • Выкинута непонятно зачем заложенная ничего не делающая команда "map" (год назад НЕ убранная).
    • Исправлена реализация "who" -- она вместо IP-адресов показываемых консольных клиентов везде печатала адрес спрашивающего.

    На пультовых коробочках теперь на всех включено -- сделан 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 &
    
  • 13.08.2013: старое содержимое директории отправлено на покой и заменено новым, remcxsd-based.
  • 17.08.2013: недурственно бы иметь консольную команду "грохнуть драйвер" -- чтоб оно делало
    SetDevState(devid, DEVSTATE_OFFLINE, 0, "remsrv: Terminated from console");

    15.09.2013: но для этого придётся сделать нормальный парсинг командной строки -- с разбиением на собственно команду и параметры.

    21.10.2013: делаем:

    1. Для начала меняем API функций, теперь им вместо cmd передаётся пара nargs,argp[] (смысл как у argc,argv[], в т.ч. argp[0] содержит былую cmd).
    2. Далее, распарсивание строки, с раскладкой в этот argp[] и подсчётом в nargs.
      • В конце каждого токена записывается '\0'. Что может быть плохо для варианта '-e COMMAND', поскольку никто не гарантирует, что содержимое argv[] лежит не в readonly-секции. Но для односложных команд проблемы не будет, поскольку на место завершающего NUL ничего не пишется.
      • Соответственно, все сравнения ведутся теперь уже с argp[0].
      • Бонус в том, что теперь оно толерантнее к пробелам: раньше "list " было ошибкой, а теперь окей.

        Плюс '#' перед любым токеном работает как комментарий.

      • Заодно было сделано, что в качестве "logout" воспринимается не точно "^D", а что угодно, начинающееся с '^D'.
    3. Собственно команда "kill". Работает, грохает.

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

    22.10.2013: за компанию еще несколько усовершенствований:

    • Добавлены remcxsd_dev_t.when и consrec_t.when, тип time_t.
    • Они заполняются RemSrvAccept{Host,Cons}Connection().
    • А команды list и who выводят эту информацию при nargs>1 -- совершенно аналогично scan.
    • Заодно, кстати, и номер порта клиента печатается.

      У list это выглядит не очень красиво, поскольку из-за давней попытки выравнивания IP-адреса добавленная инфа выдаётся отдельно через пробел.

    • ...и за компанию в remdrv_drv.c вставлена выдача егошнего порта с DRIVERLOG_DEBUG. Так что теперь можно будет разбираться, кто с кем связан.
  • 04.09.2013: какие-то странные глюки бывают у нового canserver'а.
    • В дополнение к странному "inpktsize=0", кажется, встречавшемуся и раньше (логи от такого случая см. в notes/20130816-REMSRV-PROBLEMS.txt), ...
    • ...сегодня на стороне CANGW также вылезли "Segmentation fault" и "Illegal instruction", причём оба предварялись руганью vDoDriverLog()'а, перед которой было непонятно чем вызванный FreeDevID(), а на стороне сервера -- ругань про inpktsize=БРЕД.

      И дальше, уже после завершения драйвера, в log попали сообщения вида

      remdrv[1]/DEFAULT: DEBUGP: ReturnChanGroup(): devid=2(active=-1) is inactive
      т.е., уже после того самого "непонятно чем вызванного FreeDevID()" драйвер продолжал трепыхаться (и логгинг шел через соседний драйвер, по хитроумному алгоритму "last_good_devid").

      Видимо, "непонятно" -- это когда контроллер присылал кривой пакет, то remdrv закрывал соединение, что и приводило на стороне контроллера к закрытию по причине FDIO_R_CLOSED; странно, правда, что сообщения об ЭТОМ на консоль не выводилось.

    04.09.2013: похоже, имеют место две РАЗНЫЕ проблемы:

    1. Почему-то контроллер иногда присылает битый пакет.
    2. В случае закрытия соединения со стороны сервера контроллерный код ведёт себя не вполне адекватно, где-то нет каких-то проверок, и оно иногда падает.

    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().

    • Первая мысль -- а нет ли логической кривости, когда FreeDevID() делается из чего-то, вызванного драйвером (Return*(), например), так что после оного код драйвера ещё активен и успевает дёрнуть sl_enq_tout_*(), так что этот таймаут уже не подчищается и отрабатывается позже, на "мёртвом" драйвере.
    • В связи с этим реализована концепция "uniq_checker", приближающая функционирование к тому, что было в старом API (с отдельной прослойкой, делавшей проверки).
    • Но фиг -- не помогло, всё корректно, ничего для "убиенных" не регистрируется.

      15.09.2013: а вот и да -- увидено такое. Но не только.

    • Поскольку валились ответы "cankoz_add: device (0,NN) is already in use" (а ругань "ReturnChanGroup(): devid=8(active=3) is inactive" сразу после -- т.е., это реакция на RETURNCHAN_COUNT_PONG), то вывод: почему-то не вызываются disconnect()'ы. Что может быть при невызове term_dev()'ов.

    15.09.2013: разборки-разборки, длинные и нудные. Результаты:

    1. Возникала проблема в случае, если между моментом отсылки CONFIG-пакета сервером и моментом реального вычитывания его remsrv'ом, с последующим вызовом init_dev(), сервер успевал скопытиться.

      Тогда в вызове первого же DoDriverLog(,ENTRYPOINT) получалась ошибка отправки и вызывалось FreeDevID() -- с не вполне ясными последствиями: оно ж делает free(devptr), в т.ч. портя содержимое.

    2. Кроме того -- это как бы полу-следствие: init_dev() успевал вызвать iface->add(), тем самым занимая kid, но, поскольку уже перед этим успевало (из-за ошибки) сделаться FreeDevID(), то впоследствии term_dev() уже не дёргался и, соответственно, iface->disconnect() тоже, тем самым kid оставался "in use" навечно.

    Теперь что с этим делать:

    1. НЕЛЬЗЯ прям сразу РЕАЛЬНО высвобождать devid, поскольку драйвер в этот момент может что-то делать и такое выхватывание из рук приводит к проблемам.

      Надо использовать ОТЛОЖЕННОЕ освобождение, как в fdiolib'е -- being_processed/being_destroyed (18-06-2013).

      Сейчас, правда, проблема с тем, что очень даже далеко не всегда сейчас передача управления коду драйвера находится под контролем remcxsd: еще cxscheduler и fdiolib вовлечены. Но это вопрос на будущее.

      А вопрос похуже -- как быть с layer'ами? Ведь ENTER() делается по lyrid, а потом исколняется код уже конкретного драйвера. Заставлять ещё и layer'ы все вызовы оборачивать в ENTER()/LEAVE()? И ведь мозги layer'ов тоже должны будут весьма корректно себя вести и быть готовы к тому, что в момент вызова "LEAVE()" это самое устройство будет высвобождено.

    2. Отдельный прикол -- в каком случае считать устройство за "inited"? Не надо ли и даже если init_dev() вернул <0?

      Причина проблемы в том, что вызов layer'овых disconnect() сейчас возложен на драйверовы term_d(), а не на среду исполнения, как в v4. И если вдруг чё -- то вызов сделан не будет, и "ресурс останется неподчищенным".

    Остаётся только один вопрос: а что всё ж был за прикол с приходом пакетов с бредовыми inpktsize (в т.ч. ==REMDRVP_DEBUGP)? Ведь, по идее, освобождение-devid/remcxsd_dev_t-не-вовремя не должно было давать такого эффекта: там проверка на in_use==0 стоит, и оно б просто ничего не делало.

    15.09.2013: реализуем отложенное освобождение. Некоторые пресуппозиции:

    1. Работу по "откладыванию" и "доделыванию" должны исполнять ENTER_DRIVER_S() и LEAVE_DRIVER_S(), ведь именно они ставятся вокруг каждого вызова драйверова кода.
    2. Поскольку, в отличие от fdiolib'а, здесь входы в драйверов код могут быть многоуровневыми (из-за будущего inserver), то being_processed должен быть не булевскм флагом, а счётчиком, и "можно доделывать" при уменьшении его до 0.

      being_destroyed наоборот, должен быть кумулятивным -- если хоть раз выставлен, то уж и стоять.)

    Итак:

    • remcxsd.h::remcxsd_dev_t: добавлены being_processed и being_destroyed, сразу после in_use.
    • remsrv_drvmgr.c::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. Вот откуда б?

    • А главное: учитывая, что и сейчас в основе remsrv+remcxsd лежит та же архитектура canserver'а начала 2007г., получается, что этот баг с "выхватыванием devid из-под рук драйвера" присутствовал изначально, но был слабозаметен оттого, что менеджмент privrec'ов средой исполнения (в т.ч. free() их) появился сравнительно недавно.

    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. То ли искать проблему ДО того, то ли, наоборот, надеяться, что там, в иных обстоятельствах, вылезет быстрее?

  • 30.09.2014: мелкая неприятность: поскольку __DATE__ __TIME__ печатаются в RemSrvAcceptConsConnection(), то по факту выдаётся время сборки remsrv_drvmgr.c, а не собственно программы-сервера.

    Правильно, конечно, их передавать параметрами в remsrv_main(), но пока проще забить.

remdrvlet:
  • 08.01.2012: начинаем -- просто созданием файла remdrvlet.c, чтоб оно собиралось. Ну и плюс скелетного же include/remdrvlet.h.
  • 17.08.2013: теперь тут живёт свежая, рабочая версия на основе remcxsd (протокол заселения, в компании с собратом remsrv -- в общем над-разделе).
rrund:
  • 28.01.2012: пора переселять rrund.c сюда -- к его родственникам, мутировавшим из мутного содержимого cm5307/common/.
    • Не очень корректно, конечно, держать в lib/ программу, ну да ладно -- пока потерпим.
    • И, как следствие -- теперь rrund будет и под хост-архитектуру. Тоже странновато, но зато всё супер-симметрично.
remcxsd:
  • 17.08.2013: тут будет обсуждение общих для remdrvlet/remsrv вопросов, реализуемых компонентами remcxsd.
  • 04.09.2013: надо б реализовать и ReturnChanSet() -- в интересах как минимум caniva_drv.c, чтоб обходиться ОДНИМ Return*() и отправкой серверу, вместо 4, на каждый пакет от устройства.

    04.09.2013: делаем:

    1. remdrv_proto.h: REMDRVP_RCSET, "RCSt".
    2. remcxsd_driver_v2.c::ReturnChanSet():
      • "PONG" там НЕ поддерживается.
      • Поскольку addrs[] у нас int, то слать сразу нельзя. Поэтому введено волюнтаристское ограничение в MAX_CHANS_PER_RCS=100 каналов за присест.

        Если вдруг станет мало -- то несложно сделать кусочную обработку с "выпихиванием", как в fastadc/Xh_plot.

    3. Номера каналов в пакете идут первыми -- ДО значений и флагов.
    4. remdrv_drv.c::remdrv_fd_p(): добавлена очевидная обработка.

      Тут тоже волюнтаристское ограничение в те же 100 каналов, с теми же возможностями расширения.

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

    Проверено, вроде работает.

  • 04.09.2013: по результатам разборок с предыдущим пунктом и тестирования CANIVA выходит, что новая инфраструктура как-то непотребно тормознее старой (см. статистику в разделе драйвера caniva за сегодня, но выходит четверть-треть).

    Надо опять профилировать, а сначала и просто подумать не помешает...

    05.09.2013: возможно, это из-за разницы в коде отправки пакетов: раньше оно готовило буфер и делало ОДИН n_write(), а теперь по нескольку fdio_send()'ов. Если проблема в этом -- то печально.

    19.09.2013: имеем: каждый вызов fdio_send() использует от 1 (лучший случай) до 3 syscall'ов. Если проблема в них, то надо в fdiolib'е ввести возможность указывать "сейчас начнётся пакет, не начинай его слать, пока не скажу". Т.е., пару скобок lock()+unlock(), которыми окружать пачки вызовов отправки (а их в Return*() по 3-4).

    После обеда: да, вставлено использование свежевведённых fdio_lock_send()/fdio_unlock_send(). Загрузка дохленького 50МГц процессора CANGW сократилась заметно: на v5-sr-ist (работает на ring1:32) в режиме disable_log_frame с ~54% упала до ~36%.

    Надо б еще с CANIVA проверить, для сравнения, но, скорее всего, проблема решена.

  • 14.09.2013: добавлена remcxsd_uniq_checker(). Ради чего пришлось выделить из CHECK_SANITY_OF_DEVID() мясо в DO_CHECK_SANITY_OF_DEVID() -- чтоб вместо __FUNCTION__ подсовывать другое имя, переданное в func_name.

    16.09.2013: переименована в cxsd_uniq_checker() в связи со стандартизацией.

???:
4PyQt:
  • 02.02.2013: вводим директорию для поддержки писания клиентов на Python+Qt, на основе simpleaccess и макеевского "оборачивателя".
  • 02.02.2013: для начала делаем сюда сборку всех требуемых библиотек в одну libCdr4PyQt.so.
  • 11.01.2015: в lib/Makefile добавлена авто-собираемость директорий Qcxscheduler/ и 4PyQt/. Они включаются в сборку при условии наличия файла /usr/bin/qmake-qt4.
???:
???:
v2hw:
  • 30.12.2011: создаём единую директорию, где будет жить всё, касающееся поддержки разнообразного железа в v2: драйверы, стандартные chl-морды, настройки сред исполнения.

    Делается на "архитектуре ProjectRules.mk".

    30.12.2011: показания к сему:

    • Драйверы одного и того же железа требуются в РАЗНЫХ проектах, а живут они, исторически -- в КОНКРЕТНЫХ проектах. В результате получается кривая картина зависимостей, когда qult/ и liu/ вынуждены ссылаться на istc3682/, поскольку именно там располагается kshd485_drv.c (доставшийся туда в наследство от istc/ (который был 2257)).
    • Кроме собственно драйверов, разным проектам нужен и остальной софт, касающийся поддержки железа -- например, nadc200_knobplugin полезен не только в liu/, но и в qult/.

    А так -- вытащим всё в одну точку, и уж сюда пусть ссылаются конкретные проекты.

    Общий принцип: если может быть смысл собирать драйверы под другую архитектуру, то сами исходники помещаются в поддиректорию src/, откуда симлинкуются в директории для конкретных архитектур. Директория для "обычных" всерверных драйверов всегда называется local/.

    08.01.2012: систему сборки здесь делаем на основе ProjectRules.mk, вариант с SECTION (основа взята из yweld/).

etc:
  • 14.09.2012: директория (создана давно) содержит всякие полезные файлы для контроллеров.

    14.09.2012: в настоящий момент структура такая:

    • moxa-uc-7112/.
    • ppc860-unified/ -- для мамкинских железок (исходно взято из ZELENOGRAD/UNIFIED/ и слегка доработано). Там
      1. общая инфраструктура -- rc.sh с mounthost.sh (и скелет eth0.conf), плюс...
      2. ...типо-зависимые файлы по директориям:
        • arch.conf -- уставляет ARCH_SUBDIR, указывающую на корневую для данного типа контроллеров поддиректорию внутри /mnt/host/.
        • rc.banner -- печатает сообщение на консоль.
        • rc.hardware -- загружает драйверы (CAMAC, CAN, VME).
        • rc.local -- запускает софтину (rrund, canserver, ...).

      Поддиректории по типам контроллеров --

      • BIVME2/
      • CANGW/
      • CM5307PPC/

    02.11.2012: были баги в CM5307PPC/ -- rc.local от CANGW, rc.banner пустой. Поправлено.

ppc860-unified:
  • 14.03.2013: дополнение общей схемы монтирования: теперь сделана возможность изменить стандарт "монтируется /export/HOST".

    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".

  • 05.02.2017: после замены свичей пультовой сети со старых "безмозглых" на "умные" (полу-L3) HP вылезла неожиданная проблемка: в этих свичах порт (в который воткнут провод от контроллера) начинает функционировать не сразу, как включится трансивер подключенного устройства, а с некоторой задержкой. В результате первая итерация mounthost.sh'овского монтирования --
    while ! mount -t nfs -o nolock,ro $MY_HOSTDIR $MNT_HOST
    do
        sleep 60
    done
    
    -- обламывается, и потом ждётся ещё минуту.

    05.02.2017: обнаружена проблема была Гусевым на его контроллерах -- у него монтирование было одноразовое, без повторов, и потому всё обламывалось навечно (оно бы и раньше обламывалось в случае включения питания контроллера раньше, чем у хоста).

    Гусеву был выдан вышеприведённый кусочек кода, решивший его проблему фатального облома. Но, дабы сократить время бесполезного простоя (ведь свич-то включает порт в течение секунд), он (посоветовавшись со мной) внёс в процесс монтирования небольшое дополнение:

    SECONDS=10
    while ! /bin/mount /home
    do
        sleep $SECONDS
        SECONDS=60
    done
    
    (приведён фрагмент из ЕГОшнего rc.sh, где монтирование фиксированное). Общий принцип ясен: первая пауза -- 10 секунд, а последующие уже по минуте.

    Переделывать нынешние контроллеры -- лень, т.к. CANGW в ближайшее время заменятся на "canserver'ы" (X11SAE-F с 2*PCI7841 + 4*CP602E), а CAMAC тоже остаётся всё меньше. Но если что -- рецепт есть.

    25.07.2017: в связи с переделкой пультовой сети настройки всё равно приходится менять, так что -- делаем!

    Это всё уже в hw4cx/etc/, свежесозданной.

    Заодно изменения и дополнения:

    • Все arch.conf'ы переделаны на использование 4pult/ вместо pult.
    • Все rc.local также проапдейчены.
    • Поскольку теперь контроллеры и хост-сервер в разных сетях, то в eth0.conf добавлен параметр MY_GW, который в rc.sh проверяется, и если непуст, то делается
      route add default gw $MY_GW
???:
x-build:
  • 28.01.2012: это директория, ответственная за кросс-сборку. В ней -- поддиректории под каждую архитектуру, которые
    1. Содержат кросс-правила -- файл с именем x_rules.mk, который конкретные кросс-директории должны включать вместо TopRules.mk, а уж он сам включит того, плюс как надо донастроит среду сборки.
    2. Прямо у себя собирают lib* -- чтоб каждая конкретная директория не маялась бы с этой сборкой, а уже просто ссылалась бы туда.

    P.S. Она изначально создаётся на новой системе сборки.

  • 09.08.2012: в x_libs_rules.mk, общую для всех кросс-платформ, добавлено MAKEFILE_PARTS+=... на все lib*_COMPONENTS.mk -- чтоб при модификации состава библиотек оно осознавало необходимость пере-сборки.
  • 18.08.2013: в связи с изведением cx'ной programs/server/drivers/... теперь СЮДА добавлена сборка rrund рядышком с библиотеками (тем более, что и живёт его исходник в lib/rem/).

    18.08.2013: также слегка сменено распределение обязанностей: теперь указание ARLIBS= (и свежепонадобившееся MONO_C_FILES=) перенесено из под-директорных Makefile'ов в общий x_libs_rules.mk -- воизбежание дублирования.

ppc860-linux:
  • 20.01.2012: сделана, проверена на can/cangw -- работает.

    23.05.2012: переведена на x-compile/ppc860-linux/.

  • 12.11.2012: пробуем сделать возможность profilability (надо для разборок "почему canserver жрет >90% CPU").

    12.11.2012: действия:

    • Убираем "override" в x_rules.mk.
    • Скопирован gcrt1.o из SDK (initrd_big/usr/lib/ в tools/lib/).

    Сделано. Вылезла пара нюансов:

    1. Профиль (gmon.out) тут НЕ пишется при останове программы по Ctrl+C -- нужен явный exit().
    2. В режиме профилируемости полезли глюки:
      • c4lcanmon вылетает:
        ./c4lcanmon: select()=-1 processing ":": Interrupted system call
        
      • canserver постоянно рвёт соединения с диагностикой вида
        ProcessPacket(handle=16, dev=7): I/O error
        

      Видимо, профилирующий таймер?

      На первое забил, второе исправлено fdiolib'ом (см. за сегодня).

    Профилирование работает.

arm-linux:
  • 23.05.2012: тоже сделана, реально даже еще с месяц назад, но только сейчас доведена до готовности.

    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/.

???:
drivers:
Сама drivers/:
s2v:
  • 17.10.2014: драйвер склёпан еще в прошлом году, раздел под него изготовлен сейчас.
pzframe_gw:
  • 17.10.2014: драйвер был сделан еще 15-03-2014, а сейчас создаём раздел.
    21.10.2014: мыслишка на будущее, в v4: у нас получается, что pzframe_gw -- сильно аналогичен remdrv, в том смысле, что можно точно так же указывать в конфиге все свойства настоящего устройства, но сам тип устройства ставить с суффиксом /pzframe_gw.
adcslice:
  • 17.10.2014: создаём; это драйвер для реализации "вырезок" из данных обычных fastadc. Потребность описана в этом файле за 09-10-2014.
  • 17.10.2014: приступаем.

    17.10.2014: скелет.

    20.10.2014: вроде сделано. Детали:

    • "Основным" клиентом считается adc200me, по умолчанию зашиты его параметры.
    • Параметры, описывающие fastadc-клиента, меняются:
      • maxpts -- максимально допустимое количество точек (аналог ADC*_MAX_NUMPTS).
      • nargs -- количество параметров.
      • param_ptsofs и param_numpts -- номера соответствующих параметров.
      • numlines -- канальность.
      • Заодно ptsofs и numpts позволяют сразу в конфиге указать желаемые значения, вместо умолчательных 0,1024.
    • Менять можно в том числе и ТИП данных -- вместо int32 ставить 16 и 8; ключи i, h b (как тип в cxsd-devlist).
    • Принципы работы:
      • При получении данных делается вырезка со сдвигом на "наше" PTSOFS и длиной с "наше" NUMPTS, со всех линий-каналов.

        Случаи "вылезания за границу" пока никак не отрабатываются (в буфере будет оставаться старый мусор).

      • Идеология работы с параметрами: предполагается, что объём данных определяется _PARAM_NUMPTS'ом, поэтому отдаётся info от реального устройства, но в нём подменяются PTSOFS и NUMPTS.
      • Т.е., карта параметров совпадает с оной у целевого устройства.
      • Записывать можно только в PTSOFS и NUMPTS (причём как через скалярные каналы, так и через запрос большого), остальные игнорируются и возвращаются от реального устройства.
    • Очевидным образом, для nadc333 этот slicer не годится, поскольку в том количество линий не фиксировано, а указывается параметрами.

    Теперь испытывать.

    21.10.2014: к вопросу об испытаниях: можно ведь натравливать драйвер и не на настоящий adc*me, а испытывать в отдельном сервере (хоть вообще на другом компе), через pzframe_gw, смотрящий на настоящий драйвер.

    21.10.2014: да, именно так и испытано. После фиксенья нескольких багов-опечаток вроде работает.

???:
CAN:
  • 08.01.2012: поддиректория can/ (в отличие от былой canbus/).

    08.01.2012: делаем.

    • src/: скопировано всё "стандартное" содержимое.
    • socketcan/: в основном скопировано из liu/drivers/sktcan/.

      Главная модификация пришлась на Makefile. Старый был сделан крайне странно, под "как бы внутридеревную" сборку, там ставилось SRCDIR=../../../cx/src. Сейчас переделано под стандартное ProjectRules.mk -- выглядит, конечно, страхолюдненько: этакая смесь стилей.

    • marathon-2.4/: поскольку тут копировать Makefile было неоткуда, то за основу взят оный от socketcan/.
    • cangw/: по образцу nppc_canserver/, отличия же во внешней libremsrv и, естественно, в отличающемся Makefile.

      Работа не особо сложная, оно собирается, но результат надо будет проверить.

    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: некоторые перетряски в преддверии перехода на новый remsrv и обновлённый драйверный API.

    31.07.2013: списочком:

    • В ListOfCanDrivers.mk теперь указывается список именно ДРАЙВЕРОВ (устройств) -- CANDRIVERS, а не список исходников CANDRIVERSSOURCES.
    • Имена исходников теперь NNN_drv.c вместо NNN_src.c.
    • Убран ставший ненужным remsrv_DEFINE_DRIVER.h.
  • 24.09.2014: а чё б не сделать canserver под x86 -- т.е., под хост-платформу?

    Смысл -- что при переводе управления магнитной системой линака с cangw-magsys на комп с PCI-CAN под SL-6.3 переносить туда сервер -- ай-яй-яй, поскольку поедет вся конфигурация. А вот если сделать этот комп просто таким же набором драйвлетов -- для клиентов всё останется как есть.

    Делать это в директории socketcan/, там всё равно уже всё имеется, надо только добавить собирание еще одного бинарника из готовых кубиков (один из которых, правда, canserver.c -- надо симлинковать из ../c4l-cangw.

    25.09.2014: делаем

    29.09.2014: доделываем. Чтоб не было зависимости между директориями, чья собираемость может включаться независимо, основа canserver'а унесена из c4l-cangw в src/:

    • Сам canserver.c.

      Для правильного "issue" введён define CANSERVER_ISSUE, в котором можно указать нужную строку.

    • Правило для генерации canserver_drivers.c уехало в src/ShadowRules.mk.

    Ну и собственно в socketcan/ теперь v2sktcanserver делается (для этого, кстати, пришлось генерить пустой canserver.c, иначе make не хотел -- известный прикол с ним).

cpci:
  • 09.01.2012: сюда скопированы драйверы из liu/drivers/cpci/.

    09.01.2012: после простейшей адаптации Makefile всё собралось.

    Главная кривизна -- присутствует явная ссылка CPPFLAGS+=-I$(PRJDIR)/../uspci/include...

    07.05.2012: а как от кривизны избавиться? Поселить uspci сюда, в v2hw/kernel/x86_uspci/?

serial:
  • 23.05.2012: оно было сделано еще энное время назад, но только сейчас доводим до завершенного состояния -- с moxa.

    23.05.2012: поскольку собирается moxa не везде под LINUX-X86, и внятно определить собираемость ЗАРАНЕЕ нельзя (т.к. дело в левости -- GLIBC_2.3), то довольно от балды выбрано требование, что ядро >=2.6.18. Сама проверка в serial/Makefile идентична оной в can/Makefile.

  • 18.06.2012: радикальное изменение в API piv485_lyr/nkshd485: раньше в _in() полученные данные передевались "как есть", вместе с байтом адреса в начале и контрольной суммой в конце. Теперь же они отбрасываются -- piv485_fd_p() передаёт (...,dlc-2,data+1);
moxa:
  • 23.05.2012: оно также было сделано еще давно, а сейчас начало стандартизованно собираться (а не в make -C).
???:
camac:
  • 30.08.2012@Снежинск-каземат-11: создаём, для непосредственной цели изготовления тут внутри src/camac_fastadc_common.h; инфраструктуру же сборки будем городить чуть позже.

    30.08.2012@Снежинск-каземат-11: делаем и инфраструктуру сборки, с единственной директорией camac/cm5307_ppc_drvlets/. Покамест просто для проверки компилируемости pzframe-драйверов, так что халтурновато (хоть уже и с src/ShadowRules.mk):

    • Без системы исполнения (коей пока просто не существует ввиду неготовности libremdrvlet).
    • Без поддержки сторонних директорий -- т.е., без DirRules.mk.
    • Без поддержки абстрактных драйверов.
    • И само содержимое makefile'ов, мягко говоря, далеко от совершенства.
    • А главное -- там пока НИГДЕ нет pipe-обработки LAM'ов!

    21.07.2013: за последние несколько дней состояние сильно продвинулось:

    • В связи с появлением libremdrvlet есть система исполнения.
    • Добавлена pipe-обработка LAM'ов.
    • Сегодня сделана поддержка абстрактных драйверов -- camac_abstract_common.h. Детали:
      1. Оно существенно урезано по сравнению со старым std_abstract_iface.h+i_cm5307.c. Никаких там больших каналов и DRIVERLOCAL, плюс убраны N2, set1, set2. Всё, требующее подобных вещей -- должно сразу делаться ОБЫЧНЫМИ драйверами. Абстрактные же -- чисто для тривиальных чтения/писания, как в индикаторе магистрали (i033).
      2. Сама технология использования радикально отличается. Теперь нет никакой особой инфраструктуры (сборки) для абстрактных драйверов. Файлы драйверов имеют стандартные имена ТИП_drv.c, а уж внутри должны сами сделать
        #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!

  • 26.07.2013: надо бы API WATCH_FOR_LAM() перетащить прямо в сам cm5307_camac.[ch] -- чтоб оно было независимо от driver-API, и доступно cm5307_test'у.

    26.07.2013: для этого оно должно будет сразу САМО пользоваться cxscheduler'ом.

    Заодно для независимости придётся сделать, чтоб оно возвращало NULL при успехе и строку-описание при ошибке, вместо использования DoDriverLog()'а.

    Получасом позже: да, теперь оно возвращает строку-описание либо NULL.

    29.07.2013: ...впрочем, cm5307_test этим всё равно не пользуется, поскольку вломы было его делать на cxscheduler'е -- у него своя личная копия всего этого кода, с соответствующими модификациями (криво, да).

  • 18.08.2013: добавлено копирование сюда из x-build/'а и инсталлирование rrund.
  • 18.08.2013: крайне желательно иметь возможность сторонней сборки CAMAC-драйверов ("внедеревной" -- из директорий вне v2hw/). Так что начинаем изготавливать cm5307_ppc_drvlets/DirRules.mk.

    22.08.2013: за вчера-сегодня сделано. Пока не мега-красиво, но работает.

    • "На стороне" ссылка на v2hw/ должна указываться в V2HDIR.
    • Тамошние директории должны иметь аналогичную структуру -- BASE/src/ с исходниками и ListOfCamacDrivers.mk, а сборка в BASE/что-нибудь/, где и ...
    • Makefile'ы должны иметь вид
      PRJDIR=		../..
      SECTION=	$(V2HDIR)/drivers/camac/cm5307_ppc_drvlets/DirRules.mk
      include		$(PRJDIR)/PrjRules.mk
      
    • В PrjRules.mk пришлось внести изменение, поскольку теперь "вне-cx/src/'шность" SECTION может быть не только в случае соответствия шаблону .*, но и при /*.

      Поэтому строка

      ifeq "$(patsubst .%,,$(SECTION))" ""
      должна быть заменена на
      ifeq "$(filter-out .% /%,$(SECTION))" ""
???:
vme:
  • 26.07.2013: создаём раздел (директория появилась еще в прошлом году, и содержала bivme2/ с копией того, что было в cx/).

    Здесь поселим драйверы со стандартным разделением на src/ и "РЕАЛИЗАЦИЯ/". Покамест единственная реализация -- именно BIVME2, работы будем вести в nbivme2/.

  • 26.07.2013: делаем nbivme2/.

    26.07.2013: детали:

    • Инфраструктуру копируем с camac/ -- файлы bivme2_DEFINE_DRIVER.[ch] и Makefile.
    • А вот внутренности пока заточены под прямое использование bivme2_io -- переделаем на обобщённый API потом, в случае надобности.
    • Еще из минусов: оно пока (как и cm5307_ppc_drvlets/) никак не рассчитано на стороннюю сборку (никакого DirRules.mk), да и общая структура оставляет желать лучшего -- отдельно указываются несколько .d-файлов.
    • В отличие от старого подхода, где драйвера получали имена вида ТИП.drvlet, теперь будет префиксирование -- bivme2_ТИП.drvlet.

    18.08.2013: оно уже превратилось в просто bivme2/ и вроде б работает, так что "done".

  • 18.08.2013: добавлено копирование сюда из x-build/'а и инсталлирование rrund.
  • 22.08.2013: сделана поддержка сторонней сборки по образу и подобию camac/cm5307_ppc_drvlets/.
???:
???:
???:
chlclients:
???:
fastadc:
  • 09.01.2012: сюда переезжают стандартные клиенты и knobplugin'ы из liu/y/.

    Пока что -- всё скопом, как _data, так и _gui/_knobplugin/main. Если cx/src/lib/fastadc/ разделится -- то и тут подумаем.

    10.01.2012: сделано, оно всё собирается, в т.ч. ненужные самой директории *_knobplugin.o.

    13.05.2012@Снежинск-каземат-11: а нифига -- вот как раз *_knobplugin.o там НЕ собирались.

    • Разборки показали ляп в Makefile -- видимо, при переезде на новую систему сборки переменная ADCS исчезла, заменившись на EXES, но использование её в UNCFILES осталось.
    • То исправлено, но вылезла новая неприятность: для тех файлов не генерятся .d-зависимости.
    • Пока что проблема решена с помощью DIR_DEPENDS.

      Но вообще-то это халтура -- проблема именно в разблюдовке имён, в компетенции то ли GeneralRules.mk, то ли xmclients/DirRules.mk. Дело в том, что НЕТ возможности локальной директории указать список файлов "для готовки", которые входили бы в список "для них генерятся зависимости".

  • 14.08.2012: сделан fastadcclient -- программа, включающая все fastadc-плагины. Определения LOGD_*ADC* содержатся в fastadcclient.h. Сам .c тривиален.
???:
lib:
  • 02.05.2012: создана пока что для помещения в неё tsycamlib и miscXutils(gray2image).

    Библиотеки перемещены.

???:
xmclients:
  • 02.05.2012: создана для перемещения сюда "стандартных" клиентов (*adc* и tsycam).

    Оные перемещены.

    14.08.2013: остался один tsycam -- все *adc* изведены.

???:
given:
  • 18.08.2013: создаём директорию для переселения туда всех файлов, полученных "извне" -- т.е., всякие специфичные include'ы, библиотеки, и т.д. и т.п. В т.ч. те, что по какой-то причине не попали в соответствующую поддиректорию в work/x-compile/.

    Собираться/генеритья они никак не будут, и сама директория given/ в списке сборки никак не фигурирует, но на её содержимое смогут ссылаться прочие директории.

    18.08.2013: что сделано конкретно сейчас:

    • bivme2/ -- туда ушли libvmedirect.[ha] из drivers/vme/bivme2/.
    • marathon-2.4/ -- candrv.h из drivers/can/marathon-2.4/.
    • cangw/ -- can4linux.h из drivers/can/c4l-cangw/.

    Замечания:

    1. По-хорошему, туда бы надо и uspci.h поселить, а то сейчас есть уродливая ссылка -I$(PRJDIR)/../uspci/include. Но, с другой стороны, ...
    2. ...была же задумана у нас поддиректория v2hw/kernel/, где и должны б жить такие "специфичные" вещи, и даже начата была возня в cm5307-ppc/...
???:
???:
???:
win32:
  • 25.12.2004: некоторое время назад Саше Антонову было поручено разобраться с тем, что потребуется подперекурочить для портируемости под Win32, и ныне известен результат. Итак:
    • Работа с памятью -- обычным образом, malloc()/realloc()/free().
    • Проблем с максимальным размером при отсылке -- нету, даже два мегабайта прекрасно отправляются за один прием.
    • select() вполне работает для проверки, какие из сокетов готовы для чтения/записи.
    • Все IP'шные танцы с бубном -- htons(), gethostbyname(), etc., также совместимы.
    • Единственное кардинальное отличие -- что read()/write() с сокетами НЕ работают, надо использовать send()/recv().

    Ну что -- в дополнение к n_read()/n_write() и uintr_read()/uintr_write() делаем еще n_recv()/n_send() и uintr_recv()/uintr_send()?

    Кстати, uintr_write() не содержит логики на тему OPTION_CYGWIN_BROKEN_WRITE_SIZE -- это оно так и должно быть, или что?

  • 06.02.2005: кстати, о птичках -- ведь в Форточках fd_set устроен по-другому (это массив int'ов), а сами файловые дескрипторы -- это НЕ небольшие числа, как принято в POSIX'е, и могут иметь очень даже произвольные значения.

    06.02.2005: Т.о., нынешняя архитектура с массивом, индексируемым файловыми дескрипторами (принятая в cxscheduler'е, будущем fdiolib'е, а ныне -- в cx-server'е (основной цикл и connlib)) под Win32 работать не будет в принципе.

    Что будем делать? В принципе-то решение само напрашивается:

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

    Т.е., каждая из затрагиваемых библиотек (cm5307_drv.c не считается -- он переедет на fdiolib) заводит внутри себя массив некоторого размера, достаточно произвольного. Детали реализации:

    • Размер этот определяется неким #define'ом, позволяющим указание из командной строки -- например, так:
      #ifndef ARRAY_CAPACITY
        #define ARRAY_CAPACITY 1024
      #endif
      enum {CAPACITY = ARRAY_CAPACITY};
      
    • Далее менеджмент этих вещей ведется обычным принятым образом -- при добавлении находится первая свободная ячейка (как это сделано сейчас в cxlib), плюс всегда помнится количество занятых ячеек -- для циклов по этому массиву (таковая технология у нас уже используется в макросах GENERIC_FD_*()).
    • Все циклы делаются именно по этому массиву, а не по дескрипторам. В том числе и поиск после select()'а.
    • Из минусов: частенько придется выполнять поиск ячейки по некоему дескриптору.

      В fdiolib'е то это просто -- можно при регистрации дескриптора в cxscheduler'е указывать в качестве privptr именно номер ячейки.

      А вот в самом cxscheduler'е, особенно при модификации маски событий -- сейчас-то там ничего не проверяется, и просто идет работа с {r,w,e}fds; что ж -- в будущем ради оптимизации тоже не делать проверку, что данный дескриптор у нас зарегистрирован?

    Естественно, таковая модификация должна быть проделана ТОЛЬКО с cxscheduler'ом и с fdiolib'ом, а старый сервер трогать не надо -- пусть остается как есть, ему (с его хитрой трехпроцессной архитектурой и с sendmsg()) форточко-совместимость и не требуется.

    02.05.2005: хе, заново (склероз -- приятнейшая из болезней!) пришла в голову та же идея -- "добавить лишний уровень косвенности", и ввести тип fdio_handle_t. С парой дополнений:

    • Понадобятся также fdiolib'овские функции для реализации стандартных POSIX'ных вызовов, но с fdio_handle_t вместо fd -- типа fdio_accept().
    • А после этого чем fdiolib будет принципиально отличаться от stdio?

    И сразу соображение: а зачем, собственно, нам иметь fdiolib (читай -- cxsd) под Форточки? А оно нам туда надо?

    19.12.2011@Снежинск-каземат-11: сейчас увидел этот вопрос "оно нам туда надо?" -- да конечно надо! Ведь fdiolib теперь будет вовсе не только для cxsd, а вообще одним из корневых компонентов системы, делающим возможной модульно-кирпичиковую конструкцию, когда "кубики" с формально-разных уровней могут собираться вместе в единую программу (например, утилита для диагностики CAN-устройств, содержащая в себе сразу и GUI, и невидимый снаружи сервер с драйверами конкретных устройств).

Система запуска:
Общее:
  • ??.10.2003: возникла мысль, как можно "расшить" starter от компиляции с исходниками всех chlclients, да и заодно дать ему способность отображать состояние систем, имеющихся только в, например, xmclients.

    Основная идея: он не компилируется с теми программами, а подгружает их через dlopen(), и берет из них "формальное описание". Некоторые вопросы/тонкости:

    • Вопрос: можно ли делать dlopen() прямо самой исполняемой программы, или придется на каждого клиента генерить дополнительный .so-файл? Если Linux такой финт позволяет, то надо проверить и другие ОС.
    • В любом случае в подгружаемом файле надо будет завести специальную структуру, содержащую информацию, которая сейчас описывает клиента в SimpleClients.lst (заголовок окна, имя сервера), плюс ссылки на physinfo, etc.
    • Есть некий вопрос, как мониторировать системы, работающие только с большими каналами. Видимо, надо их обозначать в той структуре неким специальным образом, и cda, если не ошибаюсь, вместе с cxlib'ом и самим сервером сейчас вполне способны понимать пакеты с 0 форков (т.е., пустые, без запросов) и присылать на них ответы, каковые и будут достаточны для определения "живости".
    • Как поддерживать "сторонние", т.е., не-CX-системы -- например, гусиные? Ответ: в любом случае ведь понадобится некий файл со списком мониторируемых подсистем (типа ~/cx/conf/starter.conf), так можно сделать его состоящим не просто из "по одной строке на название системы/бинарника", а чтобы каждая строка могла опционально содержать (через ","?) описание, как запускать и как тормозить такую стороннюю систему. Видимо, отдельно понадобится еще указывать имя класса окна такого "иностранца" -- чтобы можно было его найти и сделать raise.

    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-код действительно непонятно как грузить куда попало.

    Заодно проверил на других рядомлежащих ОС. Результат занятный.

    Sky/IRIX-6.5
    компилируется и линкуется даже безо всяких -ldl, но при запуске дает SIGILL.
    Rdist/BSDI/3.1
    не желает линковаться даже при указании -ldl -- все ругается, что "Undefined symbol _dlNNN referenced from text segment". А при явном указании /usr/lib/libdl.a в командной строке все-таки слинковалась. Потом понял -- оно почему-то требует при компиляции .c->.exe, чтобы -ldl указывалось ПОСЛЕ имени .c-файла.

    Ладно, откомпилировал, запустил -- ругается аналогично 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 так, что теперь

    1. ../черт-знает-где/allsystems.h ему не нужен.
    2. Вся информация берется, аналогично chlclient, из _db.so-файлов.
    3. Список систем читается из файла cx-starter.conf, который ищется сначала в ./, а потом либо в ~/cx/configs/ либо в $CX_ROOT/configs/.

    05.02.2004: поскольку реально заявленная в заголовке темы цель (освободить cx-starter от allsystems.h) достигнута, и этот вариант работает, помечаем раздел как "done". Остальные цели -- поддержка больших каналов, staromakh и "иностранцы" -- заслуживают отдельных разделов.

    19.04.2004: насчет "иностранцев" через cx-starter.conf: а вот ЭТА потребность послужила уже последней каплей в принятии решения о создании библиотечного модуля paramstr_parser.[ch]. Все потенциальные проблемы -- типа необходимости указывать команды с пробелами -- решатся сами собой.

  • 18.04.2004: хочется, чтобы chlclient'ов можно было пускать напрямую из cx/exports/bin, без установки $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: а еще захотелось -- чтобы оно искало .so-файл также и рядом с самим "бинарником"/симлинком -- для возможности пускать (в основном -- "сторонние") програмы прямо из директории компиляции, безо всяких "make exports".

    05.07.2004: дык -- какие проблемы: вводим в OpenDescription() еще один шаг -- F_ATARGV0DIR, который практически запараллелен с F_ATDOTDOT, только используется другой формат в snprintf().

  • 10.06.2004: так, типа мысль/идея/звоночек: раз у нас теперь все описания подсистем -- chlclients -- содержатся в .so-файлах, то есть риск заполучить несовместимость при изменении, например, формата elemnet_t.

    Так что -- надо как-то применять версионирование и здесь...

    11.06.2004: ну точно -- вот сегодня намылился выкинуть LOGT_WRITEM, так что значение LOGT_MINMAX изменится, а проверка -- йок...

    12.06.2004: вот осел!!! Ведь это версионирование было заложено в схему (subsysdescr_t и OpenDescription()) с самого начала, просто я про это забыл!!!

  • 08.07.2004: как насчет выдернуть descraccess.c в отдельную библиотеку, чтобы ею могли пользоваться СОВСЕМ произвольные программы, в частности, будущий "vacuum с гистограммами"? И тогда можно будет вернуть chlclient.c в chlclients/.

    (Замечание: по смыслу-то эта библиотека очень близка (родственна) 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/" -- а это реально нужно? Надо подумать, и если да -- то сие есть действие совершенно несложное.

  • 19.07.2004: кстати, а как насчет ввести в subsysdescr_t дополнительное поле -- clientprog? По умолчанию -- "", что будет означать "chlclient".

    30.08.2004: еще явно потребно иметь и поле options, где указывать флаги типа "notoolbar" и "nostatusline". Нужно, в частности, для мелких программ Феди-jr, у которых внутри всего-то пара полей, и всякие декорации (да и работа с файлами тоже) им совсем ни к чему.

    31.08.2004: поля добавил. Что реально было сделано:

    • Изменения в cxdata.h: собственно добавление полей, плюс макрос DEFINE_SUBSYS_DESCR().

      Продвигать CXSS_SUBSYS_VERSION_MAJOR не стал -- предыдущая-то еще не успела расползтись, достаточно ручного обновления на пульту.

    • В mkclient.sh пока просто добавлены в DEFINE_SUBSYS_DESCR() пара "". Парсинга пока что нет.
    • Добавил параметр options к ChlRunSimpleApp(), и вставил его парсинг и обработку -- практически скопировав из махинаций с eleminfo_t.options в Chl_gui.c. Здесь сепараторами назначены не только пробел и табуляция, но еще и ',', для удобства указания в SimpleClients.lst.

      За компанию удалил из Chl_simple.c #include Xm/{FileSB,SelectioB}.h, остававшиеся там еще со времен, когда файловые диалоги НЕ были унифицированы.

    • Соответственно, подправил chlclient.c. Заодно выкинул оттуда старый, за-#if-0'енный код на тему setenv("CX_SERVER"). А еще там имелся столь же давно нафиг не нужный закомментированный cda.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, а на что-нибудь, причем если оно !="-".

  • 27.08.2004: надо б строку "CX_ROOT" загнать для приличия в какой-нибудь #define.
cx-starter:
  • 19.04.2004: захотелось, чтобы и cx-starter.conf в cx-starter'е искался "по-умному" -- и в "dirname(argv[0])/../configs/". (Не то, чтобы это реально было сверхнужно, но для отладки дюже удобственнее.)

    02.04.2007: поскольку сейчас уже давно совсем другой подход к config-файлам, с возможностью указания пути к нему, и не файла вообще а директории -- "obsolete".

  • 12.07.2004: насчет внутренностей cx-starter'а -- за прошедшее время появилось несколько идей.

    12.07.2004: вот они:

    • (давно): Надо б этому типу как-то уметь config-файл указывать! Чтоб не париться с Xt'шным парсингом опций -- например, если argv[1] начинается с '@' -- это это ссылка на конфиг.
    • 14.06.2004: Какие ключики могут понадобиться в config-файле для "иностранцев":
      • foreign -- что это не-cx подсистема. Второй вариант -- "noncx". В любом случае, надо это делать как PSP_T_FLAG, и -- в будущем, если появится прямая поддержка Epics'а, можно будет ввести три варианта: SUBSYS_CX=0/*default*/, SUBSYS_EPICS=1, SUBSYS_FOREIGN=2 (а старые ключевые слова можно будет оставить для совместимости).
      • app_class="name" -- имя класса окна (для поиска его на экране -- при проверке на запущенность).
      • start=[@host:]cmdline -- команда запуска. Она, естественно, может содержать пробелы -- это может выглядеть, например, как
        start=@linac1:"cd ~/smc && ./smc podpol"
        (а можно и весь аргумент засунуть в кавычки).
    • 14.06.2004: видимо, придется для запуска других программ изготавливать свой аналог popen(). За основу можно взять curcx/src/chl/chl_data.c::OpenPipe() (она когда-то давно была позаимствована из NEdit'а).
    • 12.07.2004: надо б иметь какой-то способ "показывать" на пункт [кнопка] и [**сервер**] -- чтобы при исполнении команд "запуск", а главное -- "запуск всего" пользователю было б видно, чем оно занимается.

      Самое простое, что приходит в голову -- это слева от кнопок и от leds серверов добавить еще по одной колонке, в которую класть XmLabel (или что-то подобное), которая обычно пуста, а при надобности -- содержит символ ">". (А при сильном желании можно туда и циферку таймаута писать, как у Олега.)

    • 12.07.2004: и, процесс запуска как одной подсистемы ("сначала -- сервер, потом -- еще сервер, ..., потом -- клиент"), так и всех их скопом можно сделать на основе seqexecauto -- malloc()'ом выделять память под энное количество команд, а потом программно генерить sea-код.

    19.07.2004: на тему отображения статусов не-CHL-/не-CX- (или, как с rfsyn, доисторических CX-) клиентов:

    • В таких случаях вместо leds в соответствующую колонку помещается специальный виджет (а лучше -- такой же круглый XmToggleButton), одна штука.
    • Способ извлечения статуса указывается в config-файле ключиком statuschkcmd="command" (каковая возвращает exit-status 0 (true) при запущенности и !=0 (false) при отсутствии "сервера"). По хорошему этот ключ должен понимать тот же формат "[@host:cmdline]", что и start=.
    • Частота проверки статуса указывается ключиком statuschktime=SECONDS. Значение 0сек. (принимаемое по умолчанию) отключает периодические проверки.
    • Пользователь может вызвать проверку "руками", щелкнув по togglebutton'у. Тем самым будет имитироваться отокаревский вариант с "проверкой по запросу".
    • Проверка по нажатию пользователя будет прерывать уже запущенную проверку -- хоть периодическую, хоть пользовательскую же. Это нужно для случаев зависания "проверяльщиков".
    • При отключенной периодической проверке обычно индикатор будет не красным/зеленым, а серым -- чтобы не смущать оператора показом устаревшего статуса. В таковое состояние он будет переходить, например, через 10сек после получения результата. Подобное же "посерение" имеет смысл делать и при запуске проверяльщика -- чтобы при его зависоне не горел устаревший статус.

    05.06.2007: учитывая, что в будущем всякие не-родные клиенты все же будут поддерживаться прямо в cda, все вышеописанные танцы с бубном выглядят излишними, так что помечаем как "withdrawn". Хотя идея и хорошо продумана, практически готовый проект.

    19.07.2004: учитывая, что количество и длина ключиков для не-CHL-систем могут быть весьма велики:

    Стоит предусмотреть в "читчике config-файла" возможность разбиения строки на несколько при помощи бэкслэша. (Где-то ведь такая фича была -- если последний символ строки -- бэкслэш, то читаем следующую строчку и прилепляем ее в конец этой.)

    19.07.2004: И, кстати: а не придется ли делать, чтобы можно было в командах/параметрах указывать ${ПЕРЕМЕННАЯ} а то и вовсе %{ПАРАМЕТР} (типа имени хоста)?

    (Ага, ню-ню, еще в psp поддержку $-переменных засунуть...)

    26.07.2004: за последние три дня крупно переделал внутренности cx-starter'а на тему того, откуда и как берется список систем.

    Итак -- теперь вместо былых халявных проверок сделана последовательность из трех шагов:

    1. ParseCommandLine() -- просматриваем командную строку и загоняем ее в reqd_count::reqd_systems[].
    2. ReadConfig() -- подыскивается подходящий config-файл и считывается в config_count::config_info[].
    3. 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'ов:

    • Стоит слегка перекурочить "обычные" LED'ы -- чтобы они светились в состоянии set=False (т.е., ставить им XmNunselectColor вместо XmNselectColor и поддерживать состояние XmNset=False). Это будет более "консистентно" с остальными выпуклостями. 29.07.2004: все сделано в Chl_leds.c.

      А индикаторы a_ и d_ не трогаем -- поскольку они пользуются протоколом XmNvisibleWhenOff=False.

    • А с индикатором статусов не-CX-подсистем стоит сделать так: в обычном состоянии он тоже "выпуклый", и цветом отображает состояние (красный/зеленый, при "неизвестности" -- цвета background'а). А по нажатию -- т.е., при запуске проверки, но ДО получения результата -- он становится "темнее", как обычный включенный ToggleButton, показывая "задумавшесть".

    27.08.2004: (реально давно, с полмесяца назад, но записываю сейчас):

    Надо б иметь возможность указывать НЕСКОЛЬКО config-файлов/директорий. Как? А так: ReadConfig() вызываем прямо из ParseCommandLine(), помечая это переменной типа "config_was_specified". А сортировать в ReadConfigDir() -- не весь список, а только тот слайс, что был добавлен ею.

    02.04.2007: поскольку cx-starter используется уже не первый и даже не второй год, то помечаем этот первоначальный рздел как "done".

  • 26.08.2004: итак, cx-starter приближается к совсем юзабельному состоянию.

    26.08.2004: сделал, что по нажатию на кнопку проверяется, есть ли уже такая программа на экране. Это было хитрО! Highlights:

    • Во-первых, надо знать не app_class, а именно app_name -- в частности, потому, что тем же app_class'ом помечаются и все popup'ы, а вот app_name -- уникален.
    • Во-вторых, хотя бы просто найти указанное окно на экране -- великая проблема! Проблема оттого, что это окно зарыто где-то на 2-м/3-м уровне, от-reparent'енное window manager'ом.

      Сначала я, по наивности, думал, что достаточно взять кусок из 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()). Проблемы:
      1. С виртуальным десктопом -- что другие экраны, что другие десктопы... Но с этим, похоже, так просто ничего не сделаешь, а лезть в "extended windowmanager hints" (или как оно называлось -- "net_wm"? EWMH?) как-то неохота.
      2. А вот как насчет фокусировать при этом окно? XSetInputFocus() -- штука очень нетривиальная, и фиг его знает, как оно интерферирует с WM'ами. Надо разбираться. 15.06.2008: Ой, ну о-о-очень нетривиальная, прямо опухнуть можно, щаз! Сделано, легко (см. раздельчик за сегодня)!

      В общем -- по первости достаточно и деиконизировать и вытащить, это уже сделано. А чтобы привлекать внимание -- можно поверх окна класть другое окошко, и поморгать им секунду, как это делает editres по команде "flash active widgets".

    • Да, еще из недостатков: такой поиск окна занимает очень большое время -- и X-сервер, и программа начинают существенно подгружать процессор. Плюс в том, что операция это -- крайне редкая.
    • По ходу дела пришлось выяснить, что же такое "атом", и что делает 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: итак, что еще надлежит реализовать:

    • Надо ввести параметр "title=" и возможность поиска окна по нему.
    • Параметр "chaninfo=chanref{,chanref}" -- при этом чтобы OpenDescription() вообще не делалось. 29.09.2012: ага, не делается; это аукнулось -- см. комментарии за сегодня.

      А при указании номера канала меньше 0 (типа "linac1:0!-1") чтобы этот канал НЕ использовался, а только делалось бы соединение. (А для остальных, не-первых серверов, как? Или -- вводим этот хак в cda, чтобы при n==-1 cx_{get,set} реально не исполнялись?)

    • Показывать окно a-la editres, плюс -- рисовать одновременно ПОВЕРХ СЕБЯ стрелку в нужном направлении, где это окно. Такой способ позволит решить и проблему "окно в другом виртуальном экране".

    03.09.2004: кстати, мысль, как запускать cx-starter:

    (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` &)
    
    Плюс, возможно, еще какая-нибудь иммунизация на тему SIGHUP a-la nohup (но чистый nohup использовать нельзя).

    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 -- и мерцание, и указание стрелкой.

  • 27.08.2004: надо б сделать так, чтобы вся информация из _db.so-файлов вычитывалась и копировалась к себе, а после того -- им бы делалось dlclose().

    27.08.2004: ага, попробовал -- сделать-то это можно, но много маеты с physinfo, особенно в варианте physinfo-dbase.

  • 29.08.2004: история разочарований и радостей -- запишем здесь, на память :-):

    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 менять не буду, отложу на неделю, а будет такое:

    1. Общее: произведена смена софта:
      • Всех программ вообще, новый интерфейс: нет сетки, большой красный курсор, программы появляются не в левом верхнем углу.
      • RfSyn
      • ИПП
    2. ИПП:
      • Совсем новый, однооконный интерфейс. Он и удобнее, и занимает меньше места, да и кнопки сделаны пиктограммами вверху.
      • Отображается "перегрузка" -- бордовым.
    3. Обычные программы:
      • Они стали компактнее -- убрана сетка, все "подсжато".
      • При "проблемах" с железом (отключенный блок, проблемы с контроллером, дохлый блок, перегрузка по входу) каналы могут "бордоветь".
      • По нажатию ПравойКнопки вместо былого окошка "Шаг" теперь появляется окно "Свойства ручки". Там есть и шаг, и еще море параметров, плюс -- списочек флагов, и если какой сработал -- то он подсвечен бордовым. Наведя туда мышь, можно увидеть комментарий, в чем дело.
      • По Ctrl+ПравойКнопке появляется окно "Большое число" -- когда значение канала виднО издалека.
      • Double-click на заголовках колонок позволяет эти колонки отключать. Аналогично можно отключать целые элементы.
    4. 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 до готового состояния... Ладно, план на будущее такой:

    1. cx-starter
    2. новый vacclient
    3. файловые диалоги

    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: обнаружилось, что детки-то после своего завершения оставались висеть как зомби!

    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).
  • 11.10.2004: стало ясно, как иммунизировать программы от закрытия их parent-xterm'а: просто ставить 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: насчет возможности разбивать строки на несколько при помощи бэкслэша '\\'.

    28.10.2004: сделал первоначальную версию, работает.

    Вообще-то выглядит она малек некрасиво, схема такая:

    while (fgets(...)) // Основной цикл чтения файла
    {
        do
        {
            ...проверяем, если отсутствует '\\' в онце строки -- то break;...
            fgets(к-концу-уже-считанного);
        } while (1);
    }
    
    Уродство -- в том, что мы имеем ДВА fgets()'а.

    Сейчас прошерстил все содержимое ~/work/ на тему, где ж еще я подобную фичу делал, и как. Нашел -- опять в work/ucam/mkdb.pl. Там схема такая (это часть функции ReadLine(), детали выкинуты):

    $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;
    }
    
    Надо бы перейти на аналогичную схему -- выглядит более "straightforward".
  • 29.10.2004: давным-давно пора сделать, чтобы по нажатию кнопки приложения оно само пыталось бы стартовать все серверы.

    29.10.2004: сделал -- оно просто проходится по всем серверам и делает им ChangeServerStatus(,,1).

    30.10.2004: да, еще, для не-cda-программ -- надо было сделать, что если при сим процессе реально происходит запуск сервера, то надо немножко подождать, чтобы сервер успел стартовать. А то клиент запускался, а готового сервера еще не было. Вылезло это на чугуевском spectr'е.

    Вставил -- 1сек., половинки оказалось мало :-).

  • 30.10.2004: перед запуском cx-клиентов НЕ делалось "cd .../settings/SYSNAME".

    30.10.2004: вставил -- теперь делается cd, в обоих вариантах -- хоть в $CX_ROOT, хоть в $HOME.

  • 30.10.2004: обнаружил, что почему-то кнопки меньше по высоте, чем alarmled'ы. Тоже глюк сетки с одной строкой?

    08.11.2004: да, это был именно тот глюк. Сейчас он исправлен, так что -- "done".

  • 30.10.2004: еще мысль: а как насчет того, чтобы делать геометрию, с которой cstart пускает cx-starter'а, настраиваемой per-host?

    30.10.2004: да, есть пара идей:

    1. Сделать в дополнение к файлу cx-starter-HOST.conf еще какой-нибудь "cx-starter-geom-HOST.conf".
    2. Прописывать геометрию прямо в cx-starter-HOST.conf, в виде комментария.

      (Подобный подход используется в команде chkconfig -- она берет информацию о подсистемах прямо из файлов /etc/rc.d/init.d/SYSTEM -- в строчках вида "# chkconfig:".).

    31.10.2004: да, сделал, по второму варианту -- cstart ищет в конфиге строку вида "#GEOMETRY=...", и если находит -- то указывает cx-starter'у ключ "-geometry ...". Если не находит -- то не указывает, и оно запускается как есть.

  • 30.10.2004: кста-а-ати!!! Ведь выбранный ныне вариант указания config-файла -- просто именем в командной строке -- имеет еще один, ранее непредусмотренный плюс:
    Если поместить сам бинарник cx-starter в какое-нибудь стандартное место, то, если вставлять первой строчкой в config-файлы нечто типа
    #!/usr/local/bin/cx-starter
    можно будет уставлять этим config'ам атрибут "x", и пускать их как скрипты.

    Не то, чтобы это реально требовалось, но -- приятно!

  • 01.11.2004: как-то пустовато в log-окошке для cx-starter'а, изготавливаемом cstart'ом...

    01.11.2004: вставил в самое начало main() печать коротенького сообщения -- "CX-starter, CX version M.N".

  • 01.11.2004: вообще -- cx-starter практически готов, за вычетом лишь "указания окна" -- моргания желтеньким поверх него, плюс указание на него стрелочкой.

    25.04.2007: сделано, наконец-то.

  • 01.11.2004: как-то шибко уныло выглядит cx-starter: все монохромно. Стоит сделать кнопки другим цветом.

    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=", в котором бы указывались доп. параметры, добавляемые при запуске CX-клиентов. Например, всякие там "-xrm ...".

    03.11.2004: сделал. Если присутствует "params=", то она добавляет указанное через пробельчик к генерируемой команде запуска.

  • 06.12.2004: позорище -- обнаружил в функции 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: обнаружил, что запущенные cx-starter'ом программы оставляют также "висеть в списке процессов" свои /bin/sh'и.

    06.12.2004: все просто -- /bin/sh корректно висит, пока его child не завершится. Чтобы сего не происходило, надо перед именем программы ставить "exec". Сделал.

  • 13.01.2005: давно назрело -- надо, чтобы cx-starter'ные сообщения в stdout/stderr префиксировались бы датой+временем -- для понимания, когда что происходило. А то прямо безвременье какое-то.

    13.01.2005: сделал -- теперь есть функция curtime(), возвращающая указатель на статический буфер, содержащий текущее время в формате DD/MM/YYYY-HH:MM:SS. Эта метка времени печатается перед каждым сообщением о "RunCommand()", а также при старте самого cx-starter'а.

  • 11.04.2005: вылезла проблема, что cx-starter не позволяет системы с '-' в идентификаторах.

    11.04.2005: Исправил... Вставил проверку, что если это не-первый символ, то может быть и '-'. А в psp это уже и так возможно (хотя -- нам и неактуально :-).

  • 04.07.2005: почему-то cx-starter не обнаруживает уже имеющегося окна ИПП-32. При этом config-строка --
    ipp             chaninfo=linac1:13!-1,linac1:12!105 \
                    title="ИПП-32" comment="Измерения положения пучка"
    
    а в самой программе заголовок указывается как "ИПП-32".

    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 на будущее. Для дуростей типа гусиных -- пригодится.

  • 16.07.2005: итак, чего из реально нужных "красивостей" в cx-starter'е еще не хватает -- wishlist:
    • Фокусировать окно. 15-06-2008: сделано!
    • Моргать поверх него желтеньким и показывать стрелочкой. 25-04-2007: сделано.
    • Уметь РЕАЛЬНО поддерживать "title=".
  • 16.09.2005: вот в разделе про paramstr_parser были всякие рассуждения про символ комментария -- '#', и что оный надо указывать в terminators. А собственно в cx-starter'е это сделать -- забыл!!!

    16.09.2005: исправлено. Самое интересное, что там УЖЕ был #define CFG_COMMENT "#", он присутствует как минимум с STABLE/cx.20040802. Его и вставил в вызов psp_parse().

  • 19.09.2005: пожалуй, потребен help также и в cx-starter'е.

    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 просто тихо предупреждал:

    cx-starter.c: In function `HelpCB':
    cx-starter.c:1530: warning: declaration of `w' shadows a parameter
    
    а вот gcc4 под FC4 уже ругался --
    cx-starter.c:1530: 'w' redeclared as different kind of symbol
    cx-starter.c:1507 previous definition of 'w' was here
    
    (что там ему "different" -- для меня загадка).

    Вообще-то переменная совершенно не требовалась, так что я просто выкинул ее.

  • 05.10.2006: обнаружилась "приятная" фишка: я запустил cstart на linac1, находясь в /export/viper/..., а через несколько дней отрубилось электричество, и моя машина свалилась. Результат -- из-за того, что у cx-starter'а CWD=/export/viper/..., попытки запускать всякие foreign-программы (maha, odgirl), в start= которых не было cd ..., ничего не давали -- все висло.

    А всего-то и надо -- делать бы cd куда-нибудь.

    05.10.2006: делов-то -- вставил в самое начало просто cd (в $HOME).

  • 05.10.2006: логи в окошке "cx-starter logs" -- это хорошо, но, во-первых, хочется, чтобы они и сохранялись, а во-вторых -- удаленно-то в окошко не заглянешь!

    Надо б как-нибудь отправлять stdout+stderr на tee...

    07.10.2006: да, сделал. Оно теперь вместо былого

    ~/pult/bin/cx-starter $GEOM_OPT $GEOMETRY $CONFIG_FILE $PARAMS_FILE
    запускает
    /bin/sh -c \
    "~/pult/bin/cx-starter $GEOM_OPT $GEOMETRY $CONFIG_FILE $PARAMS_FILE 2>&1 | tee -a $LOG_FILE"
    где LOG_FILE=/var/tmp/cx-starter.log.

    Все работает прекрасно -- в лог валятся ВСЕ сообщения, включая ругань сервера при запуске.

    Единственное отличие: теперь даже при закрытии 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'ем... Что делать?

  • 05.10.2006: программы, запускаемые командой "ssh host COMMAND", имеют в качестве stdout'а не терминал, а сокет. В результате stdout оказывается block-buffered вместо надлежащего line-buffered, и сообщения, к примеру, у Гу-гу в "cnv" не печатаются когда надо. Пльохо!

    (Вообще-то это проблема скорее 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: в связи со всеми вышеупомянутыми махинациями -- в основном, из-за использования tee -- потребовалось разобраться с использованием консольного вывода в cx-starter'е и упорядочить оный.

    07.10.2006: это свелось к двум вещам:

    1. Добавлению в самое начало main()'а вызова setvbuf(stdout,NULL,_IOLBF,0);.
    2. Почему-то сообщение RunCommand("...") печаталось на stderr -- теперь оно идет на stdout.

    Все работает корректно с tee.

  • ??.12.2006: а интегрально наиболее правильно было бы сделать логгинг и вывод в xterm так:
    • stdout+stderr всех программ отправляются файл;
    • а уж этот файл отправляется на xterm при помощи tail -f.
    т.е., безо всяких там tee.

    Единственная "тонкость" -- следить-то надо (кажется) за ДВУМЯ процессами сразу: и за cx-starter'ом, и за tail'ом.

    02.01.2007: некоторые соображения по теме:

    • Во-первых, можно посмотреть в сторону "coprocesses" в zsh. И, возможно, в обычном /bin/sh (bash) найдутся какие-нибудь аналоги.
    • Во-вторых, в принципе можно легонько сжульничать, при помощи хитрой команды
      CX-STARTER .... | tail -f /var/tmp/cx-starter.log
      где "CX-STARTER" -- это такой хитрый запуск cx-starter'а, который бы все-таки отправлял stdout+stderr в нужный файл. (Хитрость в том, что запускается как бы pipeline, так что shell следит за ДВУМЯ процессами, но второй реально читает не свой stdin, а указанный файл. С первым процессом вообще-то аналогичное жульничество.)

      Проверялось на zsh'е -- работает.

    • А в-третьих -- а зачем, собственно, следить за ДВУМЯ процессами? Фиг уж с ним, пусть будет как раньше: терминал следит только за cx-starter'ом, и как он исчезает -- то и close.

      Хотя, обеспечить пришибание tail'а все-таки надо...

    08.06.2007: угу, а еще при использовании tail'а автоматом исчезла бы проблема с access-time и tmpwatch'ем...

  • 30.12.2006: в qult/ появилась потребность добавлять blklist'ы для другой машины, кроме linac1 -- ring1. И тут-то стало очевидно, что я почти повторил токаревский ляп -- в именах blklist'ов имя хоста-то отсутствует!

    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)
    -- глючило просто жутко!
  • 21.01.2007: для избавления от привязки к некоей зашитой структуре директорий -- которая сейчас помечена в исходнике флажками "DIRSTRUCT" -- можно ввести в config-файл специальные .-директивы, которые бы меняли эти пути. (Хотя там, в принципе, и сейчас-то уже поддерживается переменная $CX_ROOT -- сделать в конфиге директиву ".setenv VARNAME VALUE"?)
  • 30.01.2007: давным-давно надо было избавляться от отокаревского наследия -- директорий pult/conf/, населенных ныне только симлинками на ../configs/.

    30.01.2007: да, оставалась ссылка в одной-единственной строчке в ChangeServerStatus() в двух экземплярах -- исправил на configs/.

    Теперь можно на пульту с чистой совестью изводить старые директории с симлинками, заодно переходя на blklist-HOST-N.lst.

    02.02.2007: да, на пульту также перешел.

  • 31.01.2007: из-за того, что на кнопках пишутся просто ключевые слова -- имена/названия подсистем -- невозможно иметь кнопки с русскими названиями.

    31.01.2007: понадобилось это для Гусева -- он попросил на сварке поставить cx-starter, т.к. там уже четыре программы, и запускать их каждый день вручную стало утомительно.

    Проблему решил очень просто -- введен еще один параметр, label=, в котором можно указать произвольную строку. При неуказанности используется, как и раньше, имя подсистемы.

  • 02.04.2007: захотелось, чтобы у CX-starter'а была своя пиктограмма.

    02.04.2007: сделал. Логика: "starter" -- это запуск, т.е. -- должна быть стрелка вправо-вверх. Очень похожая вещь -- это символ Марса (копье-за-щитом), он же символ железа (в смысле элемента), и, как следствие последнего, символ Volvo.

    В нашем варианте -- это круг (как бы пустой внутри, а не как щит) со стрелкой вправо-вверх справа-сверху. Цвет -- как у Xh'ных окон, светло-серый, с 3D-эффектом. Поскольку по пиктограмме будут щелкать мышью, то, чтобы серединка не была пустой и некликабельной, она заполнена светло-голубым цветом (steelblue вытянутый до максимальной яркости) и в ней нарисован как бы стрелочный прибор -- шкала со стрелкой, указывающей вправо-вверх.

    Сам файл -- include/pixmaps/cx-starter.xpm.

  • 21.04.2007: надо уметь указывать -- в srvparams -- имя юзера, под которого надо делать ssh. Нужно -- поскольку на tino всё уже под oper'ом, а на пульту -- все еще oduser.

    В принципе, конечно, можно указывать это через параметр start=, но это криво.

    (А для клиентов, поскольку нестандартные/ssh-клиенты все равно указываются через start=, то изначально можно использовать синтаксис "@USER@HOST:COMMAND".)

    05.06.2007: да, ввел параметр srvparams_t.user (maxlen=32-1). И, если at_host!="", то команда запуска/останова сервера принимает вид именно "@<USER@>HOST:...".

    10.07.2007: проверил -- работает. "done".

  • 23.04.2007: приступаем к добавлению фич "моргать поверх окна клиента желтеньким" и "указываеть на него стрелочкой".

    25.04.2007: за эти три дня сделал (в основном в один день -- вчера, но из-за мелкого ляпа доделывал сегодня). Highlights:

    • Общее:
      • Собственно мерцание реализовано обычным Xt-таймаутом -- процедура вызывается каждые 1/2 секунды и делает manage/unmanage.

        Была идея на время мерцания входить в собственный event loop, который бы не брал бы события клавы и мыши (чтобы юзер не мог нажать еще что-то), благо, на опыте XhProcessPendingXEvents() мы это умеем делать. Но потом решил забить -- во-первых, излишние сложности, а во-вторых, не факт, что корректно бы работало.

      • При нажатии на другую кнопку во время мерцания оно корректно "перенацеливает" все на нового клиента, имеющийся же таймаут грохает.
    • Моргание поверх окна:
      • Окошко, моргающее поверх клиентского -- оказалось достаточно одного she--виджета, создаваемого через XtVaCreatePopupShell(,overrideShellWidgetClass,...) с указанием нужного background'а.

        При наведении на клиента этому окошку уставляются координаты и размер клиентского (которое является именно рабочей областью окна, без декораций). Для пересчета координат в "глобальные" используется XTranslateCoordinates().

      • Собственно фон взят не желтый, а XH_COLOR_JUST_AMBER.
    • Указание стрелкой:
      • Делается виджет drawingArea, которому manage/unmanage одновременно с прямоугольником поверх клиента.
      • Виджет этот приаттачивается всеми сторонами к workSpace, так что имеет размер окна. Что для меня загадка -- ПОЧЕМУ он виден, ведь создается-то ПОСЛЕ сетки, и должен бы быть ПОД ней...
      • Чтобы стрелка рисовалась как бы просто сверху всяких кнопочек -- сделано backgroundPixmap:=None. Так что сервер нифига не затирает, а мы -- рисуем только стрелку.
      • Стрелка рисуется голубым цветом, при помощи XFillPolygon(). Вместо взятого в IncDecB.c режима Convex пришлось поставить Nonconvex, ибо нифига она не округлая, и рисовалось похабно (искаженно, и если часть окна была obscured, то не рисовалось вовсе).
      • Вместо выбора одного из 8 направлений и потом соответствующего набора точек -- сделан ОДИН массив, в координатной сетке (0..100,0..100), координаты из которого масштабируются до текущего размера окна (минимум из wid,hei) и затем поворачиваются на нужный угол.

        Угол считается через atan2() от разницы координат центра клиентского окна и нашего, а поворот делается синусами и косинусами.

  • 04.06.2007: местами подсистем бывает уже довольно много, и хочется уметь "группировать" их. Для этого хорошо подошел бы горизонтальный сепаратор -- пусть (из-за отсутствия COLSPAN у XmSepGridLayout'а) только между кнопками.

    Указывать такой сепаратор можно было бы символом '-' в config-файле или в командной строке. (А на будущее, если захотим не просто сепаратор, а метку -- можно будет использовать синтаксис "-МЕТКА" или "-:МЕТКА".)

    05.06.2007: сделал. Основные мозги/изменения пришлись на AddSubsystem():

    1. Дубликатность систем, начинающихся с '-', больше не проверяется.
    2. Все такие системы безусловно маппируются на sepr_config (.kind=SUBSYS_SEPR).
    3. Вместо нормальной строчки с кнопкой создается лишь сепаратор в 0-й колонке.
    Плюс, в ReadConfigFile() имя подсистемы теперь может и НАЧИНАТЬСЯ с '-'.

    Сами сепараторы делаются простой черной линией (XmSINGLE_LINE).

    Жаль, конечно, что они лишь между кнопками, но уж как есть.

    26.05.2013: дополнение: если указывается не просто "-", а двойной -- "--", то вместо одиночной линии (SINGLE_LINE) используется двойная (DOUBLE_LINE).

  • 15.06.2008: сделал фокусирование окна клиента при "вытаскивании найденного уже запущенного окна наверх".

    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: надо бы уметь указывать srvparams сразу ГРУППЕ серверов.

    15.11.2008: причина -- при установке системы на "grin" с 30"-монитором (NEC3090) там был сделан юзер oper, вместо старого oduser. И понадобилось засунуть в менюшку cx-starter-grin.conf ВСЕХ клиентов с linac1 и linac2; а для запуска серверов-то оно должно ходить на linac1, причем -- именно под oduser'ом! И пришлось для каждого сервера писать ".srvparams linac1:NN user=oduser" -- мутотень!

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

    • Простейший вариант -- указывать "srvparams * ...", и тогда оно будет эти параметры использовать по умолчанию для всех серверов.
    • А лучше -- "srvparams BASE*". В таком случае, если в имени сервера имеется '*', то оно будет сравнивать, совпадает ли начало проверяемого сервера с BASE, и если да -- то использует это умолчание. Этот вариант покрывает все -- в т.ч. и глобальное "srvparams *".

      В любом случае, алгоритм использования srv_info[] с нынешнего простого "ищем, есть ли строчка с таким .name" надо будет поменять на "прочесывание". А именно: идем по всему списку, и смотрим -- если текущая строчка "интересует" нас (т.е. -- либо полностью искомое имя, либо подходит по шаблону), то копируем из нее то, что не-NULL.

    24.11.2008: вроде сделал, теперь надо проверять.

    03.12.2008: да, проверил -- работает.

    07.07.2009: и как я тогда проверял, КАК оно могло работать?! Там стояла проверка с помощью cx_strmemcasecmp() вместо нужной cx_memcasecmp() -- и естественно оно всегда отдавало FALSE, поскольку длины не совпадали!

    Исправил -- теперь вроде пашет.

  • 25.11.2008: кстати, а не сделать ли, все ж таки, чтобы cx-starter и КЛЮЧИКИ понимал из командной строки? А то сейчас он все "слова-без-'/'", что остаются в 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.

  • 14.02.2009: обнаружил, что в programs/runner/Makefile еще с начальных времен (минимум с 23-11-2004) была конструкция
    cstart:		cstart.sh
    		cp -p $< $@  &&  chmod +x $@
    
    -- при том, что никакого cstart.sh никогда не существовало! Видимо, хотел сделать, да забыл.

    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}.

  • 06.04.2009: Малютин обнаружил дурацкое поведение -- если быстро несколько раз нажать на кнопку запуска программы (конкретно это был гусиный syn на linac1), то она запустится именно в нескольких экземплярах.

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

    Первая мысль -- тупая -- что надо после запуска делать паузу на секунду-другую.

    Более разумная мысль -- запоминать момент последнего запуска, и НЕ запускать раньше чем через, например, 5 секунд. Это автоматом исправит и потенциальную проблему дребезжащих кнопок мыши.

    06.04.2009: да, сделал -- добавил поле subsys_t.last_start_time, заполняемое именно в момент исполнения RunCommand(), и вставил проверку, что прошло не менее 5 секунд.

  • 27.08.2009: а еще ведь может возникнуть потребность указывать параметры и ssh'у! И чё, и как?

    27.08.2009: по идее -- надо делать аналогично .srvparams: например,

    .sshparams ШАБЛОН ПАРАМЕТРЫ
    где обработка ШАБЛОНа полностью аналогична оной у srvparams.
  • 10.05.2010: надо по просьбам трудящихся вэппдвухтысячнящихся (читай -- Роговского) добавить config-флажок/параметр, который бы заставлял мониторировать лишь РАБОТОСПОСОБНОСТЬ канала, но НЕ ЗНАЧЕНИЕ. Т.е., чтобы отображались лишь флаги, касающиеся "состояния" канала, а диапазоны и alarm'ы -- нет.

    10.05.2010: очевидное решение -- в получаемых от CdrProcessGrouplist() флагах оставлять лишь серверовы CXRF_SERVER_MASK да CDR_FLAG_DEFUNCT (ну и еще, скорее на будущее -- CDA_FLAG_NOTFOUND).

    А флажок/параметр, видимо, назовём ignorevals.

    Сделано -- надо подождать результатов проверки.

    03.06.2010: проверено, работает -- "done".

  • 03.06.2010: Роговский поинтересовался -- а нельзя ли сделать так, чтобы ЗАПУЩЕННЫЙ cx-starter мог пере-считывать свою конфигурацию, БЕЗ перезапуска?
  • 03.06.2010: еще просьба от Роговского, по расширению возможностей chaninfo=: чтоб можно было проще указывать несколько каналов одного сервера -- указывая один раз сервер, а потом список каналов, или диапазон.

    03.06.2010: в принципе, можно сделать так: пусть ПОЛНЫЕ спецификации разделяются ';' (вместо нынешней ','), а через ',' можно будет указывать дополнительные каналы для того же сервера. Для диапазонов напрашивается '-', надо только делать это аккуратно, чтоб не было синтаксического конфликта с "-1" (хотя, похоже, просто автоматически всё выйдет нормально).

  • 31.07.2010: обнаружилось, что cx-starter совершенно неприспособлен для "переносимых совсем-локальностей" -- т.е., чтобы сервера имели имена вида ":N" или "localhost:N".

    Первый вариант он вообще игнорирует -- даже старт делать не пытается, а второй будет рассматривать как просто-какой-то-хост.

    (Вылезло при настройке локальности на ноуте l6-dovzhenko-nt, чтоб Довженко мог оное использовать как возимый стенд, и при этом там всё -- копия bike.)

    31.07.2010: фиксим.

    1. "игнорировал" он оттого, что в самом начале SplitSrvspec() имелась проверка на !isalnum(*spec). Дополнил её исключением на ':'.
    2. После этого открылась целая банка с червями -- внутри функции не было перед memcpy() проверки на тему len==0. Добавил её.
    3. После тех двух шагов оно начало понимать локальные случаи -- причём, серверу передаётся имя вида blklist-N.lst.

      А всё потому, что в ChangeServerStatus() с незапамятных времён была поддержка этого случая, просто она никогда не использовалась в силу вышеозвученных обстоятельств.

      Следствие -- для таких "переносимых локальностей" можно делать симлинки типа blklist-46.lst->blklist-bike-46.lst.

      Что интересно -- в IsThisHost() никакие исправления вносить не понадобилось, хотя она и возвращает 0 для hostname[0]=='\0': просто и так имя было "", и именно этот факт является в дальнейшем коде признаком локальности.

    4. Также теперь считается локальным "localhost".

      Но вообще-то с IsThisHost() стоит поразбираться попристальнее -- какой-то там странноватый код имеется:

      1. Похоже, НЕ понимает хостов без доменной части.
      2. Имеется некий мутный флажок is_fqdn, который присваивается (весьма странным образом!), но потом не используется.

    02.08.2010: проверено -- то указание ":46" на l6-dovzhenko-nt работает, "done".

  • 05.08.2010: надо бы кроме сообщений о запуске команд также "протоколировать" и вызовы XKillClient() -- чтобы по аналогии с убитием серверов можно было видеть в логах и убитие программ.

    05.08.2010: сделал.

    Правда, вылезла мелкая неприятность: печатать ID окна -- та еще радость: непонятно какой %-формат выбрать, ибо даже на %lx и конверсию (unsigned long)win gcc ругается (хотя в /usr/include/X11/... есть определения "typedef unsigned long XID" и "typedef XID Window").

  • 05.08.2010: кстати, непорядок, что вся "отладочная" печать сделана вразнобой -- надо бы унифицировать, и, кстати, надо печатать и имя программы (хотя бы просто "cx-starter") -- поскольку в cx-starter.log попадает вывод и других программ.

    05.08.2010: делаем -- просто скопировав cxlib'овскую reporterror(), с переименованием её в reportinfo() и заменой stderr'а на stdout.

  • 21.09.2010: на ЛИУ перестало хватать 50 подсистем (а они там включают кнопки типа [shut-l5s0]). Так что сделал MAXSYSTEMS=100.
  • 06.02.2011@Снежинск, в "Соболе" утром по пути в каземат: время от времени требуется перепустить cx-starter с обновлённым конфигом. И при монтаже/отладке машины такая потребность бывает часто.

    Сейчас делается просто -- программа грохается и запускается заново. А было б очень удобно что-нибудь типа FVWM/AnotherLevel'овского "Restart"...

    06.02.2011@Снежинск, в "Соболе" утром по пути в каземат: даже если оставить в стороне вопрос "а как это делать из пользовательского интерфейса" (ну там -- менюшка, вызываемая по правой кнопке при нажатых Alt+Ctrl), то главная проблема -- а как делать собственно рестарт? В голову приходят такие идеи (в порядке появления):

    1. Делать execv(argv[0], argv). Но:
      1. Где гарантия, что argv[0] содержит путь, реально пригодный для exec()?
      2. От первоначального argv[], благодаря Xt, к этому моменту остались уже рожки да ножки -- только наши параметры, а всё своё/Xlib'овское он выпотрошил.

        Предварительно делать копию argv[]?

      Хотя это был бы единственный 100%-"корректный" способ, позволяющий на ходу обновлять и сам бинарник cx-starter.

    2. Грохнуть всё (подсистемы и содержимое окна), и заново выполнить все первичные действия -- начиная с ParseCommandLine().

      К сожалению, малореально -- cda и Cdr не очень-то поддерживают гроханье...

    3. Жульнический вариант: прямо в самом начале main(), непосредственно перед XhInitApplication(), сделать fork(), и собственно работу пусть выполняет 2-й, child-процесс, а 1-й пусть висит и ничего не делает. По команде restart 2-й передаёт её 1-му (сам затем завершаясь), и всё повторяется заново.

      Для общения между процессами можно использовать pipe(): EOF в читающем конце -- завершение, любой другой байт -- рестарт. Саму pipe-пару достаточно создать один раз еще ДО первого fork()'а.

      (Можно было бы держать в резерве 2-й процесс, и так потихоньку передавать эстафету, но тогда нарушится семантика в отношении wait()'а -- когда 1-й процесс после первого рестарта завершится.)

    В общем, не срочно, но если припрёт -- можно будет легко сделать.

  • 06.02.2011@Снежинск-каземат-11: почему cx-starter не видит запущенные chlface-утилиты kshd485,curvv и запускает новые экземпляры?

    27.09.2012: разобрался -- из-за принятого 30-08-2004 принципа, что при указанности chaninfo=... "чтобы OpenDescription() вообще не делалось". Раз description отсутствует, то и ихние app_name'ы из-за этого неизвестны, и cx-starter их окон просто не видит.

    Соображения:

    • ТОГДА решение принималось ради не-chlclient-подобных клиентов (у которых и не было _db.so-описаний); сейчас же ситуация иная -- тренд в том, чтобы ВСЁ делать связками описание+спецклиент.
    • Как бы то ни было, решение обозначенной проблемы -- при указании chaninfo= также обязательно указывать и app_name=.
    • С другой стороны, если уж стандартные клиенты так используются, то им надобно указывать и каждому свой ОТДЕЛЬНЫЙ app_name, чтоб при использовании нескольких штук они бы различались.
    • Но -- это возможно для fastadc-клиентов, а для Chl_app-клиентов такая возможность отсутствует, поскольку там синтаксис ИМЯ=ЗНАЧЕНИЕ зарезервировано для указания РУЧКА=ЗНАЧЕНИЕ_ДЛЯ_ЗАПИСИ.

    Сейчас сделано следующее:

    1. Исправлена халтура, имевшаяся в lib/fastadc/fastadc_main.c::FastadcMain(), что там по умолчанию вообще отсутствовали app_name/app_class. Теперь в качестве оных ставятся указанные в def_app_name и def_app_class -- они прописываются прямо в text2fastadc_main_opts[]. И в делаемом yzframe_main.c тоже.
    2. Проанализированы все НЫНЕШНИЕ cx-starter-*.conf -- сейчас такая проблема практически нигде не стоит.

    А насчёт возможности указания 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) утверждает, что должно)...

  • 05.09.2012@Снежинск, в "Соболе"-805 по дорожке из 11-го каземата на КП (последний день, уже уезжали): а ведь удобно было б, чтобы все сервера хоста запускались сразу при загрузке системы -- тем более, что уже и /etc/init.d/cx-servers есть, он мог бы делать "su -c '~oper/.../что-то'".

    11.09.2012: более детальное рассмотрение:

    • Работу делать действительно должен бы какой-нибудь скриптец в ~oper/pult/, а cx-servers -- только запускать его.
    • Список серверов этим скриптцом чтоб брался из какого-нибудь файла, типа cx-servers-HOSTNAME.conf -- числа, разделённые пробелами (для "for N in `cat ...`...").
    • А вот собственно процесс запуска -- уже совсем не так тривиален, поскольку сокровенные детали о процессе запуска содержатся сейчас в cx-starter'е. В частности, умение читать .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: подготавливаем простейший вариант:

    1. В src/doc/etc_init.d_cx-servers запуск ~oper/pult/bin/start-all-servers (при его наличии) добавлен.
    2. Скрипт programs/runner/start-all-servers сделан -- ровно как нарисован выше.
    3. Собственно run-cx-server сделан в тупом варианте -- безо всякого чтения config-файлов, просто зашито -b200000.
  • 03.04.2013: хорошо б cx-starter умел при своём запуске стартовать указанные в командной строке подсистемы.

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

    И что -- сначала делать паузу 5 секунд, в течение которой ничего не позволять? ...неа, лучше ПЕРЕД КАЖДОЙ подсистемой делать паузу 5 секунд (поскольку разные подсистемы могут использовать один сервер, и если тупо стартовать все подряд -- то он попробуется запуститься несколько раз).

  • 24.05.2013: в связи с диким количеством подсистем на linac3 высота окошка cx-starter'а стала превышать высоту одного экрана (1200 пикселов). В связи с этим встала задача -- как бы этак мочь вместо единой высокой колонки группировать хитрее?

    А вот КАК?

    • Гусини предлагает пускать несколько starter'ов.
    • В KEK'е, помнится (17-03-2005), был древовидный "KEKB Task Launcher".
    • Многоколоночность?

    26.05.2013: да, МНОГОКОЛОНОЧНОСТЬ. Вопрос "как?", варианты: 1) указывать число строк (или брать от высоты экрана/окна?); 2) в конфиге указывать "переносы". Выбираем второй вариант; указанием "переноса" будет хитрый вариант сеператора -- 3 дефиса (т.е., 1 -- сепаратор, 2 -- двойной, 3 -- перенос).

    Приступаем...

    27.05.2013: доделано, на пульт поставлено.

    Декорирование и уставление размеров сеткам вынесено в отдельные функции InitCol()/FiniCol(). Было еще желание сделать, чтоб при отсутствии индикаторов состояния/серверов во 2-й/3-й колонках сами бы эти колонки убирались (для гусиностей), но там не так тривиально, так что пока забито.

    27.05.2013: а вообще явно надо программу переписать "from scratch" -- ибо концепции сильно поменялись.

    1. Реально никому нафиг не нужно ни "читать директорию", ни указывать список подсистем в командной строке. Так что -- надо радикально упрощать общее устройство.
    2. В менеджменте растущих массивов переходить на SLOTARRAY.
    3. Парсер использовать ppf4td.
    4. Собственно создание "строчки" (подсистемы) надо сделать более модуляризовабельным: чтоб оно создавало 3 объекта -- кнопку и пару сеток статуса, а уж куда это всё деть -- пусть думает "вызывальщик". Тогда и древовидность реализовать станет несложно.
    5. (03.06.2013: и сделать возможность пере-считывать конфиг прям из самого starter'а. Лучше всего по технологии "объект-конфигурация", чтоб считывание отдельно, а активация отдельно (как в v4'шном libcxsd).
  • 31.05.2013: возникла надобность запускать НЕСКОЛЬКО cx-starter'ов -- чтоб у подсистемы BPM'ов накопителя был бы свой starter (там 16 кнопок).

    А для этого надо уставлять ДРУГИЕ app_name/app_class. И опять вылазит проблема курицы/яйца...

    01.06.2013: можно сделать с симлинками -- чтоб "разные" экземпляры имели разные имена, а app_name/app_class уставлять в basename(argv[0]).

    Так и сделано, причём app_class[0] делается toupper().

  • 25.09.2013: в связи с тотальным переходом на однопроцессный сервер теперь по умолчанию запускается ./sbin/csxd (вместо ./sbin/cxd).
  • 29.10.2015: а нет ли утечки памяти в cx-starter'е? Почему-то на linac3/worker1 (SL-6.3/x86_64) он жрёт аж по 228М -- куда столько? Или это дело в библиотеках?

    29.10.2015: да, похоже на библиотеки: свежезапущенный процесс сначала брал (VIRT/RES) 226m/13m, а через минуту стало те же 228m/14m. Так что считаем проблему отсутствующей, а раздел "withdrawn".

chlclient:
  • 26.01.2004: chlclient.

    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 готов.

Документация:
  • 02.08.2004: возникла потребность изготовить хоть сколько-то пристойное описание для Knobs_simple, так что сел писать Knobs.html. За компанию, чтобы не было битых ссылок, изготовил {Cdr,useful,Xh}.html, пока только "скелеты".

    А потом, поразмыслив, вытащил их все в поддиректорию doc/lib/. Кстати, пожалуй, имеет смысл аналогичным образом "дублировать" в doc/ и остальное дерево директорий.

Система сборки:
  • 03.10.2003: запрос от Олега: ему потребно, чтобы "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" там отсутствовало. Поправлено.

  • 03.10.2003: и еще пожелание от Олега: чтобы точку установки при "make install" можно было указывать при помощи "PREFIX=...", а не "INSTALLROOTDIR=...".

    10.10.2003: вставил простенькую проверку -- типа

    ifneq "$(PREFIX)" ""; INSTALLROOTDIR=$(PREFIX); endif
  • 03.12.2003: воспользоваться преимуществами gcc-3:
    • Проверить, можно ли отключить давно надоевший "warning: missing initializer (near initialization for `some-struct.field')".
    • Пройтись по всем warning'ам, в т.ч. с 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".

  • 03.12.2003: "man 2 wait"::NOTES

    30.11.2012: ну посмотрел -- и что там такого? :-) Ничего особо полезного (для портабельности всегда надо корректно делать wait*(), да и всё). Так что -- "withdrawn".

  • 21.01.2004: заметил, что почему-то валялись файлы lib/Knobs/*.dep, которые обычно вообще-то удаляются.

    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: поскольку уже есть cm5307-ppc, в который можно грузить CX-сервер, и грядет CAN-ppc (в котором БЕЗ этого вряд ли обойдешься), то надо думать о возможности компилировать CX-сервер, cxlib, cda и utils/ "под другую платформу".

    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>.

  • 09.07.2004: кста-а-ати... Обнаружилось, что в Rules.mk CPPFLAGS=$(DEFINES) -I$(SRCDIR)/include, и все! Нету ни "LOCALDEFINES", ни "LOCALINCLUDES" -- в результате пользователям Rules.mk приходится всячески выпендриваться, например, делая ПОСЛЕ include оного всякие "CPPFLAGS+=...".

    Лучше бы для гибкости ввести такие "вакантные места".

    06.08.2006: это все сделано в 4cx/src/GeneralRules.mk, так что тут -- "obsolete".

  • 11.10.2004: попробовать прогнать все дерево CX через gcc-3.4.2 и gcc-4.0. Посмотреть на результаты, возможно, подправить что-нибудь, адаптировать к ним систему сборки.

    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. Что обнаружено:

    • Большое количество ругательств "warning: use of cast expressions as lvalues is deprecated" на конструкции вида "((char*)(src))+=bytesize". Причина оных -- "src" является указателем на какую-нибудь структуру (либо void*). Таковые места -- n_{read,write}, три места в cxlib.c.

      В принципе, это меняется на конструкцию вида "*((char**)(&src))+=bytesize". А стоит просто такой макрос сделать -- INC_PTR_BY_BYTES(p,bytes).

    • Почему-то раньше не обнаруживалось "control reaches end of non-void function" в cxscheduler.c::sl_enqueue_timeout_after(), хотя там был забыт return -- пофиксено.
    • Сообщение "label at end of compound statement" теперь стало ошибкой, а не warning'ом. Жертвой пали seqexecauto.c, paramstr_parser.c, Xh_toolbar.c, Knobs_internals.c, Cdr.c, tsycamlib.c, cxd_manager.c, cxdpartlib.c, cx-server_dbase.c, cm5307_drv.c, descraccess.c -- пофиксены.
    • При сборке командой вида "make GCC=..." получался облом в {uclinux,ppc}_drvlets, где уставлялся свой, кросс-компилятор. Пришлось добавить словцо "override".

    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: всё-таки надо избавляться от этого приёмчика -- инкрементирование типизированного указателя на сколько-то БАЙТ -- полностью.

    • Проблема вылезла на gcc-4.4.6@SL-6.3/x86_64. Там происходили феерические заскоки -- и сервер SIGSEGV'ился, и cxlib разнообразно ругался. Как выяснилось -- всё из-за этого хака: в некоторых местах указатель не сдвигался вовсе.
    • Гугление натолкнуло, среди прочих комментариев, на страницу GCC Bugs, где есть раздел "Casting does not work as expected when optimization is turned on". Резюме: при оптимизации -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) удалено.
  • 10.01.2005: давно пора бы пройтись по всем warning'ам на тему "incompatible pointer type" -- они потенциально весьма опасны. Во всяких Chl & Co. надо произвести сепарацию при помощи ABSTRZE()+CNCRTZE().
  • 20.01.2005: заметил, что почти НИГДЕ в LocalRules.mk "пользователям" нет возможности указать свои, локальные библиотеки -- только в драйвлетах (там оно было введено для Чугуева).

    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/drv_i/. Туда будут складываться "файлы описания" драйверов.

    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.

    Осталось только следующее:

    • Во-первых, как-то надо бы включить директорию include/drv_i/ в процесс сборки.
    • Во-вторых -- сделать и директорию exports/include/drv_i/.
    • В-третьих -- как-то надо б это обвязать и во внедеревных проектах -- типа тех же istc и pult, чтобы ВЕЗДЕ были одинаковые ссылки -- на "drv_i/NAME_drv_i.h.

    14.02.2006: мда, уже почти год прошел :-) Включаем include/drv_i/ в процесс сборки/экспорта:

    • Добавлена в src/EXPORTSTREE.
    • Добавлена в src/include/Makefile::SUBDIRS.
    • Создан простейший Makefile.

    16.02.2006: "приличия для" -- чтобы при отсутствии файла make обламывался прямо в drv_i/, еще на стадии "all", добавлена строчка:

    files: $(DRV_INTERFACES)

    06.08.2006: да, а сейчас -- "уже почти полтора года прошло", так что, как давным-давно надо было, "done".

  • 25.04.2005: потребно обобщить использование $(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'ы считаны.

    Сделано -- работает.

  • 04.05.2005: а вообще -- следует завести унифицированную структуру Makefile'ов -- с EXES, LIBS и "составными частями EXES". Чтобы ВЕЗДЕ все указывалось одинаково. Т.е.:
    • Список бинарников ВСЕГДА указывается в $(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"».

  • 04.05.2005: опять же, уже для CXv4: следует вынести драйверы из programs/server/ в совершенно отдельное место. А возможно -- даже и в отдельное дерево (хотя это -- вопрос спорный).

    30.11.2012: да, там это сделано.

  • 14.06.2005: опять же про драйверы: столкнулся (при изготовлении marcankoz_drv) с тем, что там АБСОЛЮТНО не поддерживаются "сборные" драйверы -- из нескольких .c/o-файлов.

    14.06.2005: главная проблема была в том, что зависимости-то для .so'шек указать можно, а вот autodepend оно б не делало.

    Так что добавил в drivers/LocalRules.mk "входной параметр" SUBFLSSOURCES, используемый при организации списка $(ALLDEPENDS). Так что локальная проблема решена.

    Вообще-то это ОБЩАЯ проблема, и нужно какое-то общее решание для таких "автоматизированных" директорий -- типа drivers/, utils/, xmclients/. Сейчас местами поддерживаются LIBSSOURCES, а нужно еще нечто для под-файлов.

    Варианты:

    1. Как сейчас -- просто лишний список $(SUBFLSSOURCES). Просто, но немодульно.
    2. Навороченно-деревянный вариант: ведь в CXv4 мы везде причешем так, что указываться будет список "готовых файлов", безо всяких суффиксов, так?

      Так вот -- можно для таких составных 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".

  • 23.08.2005: собственно -- а почему это у нас почти каждый Makefile/LocalRules.mk начинается с
    .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)
    
    • Суть косяка в том, что такая спецификация ЯВНО предполагает, что Makefile'ом является именно файл с именем Makefile.
    • А тут на днях понадобилось сготовить файл с другим названием -- в work/tests/ для компиляции из test_sc.c правила описываются в test_sc_Makefile.mk, и -- упс! Ругается:
      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.
      
    • Стал разбираться, откуда вообще берутся правила для .mkr-файлов, и тогда увидел эту забитую гвоздями ссылку.
    • Проверка путём "touch Makefile" ситуацию чудесным образом вылечила.
    • Как показало рытьё в "info make", можно список считанных Makefile'ов содержится в $(MAKEFILE_LIST), первым в этом списке и будет "базовый" файл, который был указан в -f.

    Замена

    Makefile
    на
    $(firstword $(MAKEFILE_LIST))
    решила проблему.

    P.S. Предыдущая дата на GeneralRules.mk была 23-07-2014.

  • 05.11.2005: кстати, надо будет в CXv4 сделать Rules.mk так, чтобы этот файл годился как-есть и для других проектов -- т.е., все CX-специфичности (типа определения директорий для инсталляции и $(LIBnnn) -- содержались в какой-нибудь одной секции, легко заменяемой для других проектов.

    Либо -- вообще переобозвать его во что-нибудь типа CommonRules.mk, а уж специфика пусть лежит в небольшом Rules.mk, напрямую включаемом всеми Makefile'ами, и, в свою очередь, включающем уже эти "универсальные" определения.

    06.08.2006: да, именно так, как раз по приведенному здесь проекту там и сделано -- GeneralRules.mk, подходящий для произвольных проектов, включающих его из своих Rules.mk, а уж в оных всякие $(LIBnnn) и определяются.

    Посему -- "done".

  • 05.11.2005: столкнулся с тем, что в паре макросов D,V() возникает ошибка при попытке подсунуть V() параметр типа {a,b} -- ругается, что передано слишком много параметров вместо требуемого одного.

    06.11.2005: собственно -- столкнулся с этим в lebed_{db,data}.h, там же сразу и нашел решение. Попытка "в лоб" -- заключить параметр в дополнительные круглые скобки -- ничего не дала: при сим компилятор ругается "braced-group within expression allowed only inside a function" (черт знает, что он имеет в виду и почему).

    Зато отлично сработал другой вариант -- "macro varargs". Так что -- надо ВЕЗДЕ определять как

    #define V(value...) = value
    (Самое обидное, что в cpp.info вообще НАПРОЧЬ отсутствует комментарий, как можно решать такую проблему -- абсолютно легитимную! Посему я слегка обозлился и написал вопросительное письмо на gcc@gnu.org.)
  • 22.06.2006: провел тестовую компиляцию на FedoraCore5/gcc-4.0.2@x86_64. Результаты:
    1. Процессор x86_64 в Sysdepflags.sh не поддерживается. Он рассматривается как "UNKNOWN".
    2. Часть не собирается -- проблемы с изготовлением некоторых .so-файлов на 64-разрядной машине. Выдает сообщения вида
      /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
    3. Motif-программы не собираются -- там libXm.so есть только в /usr/X11R6/lib64/, а в /usr/X11R6/lib/ -- нету.

    26.06.2006: итак:

    1. Проблема с "x86_64" -- крайне проста. Всего-то навсего надо было внести эту альтернативу. Внес. (Кстати, старая дата на файле была 17-04-2003.)
    2. А вот не-собираемость-некоторых-.so -- чуть хитрее.
      Суть ее заключается в том, что в .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, а в их "загружателей".

    3. Мерзкие они, эти RedHat'овцы! В FC5-то проблема исчезла -- там все в /usr/lib*/, безо всяких X11R6, а в FC4 -- бе... Бракоделы хреновы -- взяли и сломали нормальную работу. Небось оправдываться будут "теперь все должно быть в /usr/lib, и вообще правьте выши Makefile'ы"... Ну вот какого черта было ломать совместимость?! Написан Bugreport#196682 - /usr/X11R6/lib/libXm.so symlink missing on x86_6.

      А пока -- пришлось вводить "мелкий хак": вместо "-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 -- оно ж сейчас линкуется прямо к драйверам, а не в сервер...

  • 26.06.2006: а вообще -- имеем прямо МОРЕ проблем (и, боюсь, это не просто warning'и) из-за того, что размеры int, long int и void* отличаются...

    Да, проверил -- даже cda скопычивается каким-то дюже странным образом...

    27.06.2006: предпринял некоторые "нулевые" действия:

    1. Во-первых, позаменял все форматы, связанные с size_t и с разницей указателей (который ptrdiff_t) --- с "%d" на "%ld".

      Плюс, когда мы пользуемся "%*" то надо параметр "точность/ширина" преобразовывать к int, ибо именно этого ожидает "*".

      И -- вся печать типа time_t ныне пользуется "%lu", а само оно, естественно, преобразуется к (unsigned long).

    2. Имело место море ругани "cast from pointer to integer of different size" и обратно.

      Для решения изготовил пару функций-макросов в misc_macros.h --

      static inline void *lint2ptr(long  v){return (void*)v;}
      static inline long  ptr2lint(void *p){return (long )p;}
      
      ("lint" -- "long int") и порасставлял их в соответствующих местах (это практически всегда либо в callback'е, либо в его уставке).
    3. За компанию -- не фатально, но достали warning'и "cx_proto.h:nnn: warning: division by zero". Чтоб их минимизировать, выкинул совершенно ненужный 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):

    • Поскольку uClinux изведён, да и "%z" пролез как минимум в paramstr_parser.c, то используем "%z" повсеместно.
    • Также надо обращать внимание на "%.*s": "precision" (ширина поля, она же длина строки), должно быть int. Поэтому везде ставим приведение длин к (int).

      ...а вообще надо бы от этого хака избавляться (пользоваться нормальным препроцессором и получать токены в виде NUL-terminated-строк, а не парой указателей-"скобок").

    • Отдельный писк -- то, что accept()'у и getsockopt()'у надо передавать не int*, а socklen_t*.
  • 27.06.2006: кстати, а ведь давненько уже пора завести тип int64 прямо в misc_types.h.

    27.06.2006: BTW, статистика, какие где размеры (по данным программы sizeofs.c):
    Archcharshortintlonglong longvoid*size_tfloatdoublelong double
    x86 124484448
    x86_64 "-m32" 12448444812
    x86_64 "-m64" 12488884816
    SGI MIPS "-mabi=n32"124484448
    SGI MIPS "-mabi=64" 124888848
    alpha 124888848
    07.03.2023 E2K -m32 12448444816
    07.03.2023 E2K -m64 12488884816
    16.03.2023 E2K -m128124881684816
    17.03.2023 aarch64 12488884816

    Итак, выводы насчет интересующих нас платформ:

    1. Размер long long -- всегда 8, т.е., это и есть int64.
    2. Размер size_t всегда равен размеру указателя -- что вполне логично.
    3. И, что самое приятное -- размер long ВСЕГДА равен размеру size_t (и, следовательно, размеру указателя). Так что можно всегда для size_t использовать "%ld".
    4. И еще -- коли размер 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-битного.
    • В IEEE-754 таковой есть -- binary16.
    • А вот в C -- поддерживается только GCC на ARM и AArch64: "Half-Precision Floating Point".
    • 19.06.2021: кстати, а формат bfloat16 (поддерживаемый, в частности, в AVX-512) ОТЛИЧАЕТСЯ от binary16, т.к. является реально обрезанным binary32/"single precision"/float и имеет те же 8 бит экспоненты, но 7 бит мантиссы вместо 23 (т.е., просто младшее uint16-слово обрезано). Так что даже если б и была поддержка "short float" в GCC на x86/x86_64, то проку с того нет -- форматы разные.
    • ...кстати, при гуглении по "short float" в основном выдаётся какая-то хрень насчёт биржевой торговли, а по «"short float" c language» нашлось уже чуток полезного -- в частности, "Is a there short float datatype in C?" на Quora, откуда и была вышеприведённая ссылка по GCC.

    А полез я разбираться с этой темой из-за того, что некий контроллер вакуумного насоса (из Выборга, кажется) работает по 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: погуглил:

    • SystemReady -- не архитектура, а "программа сертификации" для ARM-серверов;
    • Loongson (ex-Godson) -- китайский вариант MIPS64,
    • LoongArch -- его развитие формально вроде уже без MIPS, но в реальности модифицированный MIPS64r4 с примесью практик от RISC-V.
  • 25.10.2006@PCaPAC-2006, WEC1, H. Nishimura, "EPICS SCA Clients on .NET x64": Слушайте, а про 32/64 -- можно ли из 64-bit app сделать 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):

    % 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
    
    (gcc *does* allow to link a mixture of 32- and 64-bit .o files, only issuing a warning, but that mix doesn't work.)
  • 06.03.2007: занадобилось уметь профилировать программы. Это делается утилитой gprof, а сама программа должна компилироваться и линковаться с ключиком -pg.

    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". Оно и понятно :-)

  • 24.11.2007: читаючи книгу Р.Стивенс, С.Раго, обнаружил, что та "лишняя"/"разнящаяся" единица, которая имеется при указании 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 не скажу -- её тогда не существовало.
    
  • 04.05.2008: вылезла какая-то странность при попытке сделать "make maintainer-clean" в work/qult/ на другой машине, не-viper'е (это был titan под CentOS-5.1). Ключик "-k" не помог -- все равно оставались лежать .d-файлы, ссылающиеся на /export/viper/... Что непонятно -- это было только в qult, а вот в liu/ все прошло предсказуемо.

    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.

    1. Само "make maintainer-clean" стало исполняться НА ПОРЯДОК быстрее -- в основном потому, что теперь ему не надо перегенирить .dep/.d-файлы по два раза.
    2. И проблема "make -k maintainer-clean не работает" ПОЧТИ исчезла. "Почти" -- потому, что оно на списках поддиректорий всё-таки обламывается, и приходится прогонять снова и снова.

      Происходит это из-за "set -e" -- что прерывается shell-цикл "for", который вне контроля make. И при каждом следующем запуске оно всё-таки проходит на один цикл дальше.

      Собственно: а ведь реально В ЭТОМ и был корень той проблемы недоочисткой-по-maintainer-clean -- флаг "-k" недо-действовал.

    3. Посему -- вставлено "умничанье", теперь оно при флаге "-k" никакого "set -e" не делает. Эффект достигается через переменную $(SET-E), при наличии "-k" уставляющуюся в пустоту.

      Оно скопировано из

      (make.info.gz)Testing Flags
      только с маленькой доработкой в виде $(firstword...), на всякий случай.

      И -- пришлось ; пхать прямо в переменную, чтобы и оно тоже исчезало, а то этот дурной /bin/sh ругался

      line 0: syntax error near unexpected token `;'

      Теперь проблема уже ПОЛНОСТЬЮ ИСЧЕЗЛА.

      В GeneralRules.mk это дополнение также портировано.

    Итого -- "done".

  • 04.05.2008: кстати, обнаружилась еще одна приятность: chlclient, собранный gcc-2.96 под RH-7.3, прекрасно подхватил .so-файл группировки, собранный gcc-4.1.2 под CentOS-5.1 (запускалось, естественно, на ней же). Так что -- у обычного gcc (НЕ-c++) совместимость просто отличная!
  • 23.05.2008: был некоторый ляп -- хоть все библиотеки при $(OUTOFTREE) и берутся из exports/lib/, а не из самого дерева, но вот include-файлы безусловно брались из дерева, т.к. стояло -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/), кстати, тоже. Сейчас поправил.

  • 24.10.2008: недавно вылезло, что наши include/*.h неюзабельны в C++: в них отсутствуют "скобки"
    #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/.

  • 21.11.2008: как выяснилось, начиная с gcc-3.3 символ __FUNCTION__ перестал быть "магическим", а превратился в обычную переменную, точнее -- в static const char [], так что теперь он ПЕРЕСТАЛ конкатенироваться со строками, и нынче для его добавления в сообщениях нужно использовать "%s".

    Впрочем, и так практически везде он выводился именно через "%s", по историческим причинам -- еще со старых gcc (2.7?), так что сия проблема осталась почти незамеченной. Единственное место было в 4cx/src/KnobsCore/KnobsCore_knobset.c, но и там поправлено.

  • 22.01.2009: созданная еще в 2003-м директория exports/win32src/ так никогда до рабочего состояния доведена и не была, и очевидно, что и не будет -- поскольку в CXv4 будет использоваться другой подход к портабельности и поддержке Win32. Так что эту win32src/ надо грохать.

    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").

  • 22.01.2009: давно пора было устранить cx_macros.h, опустевший еще два с половиной года назад, и превратившийся просто в "адаптер" для misc_macros.h.

    Сделано. К счастью, он никогда в exports/ не клался, т.к. был де-факто приватным файлом для cxlib и сервера, и использовался только ими. Так что везде, где он имелся, просто добавлены misc_macros.h+misclib.h.

  • 26.01.2009: изменения, вызванные включением поддержки OpenBSD.

    26.01.2009: часть "по теме", часть -- просто исторически давно назрело, так что идем просто единым списком:

    • В server/Makefile было забито гвоздями, что собираемся только под Linux. Покамест это просто выкинуто нафиг.
    • Так же в programs/server/ имелась туча давно нафиг не нужных .lst-файлов, еще и экспортировавшихся в configs/ -- они стерты и убраны из Makefile'а.
    • А вот проверка, что в директории кросс-компиляции следует заглядывать только на Linux@{x86|x86_64} -- отсутствовала. Теперь добавлена -- ради чего пришлось создать штуку под названием Config.mk (см. следующий раздел за сегодня).
    • В cxlib.c использовался sighandler_t, который GNU extension. Пришлось сделать свой typedef mysghandler_t.

      А вообще -- надо стараться отходить от применения signal(), в пользу более стандартизованного sigaction(). По крайней мере, в не-одноразовых, а долго-живущих обработчиках -- точно. (Реально-то весь проект "slave SIGIO handler" так никогда и не использовался, и никогда не будет :-))

    • В OpenBSD-4.2 отсутствует макрос NAN! Поэтому пришлось в cda.c и Cdr.c вставить условное определение его как strtod("NAN", NULL). В 4.4 вроде уже поправили.

      02.07.2010: замечание: для наличествования NAN НЕОБХОДИМ ключ -DGNU_SOURCE -- по крайней мере, с gcc-2.96@RedHat-7.3.

    • Там повторяется история Линукса десятилетней давности -- в /usr/include/ нет симлинка X11 на ../X11R6/include/X11. Пришлось добавить в Rules.mk десятилетней+ же давности конструкцию
      ifneq "$(wildcard /usr/include/X11)" "/usr/include/X11"
         CPPFLAGS+=	-I/usr/X11R6/include
      endif
      
      Понять бы еще, как именно это влеплять к CXv4'шному GeneralRules.mk (или в Rules.mk?)...
    • А с Xm еще хуже -- openmotif там ставится в /usr/local/ (вот же 3.14....сы!!!). Пока вставил халтурный вариант с условным добавлением -I/usr/local/include, но вообще-то надо думать о каком-то более пристойном (последовательно-ищущем?) решении, с применением $(wildcard...) и $(firstword...).
    • Но еще мерзее -- с libXm. Оно ж там лежит в /usr/local/lib/, так что надобно указывать еще нечто в дополнение к -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-схему.

    • В Xh_fdlg.c использовался макрос PATH_MAX, а вот взяться ему было неоткуда. Так что добавил туда #include<limits.h>. И в cankoz_pre_lyr.c тоже.
    • Там нет SIGSTKFLT, так что он в cxdlib.c за-#ifdef'лен. И в 4cx/.../cxsd.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/:

    • Все махинации с *sysdep*.
    • Насчет NAN -- в Cdr_treeproc.c.
    • Вот с PATH_MAX проблем не было -- в Cdr_file_ldr.c и findfilein.c уже есть limits.h, а cxsd_*.h включаются из cxsd_includes.h, где limits.h также предварительно имеется.
    • Насчет OPTION_USE_ON_EXIT было пофиксено еще тогда.

    А вот с /usr/include/... и /usr/local/... пока не стал возиться -- это в нынешней архитектуре GeneralRules.mk неочевидно, куда вставлять, так что сделаем позже.

  • 26.01.2009: добавлена новая фича/концепция -- Config.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). При этом оно стало пригодно для использования и в кросс-директориях.

  • 30.01.2009: кстати, а ведь надобность в 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'ы во внедеревных проектах.

    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/.

  • 03.02.2009: имеется странная ситуация: 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.

  • 03.02.2009: пора бы сделать так, чтобы внедеревные проекты могли б собираться независимо от собранности дерева. Т.е., первый и главный шаг к тому, чтобы работал "третий вариант сборки" -- по инсталлированному образу exports/. Это включает:
    • Экспорт всех общих .h-файлов.
    • Экспорт всех "библиотечных" модулей от кросс-компиляции.
    • Экспорт всех *.mk и связанных с ними вещей (типа mkclient.sh). 21.02.2009: на эту тему проект есть в bigfile-0002.html за сегодня.
    • ...а вот о tools/ подумаем позже. 30.11.2012: а уже подумано, еще 23-05-2012 -- теперь вместо cx/tools/ у нас отдельная/внешняя x-compile/.
  • 04.02.2009: была давняя-давняя кривость: иногда, если некие target-файлы были симлинками, то они симлинковались снова и снова при каждом проходе make. Разобрался в причине...

    04.02.2009: постановка проблемы:

    • например, .c-файл симлинковался откуда-то, но в нем были #include каких-то .h-файлов, которые тоже симлинковались. Но -- до того, как оно сгенерит список зависимостей .d, оно не знало, что эти .h-файлы нужны, и не симлинковало их. А чтобы сгенерить зависимость -- они уже были нужны, иначе вылазила ошибка!
    • Таким образом, даже статическое указание в Makefile|*.mk зависимости вида
      somefile.o: includefile.h ...
      
      не помогало, поскольку в момент генерации .d-файла это правило было ни при чем. Так что я вставлял несколько дурацкое правило вида
      somefile.c: includefile.h ...
      
      так что make знал о необходимости сделать .h-симлинк еще ДО какого-либо использования .c-файла.
    • Но это имело неприятное последствие -- если хоть кто-то из "includefile.h ..." был свежее, чем исходник somefile.c (т.е., чем тот файл, не который и указывает симлинк), то симлинкование делалось ПРИ КАЖДОМ ПРОХОДЕ make -- просто по правилам, которые и лежат в основе идеи make.
    • Вроде это и криво и некрасиво, но и на -- вроде бы напрашивающееся -- правило с указанием зависимости для .o-файла это не заменишь, ибо тогда всё ломается.

    А теперь решение:

    Но немного подумав -- точнее, сходив в душ :-) -- нашлось очевидное решение: надо указывать в зависимости не только .o, но и .d!

    1. Вообще-то это (сейчас!) выглядит очевидным -- во-первых, потому, что именно о .d-файлах и речь в той ошибке, а во-вторых, в обычных-то случаях АВТОгенерации зависимостей и записывается пара "file.o file.d: ...".
    2. И вторые пол-вывода, традиционные: нефиг извращаться, пытаться обмануть используемую систему (в данном случае make), и прятать от него информацию -- наоборот, надо с ним сотрудничать, и стараться максимально корректно ему всё объяснить!

    А подумав еще лучше -- очевидно, что надо указывать зависимости ТОЛЬКО для .d, т.к. для .o они определятся позже, уже автоматически, обычным путем.

    Итого -- подправил таким способом:

    • cm5307/common/CommonRules.mk -- в строчке касательно "$(NADC_DRVLETSOURCES...): ...".
    • canbus/gw/ppc_canserver/Makefile -- аналогично в строке "$(CANDRIVERSSOURCES... cankoz_pre_lyr...: ...".
    • А вот в chlclients/LocalRules.mk, где был аналогичный прикол -- стояло "%: %_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: но самое-то "прикольное" --

    • В cm5307/common/CommonRules.mk была еще КУЧА строк вида "somefile.c ...: includefile.h", которые в GNU Make 3.81 стали приводить к уродским результатам -- делались симлинки типа cm5307_alfo.c->cm5307_camac.h (поскольку там что-то нахимичили с порядком/сортировкой зависимостей, так что $< стало уставляться в "не то" -- см. bug #21198: "Wrong order of prerequisites with 3.81/CVS").

      Позаменял там все ".c" на ".d".

      Итого -- непосредственная проблема решена. Но остался вопрос -- что же делать с этим больным на голову make-3.81. Поскольку проблема может вылезти и в будущем, когда у некоего файла будет более одной зависимости, и оно даже не то что симлинк -- а и с обычными правилами может лажануться... (...или не может? Почему никто особо не жалуется?)

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

      $(SYMLINKS):	%: ../common/%
      		ln -sf $< $@
      
      Сейчас же ОДНО общее правило на НЕСКОЛЬКО разных групп файлов-симлинков из РАЗНЫХ директорий, так что теперь static pattern rule сделать нельзя, и приходится пользоваться спецпараметром $<.

      Вывод -- стараться использовать раздельные правила с прилагающимися 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: в качестве резюме по результатам тех разборок "с симлинками":

    07.02.2009:

    1. Постараться избегать использования $<, и применять $* (для чего использовать pattern rules). Это убъет проблему "неправильных симлинков" на корню.
    2. Постулат: у ИСХОДНИКОВ -- .[ch]-файлов, если они симлинкуются, должно быть по ОДНОЙ зависимости. Тогда можно спокойно делать общее на всех правило для симлинковки с использованием $*.

      (Кстати, при единственной зависимости, и являющейся target'ом линка, вообще все замечательно -- пересимлинковка единожды при каждом изменении исходника. Вообще идеальный вариант -- в точности как для обычных пар "цель:зависимость".)

    3. МНОЖЕСТВЕННЫЕ же зависимости надо постараться оставлять только там, где их порядок роли не играет -- например, при линковке исполняемого файла из кучи объектных. (Тут есть свои тонкости -- порядок .a-библиотек ведь ВАЖЕН, но имеется механизм для его гарантии -- способ указания ACT_LIBS.)
    4. Еще мысль немножко в сторону: ведь в будущем понадобится "теневая сборка" -- так вот, поскольку там будут симлинки вообще всего и вся, то там надо будет в каждой поддиректории в каждом Makefile или LocalRules.mk уставлять переменную "THISDIR" -- показывающую, из какой "базовой" директории надлежит симлинковать.

    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: начало поддержки Interix, aka "Windows Services for UNIX".

    22.02.2009: да, введено определение в Sysdepflags.sh (там еще с CPU дрянь -- interix'ный "uname -m" отдает как "x86", что затеняло бы x86_64) и сделан os_interix.h. И в 4cx/ скопировано.

  • 22.02.2009: пора бы избавляться от OPTION_HAS_PERSISTENT_SIGNALS -- во-первых, оно реально никогда ни на какой из поддерживаемых систем не было ==0, а во-вторых, при использовании sigaction() -- хоть через set_signal() -- и не имеет смысла в принципе.

    22.02.2009: да, выкинул отовсюду.

  • 05.03.2009: надо бы ввести такую фичу -- чтоб при сборке на системах, где нет Motif или даже вообще X11, можно б было указать в командной строке make ключик 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: да, перенес.

  • 16.03.2009: еще некоторое время назад попробовал собираться под OpenBSD-3.6, с gcc-2.95.3, оно обломилось (в частности, из-за отсутствия EOVERFLOW). А сейчас разобрался получше -- так вот, в 2.95, оказывается:
    1. НЕТУ нормальной поддержки "macro varargs", которая у нас используется.
    2. НЕ понимается вызов макроса, определенного с 1 параметром, с пустыми скобками (это нужно для макросов, реально выполняющих 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: Роговский обнаружил, что "make install" в поддиректории drivers/ во внедеревье вылетает по ошибке -- он пытается вызывать cp с единственным аргументом -- именем target-директории.

    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), теперь делается и там.

  • 05.04.2009: разобрался-вспомнил, почему в поддиректории include/ НЕ НАДО делать 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'ами, но при желании пользоваться можно.

  • 08.12.2010@Снежинск-каземат-11: НЕЛЬЗЯ использовать конструкции вида
    (const char *[]){"A", "B"}
    для прописывания статических/аллокированных указателей -- поскольку сам anonymous_array[2] аллокируется в стеке, и это место затем reused, так что статический указатель будет указывать на мусор.

    А

    (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 не использовал стек, а аллокировал статически?

  • 02.09.2011: сходу -- удалён Rules.mk.bad_install, болтавшийся аж с 2003-10-29.
  • 12.09.2011: в Rules.mk имелась крупная неприятность в разделе "exports:": в $(if ...) первый параметр -- CONDITIONAL -- был просто $(EXPORTSFILESx), БЕЗ $(strip). В результате после ликвидации (в пятницу) cm5307_drv.c и cangw_drv.c в их директориях DRIVERSOURCES стала пустой, но в какой-то НЕпустой EXPORTSFILES это отражалось.

    12.09.2011: странно, конечно, что это никак не вылазило раньше -- уж пробел-то от слепливания кучи не-определённых переменных должен был пролезть.

    Короче -- $(strip) вставлен во все 3 штуки.

  • 09.12.2011: PATH_MAX -- всё-таки великая сложность.

    09.12.2011:

    • Мы вроде привыкли, что для него нужен limits.h.
    • Но вот для MOXA (gcc-3.3.2, хбз что за базовая система) оно почему-то живёт в sys/param.h. Каковой и пришлось применять в findfilein.c.
    • Но для этого файла требуется linux/param.h, коего не было на cm5307-ppc. Пришлось его выцарапывать из SDK.
    • ...но он -- реально пустой, ему потребовался asm/param.h, каковой также взят из SDK.
    • Ну и, кроме cx/tools/cm5307-ppc/, также скопировал их в x-compile/ppc860-linux/.

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

    31.10.2012: и в 4.4.6@SL-6.3 тоже понадобилось использовать sys/param.h. Надобность вылезла на старом impacis10_drv.c.

  • 19.01.2012: переводим всё на 4cx'овскую систему с GeneralRules.mk.

    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: собственная директория cx/tools/ упразднена в пользу x-compile/.

    23.05.2012: все *.mk-файлы кросс-сборки переделаны, плюс убраны определения TOOLSDIR из обоих TopRules.mk.

    Из "неприятного" -- в x-compile/ppc860-linux/include/ пришлось завести свой экземпляр директории camac/ с файлом camac.h.

    • Раньше (еще с tools/) там был симлинк include/camac->../../cm5307/include/camac (видать, при запинывании кросс-сборки под CM5307/PPC так выкрутился).
    • В будущем, чтоб не было лишнего в общей кросс-среде, надо будет оное вытащить в v2hw/kernel/cm5307-ppc/ (зачатки там есть).

      И, кстати, у bivme2 и cangw аналогично.

    09.10.2012: а сейчас cx/Ztools/ (бывшая tools/) уже физически удалена.

  • 19.03.2014: добавляем поддержку MacOS X под именем "darwin".

    19.03.2014: конкретная потребность -- чтоб студент по имени Илья мог на своём MBP собрать.

    Действия:

    • Сделан include/sysdeps/os_darwin.h (пока тупым копированием из os_openbsd.h).
    • В include/cx_sysdeps.h добавлено его включение.
    • scripts/Sysdepflags.sh распознаёт по ключевому слову "darwin".

    !!!Ne zabyt' by v 4cx/ skopirovat'!!! 21.02.2023: скопировал, ещё тогда, 19-03-2014.

    21.02.2023: дальнейшие записи, если работа продолжится, будут вестись уже в bigfile-0002.html в созданном сегодня разделе.

Система директорий:
  • 20.01.2004: похоже, в ${HOME}/cx/ в инсталлированном варианте надо будет иметь "для текущей работы" пару директорий:
    modes/*/
    то место, куда делается chdir() перед запуском клиента; тем самым туда попадают сохраняемые режимы и логи (mode_APPNAME_*.dat и log_APPNAME.dat).
    settings/*/
    здесь хранятся считываемые программами настройки -- типа таблиц для интерполяции.

    08.11.2004: ой хрю-хрю! Сейчас-то сделана ОДНА директория -- только settings/. В ней и таблицы интерполяции, и режимы туда пишутся, и log'и, и event-log'и.

    Плюс -- базовой директорией служит ${HOME}/pult/.

  • 30.11.2004: получается, что внутри многих программ есть привязки к тому, как устроена система директорий -- cx-starter, descraccess, cm5307_drv, читалка таблиц... Это не есть гуд -- подобные вещи должны бы быть настраивабельными. Так что, пока что -- надо все подобные места пометить комментариями-тэгами /*!!!DIRSTRUCT*/.

    30.11.2004: вроде пометил.

    21.01.2007: да, по крайней мере cx-starter точно должно быть несложно настраивать .-директивами в config-файле.

  • 24.01.2013: в EXPORTSTREE добавлены settings и settings/common (понадобились для калибровочных таблиц chlclients'ов -- чтоб прямо в Makefile'ах указывать).
Общие идеи:
  • 16.09.2003@Bled: cxsd: возможность указать не только ":N", но и "address:N".

    Это чтоб или ограничивать "видимость" сервера частью интерфейсов, или вешать на разные интерфейсы разные сервера (но очевидно, что N, тем не менее, должен быть уникальным среди всех интерфейсов -- из-за /tmp/cx-N-socket).

  • 16.09.2003@Bled: какие именно могут быть проблемы оттого, что cx-porter посылает ответ сразу после accept()'а? Ведь это TCP, а не UDP...

    (Вопрос навеян рассказом Antonio Lioy о том, как форточный RPC-сервер отвечал на бредовые пакеты, отправленные ему на 135-й порт.)

  • 06.01.2004: (По следам разборок с canadc-40 и пирометром по МНТЦ) имеется необходимость иногда смотреть "сырое" значение (код) измеренного канала -- когда нет полной уверенности в коэффициенте, для отладки etc. Сейчас приходится вставлять в элемент дополнительный канал с r=1.0, или мордоваться с cx-rdt.

    А можно б сделать -- чтобы из 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.
    • В Chl, в окне "Channel properties", отображается вместе с детализацией флагов еще и это сырое значение. (Есть, конечно, другой вариант -- переключать отображение с осмысленного на сырое, но это шибко путательно).
    • В 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".

  • 06.01.2004: В Knobs/Cdr/Chl для readonly-каналов дать полю incdec_step смысл "размер быстрого изменения" и при
    |V(t=n)-v(t=n-1)|>incdec_step
    сигнализировать о сим факте.

    Некоторые замечания на сию тему:

    1. Сигнализировать можно путем инверсии -- менять fg/bg.
    2. сигнализировать имеет смысл, только если в остальном с каналом все OK -- он не желтый, не красный, не синий/бордовый -- т.е., у "FASTCHANGE" наименьший приоритет (он может перекрывать только "RELAX").
    3. При предыдущем состоянии ==FROZEN/HWERR выставлять FASTCHANGE бессмысленно. Аналогично, при самом первом обновлении (когда предыдущее curv=0.0) проверка также бессмысленна.
    4. "FASTCHANGE" -- одноразовое состояние (как "RED"), или 5-секундное -- как "OTHEROP"?
    5. ...а можно сделать мерцание -- при предыдущем состоянии тоже FASTCHANGE, чтобы новый FASTCHANGE не ставился -- тогда оно будет flip/flop.
    6. Можно ввести новый флаг "CDR_...", а может, как раз подойдет "OTHEROP"?
    7. Нет, не подойдет, поскольку это все-таки флаг Cdr (из-за того, что incdec_step -- в Knobs).

    09.01.2004: обсудил идею со Старостенкой. Результаты обсуждения:

    • Он сам о подобном задумывался, так что смысл в подобной фиче точно есть.
    • Просто сравнивать текущее и предыдущее значения некорректно -- бывает шум, так что воизбежание ложных тревог надо делать проверки за N последних циклов (причем Старостенко жаждет иметь N настраиваемым; но как минимум N должно быть индивидуальным для каждого канала).
    • Индикацию "быстроскачущести" хочется иметь не бинарной (быстро/небыстро), а "диапазонной" -- типа показывать величину градиента. Тут имеется две идеи, хотя обе так себе:
      1. Справа от поля (аналогично тому, как сделаны стрелочки [v\^]) расположить маленький прямоугольный растр, который бы являлся "свернутым" линейным "термометром", заполняющимся слева-направо, снизу-вверх. (Идея Старостенки)
      2. Расположить горизонтальную линейку непосредственно под элементом, безо всякого "свертывания", поскольку практически все управляющие ручки протяженнее в ширину, чем в высоту (а основной тип -- LOGD_TEXT -- так и просто идеально подходит для такого размещения). (Идея моя)
      В обоих вариантах имеет смысл сделать "термометр" цветным -- либо переход от зеленого к красному, либо по радуге. Но это тема для отдельного обмышления.
  • 06.01.2004: Ввести флаг CDAF_CALCERR и (!!!): сделать его =1<<16, OTHEROP=1<<17, а Cdr'ные пустить с <<31 вниз. Тогда можно считать CALCERR вместе с CXRF_... и, в т.ч., отображать его в окне "Свойства канала".

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

    14.01.2004: ты, собственно, функции {cda,Cdr}_strrflag_{short,long}() делать собираешься?!

    14.01.2004: флаги перетасовал, и CDAF_CALCERR изготовил. cda.c::cda_execformula() его даже выставляет.

    Возникли несколько тонкостей с точки зрения стыковки cda/Cdr :

    • Cdr.c::CdrProcessKnobs() "корректности ради" оставлял в полученных от cda флагах только CDA_FLAG_OTHEROP|CXRF_SERVER_MASK. Посему ввел cda.h::CDA_FLAG_CDA_MASK=CDA_FLAG_CALCERR|CDA_FLAG_OTHEROP, и использую его.
    • Возник вопрос, как же все-таки классифицировать CALCERR -- вообще-то это ведь не совсем "хардверная" ошибка. Но все-таки пришел к выводу, что проще его считать именно заодно с SERVER_MASK, так что в Cdr.h мы прибавляем его к 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".

  • 27.01.2004: наболело: надо перенести махинации с versioning (major*1000000+minor*1000) куда-нибудь в "глобальный", "общий" файл.

    А то эта схема (позаимствованная (с мелким упрощением) у 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().

  • 12.06.2004: мысль-тревога: а надо ведь при использовании CX_VERSION_IS_COMPATIBLE(that,my) очень тщательно выбирать, кого считать за "that", а кого -- за "my", т.е., какая из сторон допускает "большую продвинутость" другой стороны.
  • 18.05.2004: для "автоматического" сохранения файлов (режимов) нужен какой-то интерфейс (в libuseful?) для подбора имен вида "basenameXXXXX.dat", где "XXXXX" -- некоторое последовательное число, которое этот интерфейс и должен выбрать, прочитав директорию и найдя первое незанятое.

    (Нечто подобное делалось в нашей с Эйдельманом программе 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].

  • 18.05.2004: похоже, нам нужна функция (тоже в libuseful?), которая бы искала по нескольким директориям, где можно нарыть указанный файл: типа
    findfile(filename, "path1", "~/path2", ..., NULL)
    Т.е., чтобы она "понимала" префикс "~" как home-директорию.

    Это потребуется для всяких cx-starter'ов при поиске config-файлов. (Собственно, когда писал этот запрос в блокнот 18 мая, понимал зачем, а сейчас, 13 июня, когда вбиваю в bigfile-0001, уже подзабыл мотивировку... :-)

    20.04.2007: а вот теперь очень даже понял, зачем:

    • Первоначально-то хотелось именно ради cx-starter'а, где config-файл, при неуказанности в командной строке, ищется "интеллектуально" по "./%s","~/cx/configs/%s"|"$CX_ROOT/configs/%s".
    • Сейчас же -- это потребно для OpenDescription() и lapprox_table_ldr(), которые столь же "интеллектуально" перебирают кучу мест, где файл может иметься.

      И уже поднадоело писАть там эти case с хитрым содержимым.

    Так что -- можно реализовывать, действительно где-нить в useful, причем -- оно должно быть достаточно общим и позволять "искать" и директории (для cx-starter'а), и .so-файлы.

    Поэтому создаем новый раздел "findfilein", и дальше все идет там.

    08.08.2007: поскольку цель достигнута, то "done".

  • 30.05.2004: надо вообще поубирать все пультовые программы из chlclients/ и xmclients/, оставив там только некоторые "стандартные" программы/утилиты. А быть все эти вещи должны в отдельных пакетах, лежащих в будущем CVS'е также отдельными проектами (да хоть -- "pult"), а в дереве cx им делать нечего.

    Т.е., из 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".

  • 12.06.2004: стоит подунифицировать формат ВСЕХ структур-дескрипторов загружаемых модулей -- и драйверов, и layer'ов, и frontend'ов, и chlclient-описаний.

    Уже сейчас ВЕЗДЕ используется схема [0]=magicnumber, [1]=version, а стоит ее дополнить принципом "всегда [3]=char*-название".

    12.06.2004: сейчас это НЕ так в subsysdescr_t (там перед именем идет kind) и в programs/daemon/CxsdFrontendRec (там поля "имя" вообще нет; это унаследовано еще из server/cxsd_module.h).

    12.06.2004: сразу же взялся и все менять. Итак:

    1. В subsysdescr_t передвинул sysname перед kind. Естественно, CXSS_SUBSYS_VERSION_MAJOR продвинул с 1 на 2.
    2. В CxsdFrontendRec добавил поле name, а в DEFINE_FRONTEND_REC() -- инициализацию оного. Версию продвигать не стал, ибо однопроцессный сервер пока все равно в стадии эмбриона.
    3. Из server/cxsd_module.h секцию "Frontends interface" вообще удалил -- там оно уже никогда не станет использоваться, только в новом однопроцессном.
  • 17.01.2005: надо массово избавляться от использования ctime, поскольку она locale-dependent, и стараться везде писАть все руками при помощи localtime().
  • 18.12.2010@Снежинск-каземат-11: потенциальная проблема с несколькими файл-парсерами на основе fgets(): там стояла конструкция
    if (linebuf[strlen(linebuf) - 1] == '\n')
        linebuf[strlen(linebuf) - 1] = '\0';
    
    так что при пустых-строках-перед-EOF оно бы полезло в linebuf[-1] (в некоторых местах применялись аналогичные конструкции с предварительным linelen=strlen(linebuf)).

    Реально, конечно, такая ситуация вряд ли бы случилась -- скорее будет feof() или fgets()==NULL, но конструкция с linebuf[-1] всё равно крива.

    18.12.2010@Снежинск-каземат-11: затронуты:

    • cx-starter.c::ReadConfigFile()
    • daemon/cxsd_config.c::ReadLine() (этот реликт был за 24-11-2003!)
    • cxd_config.c::ReadLine()
    • cx-server_dbase.c::ReadLine()
    • 4cx/src/lib/Cdr/Cdr_file_ldr.c::file_linereader()
    • 4cx/src/lib/Cdr/Cdr_newf_ldr.c::file_lreader()
    • smp4td_stdio.c::smp4td_stdio_lreader()

    Многие из них были копиями. Это демонстрирует необходимость в ЕДИНОМ парсере.

...
X11:
Шрифты:
Cyr-RFX
  • 06.05.2004: New Century Schoolbook -- хоть те, что есть.

    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, так что теперь по всем "потребительским свойствам" они аналогичны остальным моим шрифтам. На пультовых машинах все обновлено.

Admin tasks:
  • 16.09.2003@Bled: обдумать "access security policy" для ИЯФ. В частности:
    • Запретить всё подряд, все компы в 192.168...., оставив лишь Sky, Inpbox, etc., и сделать proxies для нужных служб.
    • Выделить "vepp5.~", "vepp4.~", ... -- сделав их 193.124.* (и открыв на них для доступа снаружи лишь 80 и/или 22).
    • Переименовать машины -- чтобы "inp.nsk.su"=>"www.inp.nsk.su" (плюс чтоб были ns.~, ftp.~, mx.~).

    Подготовить "общую бумагу" с описанием всех этих весчей, и постараться это всё продавить, дабы натренироваться на этом примере. (Будет сопротивление (вялое, болотистое) от Дуброва?)

    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, обсуждения со Старостенко, который вкупе с Костей Лотовым и Кулипановым двигают идею оживления институтского сайта.

  • 16.09.2003@Bled: купить AVP for Linux -- подловить дирекцию, как Каплин.
  • 16.09.2003@Bled: VPNs -- для битья ИЯФской сети на лабораторные подсети.
  • 16.09.2003@Bled: а web-сервер-то стоило бы вынести наружу от firewall'а, но как класть туда /~user/...?
  • 16.09.2003@Bled: Включить в схему сети для ИЯФа машину для IDS (statserv? mx? fate?) -- snort (и для vepp5 тоже?).
  • 17.09.2003@Bled: еще раз, тулзы, которые надо поставить/понастраивать: Apache, CVS, wu-ftpd/other_ftpds, ldap, ...

    Плюс 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".

WWW:
Задачи для студентов:
Темы статей:
Доклад о системе управления для C13

16.04.2004: По утверждениям Алякринского, в ИЯФе будет в июне или июле workshop на тему нашего C13. Полезно (для "показаться народу") сделать там доклад.

19.04.2004: В докладе надо обязательно уделить внимание проблеме, приведшей к появлению seqexecauto. "Возможно, я изобретаю велосипед, но задача противная, нетривиальная, и решение мне представляется/кажется элегантным" (хотя, глядя сейчас -- как я сразу до него не догадался, ведь предпосылки очевидны).

23.08.2004: доклад был сделан уже больше месяца назад. Про seqexecauto там ничего толком сказано не было, но можно дополнить. Оно все в .ppt-файле, который надо обязательно выложить (в pdf) в "My publications".

О наблюдениях за термостабилизацией
  • 15.03.2005: @KEK: Мика показывала какие-то их "длинные" наблюдения. А мы -- можем сделать подобные же длинные логи от термостабилизации, и сделать report/статью/доклад на них.
Прикидка -- на CAN-expo
  • Примерно, о чем можно рассказывать на CAN-expo:
    • ИЯФ -- что это, установки (ВЭПП-{3,4,5,2000}).
    • НЕ только CAN -- еще CAMAC, VME, RS232/485, Tek, CCD, ...
    • ЧТО делать -- часто заранее известно лишь примерно, ТЗ меняется по ходу дела.
    • СУ из кучки самостоятельных программ -- чревата.
    • 3-layer.
    • ...
    • Ассортимент ИЯФовских CAN-устройств.
    • Протокол CAN-kozak -- свой!
    • Почему свой? Почему не CANopen?
"Древесная структура и плагин-отображаторы"
  • 11.04.2005: Идея одного из докладов "о разделении данных и их представления". Cxsd,cxlib+cda+Cdr<->Knobs+plugins+user's. Тезисы (пока не слайды):
    • Юзеры-физики -- малоумехи, низкой квалификации как программисты.
    • Такой подход дает гибкость и простоту построения интерфейса -- он не привязан к добыче данных.
    • С другой стороны -- поскольку добычу данных обеспечивает стандартная, писаная профессионалом библиотека, эта часть is reliable and feature-rich.
    • И, наконец, сама схема "дерева" -- т.е., описания данных в виде дерева, довольно элегантна и дает хорошую гибкость; и то же описание используется и в "мониторе" (нынешний cx-starter в своей следящей ипостаси), и подходит для logger'а/archiver'а, и даже для генерации web-страницы.
    (Да, это все -- о том, что есть описание данных, а юзер обеспечивает лишь свои plugin'ы, если они нестандартны -- например, для bigc, или для "схемо-картиночного" отображения элемента.

    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".

???
Идеи лета 2014
  • 21.07.2014@пляж: идеи, пришедшие в голову на пляже (может, и несколькими днями раньше):
    • cxscheduler: a simple scheduler for event-driven console applications
    • fdiolib
    • sendqlib
    • memcasecmp
    • Рекомендации по интерфейсу устройств -- ПРЕПРИНТ сделать.
    • Типа структура презентации в докторскую: "софт СУ состоит из работы с железом и интерфейса с юзером". Проблемы: 1. ... 2. ...

      Модульность решает обе эти проблемы: сама программа превращается просто в дирижера-компоновщика, определяющего, какие модули-кубики активировать. "простые программы -- [картинка], чуть сложнее -- ..."

    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'ы.

Что почитать/поизучать:
Unix:
  • 12.04.2004: IPv6

    12.04.2004: IPv6 -- насчет него вообще, и насчет поддержки IPv6 в CX: в X11R6.7 была добавлена поддержка IPv6, и краткие комментарии на эту тему имеются в Overview of IPV6 Specific Changes (локальная копия).

    Там, в частности, упоминается функция getaddrinfo(), от которой, "как за хвостик" (на нее есть man-страница), можно вытянуть ссылки на еще функции.

    Вообще мысль на эту тему:

    1. Оно у нас НЕ-first-priority, так что можно особо не грузиться.
    2. Все "исходные данные"/примеры, т.е., реализация в X11, у нас есть.
    3. Если понадобится сделать -- то все, на что IPv6 повлияет, сосредоточне в cxlib.c, cxsd_fe_cx.c и cxnetacl.c, так что внедрение будет сравнительно простым.
    Так что можно про IPv6 пока особо не задумываться. Конечно, если попадется tutorial -- очень интересно будет почитать для общего развития.
  • 08.01.2005: вроде как разобрался с давно волновавшим меня вопросом "что такое 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(), так что надлежит полагаться именно на него. Не говоря уж о том, что он лишен всех вышеперечисленных недостатков.

    Так что, надеюсь, сей вопрос для меня теперь закрыт.

  • 25.01.2005: почти случайно наткнулся на статью на тему "Shared memory transport", написанную на тему DRI для X11: Shared Memory Transport for XFree86 (локальная копия).
  • 25.01.2005: что такое "futexes" в Linux'е?

    07.02.2005: на эту тему был натравлен Гусев (в связи с CAMAC'ом у Мамкина), и он нарыл описание от "сэнсэя" Дреппера -- futex.pdf (локальная копия). Acroread-4.0(24.01.2000) его почему-то не показывает, а вот xpdf -- за милую душу.

  • 26.01.2005: на тему "ограничения количества файловых дескрипторов": у нас в коде везде, где используются fd_set'ы при появлении дескриптора стоят проверки -- а не слишком ли он большой. (Какой я умный!!! :-)

    26.01.2005: А сегодня в Bugtraq попалось advisory от Заразы, где говорится ровно про эту проблему, пропущенную в море разных продуктов (@3APA3A's security.nnov.ru, @securityfocus.com). Там упоминается также NetBSD'шный advisory на эту тему за 2002г. Хотя сама по себе идея -- на мой взгляд -- очевидна (еще раз аплодисменты мне, умному).

  • 06.04.2006: стал разбираться с давней-давней проблемой, что dlopen()'нутые библиотеки НЕ защищены от записи (в отличие от самих программ -- НЕ дают ETXTBSY), и даже более того -- что запись в эти файлы отображается и в адресном пространстве, и просто тупое копирование новых версий поверх старых приводит к SIGSEGV.

    Причем разбирался с прицелом на написать bugreport -- даже testcase изготовил, в tests/dlopen_etxtbsy/, со скриптом, который и сам все компилирует, и запускает, и перепрописывает .so'шку нулями.

    06.04.2006: итак, результаты: да, testcase приерасно падает по SIGSEGV. Думая, что это linux-specific bug, пошел проверять другие ОС, и...

    • IRIX-6.5 (Sky) -- позволяет писАть и падает.
    • OSF/1-4.0 (Axinp) -- аналогично.
    • OpenBSD-3.4 (Rainbow) -- писать позволяет, но ничего про SIGSEGV не говорит, а, похоже, просто "пропускает" далее идущий код из библиотеки: библиотеков 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 перед писанием в него.

  • 30.03.2007: случайно узнал (наткнулся в Wiki), что в GLIBC printf(), оказывается, кастомизабелен! Т.е., можно вводить свои %-спецификаторы! Описано это в Customizing printf или в "info libc" -- "info libc 'customizing printf'".

    А потом наткнулся еще на одну возможность -- что можно (как это было в Turbo Vision) создавать СВОИ типы потоков (streams), которые совсем не обязательно привязаны к файлам (именно так внутри libc реализуются sprintf() и sscanf()). Рассказ об оном -- в подразделе "Custom streams" раздела Other Kinds of Streams или в "info libc 'Other Kinds of Streams'".

Админство:
  • 16.09.2003@Bled: Узнать, что есть RADIUS.
  • 16.09.2003@Bled: PAM -- разобраться в синтаксисе rule-файлов (/etc/pam.d/...) -- что значат всякие "required"?
  • 16.09.2003@Bled: XML -- что есть "XML schema"?
  • 16.09.2003@Bled: ICMP -- почитать RFC и какой-нить tutorial.
  • 16.09.2003@Bled: SNMP -- то же, почитать RFC и какой-нить tutorial.
  • 16.09.2003@Bled: SNORT -- изучить.
  • 16.09.2003@Bled: TACACS -- что это?
  • 16.09.2003@Bled: что есть NESSUS?

    ЗАМЕЧАНИЕ (совет от Herve Debar): НЕ ИСПОЛЬЗОВАТЬ nessus для проверки snort'а -- его правила затачивают как раз под nessus.

  • 16.09.2003@Bled: получить basic knowledge того, как работают DNS, whois, TCP/IP...
  • 16.09.2003@Bled: Вызнать и изучить утилиты Linux/IRIX -- mime64/base64/pgp/sha1 etc., s/mime, pkcs/7 -- а что насчет Pine, Mozilla и прочих?
  • 17.09.2003@Bled: что есть x400Address -- X.411; directory name "X.501" /* Chadwick #2p16 */; X660/ISO9834-1 /* ~p17 */; прочитать RFC1630,791.
  • 17.09.2003@Bled: Kerberos -- overview & tutorial, Kerberos for 1) Linux; 2) X11.
  • 17.09.2003@Bled: X509 -- overview & tutorial (начало -- Chadwick #2).
  • 18.09.2003@Bled: Запомнить: наш Apache@www.inp отдает "406" при запросе "sl" -- это есть плохо! Примеры логов:
    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)"
EPICS:
  • 16.03.2005: @KEK: "Driver support"? "Device support"?

    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.

    • Record support -- это собственно обычный интерфейс, которым "EPICS server" доступается до своих record'ов. У нас -- drivers API.
    • Device suport -- нечто не имеющее у нас аналогов, и обусловленное тем, что в EPICS'е есть несколько РАЗНЫХ "record types" (а у нас -- лишь regular channels и big channels, сильно отличающиеся и имеющие весьма универсальный интерфейс). Реально это можно было бы назвать -- "record-type support".
    • Driver support -- очень похож на наши layers. Никак реально не формализован. Среди заявленных смыслов -- "Initialize a software module before individual device layers using it are initialized", "Provide a common interface to an I/O bus such as GPIB, Bitbus, Allen-Bradley etc.".

    Да, и еще -- ключевыми словами являются "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 это понятие/"уровень" присутствует чисто для полноты картины, чтоб "стек" был цельным, не оборванным.

  • 24.03.2005: @Shonan: Как у Гусева сделана поддержка асинхронной работы (ADC20, ...) при использовании ППИ -- multithreading? Или?

    11.06.2005: поговорил на сию тему.

    Хмырк... Правильная постановка вопроса -- "как бы он сделал поддержку ПЕРИФЕРИЙНЫХ крейтов".

    Как бы то ни было -- ответ получен.

    • Первая часть ответа -- что это большой зад, и он старается так не мучаться.
    • Вторая же часть ответа -- что у него есть механизм типа "автомата исполнения", которому (per-I/O-port) ставятся в очередь пары (initiator,completor). Когда очередь доходит до некоей пары, то вызывается ее "initiator", в данном случае -- шлющий энное количество NAF'ов. Потом же -- когда "придет ответ" -- вызывается "completor".

      Это весьма общий механизм -- он называется asynDriver, написан by Marty Kraimer,

      1. Там предполагается, что программер изготавливает "поддержку РЕАЛЬНОЙ работы с портом", а сам asynDriver -- это как чистый виртуальный (pure virtual) класс.
      2. Оно работает ТОЛЬКО в multithreaded environment.
      3. Гусев жалуется, что буквально за год asynDriver дико вырос (ну да -- больше 2000 строк кода, 74Кб), и стал слишком монструозен.

      Сама идея с (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 позволяет добиваться нужного эффекта (в т.ч. при помощи блокировок), но до чего ж это выходит громоздко и завернуто!

  • 24.03.2005: @KEK: Как соотносятся PV (process variable) и record в EPICS'е?

    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.".

  • 13.04.2005: да, еще -- а что у Гусева с БД? Откуда он берет описания содержимого его IOC'ов?

    11.06.2005: хе-хе, в EPICS'е стандартная БД -- это текстовые (.db) файлы, с описанием -- record'ы такие-то, у них поля имеют значения такие-то.

    Так что:

    1. То-то всякие epics'овщики на каждом ECALEPCS'е/PCaPAC'е докладываются "а вот мы БД сделали так-то".
    2. В общем-то не так уж и бредова была моя древняя идея с набором .uds-файлов -- они давали весьма полную информацию.

      Ошибкой же была идея 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: давно стоял вопрос -- а как там с динамической загрузкой?

    11.06.2005: А очень просто -- динамической загрузки в EPICS'е НЕТ!

  • 19.08.2005: кстати, нарыл описание протокола Channel Access -- как и рекламировал Марк Плешко, на их сайте: Channel Access

    10.05.2017: кстати, тот cosylab'овский линк стал нерабочим -- какой-то пароль требует.

    Поэтому ныне можно брать файл по ANL'евскому адресу, а также он сохранён локально.

  • 31.10.2005: наткнулся на некую "whitepaper" по EPICS'у -- EPICS Architecture (PostScript). Наткнулся, роя на тему что есть DDD -- в работе V.Ayvazyan'а (The Design of the Control System for CANDLE) имеется ссылка L.R.Dalesio, M.R.Kraimer, A.J.Kozubal, "EPICS Architecture", ICALEPCS 91, Tsukuba, Japan, 278-282.

    Там, в частности, упоминаются как "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."

  • 20.01.2006: поболтал сегодня с Гусевым на тему внутреннего устройства -- точнее, "модели взаимодействия record'ов с ядром epics'а". Ох-хо!!!
    1. Ситуация, когда дернули питание устройства и это узналось, по словам Гусева толком не поддерживается -- поскольку рекорд не имеет возможности по своей инициативе что-то прописать в "динамическую БД/текущее значение". Все -- только по инициативе ядра IOC'а. Такова их архитектура -- ...
    2. ВСЕ действия -- и чтение, и запись -- там делаются неким унифицированным методом "process", который дергается, а уж рекорд должен что-то сделать. Как следствие -- еще худо-бедно можно сделать не одно, а несколько "полей записи" в одном рекорде, но вот несколько полей чтения (как, например, совместно-читаемые-одной-командой атрибуты пирометра) -- полная задница, их придется раскидывать по отдельным рекордам.
    3. Там НЕТ метода "stop" (или "terminate"), только "initialize". В результате -- НЕЛЬЗЯ сделать reset на живом сервере, как в CX при помощи команды "sato", не говоря уж обо всяких plug-and-play. Это -- следствие того, что рекорды могут быть поперезавязаны ссылками ("forward link", кажется), и потому вначале читается вся "БД конфигурации", создаются рекорды, и вызывается их инициализация, а потом -- все, менять уже ничего нельзя.

    В общем -- на мой взгляд, просто крайне переусложненная (overcomplicated) модель, слабо соотносящаяся с реальным миром. Да, все эти "forward link'и" и т.д. дают весьма навороченный механизм, при помощи которого можно наворотить весьма сложные конструкции, НО -- ведь во многих случаях такая потребность возникает ТОЛЬКО оттого, что нормальных, адекватных средств нету -- вот и приходится ту же проблему "сброс питания" решать совершенно неестественными способами, с привлечением дополнительных рекордов.

  • 20.01.2006: кстати -- а поузнавать бы, какие модели драйверов приняты в других системах!
  • 20.01.2006: и -- нехило бы составить более основательный (реально -- менее поверхностный :-) обзор, какие "data display tools" используются в разных системах управления.

    Прикидочный список: EPICS -- medm, dm2k; DOOCS -- ddd; "fesa" -- knobs, ...CX -- chlclient.

  • 12.07.2006: в MEDM есть понятие "widgetless objects" -- это всякие декорации (прамоугольники, арки, etc.), рисуемые НАПРЯМУЮ на drawing area, являющейся "окном". Причем они могут даже как-то в зависимости от PV менять свой "вид" -- например, цвет.

    А вообще -- именно это и используется для рисования разнообразного "фона" типа колец, линаков, etc.

    27.07.2006: у нас это теперь тоже есть -- ELEM_CANVAS & Co. Пока статические, без смены состояния, но как фон -- уже вполне идут.

  • 28.07.2006: а еще MEDM поддерживает т.н. "Visibility" -- как одиночный объект, так и целая группа может менять свою видимость в зависимости от значения PV либо calc expression'а.

    28.07.2006: вот только, думается мне, для нас это уже несколько лишнее:

    1. С одной стороны, конечно, так можно легко реализовывать всякие хитрости типа отображения в центре кольца параметров той линзы, на которой щелкнешь -- просто при щелчке уставляется локальный регистр CurLens:=n, и для каждой линзы есть элемент с параметрами, расположенный в центре кольца, у которого Visibility=(CurLens==n).
    2. НО: реально это -- целая банка с червями, поскольку наверняка станут использовать для скрытия целых групп в каких-то режимах, так что пользователь и знать-то не сможет, что эти группы есть. Пример с неудобствами -- непоказывающиеся пункты меню в WinXP, от которых проблем намного больше, чем пользы.
    3. К тому же -- отдельный вопрос КАК "исчезать" объекты: при помощи XtManageChild()/XtUnmanageChild() -- нехорошо, ибо тогда едут аттачменты; трогая поле 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.)". Пять баллов!!! :-)

  • 26.06.2008: в CA у record'а есть 4 диапазона -- {lower,upper}_{disp,alarm,warning,ctrl}_limit. По аналогии видно, что теперь CX/Cdr'ная система диапазонов явно полна.
  • 15.07.2008: кстати, весьма детальное описание внутренностей разных типов рЕкордов есть в документе "EPICS Record Reference Manual".
  • 15.07.2008: Спросить Гусева:
    1. Как он влинковывает в EPICS'ный сервер драйверы - как они там регистрируются?
    2. Есть ли в протоколе структуры?
    3. Какие типы рекордов он использует?
    16.07.2008: спросил, вот ответы:
    1. Влинковывается - он там делает .dbd-файл, который не [только?] читается IOC'ом при старте, но препроцессируется в C++'ный код, содержащий таблицы, из которых и берется инфа о том, какие свойства у этого типа рекорда и какие функции его обслуживают.
    2. Структур нет, только массивы.
    3. Да кучей разных типов рекордов он пользуется, хотя и не всеми. Реально то, какими и почему да, можно понять, лишь глубоко вгрокавшись в саму эту странную модель разных типов рекордов.

      И, кстати, как он говорит - сейчас число типов рекордов сократили до пары десятков, вместо описанных 36.

  • 19.07.2008: еще спросить у Гусева -- а есть ли в EPICS'е блокировки, чтобы некая программа могла захватить набор каналов-записи в монопольное владение, а остальные могли лишь читать?

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

  • 28.07.2008: еще спросить у Гусева -- в medm/dm2k w-каналы отображают ли изменения другим оператором? Или там отдельно readback? А при старте -- вычитывают?

    29.07.2008: спросил -- да, там все окей, и отображают и, естественно, вычитывают. В этом смысле medm ведет себя разумно -- как наши ручки ;-)

  • 06.08.2008: еще выяснилось -- оказывается, в EPICS есть "калибровочные кривые" (аналог нашей аппроксимации), называются они "breaktables".

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

    А во-вторых -- они должны быть МОНОТОННЫМИ, поскольку там подразумевается, что они могут использоваться в обоих направлениях, что в реальности-то бывает очень редко. На эту тему в 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"

LabVIEW:
  • 22.06.2008: National Instruments идет моим путем: в NI-DAQ (legacy) был отдельный API Counter*() для счетчиков, а в NI-DAQmx это уже идет как каналы вообще -- всё управление производится через унифицированный API; с прочими видами каналов, полагаю, аналогично.

    Так что EPICS с его кучей разных видов рЕкордов -- это точно тупик.

  • 28.06.2008: у NI есть некий свой "протокол удаленного/распределенного получения данных" -- Datasocket. Поузнавать бы о нем, что это такое...

    28.06.2008: да, пошарился, попробовал поразбираться. Есть на его тему куцая страничка у NI -- DataSocket, с куцым же как бы "техническим" Integrating the Internet into Your Measurement System: DataSocket Technical Overview (в основном там просто маркетинговый треп).

    Результаты узнавания:

    • Это "протокол" модели Publish/Subscribe -- "сервер" DataSocket является просто тупо "почтовым ящиком", в который одни программы могут писАть, а другие -- получать данные.
    • Как говорит Старостенко, реально этот "сервер" к CVI'ю просто подселяется, так что выходит даже не отдельная программа-почтовый-ящик, а как libCAS.
    • Существует оно, по утверждениям NI, для LabVIEW и для CVI, ни Си, ни C++ там НЕ упоминаются (и гугление также ничего не дало). Так что, выходит, что в CXv4 ни datasocket-plugin для cda, ни datasocket-"frontend" для сервера сделать не удастся.

    08.07.2008: неа, все-таки ситуация немножко лучше! :-)

    • Узнал, поговорив на эту тему с Пашей Селивановым -- который DataSocket'ом как раз таки пользовался на C++. Есть там, судя по его словам, на что ругаться, но в основном -- приемлемо.
    • C-интерфейс содержится в LabWindows/CVI. Ключевые слова -- файл dataskt.h и функция DS_Open().
    • И еще -- смотреть надо help-файлы в CVI'е, а в сети, похоже, практически нихрена на эту тему нету.

    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
  • 27.07.2008: не совсем здесь к месту, но все же: я со злости провел исследование -- прошарил списки докладов на всех ICALEPCS и PCaPAC с 1995-го года в поисках слова "LabVIEW". Так вот -- хотя оно встречается почти на каждой конференции, но в основном в докладах о мелких махарайках (у разных народностей, но японцы с китайцами особо любят). А вот СТАБИЛЬНО -- встречаются доклады только нашего дорогого Блокланда, да пару раз L.Catani отметился (у них во Фраскати оно является "de-facto standard").
Windows:
  • 15.03.2007: несколько дней назад в BUGTRAQ появилось исслезование от 3APA3A (ZARAZA, ЗАРАЗА) "Microsoft Windows Vista/2003/XP/2000 file management security issues" (Message-ID 1199291182.20070308225837@SECURITY.NNOV.RU). И в ее обсуждении некто Roger A. Grimes упомянул (Message-ID 096A04F511B7FD4995AE55F13824B8332127F5@banneretcs1.local.banneretcs.com), что в Форточках ЕСТЬ символьные линки, причем аж в двух вариантах -- симлинки (с Vista), действующие как в Unix, и junction points (с Win2000/NTFS-3.0), ограниченные локальными файловыми системами и являющиеся частным случаем т.н. "reparse points" (и похожи на обычное монтирование в Unix). А уж hard-link'и-то были в NTFS практически с рождения -- я об этом давно знал, поскольку FAR их умеет делать (или hard-link'и -- это и есть junction points? неа, mklink имеет и то, и другое; или -- junction для директорий, а hardlink'и -- для файлов? -- вообще-то и в Unix hardlink'и на директории НЕ приветствуются).

    Ссылки по теме:

    Вывод/резюме: разные виды линков-то там есть, но вот для использования они совершенно не приспособлены:

    1. По умолчанию отсутствуют стандартные средства для их создания (хорошо хоть, что у dir обычно хватает ума их показывать/помечать) -- только в Vista появилась команда mklink, которая зачем-то различает файлы и директории.
    2. По умолчанию удаление их (Explorer'ом), вместо того, чтобы делать unlink() (уменьшать link count и высвобождать ресурсы при count==0) и удалять символьные линки (как шорткаты) зачем-то удаляет то, НА ЧТО они указывают. Бред!

    Плюс, в Форточках есть некая хрень именуемая "Creator Owner" (т.н. "специальный SID"), который Grimes атрибутирует как якобы аналог umask'а.

    Ссылки по теме:

    • CertiGuide to A+ (A+ 4 Real) - The "Creator Owner" (это вообще часть некоего более общего описания, включающего "NTFS File Permissions").
    • "If a member of the Administrators group creates a resource, the Administrators group is the owner of the resource. This group is created for each sharable resource on Windows 2000 Server or Professional. A placeholder in an inheritable access control entry (ACE). When the ACE is inherited, the system replaces this SID with the SID for the object's creator."
Всякое разное:
Узнавание опыта соседей -- что может быть использовано у нас, и что должен уметь CX, чтобы мочь использоваться там:
Разный софт:
Постеры/презентации/дизайн:
  • 16.09.2003@Bled: !!!Разработать-таки шаблон .ppt для презентаций!!!
  • 17.09.2003@Bled: Подизучить современный HTML -- что есть такое
    • "<?xml_version...>" -- "<?" -- ?
    • "<!DOCTYPE...>" -- ?

    Общий синтаксис xml: <TAG...>...</TAG> и <TAG... /> -- ?

Текущие дела:
C13/МНТЦ:
  • 02.12.2003: первоочередные работы:
    1. Запустить CX-сервер для одного CANADC40 на Hedgehog.
      03.12.2003: запустил, а что толку? :-) Блок он пока не видит.
    2. Разобраться с подключением кабеля контроллер<->CANADC40 и подключить его к Hedgehog.
      02.12.2003: взял да подключил. Оказалось достаточно имевшегося кабеля с 2 концами и 1 отводком, на концах как-то запаяны (прямо внутри разъемов) резисторы -- для "терминации".
    3. cx-setchan/cx-rdt -- проверить работу.
      04.12.2003: Yes, yes, yes, заработало! Дело было в необходимости выставить "скорость обмена по линии" на козачином блоке.
    4. Написать махараистого клиента.
      ??.12.2003: да написал, написал я сначала istc/chlclients/DB/db_istcc.h, а потом и istc/xmclients/istcc.c -- работает он.
    5. Доделать pyro-lo.c
    6. Написать CX-драйвер для pyro-lo, и перевести клиента на него.

    20.01.2004: с pyro-lo возиться не понадобилось, поскольку этот низкотемпературный пирометр нам вообще не нужен, был куплен для салимовцев, и несколько дней назад отдан им.

  • 29.11.2004: общался с Губиным на тему того, что нужно будет от программы виброизмерений. В результате имею от него подобие ТЗ "в картинках" на двух страничках.
  • 30.11.2004: давно бы пора сделать для C13 (hedgehog) запускалку как на пульту -- чтобы там был cx-starter, а в нем -- все потребные программы.

    06.12.2005: (записано 08.01.2005) сделал, работает. И даже скрипт collect там имеется. Теперь старое ~istc/work/{cx,istc}/ остались там исключительно в качестве архива, работа в них более не ведется.

Переход на пульту на новый CX (с rflags):
  • 02.12.2003: просто поставить новую версию в некую директорию, проверить работающесть, и переместить старую версию.
  • 02.12.2003: ЗАМЕЧАНИЕ: надо ОБЯЗАТЕЛЬНО разделить директории "из дистрибутива" (bin, sbin, conf, lib) -- они пока пусть будут в ~oduser/cx/ и с сохраненными режимами. Это необходимо для возможности быстрой смены версий путем перекидывания директорий.
  • 02.12.2003: довести cx-starter до такого состояния, чтобы он был способен заменить олегов start.pl.
  • 02.12.2003: все m5200-драйверы: проверять CAMAC-статусы и отдавать соотв. коды при
    1. инициализации
    2. обычной работе
    3. при таймаутах во всяких ПКС, adc200, etc.
  • 02.12.2003: все bigc-драйверы: определения параметров (enum'ы) -- вынести в .h-файлы.
  • 02.12.2003: adc200: удалить оттуда лишний параметр "STATUS" -- он теперь не нужен, ибо есть rflags.
Усовершенствование uclinux_slaves-драйверов:
  • 02.12.2003: поговорить с А.Чугуевым на тему:
    1. Отдача статусов.
    2. Что ему может требоваться от "uclinux_slaves-lib"? (LAM'ы, etc...)
    3. Постараться-таки вынести содержимое uclinux_driverbody.c в отдельную, вызывабельную (не-main-loop) библиотеку.

    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".

Knobs/Chl/Xh:
  • 02.12.2003: для начала подогнать под нужды не-chlclients-клиента для C13.

    20.01.2004: точно не скажу когда, но оно уже вполне подогнано.

    21.01.2004: Потенциальная недоделка: может в Chl понадобиться hook для вызова юзерской функции по приходу данных, плюс доступ к самим данным.

    23.01.2004: точно -- это гарантированно понадобится для управления колесом. Причем часть данных не должна никак отображаться на экране (нууу... то есть, в принципе можно и показывать, но ни к чему). Т.е., должен быть интерфейс для добавления каналов "задним числом". Либо, что будет проще, иметь понятие "скрытого элемента", который присутствует в группировке, но на экране не выводится.

    06.09.2004: (реально сделано еще в 04.2004) был введен соответствующий интерфейс в Chl, и hook тоже, а на "скрытые" элементы etc. пока было решено забить.

  • 02.12.2003: вынести "Step dialog" из _text_widget.c в Chl -- "методы элемента", сделать его как "Properties", и апдейтить там статус по rflags из 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.

  • 02.12.2003:Окошки "Open/Save mode" также унести в Xh, и сделать вместо просто имен файлов списки вида "дата:комментарий" (в стиле того, что было в User's Tree.

    08.01.2005: так во-о-от где пряталась первоначальная запись о желательности этой фичи!!!

    Поскольку все было реализовано больше месяца назад (почти ровно через год после этой записи о потребности :-), то помечаем как "done".

  • 08.12.2003: по результатам опроса Старостенки (вследствие подслушанного разговора с Гусевым и описания проблемы с Гусевым): надо бы иметь возможность менять пределы (желтые/красные) из программы (в гусином случае это необходимо).

    Вопрос: это ж опасно?! Ответ: требовать "десять подтверждений".

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

    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: с утра стукнула в голову мысль: раз у нас уже есть (обновляющееся) окошко "Properties", то теперь будет раз плюнуть изготовить окошко "Значение большим шрифтом".

    21.02.2004: Раз плюнул. В смысле -- сделал.

    На основе того же XmMessageBox'а, только здесь "Message" оставлен и используется для отображения имени knob'а. Из кнопок оставлена только [OK]. Вот с описанием шрифта пришлось помудрить -- в Xh_fallbacks.h вставлены сомнительной разумности строки

    #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, \
    
    Так что оно хватает первый попавшийся пропорциональный bold-шрифт. Пропорциональный пришлось сделать, поскольку а) иначе она хватает и масштабирует какой-нибудь misc-fixed, и б) наверняка найдется что-нибудь векторное именно пропорциональное. "iso*" -- чтоб не хапнула какой-нибудь adobe-fontspecific или wingdings.

    Замечание 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.

CAN-bus-драйверы:
  • 02.12.2003: написать свой marcan_layer.c на замену олеговому can_drv.c.

    06.08.2006: давно уже сделан, даже уже два раза, и используется. "done".

  • 13.12.2004: удаляю старые артефакты -- GetParamInt(). Ежели что -- оно осталось в BACKUP/istc.20041212.messy/.
  • 13.12.2004: А почему, собственно, у нас номер интерфейса (/dev/canN) указывается в auxinfo, а не в bus_id? Там ведь как раз для таких случаев предусмотрен синтаксис "major,minor".

    06.08.2006: со 2-й версии номер интерфейса указывается именно в bus_id -- в формате "IFACE,DEV_ID". "done".

Перевод пульта на Fedora-2/kernel-2.6, и в локальную сеть:
  • 17.06.2004: Что нужно для этого перевода:
    • Попробовать поставить FC2 на P-III и на P5; работает ли?
    • Поставить и настроить "модельный"/"мастер"-винт.
    • Портировать недиогловый CAN-драйвер на 2.6.
    • Портировать федоровский драйвер ППИ на 2.6.
    • Портировать никифоровский драйвер (Одрята) и сопутствующую инфраструктуру на 2.6.
    • Прогнать все эти вещи на модельной машине и убедиться, что оно все работает.
    • Доп.: разобраться с apcupsd.
    • Доп.: ВСЕ нужные оператору вещи (start, x2x) должны запускаться из стартового скрипта window-manager'а.

    22.06.2004: Недеогло прислал исходники драйвера для 2.6.

    25.06.2004: несколько дней назад общался с Федоровым -- он обещался помочь с разбирательством с 2.6, но сам делать этого не будет, типа занятой.

    25.06.2004: еще из прелестей, понятых в последние дни:

    • Перекурочивать пульт под "1 машина с 8 мониторами, 1 -- без" не придется, поскольку в XFree86 после июня/июля 2003г. (и в Xorg) Xtest "xineramified" и x2x работает нормально.
    • xawtv заменяется на tvtime.
Идеи - что надо обдумать на будущее:
Title1
Text1
Title2
Text2
Сохранение истории "как на магнитофоне"

09.01.2004: поговорил со Старостенкой, который утверждает, что, как минимум, Губину&Co. это сильно требуется, да и вообще это чрезвычайно полезная фича.

Во время этого разговора в голову пришла схема довольно простой и элегантной реализации.

Основная идея -- пишутся не "регулярные snapshot'ы" (типа раза в секунду), а ИЗМЕНЕНИЯ, тэгированные временем измерения. А точнее -- результаты драйверных ReturnChanNNN(). Это дает всю информацию, необходимую для "воспроизведения".

Оптимизация: для "быстрого поиска", чтобы не надо было "проматывать" весь файл, можно, по аналогии с Mpeg, время от времени (раз в минуту?) писать полный snapshot.

Замечание: запросы на запись при воспроизведении надо игнорировать -- ведь если мы, к примеру, смотрим телевизор, то он нас не слышит.

Вопрос: а не потребуется ли также записывать и запросы? И если да, то зачем? Ответ: да, нужно, для разборок -- чтобы понять, каким клиентом было вызвано какое изменение.

Очевидный вывод: записью может заниматься ТОЛЬКО модуль в cx-сервере, который вешается на hook'и в ReturnChanNNN() etc.

Возможность отправить любой канал на график
  • ??.??.2004:

    24.01.2004: только-только стал вспоминать об этой фиче...

    Когда-то давно, еще во времена Эйдельмана, у предка Chl (mapp/uxil) была возможность вытащить до 3 каналов в окошко/область отображения графиков-самописцев.

    Потом, уже в древних версиях Chl, эта идея трансформировалась в интерфейс "PopulateWithGraphs()", позволявший населить окно не элементами, а N самописцами, на каждый из которых можно было так же вывести любой из каналов.

    С тех пор в logchannet_t/knobinfo_t остались поля {min,max}disp.

    А когда начал делать KnobProps, то, вставив туда и строчку для disp, вспомнил про эту идею, и озадачился.

    Заметка: как водится, надо обсудить со Старостенкой, в каком именно виду нужнее/удобнее иметь такую возможность.

    BTW, как по-английски "самописец"?

    26.01.2004: обсудил (дату проставил по памяти, +/- день). Ответ:

    1. Да, такая фича очень полезна.
    2. Насчет "длительности" (ширины графика) -- непонятно, он, как всегда, хотел бы иметь возможность просмотреть "все", но удовлетворился бы и 100 пикселами :-).
    3. Рекомендация/поправка: надо иметь возможность отобразит не ОДИН канал, а много сразу (естественно, разными цветами). Это, во-первых, нужно, поскольку "наложение" кривых удобно для наблюдения, а во-вторых -- тем самым для всех кривых будет много места по вертикали, что дает большее разрешение.

      Подвариант (идея Старостенки): поскольку при таком наложении графики могут полностью перекрывать друг друга, можно уменьшить разрешение по горизонтали. Т.е., при 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 все и обкатаем.

    Уже сейчас в прототипе каждому виджету-графику придается инфраструктура: заголовок,

    По ходу дела возникали идеи:

    • Графики надо б отображать не по порядку следования в списке, а наоборот -- кто выше в списке, тот сверху и на картинке.
    • Следствие: к каждой подписи стоит прилеплять и три кнопочки: "[^][v][x]" -- переместить вверх в списке, вниз, и удалить.
    • Сейчас отображаемые по Y пределы берутся так: mindisp -- минимальный из rmins[KNOBS_RANGE_DISP] всех задействованных каналов, maxdisp -- аналогично. И для изменения шкалы приходится изменять параметры у всех каналов. А стоит ввести аналогичную обратную возможность: менять пределы ГРАФИКА, чтобы при этом они автоматически уставлялись и всем каналам в их {rmins,rmaxs}[KNOBS_RANGE_DISP].
    • Можно будет сделать, чтобы при перетаскивании на график производилась проверка, есть ли у канала histring, и если нет -- то чтоб создавался на лету.
    • Нужна некая инфраструктура для взаимодействия обычной Chl и графиков: чтобы при DnD корректно обрабатывалось повторное перетаскивание канала на график; надо иметь возможность авансом указывать список "историзируемых" каналов.

    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: вопрос -- КАК пользователь может отправлять некий канал на график?

    20.12.2004: результат brainstorm'а:

    1. Мой старый вариант (со времен drc): при каждом графике иметь селектор, которым можно выбирать жертву.
    2. Идея Лебедева: при каждом канале иметь включатель, при щелчке на котором появляется история для этого канала.
    3. Идея Старостенки: drag-and-drop -- чтобы мышью можно было "перетащить" любой канал на любой из графиков.
    4. Идея Гусева: набор "имени" канала в специальном текстовом поле графика; а уж имя в это текстовое поле можно и перетаскивать drag-and-drop'ом.

    Варианты (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".

  • 20.01.2005: надо сделать, что если цвет НЕ указан, то выбирается "очередной".
  • 24.01.2005: желание иметь фичу: чтобы при шевелении мышью по полю самописца отображалось бы то значение (число), что под курсором. Т.е. -- ВСЕ значения, видимо.

    Ну и где б это можно было рисовать? Места то везде жа-а-алко!!! (А так -- можно б иметь место справа от метки с именем канала.)

    А что -- может, ЗАМЕНЯТЬ на это время метку? Или, может, иметь ВТОРОЙ виджет (уже XmText), который бы на время отображения лежал ПОВЕРХ метки?

  • 24.01.2005: и можно б подумать на тему более слабой связи между хранящимся массивом и пикселами на экране. Тогда будет вот что:
    • Можно будет иметь scrollbar, позволяющий показывать то, что уже скрылось за левым краем (и помнить, соответственно, хоть несколько суток данных).
    • Иметь возможность масштабировать данные по времени -- например, что один пиксел демонстрирует не 1 такт, а 10/100/etc.
    • Соответственно, при отображении с неединичным масштабом может иметь смысл показывать значение не из одной ячецки, а усредненное по диапазону, соответствующему этому пикселу.
  • 20.07.2006: а может, сделать как в MEDM/dm2k -- просто отображатор "самописец" (как у них "strip chart")? И им этого хватает, и Гусеву -- может, и нам тоже пока этого хватит?
  • 18.09.2006: так, теперь есть полное знание, как именно надлежит реализовывать отправление каналов на графики в Chl.

    18.09.2006: собственно, знание подспудно накапливалось, а последней каплей стала реализация adc200.c. Итак:

    • Рисовать графики надо в отдельном окне, которое как бы диалоговое, но НЕмодальное.
    • Окно содержит:
      1. Собственно графики -- две вложенных drawing area (для графиков и для оформления)
      2. Область "описания каналов" справа, в которой на каждый из каналов отводится строка, содержащая:
        • Название (метку) канала.
        • Текущее его значение.
        • [опционально] Значение под мышью.
        • Кнопку [-] или [x], удаляющую этот канал из списка.
      3. [опционально] Снизу некую "status-область", где можно было б гнать данные с реперов и/или из-под мыши.
      4. Кнопку "Закрыть", прикрывающую все окно (для юзеров, не умеющих double-click на кнопке слева вверху). (Красоты ради и обе drawing area, и описания каналов помещаются в форму, которой ставится XmNbackground:=COLOR_GRAPH_BG, и им самим (кроме кнопок) он же.)
    • Окно можно resize'ить -- при этом по вертикали оно подгоняет масштаб так, чтобы min/max влезали в имеющуюся высоту; по горизонтали же отображеются столько точек, сколько есть.
    • При всех событиях, потенциально меняющих размер окна, делается уставка -- указываются [minw,maxw]x[minh,maxh] так, чтобы минимум графика был, скажем, 100*50 пикселов.

      Делается это так: считываются текущие размеры shell'а, из них вычитаются текущие размеры drawing area, которая график, и это плюс 100*50 идет за минимум.

    • Каждый из каналов масштабируется при отрисоввке независимо -- "вписываясь" в свои [mindisp,maxdisp].
    • "Как отправлять канал на график" -- да просто, нажатием Shift+Btn3 (в баню DnD! :-).
    • Канал добавляется в конец списка, если все места заняты -- то первый выкидывается.
    • При добавлении окно вытаскивается наверх и де-иконифицируется.
Махинации с "колоризацией" и прочими статусами отображаемых каналов "на лету"
  • 12.04.2004: Вот:

    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: в продолжение тех мыслей:

    • Ввести server-side-флаг LOCKED.
    • А за компанию -- CDA_FLAG_LOCALLOCKED, для блокирования изменений в границах самой программы (вспомнить бы еще, зачем я его придумал -- ведь какие-то идеи использования были!).

    09.02.2006: ну ладно, CXRF_LOCKED -- а почему не CXRF_READONLY?

    19.08.2007: собственно -- а ведь имеющаяся сейчас (с 26-02-2007) в alarmonoffled фича "victim=" уже решает проблему с обес-sensitive'ньем, чего для >90% реальных случаев -- типа гусиного запрета изменений -- вполне хватит. Хотя, конечно, DIM красивее...

В cxsd drivers API: междрайверное взаимодействие

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

Современная модель -- делается "большой драйвер", который включает в себя код из драйверов всех задействованных блоков, и отдает "наружу" только несколько каналов для управления собой, а каналы тех блоков -- прячет. Так (кажется) было сделано Олегом в rfsyn, аналогично поступил А.Чугуев в какой-то своей алхимии для накопителя.

ВОПРОС: а не надо ль ввести в сервер некую инфраструктуру, чтобы драйверы могли переговариваться/договариваться сами?

"ПРОТИВ" этой идеи:

  1. Драйверы уже сейчас могут договариваться стандартными средствами Unix -- тот же Чугуев что-то такое изображал.
  2. Что именно за инфраструктуру делать -- непонятно. Пока только общие пожелания -- "сделайте мне красиво".

А не понадобится ли вводить совсем-совсем-"метасредства" типа callback'ов по обновлению значений в каналах "подчиненных" драйверов, запрета на запись в подчиненные каналы клиентами, и т.д. И вообще это скорее тянет на "сверхобщую" инфраструктуру типа EPICS'ной.

"Красивая" работа с УШД

10.05.2004: у Гусева "ручная" работа с УШД сделана очень элегантно: стрелки "идти" совмещены с концевиками.

[<-][шаги][->]
Т.е., когда доходит до концевика, то красным подсвечивается стрелка ходьбы в ту сторону. И это правильно -- ведь в ту сторону идти больше незачем. (Но она не disable'ится -- может, и незачем, но возможность идти туда остается.)

ВОПРОС: можно ли подобное сделать в рамках CX/Chl? Да, можно -- есть ведь и подсветка, и "протокол кнопок", и формульные вычисления, позволяющие подшаманить любой нужный набор битов.

ЕСТЬ ПРОБЛЕМА: вставить такой "блок управления УШД" в обычный элемент чаще всего не получится -- шибко много колонок занимает (еще ведь кнопка [СТОП], и еще кумулятивный счетчик шагов...). Т.е. -- специальный элемент "управление десятком УШД" сделать не проблема, но возникает-то необходимость вставлять такой "блок" в ОДНУ колонку.

Вариант решения 1: изготовить специальный Knob-type -- "УШД", который из 32 бит извлекает биты статуса и собственно число шагов, и имеет надлежащее поведение с отдачей команд и покраснением. ПРОБЛЕМА: во-первых, громоздко, а во-вторых -- упихивать всю информацию в 32 бита (которые к тому же передаются через double) -- мерзкое занятие (еще ведь надо будет ему должную хитрую формулу назначать).

ВАРИАНТ РЕШЕНИЯ 2: ход песцом: а что, если разрешить "вложенные элементы"? Т.е., чтобы в клетку для Knob'а впихивалась очень специальная сущность, представляющая собой сетку с каналами (W*H, обычно H=1), которая бы содержала ТОЛЬКО каналы (без меток и, наверное, без сепараторов). Сделать-то сетку -- не проблема, но КЛЮЧЕВОЙ ВОПРОС: как обеспечивать такой "вложенный элемент" связью-с-данными? Есть два варианта:

  1. выдавать ему свой собственный ElemInfo, со своим content (тогда этот content надо как-то регистрировать, причем правильнее всего -- в Cdr);
  2. позволить "потреблять" нужное число каналов из "родительского" content'а.

    А вот это -- сплошная проблема. Во-первых, тогда перестанут работать тривиальные вычисления типа "где находится [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 вообще НИГДЕ не упоминается.
  • На LOGT_WRITE{1,M} проверка действительно делается -- в определении ki->is_rw. И это ВСЕ!
  • Есть еще проверки на тему LOGT_{DEVN,MINMAX} -- на предмет специфичной работы с ними.
  • Современный вариант staromakh.c вообще ничего не волнует -- он написан на чистой cda, и просто сам смотрит на .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'ом.

Недоделанности:

  • Пока что (в первом приближении) надо ОБЯЗАТЕЛЬНО указывать опцию элемента "noshadow".
  • Еще из неприятностей: содержимое вложенных элементов не идет inline с остальным -- из-за того, что у всех клеток стоят margin'ы по 1 пикселу, и в результате у вложенных элементов сдвиг на этот самый пиксел в начале и в конце. Вот если бы вместо margin'ов ("cellpadding") можно было указывать размер сепараторов ("cellspacing")...
  • Как оказалось, метод ShowAlarm() делает несколько не то, что надо -- половина работы зачем-то выполняется прямо изнутри Knobs (махинации с flipflop), а метод -- только подсвечивает (явно рудимент годовалой давности, после деления oldChl на Chl и Knobs).

Как бы то ни было, начальный вариант вложенных элементов работает!!!

Итого: на 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'ем спасала, что ли...

Пофиксил.

"Интеллектуальное" расцвечивание
  • 31.07.2004 (реально -- мысль зрела давно): как бы этак иметь возможность делать колоризацию knob'а не на основе своего значения, а на основе какого-нибудь другого канала?

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

    Возможно, удастся сделать нечто подобное, введя дополнительное поле -- colformula, которое также будет рассматриваться как read-formula, и все колоризационные сравнения будут делаться с ее значением; а если оно ==NULL -- то с обычным значением knob'а.

    19.04.2005: йоу-йоу!!! Итак: в CXv4 понятие "colformula" будет сразу. А в нынешнем CX -- появилась идея, как это можно сделать, см. раздел "colformula".

    20.04.2005: сделал, "внешняя колоризация" изготовлена и проверена, работает успешно.

Перемещение physinfo в БД/сервер

10.08.2004: возник в голове некий план-проект плавного ухода от размещения physinfo в клиентах. (Последней каплей стала потребность Феди-jr. как-то указывать physinfo в своей программе, а ему для этого пришлось #include всякого из chlclients/DB/.)

Итак, исходные данные:

  • Уже сейчас отсутствует "полная картина": например, если одним сервером пользуются несколько клиентов (как v1000+magcorr), то physinfo по ним размазано, и в виде ОДНОГО массива оно не существует.
  • Нет возможности оператору или ему подобному ОПЕРАТИВНО внести изменения в калибровки, а ведь может понадобиться!

Мысли по реализации:

Получение в сервер
Промежуточный "хак": перечислять строки physinfo в blklist.lst, после списка блоков. В простейшем (и, видимо, вполне достаточном до реальной БД варианте) -- строчки вида
:NUMBER n r [d [q]]

Наверное, разумно будет дать возможность указать n как "first-last" -- чтобы присваивать physinfo сразу группе каналов.

Возникает вопрос: а как насчет иметь возможность r и d указывать не готовым числом, а выражением? Сейчас такое активно используется. И даже более того: такой же вопрос останется и при переходе на реальную БД -- числа удобнее будет указывать не double'ами, а именно строками-формулами.

Хранение в сервере
На каждый канал дополнительно отводится по несколько полей, которые и заполняются при чтении blklist/БД, и отдаются клиенту по запросу:
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()'а.

В общем, у обоих подходов есть и плюсы, и вполне преодолимые минусы.

Интерфейс с клиентом
Вводим дополнительный "класс fork'ов" -- '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". Это автоматически обеспечивает разумные значения по умолчанию.

Путешествие калибровок от cxlib к cda
Вообще-то имеется дилемма: можно делать операцию получения калибровок синхронной, а можно -- нотификационной. С синхронной -- все понятно. А в случае нотификационной -- можно иметь интерфейс по образу и подобию findfirst+findnext/opendir+readdir. Т.е., вводится дополнительный код нотификации -- CAR_DBASE, и две функции:
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 будет меняться соответственно, так? Посему надо будет

  • иметь API-вызов, чтобы драйвер мог указывать новое значение q;
  • какой-нибудь ("нотификационный") способ уведомления клиента об этих изменениях. Как вариант -- отсылать такой 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.

"Украсивливание" связки Cdr/Knobs, переход в расцвечивании и поведении на флаги

16.08.2004: насчет поведения: вводим поле "behaviour", а в нем -- флаги

  • IS_ALARM -- бибикать при !=0;
  • IS_BUTTON -- срезать OTHEROP;
  • IS_LIGHT -- подсвечиваться.

Нынешний LOGD_ALARM -- это IS_ALARM+IS_LIGHT.

Возникает вопрос: а как насчет иметь кнопки, которые могут и disable'иться, и краснеть (как гусиные для концевиков)? Видимо, надо иметь (формализованным!) примерно такой протокол:

  • маска 1 -- краснеть при IS_LIGHT, плюс бибикать при IS_ALARM;
  • маска 2 -- disable'иться.
(вообще-то, при отсутствующем IS_BUTTON можно как краснение/бибиканье трактовать не только 1, а вообще !=0.

18.08.2004: кстати, а как насчет ввести дополнительный цвет -- как гусино-epics'овый "белый" -- что данные еще не пришли. Частично это смахивает на нынешний "цвет гусиной кожи" -- как бы этак их скоррелировать?

20.08.2004: ага... а не ввести ли сразу флаг CDR_FLAG_LIT, для сигнализации включенных "лампочек"?

И вообще: давно пора CDR_FLAG_ALARM_* перевести с битовых флагов на битовое поле -- они ж все равно могут быть только по очереди, вот и сделаем {NONE=0,RELAX=1,YELLOW=2,RED=3}. Тем самым и проверки упрощаем, и экономим один бит (а если понадобится больше состояний -- добавим еще один бит). 16.06.2006: ага, хрен, а не битовое поле! С битовым полем пришлось бы для "общего статуса" делать хитрые сравнения этих битовых полей на тему "кто больше/серезнее", так что идея "битоых полей" давно похоронена.

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 :

  1. У нее и предыдущее значение есть, для сравнения.
  2. И доступ к ->ShowAlarm().
  3. И тогда это будет и с любыми виджетами работать, у которых 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: ночью посидел/подумал -- вообще-то можно/стоит сделать именно сейчас, просто надо, чтобы:

  1. Параметр attn_time сбрасывался ТОЛЬКО
    1. В момент прихода данных -- если интервал истек, в CdrProcessKnobs() (а сейчас туда суют свой нос и ChooseColorState(), и ChooseStateRflags() -- что совсем не их дело!);
    2. В SetAttnState() при onoff==0.
    И на тему истечения промежутка с момента включения состояния chooser'ы проверять не должны, не их это собачье дело -- раз включен attn-режим, значит надо на него и ориентироваться.
  2. ChooseStateRflags() НЕ должно само вызывать (НЕ ЕЕ ЭТО ДЕЛО!):
    • ни SetOtherop() -- флаг OTHEROP все равно меняется только по приходу данных либо по уставке значения от пользователя, каковые точки четко локализованы в коде,
    • ни SetAlarm() -- реально момент, когда параметр new_v!=ki->curv есть в одном месте, также по получению значения

    Т.е. ChooseColorState() и ChooseStateRflags() -- пусть будут "чисто математическими", ВЫЧИСЛЯЮЩИМИ, а не "меняющими состояние".

  3. SetAttnState() же также пусть ТОЛЬКО УСТАВЛЯЕТ параметры ki->attn_*, но НЕ вызывает SetColState().
  4. А соответствующую смену состояния -- последовательные ChooseStateRflags(), ChooseColState(), SetColState() -- нехай исполняют те точки ("наивысшие" в цепочке/стеке), откуда произведен вызов, потенциально меняющий состояние. Так взаимозависимость/потенциальная "зацикленность" и устраняются.
(Короче -- чтоб КАЖДОЕ ЗАНИМАЛОСЬ СВОИМ ДЕЛОМ, а не все всем скопом, чтобы была ИНКАПСУЛЯЦИЯ!)

03.02.2006: начал реализовывать вышеуказанный проект. На сегодня успел исполнить только первый пункт -- про attn_time, а то уж сильно спать охота :-)

06.02.2006: продолжаем пьянку.

А зачем, собственно, у нас есть attn_time+attn_period? Ведь достаточно-то иметь просто время, когда attn-срок ЗАКОНЧИТСЯ, ибо момент начала -- никого не интересует. Тогда экономится и лишнее сложение, и лишнее поле в каждом knob'е. Вообще-то, это рудимент старой системы -- когда был не "attn", а "relax", а потом еще и происходил переход на relax+otherop. Потому:

  • В Knobs_typesP.h::knobinfo_t: attn_time переименовано в attn_endtime, а attn_period удалено вовсе.
  • В Knobs_internals.c::SetControlValue() это отражено.
  • В Cdr.c -- надлежащим образом подпеределана вся арифметика.

По делу:

  • В ChooseStateRflags() оставлено только выяснение и уставление текущего состояния флажка CDR_FLAG_ALARM_ALARM, а определение изменения его значения и соответствующий вызов SetAlarm() переложены на CdrProcessKnobs().
  • Аналогично с OTHEROP -- перетащен вызов SetOtherop().

    И, поскольку есть ЕЩЕ точки, где меняется (точнее -- сбрасывается) состояние этого флага -- SetControlValue() и CdrSetKnobValue() то и туда вставлено SetColstate(,ChooseColorState(...),...).

  • Вопрос, собственно -- а почему этих точек ДВЕ?!

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

07.02.2006: причесал "остатки" -- неиспользуемые параметры и переменные в функциях (поскольку все стало красивее, и потребность в "лишних сущностях" подуменьшилась).

Идеология: конфигурация для "косвенных сущностей"
  • 27.10.2004: назрело-то давно, а сегодня решил тут записать...

    Есть такие ситуации, когда в config-файле указываются некие сущности (драйверы в сервере, подсистемы в cx-starter'е), которые, в свою очередь, ссылаются на другие сущности (layer'ы и серверы, соответственно).

    И при "первом использовании" эти "косвенные сущности" надо инициализировать. А при инициализации им нехило бы передавать некие конфигурационные параметры -- какой-нибудь baud_rate для CAN-layer'а, ключики (-w0) для cx-сервера, и т.д.

    НО: сейчас-то config-файлы содержат только список и параметры "первичных сущностей", а при них указывать свойства для вторичных -- некорректно! (Некорректно потому, что ссылающихся на "косвенные" может быть много, и кто из них окажется тем, из-за кого layer/сервер будет "инициализирован" -- заранее непонятно. Не дублировать же во всех...)

    Так что, проблему надо как-то решать...

    27.10.2004: Напрашиваются два варианта:

    1. Иметь отдельный файл, в коем перечислены косвенные сущности и их параметры. При ссылке на такую сущность программа лезет в файл-БД, и там находит искомое. Такой подход используется в insmod -- /etc/modules.conf.
    2. Впихивать эту информацию прямо в основной config-файл каким-нибудь специальным образом -- например, в строки вида
      .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 вставлено, что оно пытается подхватывать этот файл, если он есть.

Вставка больших каналов в элемент
  • 06.12.2004: призадумался о том, что и как надо будет сделать для достижения такой цели. Получился примерно такой список:
    • Требуются некие стандартизованные "отображаторы" -- нечто вроде нынешних Knobs, но для больших каналов. (Для начала-то делать просто так, "по месту", для конкретной программы, а потом -- свести в библиотеку.)

      Еще вопрос -- как называть-то библиотеку будем? :-)

    • Воизбежание холостых вызовов/перерисовок, надо в cda иметь для каждого большого канала счетчик, увеличивающийся с каждым приходом данных, и в "bigc_knobinfo" -- тоже, и при != -- производить перерисовку.
Введение очереди с таймаутами в CAN-драйверы
  • 11.12.2004: примерно сформулировал требования к этому механизму:
    1. Это должен быть массив на N элементов, где N задается драйвером конкретного устройства при инициализации (у ЦАП -- один размер, а АЦП -- другой, и т.д.), а размер элемента -- стандартный, определяется максимальным размером kozak-can-пакета.
    2. Операцию пере-отправки пакета при истечении таймаута выполняет сама очередь (точнее -- layer), без помощи драйвера.
    3. Также нужна операция "пробуй отправить следующий пакет" -- это когда на что-то пришел ответ.
    4. Должна быть операция "проверить, есть ли такой-то пакет в очереди" (проверка выполняется указываемой функцией, ибо просто побайтного сравнения может быть мало). Используется, когда приходит повторный запрос на некую махинацию.
    5. Плюс, операция "удалить такой-то пакет из очереди" -- это когда приходит адекватный ответ, и пакет запроса становится ненужным.
    6. Похоже, две предыдущих вещи можно унифицировать -- сделать итератором, которому указывается, что именно делать с пакетом, если он найден: то ли вернуть "да, нашел!", то ли грохнуть пакет и искать дальше.
    7. Требуется также операция "очистить очередь" -- при получении от устройства пакета "Я -- Вася Пупкин!". При этом, естественно, надо сразу же заказывать чтение всех каналов, т.о., очередь тут же и заполнится.
    8. Все это вполне может быть layer'онезависимым -- некая доп. библиотечка, общая для всех kozak-can-layer'ов.

    В принципе -- вот и готовое ТЗ, можно реализовывать.

  • 12.12.2004: начал реализовывать.

    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):
      1. QFE_FIND_FIRST -- искать первый попавшийся;
      2. QFE_FIND_LAST -- искать последний (после чего можно определить, первый ли он в очереди, т.е., был ли уже послан этот пакет устройству);
      3. QFE_ERASE_FIRST -- удалить первый попавшийся;
      4. QFE_ERASE_ALL -- удалить все такие.
      Причем он возвращает не просто да/нет, а более тонко:
      1. QFE_NOTFOUND -- нэма.
      2. QFE_FOUND -- найдено, но не первым.
      3. 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" :-)

  • 06.06.2005: возможные дальнейшие усовершенствования механизма очередей (в первую очередь -- CAN'овских):
    • Request-specific timeout: если заранее известно, что ответ придет СОВСЕМ не мгновенно (например, команда калибровки козаковского блока занимает около секунды, в течение которых не имеет смысла делать что-то еще -- это сюда?). В смысле -- чтобы если некое поле в canqelem_t будет !=0, то уставлять ЭТОТ таймаут вместо стандартных 100мс.
    • Дальнейшее развитие предыдущего: а также некое поле "принудительная задержка" -- чтобы пока оная с момента отправки пакета не пройдет, ничего не предпринимать (аналог sea_step_t.chkdelay в seqexecauto).

      Например, можно ее указывать отрицательным значением поля "req-specific timeout".

    • Возможность указывать некий callback, вызываемый в момент отправки пакета из очереди, который бы инициировал некоторые действия (ставил бы блокировки, etc. -- "начало транзакции").

    Эти мыслишки появились после обсуждения с Гусевым устройства ЕГО варианта работы с CAN-bus'ом (ключевое слово -- "транзакция").

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

"Тени"
  • 20.12.2004: собственно, первый раз упомянут термин "тень", и осознана эта концепция.

    20.12.2004: произошло сие во время разговора с Гусевым, около 11:30, так, между делом.

    Речь зашла о разных alarm'ах, как их надлежит отображать. Гусев выступал в том смысле, что после исчезновения alarm'а им надо еще несколько секунд светить -- чтобы оператор понял, что же бибикало. В ответ было сказано, что у меня и так соответствующая лампочка подсвечивается зеленым. Женя же стал возражать на тему о нескольких "уровнях" (severity) alarm'ов. Вот тут-то и вылезла идея:

    После изчезновения alarm'а надо показывать его ТЕНЬ.

    Причем для каждого состояния alarm'а тень -- СВОЯ.

    (Корректнее было бы назвать это не "тень", а "след" (trace) -- то, что остается после того, как сам объект исчез. Но, похоже, я обчитался «Хроник Амбера» :-)

    И -- тени бывают не только у alarm'ов, это более общее явление. Например -- у графиков/гистограмм, когда в дополнение к текущей картинке более бледно показывается еще и предыдущая.

  • 21.05.2007: Зюбин в своем отзыве на диссертацию подал идею: в качестве тени (или в дополнение к ней) можно отображать не предыдущее значение, а МАКСИМУМ за последние N циклов, чтобы оператор, пропустив оное, мог успеть увидеть его тень.
О работе с памятью
  • 20.12.2004: это результат отчаянных попыхов с реализацией "продвинутых" механизмов работы с памятью. После этих попыхов на меня снизошло просветление, и стало окончательно ясно, как правильно поступать в тех случаях.

    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, на такой механизм.

    • Если имеется FIFO из элементов фиксированного размера (например, очередь в marcan-layer'ах, или история в knob'ах), то надо это делать не буфером с постоянным сдвигом на 1 элемент при удалении элементов, а кольцом.

      Для реализации подобного кольцевого буфера требуется следующее:

      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) повсеместно забито.

    Реализация -- обычно пара функций GetZZZSlot()/RlsZZZSlot(), плюс сервисная (для Get()'а) FindFreeZZZSlot() (ключевое слово -- "slot"). В роли "ZZZ":

    • V4conn -- cxsd_fe_cx.c, cx_client.c (разные стороны CX-соединения).
    • Srvconn -- cda_api.c.

    А затем и в других местах:

    • В cdrclient.c -- для складирования команд для будущего (возможно, периодического) исполнения (ZZZ=Cmd).

      Проверить бы по архивам, не отсюда ль всё пошло...

    • Аналогичная схема использована в fdiolib.c для массива watched (хоть и без формализации функций, но 02-08-2011 поиск был вытащен в find_free_watched_slot()).
    • А в fastadc_data.c (ZZZ=Cblist) оно даже было 25-04-2011 доведено до уровня макрос-библиотеки DEFINE_SLOT_FUNCS(), генерящей GetCblistSlot()/RlsCblistSlot(). Причём там весьма продвинуто -- и "storage class" указывается (auto/static), и возможность манипуляции не только со статическими переменными, но и с полями структуры (указывается через троицу параметров obj_pfx, obj_ref, obj_arg...).

      Короче -- практически готовый код для помещения в misc_macros.h рядышком с GENERIC_FD_*().

    • В fastadc_knobplugin.c -- полная копия, только без макроса.
  • 08.06.2005: идея насчет того, как устроить в fdiolib'е массив структур-описателей. (Реально -- не только в fdiolib'е, а в любой библиотеке с индексацией по файловым дескрипторам.)

    08.06.2005: исходная предпосылка: размер структуры в принципе немаленький -- >=100 байт, а если FD_SETSIZE==1024, то получается, что 100Кб тратятся практически "за просто так".

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

    Т.е. -- заводится один массив int'ов -- clnidx[FD_SETSIZE], и -- ПУЛ структур, растущий по мере надобности при помощи GrowBuf().

    Overhead будет минимальный: в начале каждой функции вместо

    cp = clients + fd;
    будет
    cp = clients + clnidx[fd];
    -- можно эту махинацию даже выносить в макрос наподобие "fd2cp(fd)".

    Плюс, естественно, механизм выделения ячейки -- отрабатывающий только один раз, при "заведении" клиента. Либо находится свободная ячейка, либо пул увеличивается.

    Дополнительные мысли:

    • Если жалко тратить статически 4Кб на clnidx[], то можно и этот массив сделать динамическим -- в конце концов, чаще всего дескрипторы лежат даже ниже 100, так что можно при добавлении клиента устраивать так, чтобы массив становился не меньше fd, и при его росте для порядку забивать новорожденные позиции -1.
    • И, конечно, надо помнить, что при ЛЮБОЙ махинации, могущей затронуть (читай -- реаллокировать) пул, надо ОБЯЗАТЕЛЬНО заново инициализировать cp -- как это сейчас делается с si в cda_register_formula().
    • И, кстати, подобную lookup-таблицу можно будет ввести в cxlib.c -- чтобы в 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 напрямую -- естественно, будет необходим.

  • 07.02.2012@Снежинск-каземат-11: к сожалению, способ "растущего массива слотов" не катит в случае, когда надо куда-то в другое место передавать УКАЗАТЕЛЬ либо на саму запись (например, в качестве privptr'а для callback'а), либо на её часть (например, sl_timeout_t).

    07.02.2012@Снежинск-каземат-11: состояние дел и понимания на текущий момент:

    • Потребность в таких вещах есть, и очень серьёзная -- например, некоторым драйверам (ist_xcdac20) требуется БОЛЕЕ заложенных CX_MAX_DATAREF_EVENTS_PER_DEV=10. Причём насколько более -- нелимитировано.
    • Первоначально была идея ввести "smart callbacks": чтобы туда, где будут "использоваться" такие callback'и, передавался бы не указатель privptr, а пара {указатель на объект "smart callback list", номер слота в нём}.

      И в качестве указателей вперёд/назад при организации "списков callback'ов" тоже использовались бы такие дуплеты.

      Тогда б ввели пару типов:

      1. cb_array_t -- растущий массив. Содержит текущий указатель на realloc'ируемый массив и размер элемента в нём.
      2. cb_array_ptr_t -- указатель-индекс: {cb_array_t *a, int idx}.

      (Вот зря я ТОГДА, в ноябре-декабре 2011, когда это придумал, сразу не записал!!! Попозже, ближе к вечеру: нифига -- частично записал, в 201112-SNEZHINSK-ACTIVITY.txt, "Заметки...", 20-12-2011, вечер-душ.)

    • Но это не прокатит --
      1. Оно тогда полезет в слишком большое количество сторонних библиотек (как внутри-cx'ных, так и просто внешних).
      2. Иногда надо РЕАЛЬНО предоставить другой библиотеке некий кусок памяти -- например, sl_timeout_t для cxscheduler'а.
    • Т.е., идея-то была недурна -- фактически, перейти от "просто pointer'ов" к "smart-объектам"; ну, или так -- от указателей к идентификаторам.

      Но -- увы, облом...

    Задача выглядит принципиально неразрешимой -- вообще никакой ценой.

    07.02.2012@Снежинск-вечер-в-УАЗике-буханке-с-полигона: (нас забыли, и УАЗ прислали ближе к 8 вечера) сейчас для решения конкретной потребности ist_xcdac20 можно поступить тупо и просто -- увеличить CX_MAX_DATAREF_EVENTS_PER_DEV с 10 до 100.

    Некрасиво, но зато мгновенное решение, для которого и делать-то ничего не надо.

    07.02.2012@Снежинск-Снежинка-305-душ-вечер: придумалось решение. Исходные данные у нас таковы:

    1. Размер массива заранее не известен, и может быть как 0, так и 1000+.
    2. Каждый конкретный элемент массива должен после рождения оставаться в фиксированном месте.

    Размышления:

    • На первый взгляд требования кажутся несовместимыми.
    • Затем в голову лезут мысли о хитрющей структуре, с отведением "групп" элементов (например, по 100) -- как это сделано в ext2 и подобных. Но менеджмент такой структуры -- вешалка, а адресация в ней -- жуть.
    • Совместить "несовместимое" можно просто: каждый элемент отводить malloc()'ом индивидуально, а вот массив, в котором хранятся указатели на эти элементы -- растить обычным способом.

      Тогда и каждый элемент остаётся на одном месте, и растущий массив есть. Минусом, конечно, изрядная фрагментация из-за отведения толпы мелких структурок. Зато адресация очень проста.

    08.02.2012@Снежинск-каземат-11: засим поставленную в заголовке проблему считаем идеологически решенной, так что "done".

  • 08.02.2012@Снежинск-каземат-11: а теперь конкретика насчёт общего подхода к разным вариантам таких "массивов слотов", которые хотя бы иногда хочется иметь "растущими".

    08.02.2012@Снежинск-каземат-11: по пунктам:

    • В отличие от имевшейся в fastadc_data.c реализации, функция GetZZZSlot() должна возвращать не указатель на слот, а его ИНДЕКС. При ненайденности -- -1.

      Это и используется во всех остальных применениях (4cx/ etc.), и идеологически вернее -- и высокоуровневее, да и операция "получить указатель на i-й элемент массива" корректнее, чем "получить индекс элемента из указателя на него и на массив".

      И вообще это полезнее -- т.к. совместимо с прочими подвидами "массивов слотов".

    • Можно вообще сделать гибкую схему -- несколько РАЗНЫХ вариантов GENERIC_SLOTARRAY_DEFINE():
      1. GROWING -- растущий массив (обычный).
      2. FIXED -- фиксированный массив.
      3. GROWFIXELEM -- вышеописанный хитрый растущий массив с фиксированными элементами.

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

      (Естественно, кроме выбора определителя она должна также менять и определение поля "массив" -- type *arr либо type arr[n].)

    • А GENERIC_SLOTARRAY_DECLARE() для разных вариантов будет одна -- ведь прототипы (API) должны совпадать.
    • Следствие 1: нужна еще одна функция -- AccessZZZSlot(), которая бы отдавала указатель на конкретный слот. FIXED: array+i; GROWING: array+i; GROWFIXELEM: array[i].
    • Следствие 2: также нужна функция и гроханья массива -- DestroyZZZSlotArray(), которая в GROWFIXELEM-варианте делала бы free() и каждому слоту.

      А уж задача самой программы сделать СВОЙ RlsZZZSlot() для каждого элемента. А тот, в свою очередь, должен получать УКАЗАТЕЛЬ на элемент от AccessZZZSlot(), и для отсутствующих слотов будет NULL -- ничего не делать.

    • По-хорошему, надо б макросам DEFINE_...() указывать и "максимум" -- выше какого количества массив расти не должен. Пока можно его просто не использовать. А для FIXED оно будет как раз размером массива.
    • Макросы-определители назовём так: 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*-вариантах: если все свободные помещать в список.

    Правда, не всё так тривиально:

    • Ладно еще, что при разрастании GROWFIXELEM-массива (а смысл сие имеет ТОЛЬКО для него) надо будет все новые добавлять в список, и при освобождении -- закидывать в него обратно.
    • Главное -- что выигрыш от списка будет, только если можно будет при поиске свободного сразу брать ПЕРВЫЙ элемент списка. А для этого по-хорошему придётся держать список упорядоченным -- иначе стандартные проходы по нему будут слишком долгими.
    • Идея-следствие: лучше подумать о том, чтобы иметь в дополнение к полю "allocd" также поле "max_used" -- аналогично менеджменту fd_set'ов. Поскольку последовательный перебор элементов происходит намного чаще, чем добавление/удаление (единственное исключение -- потенциальный менеджмент таймаутов, там всё по-другому).

    По-хорошему, надо вводить 3-й макрос -- для объявления ПОЛЕЙ. Например, GENERIC_SLOTARRAY_DEFINE_NNN_FIELDS(). И получится у нас по факту нечто в стиле C++-template-класса: наборчик полей -- это собственно "объект", а функции -- его методы.

    13.05.2012@Снежинск-каземат-11: первоначальная идея в предыдущем пункте -- бессмысленна (так что делаем её withdrawn), а вот замечания-следствия из неё -- однозначно заслуживают реализации.

    (Сегодня появлялась еще идейка-"улучшение" предыдущего: кроме "max_used" помнить еще и "first_used". Типа, чтоб обходиться без списка. Но эта идея для сокращения времени поиска свободного элемента бесполезна -- само поддержание "first_used" потребует больше маеты. А вот для ускорения перебора -- выглядит осмысленно; хотя, насколько "часты" ситуации, в которых набор получается с "пустотой" в начале?)

    06.01.2013: всё вышеописанное уже реализовано и используется, так что раздел "done".

    Хотя насчёт дополнительного списка свободных элементов для быстрого поиска -- да, оно так сделано в новом cxscheduler для таймаутов.

  • 21.02.2012: еще пара глобальных принципов:
    1. Нельзя использовать никакие clientside-supplied structs. Надо отдавать "наверх" только handle'ы, а внутри использовать какой-нибудь из SLOTARRAY.
    2. Вместо "callback"'ов использовать evproc'ы, с дополнительным параметром reason.

    21.02.2012: по пунктам:

    1. Это по опыту маеты с большими каналами -- RemitBigcRequestsOf(), ReRequestBigcs(), ... -- в сервере.

      Основные сложности там заключались именно в том, что нижний уровень -- cxsd_bigc.c -- вынужден иметь дело со структурами, предоставленными более высокими уровнями (cxsd_drvmgr/cxsd_driver). И:

      1. Те в этих структурах держат свои данные, так что их нельзя просто bzero() когда захочется, да еще и связывание в списки (поля next/prev) в них же, что отдельно доставляет.
      2. Из-за такой "совместной" ответственности за структуру, ни один из уровней не может целиком ею управлять, и оба приспосабливаются друг к дружке.

      Если бы между уровнями передавались только handle'ы -- никаких бы проблем не было.

      Кстати, аналогичный прикол и с devstat_evproc'ами -- из-за clientside-supplied structs там даже деактивацию по DRVS_OFFLINE делает не CallDevsOnchgCallbacks(), как надо бы, а inserver_devs_callback().

      Отдельный вопрос: а что в свете этого делать с cxscheduler'овыми таймаутами? 06.01.2013: сделано -- от них избавлено в пользу sl_tid_t.

    2. Помимо собственно "прихода данных" бывают также и иные события -- всякая там смена статуса. И для них приходится вводить отдельные "callback"'и -- как on_stop {chan,bigc}_cbitem'ах. А при evproc с параметром reason -- достаточно добавить еще один код причины.

      (И ведь было же именно так сделано в cxlib'е с самого начала! И fdiolib такой же.)

Будущая "версия CX-3.0"
  • 16.01.2005: потихоньку, эволюционно накапливаются некоторые изменения и потребности, которые в сумме вполне могут привести к "версии 3.0" (как из X10 появилась X11).

    16.01.2005: эта "новая версия" вполне может быть и не вполне совместима с текущей, причем эта "революционность" будет не столько вынужденной, сколько "самополучившейся" -- если новые фичи просто вытеснят старые.

    На данный момент мы имеем следующее:

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

      Здесь, кстати, за компанию вылезет напрашивающееся "слияние" понятий type/LOGT_* и kind/LOGK_*. Плюс, "повсеместность" поля options.

      И, так, между делом -- можно будет иметь просто внеэлементные knob'ы, которые прямо в "корневом списке". (Да и вообще, наверное, исчезнут многие ограничения и появится много разных возможностей.)

    • Можно будет вставлять большие каналы прямо в элемент.
    • Появится поддержка самописцев.
    • Будет несколько улучшен прикладной протокол между клиентом и сервером:
      1. можно будет смешивать обычные и bigc-запросы в одной посылке;
      2. обнормалится работа с большими каналами -- введем "параметры представления" и отдельные команды установки/чтения отдельных параметров больших каналов (так же, как сейчас читаются/пишутся обычные каналы);
      3. да и вообще напрашивается возможность любые -- и обычные, и большие -- каналы заказывать разными способами: и раз в цикл, и с указанной частотой, и по готовности, и т.д. (А еще "callback'и" и/или "alarm'ы" -- по приходу данных от драйвера, по изменению в каких-то пределах, и т.д.).
    • Кроме того, есть планы ввести некоторое разграничение доступа -- например, режим "readonly" (см. на эту тему bigfile.html).
    • А вот низкоуровневый (транспортный) протокол показал свою адекватность. С давно известным мелким замечанием -- что endian conversion надлежит делать в сервере (под что уже сейчас реально все и заточено).

    В общем -- все эти изменения отлично станут причиной/основой появления "CX version 3". В нынешней архитектуре -- в принципе, все достаточно адекватно, и надо в конце концов на ней защитить кандидатскую. (А v3 отлично потянет на докторскую.)

  • 16.01.2005: Поконкретнее насчет "слияния" type/LOGT_* и kind/LOGK_* -- как бы это можно было организовать?

    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 удалено.

  • 16.01.2005: Так как насчет помещения всей информации об элементе -- нынешнего содержимого elemnet_t/eleminfo_t -- в "стандартизованный инод", каковым будет выступать logchannet_t/knobinfo_t?

    А можно ли и саму "подсистему" положить в "стандартизованный инод"? И что насчет такого эфемерного понятия, как "группировка"?

    16.01.2005: С группировкой в общем-то все довольно просто -- ведь группировка является некоторым количеством последовательных элементов, т.е. -- считай, что в точности те же данные, что и для "элемента"/"подэлемента" (каковым и будет подсистема), с отличием, что используется "количество", а не NULL-terminated. Единственное -- поле fromnewline, но уж с ним-то разберемся -- например, можно упхать его в options.

    17.01.2005: с полем options надо поступать так: ВЕЗДЕ (и для ручек, и для элементов, и для подсистем) парсить его при помощи ОДНОЙ и той же таблицы, а ненужные поля просто игнорировать.

    18.01.2005: ну что -- просто берем и начинаем смотреть, каких полей элемента и подсистемы не хватает в нынешнем knobinfo_t, а что, наоборот, хорошо ложится на имеющееся.

    • По элементам:
      • ident, label -- великолепно совпадают. Вообще универсальные вещи.
      • options -- аналогично.
      • colnames, rownames -- отсутствуют.
      • type->look, однозначно.
      • count, ncols -- йок.
      • channels/content -- то же место, которое сейчас занимает formula. Вообще, кстати, стоит переименовать formula+revformula в dirsrc/revsrc (direct/reverse).
    • По подсистемам:
      • sysname->ident.
      • win_title->label (а еще есть comment :-).
      • options -- как есть.
      • defserver -- кю...
      • magicnumber,version -- вообще относятся не к описателю системы, а к совсем другому -- к символу-дескриптору-в-файле.
      • phys_info,phys_info_count -- с появлением БД вообще исчезнут.
      • grouping->formula/dirsrc.
      • clientprog -- реально не используется; запхать в revdatasrc?
      • icon, app_name, app_class -- эти "иксизмы/мотифизмы" не ложатся никуда и никак.
      • kind -- а что это вообще такое? Откуда оно взялось? Появилось в районе 02.2004, но почему, к чему, и зачем? В любом случае -- под него место найдется.

      Явно просматривается пара мыслей:

      • Во-первых, а не вытащить ли все-таки часть информации в совершенно отдельную от "обобщенного инода" структуру "описатель подсистемы" -- app_name, app_class, icon, clientprog (а на первое время -- и phys_info+phys_info_count).

        А "корнем" сделать обычный элемент, введя в дополнение к типам MULTICOL и SINGLECOL еще "ROWCOL", который станет раскладывать свои подсодержимые так, как это сейчас делает группировка.

      • Во-вторых, вообще-то уже давно стоит вопрос -- а как бы этак иметь в рамках одной программы несколько серверов, НЕ прибегая при этом к формулам и CMD_GETP_BYNAME_I().

        Так вот -- а не сделать ли все-таки defserver полем элемента, чтобы каждый элемент мог иметь свой сервер по умолчанию, а если это поле -- NULL, то уже тогда наследовать его от "parent'а"?

    19.01.2005: Итак, что мы видим -- многие вещи отлично унифицируются, и они ДОЛЖНЫ быть унифицированы (ident, label, options), но некоторые -- не очень-то. Так что у нас три пути:

    1. Втихушку использовать "стандартные" поля knob'а под что попало -- как сейчас ссылка на подэлемент лежит в поле formula. Криво!
    2. Наделать энное количество #define'ов для alias'енья таких случаев. На подобном можно огрести проблем.
    3. Сделать часть "унифицированного инода" union'ом (как это сделано в ядре Linux в fs.h::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: кста-а-ати... Элементарная логика подсказывает, что в CXv4 избавляться от концепции "logNNNnet_t" нельзя -- наоборот, ее надо подразвить и обудобить!

    15.04.2005: Первоначально я рассматривал сие как временное явление (мда, оно так временно года с 1997-го... :-), пока не будет БД, из коей и должны читаться все описания.

    Но потом появились Chl-, но не-simple-программы. А уж они НИКАК не должны быть привязаны к БД -- все описания их содержимого должны иметься прямо в них самих.

    Так что -- надо будет

    1. Придумать удобную структуру, как все указывать в тексте, причем гибко, а не так, как сейчас китайская запись из определения массивов, структур, etc. (да еще и со зверскими gcc'шными заклинаниями для inline-указания вложенностей).
    2. Потребуется также компилятор формул -- чтобы их записывать сразу в человеческом виде, без польской нотации.
    3. И каналы указывать сразу именами (хоть "linac1:9!34", хоть "vac1.14", хоть -- аналог нынешнего -- "117").
  • 15.04.2005: кстати -- а ведь кроме LOGT_{SUB*,NOOP,KNOB,BIGC} могут быть еще и ALARM (где-то в bigfile.html за 2003 было) и -- возможно -- еще какие-то компоненты, например, таймеры (как в LabView).

    15.04.2005: что до alarm'ов -- то с ними пока вообще ничего не понятно.

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

    04.12.2005: стало окончательно ясно, что сей раздел в том виде, который обозначен в заголовке -- стоит закрыть. Посему -- "widthdrawn".

    А "продолжение" -- в разделе от 01-06-2005 "не потребуются ли нам "невидимые объекты" -- как в LabView таймеры..."

  • 16.04.2005: Yes!!! А еще надо иметь специальный LOGT_USER. Такой же "оконечный" элемент, как и KNOB, BIGC и NOOP. Это будет означать -- что будет отыскан указанный в look плагин, но больше ничего делаться не будет -- этот плагин сам набъет нужного содержимого.

    Такое нужно для всяких не-simple/не-БД-программ, типа impacis10, istcc и прочих phm-*.

    09.05.2006: возникает только небольшой "вопрос" (вопросец возник при создании linbpm) -- а как указывать этим LOGT_USER каналы, с которыми им надлежит работать?

    Ведь частенько эти LOGT_USER просто берут и отображают данные с НЕСКОЛЬКИХ каналов -- например, из каналов chanX и chanY они рисуют двумерную "картинку" поперечного положения пучка.

    • В linbpm пришлось самим плагинам обладать "знанием" о том, к каким каналам им обращаться. Некрасиво!!!
    • Можно указывать ссылку на канал(ы) в widdepinfo/options, но так тоже криво -- это текстовый параметр!
    • А ведь если найти способ корректно указывать номера каналов, то и cx-starter сможет нормально следить за такими подсистемами...
    • Можно, конечно, делать такие "LOGT_USER" реально LOGT_SUBELEM'ами -- тогда просто указывать нужное количество отдельных каналов-knob'ов, как сейчас с vacclient'ом; и cx-starter'у будет хорошо.

      Но, опять же -- криво это, неудобно (каково плагину шариться по своим под-knob'ам!), да еще и чревато ошибками (а вдруг не ту структуру вложенных сделаешь -- и ведь фиг разберешься, и Cdr ничем помочь не сможет, не проверит!).

    • Так что правильным вариантом выглядит, чтобы LOGT_USER'ам мог указываться массив каналов (и, возможно, еще и массив больших каналов), и уж Cdr сама пусть занимается "добычей" этих данных, а компонент -- только отрисовкой.

      Да, и для возможности проверки -- в описании компонента пусть наличествует указание, сколько каналов ему нужно ([min..max]).

    10.05.2006: угу, а только это... а как с формулами? Хотелось ведь бы, чтоб можно было указывать не только обычные каналы, но и формулы. И?

    А что, если вообще под-уровнять формулы с каналами? Т.е.:

    • В "исходных текстах" каналов будет всего ТРИ поля, а не четыре -- "источник", "как писать" и "как колоризовать", и уж по внешнему виду отличать, формула это или один канал.
    • Регистрация формулы также будет возвращать cda_objhandle_t (а не excmd_t).
    • И, например, старший бит cda_objhandle_t будет указывать "вид" -- канал/формула.
    • А далее API, различающий "канал/формула" перекладывается с Cdr на cda.
    • В частности, "destroy" будет сам смотреть, что ему надобно сделать -- вычеркнуть канал, либо сделать free() формуле.
    • (BTW, а не ввести ли все-таки reference-counting каналам?)
    • Останется только некоторый вопрос -- а КТО и КАК будет собственно вызывать исполнение формулы?

    02.08.2006: да, надо еще в дополнение к собственно значениям не забыть о флагах и тэгах -- они тоже необходимы. (Правильно, минимум/максимум и прочие алармы "поддерживаться" не будут, ибо бессмысленно -- это уж дело компонента, как именно он с данными будет обходиться.)

    02.08.2006: @пляж идейка, как можно было бы прямо сейчас эмулировать LOGT_USER-knob'ы с указанием им каналов: делать ЭЛЕМЕНТЫ, содержащие некоторое количество каналов, а оно уж пусть оттуда берет. Бе-е-е, полный повтор идеи от 09-05-2006 :-)

    09.04.2007: а ведь идейка эта оказалась очень полезной:

    1. Именно так реализован текущий вариант ipp -- и управление вводом датчиков -- [*], и параметры M (чувствительность) и P (задержка) сделаны именно как просто ручки в элементе.
    2. А в ndbp это позволило пойти еще дальше -- такие параметры-каналы сохраняются и восстанавливаются при махинациях с режимами наравне с обычными каналами. Вот это -- ОГРОМНОЕ достоинство!

    14.06.2007: в продолжение идеи "под-уровнять формулы с каналами": да, надо и можно, и несложно. Highlights:

    • Предпосылки:
      1. Определения что каналов, что формул будут текстовыми (и реально на обычные knob'ы этих вещей будет ТРИ -- read,write,colorization).
      2. Формулы смогут быть разными -- cda/formula, tcl, etc. -- плагиновыми.
    • Следствие: указания подобных объектов ВСЕГДА будут текстовыми.
    • Вывод: надо эти вещи унифицировать, введя, например, специальный тип-запись "описание объекта", состоящий из 2 полей: тип объекта (канал, формула, tcl-программа) и собственно "объекта" (chanref, formula, ...).

      И иметь две функции: "getvalue()" и "setvalue()".

      И -- да, есть вопрос, кто же и когда будет вызывать именно ИСПОЛНЕНИЕ формул (поскольку если канал можно дергать много раз, просто читая его текущее значение, то ФОРМУЛУ надо исполнить однажды, в момент прихода данных).

    • Детали:
      • Поскольку libKnobs вообще-то должна быть независима от libcda, то тип-запись можно эмулировать двумя полями -- int type, void *data. 15.06.2007: неа, просто сделан общий, всеми-включабельный файл cx_common_types.h, а в нем определен тип-запись CxDataRef_t.
      • В LOGT_USER'ах, соответственно, просто заводится malloc()'ed-массив таких "записей".
      • Указывать же список требуемых LOGT_USER'у каналов можно по "технологии", описанной 04-06-2007 для "параметров knob'а".
      • Кстати, и "параметры knob'а" тут надо не забыть корректно связывать с этими универсальными-объектами.

    15.06.2007: ой ли? С введением всяких хитростей (типа параметров ручек) даже подготовка к исполнению формул может стать подороже, так что -- имеет смысл для ЧИТАЮЩИХ "объектов" выяснять, что они есть, чтобы, возможно, простые каналы спрашивать у cda более простым вызовом...

    Неа!!! Все и так очень несложно -- вопрос оптимизации: ведь накладные расходы нас волнуют только при МАССОВОМ вычитывании, так? Т.е. -- при обходе поддеревьев по приходу данных. А там мы можем просто сразу заготовить все "общие" параметры -- в т.ч. массив указателей на "localreginfo", а потом перед каждым вызовом подправлять только специфичное для данного knob'а -- в частности, [1]-й элемент переставлять на массив параметров.

    И отдельно насчет "типа-записи CxDataRef_t": да не нужно оно!!! Пусть все инкапсулируется в cda за типом cda_objhandle_t -- например, отвести специальный код sid'а (все единицы) на "формулу", и завести отдельный массив "формулы", индексируемый objofs'ом; а уж там-то и помнить "тип" исполнителя такой формулы.

    BTW -- можно и большие каналы в cda "унифицировать" с обычными, например, выделив один битик на "тип".

    18.06.2007: решение-то очень простое и очевидное:

    • Функции "getvalue()" дается булевский параметр, означающий "обсчитать сейчас" (1) либо "дай значение" (0), обсчитанное ранее и складированное в "кэше"...
    • ...при каждой формуле, в ее "info-дескрипторе", заводится "кэш", аналогичный обычному каналу: текущее значение, сырое значение, флаги, возраст
    При отдаче обычных (не-формульных) каналов этот параметр, естественно, просто игнорируется.
  • 16.04.2005: мдяяяя... Поскольку наши древесные структуры будут подходить не только для экранного отображения при помощи Knobs, но и для web-отображения, встает вопросец -- а как лучше такое делать? Вставлять подобную функциональность в libKnobs или не стоит?

    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: надо будет завести "четвертый диапазон", в дополнение к norm, yelw и disp -- input/allowed, для ПРЯМОГО указания разрешенных для ввода значений.

    22.04.2005: сейчас ограничение на ввод устанавливается по "ужайшему" из указанных диапазонов; это неудобно. Особенно это неприятно стало после появления концепции "внешней колоризации", когда указываемые для пожелтения/покраснения диапазоны не имеют НИКАКОГО отношения к самому значению, а относятся к "колоризующему".

    Так что: в CXv4 надо ввести еще один -- даже не 4-й, а 0-й диапазон -- "INPT" или "ALWD".

    И есть два варианта: либо (1) ограничивать ввод ТОЛЬКО по нему, плюя на norm и yelw, либо -- (2) если хочешь иметь "типа неограниченный ввод", точнее -- отключить ограничение по norm/yelw, то надо указать какой-нибудь более широкий диапазон.

    В общем, правила "наследования" вылазят такие:

    • Если inpt не указан, то либо (@1) ничего не делать, либо (@2) брать широчайший из {norm,yelw}.
    • Если disp не указан, то берется широчайший из трех остальных.

      НО! Если имеется 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.

  • 23.04.2005: кстати, когда будет plugin-архитектура с добавлением множественных таблиц плагинов, то может возникнуть "проблема" с интерфейсом simple-knobs. Сейчас там параметр look= сделан типом 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", однако!

  • 23.04.2005: а еще хорошо бы иметь (для не-simple-программ) возможность указывать в "общем описании" некие "свои" элементы/ручки, которые НИКАК не будут привязаны к соединению -- те, что сейчас создаются программами типа phm-tsyline.c "вручную".

    Т.е. -- как-нибудь указывать, что в иерархии внутри этого элемента надо вызывать не обычный CdrSetKnobValue(), а нечто из юзерской программы.

    А может, эту идею вообще генерализовать, и иметь "плагины установки значений"? По умолчанию -- Cdr, можно -- свой, а можно -- вообще что-нибудь типа Epics'а...

    Точнее, так:

    Даже плагин не "установки значений", а "интерфейса реальных данных". И доступ к нему тоже должен вестись через Cdr, стандартными же методами -- благо, там все уже вполне пристойно продумано и сделано.

    28.04.2005: О! Точно!!! Уже ведь была идея --

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

    А что, если обобщить:

    Этот "defserver" может содержать не только ссылку на сервер, но еще и "префикс протокола" -- через '/' (что уже используется в tsycam.c), например, "cx/linac1:5", или "epics/linac", или "local/"

    Да, и по-хорошему, нужна бы еще одна фича:

    Чтобы поддерживались "макросы" -- т.е., чтобы можно было и ТУТ ТОЖЕ указывать не "cx/linac1:5", а "cx/$LINVAC".

    Мдя... Есть 2 "замечания/контраргумента":

    1. Нехило бы иметь ДВА уровня макрорасширения: первый -- в самой БД, а второй -- уже в клиенте. И, очевидно, эти макрорасширения должны иметь разные префиксы. (Вначале думал о '$' и '%', но см. следующий пункт.)
    2. В проекте разборщика формул от 12-01-2004 символы '$' и '%' уже были зарезервированы под ссылки на каналы и регистры. Есть еще идеи, какие бы символы задействовать под макросы? :-)

      (Ага, из разумных идей -- остался только '@', да еще можно под регистры занять '#', хотя это и мерзко...)

    05.02.2007: есть идея, как просто делать ДВА уровня макрорасширений с использованием ОДНОГО символа префикса:

    Символ -- '$', как в shell и make, и, как в make, двойное указание -- "$$" -- дает литеральный $, который просто передается на уровень ниже. Таким образом, просто $NAME будет означать расширение на уровне БД, а $$NAME -- на уровне клиента.

    Не самый красивый подход, конечно, но вполне работоспособный -- пока не придумаем что получше.

    15.06.2007: да, разумнее всего будет использовать символы/префиксы следующим образом:

    • $МАКРОС -- макрорасширение.

      (Остается вопрос с двумя уровнями, ибо хочется мочь вбивать ОДНИ И ТЕ ЖЕ тексты и в БД, и в локальные файлы.)

    • %РЕГИСТР -- ссылка на локальный/ручковый регистр.
    • КАНАЛ -- ссылка на канал, никаких префиксов.
    • функция() -- вызов функции, также никаких префиксов.

      (Определение не по скобкам, а тупо по имени -- зарезервированному слову; нефиг каналам давать названия типа "sin", "log", etc.)

  • 28.04.2005: вот я все с неофитским рвением изобретаю, где бы еще использовать столь удобную концепцию плагинов (сейчас уже -- в psp, будет в knob-displayers и в data-access-interface; а еще и текстовые форматы почти готовы для плагинов :-).

    А как это все с точки зрения security? Т.е., имеем в результате тотальную программируемость -- ровно то, что закладывал Microsoft, и за что его сильно критикуют, ибо это у них крайне плохоконтролировабельно. (Да и Java и всякие Symbian в этом смысле немногим лучше...)

    В принципе -- тут-то программируемость чисто локальная: т.е., никаких тебе ActiveX, а плагины только те, что предоставляются либо библиотекой, либо программой. В отличие от microsoft'овских продуктов, где все динамически.

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

    17.05.2006: проблема динамической загрузки того-что-не-прошено может быть смягчена такой мерой: в описании подсистемы добавляем (опциональное) свойство/поле "требующиеся библиотеки knob-плагинов" -- аналогично тому, как в ELF-файлах указывается список требующихся им динамических библиотек. А уж knobber будет пытаться загрузит указанные "библиотеки" из некоей стандартной директории.

    И тогда если вдруг нужное не найдено -- не беда, будут использоваться default-плагины. И даже более -- в режиме "high-security" можно динамическую загрузку запрещать.

  • 28.04.2005: В CXv4 нужен будет конвертер для группировок из БД в .c-представление и обратно. Чтоб можно было рисовать всякие phm-tsyline прямо в БД.
  • 06.05.2005: напрашиваются такие идеи-усовершенствования в указании параметров Knob'ов:
    1. Вынести описание "списка для selector'а" из widdepinfo в отдельное поле, чтобы ни у кого не возникало проблем с парсингом.
    2. Сделать "унифицированный" список опций, который бы использовался ВСЕМИ Knob-widget'ами, просто чтобы они каждый оттуда брали б свое.

    Как вариант, можно "список для selector'а" сделать каким-нибудь отдельным стандартным параметром в widdepinfo (будущем options).

  • 12.05.2005: насчет БД-доступа (навеяно докладом на Конкурсе Юных Щенков логашенкиного Анисенкова): а ведь нехило бы иметь кроме БД еще и "локальные" таблицы/режимы/etc. Можно это сделать опять путем префиксов при ссылках. Вот только тут префиксы типа "proto/" не подойдут -- скорее, URI/URL-подобные "proto:".
  • 14.05.2005: надо будет все-таки на пути к CXv4 сделать и промежуточную -- CXv3. И вот почему.

    Сегодня было сборище по поводу индукционного ускорителя. Там нужна система управления, не-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 необходимо.

  • 17.05.2005: ДА, ДА, ДА, нужна "библиотека обслуживания структур Knobs", к компетенции которой и будет относиться Knobs_typesP.h, и которая будет обязательна для использования как libKnobs, так и libCdr.

    Вот только старый вопрос остается -- а как же ее назвать? Смысл ее -- это как бы быть "методами классов из 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 надо разделить на две части:

    1. Платформно-независимую KnobsCore (сводящуюся к Knobs_widgetset (поддерживающую плагиновость и все LOGT_*) и Knobs_simple);
    2. Платформно-зависимую "набор стандарных ручек" (StdKnobs?). И иметь разные ее реализации для Motif, Qt, Win32, etc.

    30.08.2011: да, v2'шная libdatatree сегодня официально запущена в эксплуатацию.

  • 27.05.2005: насчет "А не унифицировать ли номера обычных и bigc?" (22.03.2005@ShonanVillage): да, стОит. Некоторые идеи на эту тему:
    • (еще тогда стало ясно) Это означает, что и большие каналы должны допускать обращение к ним через интерфейс обычных -- т.е., иметь некое 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.

  • 01.06.2005: BTW, можно указывать тип отображатора не просто именем, а СПИСКОМ имен в порядке убывания подходящести -- через запятую, как шрифты в HTML/CSS.

    29.08.2005: (давно, но все забывал записать) и при поиске приоритет у юзерских отображаторов -- чтобы можно было так переопределять стандартные типы.

    22.12.2010@Снежинск-каземат-11: собственно -- в 4cx в KnobsCore_knobset.c::GetKnobLookVMT() запятая уже обрабатывается (фиг знает сколько лет -- видимо, с момента реализации), так что "done".

  • 01.06.2005: кстати, а все-таки -- не потребуются ли нам "невидимые объекты" -- как в LabView таймеры, а в Delphi -- всякие datasource'ы и dataset'ы?

    И чтобы потом можно было как-нибудь ссылаться на эти "невидимые" объекты.

    Если их делать -- то явно надо будет иметь ОТДЕЛЬНЫЙ список таких "невидимых объектов", дабы они не мешали обычной "древесной" работе 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 -- впечатляют, более широкий репертуар требует уже именно секционированного формата.

  • 12.06.2005: мысли насчет того, как организовывать чтение БД:
    • Надо разделить ЧТЕНИЕ и ИНИЦИАЛИЗАЦИЮ по результатам чтения. Т.е., вначале всю БД прочитали во внутренние структуры/массивы -- а потом уж делаем старт всего.
    • Чтение при этом можно делать РАЗНОЕ: СУБД, blklist, a-la uds-файлы.

    Кстати, а вот при отдельном чтении-то и можно будет вернуться к старой идее: что при "reload'е" БД читать все при живой системе -- в "отдельный объект-БД", а потом -- грохать старое и копировать в "реальное".

    13.06.2005: и, кстати, можно будет вернуться к давним (древним!) мыслям о том, что системе можно сделать "stop" (pause) и "start".

    22.06.2005: плюс см. мысли за сегодняшнее число в разделе о "CX server library".

  • 13.06.2005: идеи насчет консольного интерфейса.

    13.06.2005: много :-). Вот они:

    • (12.06.2005) Driver-specific "dynamic" methods -- которые "call ID command [ARGS...]".
    • Возможно, frontend'ы будут иметь некие специфические команды. Как делать их? Варианты:
      1. Разрешать регистрировать свои команды -- как это делает EPICS (ага, а cleanup тогда будет еще навороченней -- еще их надо не забывать!).
      2. Аналогично драйверам -- иметь команду типа "call_fe ID command [ARGS...]".
    • Надо уметь получить список frontend'ов. Вообще -- у нас уже ЧЕТЫРЕ разных сущности, список которых надо уметь получать: клиенты ("clients"), консольные клиенты ("who"), драйверы ("scan"), будущие frontend'ы, а еще и будущие layer'ы.

      Может -- ввести, a-la GDB, общую команду-мультиплексор "list", которой уж говорить, что нас интересует -- "list WHAT", где WHAT -- "clients", "consoles", "devices", "frontends", "layers".

    • Возможно, самим cxsd'шным консольным командам также стоит сделать описатель списка параметров -- где int, где строка, и т.д.
    • И, видимо, надо делать "умный" парсинг, который бы понимал кавыченье (вот epics'ный shell -- понимает).
    • Текстовые ответы на команды должны выполняться не так, как сейчас -- в локальный буфер, "p+=sprintf(...)", а некоей (передаваемой команде -- зависит от клиента, читай frontend'а) функцией "добавить к сообщению клиенту", которая, в случае cxsd_fe_cx, сведется к добавлению данных к fdiolib'овскому буферу отправляемого пакета.
  • 16.06.2005: а ведь понадобятся и МНОЖЕСТВЕННЫЕ таймауты в Driver API, а не только по штучке на драйвер.

    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'ы, так что раздел стал тотально устаревш.

  • 16.06.2005: можно будет ввести стандартный тип элемента-отображатора "Закладки" -- чтобы оно создавало по закладке для каждого своего содержимого, назначая закладке в качестве имени имя этого подкомпонента.

    Кстати, можно будет воспользоваться для создания "закладок" (если самому будет лень их делать) комплектом Microline Widget Set, доступным либо в старой Mozilla, либо в новом NEdit'е. (Правда, там напряго с компиляцией, но разберемся.)

    20.04.2006: да, а можно обойтись и стандартной motif'овской функциональностью -- оказывается, начиная с 2.2 в нем есть XmTabStack, который ровно что надо и делает. И точно также умеет приделывать закладки и слева/справа/сверху/снизу.

    В любом случае, "типа проект":

    • Оно должно считать себя за одноколоночный элемент, и параметр ncols -- игнорировать.
    • Оно должно принимать в widdepinfo один параметр -- "расположение" (можно селектором -- tabs=top|bottom|left|right, либо "флагами" -- просто top|bottom|left|right).
    • Метки закладок надлежит брать так же, как заголовки строк -- либо из rownames, либо, при "?" -- из метки n-й ручки.

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

    28.04.2006: изготовил, в work/tree/testtree.c -- по вышеприведенному проекту. Детали:

    • Параметр "расположение" сделан просто флагами top|bottom|left|right (кстати, оное НЕ в widdepinfo, а в eleminfo_t.options).
    • Единственная "неприятность" -- оно размеры ВСЕХ закладываемых подвиджетов делает по максимальному, так что пришлось вводить промежуточные XmForm'ы, в которые уже вкладывать подэлементы: при этом форме размер уставляется, но, поскольку содержимое к ней справа+снизу не привязано, то на нем это никак не сказывается.
    • Поскольку в XmTabStack'е имеется bug#1337 "Keyboard traversal in XmTabStack is nonfunctional", пришлось просто уставить егошнему XmTabBox'у (имя "tabBox") XmNtraversalOn:=False.
    • Остальной же "тонкой" настройкой типа параметра XmNtabMode я не занимался -- при надобности сделаю.

    В общем, считаем что "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'а.

  • 17.06.2005: о "статусе" cxsd -- навеяно предзащитой Славы Середнякова 16.06.2005.

    18.06.2005: у Славы к программе прикручен epics'ный CA-сервер -- как?

    А не сделать ли в CX аналогично -- "ядро" cxsd -- "подселенцем"? Т.е., чтобы там была "симметричная" архитектура, БЕЗ принудительной ведущей роли cxsd?

    (Опять же -- такая возможность нужна скорее для "корректности"/"красивости".)

    20.06.2005: угу, ведь в принципе-то в cxsd действительно есть несколько ОТДЕЛЬНЫХ компонентов:

    1. Собственно "ядро" -- будущий cxsd_data.c, который и является "коммутатором данных". Здесь же -- cxsd_execcmd.c.
    2. Среда исполнения -- cxsd.c, инициирующий весь процесс -- парсенье config-файла, обработку опций командной строки, и т.д.
    3. CX-frontend -- cxsd_fe_cx.c.
    4. Собственно драйверы (каковые в случае "подселяемого CX-сервера" никак не нужны).
    5. Полу-сторонняя вещь -- загрузчик модулей (драйверов, layer'ов, frontend'ов).

    Напрашивающиеся выводы:

    1. Произвести четкое отделение "ядра" от "среды исполнения" -- стоит. При этом придется ЧЕТКО выделить именно "cxsd API" -- интерфейс для той сущности, к которой его подселяют.
    2. С другой же стороны, предоставлять собственно возможность подселяемости -- НЕПРАВИЛЬНО, т.к. НЕФИГ создавать standalone-программы, предоставляющие CX-доступ к своим внутренностям.

    (Хотя -- такая фича дала бы возможность быстро присобачивать 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: битым текстом:

    1. Итак, какие варианты, позволяемые плагин-модульной архитектурой СУ, могут использоваться для интеграции с другими СУ:
      • [клиентские] транспортные уровни, использующие чужие протоколы - дают доступ к аппаратуре, обслуживаемой другими СУ;
      • подселение в сервер client frontend'ов для чужих протоколов дает native-доступ к нашей аппаратуре клиентам, созданным для других СУ; в частности, позволяет использовать с нашей СУ большое количество софта для анализа и моделирования, написанного, например, для EPICS;
      • подселяемый сервер может добавляться в давно- или другими-сделанные монолитные (do-everything) программы, тем самым бесшовно интегрируя их в СУ.
    2. Разработанное разбиение системы на компоненты дает огромную гибкость, позволяя соединять их кучей способов:
      • обычная 3-уровневая система;
      • подселяемый сервер;
      • монолитная программа для малой автоматизации, сразу включающая нужные драйверы, безо всякой сетевитости. (Но при этом оно все равно остается 3-уровневой схемой - просто все 3 слоя слинкованы вместе.)
      • Сочетанием двух предыдущих примеров можно создавать программы для стендов, могущие при надобности отдавать данные и по сети.
      Ключевым фактором тут выступает то, что драйверы/модули в сервере имеют тот же API доступа к другим каналам, что и клиенты. Это и упрощает работу, и позволяет селить клиентов и драйверы в одну программу, и можно при надобности (смене понимания) перетаскивать мозговитый код между сервером (1.5-й уровень) и клиентами.

    Короче - красиво выходит, хоть презентацию на следующий ICALEPCS готовь.

  • 20.06.2005: на тему "удобного получения информации о драйверах -- что бы еще хотелось:
    1. При смене статуса чтоб драйвер мог бы предоставлять еще и текстовое описание проблемы, а не только в log-файл (это уже озвучивалось -- см, рассуждения о "cx_geterrdescr" от 15.06.2005).
    2. Время смены статуса -- записывать struct timeval'ом.
    3. Как-нибудь бы уметь смотреть "текущее состояние" драйвера -- статус: например, сколько сейчас пакетов в очереди на отсылку. Видимо, это должен быть именно специальный метод драйвера, которому бы передавался буфер для сообщения и размер оного.

    Последние два пункта -- видимо, должны смотреться специальной командой -- типа "status <ID>".

  • 28.06.2005: насчет возможной "компрессии" при передаче типов knob'ов от сервера к клиентам:

    Можно передавать не все строки, а -- как атомы в X11 -- вначале "словарь", а в описаниях knob'ов -- номера строк из этого словаря, например, 16-битными числами.

    Другое дело -- а оно нам надо? Да, экономия будет -- байт по 5 с каждого knob'а на больших группировках, зато -- сколько маеты: составить словарь, а в клиенте -- вначале делать lookup по словарю, а когда все будет раскодировано -- словарь выкинуть. Нафиг? У нас ведь все равно описания knob'ов будут состоять из моря varlength-строк, так что...

    03.07.2005: нет, точно не надо :-)

  • 03.07.2005: насчет "стандартизации psp-парсинга параметров «компонентов»":

    Надо делать psp-парсинг auxinfo в параметры "принудительно" -- т.е., как это делается для модулей в ядре Linux.

    Как такое сделать -- просто: в driverrec'е указывается также и psp-таблица, а там уж -- есть 2 варианта: либо парсинг прямо в privrec, либо -- указывать отдельно размер и прочие параметры спец. структуры. Но второй вариант мне нравится меньше.

    И абсолютно аналогичный финт стоит проделать с knob'ами.

    08.08.2005: Йоу!!! А ведь нам этот принудительный парсинг дает еще одно огромное преимущество:

    Если будет какой-то «visual design tool» для интерфейсов программ, то он сможет элементарно вычитывать поддерживаемые конкретным компонентом через widdepinfo "свойства" и представлять их в окошке "Knob specific properties" в виде списка -- название/значение, и даже сразу производить верификацию вводимых параметров.

    И, хотя это чуть сложнее ---

    Аналогично с параметрами драйверов: они могут вычитываться прямо из драйверов. Но тут проблема --- что редактор-то БД доступа к драйверам может ну СОВЕРШЕННО никак не иметь.

    08.08.2005: Кстати, параллельно некоторые мысли по поводу того, как мог бы быть устроен этот самый "visual UI design tool" (придумано вчера, на пляже :-):
    • Насчет "выделения" компонента:

      Как махинировать с компонентами на design-экране: каждый компонент вставляется не "сам по себе", а -- на его место ставится форма, которая включает его (последним!), причем самым первым идет специальный прозрачный виджет (InputOnly), который блокирует (перехватывает) мышиные события -- он приаттачивается ко всем краям формы, плюс четыре тонких виджета, которые включаются при получении фокуса -- также приаттачиваются к краям.

      Естественно, в таком разе придется вводить "параметризованное создание knob'а" (указанием функции, которую вызывать в качестве CreateKnob()) -- чтобы контейнеры вставляли в себя не knob-жертву, а эту форму.

      При этом проблема только, что КЛАВИАТУРНУЮ-то навигацию мы так не заблокируем...

      И вообще -- а не лучше ли, наконец-то, научиться ЛОВИТЬ ПЕРЕМЕЩЕНИЕ ФОКУСА, и по оному событию как надо перемещать ту рамку на поверх "жертвы"? Проблема лишь в том, что XmN[losing]focusCallback имеют только XmText/XmTextField, а нам его придется имитировать...

    • При модификации некоего компонента надлежит его (со всей под-иерархией, естественно), грохать, и создавать заново.
    • Нужен "flat" (spreadsheet) view.
    • Оно должно уметь сохранять/загружать как из БД, так и из "локального файла".

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

  • 03.07.2005: об "асинхронности" отсылки данных от сервера клиентам: как? Единственное, что приходит в голову -- просто по самой природе задачи -- это "a-la ReturnChan*".

    Т.е., клиент (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 именно так и делается -- там целый раздел уделен тому, как следует отводить идентификаторы.

  • 04.07.2005: надо будет слегка поменять имена header-файлов, для большей интуитивности -- в стиле X11:

    Нынешний cx.h становится cxlib.h (a-la Xlib.h), а нынешний cx_types.h превращается в cx.h (a-la X.h -- "определения", глобальные для системы).

    06.08.2006: в 4cx/ с самого рождения уже так и сделано -- ровно по этому проекту. "done".

  • 05.07.2005: даже если забыть про то, что "формулы -- это концепция", а ведь и правда -- с чего так формулы привязаны к cda? Чисто исторически.

    А де-факто, по логике, им место в совсем отдельной библиотеке, которая будет пользоваться УСЛУГАМИ "интерфейса доступа к данным" (через некий API/интерфейс), и этим интерфейсом не обязательно должна быть cda (хотя чаще всего будет она).

    При этом мы получаем свободу, как делать "универсальный доступ к данным" -- либо поддержка "Channel Access etc." будет прямо в cda, либо в совсем другой библиотеке, предоставляющей cda-подобный интерфейс, плюс что-то в Cdr'е.

    14.06.2007: да, реально ЕСТЬ причина привязки формул к cda: auxservers. Ведь cda_register_formula() сейчас вручную добавляет каналы-по-имени.

    Собственно, ответ, как это обойти, очевиден -- ввести API для сего действия.

    11.08.2011: поскольку большая часть написанного тут уже учтена, да и с введением "сценариев" концепция слегка мутировала (и часть описанного стало irrelevant), то "done".

  • 10.07.2005: вопрос: а нафиг вообще иметь поле kind (LOGK_{DIRECT,CALCED})? Ведь можно делать проще: если формула указана -- использовать ее, если нет -- то номер канала.

    13.03.2006: да, абсолютно незачем -- так что из 4cx/.../Knobs_typesP.h поле "srctype" выкинуто.

  • 22.07.2005: итак, насчет интерфейса драйверов:
    • Метод fd_io() устраняется вовсе.
    • Метод init_blk() будет возвращать статус.
    • Параметр bid будет передаваться только в init_blk() -- как показывает опыт, он нигде в других местах не используется.

    06.08.2006: в "совместимом коллеге-прототипе будущего API" -- cm5307_dbody.h -- несколько месяцев назад именно такой интерфейс и сделан (только с массивовым businfo вместо busid). Удобен.

    11.08.2011: тему уже давно можно считать за "done".

  • 19.08.2005: (назрело-то давно...) Похоже, нужен какой-то механизм, который бы объединял формулы и seqexecauto. Смысл -- для "программирования без программ".

    В частности, это просто очень-очень прет для той самой мерзкой задачи -- "процедура выполнения 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() вызывается по приходу данных.

    Неясности же --

    1. Как и куда вообще sea-программа будет вставляться? (Как выглядеть-то -- понятно: как обычно, но вместо функций -- формулы.)
    2. Как оно будет "запускаться"? Понадобится какой-то способ в формуле сказать "запусти такую-то программу".
    3. Аналогично -- надо уметь делать "стоп".

    17.09.2005: ну-ну... Чуток подпоразмышлял, и... В качестве варианта идеи: можно такие спец-sea-программы запихивать как раз в список "невидимых объектов" (см. на тему "таймеров" за 01-июня-2005).

    Общее резюме от такого подхода -- что sea получается НЕ в формулах, а "выше"/отдельно, а формулы -- лишь подспорье. Это все-таки не совсем то, о чем были первоначальные думы: хотелось-то уметь именно к КОНКРЕТНОМУ KNOB'У привязывать не только формулы, но и "методы"/"действия".

    Так что надо продолжать думать, генерить идеи дальше.

    08.09.2013: в принципе придумалось -- см. раздел "О некоем скриптовом языке..." за сегодня. Здесь же ставим "obsolete".

  • 19.10.2005@St.Genie/CERN: "надо б chlclient'у выбрать название попристойней".

    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/, как и сейчас.

  • 02.11.2005: надо как-то стандартизовать форматы файлов всех подсистем, не только chl-based.

    Столкнулся сегодня при создании phm-lebline.c на основе phm-tsyline.c -- они ж очень похожи, и реально уж по крайней мере функции-фильтраторы ну уж никак не должны отличаться!

  • 02.11.2005: в связи с тотальной plugin-архитектурой возникает вопрос: а с командами как быть? Как их (глобальные, на уровне приложения) привязывать к plugin-компонентам?

    02.11.2005: имеется некий "предварительный" ответ на этот вопрос, состоящий из двух частей:

    1. Иметь некую таблицу пар (команда,указатель-на-функцию-обработчик). Это некий аналог "виртуальных команд" (или как там они назывались) в борландовском C++ под Windows -- которые описывались синтаксисом типа "код-команды=функция".

      Вопрос только, куда эту таблицу засовывать, чтобы она сканировалась Xh-обработчиком? Дополнительный "невидимый плагин"? А как он будет связан с собственно содержимым окна -- как он будет туда адресоваться? Или как-то диспетчеризовать команду по объектам -- как это делалось в TurboVision и GREMLIN?

    2. А команды должны быть не числами-кодами, а строками -- как будут строками и типы плагинов. Естественно, стандартные из них должны определяться символьными константами черед #define.

    Вообще, надо принять как общий принцип -- все потенциально разнообразящиеся/расширябельные вещи должны кодироваться не числами, а строками.

    09.06.2006: еще вариант -- чуть более реалистичный/приземленный:

    • Нам ведь нестандартные (не-Save/Open/etc., например -- {Get,Rst}Cosmo в ipp) команды для чего нужны? Пра-а-авильно, чтобы их обработала некая специфичная для программы логика.
    • А где будет эта специфичная для программы логика? Да, в knob-plugin'ах!
    • Следовательно -- вводим в knob-plugin'ы метод "HandleCommand()", принимающий аргументом команду (да-да, строку). И при нажатии на кнопочку -- оно бродкастит команду по всему дереву knob'ов. Естественно, где метод равен NULL -- как у большинства -- ничего производиться не будет.

      А поскольку юзер, по сравнению с компом, существо медленное, то временнЫе затраты на этот бродкаст пренебрежимы.

    20.07.2006: да-а-а, а не расширить ли понятие "текстовые команды" с просто строк на "командные строки"? И получим мы тогда аналог actions в Xt -- короче, повторим эволюцию, в результате которой два десятка лет назад Xt вышла такой, как вышла. Вопрос -- а оно нам надо? :-)

  • 02.11.2005: кстати, такой идеологический вопрос/соображение: а многооконные возможности Xh'а -- они для нашей CXv4'шной plugin-архитектуры нужны?

    02.11.2005:

    • С одной стороны, по идее -- нет, разные (по крайней мере, "равноправные") окна вполне можно будет делать просто разными "приложениями", которым работать в рамках одного процесса совсем не обязательно
    • С другой -- а что мешает будущему "chlclient'у" указывать как бы "надгруппировку" -- т.е., несколько ссылок на группировки, которые он бы запускал в отдельных окнах, а уж как там они будут взаимодействовать друг с другом -- только ли через сервер или через общие переменные -- это личное дело их plugin-компонентов.
    • А как вообще поступать с потенциально требующимися "вторичными" окнами -- как у Гусева, или "параллельными" -- как было в старом ipp... Это отдельный вопрос, на который у меня пока даже и намека на ответ нету.

    Так что -- из общеидеологических соображений все же надо делать всю эту "дисплейную инфраструктуру" БЕЗ статических переменных (никаких там onewin!).

  • 16.12.2005: К вопросу о поле "busid": сейчас идет всякий выпондреж с ','/';', что есть очень некрасиво... Это пошлО с того, что busid передается ВСЕМ методам (от чемго мы хотим отказаться). Ну так --

    Разрешить указывать, например, до 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".

  • 23.01.2006: уже сейчас есть потребность иметь дополнительный числовой параметр (сейчас -- число циклов) для LOGT_DEVN, LOGT_MINMAX, возможного будущего LOGT_AVG и им подобных.

    Т.е. -- просто еще одно число в определении knob'а.

  • 23.01.2006: а вообще -- нафига будут нужны LOGT_* (DEVN, MINMAX, AVG), если поддержка этих операций появится в формулах?

    И, кстати -- а когда, собственно, вообще появилась концепция "LOGT_*" (в первую очередь -- LOGT_DEVN, так?)?

    Когда, когда -- давно! Еще в UXIL (Ucam X Interface Library) оно было -- там в uxil.h (06-11-1998, но наверняка появилось раньше на год/два) имеется LOGT_DEVN=1001. Наверняка корни этого уходят куда-то в drc/ekc/..., но рыть туда уже вломы.

  • 30.01.2006: есть такая неприятная особенность -- при запуске клиентских программ некоторое время (реально -- <=1s) в тулбаре горит красная лампочка, и каналы бледно-бирюзовы -- выглядит, словно сервер подтормаживает.

    Причина -- запрос-то на данные послан, но ответ придет только когда закончится очередной цикл сервера.

    Так что -- в будущем, когда в протоколе в пакетах запросов появится флажок "когда" (немедленно, по обновлению, по концу цикла), то надо будет, чтобы cda посылала ПЕРВЫЙ запрос именно с когда=немедленно.

  • 02.02.2006: как можно было бы РЕАЛЬНО ("корректно", а не по изменению-на-сколько-то) определять "changed-by-another-operator":

    У нас же ведь у каждого клиента есть уникальный ID, так? Значит, можно вместе со значением канала отдавать и "ID последнего менявшего", а уж cda сможет просто сравнивать ID-изменившего-этот-канал со ID-этого-соединения (при установлении соединения выспросив его client-ID у cxlib'а).

    Сложность (помимо протокольного overhead'а) -- в том, что вообще-то операция записи неатомарна, и нет такого понятия, как "вот этот персонаж выполнил запись"; собственно инициирование записи и возврат значения от драйвера на нее -- разнесены. Можно, впрочем, в момент отправки-драйверу-на-запись складировать в некое место (c_wr_requestor[]) ID инициатора, а по получению данных в этот канал -- копировать из c_wr_requestor[] в c_last_writer[]. Естественно, с надлежащей поддержкой ситуации "next" -- какой-нибудь c_next_wr_requestor[].

  • 15.02.2006: о сохранении и восстановлении режимов, часть 1.

    В некоторых приложениях (типа того же rfsyn'а) может быть ситуация, когда НЕЛЬЗЯ отправлять все значения сразу -- нужно в некоторой последовательности, с задержками.

    Можно в структуру "knobinfo_t" добавить поле "приоритет" ("задержка"), определяющее, через какое время (в циклах? в секундах?) после загрузки режима надо отправить значение в сервер. (Аналогично полю "pass" в /etc/fstab.)

    В случае использования такого подхода возникает несколько последствий или возможных задач:

    1. Надо иметь поле "knobinfo_t.loaded_value". Это в любом случае есть гуд, т.к. шаги "чтение файла" и "отправка режима" расшиваются.
    2. Надо будет иметь "как бы фоновую задачу", выполняющую такую пошаговую отправку.
    3. На время, пока "отправка режима" выполняется, всякий ввод должен блокироваться (XtSetSensitive(workSpace,False) плюс блокировка кнопок? 24.10.2012@Снежинск-каземат-11: фиг -- на subwin'ы это ж не повлияет.). И, естественно, сие должно как-то индицироваться, включая оставшийся до конца период. Плюс -- возможность прерывания этого процесса.

    01.04.2006: есть еще один "потребитель" упорядоченной/последовательной загрузки режимов: "полуискусственные" каналы, арифметически привязанные к настоящим. Сегодня это вылезло с ausacc, где есть "пары" GVI-каналов (стартовый/стоповый импульсы), и удобнее вместо второго значения работать с его СМЕЩЕНИЕМ от первого. Но при загрузке режима формула имеет доступ к СТАРОМУ значению "базового"/стартового канала, и именно к старому будет прибавлять считанное из режима смещение, что есть плохо. А так бы -- сказали б, что очередь "смещательных" каналов -- через 2 цикла, да и все...

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

    04.10.2012: еще прикол касательно режимов (реально уже неделю над ним голову грею).

    • Есть такая штука: корзина корректоров, питаемых ОДНИМ источником (40А, кажется), управляемых одним CAC208. Так вот: СУММАРНЫЙ ток этих корректоров НЕ МОЖЕТ ПРЕВЫШАТЬ некоей границы, определяемой возможностями источника.
    • При ВВОДЕ уже придётся выпендриваться -- не позволять ставить значения больше, чем (максимум - сумма_остальных - текущее).
    • Но с загрузкой режима еще хуже -- там НАБОР значений может быть внутренне непротиворечивым, но получается, что при загрузке НОВЫЕ значения будут ограничиваться ТЕКУЩИМИ.

    Ну и что можно сделать?

    1. Федя (с Петренкой) заявляет, что "это же корректоры -- можно просто сначала везде записать нули". И как эту "предварительную запись нуля" пристегнуть к модели "поочерёдной загрузки"? И откуда брать значение "нуля" (из пресловутого defnorm, реально так нигде никак и не используемого?)?
    2. ...совсем по-хорошему -- надо бы сначала всё "выключать", потом делать изменения, а потом всё оптом "включать". Типа транзакции. Но это решительно unreal.
  • 15.02.2006: о сохранении и восстановлении режимов, часть 2.

    Как сохранять и восстанавливать данные для больших каналов и для LOGT_USER? Да еще учитывая, что сохранялки/читалки могут быть разные -- file://, DB://, etc...

    Ответ: строками!

    Детали идеи:

    • Конкретный формат строки -- личное дело конкретного knob-плагина, а уж задача сохранялки/восстановлялки корректно за/раз-escape'ить все содержимое. И, видимо -- для простоты стоит делать это "поле" последним в строке файла.
    • Хотя в случае больших каналов нужна какая-то стандартизация -- видимо, вначале, списком "параметр=значение" (a-la PSP) пускать параметры, а потом -- массив собственно данных.
    • При сохранении вызывается некий метод "составь строку", который, например, следующим образом обращается с памятью: он возвращает указатель и флажок, указывающий, надо ли делать free() после использования.
    • При восстановлении же просто вызывается метод "на, разбирайся и уставляй".
  • 15.02.2006: еще на тему БД/автоопределения/"PlugAndPlay":

    А если в CxsrvMainInfoRec ввести поле "name"?

    По-хорошему, надо б еще уметь и отдельные параметры в CxsrvBigcInfoRec как-то поименовывать...

  • 10.03.2006: о "стилях" -- понятия "color" и "style": сборище идей.

    10.03.2006: собственно, идеи-то приходили последовательно, все больше по командировкам:

    • 16.03.2005@KEK: В дополнение к widdepinfo иметь поле "style" -- для мелких, крупных, etc. knob-independent подвидов (перекрывается с "color").
    • 17.03.2005@KEK: ?: А что, если наши LOGC_{IMPORTANT,HILITED} заменить на "группы" -- 1, 2, 3, 4, чтобы несколько разбросанных по элементу каналов "объединять" вместе.

      (Навеяно также и разноцветными кнопками -- оранжевые, зеленые, синие... fotki/05_03_17_banya/P3170258.JPG -- справа. Вот как бы это сделать, а?)

    • 17.03.2005@KEK: Итого, при отображении -- три "ортогональных" параметра: look, style, color.
    • 26.09.2005@BINP: "Style" knob'а - не predefined, а просто некая строка-слово, а для каждого виджета (LOGD_...) есть некая таблица соответствия, что "zzz" - это "крупный размер, оранжевый цвет". Получается a-la CSS (ага, еще список применяемых стилей сделать и наследование - совсем как в CSS).
    • 14.10.2005@ICALEPCS2005: BTW, а не ввести ль понятие "style" у "верхнего" узла -- subsystem, чтоб можно было указать что "вот тут -- элементы управления более контрастные". Ну прям CSS!

    И вот глядя на это все -- а нужно ли нам поле "color"? Если да, то зачем? Ведь оно полностью поглощается в случае хоть какой-то реализации концепции "style".

  • 13.03.2006: некоторые "мысли" насчет алармовости и "кнопковости" (также сборище).

    13.03.2006: это тоже наполовину по командировкам :-):

    • 13.10.2005@ICALEPCS2005: Кстати, наша архитектура -- что "алармовость" канала определяется его look'ом -- очень неадекватна (отсюда и хак в Cdr'е по LOGD_ALARM).

      А с plugin-архитектурой по char*, ','-separated, это будет и совсем неприемлемо.

      Так что -- надо изобретать способ указать свойство "алармовости" прямо в описании knob'а. Это слегка смыкается с задачей указывать string-list для селектора.

      Может - кроме "type" (knob|subelem|decor|user) ввести еще и "subtype" для knob'ов, в зависимости от которого иметь доп. поля? М-м-м... Не очень нравится...

    • 14.10.2005@ICALEPCS2005: (к предыдущему): поле "список" будет всегда; "алармовость" будем как-нить указывать флажком; все *_LED и _ALARM станут одним виджетом, а алармовость у него будет force'ить options-параметры "red" и "round".
    • 13.03.2006 (реально -- на прошлой неделе): вообще-то, раз сейчас у элементов (точнее -- контейнеров) имеются "кумулятивные" флаги, то -- а нужна ли в CXv4 вообще архитектура "ShowAlarm()" (при условии, что у элемента также будет метод "обновление данных", вызываемый "вместо" knob'ового SetValue(), как callback)? Ведь элемент может просто хранить "предыдущие" флаги, и именно на изменение реагировать, как сейчас на E_ShowAlarm_m().

      Хотя, с другой стороны, конечно, сейчас-то есть специализированная архитектура алармов, а то будет лишь "побочный эффект", который кто-то может и не[до]поддерживать. Да, конечно, идея "передачи аларма вверх по цепочке более корневому контейнеру" в нынешнем варианте работать не будет -- поскольку uplink будет у всех элементов, в т.ч. верхнего уровня, но уж это-то как-нибудь решим.

    • 13.03.2006: во!!! Попался на глаза отвергнутый раздельчик за 15-04-2005, где упоминался потенциальный LOGT_ALARM, и -- а не решение ли это как раз проблемы указания алармовости (и кнопчатости?) для не-отображаторных клиентов?

      Т.е. -- вводим отдельные типы 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: йоу -- проблема указания алармовости ручки решается очень просто! Итак:

    • нас ведь не будет нужды в имеющейся ныне ОЧЕНЬ близкой корреляции logchannet_t и knobinfo_t (да и сам logchannet_t станет едва ли нужен) -- все потому, что вместо нынешнего dumb-and-straightforward CdrCvtNNN будут конвертеры из разных исходных форматов в группировки.
    • Следовательно -- и проблема "как указать алармовость в logchannet_t?" также исчезнет, поскольку не понадобится никакого дополнительного поля, в котором пришлось бы неудобным образом этот флаг указывать.
    • А флаг _B_IS_ALARM уже есть!
    • Следовательно -- просто конвертеры будут кроме ключевого слова "knob" понимать и слово "alarm", являющееся почти синонимом, но дополнительно уставляющее флажок _B_IS_ALARM.

    Так что -- вуаля -- проблема исчезает, при взгляде под правильным углом ее просто не существует!

    "Лампочность" и "кнопковость" можно указывать так же -- если только они вообще когда-нибудь зачем-нибудь понадобятся (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, могущие быть указаны пачкой (разделители -- либо тот же '/', либо ',').

  • 15.03.2006: следует битым текстом сделать замечание о предполагающемся понятии "группировка" (поскольку разные идеи, приведшие к сегодняшнему состоянию, пораскиданы по многим местам):
    • Группировка будет ПРОСТО ОБЫЧНЫМ ЭЛЕМЕНТОМ, ничем от него не отличаясь -- кроме того, что у нее не будет "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".

  • 16.07.2006: еще с начала разработки нынешнего (лето 2006) варианта vacclient'а возникал вопрос: а КАК делать в унифицированной plugin-модели ТАКИЕ программы -- т.е., с двойным отображением одной и той же [под]иерархии? Клонировать -- низзя, криво. А как?

    Ответ-то лежал на поверхности: плагином будет именно группировка (ну или -- некий "содержащий" элемент. И она уж как надо "начинит" себя: создаст одну "закладку" с обычным набором подэлементов (которые будут сеткой), и другую -- с гистограммой, использующей ту же иерархию.

  • 28.07.2007: по опыту реализации ELEM_CANVAS, с потенциально-будущими "динамическими" декорациями:

    Методы (таблицы -- 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'ы тоже.

  • 31.07.2006: пришло в голову -- а ведь если все элементы переедут в Knobs, то как же будет реализовываться метод knobs_emethods_t.SetPhysValue()?

    31.07.2006: что ж, ответ напросился: СТАНДАРТНЫЙ libKnobs'овый метод SetPhysValue() будет просто пытаться вызвать метод SetPhysValue() своего "хозяина". А вот для элемента верхнего уровня -- группировки -- методы будет предоставлять уже Chl. Все равно всякие ShowProps() и ShowBigVal() может делать только он -- так и SetPhysValue() тоже. Это ведь и станет "сутью Chl'я".

    (Или вообще элемент-группировку реализовывать именно в Chl'е? Надо подумать...)

  • 30.08.2006: возможно (и даже наверняка!), уже где-то об этом писал, но все же повторюсь:
    Надо иметь возможность создавать ЛОКАЛЬНЫЕ иерархии, у которых бы "отправка" значений сводилась к вызову личной функции программы.

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

  • 01.02.2007: в качестве "апофеоза" мыслей об именовании и ссылках на аппаратуру из группировок (20-01-2007 и 21-01-2007), а также имении параметра "defserver" полем любого элемента, а не только группировки (18-01-2005 и 28-04-2005) --
    • В каждом контейнере (элементе, подэлементе) -- есть поле "base", которое by default добавляется к списку/строке "base..." содержателей (элементов ("parent"'ов), содержащих этот) -- как это делается с именами поддиректорий. Если это поле пусто -- как будет у большинства -- то его словно нету вовсе.

      Но: если оно начинается с '.', то это не "относительная", а "абсолютная" ссылка, и префикс от parent'ов не используется.

      Это как бы объединение, надмножество той схемы с опциональным указанием defserver'а в элементе, и DOOCS'овской "базы".

    • "URI" может содержать и указание протокола -- "cx/...", "epics/...", "tine/...", "corba/...", ...
    • Насчет "уравнивания формул с каналами": будет именно ТРИ СТРОКИ -- формулы, и для указания просто одного канала можно будет писАть "=ССЫЛКА-НА-КАНАЛ".

    07.02.2007: по первоначальной идее префиксом абсолютности предполагалось взять '/', а не '.' -- по образу и подобию TINE, но по здравому размышлению стало ясно, что это неудобно: во-первых, '/' используется в указании протокола и как оператор деления, а во-вторых, раз уж у нас компоненты имен разделяются точкой, то и префикс надо иметь такой же. Именно так делается и в файловой системе (с '/'), и в языках программирования (с '.').

  • 05.04.2007: (тоже, возможно, повторюсь):
    Надо ввести свойство (большого?) канала -- строка-идентификатор ("adc200", "tsycamv"), чтоб программы могли проверять, что "это то", и для квази-plug-and-play.

    Возможно -- стоит реализовать даже более общую вещь: некое описание "что есть что" для всей аппаратуры, чтоб можно было at-runtime понимать, что "вот это -- осциллограмма, с такой-то раскладкой данных", а "вот это -- матрица пикселов"; плюс, чтобы можно было понимать, что есть обычные каналы (текстовое поле для произвольного ввода, шкала/бегунок, селектор, ..., ???). Опять же -- из описания больших каналов должно быть ясно, какими именно типами ручек надо представлять на экране их параметры. (В идеале -- чтоб этого описания полностью хватало для изготовления пользовательского интерфейса [такого большого канала].)

    Пока какие-то сумбурные мысли, безо всякой конкретики -- просто "а вот хорошо было бы чтобы..."... Хотя и очевидно, что все это должно храниться в базе данных и как-то пересекаться с тем, что есть уже сейчас (минимум/максимум, список значений для селектора, ...). Еще раз, более внимательно, почитать об ASN.1?

  • 18.05.2007: хочется иметь такую фичу: чтобы при старте программы она вычитывала некий файл в "переменные"/"локальные регистры".

    18.05.2007: Смысл/причина -- в "Вычислениях им. его преосвященства о.Федора Еманова" имеется некоторое количество коэффициентов, которые сейчас приходится вбивать в программу (формулы) гвоздями, а хочется, чтобы оно было легконастраиваемым.

    Формат такого файла может быть простым -- энное число строк вида ИМЯ_ПЕРЕМЕННОЙ=ЗНАЧЕНИЕ, и, возможно, чтобы на одной строке могло быть несколько переменных -- как бы таблица. Переменные-то ведь будут адресоваться по именам, вот эти имена в файле и писать.

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

    И -- не обязательно просто файлы, можно бы эти сущности, наряду с таблицами интерполяции, держать в БД.

    19.05.2007: о -- есть проект решения на прямо сейчас!!!

    Сопоставляем каждому такому параметру по "синтетическому" knob'у, которые все помещаем в отдельный элемент (возможно -- SUBWIN, чтоб редактировать удобно было скопом). Тогда можно просто генерить файлы режимов, которые будут содержать лишь этот элемент, и загружать их -- разные по мере надобности.

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

  • 04.06.2007: уже давно стоит проблема, что всякие коэффициентики, параметры и прочие магические часла зашиваются в формулах. Примеры -- разрешенный предел по температуре входной воды в linthermos, границы "ненулевости" уставок в linmag, и т.п.

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

    А -- мочь к каждому knob'у прилагать (опционально) несколько параметров -- массив -- доступных для формул и меняемых в окне "Knob properties".

    Детали:

    1. Формулы должны мочь доступаться к ним примерно как к локальным регистрам.
    2. Должны эти вещи быть доступны и для "встраиваемого скриптового языка" -- видимо, синтаксис ССЫЛКА.НА.РУЧКУ.ИМЯ-ПАРАМЕТРА.
    3. Естественно, оне должны сохраняться/восстанавливаться вместе с режимами.
    4. Каждому параметру кроме значения сопоставляется также идентификатор-имя (для адресации) и, возможно, human-readable комментарий.
    5. По изменению юзером надо как-то вызывать пересчет основной и колоризующей формул, что ли...
    6. А типизацию (double/int/...) мы вводить не собираемся? А разрешенные пределы для этих значений?

    Главная же проблема будет -- КАК описывать эти параметры в описании ручки:

    1. В бинарном/локальном виде -- кол-во + ссылка на массив структур. Или отдельно -- массив значений (чтобы формулам скармливать) и массив имен?

      Неа -- надо заводить какой-нибудь спец. тип "массив/список регистров" в cda и тут применять уже его. А Knobs от cda-то отвязана! Ох... Завести еще какой-нибудь .h с соответствующими определениями, включаемый и в cda.h, и в Knobs_types.h/datatree.h? 15.06.2007: угу -- cx_common_types.h, и в нем тип CxKnobParam_t.

    2. А в исходном?

      В для-.so-файлов -- видимо, тоже массивчик, как сейчас делаются описания подэлементов.

      В текстовых и from-server/БД описаниях -- видимо, тоже некая вложенность на основе фигурных скобок {...}.

    После сего уж точно надо сделать, чтобы диапазоны и шаг также были доступны как "локальные параметры" (только для чтения?) и формулам, и tcl'ю. Или вообще сделать единый массив параметров, который ВСЕГДА начинается с диапазонов и шага?

    15.06.2007: поскольку из-за этих параметров мы получаем как бы несколько наборов локальных регистров (что неудобно), то как бы с ними обращаться?

    Решение довольно несложно:

    1. Исполнителю формул передается МАССИВ указателей на "localreginfo", где, например, 0-м элементом идут локальные регистры, 1-м -- параметры, и т.д.
    2. При регистрации формулы выполняется поиск по именам (видимо, среди ВСЕХ наборов), и потом в "ссылке на регистр" старшим байтом кодируется номер набора, а младшими тремя -- номер регистра.

      И потом, считывая из формуло-программы "ссылку на регистр", адресуется примерно как

      arginfos[(reg_n>>24)&0xFF].vect[reg_n&0x00FFFFFF]

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

    Следствие 2: структуры и массивы локальных регистров и параметров должны быть унифицированы. И с ними -- естественно, унифицированы врЕменные регистры (если оные вообще будут нужны).

    Замечание: вообще-то стоИт и вопрос сохранения локальных регистров с режимом -- ведь если параметры сохранятся с ручками, то как же регистры? Ответ -- надо и их тоже сохранять, ЛЮБЫЕ (а сейчас сохранятся те, которые маппируются на ручки). Но: часть-то (реально -- бОльшая) ведь являются рабочими, и их сохранять незачем. Как разделить? Ответ: пусть регистры и параметры, чьи имена начинаются с '_', не сохраняются. Это аналогично ручкам/элементам, у которых не сохраняются те, чьи имена начинаются с '-'. Дополнение: надо бы этот принцип с '_' распространить и на ручки.

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

  • 27.06.2007: насчет "доступа к параметрам больших каналов как к обычным каналам": есть идея...

    27.06.2007: вступление: мы ведь в любом случае хотим сделать в интерфейсе сервер/драйверы вызов "уставить параметры большого канала" -- просто уставить, на будущее, БЕЗ выполнения измерений.

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

    Другой вариант -- ввести кроме типов 0 (ro) и 1 (rw) еще дополнительный: 2 (bigc param). Этот вариант более streamlined и straightforward. (Можно, кстати, ввести еще и тип 3 -- bigc.)

    Тогда можно будет адресоваться к таким каналам-параметрам напрямую, как к обычным каналам. И в БД им могут авто-даваться имена вида "...ИМЯ_БОЛЬШОГО_КАНАЛА.ИМЯ_ПАРАМЕТРА".

    В любом случае, останутся проблемы:

    1. Нынешняя реализация MarkRangeForIO()/ConsiderRequest() упаковывает вместе для передачи драйверу в одном запросе все однотипные каналы, принадлежащие одному драйверу. А надо -- по границе большого канала делить. (Вопрос, конечно, технический, и несложно решаемый, но все же...)
    2. Получится пресловутый aliasing -- одна и та же сущность будет видна как две разных. Опыт с УРами/СДСами -- WR1/WR8 показал, что это хреновато.
    3. Надо на факт "ReturnBigc()" реагировать дополнительно, в смысле каналов -- если на них зарегистрирована подписка, либо запрос "пришли при готовности данных". Также лишние/неприятные действия.
    4. И, опять же, при изменении параметров через API больших каналов должны меняться также и данные в таких парамо-каналах. Т.е. -- этакий "искусственный ReturnChanGroup()" из ReturnBigc().

    Не самая красивая реализация выходит, но -- довольно практичная и заведомо реализовабельная и работоспособная.

    Понятно, конечно, что внутренняя реализация/мозги cxsd_channels/chsd_bigs для будущего более гибкого API получатся совершенно другими, и, возможно, описанные в этом разделе идеи совсем не пригодятся, либо проблемы в будущей парадигме решатся сами собой. Но -- есть шанс, что подобные проблемы возникнут и там.

Групповой инкремент каналов в Chl/Knobs:
  • 24.02.2005: сегодня Старостенко прикопался с такой вещью: что иногда надобно, чтобы ОДНИМ движением менялась сразу куча каналов. Например -- в rfsyn'е чтобы жмешь стрелку вверх на одном канале ГЗИ, а вместе с ним чтоб менялись еще некоторые.

    24.02.2005: Идея Старостенки, как это сделать:

    Чтобы к каждому такому полю прилагалось по checkbox'у, и когда пометишь несколько полей, а потом начинаешь менять одно из них, то одновременно на столько же меняются и остальные. (А если меняем канал, НЕ помеченный, то это больше ни на ком не сказывается.)

    Естественно, первоначально мне сия мысль не очень глянулась: чекбоксы -- это лишнее место на экране... Стал думать -- а как бы этак сделать, чтобы ЛЮБУЮ ручку можно было так "выделить": обводить доп. бордюром -- места нет; ввести еще один colstate -- тоже нехорошо, будет интерференция (у кого приоритет -- у помеченности или у красноты?).

    Кроме того, сразу в мозгу прозвучал звоночек: а, собственно, что -- менять каналы в группе только на то же самое число? Т.е., все, например, на единицу? А на что-то пропорциональное (INC*coeff) через некоторое время господа физики не захотят ли? -- Да, сам Старостенко об этом тоже задумывался, но ни к чему конкретному не пришел. Сошлись на том, что он подумает, а я поговорю с вэппманами -- у них подобные вещи используются.

    Как можно было бы сделать "коэффициент": либо просто "распространять" событие нажатия по всем "помеченным" ручкам, а те пусть пользуются своим полем шага, либо ввести к шагу еще одно поле -- "шаг-2", которое и будет "коэффициентом" (по умолчанию -- 1.0).

    И так случилось, что через пять минут позвонил Карнаев, и я с ним на эту тему пообщался. Как всегда, у них все оказалось проще, чем я прикидывал (ну еще бы -- сделано давно, на Одрятах, многое делать было просто лень :). Итак:

    У них эта фича называется "связанные каналы". В описателе канала можно указать, что к нему привязаны еще такие-то (список), и когда его крутят, то те тоже крутятся (хотя оператор этого почему-то не может видеть "можно было сделать, но не стали" -- какие-то ограничения пользовательского интерфейса -- что мешает просто нарисовать в том же "окне" и привязанные каналы?). И при желании можно привязать каналы друг к дружке -- чтобы трогая любой из них, двигались оба.

    И сдвиг ВСЕГДА делается на ОДНО И ТО ЖЕ число -- они не стали вводить коэффициент. Собственно, Карнаев так и посоветовал -- сделать именно такой простой вариант, а если кому-то мало будет -- пусть улучшают сами.

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

    Что касается пользовательского интерфейса "пометок":

    • Либо делать "связывабельными" только текстовые поля, и тогда несложно в дополнение к TEXT и TEXTND ввести еще и "TEXTGROUPABLE", когда в форму-контейнер добавляется также маленький чекбоксик.
    • Либо -- можно ввести эту фичу во все не-примитивные ручки, т.е. те, у кого есть контейнер-форма. И -- в каком-нибудь (левом верхнем?) углу поверх всех кладется малюсенький виджетик-прямоугольничек, который в состоянии "помечено" managed, а в непомечено -- unmanaged. А команда отметить/разотметить дается как-нибудь отдельно -- типа "Shift+Пробел", или Ctrl+Btn1.

      (Где-то я эту идею подсмотрел -- в Excel'е, либо в Corel'е, да и в XFig'е при инструменте Edit все точки помечаются малюсенькими прямоугольничками.)

    Осталась пара вопросов:

    1. А что, если пользователь просто ВВЕДЕТ число в текстовое поле, безо всяких стрелочек -- тогда связанные поля сдвигать на разницу (новое-старое), или не трогать, а реагировать только на стрелки?
    2. И -- ведь механизм-то отмечания неэквивалентен привязыванию. Во-первых -- привязка указывается перманентно, прямо в файле, и, с одной стороны, не надо при каждом запуске отмечать каналы, а с другой -- снять привязку нельзя.

      И, кстати -- а не ограничить ли "группы отмеченности" границами элементов?

    Я склоняюсь к тому, чтобы сделать в простейшем варианте -- с чекбоксами при текстовых полях, при помощи "LOGD_TEXTGROUPABLE", и с какой-нибудь простенькой передачей событий -- либо события нажатия стрелки, либо события "сдвинься на такое-то число".

    25.02.2005: да, дал прочитать все вышенаписанное Старостенке -- он согласился/одобрил. И -- стоит сделать именно в простейшем варианте. Ответы на вопросы:

    1. При вводе числа в текстовое поле меняется только это поле, другие не трогаются.
    2. "Перманентная", "жесткая" привязка не требуется -- нужна именно возможность произвольно, по желанию в живой программе "связать" несколько каналов.

      И -- естественно, связывание ограничивается внутренностями одного элемента.

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

    11.04.2005: СДЕЛАЛ, именно по простой схеме, описанной выше. Highlights:

    • Введен новый код отображатора -- LOGD_TEXTGPBL ("text GrouPaBLe").
    • При оном дополнительно создается non-traversable чекбоксик, к которому уже прикрепляется текстовое поле. Чекбокс создается "без метки" при помощи тех же танцев с бубном, что и в alarmonoffled (оттуда и скопировано).
    • Учтен опыт проблем с колоризацией виджета [v\^] (14.09.2004), так что сразу добавлено отдельное расцвечивание чекбокса.
    • Введено поле knobinfo_t.is_ingroup, каковое этим чекбоксиком и меняется.
    • Делается так, словно это нажатие поступило раздельно во все задействованные виджеты, НО -- изменение производится на шаг ВИНОВНИКА. Это фича такая -- шаг изменения зависит от того, из какого виджета нажимаются стрелки.
    • Там в TextUpDownHandler() имеется собственный цикл по содержимому uplink'а, который проверяет, что если LOGT_WRITE1 и is_ingroup, то проворачивается указанная операция. Сам "виновник" при этом пропускается -- с ним махинация проделывается отдельно, как раньше.

      А по-хорошему следует завести в Cdr'е итератор, который бы проходил по всем child'ам указанного элемента, при надобности -- даже рекуррентно. (Хотя в данном случае рекуррентность -- малополезна, ведь если вызов из верхнего уровня нижний затронет, то из нижнего -- верхний оставит в неприкосновенности.)

    • Там имеется некий интеллект, проверяющий, К ЧЕМУ добавлять/убавлять: то ли к curv, то ли к userval. Если бы всегда использовать curv, то будет некорректно -- два нажатия подряд, БЕЗ прихода между ними данных от сервера, использовали бы одно и то же значение поля curv.
    • Возможность переключать "загруппированность" с клавиатуры отсутствует.
  • 15.04.2005: на тему дальнейшего развития -- инспирировано мыслями о будущем использовании в nmagsys.

    15.04.2005: итак: с местом в nmagsys'е туго, чекбоксы там впихивать просто некуда, так что надлежит думать о других вариантах пометок. Есть пара вариантов:

    1. Старая идея -- о "квадратиках в уголках". Штука очень правильная, места совсем не занимает, недостаток один -- включить мышью никак нельзя.
    2. Еще мысля, пришедшая в голову и мне, и Старостенке: перед текстовым полем иметь тонкую вертикальную полоску в пару пикселов -- этакую имитацию чекбокса. В обычном состоянии она или вообще не видна, или бледная, а во включенном -- либо как чекбокс, либо какая-нибудь цветная. Плюсы -- она активируется мышью; минус -- все же занимает некоторое место.

    Кроме того, насчет интерфейса активации/деактивации:

    • Нужна клавиатурная комбинация.
    • Особенно для "квадратика в уголке" -- требуется комбинация типа Ctrl+Shift+Button1.
    • Нужна комбинация для снятия всех отметок скопом.
  • И вообще -- на тему обобщения:

    А не заменить ли отдельный тип виджета "...GPBL" на knobinfo-флажок "groupable"? Ведь группировабельность может требоваться не только LOGD_TEXT'у, но и в других "плавно изменяемых" (т.е. -- недискретных) виджетах -- в LOGD_SLIDER и LOGD_{DIAL,KNOB,GAUGE}.

    (Ох, доунифицируемся мы текст, слайдер и ручки! :-)

    27.05.2005: так, а почему дата не проставлена?! >:-| В любом случае -- идея реализована, так что -- "done".

  • 05.05.2005: а еще -- надо бы "подружить" группировабельность с режимами. В частности -- уметь СОХРАНЯТЬ статус "сгруппированности" вместе с режимом.

    Плюс -- еще дальнейшие усовершенствования.

    05.05.2005: это потребует:

    • Ввести behaviour-флаг "groupable".
    • Ввести параметр "grpcoeff" и флажок "grpcoeffchg".
    • Ввести в Chl_knobprops.[ch] поддержку его изменения (при уставленном KNOB_B_IS_GPBL.
    • Ввести в Cdr{Save,Load}GrouplistMode() поддержку ключевого слова "grouped" и параметра "grpcoeff" (сохраняемого при уставленном grpcoeffchg).
    • Возможность программного управления отображением флага is_ingroup -- чтоб он модифицировался при восстановлении режима.

    По нынешней поддержке text-widget'ом:

    • Заменить нынешний LOGD_GPBL widdepinfo-флагом "groupable".
    • В связи с чем сделать в Knobs_text_widget.c нормальный psp-парсинг.
    • Ввести также widdepinfo-флаг "grouped", позволяющий сразу создавать уже загруппированный виджет.

    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".

  • 07.05.2005: насчет "итераторов, как делать обход, рекуррентно ли" -- есть мысля:

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

    Это точно понадобится где-нибудь в magsys'е, по той же причине, что обозначена за 22.10.2004 -- элементы часто являются лишь способом расположения каналов на экране.

    Кстати, это чем-то начинает напоминать поведение форм -- <FORM> в HTML -- они создают "параллельную" иерархию.

    23.05.2005: да, а по-хорошему тогда надо бы делать и ГОРИЗОНТАЛЬНЫЕ барьеры, а не только по вертикали -- как раз в nmagsys это будет намного полезнее. Вот только сие реализовывать будет сложно -- барьер по окончанию-то несложно, а вот начало искать -- посложнее будет...

  • 18.05.2005: пристал к Старостенке на тему "какой вообще вариант, в пределе, «коэффициентов» может потребоваться, включая самые бредовые идеи?". Результат есть:

    В пределе может захотеться делать формулы -- что менять не просто на коэффициент, а с учетом значений некоторых каналов, например,

    delta = a + chan1*k1 + chan2*k2

    Что ж -- формулы у нас есть, так что даже этот, наиболее фантастичный, вариант сделать можно. Надо будет только

    • Уметь передавать формуле некоторые параметры, а то сейчас параметр один -- userval (или делать это через localregs?).
    • Уметь брать не только ci->physval, но и ci->"userval" (ныне есть лишь usercode) -- чтобы корректно отрабатывались формулы по ОТОБРАЖАЕМЫМ значениям, еще ДО того, как вернется ответ-подтверждение от сервера. (Именно с этим сейчас связано использование victim->userval в Knobs_internals.c::TextUpDownHandler().)
  • 24.05.2013: наблюлась странность: в магнитной коррекции накопителя (но, скорее всего, в прочих тоже) если загруппировать сразу много ручек, и потом начать их интенсивно крутить (что зажать стрелку, что переменчиво вверх/вниз), то в какой-то момент происходит рассинхронизация: часть значений как будто пропустила шаг.

    24.05.2013: есть две гипотезы:

    1. Проблема происходит в "точке перехода через 305" у Козака (набор точек, когда соседние числа дают расхождение >305uV).
    2. Уставка не успевает отрабатываться (сгруппировано ж МНОГО), и сервер возвращает (пусть и с тэгом>0) предыдущее значение -- причём, видимо, ДВА цикла подряд, так что алхимия с "userval_valid" перестаёт работать.

    Скорее всего, проблема во втором варианте. Или оба фактора сразу?

    Второй -- в будущем явно надо как-то более корректно проверять; например, не сбрасывать "wasjustset (и userval_valid?)", пока age не покажет, что чтение драйвером произошло ПОСЛЕ модификации в клиенте.

Об очередях в драйверах
  • 27.02.2005: у нас имеется уже ТРИ драйвера, которые реализуют очереди на отсылку: canadc40_drv.c (в перспективе -- marcankoz*), kshd485_drv.c и impacis10_drv.c. И просматривается, мягко говоря, некоторая общность.

    27.02.2005: хронология была такой:

    • Первым был kshd485_drv.c -- там было просто НЕОБХОДИМО иметь очередь запросов, а из-за дурацкого устройства аскольд-волковского протокола (да и из-за того, что связь может быть просто прервана) требовалась также функция "перезапроса по таймауту". Это уже тогда делалось примерно по тому сценарию, который был посоветован Токареву 18.06.2003 (см. bigfile.html). Это было сделано довольно простенько, "по быстрому", со сдвигаемым буфером.
    • Далее механизм "очередь запросов, с поддержкой перепосылок по таймауту" появился и в моей реализации CAN-драйверов. Здесь я уже осознал, что буфер следует делать кольцевым. На данный момент механизм в canadc40_drv.c еще не завершен и до конца не испытан. Но в нем появился довольно хитрый (точнее -- выматывающий из программиста душу необходимостью точно-математического разбора всех вариантов :-) механизм "условной постановки в очередь" и, более общо, "итераторов по очереди (foreach...)".
    • А при реализации impacis10_drv.c вылезла еще одна потребность (которая есть и в предыдущих случаях, но тут почему-то она проявилась наиболее ясно и необходимо) -- уметь заменять содержимое команды, помещенной в очередь, но еще не отправленной в устройство. Например -- если вначале приходит команда "установи A:=1", а потом тут же -- "установи A:=0".

    Напрашивающиеся выводы:

    • Этот же механизм "исправления" очевидным образом будет полезен и в двух предыдущих случаях.
    • Уже последнюю реализацию -- в impacis10_drv.c -- я делал почти копированием с canadc40_drv.c, временами поглядывая и на kshd485_drv.c (поскольку тот тоже работает через COM-порт, да и тоже с байтовыми строками -- реализацию fd_p() брал именно оттуда).

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

      И там же надо будет исправить некоторое количество "недодумок" -- в частности, сделать, чтобы "do_send" имел бы коды возврата не просто 0/+1/-1, а унифицированные с "условной постановкой в очередь".

      Основная проблема на пути реализации такого обобщенного механизма/библиотеки -- разнородность клиентов, по-хорошему требующая механизмец типа C++'ных templates. Хотя -- в connlib'е ж мы выкрутились, хитрым сочетанием обычного C и препроцессора. Да и в CAN-реализации уже кое-какие наметки (чисто C'шные) имеются. Так что -- прорвемся :-)

    • В impacis10_drv.c проявилась еще одна вещь/потребность/кривость, пока вообще никак не решенная.

      Пусть у устройства есть отдельно команды "выставить значение в A" и "выставить значение в B" плюс одна команда "прочитать A и B" -- таков IMPAC IS10, у которого команда "pa" возвращает море всякого. Тогда -- возможен сценарий, при котором драйверу приходят запросы на запись в A и в B, он при этом, для подтверждения, сразу после команды записи запросит и чтение, которое вызовет ReturnChanGroup() и для B.

      НО!!! Ведь тем самым он в ответ на "запиши в B" вернет не подтверждение выставления запрошенного значения, а СТАРОЕ значение -- и оно будет сервером и клиентами воспринято как новое, а позже -- словно само по себе появится "опять изменившееся" значение. И это со всеми вытекающими последствиями, типа оранжевения не к месту, как было (есть :-) у глючных отокаревских CAN-драйверов.

      Как решить эту проблему? Помечать при постановке в очередь запроса на запись этот канал как "не-следует-его-возвращать", т.е. блокировать? А когда тогда разблокировать, по какому принципу -- ставить в очереди вместе с запросом и пометку "как отправишь этот запрос -- разблокируй то-то"? Шибко уж это муторно, да и надо тогда не забывать при разных видах "сбросов" (очистка очереди, пропажа связи, etc.) надлежащим образом подобные пометки сбрасывать... Б-р-р-р!!!

    20.04.2005: возникла еще одна "общая" потребность:

    • Флаг-пометка в элементе очереди "удалить сразу после отправления".
    Это вылезло в canadc40_drv.c при работе с выходным регистром. Детали/объяснение см. в разделе "Введение очереди с таймаутами в CAN-драйверы" за сегодняшнее число.

    25.04.2009: насчет "Б-р-р-р!!!" -- надо ставить не пометки "блокировать", а пометки "вернуть", и ставить их надо в callback'е по отправке пакета. А при сбросе очереди -- нулить их (ну и при получении ответа -- тоже нулить :-)). Тогда всё становится ОЧЕНЬ просто.

    (Идея-то эта оформилась в голове уже давным-давно, но почему-то до сих пор не была записана.)

  • 05.05.2005: есть еще одно правило, которое надлежит соблюдать: при "восстановлении связи с устройством" -- это может быть как получение пакета GETDEVATTRS/0xFF от CAN-устройств, так и получение ответа на "ping" после возникновения таймаутов -- надлежит ОБЯЗАТЕЛЬНО вычитывать все возможные параметры, хранящиеся в устройстве.

    05.05.2005: Для CAN-устройств это значение выходного регистра, для КШД-485 -- всякие serial'ы, для Impac IS10 -- аналогично. Плюс, в обязательном порядке -- все каналы записи, хранящиеся в устройстве.

    Есть еще полурелигиозный-полуфилософский вопрос: при таком восстановлении связи -- очередь сбрасывать? Вот kshd485_drv.c сбрасывает ее прямо при ПОТЕРЕ связи; и там это к месту. А в остальных случаях? 06.02.2011@Снежинск-каземат-11: да, однозначно сбрасывать. Так сейчас везде и делаем.

    27.04.2009: этот принцип соблюдается уже года три -- с введением SetBlockStatus() и нынешней (2-й) версии CAN-драйверов, так что "done".

  • 03.09.2005: (придумано и записано на пляже) примерный первоначальный проект такой общей библиотеки -- "sendqlib".

    Заводим структуру -- "описатель очереди":

    typedef struct {
      size_t    elem_size;
      sq_snd_t  sender;
      void     *ring;
      int       ring_size;
      int       ring_start;
      int       ring_used;
      . . .
    } sendq_t;
    

    И некоторое количество методов:

    int  sq_init (sendq_t *q, int qsize, size_t esize, sq_snd_t sender);
    void sq_clear(sendq_t *q);
    int  sq_enq  (sendq_t *q, void *e, sq_enqcond_t how);
    int  sq_foreach(...);
    

    "Поверх" же этой библиотеки, для ее использования в каждом конкретном случае, наворачивается маленькая "конкретизующая" библиотека-wrapper (как сейчас сделано в сервере -- *fdio.[ch] поверх connlib'а), которая и реализует метод "sender", и предоставляет своим клиентам "правильный" интерфейс -- с ZZZelem_t* вместо void*.

    22.06.2012: поскольку sendqlib наконец-то не только сделан, но уже и проверен, то "done".

  • 23.02.2009: идея, как "бороться" с устр-вами, у которых 1 команда читает сразу кучу параметров (уставляемых ОТДЕЛЬНЫМИ командами': Тривиально - надо иметь флажки на каждый параметр, "был ли он запрошен", и если да - то делать ему ReturnChan*(), иначе - нет. Уставлять же флажок - при чтении, а также при записи с последующей отправкой тестовой команды чтения "всего".

    23.02.2009: ... Неа, зад: если прилетает ДВА запроса на разные каналы, и 2-й еще до получения ответа на 1-й, то флажок 2 выставится сразу, и ответ на 1-й заодно вернет и 2-й, значение которого еще старое. И даже более того - по 1-му пакету флаг будет сброшен, и 2-й ответ проигнорируется...

    И че ж теперь -- вводить в элементы очереди еще и "callbacks", вызываемые в момент отправки пакета (чтоб взводило флаг в нужный момент, а не раньше)?

    Да, так и делаем! Еще некоторые детали будущей реализации:

    • Назовем именно sendqlib, а жить будет в libuseful (поскольку по классу оно похоже на seqexecauto).
    • Для добавления элемента в очередь будет "общая" функция, принимающая ВСЕ возможные параметры (oneshot, callback-on-send, ...), а остальные "короткие" convenience-функции будут лишь её обертками.
    • "Параметризация" будет устроена так: делаем стандартную структурку, содержащую поля, общие для ЛЮБОЙ очереди, и клиент должен будет обязательно размещать её в начале своей структуры элемента. Этими общими полями являются:
      1. oneshot -- надо ли выкинуть элемент из очереди сразу после отсылки.
      2. timeout_duration -- длительность ожидания перед повторной посылкой (если ==0 -- то используется длительность по умолчанию, указанная при инициализации очереди).
      3. callback + privptr -- функция-callback, дергаемая в момент отправки элемента. Вероятно, этой функции надо также передавать флаг "элемент отправляется впервые/перепосылка".

    И, кстати, список клиентов sendqlib'а:

    • cankoz.
    • impacis1x (если будем переделывать, то -- сразу с поддержкой N устройств на одной линии).
    • kshd485 (если вдруг понадобится).
    • Термодатовские железки с RS232/385.
    • Возможно, Tektronix'ы (хотя там -- хбз, понадобится ли очередность, или можно обойтись асинхронной работой по TCP, как с cm5307).
Алгоритм автоопределения набора измеряемых каналов для драйвера типа Липа и CANADC40
  • 15.04.2005: постановка проблемы: для драйверов многоканальных АЦП либо приходится указывать драйверу набор каналов заранее (CANADC40), либо он меряет все по штучке, по мере надобности (Липа Ц0612).

    16.04.2005: идея эта была инспирирована разговором с Гусевым, который недоуменно потыкал в крейт магнитной коррекции, любопытствуя -- а почему, собственно, там все время горят (мерцают с высокой частотой) светодиоды L и Bus? И действительно -- а с чего я так неоптимально произвожу измерения -- ведь Липа позволяет заказать все каналы сразу, и как будет готова -- свистнет.

    Вариант решения: драйвер должен ПОМНИТЬ, что его просили мерять, т.е. иметь список-набор каналов. Например, массивом, в котором ноликами обозначены "ненужные" каналы, а единичками -- запрошенные.

    Наборов таких должно быть несколько (на примере Ц0612):

    • Текущий, по которому ведется цикл измерений -- он "неким образом добывается" перед началом цикла, и далее не трогается. Драйвер может запросить либо измерения с минимального номера до максимального, либо "интеллектуально" идти по списку, вычленяя последовательные группы единичек и запрашивая измерения групп (в пределе это будет одна группа -- с нулевого по последний).

      В случае с Липой Ц0612 надо будет находить не просто группы единичек, а еще и таких, у которых уставки (T,D) одинаковы.

    • "Прошенный" -- в него проставляются единички, когда прилетает от сервера запрос на некий канал.
  • 07.11.2005: кстати! А можно ль для canadc40 и cac208 поступать проще -- если драйвера спрашивают о канале, который ВНЕ сконфигурированного диапазона, то просто послать блоку запрос на измерение этого канала?

    09.11.2005: а вот хренушки -- НИКАК нельзя от этих CAN-АЦП попросить измерить канал #N: просто нету такой команды, да и сама идеология работы устройств подобного не допускает -- нету у них "одноразового" режима, не говоря уже о том, чтобы он корректно вклинивался в нормальную многоканальную работу.

Изготовление инфраструктуры для reconnects и driver's API и более корректная отработка каналов записи
  • 17.04.2005: план действий таков:
    • Добавление поддержки 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: сделано.
    • Вставить в cm5307_drv.c поддержку автоматических reconnect'ов с использованием вышеперечисленной функциональности. (Естественно, с переводом перед connect()'ом дескриптора в неблокирующееся состояние.)

      И надо б иметь там флажок типа "is_suffering" по образу и подобию cda'шного -- чтобы оно ругалось только при переводе его в 1, а не постоянно. 28.06.2005: новый cm5307_drv окончательно протестирован -- работает.

    20.05.2005: и еще пункт --

    • одновременно надо будет позаботиться, чтобы при "-wY" на каналы ЗАПИСИ неработающих блоков также отдавалось бы что-нибудь нехорошее типа age>5.

    29.06.2005: поскольку давно (25.05.2005) стало ясно, что вышеозначенное требование -- бессмысленно (такие каналы и без того будут бордоветь/поболотневать), то помещаем его в <S>.

    14.04.2006: поскольку все сделано еще прошлым летом, то -- "done".

"Умная" процедура выполнения reset'а источнику
  • 18.04.2005: возможно, будет потребность выполнять для источников такую последовательность шагов:
    1. Юзер жмет в окошке кнопку [Reset].
    2. Запомнить текущее значение.
    3. Отправить в блок значение "0" (на экране же -- оставить то, что было!).
    4. Отправить куда-то еще команду "reset" (обычно -- просто, например, в канал УРа).
    5. Подождать некоторое время (несколько секунд).
    6. Отправить в блок старое, запомненное значение.

    Если в течение этого процесса юзер начинает что-то делать -- обломить весь процесс нафиг.

    Так вот -- похоже, все это прекрасно можно реализовать прямо на формульном языке cda. При этом используются механизмы:

    • Регистры -- для запоминания старого значения и "флагирования" процесса сброса.
    • CMD_GETTIME -- для задержки в N секунд.
    • CMD_CASE -- для проверки, что этот режим и потому надо в читающей формуле отдавать не реально имеющееся в канале значение, а то, что в регистре.

    Что еще? Чего сейчас не хватает? Похоже, только условной операции записи -- как в канал, так и в регистр.

    А может, все-таки сделать наконец именно ПРЕФИКСНУЮ архитектуру -- как в каких-то современных процессорах? Ее вариант в нашем случае будет таким: есть отдельно команда, могущая уставить "флаг пропуска", а ВСЕ команды реально делают свои действия только в случае, если этот флаг не уставлен. И по умолчанию этот флаг все время сброшен.

    И -- кажется, при сим мы будем иметь-таки ситуацию, когда команда записи будет производиться из ЧИТАЮЩЕЙ формулы...

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

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

    • Заводить "очередь из двух флагов" -- thisflags и nextflags.
    • В начале -- при выборке очередной команды -- делать "сдвиг" 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.".

    • Во-первых, OP_ALLOW_W работает, и как надо.
    • Во-вторых, TEST также отрабатывает как планировалось.
    • В-третьих, CMD_BREAK_I сильно облегчает жизнь -- вместо "постоянного префиксирования всех ветвей" смог обойтись двумя "if (...) break"'ами.
    • В-четвертых, я долго-долго маялся, пытаясь понять, что ж оно не пашет -- оказалось, что я из ВРЕМЕНИ ОКОНЧАНИЯ вычитал текущее, и оно сразу же было положительным, а я по >0 считал что "истекло". Надо либо считать, что истекло, когда стало <=0, и тогда пропускать BREAK (как я сейчас и делаю), либо вычитать ВРЕМЯ ОКОНЧАНИЯ ИЗ ТЕКУЩЕГО.
    • В-пятых, ежику понятно, что для оптимизации надо СРАЗУ, в команде записи, прибавлять время ожидания и сохранять в регистре уже время окончания.

    Приведу тут весь "код" программы, записи для:

    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 по следующей схеме:

    1. В стек пихаются три числа, в зависимости от операции сравнения: < -- 1,0,0, >= -- 0,1,1 и т.д. Символ "<" отвечает за 1-е, "=" -- за 2-е, ">" -- за 3-е (тут, кстати, очевидно, что паскалевско-бэйсиковское обозначение "<>" логичнее сишного "!=").
    2. Потом запихивается левый операнд, за ним правый, и производится вычитание, а затем _CASE. И -- вуаля! -- имеем 1 при истинности сравнения и 0 при ложности.
    3. А потом остается просто сделать _TEST, и -- готовы флаги для ограждаемой команды (параметры которой, естественно, должны быть отправлены в стек еще ДО махинаций со сравнениями).

    Единственное неудобство -- шибко уж длинно и громоздко. Но -- а какова альтернатива? Иметь трехоперандную команду "сравнение", которой первыми двумя операндами указывать 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 не было точки с запятой. Добавил.

Об интерфейсе удаленных драйверов
  • 25.04.2005: давно грызет, что драйверы, работающие на удаленных системах (cm5307, будущий CAN-GW) несколько "совсем не такие", они МОНОПОЛЬНЫ, и неспособны работать в составе 1-threaded-сервера.

    Надо обеспечить "подчиненным" драйверам ОБЫЧНЫЙ интерфейс, такой же, как предоставляет cxsd_driver.h.

    01.07.2005: да, в развитие той мысли -- идеи, родившиеся о вопросе "расшивки" DriverBody() (постановка проблемы там -- чтоб драйвлет мог иметь более хитрую архитектуру, чем просто тупые "ответы на вопросы" о чтении/записи/bigc -- например, для ИПП).

    1. Выносим нынешнее содержимое DriverBody() в "HandleInput()", вызываемую по select()-готовности дескриптора 0 на чтение. Она возвращает код -- -1:Fail, 0:Nothing, 1:Config, ?:Chanop, 2:Bigc, ..., и заполняет передаваемый ей union данными из пакета.

      Замечание: буфер этой функции предоставляется ее вызывателем

    2. Также процесс отсылки данных как каналов, так и bigc выполняется функциями с именами ReturnChanGroup() и ReturnBigc(), с тем же списком параметров, что и в cxsd.
    3. Соответственно, остающийся для совместимости (поддержки нынешних абстрактных драйверов) DriverBody() становится просто "клиентом" вышеописанной системы.
    4. RegisterDriverTimeout() с теми же параметрами, что в сервере; работает с "0-м таймаутом" (будущего многотаймацтного интерфейса).
    5. RegisterDriverFD() -- аналогично.
    6. А уж переделать DoDriverDebug() в DoDriverLog() -- сам бог велел :-)
    7. Следствие: в API даже *нынешних* драйверов понадобится параметр magicid.
    8. Все "локальности" (столь долго не дававшие мне покоя -- всякие дурацкие "DRIVERLOCAL()") решаются при помощи privrec'а.
    9. Расшивка однопоточности:
      1. Имеем в privrec'е массив флагов "reqd[NUMCHANS]" (и, возможно, reqd_v[] -- для записи).
      2. Там же параметр "curchan" (или "curset", для ИВА).
      3. Параметр "curop" (которая стадия действия) -- опциональный, для случаев, когда измерение происходит в N стадий, как, возможно, в ИВА.
      4. И имеем "цикл" в стиле sea, бредущий по этому списку, и если "reqd[curchan]" выставлено, то инициирующий действие и регистрирующий таймаут на надлежащее время. (LAMs?)

        Соответственно, если список пуст -- надо "засыпать", и заново активизироваться по приходу запроса.

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

      Замечание 1: для ЦАП'оподобных и прочих "не-интегрирующих" блоков такая алгоритмика не требуется -- ведь работа их драйвера сводится к чтению/записи регистров, каковые операции происходят практически мгновенно.

      Замечание 2: а вот для случаев типа ППИ и/или периферийных крейтов -- т.е., когда выполнение самого NAF'а не является "мгновенным" -- такой алгоритм, возможно, и не самый лучший. Тут уж можно выбирать из него и того, что сейчас используется для CAN-драйверов.

    10. А можно ли это все формализовать, чтоб были реально абстрактные драйверы -- и для cm5307, и для in-server?
    11. Ведь, собственно, вышеописанное -- и есть "кошерный" алгоритм работы таких "драйверов многоканальных блоков".

    Решение на сегодня: Собственно, для решения проблемы ИПП достаточно будет "малой крови" -- первых трех пунктов. Остальные же "глобальности" оставим на будущее -- если когда-нибудь реально потребуется возиться с "не-slave" CAMAC-драйверами. Пока это -- нафиг не нужно.

    16.05.2006: dbody 25.07.2013: сейчас всё сделано даже лучше -- унифицированный API, вместо dbody реализация через remdrvlet+remcxsd на основе cxscheduler, с LAM-via-pipe.

    15.05.2012@Снежинск-каземат-11: поскольку планируется в v2hw/ делать новую версию абстрактных драйверов, так, чтобы они годились как для cm5307, так и для сервера, то:
    1. Что делать с ППИ -- действительно загадка, уж слишком оно всё ломает. Либо правда на sendqlib -- там формально всё есть, но...

      Короче: в отличие от CAN, где только посылки и легко сделать layer для селивановского CANGW, NAF'ы требуют и ответов, так что сделать "интерфейс" для ППИ -- не удастся, там вся структура драйверов должна радикально меняться. Так что, ППИ -- в топку.

    2. Если захочется делать годящиеся-для-сервера CAMAC-драйверы для блоков с НЕмгновенной отработкой (типа той же Липы) -- то однозначно по проекту (9).
    3. А вот абстрактные драйверы -- пусть уж будут ТОЛЬКО для простого железа, которое безо всяких LAM'ов.

    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.
    • В результате в какой-то момент делалось rqn=0 при cur=0 (т.е., НЕ -1!), а потом при вызове из LAM_CB() делалось еще раз rqn--, превращаясь в -1.
    • И после этого уже никогда StartMeasurement() не делалось, а только сплошь молотился возврат с CXRF_IO_TIMEOUT из TOU_CB() (ведь cur-то оставалось >=0).
    • Странно скорее то, что оно происходило столь нечасто. Видимо, в редких случаях, когда почему-то щелкнул таймаут, а потом и LAM. Например, процессор CM5307 был чем-то сильно загружен и не давал управления драйверу. А таймауты обрабатываются ДО дескрипторов, так что если даже блок не виноват и LAM успел возникнуть за это время, то всё равно будет сначала таймаут (и потом тут же LAM).
    • Кстати, при многоканальных измерениях в таком случае сразу после таймаута будет считаться "сработавшим" следующий канал (только что запрограммированный на измерения), и ему отдастся измерение от "оттаймаутившегося".

      Но как решить такой прикол -- уже неясно, поскольку получение прерывания отделено от его обработки, и в момент таймаута нет возможности гарантированно погасить LAM/IRQ.

      Касается, кстати, и PCI'ного железа, типа ADC200ME.

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

    14.01.2014: да, в c0612 глюки тоже были -- вылезло на магнитной системе линака. Там оно выражалось в том, что каналы бОльшую часть времени не мерились и стояли гусино-синие. А при натравливании туда клиента c0609 (они совместимы по карте) резко всё начинало мериться -- поскольку запрашивались все каналы, без дыр.

Некоторые "общедрайверские" принципы
  • 06.05.2005: если пишем драйвер для какой-нибудь экзотической железки, то надо постараться МАКСИМАЛЬНО реализовать ее функциональность, в частности, поддерживаемую DOS'овской программой.

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

    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).

  • 01.12.2005: (опять в душе :-) нынешний kshd485_drv.c при любых протокольных ошибках -- csum!=0, последовательность (SHIFT,STOP) в пакете -- тут же делает DisconnectBlock().

    Подобное поведение хорошо для отлова проблем с железом либо кабелем, НО: при hotplug'е оно ОЧЕНЬ МЕШАЕТ, поскольку в момент включения подобные ляпсусы по кабелю вполне могут приходить. Как решать?

    • Проект 1: считать такие ошибки ("err_ctr++"), и отстреливаться только при превышении некоего порога -- например, 5...10 раз.
    • Проект 2: поскольку при постоянных включениях/выключениях счетчик ошибок все-таки переполнится, можно его сбрасывать по приходу правильных пакетов.

    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).
    • После добавления DoReset()'а -- стало писать лавиной.
    • Причём, ДО -- можно было даже не только рестартовать сервер, но и просто сделать sato драйверу, и всё очухивалось. Отсюда и возникла мысль, что всё окей, но просто драйвер у себя что-то не сбрасывает.
    • Возникло подозрение, что дело в каком-то COM-port'овском таймауте/задержке, и подозрение пало на close_delay, которое обычно 50=0.5с. Хотя и странно -- у нас-то выдерживался интервал аж в секунду...
    • ...

    11.02.2011@Снежинск-каземат-11: тьфу ты, там всё было просто и тривиально -- результат дурацкого misdesign'а, сделанного еще при рождении драйвера (в 2003-м?).

    • Я-то думал, что r=0 -- это EOF, и потому даже вставил хитрую паузу на 5с, чтоб оно пере-открывало COM-порт в _tout_p, для чего ввёл искусственный код пакета 0xFF.
    • Но это не помогло -- всё равно пёрло r=0!
    • А вот sato почему-то помогало, так что в порядке проверки вместо пере-открытия был внаглую вставлен вызов ResetBlock(magicid) -- и он давал результат!!!
    • Подумал, поискал, и понял: просто перед read() НЕ делалась проверка на переполнение буфера, и оно просило 0 байт -- а по стандарту, "If count is zero, read() returns zero and has no other results.". Так что это был никакой не EOF. ТщателнЕе надо!!!
    • Так что -- во-первых, вставил проверку, не заполнен ли уже буфер, а во-вторых, DoReset() его сбрасывает, делая inbufused=0.
    • И, скорее всего, никакой re-open() там и не требовался, всё бы отлично и так работало -- вовсе RS232 не так жуток и загадочен. Но убирать всю эту алхимию уже вломы.

    Итого -- локальная проблема решена, но вообще-то нефиг больше писать такие халтурнейшие драйвера.

    07.09.2011: ёлки, ведь еще тогда в Снежинске проверил, всё работает -- так что "done".

  • 27.02.2006: насчет "командных каналов": потенциальная проблема заключается в том, что при загрузке режима вызывается 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 с оным содержимым.

  • 30.08.2006: насчет порядка параметров больших каналов -- надо ВНАЧАЛЕ пускать наиболее важные -- PTSOFS, NUMPTS -- а уж за ними остальные. А то в adc200 наоборот -- и это криво, ибо нельзя пометить numpts как persistent без того, чтобы предыдущие (а в нем это -- все остальные) также неявно не стали persistent...

    (Ну да, в CXv4 должон быть интерфейс индивидуальной адресации параметров, но все же.)

  • 16.10.2007@ICALEPCS-2007,TOPB2Для импульсных машин надо уметь ждать измерений долго, а не фиксированный таймаут, как сейчас в adc200.

    Так что -- надо ввести некую управляемость этим, причем в двух аспектах:

    1. Указание "сколько ждать";
    2. "Прерывание" - канал (или что? команда?) "стоп".

    Следствие -- adc200/333 надо переводить с abstract на "intelligent"-драйвер.

    (Отдельный вопрос -- ЧТО возвращать по [СТОП]у -- массив нулей? А rflags какой?)

    04.01.2008: собственно, это выходит вполне общедрайверский принцип -- надо мочь указывать, СКОЛЬКО ждать, а драйвер пусть инициализирует этот параметр каким-нибудь разумным default'ом.

    И -- да, все такие драйверы должны быть не-абстрактными, чтобы корректно мерять интервалы времени, а не в каких-то условных циклах.

    07.09.2011: во-первых, это всё уже реализовано во всех новых драйверах быстрых АЦП. Посему "done".

    Во-вторых, с введением каналов SHOT и STOP параметр "время ожидания" фактически стал ненужным. Он, конечно, остался, но реально используется чисто для отладки.

  • 20.12.2010@Снежинск-каземат-11: еще общедрайверный принцип: если команда записи может изменить состояние других каналов, то ОБЯЗАТЕЛЬНО после записи надо сгенерить чтение, иначе будет один доп.цикл висеть старое значение - из-за того, что чтение было произведено ДО записи.

    20.12.2010@Снежинск-каземат-11: это вылезло на драйвере panov_ubs_src.c.

    • "Командные" каналы, типа HEAT_ON/HEAT_OFF, влияют на состояние статусных битов (таких, как PANOV_UBS_CHAN_STAT_ST1_HEATIN).
    • Но эти каналы НЕ посылали GETDEVSTAT после своих команд, рассчитывая на то, что статусы и так читаются каждый цикл теми, кому надо.
    • НО: GETDEVSTAT отсылался ДО этих команд (поскольку измерения присылаются клиентом перед записями), и более в этом цикле не высылался?
    • В результате, хотя практически с начала цикла реальные значения статусов уже изменились, но их никто не вычитывал и в конце цикла клиенту отсылались СТАРЫЕ значения.
    • Это критично влияло на ручки [Выкл]/[Вкл]: они "перемаргивались".

    Правда, конкретно с УБСом отправка запроса GETDEVSTAT сразу после команды не помогла. Проблема в том, что у Панова статус меняется НЕ СРАЗУ, а через 20-60мс (поскольку он собирается от под-устройств, с которыми связь по внутреннему CAN).

    Так что принцип уже ОБЩЕДЕВАЙСНЫЙ: при такой взаимозависимости статусов от прочих команд (т.е., биты статуса являются как бы "подтверждающим чтением" для команды "записи") уже само устройство должно по собственной инициативе высылать GETDEVSTAT (0xFE); в случае с УБСом -- после ответов от под-устройств на команды.

    Панов так и сделал (точнее, у него теперь GETDEVSTAT отправляется сам при любом изменении статусных битов) -- и проблема исчезла.

  • 09.02.2011@Снежинск-каземат-11: еще некий вопрос -- ведь драйверы должны позволять указывать в auxinfo начальные значения ВСЕХ управляющих каналов; по-хорошему,
    • как синтетических (типа козачиных "beg" и "end" -- с этими самое простое),
    • так и реально живущих в блоке -- типа 485'ных VELOCITY и ACCELERATION;
    • в т.ч. и просто каналов записи -- типа уставок ЦАПов, всяких масок выходных регистров, и т.д.

    10.02.2011@Снежинск-каземат-11: вопрос вылез при реализации начального указания уставок в kshd485_drv.c (детали см. в 201102-SNEZHINSK-ACTIVITY.txt за 09.02.2011).

    Тут есть 2 крупных аспекта:

    1. Как именно это реализовывать -- зачастую запись бывает нетривиальна.

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

    2. Но тут встаёт вопрос об "устойчивости" -- как её обеспечивать, и надо ли. Аспекты:
      1. Если реализовывать канал [Reset] (как сейчас в xcac208/xceac124), то метод "в конце init_d() вызывать собственный _rw_p()" уже не пройдёт -- ведь значения надо запоминать на будущее. Возможно, достаточно просто по [Reset] повторять вызовы _rw_p() для всех указанных; а может -- более хитро, как в xcac208 замутная связка DoSoftReset()+SendMULTICHAN().
      2. Позволять ли записываемым юзером значениям перепрописывать указанные в auxinfo (как это сейчас сделано в kshd485_drv.c), или же хранить те как неприкосновенный образец и восстанавливать по [Reset] (как сделано в xcac208_src.c)?
      3. И -- как относиться к значениям "настоящих" каналов, считывабельным из блока: хоть когда-то их использовать (по 0xFF/is_a_reset), или же "настаивать" и всегда перепрописывать их "указанными"?

    Хочется ведь максимально унифицированный (годящийся для всех видов драйверов и вариантов каналов), элегантный и расширябельный вариант, так? Тогда в принципе подходит такой проект, основанный на "в конце init_d() вызывать собственный _rw_p()":

    • Парсить значения в массив в privrec'е, индексируемый по номерам каналов.
    • Первоначальные значения в этом массиве пусть будут "невозможными" -- чтоб отличать от настоящих чисел. Этакие "NaN для целых".
    • С каналами, которые в блоке живут совместно с другими значениями (как всякие маски, prescaler@cgvi8, velocity@kshd485, etc.) -- надо обращаться полностью-корректно, помечая как запрошенные-для-записи, отправляя запрос на чтение и делая запись при ответе.
    • Собственно запись делать путём вызова собственного _rw_p().
    • Для синтетических настроечных каналов, которые вызывают SendMULTICHAN() -- ввести флажок в privrec'е, который бы блокировал ту отправку, которая всё равно произойдёт далее по ходу _init_b().
    • Насчёт устойчивости и "позволять ли записываемым юзером значениям перепрописывать указанные в auxinfo":
      1. НАСТРОЕЧНЫЕ, синтетические -- да, пусть переписывают. Да, это отменяет используемый в xcac208 подход к [Reset] -- а нужен ли он?
      2. С прочими -- неясно.

    28.02.2014: возможно, решение ИЗНАЧАЛЬНОЙ задачи (чтоб при старте настройки в устройствах были прописаны) лежит совсем в другой области: аналогично TANGO, где персистентность значений обеспечивается встроенным компонентом сервера, или EPICS'ному autoSaveRestore.

    Т.е., недурно бы какой-то такой функционал иметь в сервере, чтоб можно было ОДИН раз произвести настройки, и потом бы они при старте сами загружались и передавались бы драйверам как просто команды записи в каналы.

    01.04.2014@Снежинск-каземат-11: вот вчера сделал возможность указывать в auxinfo умолчательные значения параметров измерения, типа NUMPTS -- чтоб на ЛИУ сразу при включении ставились "правильные" значения.

    ...а сегодня после дополнительных расспросов стало ясно, что это всё вовсе НЕ в чугуне отлито, что значения тех уставок в реальности зависят от времён, указанных в dl200 и по-хорошему надо б иметь кнопку "инициализация", которая бы рассчитывала NUMPTS и PTSOFS на основе значений в dl200. И, главное, что указание в auxinfo -- СЛАБОполезно.

  • 24.02.2011: еще: становится очевидно, что надо задачи типа "давать запуск раз в N секунд" (dl200me, frolov_d16, cgvi8, ...) перекладывать с клиентов на ДРАЙВЕРЫ.

    24.02.2011: смысл -- надо, чтоб ВСЕ параметры СУ были б доступны через стандартные механизмы работы с каналами, ВСЕМ программам одновременно. Т.е., надо минимизировать количество "local state" в клиентах.

    Конкретно для "запуск раз в N секунд" будет еще пара плюсов:

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

    Конкретно в драйвере dl200me свободные каналы под это есть; nfrolov_d16 -- расширим (он пока не использовался); проблема только в cgvi8 -- там свободных каналов нет (разве что xcgvi8 сделать, по аналогии с xcac208).

    21.06.2011: в dl200me это было реализовано еще в апреле, сейчас проверено -- работает (хотя в апрельском fast-варианте был баг, обусловленный инвертированием prog/ext между железом/каналом; пофиксен).

    02.08.2011: это всё, конечно, хорошо, но не перевести ли эти времена с СЕКУНД на что-то более мелкое? И тогда вопрос -- на что?

    • Везде (почти) вроде стараемся использовать МИКРОсекунды; это даст максимальный период 2000 секунд -- вроде нормуль (реже -- пусть программа уже заботится).
    • Хотя, конечно, реальная гранулярность -- едва ли возможна выше 10Гц, так что в идеале надо бы МИЛЛИсекунды; это и период даст охрененный. Недостаток -- сколько уж можно везде делать по-разному.

    Короче -- останавливаемся на МИКРОсекундах.

    03.08.2011: а вот и нет -- МИЛЛИсекунды!

    1. Роговский, долгое ожидание запусков
    2. "Время с момента начала ожидания" в fastadc_* -- милли.
    3. ???
  • 06.09.2011: некоторые размышления о формировании карты каналов -- по результатам осмотра CAN-драйверов x* (с унифицированными 50 первыми каналами), плюс создания карт каналов для VADC16 и VDAC20. Кратко: 100 вместо 50, плюс отдавать SWver и HWver каналами.

    06.09.2011: итак:

    1. Собственно, а зачем ужиматься -- пусть даже в 50 каналов (с делением 20:30 между config-каналами и регистрами)? Проще сразу сделать 100, с разделением 50:50. ЭТОГО заведомо хватит вообще на любые потребности для подобных устройств, могущие возникнуть в будущем.

      Да и на остальные каналы (ЦАП, АЦП) можно выделять блоки, кратные 100.

      Дополнительный плюс -- в том, что все карты сервера будет просто прикидывать, при кратности 100.

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

    2. Памятуя принцип "делать так, чтобы ВСЕ сущности были адресуемы в виде каналов": нужно и HWver, и SWver, и даже Serial (где он есть) ВСЕГДА отдавать в виде каналов. Тем более, что в 100-канальной стандартной карте места точно хватит.

    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). Разбивка такая:

    • 0-19: CONFIG-область (то же, что раньше); незанятыми остались 6,7,8 и 16,17,18,19.
    • 20-39: w-каналы; калибровка, включение digcorr, ...
    • 40-59: r-каналы; 40:HW_VER, 41:SW_VER; там же будет текущая поправка цифр. коррекции.
    • 60-99: регистры.
    • 100-199: ADC; т.е., резерв на 100 каналов.
    • 200-299: ЦАП; резерв на 32 канала, 3 области. 200-231: OUT, 232-263: OUT_RATE, 264-295: OUT_CUR; 295,296,297,298 -- не используются.
    • Т.о., стандартная карта каналов теперь должна выглядеть так: w40,r20,w8,r8,w1,r1,r22,r100,w64,r36.
    • Понятно, что для совсем-не-ЦАП/АЦП-блоков -- типа CURVV, CIR8 и т.д. -- стандартными будут только первые 100, а специфические регистровые каналы, видимо, придётся положить в область 100-199; с пановским vv2222 -- возможно, еще страннее (например, ВСЁ упхать в первую область, либо тоже в 100-199).
  • 09.11.2011: не совсем драйверский, но общий и очень близкий к драйверам принцип: надо для каждого стандарта электроники иметь "тестовую утилиту", "монитор", причём -- они все должны
    1. Реализовывать максимум возможного из того, что умеет целевая электроника.
    2. Быть максимально похожими по ключам и параметрам командной строки.
    3. Иметь максимально похожие имена -- типа "EEE_NAME" (где EEE -- тип аппаратуры, NAME -- некое общее имечко).

    09.11.2011: более подробно:

    1. Что у нас есть сейчас:
      • CAMAC@CM5307: cm5307_alfo. Умеет только NAF'ы, но НЕ умеет ничего с LAM'ами, и, как следствие, ориентирован исключительно на одиночные NAF'ы -- последовательности и паузы делаются множественными вызовами из shell'а.
      • CAN: XXXcanmon, умеет всё, что нужно.
      • USPCI: uspci_test, умеет всё, что нужно.
      • BIVME2: планируемый bivme2_test, по проекту должен был быть аналогичен uspci_test (просто с копированием основной инфраструктуры (основного цикла) и заменой специфичных потрохов).
      • 31.08.2012@Снежинск-Снежинка-холл-вечер: (при писании e-mail'а Феде)
        PIV485: piv485mon.
    2. Унификацию по ключам командной строки можно сделать только явочным порядком -- аналогичностью да копированием кусков кода, никакой общей библиотеки тут не сварганить (просто непрактично).
    3. Выбор имени -- задача... Напрашиваются такие:
      • _test -- не вполне адекватно, поскольку это не "тест", а некая утилитка.
      • _mon -- монитор, для CAN -- адекватно, в остальных же случаях перекрывается с общей концепцией "загрузочный монитор в ROM/flash".
      • _con -- "консоль", опять же, аналогично _mon, некоторая неоднозначность.
      • _tool -- недурно, но -- КАКОЙ tool?

    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) -- НЕ отваливать по ошибке.

  • 15.02.2012: еще принцип, который существенно упростит жизнь:
    Код, занимающийся В/В по сети/CAN/RS485, надо обязательно снабжать включабельным логгингом пакетов.

    15.02.2012: пусть даже это "неудобно", как было с kshd485/piv485lib, где

    1. Приходят пакеты не сразу, а по байтам.
    2. Отправлять приходится "за-escape'ленные" данные.

    Ответы на те проблемы просты и прямолинейны --

    1. Логгить надо именно ЦЕЛЫЙ пакет, иначе он не воспринимается. Если уж так надо и по-байтно по мере прихода -- ну вставить доп.логгинг с DRIVERLOG_DEBUG.
    2. Аналогично, если данные уходят в линию пережеванные логгить надо ОБА варианта: первый -- для понимания, что генерит сам драйвер, второй -- для предъявления автору железки при разборках :-)
"Управильнивание" cm5307_drvletbody
  • 04.07.2005: Желаемые фичи/план действий:
    • Вытаскивание большей части содержимого DriverBody() в HandleInput() и ReturnChanGroup()+ReturnBigc().
    • #define'able buffer-size -- см. проект за 07.04.2005.
    • ?DoDriverDebug()->DoDriverLog()?

02.08.2011: поскольку задача уже давным-давно решена, то всё это помечаем "done".

Идеи о Plug and Play (PnP)
  • 10.01.2006: на данный момент существенным препятствием является линейная адресация каналов. А ведь идея (но не реализация :-) решения этой проблемы очень проста:
    • Превращаем chanaddr_t из НОМЕРА в HANDLE -- он может быть произвольным. Это и открывает возможность для PnP (пример кодирования: старшие 16 бит -- ID устройства, младшие 16 -- номер канала в устройстве).
    • А номер 0 -- "зарезервировать", отдав его "пустому" устройству, так что реакция на работу с ним будет -- игнорирование.

    Таким образом, при disconnect'е устройства можно будет всем клиентам сообщать "тю-тю", и они (cda) будут сбрасывать его handle в 0.

    14.01.2006: кстати, так, в дополнение -- "упрочнение" этого механизма:

    • В самом cda зарезервировать cda_physchanhandle_t==0, чтоб он также отображался на nop/null.
    • И на каждый sid также делать 0=>ничто?
    • И -- также поступить с sid==0.

    27.04.2011@Снежинск-каземат-11: господи, какая фигня -- "Превращаем chanaddr_t из НОМЕРА в HANDLE"!

    Давным-давно ж уже понятно (но, видимо, нигде явно не записано) -- при перечитывании БД сервер должен посылать всем клиентам уведомление о смене конфигурации, и они выполнят пере-резолвинг имён в chanaddr_t.

    • А та идея в начале января 2006-го -- видимо, рассчитывалась под CXv2, БЕЗ резолвинга. Сейчас -- малоактуально, всё равно в v2 PnP добавляться не будет, а в v4 изначально предусмотрен резолвинг.
    • Ну и мысль насчёт "сбрасывать handle в 0" -- также уже слабоактуальна, поскольку сервер будет "отсеивать" запросы с chan=-1, и можно держать "неизвестные" каналы хоть за 0, хоть за -1. А в интерфейсе они отобразятся как COLALARM_NOTFOUND (т.е. -- супер-тёмносерым).
Кое-что еще о цветах...
  • 13.01.2006: две раздельных идеи:
    1. Цвет типа "hwerr" -- раз там НЕ нужна читабельность символов, а нужна только ЗАМЕТНОСТЬ -- можно сделать темно-серым, да хоть "#404040".
    2. У Гусева в программах тренировки клистрона есть фича -- поля, измененные САМОЙ ПРОГРАММОЙ, подсвечиваются другим (розовым) фоном, пока их не поменяет пользователь.

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

      Вопрос-следствие-1: а как это технически реализовывать? Флаг "было-изменено-программно" явно должен быть свойством knobinfo_t, и должен иметь смысл только при is_rw!=0. И Cdr'у это учитывать совсем несложно. А вот КАК ПРОГРАММА БУДЕТ УКАЗЫВАТЬ "ИСКУССТВЕННОСТЬ" ИЗМЕНЕНИЙ? Особенно -- формулы (они-то -- completely cda!)!

      Вопрос-следствие-2: какой цвет? Идея 1 (подсмотренная случайно, в момент вдохновения на каком-то сайте): бледно-зеленый. Он и не слишком бросается в глаза, но и заметен, и при этом легко отличим от всего остального. Например, как на Google Books -- "#dcf6db" (но лучше, для заметности -- чуть менее бледный). Идея 2 (старостенкина): цвет-Феди-младшего "#d8e3d5" -- практически такой же бледно-зеленый :-).

      (После того, как попенял Гусеву, что он не придерживается единых стандартов цветности, и в разных его программах розовый означает разное)

    14.01.2006: вообще-то -- уже есть ответы на оба пункта!

    1. Совсем нечитаемый темно-серый фон отлично подойдет для будущей ситуации "channel not found" -- когда канал по указанному имени не найден.
    2. А вот состояние "programmatically changed" -- это ж явно должна быть прерогатива cda, а никак не Cdr! Детали:
      • Состояние называем PRGLYCHG. (Просто "PRGCHG" нельзя -- непонятки ("Program changed"?).)
      • Для уставки его из формул заводим дополнительный набор команд~--- CMD_PRGLY_SETP*() (Как? Отдельный вопрос... Если б их унифицировать с просто SETP*...)
      • Для программ вводим функции cda_prgsetphyschan{val,raw}(), которые внутри будут маппироваться все на тот же setphyschanvor(), к коему добавится еще один параметр.
      • "Алгоритм": prg-вызов будет уставлять сей флажок, не-prg -- сбрасывать. Плюс, сбрасываться также будет и по уставлению флага OTHEROP -- раз уж кто-то значение поменял, то факт нашей когда-то-программно-измененности теряет значение.

    16.01.2005: Приступаем. Протокол:

    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, где

      typedef enum {VALUE_DBL, VALUE_RAW} dbl_or_raw_t
      ради большей читабельности (а то в 0 и один можно уже запутаться), плюс чтобы дать gcc хотя бы потенциальную возможность отслеживать ошибки (хотя это и не Ada с ее "type xxx is new yyy", что сходу запрещало умолчательное преобразование между этими типами). Былое "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 -- все расцвечивается как надо.

    (Естественно, это все в основном касается именно PRGLYCHG, а NOTFOUND -- постольку-поскольку.)

    30.01.2006: некоторые размышления о будущем NOTFOUND (в cda@CXv4):

    • Группировка на экране будет сразу создаваться ВСЯ, но дескрипторы каналов в physlist[] первоначально уставятся в "никакой" (то ли 0, то ли -1), и cda-флаги (chaninfo[].rflags) им будут по умолчанию ставиться в CDA_FLAG_NOTFOUND.
    • И в момент соединения с сервером первым делом будет отсылаться запрос не на получение данных, а на резолвинг имен.
    • А по получению ответа заполняются все дескрипторы каналов в physlist[] и флаги NOTFOUND сбрасываются -- естественно, именно для НАЙДЕННЫХ каналов.
    • В случае же разнообразных пертурбаций (например, по прилету от сервера уведомления "DB changed") первоначальная операция повторяется -- проставляется "никакой" дескриптор и флаг 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() также переведено на эту константу.
  • 14.01.2006: Связанная вещь: вообще-то давно надо превратить цветовые состояния в "граждан первого класса". Что включает:
    • Введение функций CdrStrcolstateShort() и CdrStrcolstateLong(), и перевод Chl_knobprops.c::DisplayCurVals() на них.
    • "Границы" -- COLALARM_FIRST, COLALARM_LAST.

    16.01.2006: сделал, и DisplayCurVals() перевел.

    "Done".

    14.03.2006: дополнение: нужна функция для обратной конвертации -- CdrName2colalarm(char*name).

    Сделана.

    Замечание: при ненахождении она возвращает -1, что, с одной стороны, не совсем корректно -- т.к. оная не принадлежит к пространству значений типа colalarm_t, а с другой -- позволяет максимально просто детектировать проблемы.

  • 16.01.2006: кстати, насчет *str*{short,long}: есть мысль, как это все максимально обобщить и поставить на поток! Итак:

    Надо изготовить (в 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: а еще -- ВСЕГДА надо иметь функцию для обратной трансляции, строка->число! И она также должна создаваться теми макросами. Вот это-то и будет реальное "заведение констант как «граждан первого класса»".

Еще один общий -- "общебиблиотечный" принцип: имя программы и отладочная информация:
  • 26.01.2006: касается каждой хоть сколько-то крупной библиотеке, имеющей дело с В/В, типа cda и cxlib:
    • Ей должно передаваться (от более высокого уровня) имя программы, которое под Linux будет втихую заменяться program_invocation_short_name.
    • Плюс -- отдельно! -- "имя подсистемы", которое либо берется библиотекой самой откуда-то (сия возможность может быть у Cdr и Chl), либо уставляется сверху (в cda).
    • И для выдачи диагностики должна быть функция типа "reporterror()", которая всегда и используется, безо всяких там "fprintf(stderr,...)".

      И эта функция должна выдавать сообщение в формате

      CURTIME: PROGNAME[/SUBSYSNAME]: LIBNAME: ТЕКСТ
"Некоторые детали проекта и реализации будущей плагин-архитектуры"
(Собственно, все сии мысли полезли при реализации vacclient'а -- резоном являлось желание при помощи plugin-архитектуры ИЗБАВИТЬСЯ от необходимости иметь отдельную программу, обходясь именно надлежащими плагин-элементами и плагин-отображаторами.)
  • 04.03.2006: итак, цель -- реализация vacclient'а при помощи plugin-архитектуры, БЕЗ отдельной программы.

    10.03.2006: что для этого нужно:

    • Возможность делать plugin-ЭЛЕМЕНТЫ уже прямо сейчас. 15.03.2006: сделано, хоть и в дегенеративном варианте.
    • Возможность указывать свой, "локальный" help.
    • Возможность указывать свое содержимое тулбара и -- ГЛАВНОЕ! -- обработчик команд.
  • 04.03.2006: о help'е:

    04.03.2006: Ход мыслей таков:

    • Надобно уметь указывать текст help'а для chl-like программ. Как? В «секции» "атрибутов"!
      • Секцию "атрибутов" можно ввести уже сейчас -- в конце subsysdescr'а.
      • Структура -- таблица {"метка","значение"}, конец -- {NULL}.
      • А уж в CXv4 все делать так (в т.ч. -- саму группировку, sea-подпрограммы, icon, etc.).
    • "ShowHelp()'у" передавать строку вида
      "PARTTYPE:data\v"
      "..."
      "\0"
      
      т.е. -- набор пар ТИП:ДАННЫЕ, разделенных '\v', где возможные PARTYPE и их данные --
      "KEYS:key\fcomment\?" {~} "\v"
      "TEXT:...\v"
      "COLORS:fg\fbg\flabel\ftext\?" {~} "\v"
      
      И тогда оное можно будет указывать прямо в .c/БД/где-охота-еще.

      (Плюс -- если вся "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() заменить на него, но -- стало ясно, что далать надо именно СО СТРОКАМИ, так что первоначальный проект закрывается.

  • 06.03.2006: отдельный вопрос -- а КАК именно мочь указывать chlclient'у, что ему надлежит подхватить такую-то библиотеку с плагинами?

    (С самого появления идеи плагин-архитектуры (в январе 2005?) сей вопрос как-то молча обходился. Видимо, подразумевалось то ли использовании отдельных программ, регистрирующих свои компоненты, то ли некоей директории-репозитария, a-la /usr/lib/mozilla*/plugins/ или {/usr/lib/gimp*,.gimp*}/plug-ins/, откуда б просто сходу всасывались все .so-файлы.)

    07.03.2006: собственно, раз ручки "ссылаются" на отображаторы, каковые могут располагаться в неких "динамических библиотеках", то можно прямо в группировке иметь отдельное поле со "списком библиотек" -- как исполняемые файлы содержат список требуемых .so'шек.

    Минус такого подхода -- ?( затрудняется/исключается возможность запускать программы "в разных видах", как это сейчас можно делать с вакуумом, по разному выглядещим при использовании chlclient и vacclient ?).

    06.08.2006: комментарии по теме:

    1. Мысля об указании списка библиотек уже повторилась (независимо!) в комментарии за 17-05-2006 о "как в ELF-файлах".
    2. Проблема "возможности запуска программ в разных видах" -- похоже, просто не стоит. Это ясно показал пример современной версии vacclient'а: там "плагин" (точнее, то, что БУДЕТ grouping-плагином) умеет работать и в гистограммном, и в числовом режимах.

      И, кстати, в конце концов ничто не мешает ввести в будущем knobber/pult ключик, который бы подавлял подгрузку указанных в описании подсистемы plugin-библиотек.

  • 07.03.2006: насчет символов-разделителей -- существует некоторая неадекватность в выборе разделителей под конкретные цели.

    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: насчет обращения с заголовками колонок и столбцов -- нынешняя схема несколько неудобна.

    02.08.2006: во-первых, первичнее -- rownames, а не colnames, как сейчас (потому что столбцы -- штука опциональная и вторичная, имеющая смысл только для ELEM_MULTICOL).

    Во-вторых, нынешний подход "метка берется из {row,col}names, если она "?" -- то от первого knob'а" -- неудобен. И тем, что для стандартного варианта "метку брать от knob'а" приходится писать нужное количество "?\", и тем, что метку "?" сделать никак нельзя (а для колонок -- хотелось, и не раз!). Так что его слезует изменить:

    • Брать метку от knob'а надо, просто если метка из {row,col}names -- пустая. Это резко все упростит и высвободит "?".
    • Если же зачем-то вдруг припрет НЕ иметь метки -- то для этого можно задействовать комбинацию "!-!" (уж она-то точно никогда не понадобится :-).
    • Теоретически возникает сомнение -- если метку-то железно надо брать от первого knob'а, то надо ли так же брать тултип, или для него иметь иные правила? Считаем -- что надо, уж если метку берем, то и тултип не помешает.
  • 16.08.2006: надо б посмотреть "спецификацию плагинов" в Netscape/Mozilla. Коли уж это самый известный и бросающийся в глаза вариант. Опять же -- там и отображение+В/В есть, и обмен данными, который производит "хост-приложение" для плагина. P.S. Ну и аналогично для IE тоже можно глянуть. Только быть готовым к шизе :-)
Об отображаемых единицах (units):
  • 31.03.2006: вылезло сегодня -- при очередной возне с ausacc, где надо отображать задержки в "секундах".

    Дело в том, что там надо отображать числа в каких-нибудь осмысленных временнЫх единицах, но разброс уж дюже большой: от 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).

    Минусами такой штуки было бы:

    1. Усложнение misc_printffmt.c -- в частности, пришлось бы в GetTextColumns() "хитро" считать ширину поля; хотя это-то самое простое.
    2. Главное же -- ВСЕ места, связанные с понятием "dpyfmt", пришлось бы переводить на специальную функцию (которой тоже жить в misc_printffmt.c), проверяющую тип формата, и делающую нужный вариант sprintf()'а -- либо обычный, либо, при "%t", вышеописанный хитрый.

      (Уж после этого-то точно будет ну совсем не проблемой туда же упхать и поддержку "логарифмичных" форматов, типа указанных в начале этого раздела за 31-03-2006.)

  • 24.03.2009: идейка, как можно реализовать подобие первоначальной потребности отображать разумные единицы (правда, только на экране) -- для времен, которые могут быть от наносекунд до десятков секунд (это в фроловском D16):

    Отображать ДВУМЯ полями, расположенными друг за другом: число ("мантисса") и readonly-селектор с единицами ("экспонента" -- ns/us/ms/s).

    Детали:

    • Конечно, это будет ТОЛЬКО на экране, но ни в режимы, ни в логи это попадать не будет.
    • Но это и плюс -- наличие в файлах только чисел сильно упрощает задачу.
    • Числа ВСЕГДА будут ходить в унифицированных единицах -- например, в наносекундах.
    • И ручка-число, и ручка-единицы будут пользоваться формулами (то ли с log10(v)/3, то ли серией сравнений с 1e3, 1e6, 1e9 etc.). И должна быть какая-то верхняя граница -- например, всё, что больше секунды, показывается уже секундами, БЕЗ дальнейшего даунскейлинга в минуты и часы.
    • Бесплатный бонус -- за счет cellspacing'а между двумя ручками единицы будут отделены от числа надлежащим узким пробелом.

    24.03.2009: а вот контр-идея: ведь в datatreeP.h в dataknob_knob_data_t ВСЕГДА есть поле list. Так вот -- а что, если сделать такой специальный ручк, который как бы текстовое поле, но чтоб он сам производил такой хитрый процессинг числа, деля его на "числовую" и "единицевую" части. Только надо ж ему как-то указывать "числовой фактор" -- шаг между единицами (который обычно 3, но бывают-то и нелинейности -- сантиметры(-2), дециметры(-1), метры(0), километры (+3)).

    А вообще -- подумать, что ли, об общей более навороченной системе отображаемых единиц, a-la LabVIEW? Неа, в качестве мечтаний -- может быть, но в реальности в наших масштабах потребность в ней минимальна, и не окупит трудозатрат на её разработку и поддержку.

Еще о "колоризации алармов"
  • 04.04.2006: сегодня было высказано пожелание от Клющева и Паши: чтобы та лампочка в linvac/ringvac, которая была первой, как-нибудь бы выделялась. Смысл -- при возникновении лавинообразных процессов обнаруживать точку начала.

    Ну и -- вот как такое можно сделать? (И -- как (каким цветом?) выделять?)

    04.04.2006: после обеда -- в принципе, делалось бы легко. Как:

    • Цвет: у нас же это "выделенная"/"важная" информация, так? Вот и будем таким каналам ставить фиолетовый либо синий background.
    • Для чего вводим COLALARM_FIRSTALARM.
    • Введем еще один "тип" -- LOGD_ORDALARM (ORDered).
    • Плюс -- флаг KNOB_B_IS_ORDALARM.
    • Вводим поле eleminfo_t.alarm_first (тип -- Knob), которое:
      1. Уставляется равным knob-инициатору при переходе от alarm_on==0 к !=0.
      2. Сбрасывается в NULL при переходе к alarm_on==0.
    • А в ChooseColorState() смотрим, что если "IS_ORDALARM и выставлен бит VALUE_LIT_MASK, и alarm_first==ki", то colstate=COLALARM_FIRSTALARM.
    • Стандартный вопрос -- "какой приоритет?" -- имеет простой ответ: ниже YELLOW. Т.е., перед/после "attn" (наверное, выше PRGLYCHG).

    А "бы" -- потому, что у нас "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", а если вдруг возникнет подобная потребность, то готовый проект реализации -- есть выше.

  • 11.04.2006: кстати, а в CXv4 потребно будет иметь "раздельное" указание: т.е., тип отображатора -- "led", а в "описании поведения" -- "alarm,firstalarm" (еще один флажок в дополнение к той самой алармовости).
Еще немного о красивостях
  • 18.04.2006: Старостенко пожаловался, что для новой системы синхронизации Гусеву было бы хорошо иметь виджет-контейнер-дерево, поскольку там именно древовидные зависимости компонентов/каналов. Но: "стандартный" motif'овский компонент (который в Motif-2.x), имеющийся в periodic'е, нифига не умеет, и толку от него как от козла.

    И тут-то я вспомнил о существовании некоей Microline Widget Library от давно покойной фирмы NeuronData. Эта библиотека содержала, в частности, виджет "дерево", и доступна в исходниках ранних версий Mozilla и нынешнем NEdit. Я даже когда-то с год назад пробовал ее откомпилировать, но там какие-то сложности с Makefile'ами -- они вроде как заточены под другую иерархию директорий, коей нету (и в Mozilla тоже), и попросту не работают.

    18.04.2006: что ж -- взял библиотеку из NEdit'а. Отдельная сборка действительно не работает, а вот nedit'овский "make linux" часть библиотеки собрал. "Часть" -- только контейнер-закладки, поскольку только оно нужно самому NEdit'у (кстати, а tooltips он берет LessTif'овские). Пришлось компилировать и библиотеку, и директорию examples/ ручками. В конечном итоге вполне заработало.

    И -- йо-о-оу!!! А вещь-то это действительно классная. Сделано просто ИСКЛЮЧИТЕЛЬНО качественно, работает великолепно, и демонстрашки в том числе. Коммерческий проект, блин... Summary наблюденного:

    • Там имеются: некое собственное окружение -- XmL.[ch] -- энное количество функций, а также собственно виджеты Folder (контейнер-закладочник), Progress (индикатор прогресса), Grid (сетка для электронной таблицы) и Tree (дерево, наследуется от Grid).
    • Настраиваются все-все возможнейшие параметры -- цвета, шрифты, поля, ...
    • Но вот виджеты ни в сетку, ни в дерево НЕ ВСТАВЛЯЮТСЯ, увы...

    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+. Хотя и тормозновата.

    • Там есть "абстрактный" виджет XmHierarchy, от которого наследуются XmOutline (искомое дерево) и XmTree (аналог того, что в Xaw (пример -- Editres)).
    • Существует вся эта радость, судя по комментариям в .c/.h-файлах -- с 1990г.! Но -- это был proprietary-код фирмы ICS.
    • "Технология": у каждого child'а этих менеджеров указывается XmNparentNode -- его "владелец" в древесной иерархии, каковая полностью отдельна от обычного parent/child-relationship.
    • А вот для использования в CX/Cdr сей виджет слегка проблемен: КАК указывать иерархию? Нынешний proposal с вложенностью компонентов "tree" -- как-то естественнее...
    • P.S. А еще там есть виджет-менеджер XmTabStack -- наконец-то нормальный закладочник! И: он умеет закладки показывать не только сверху/снизу, но и СЛЕВА/СПРАВА, и вот тогда -- он умеет писАть строки вертикально!!! Пытался найти, как он это делает -- фигушки, не нашел.

    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: некоторые идеи/план действий, раз уж я так в этом завяз :-):

    • 1a. Попробовать реализовать то же самое с помощью XmColumn,
    • 1b. XmForm.
    • 1c. Посмотреть на результат -- хоть видно будет, Motif виновен или Антонов.
    • 2. При помощи XmOutline -- по описанному выше проекту.
    • 3. Попробовать-таки при помощи XmRowColumn.

    27.04.2006: начал. Итак:

    • 1a. Хренушки. XmColumn -- это совсем не то, там НЕЛЬЗЯ в левую колонку поставить какой-то виджет: туда лепится ТОЛЬКО XmLabel, который оно делает само.
    • 1b. XmForm-based -- сделал, без особых напрягов, практически тупым копированием Grid-based-варианта с минимальными модификациями. Работает, скотинка, с первого же раза, и никак визуально не отличается.
    • 1c. Да, результат -- такой же: нифига не ходится. А ведь при этом в программе вообще НИ ОДНОЙ сетки нету... Так что -- Motif. :-(

      Кстати1: попробовал также вернуть XmNtraversalOn=True для DrawingArea -- не помогло.

      Кстати2: оно не то что "не ходится" -- оно и НЕ ВВОДИТСЯ! Т.е., почему-то ВЕСЬ клавиатурный ввод уходит "в никуда", а самому виджету это и вовсе не поступает.

    30.04.2006: придумал, как делать "disable" при использовании XmTree/XmOutline: точно так же, как и создавать -- т.е., идти по дереву, и если "обрабатываемый" knob -- это подэлемент, и с тем же типом, что наш, то "нырять" в него и проделывать то же самое.

    01.05.2006: ага, щас -- там же надо учитывать, что задисэйбленность могла быть и локальная -- и тогда мы "по ошибке" вложенные компоненты "раз-дисэйблим"... :-(

    Сделал так -- все равно не вполне корректно: надо все же имитировать протокол "is_ancestor_sensitive", а то сейчас НЕ делается disable переключалке вложенных веток, и ею можно дергать туда/обратно, а еще при ссылке на канал будет то же самое...

    Да, и, кстати --

    • 2. При помощи XmOutline и XmTree тоже сделал. Параметром определяется подвид: outline|hierarchy (XmOutline), tree (XmTree, XmNconnectStyle=XmTreeDirect), laddertree (XmTree, XmNconnectStyle=XmTreeLadder).

    Кстати, по чистой случайности 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: вообще-то проделанная работа оказалась реально полезной/нужной/востребованной, по двум причинам:

    1. Я хорошенько разобрался, что к чему и как, так что легко смогу включиться в работу по "новому RFSyn'у".
    2. Компонент "закладочник", изученный по ходу дела и изготовленный в testtree.c, практически в неизменном виде пошел в linbpm, где он был очень нужен.

    10.05.2006: в продолжение

    • 3. При помощи XmRowColumn не выйдет -- у нее все-таки нету режима, позволившего бы и сохранить ширины drawingarea-"палочек", и растянуть их до высоты соседствующих виджетов-содержимых, и при этом не сделать ВСЕ виджеты одного размера. Оп-паньки, в общем.

    Насчет "потери клавиатурных событий":

    1. СОбытия-то виджетам приходят: стрелки вверх/вниз работают. Но -- почему-то события куда-то теряются. Причем -- теряются именно в такой конфигурации, а вот с обычным ELEM_SINGLE -- все окей. И -- теряются только в текстовых полях.
    2. Для упрощения системы отключил toolbar и statusline. Не помогло.
    3. Подумал было, что проблема в том, что в апреле 2005-го я пробовал сделать "глобальные акселераторы" (F2 etc.). Посмотрел -- да за-#if 0'ено это все.
    4. Еще подумал, что проблема (ведь текстовое поле же!) в том, что мы перехватываем события от клавиатуры. Отключил перехват для стрелок вверх/вниз -- ничего не изменилось.
    5. А вот полез в 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'ах

    if (e->uplink != NULL) e->emlink = e->uplink->emlink;
    
    Но толку-то -- сие работает только тогда, когда хоть где-то "вверху" есть обычный, Chl_gui.c'ный элемент, который и предоставляет сии методы.

    Так что -- либо втаскивать эти knobplugin'ы в Chl_gui.c, либо делать некий "стандартный" API, либо -- что самое лучшее -- как-то реорганизовывать этот API: например, чтобы Chl заполнял некое "вакантное место" (hook) "показать свойства knob'а".

    13.12.2009: итого:

    1. Элемент-дерево уже давным-давно отработан, но в реальном деле так и не потребовался, и сейчас результаты тех разборок имеются в ARCHIVE/work/tree.20060822/testtree.c -- если понадобится, там всё готовенькое.
    2. Проблема "потери клавиатурных событий" еще какое-то время назад решена уже общим образом -- в KnobsKbdHandler() теперь *continue_to_dispatch=False делается ТОЛЬКО в случае обработки кнопки.
    3. Задачка с дисэйбленьем групп ручек на лету также обрела своё решение -- в параметре victim= плюс см. послеследующий раздел.

    Так что -- "done", давно пора.

    27.08.2013: сейчас потребность в использовании элемента-дерева возвращается, поскольку надо забирать у Гусева rfsyn. Дополнения по сравнению с тогдашним:

    • Требуется возможность сворачивания подветок -- фича, тогда не сделанная и в XmOutline никак не проверенная.
    • И еще -- функционал нужен такой, чтоб можно было горизонтальные наборы полей выстраивать в ровные колонки, безотносительно глубины вложенности.
    • ...а вот чекбоксы, дисэйблящие подветки (как в testtree.c) не нужны совершенно.
  • 16.05.2006: насчет "элемент заодно должен также быть и каналом" -- нет, не должен!!!

    Все намного проще -- надо считать ПЕРВЫЙ из вложенных в элемент каналов -- как раз "выключателем".

    Чтобы сие реализовать, требуется 3 шага:

    1. Флажок в options, сигнализирующий, что элемент работает в режиме "отключаемости". При этом "корневым" виджетом ставится именно первая ручка, а не метка элемента.
    2. В дополнение к флажку также проверить, является ли первая ручка РУЧКОЙ, а не NOP'ом или SUBELEM'ом -- т.е., type!=LOGT_SUBELEM и kind!=LOGK_NOP (блин, как в 4-й версии будет проще! :-).
    3. Дополнить knobs_emethods_t методом "пришли данные" -- аналог knobs_vmt_t.SetValue(), который вызывался бы Cdr'ом сразу после CdrProcessEleminfo() для содержимого.

      И уж в этом методе в зависимости от текущего значения в 1-й ручке менять sensitivity.

    22.05.2006: в продолжение -- пара мыслей по теме:

    1. Возникает ведь проблема -- в "режиме отключаемости" по хорошему-то надобно, чтобы в "заголовке"/"корне" отображалось ДВА knob'а -- собственно выключатель и "текстовое поле". А потому --
      • В баню "флажок в options"! Используем поле ncols (в v4 -- param1) для указания, сколько именно knob'ов надлежит поместить "наверх" -- они укладываются рядышком в форму, каковая и уставляется "корнем".
      • А первый из этих knob'ов и считается "выключателем". (А можно и в n*65536 (будущий в v4 param2) указывать. 12.07.2010: замечание: ага, щаз-з-з, в param2 (видимо, имелся в виду param3) -- ведь param2/param3 уже заняты под ncols/nflrs. Так что пришлось бы вводить param4.)

        23.12.2010@Снежинск-каземат-11: да вот нифига -- под ncols,nflrs заняты param1,param2, а param3 свободен.

    2. Насчет "отключения ветви":
      • Во-первых -- а надо ли? Ведь в частности требуется иметь возможность, выключив некие запуски, поменять часть "привязанных" уставок. Так что disable делать -- низзя.
      • Во-вторых -- у нас ведь уже ровно для нужд rfsyn'а введен LOGC_VIC, с возможностью желтеть в отключенном состоянии. Вот им можно и пользоваться.
      • А можно и пойти дальше -- раз уж делать метод knobs_emethods_t.NewData(), то можно в нем вычитывать colstate knob'а-"выключателя" и дублировать его в свой background (это, конечно, подходит только для form/grid-based-вариантов).

    26.02.2007: и еще по теме:

    1. Disable'ить ВЕСЬ элемент нельзя вообще ни в коем случае -- ведь сам-то выключатель также принадлежит этому элементу, и им тогда фиг щелкнешь.
    2. Некоторая часть данной функциональности реализована сегодня в ключике "victim=" alarmonoffled'а.

    13.12.2009: поскольку придумано более общее решение (см. следующий раздел), решающее также и эту проблему, то помечаем как "withdrawn".

  • 13.12.2009: о возможности помещать ручку/подэлемент в заголовок элемента: всё намного проще -- надо просто мочь помещать ПЕРВЫЙ из вложенных в элемент каналов рядом с заголовком, а уж этот канал может быть alarmonoffled'ом с victim'ом. И не нужно никаких хитрых проверок, что перечислены в начале предыдущего раздела.

    13.12.2009: детали/идеи:

    • Заголовок теперь должен помещаться не непосредственно в форму элемента, а через промежуточную форму, в которой с ним могут справа соседствовать дополнительные штучки -- ручки и кнопочки.

      Аттачится метка слева к форме, а справа -- либо к форме, либо к ближайшему соседу. "Штучки" же аттачатся справа налево: к правому краю самый крайний, следующие -- к нему; кнопки -- справа, каналы -- перед ними.

    • ЗАМЕЧАНИЕ: поскольку elementForm не будет у виджета-заголовка прямым parent'ом, то в *TitleClickHandler() может понадобиться аккуратность. Но в нынешней реализации неактуально -- там используется ссылка на сам элемент (v2) либо его container (v4).
    • Необходимость сделать "заголовочный" канал указывается options-параметром firstattitle, который можно сделать либо флагом, либо целочисленным -- чтоб говорить, СКОЛЬКО каналов поместить к заголовку.
      • Более одного нужно мочь указывать, если хочется иметь там несколько каналов, но подэлемент городить нельзя -- чтоб disable'итель был в том же элементе, что и его victim.
      • (Хотя в CXv4 надо б семантику victim= расширить: чтоб там можно было указывать ПРОИЗВОЛЬНУЮ ссылку, стандартного синтаксиса -- включая и относительную адресацию, и начиная-с-корня, и в-произвольном-месте-иерархии (как нынешние .имя). Естественно, поиск должен будет делаться средствами то ли Cdr, то ли datatree.)
      • При notitle сей параметр, естественно, обнуляется.

      23.12.2010@Снежинск-каземат-11: очевидно, что никакой firstattitle не нужен, а количество для-в-заголовок указывается в param3.

    • Если потребуется дисэйблить всё содержимое элемента (всё то, что ниже заголовка) -- то надо будет это содержимое помещать в недекорированный под-элемент, которого и ставить victim'ом.
    • Собственно реализаторы элементов (в первую очередь касается multicol/grid) должны будут корректировать свою арифметику на значение firstattitle -- начинать расстановку с F-го канала, а число считать на F меньшим.
    • Вопрос лишь в том, КТО И КОГДА будет создавать ручки у заголовка?

      Резоны:

      1. Они нужны живыми уже в момент создания заголовка и кнопок (для аттаченья).
      2. все реализаторы по определению пользуются *CreateContainer()'ом.
      Следовательно -- на *CreateContainer() это очевидным образом и вешается. На крайняк -- можно будет кроме populator'а передавать также и "attitle_populator".

    P.S. Да, собственно, зачем всё это НЫНЧЕ нужно -- для управления пановскими модуляторами: там он хочет, чтобы управление дисэйблилось, если УБС не-online.

    27.03.2011@Снежинск-каземат-11: кстати, очень полезна возможность размещения ручек в заголовке была б для еще одной фичи: если сделать ELEM_SUBMENU, то можно было бы в заголовках стоек размещать "локальные меню" с командами типа "Включить все УБСы", "Выключить все", и т.д. -- аналог "общего управления", но только для конкретной стойки.

    19.04.2011@Снежинск-каземат-11: за вчера-сегодня эта концепция реализована (хоть и чуток по-другому), посему "done".

"Хитрые" alarm'ы
  • 17.07.2006: в термостабилизации возникла потребность в "отключаемых" alarm'ах.

    Смысл: когда температура входной воды становится слишком большой, надо начинать бибикать, и оператор должен принять меры. НО! Остывать то эти кубометры воды будут долго, и бибикать все это время -- трындец.

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

    1. Просто дополнительный чекбокс "молчать!" к каждой alarm-лампочке. Если нажат -- то лампочка подсвечивается (COLALARM_RED?), но не бибикает.
    2. Чекбокс, но самоотключающийся через какое-то время -- например, через час.
    3. Чекбокс, отключающийся при следующем переходе NO_ALARM->ALARM. Т.е., каждое новое срабатывание обязательно будет приводить к бибиканью. (Но этот вариант плох для ситуаций, когда значение "на границу" -- будет постоянный дребезг.)

    Реализовывать же любой из этих вариантов можно двумя способами:

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

      Самоотключение делается при помощи CMD_GETTIME и сохранения в регистре этого значения "плюс час".

      Отключение при переходе -- просто отключаем при переходе ALARM->NO_ALARM.

    2. При помощи специального knob-виджета, имеющего внутри как лампочку, так и чекбокс. Тогда обходимся безо всяких регистров -- все в privrec'е.
    3. О! А зачем "специальный" knob-виджет? Можно все повесить и на нынешний вариант. Мы же все равно имеем XmNvalueChangedCallback -- для readonly-лампочек восстанавливаем там правильное значение; так вот -- по щелчку на такой readonly,turn-offable,alarm-лампочке делать это самое отключение.

    29.07.2006: мысли по теме...

    В MEDM/ALH есть понятие "to acknowledge alarm" -- оператор подтверждает, что он этот alarm увидел. Пока этого сделано не будет -- оно будет моргать и бибикать.

    Так почему бы и у нас не ввести такое понятие -- что alarm бибикает и моргает, пока юзер не щелкнет кнопочкой, что "он увидел". Когда щелкнет -- то лампочка остается гореть, но иных аудиовизуальных эффектов не производит. А при появлении любого другого alarm'а (т.е. -- при инкременте alarm_on) флаг "acknowledged" сбрасывается и оно начинает орать опять.

    Таким образом -- реализуем вариант 3+c, реализуем очень просто -- на уровне элементов (где реально и живет "интеллект бибиканья"), с минимумом действий со стороны knob'ов.

    Пара замечаний:
    1. В 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'ы -- вообще религиозный вопрос, плюсы и минусы есть у обоих подходов); с другой -- на мой взгляд, у них все-таки "несколько слишком переусложненно".

    2. Там есть "залочивание" alarm'ов -- даже если alarm уже неактивен (исчезла его причина), то сам факт его существования запоминается, и для сброса надо его "acknowledge". (Ключевые слова -- "transient", "поле ACKT".)

    Реализовал 3+c -- детали см. в разделах по alarmonoffled и Chl_gui.

    Так что -- считаем раздел за "done" (хотя "полный проект" со всякими автоотвисами через указанный срок и не исполнен, но, скорее всего, оное и не потребуется.)

О предполагаемой системе термо-давло-контроля
  • 23.07.2006: есть проект обвязать все наши помещения системой контроля температуры и давления охлаждающей жидкости. Т.е., за 2мегарубля это нам предлагает сделать некая фирма "Горный+", но Старостенко прикидывает, как бы этак обойтись своими (Лебедев+Шпомер) силами.

    Читать датчики предполагается через CANADC40, и надо данны отображать "красиво", как бы на карте помещений. И есть желание, чтобы сия система была "масштабируемой" -- на весь ИЯФ (хотя бы просто копированием).

    23.07.2006: понятно, что в принципе не есть проблема при наличии железа (датчики плюс кабельные трассы) сделать такую систему хоть сейчас -- при требуемой точности CANADC40 сможет опрашивать все каналы с частотой порядка 1Гц. Но!

    1. Надо показывать как бы КАРТОЙ. Так что, ELEM_MULTICOL не катит, нужен ELEM_CANVAS.
    2. Раз масштабируемость -- то CAN-адаптерами однозначно должны быть CANGW.

    02.08.2006: КоКо рассказал о такой вещи -- что там не статическое отображение, а можно "наезжать" (zoom), меняя степень детализации; и что в Германии (DESY? BESSY?) ему главная инженер показывала аналогичную систему, отображающую вообще ВСЕ инженерное оборудование комплекса, и там даже писалось название папки (в шкафу), в которой можно найти техническое описание указанной точки.

    02.08.2006: и вообще -- существенные результаты от реализации прототипа "предполагаемой системы" мы уже получили: создан ELEM_CANVAS (вау!), он же послужил толчком к управильниванию Chl.

    А собственно система термо-давло-контроля... Даже если ее и сделает "Горный+" ("Поздемный-" :-), то оно и к лучшему -- мы не будем отвлекаться от основной деятельности.

Отключаемость каналов в cda
  • 31.10.2006: при общении с салимовскими вылезла такая штука: у них есть некая управляющая станция с более чем сотней каналов, связь с которой идет через RS232. И -- у них программа под DOS, которая дергает оттуда только те каналы, которые сей момент нужны (участвуют в управлении и/или отображаются на экране). Таким образом, даже хилой пропускной способности RS232 хватает.

    Вопрос -- а как бы этак подобного добиться? Чтобы и в CX каналы могли вычитываться только при РЕАЛЬНОМ использовании, а не "всегда все, хоть когда-то троганные программой"?

    31.10.2006: решение-то довольно простО (и хорошо стыкуется с предполагаемой для CXv4 канально-ориентированной cda):

    • Основная идея: у сервера выспрашивается не "имеющийся список задействованных каналов" (нынешний physlist[]), а некий другой массив -- "список используемых в данный момент каналов", являющийся выборкой из списка задействованных.

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

    • Каждому каналу сопоставляется еще и "current-reference count", и операции "ПриостановитьКанал()" (-=1) и "ЗадействоватьКанал()" (+=1), и...
    • Канал мониторируется при "ref_count>0"; "список мониторируемых" монтируется заново при переходе ref_count между 0 и не-0.
    • При "рождении" канала он 0, а потом прямо в "add_physchan()" делается "ЗадействоватьКанал()" -- так и получаем желаемый результат.
    • И уж уровень GUI может дергать Приостановить()/Задействовать() по мере исчезновения/появления ручки на экране.
    • (Отдельный вопрос -- КАК отрабатывать моменты приостановки/задействования в смысле тэга возраста: во-первых, надо бы его сразу делать "бесконечно старым", а во-вторых -- при появлении надо бы чтобы эта старость видна не была -- как otherop реально отрабатывается только при приходе свежих данных.)

      @07.11.2006: можно при "появлении" уставлять colstate=COLALARM_JUSTCREATED. Так хоть и будет "видно", но зато будет вполне ясно, если/почему канал еще не успел обновиться.

  • 16.03.2009: еще в условно ту же степь -- "отключаемость каналов в сервере"...

    16.03.2009: возникло это при общении с Лешей Пановым, который хочет для отладки своей CAN-железки (в будущем долженствующей сидеть внутри модулятора, и даже вообще быть скрытой за общим интерфейсом) иметь НЕпериодичность, т.е., чтобы работа производилась ТОЛЬКО по запросу.

    Естественно, такое извращение надо реализовывать исключительно как standalone-программу, НО: ведь всю обвязку-то для CAN писать отдельно с нуля ломает! Несколько мыслей/замечаний по этому поводу:

    • Так что -- хочется мочь использовать драйверы как бы вне СУ.
    • Это явно только для монолитных программ с пришитым CX-ядром/системой исполнения.
    • А насколько это стыкуется с нашей СУ, или лучше не соваться в жту степь, чтобы не ломать модель?

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

    (Естетсвенно, речь о будущей модульной архитектуре в CXv4, с подселябельными сервером и драйверами.)

    17.03.2009: неа, лучше не возиться с драйверами, а делать упор на удобство/простоту/скорость создания ИНТЕРФЕЙСОВ -- чтобы такие тестово-отладочные программы, как для Панова, можно было бы нарисовать в .subsys-файле, а программа имела бы удобный API к ручкам (отработку нажатия кнопок, и возможность в любой момент сообщить новое значение текстового поля). А доступ к CAN -- на основе cxscheduler.

    22.04.2009: собственно, а в чем проблема-то -- ну так и сделать такой типа умный драйвер, который бы тупо ПОМНИЛ последние значения и всегда отдавал бы их (чтобы не синело), а РЕАЛЬНО читал бы по каналу-команде.

И еще один "общебиблиотечный" принцип: текстовое (human-readable) описание последней ошибки
  • 25.01.2007: надо в каждой из "стека" библиотек -- Chl, Knobs, Cdr, cda -- иметь функцию, которая бы возвращала текстовое описание последней ошибки.

    При этом более "внешняя" библиотека, получив отлуп от более низкоуровневой, сможет сформировать свое сообщение из сообщения нижнего уровня, дополнив его своей информацией -- например, "адресом". К примеру, если cda отказала в register_formula(), то Cdr'овское сообщение будет состоять из причины (что именно с формулой не так) и имени knob'а, на котором случилась ошибка.

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

    Т.е., смысл сего -- чтобы доносить до верхнего уровня ошибки с самых нижних, нечто в стиле плюсовых exception'ов.

    И по-хорошему, надо бы отходить от практики печатания сообщений о подобных ошибках внутри нижних библиотек -- переходя на "exception'оподобность"; из нижних же библиотек можно оставить печать лишь асинхронных ошибок -- типа разрыва соединения.

    25.01.2007: вопросов/проблем при реализации всего пара:

    1. Что сообщение осмысленно ТОЛЬКО после возвернутого кода ошибки. Аналогично errno.
    2. А следствие -- надо постараться в максимальном количестве мест, где детектируются ошибки, эти сообщения не забывать генерировать.

    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()'ом.

  • 22.01.2011: мы широко используем подход "lasterr". Но с этой концепцией надо что-то делать.
    1. Оно отдельно реализовано в куче мест -- уж хоть на общие макросы бы перевести (lasterr_skel.h?).
    2. Оно НЕ mt-safe, хотя сами psp, Cdr (конвертирующая часть) -- очень даже.
  • 28.01.2011: надо проверить длины буферов и т.д. и т.п.

    Конкретная проблема -- что на 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 точно из-за этого.

  • 21.04.2014: нововведение: эти функции возвращают не просто указатель на свой буфер, а только если он непуст (т.е., [0]!='\0'), иначе -- strerror(errno).

    Смысл -- чтоб при "дурных" ошибках в глубине (типа обломившегося malloc()'а и прочих open()'ов) функции библиотек могли бы просто делать return ERROR, а не возиться с заполнением описания ошибки. Естественно, предполагается, что в самом начале делается *ClearErr().

  • 21.04.2014: по-хорошему надо бы такие функции называть не *LastErr/*last_err, а *LastErrStr/*last_err_str. Чтоб отличать от функций, возвращающих КОД последней ошибки -- вроде fdio_lasterr().

    21.04.2014: да, надо бы, но пока забиваем.

О некоем скриптовом языке для автоматизации однообразных циклических и последовательных процессов управления
  • 26.01.2007: возникло сегодня в связи с предстоящей необходимостью как-то реализовывать разнообразные алгоритмы прогонок в программе термоциклирования катушек на экспериментальном производстве.

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

    И эти алгоритмы надлежит подселять к обычной chlclient-подобной программе.

    Имеющиеся варианты реализации такого:

    1. Каждый раз по новым требованиям подпеределывать этот "тренирующий" кусок кода -- как они и делают сейчас в dos'овской программе.
    2. Иметь некий внутрипрограммный API, и реализовывать "тренировщиков" как .so-файлы, которые могут подгружаться при запуске (какой укажут).
    3. Создать некий скриптовый язык с состояниями (a-la SNL). Чтоб там можно было эти процессы описывать на почти-естественном языке. Детали:
      • Имя "скрипта" программе указывается в командной строке. С символа '#' начинаются комментарии. Следствие -- можно файлам вначале прописывать #!/.../thermotrain.
      • Все используемые параметры должны объявляться, с описаниями, типа
        param NAME "title" description...
        -- это позволит автоматически генерить панель настроечных параметров.

        Все переменные должны также объявляться --

        var NAME
        как и используемые измеряемые параметры --
        mes SPEC
      • Измеряемые параметры -- это просто ссылки вида ЭЛЕМЕНТ{.ЭЛЕМЕНТ}.КАНАЛ в иерархии самой программы (как это делается в istcc).

        (И не префиксировать ли эти имена символом '$' -- и вообще синтаксис сделать похожим на будущий язык формул.)

      • Все "объекты" -- параметры, переменные, каналы -- для программы объединяются в один массив, и при "компиляции" в "код" вставляются просто номера ячеек в этом массиве.
      • И после этого всего -- а не сделать ли этот скрипто-язык отдельным достаточно общим модулем, подходящим для использования в разных программах?

    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: собственно, с "языками" все понятно -- надо иметь их ДВЕ штуки:

    1. Виртуальную машину формул -- для вычислений; с транслятором, по проекту cda::12-01-2004.
    2. Embedded TCL -- для махинаций (типа demag'а и прочих длинных действий типа термоциклирования и салимовскостей).

    Плюс -- организовать бы какой-нибудь state machine, в идеале -- чтоб action'ами и checker'ами (или что там будет) могли работать как формулы, так и tcl-куски (плюс, и обычные C-функции, естественно :-).

  • 08.09.2013: по результатам длительных размышлений на тему "а как бы этак скрестить формулы, SEA/seqexecauto и сценарии", в т.ч. от 20-05-2005 («"формулы" -- это КОНЦЕПЦИЯ»), 19-08-2005/16-09-2005, 22-12-2010/24-12-2010 (и далее по Chl_scenario).

    Вроде бы выработано решение -- надо, и тут будем обсуждать детали технологии.

    08.09.2013: краткое резюме мыслей на настоящий момент:

    1. "Формулы" становятся КОНТЕКСТОМ. Чтоб там хранить некое состояние. И чтоб исполнение возвращало результат -- завершено или "работает"; и если "работает", то даже ПИШУЩАЯ формула должна как-то дёргаться по приходу данных (а КАКИХ, от какого сервера?
    2. О синтаксисе (и общей концепции вида):
      • (Отвергнутая идея) Связь с "SEA": просто синтаксис формул сделать таким, как бы каждая строка-команда из двух частей (через какой символ?), где первая -- действие, а вторая -- условие-проверяльщик. А паузы как?
      • (Принятая идея)Да нифига -- надо просто сделать команду типа "wait_until", которая б тормозила сценарий до исполнения некоего условия. А пауза -- да прямо командой "sleep".

        Тогда "три компонента строки-команды" превращаются просто в три последовательных опциональных команды, выполняющих роли действия, паузы и проверяльщика.

    3. Для возможности махинировать вне пределов cda (например, писать в другие ручки -- что реально будут реализовывать "extension'ы" языка) надо б как-то указывать "контекст местоположения" -- например, передавать в "execformula()" под.private-pointer, который по факту был бы указателем на Knob-содержатель формулы-сценария.
    4. Отдельный вопрос -- организация циклов. Пока непонятно.
    5. Надо держать в уме, что clientside-сценарии не всегда являются хорошим подходом (вспоминаем авто-стреляние с dl200me@liu, которое корректнее оказалось перенести в драйвер).

      Поэтому технология должна годиться и для использования в сервере -- как работает SNL@EPICS.

"IOC health monitor"
  • 19.05.2007: по сегодняшнему опыту, когда прыгало питание и контроллеры перегружались -- а не сварганить ли нам программу "IOC health monitor", примерно по образу и подобию той, что видели на PCaPAC-2006?

    19.05.2007: то есть -- чтобы программулька просто запускала на контроллере некую программку, и постоянно (раз в N секунд) слала бы ей application ping.

    Вообще-то это делается даже тривиальнее -- создается некий специальный тип драйвера, этакий "noop для подчиненного контроллера", и из него регулярно пытается вычитаться некий канал. Неа, такое не прокатит -- сервер пошлет запрос и будет ждать уже до TCP'шного таймаута (900 секунд).

    Так что -- простенький протокол, чтобы на стороне контроллера просто делался "echo reply", а на стороне хоста программа ВНЕ ЗАВИСИМОСТИ от прихода/неприхода ответов раз в N секунд слала бы запросы.

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

    • При "броске питания" она лишь ненадолго (~0.5 минуты) взморгнет синей и потом красной лампочкой, и опять позеленеет -- так что реально оператор едва ли успеет заметить.
    • Собственно серверам, связанным с такими контроллерами, оно никак не поможет.

    Так что -- "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").

  • 28.05.2007: а вообще-то, если просто хочется сократить время обнаружения отпаданий с KEEPALIVE'овских 7200 секунд примерно до ETIMEDOUT/ECONNRESET'овских (максимум) 900, то можно ввести application ping -- чтобы, например, раз в минуту дергался некий appping_timeout_proc, который бы посылал пакет "KEEPALIVE", на который драйвер может отвечать (а может и не отвечать, это уже менее важно) таким же пакетом.

    Это приведет к тому, что

    1. Даже неиспользуемые в течение некоторого времени блоки/драйверы (типа ЦАПов и УРов) будут "пинговаться" и их сдыхание будет обнаруживаться быстрее.
    2. Ситуация, когда послан некий запрос (e.g. к adc200), а еще до получения ответа устройство отрубили, также покрывается.

      Только надо будет разобраться с тем, как бы все-таки сделать "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!

    Делаем:

    • Ввел в switch()'и в _fd_p() обоих драйверов пустышки case ***_PING:;. Оно и так бы ничего не вякало, но -- для правильности.
    • Вводим собственно "heartbeat" с периодом 5 минут. Чтобы не маяться с включением/отключением его при оживлении/умирании драйвлета, тупо молотим ПОСТОЯННО с периодом 300 секунд, а PING шлем только при is_ready.
    • И даже первоначальная установка в init_b() делается тупо прямо вызовом HeartBeat().
    • Гадкость: поскольку все прочие функции, имеющие дело с fd, получали его как параметр при вызове сервером, то пришлось добавить 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) -- всё отлично работает.

    Сценарий:

    • Запускаем сервер.
    • Грохаем всех клиентов, включая cx-starter (чтоб постоянные read-запросы не генерили).
    • Дергаем питание CANGW.
    • Вуаля -- через 5 минут после запуска сервера он пытается послать PING, получает в ответ пакет RST, и драйвер получает уведомление о закрытии сокета.
    • Единственная "странность": судя по дампу ethereal'а, он шлет первый ARP через 5 секунд после RST, хотя в ScheduleReconnect() стоит 10-секундная пауза.

      Видимо, это уже личное поведение самого ядра -- оно так же шлет ARP и при отсутствии в течение некоторого времени просто TCP'шных ACK-ответов (если погасить питание контроллера в процессе обычного обмена пакетами).

    • И, соответственно, если контроллер в момент PING так и не включен, то начинается стандартная процедура перепосылки TCP-пакетов, которая заканчивается (через 900 секунд, что ли) односторонним закрытием сокета, а дальше -- то же самое.

    И еще замечание на тему "как вообще могла возникнуть та ситуация, если клиенты постоянно генерят 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 на сварке. Позорище!!! :-)

О запуске внешних команд по нажатию knob'ов...
  • 22.05.2007: итак -- свершилось: Старостенко жаждет, чтобы переключатель камер при выборе некоей камеры уставлял tvtime'у специфичные для этой камеры параметры. А делается это -- через tvtime-command, т.е. -- надо уметь вызывать внешние команды. Чего всегда и боялись, ибо это сразу откроет банку с гнусными пауками (can of worms)...

    22.05.2007: итак, есть три различных проблемы:

    1. Самая страшная -- это security. Если разрешить запуск произвольных команд -- то будет полная задница. Security как понятие нафиг исчезает.

      Идея решения: сделать список разрешенных команд, как в sudo (там это /etc/sudoers). Все, что не разрешено -- то запрещено. Отдельный вопрос, ОТКУДА брать этот список (локальный файл? БД? ...?).

    2. Как именно указывать эти команды? Есть два варианта:
      1. Сделать специальный knob-type, выглядящий кнопкой (как уже сейчас есть actionknobs), которым бы в options указывалась команда. Но это ограниченный вариант.
      2. Сделать специальную CMD_EXEC (чувствительную к FLAG_SKIP_COMMAND!), которой бы также указывалась командная строка. Это проканает с любым типом knob'а, и, собственно, является единственным подходящим вариантом для нашей конкретной задачи (camsel).
    3. В любом случае, надо как-то уметь бы параметризировать команды -- хотя бы значениями регистров, а то фиксированные команды -- это задница. Тут, в соответствии с п.2 варианты такие:
      1. Отдельным ключиком (ключиками?) указывать в options то, из каких регистров брать параметры.
      2. Сделать, чтобы CMD_EXEC брала бы из стека также и количество параметров, а потом -- еще значения в указанном количестве (аналогично OP_POLY).
      В обоих вариантах, в командной строке должно быть можно указать и некоторое количество форматов %e/%f/%g, а уж оно должно проверить, что форматы корректны и что их число совпадает с количеством параметров.

      И -- в ИМЕНИ команды (т.е., до первого пробела) форматы запрещаются, из соображений security. Надо такую параметризацию -- делайте скрипты.

    Вариант с CMD_EXEC удобен, кстати, еще и тем, что может использовать собственно уставленное в knob'е значение -- хоть это текстовое поле, хоть селектор. А при наличии CMD_EXEC кнопкоподобный "KNOB_EXECCMD" эмулируется тривиально.

    В общем, правильнее будет остановиться именно на формульном варианте.

    Кстати, а как это сделано в MEDM/dm2k?

Об отправке параметров БОЛЬШИХ КАНАЛОВ (да и просто произвольных экранных штучек) на график
  • 31.07.2007: Малютин возжелал, чтобы в ndbp стало можно отправить на график и показать в bigval'е значение "avg", которое вообще даже не то что ручкой -- а и параметром большого канала не является! Это просто XmText, в котором показывается то число...

    31.07.2007: конечно, частично (хоть и не в этом случае) нас могла бы спасти идея от 27-06-2007 насчет "доступа к параметрам больших каналов как к обычным каналам".

    А вот как делать ТУТ?

    • Первый, напрашивающийся-на-будущее "кошерный" вариант -- делать все такие штуки не виджетами,а ручками, и, перехватывая нажатия Button3, вызывать соответствующие методы parent'а.

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

    • Второй, более реалистичный на прямо сейчас -- сделать таким числам неотображающиеся "тени"/"синонимы"-ручки, внутри самого этого элемента-осциллографа, маппирующиеся на локальные регистры. Эти регистры программно обновлять, а Button3 так же перехватывать, подсовывая ссылку на ручку-синоним.

    Оба варианта кривы, не хватает им ни красоты, ни общности.

    01.08.2007: по некоторому размышлению -- все становится совершенно ясно и несложно!

    Во-первых, весь подобный сервис будет обеспечиваться ТОЛЬКО для knob'ов -- не суть важно, обычных или же simple, и откуда там берутся данные (параметры большого канала, локальные) -- тоже неважно. Главное -- что именно у структуры "ручка" есть и история, и "методы".

    Во-вторых, в CXv4 все будет крайне просто: "parent'у" ручки будут (как-то) назначаться методы ShowBigval() и ToHistPlot(), а сдвиг истории будет выполняться обычным вызовом CdrProcessKnobs(), который увидит локальность этой ручки, так что не станет дергать cda, а сразу использует значение curv, и проделает лишь оформительство типа сдвига истории.

    В-третьих, сейчас, в CXv2, это проделывается примерно тем же макаром -- Widget заменяется на Knob, которому "назначаются" методы для отправки на bigval и график, а сдвиг -- делается "руками", по приходу данных в большой канал. Вопрос только, когда и кто будет дергать обновления в отображающих окнах? Видимо, пусть сами идут, раз в секунду.

    12.12.2007: и еще Малютин с Танькой-Рыбкой захотели уметь отправить на график также linipp'шные числа (число частиц, и т.п.), рисуемые на графиках. Задача из той же оперы.

О "визуальной" загрузке режимов
  • 30.11.2007: Малютин раскудахтался, что он хочет иметь такую вещь: чтобы можно было "посмотреть", что сохранено в режиме -- как бы загрузить его, но БЕЗ отправки в аппаратуру, и посмотреть значения, причем те, которые отличаются от текущих, чтобы как-нибудь помечались. И чтоб потом, как понравится, можно было нажать "Update", и оно бы отправило вес в аппаратуру.

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

    30.11.2007: собственно, несколько вопросов -- идеологических:

    1. При "просмотре" программа должна ведь переходить в режим "пауза", так?
    2. И -- кроме возможности "принять" режим должна ведь быть возможность от него и отказаться, правильно?
    3. А при загрузке второго, третьего, ... режимов -- оно должно показывать отличия от ТЕКУЩЕГО или от ПРЕДЫДУЩЕГО?

    И еще вопросы технологические -- а как бы это реализовать-то?

    1. Как "помечать"? Цвета-то уже все поразобраны...
    2. Как вызывать такую загрузку-"просмотр" -- UI?
    3. Куда-то (опять на тулбар?) придется поместить кнопки "Принять" и "Отказ", плюс -- придется всем зевести кнопку "Пауза" (или ее и сделать за "Отказ"?).
    4. Принятие отдельных каналов как делать? Текстовых -- вроде просто, нажать в них [Enter], и дальше все получится автоматом.
    5. Как "исполнять" просмотр -- вроде понятно: придется завести некий глобальный флажок/счетчик, который если !=0, то собственно Cdr.c::ParseChanVals() станет выполнять несколько другие действия вместо SetControlValue(,the_v,1) -- например, ",0" и производить "сравнение".

    03.12.2007@бассейн: а можно ведь показывать не каким-то другим цветом, а инверсией -- менять местами foreground и background. Тогда, с одной стороны, никакой дополнительный цвет изобретать не надо, с другой -- все будет отличнно читабельно, а с третьей -- будет показываться и "статус" новых значений.

    Реализовать же это можно было бы -- введя доп. битик в rflags, при наличии которого в самом конце выбора цветов fg и bg просто менялись бы местами.

    09.12.2007: ага, битик -- фигушки: ChooseKnobColors(), выбирающая цвета, доступа к rflags не имеет, действуя только на основе newstate. И это правильно, ибо нефиг, цвет должен однозначно определяться состоянием. Так что SetColstate() -- v4'шная set_knobstate() смогут работать корректно, просто сравнивая номера состояний.

Listen-сокеты ВСЕГДА должны быть non-blocking!
  • 24.11.2007: еще по прочтении книги Стивенса и Раго -- слушающие сокеты обязательно должны быть неблокирующимися. Обоснование: если клиент после отправки SYN (т.е., когда запрос уже попал в backlog), но еще ДО вызова сервером accept() успеет прислать FIN (например, сдохнет), то запрос из очереди будет удален, и когда сервер дойдет до принятия соединения, то просто заблокируется до следующего запроса (который может и никогда не придти).

    08.12.2007: тэкс, смотрим -- fdiolib делает ВСЕМ своим дескрипторам O_NONBLOCK, так что с ее клиентами все окей.

    Опять же -- cx/src/programs/daemon/ трогать не будем.

    Итого -- остаются 3 точки:

    1. cx-porter.
    2. rrund: а он и не многозадачен, и как раз и "должен" зависать на accept()'е. Так что -- неактуально.
    3. canserver: использует fdiolib, так что с ним проблема отсутствует. Вычеркиваем.

    Итого -- остался ТОЛЬКО 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: еще некоторое время назад Малютин расквакался, что хочет мочь сохранить всякие параметры -- шаги, диапазоны -- отдельно от собственно режимов. И еще -- чтобы оно некие "умолчательные" параметры автоматом считывала при запуске.

    16.12.2007: в принципе, он скорее прав, чем неправ -- что-то с этим делать нужно. Конечно, частично проблема снимется в CXv4 -- с введением текстовых описателей, вполне user-editable. Но -- какой-нибудь способ сохранения параметров отдельно от режима совсем не помешает.

Выделение ручек, находящихся в режиме редактирования
  • 06.02.2008: вчера, после беседы с Пашей Селивановым, возникла мысль, как расшивать ситуацию "у ручки включен режим редактирования, и она в течение минуты будет показывать старое значение, хотя другой оператор мог число уже поменять" (это было у Старостенко с Петренко, на гусиной программе, за что Гусев был бит).

    06.02.2008: собственно идея -- ручку с usertime!=0 надо как-то выделять. Первая мысль -- цветом; вторая -- цветным бордюрчиком вокруг. На бордюрчике и остановился.

    "Очевидная" идея -- включать/выключать бордюрчик одновременно с уставкой/обнулением поля usertime. А вот КАК это делать -- некая хитрость:

    • Собственно отображение рамки было возложено на Xh -- этим занимается специально созданный модуль Xh_hilite.
    • А вот с собственно ВЫЗОВОМ включения/выключения рамки -- дело обстоит хуже. Загвоздка в том, что если включение делается из одной точки -- UserBeganInputEditing(), куда и вставлен вызов XhShowHilite(), то СБРОС usertime и, соответственно, ОТКЛЮЧЕНИЕ рамки может требоваться из нескольких мест:
      1. Knobs.c::SetControlValue() -- к ней сводятся CancelInputEditing() (Esc и увод фокуса) и Enter, а также SetKnobValue() по истечении 60 секунд для простых ручек. Здесь пока что все просто -- это все тот же самый KnobsInternals.c.
      2. Cdr.c::SetControlValue() -- при истечении 60 секунд, когда ручка возвращается к автообновлению. Вот это уже хуже, поскольку libKnobs и libCdr формально вообще никак не связаны, у них даже нет общей, используемой ими обоими библиотеки, через которую можно было бы их связать.
    • Поэтому пришлось прибегнуть к весьма искусственному приему -- связывание через Chl, который является "клиентом" их обоих.

      Для этого введен hook cdr_unhilite_knob_hook(), вызываемый, если он != NULL (таким образом, для GUI-less клиентов/иерархий ничего не меняется), при сбросе usertime в ноль. Уставляется этот hook в довольно неочевидном месте -- в ChlSetGrouplist(); просто потому, что, как было установлено, эта функция вызывается всеми существующими на данный момент видами клиентов.

      Реально, простоты ради, коли уж все равно пошла такая халтура, Chl не "связывает" Cdr с Knobs, а самостоятельно делает XhHideHilite(k->indicator). Это, конечно, халтура, ибо дублирование кода, а должно бы делаться какой-то одной Knob'овской функцией, но пока потянет.

    • Некоторое изменение -- поскольку теперь надо вызывать снятие прямоугольника только при ПЕРЕХОДЕ usertime в 0, то обнуление делается не безусловно, как раньше, а только при ki->usertime!=0.
    • А вот в CXv4 все намного корректнее -- там есть отдельная библиотека libdatatree, услугами которой пользуются и libMotifKnobs, и libCdr, и махинации с usertime исполняет именно она. Поэтому в ней можно будет предусмотреть пару hook'ов (вкл/выкл рамки), которые будут заполняться by libMotifKnobs (самой правильной на данный момент точкой выглядит MotifKnobs_HookButton3() -- она вызывается при создании любой ручки).

    Just for information:

    • Собственно идея вылезла из того, что Паша очень жаждал отправлять в сервер каждое/любое значение, образующееся в результате редактирования текстового поля (например, 600+BackSpace->60, +5->605), а я его убеждал, что так делать не стоит, а надо лишь по нажатию Enter. И чтобы было очевиднее, что отображаемое число НЕ является прописанным в аппаратуре, надо ручку как-то подсвечивать.
    • А, поскольку разных цветов используется уже по самое не могу, то мне пришла в голову мысль вместо смены цвета рисовать вокруг ручки зеленую рамочку.
    • Upd 08.02.2008: а Гусев, оказывается, уже тоже пришел к выводу, что надо число выделять, но остался при мнении, что это можно делать просто еще одним цветом.

    08.02.2008: а покамест -- "done".

    24.03.2009: насчет CXv4 -- какие, нафиг, hook'и?! Во-первых, нужна ОДНА функция, которой бы передавалось состояние (on/off). А во-вторых -- не hook, а просто МЕТОД контейнера! И тогда мы автоматом получаем все бонусы тамошней архитектуры -- и потенциальную множественность и "переопределяемость" (у разных элементов -- разные способы выделения).

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

    24.03.2009: а вот и нет -- попытка реализации через методы оказалась просто ДИКО затратной!

    • Во-первых -- потому, что непонятно, какой именно тип контейнера/граппировки будет topmost, так что неясно, кому же из них надо уставлять в не-NULL это поле dataknob_cont_vmt_t.ChgEdtState. А уставлять ВСЕМ контейнерам -- ну тогда какая уж тут, нафиг, "гибкость"...

      Как бы вроде решение -- тогда уж надо было бы ставить этот метод в ChlTopVMT, втихушку подпихиваемый Chl'ем. Но в таком случае это будет только для Chl-программ, что неправильно.

    • И во-вторых -- вся эта алхимия не будет работать для простых ручек (simple knobs), поскольку у них "контейнером" -- с точки зрения методов -- работает Simple_fakecont/Simple_VMT, а там уж никаких "оформительских" методов не может быть по определению.

    Так что -- надо возвращаться к идее с hook'ами:

    1. Это самый дешевый вариант (пусть и не самый формально-правильный).
    2. Сам "hilite" -- относится по смыслу всё-таки скорее к MotifKnobs (даже не к KnobsCore!), так что в основном там он и должен реализовываться, а datatree, так и быть, может немножко поспособствовать.

    Короче -- так и сделал. Для уставки hook'а сделана функция set_knob_editstate_hook() -- это чтоб не светить саму переменную наружу и не маяться с extern и инициализацией.

    P.S. Вот за каким, спрашивается, чертом полез СЕЙЧАС портировать hilite в 4cx/, а? Огреб только кучу сложностей. А занялся бы этим потом, при возникновении РЕАЛЬНОЙ потребности -- всё прошло бы легче и само собой.

    25.08.2011: нынче ответ на вопрос "за каким чёртом" появился: зато теперь будет очень просто перевести CXv2 на datatree -- потому что вся чёрная работа была проделана тогда, два с половиной года назад, а сейчас осталось просто заиспользовать этот код.

    05.11.2013: еще в продолжение -- сегодняшние мысли на тему "как правильно сделать в v4" и результаты обдумывания с учётом результатов за 24-03-2009:

    1. Собственно "озарение" (традиционно в душе): делать show/hide методами элемента, и иметь по штуке на обычное окно и subwin (ровно как 24-03-2009/первое).
    2. Но 24-03-2009/второе приведены здравые возражения -- а как определять, в каких точках дерева надо его ставить, а в каких нет? Ведь subwin'ы -- лишь родители, а внутренности у них в отдельных multicol'ах.
    3. После чего красивой начинает выглядеть идея "перевести это на OverrideRecirect-окна" (23-07-2010, раздел по Xh_hilite).
    4. ...но вспоминается потенциальная проблемность в других средах -- типа Windows и Compiz/Wayland/..., где эти формально разные окна вовсе не факт, что будут взаимовыглядеть правильно.

      Хотя, тултипы-то работают.

Слово "treat"
  • 13.02.2008: стыд мне и позор -- в нескольких местах в комментариях стояло "treate" вместо правильного "treat".

    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: в процессе обсуждения общей архитектуры софта для карботрона с Карнаевым и Чеблаковым опять всплыла тема "четвертого уровня" в 3-уровневой архитектуре.

    03.07.2008: вообще-то впервые эта проблема была замечена еще на ICALEPCS-2005 в Женеве, в последний день:

    FR2.6, M.Clausen, "EPICS Office": Важная идея: разделения приложения как сущности, выполняющей некий обсчет/действия, и отображения (интерфейса). Приложение может быть запущено постоянно, "standalone", а интерфейс к нему -- по мере недобности. 2 следствия:
    1. Архитектура становится не 3-, а 4-уровневой.
    2. Приложение словно бъется пополам -- надо как-то координировать "приложение" и "отображатор".

    А в остальном -- словно воспроизводство нынешней границы "сервер<-+->клиент", т.к. с точки зрения cx-starter'а, при запуске "интерфейса" надо стартануть "приложение".

    А вот по результатам нынешнего разговора:

    • Во-первых, "по смыслу" этот уровень располагается между верхним (№1) и средним (№2), так что он скорее не "четвертый", а "полуторный".
    • Первая мысль, как бы правильнее такие вещи реализовать -- это завести "пустой" сервер, БЕЗ аппаратуры, но с кучей каналов-"почтовых ящиков", из которых программы-клиенты будут читать, а уж программы-считалки -- класть туда.

      Плюс такого подхода -- что доступ к таким "данным" будет идти по тому же самому протоколу, что есть однозначно правильно.

      (Но как именно реализовывать почтовый ящик? В смысле "оповещений" клиентов об изменении данных? Видимо, просто noop-драйвером. Но неприятно, что "конфигурация" каналов будет в ДВУХ местах -- у собственно насчитывающей программы, и в конфиге такого "почтоящичного сервера".)

    • Так что -- правильнее реализовывать такие программы-насчетчики именно как ДРАЙВЕРЫ, а исходные данные они будут получать по тому же (CX?) протоколу от серверов-соседей -- все равно предполагается иметь поддержку cxlib+cda+... в сервере для таких целей.
    • Но "проблема" исключительно в том, как для CX-starter'а помечать такие сервера, как "зависящие" от других серверов -- что перед их запуском (в т.ч. при "вынужденном" запуске для клиента) надлежит запустить еще некий другой сервер.

      И -- должна ли быть возможность пускать сервера, к которым нет клиентов, такие как бы "демоны"? И как вообще таких пускать?

      (А "зависимость" -- все равно понадобится, если в сервере будет "chaincx".)

CXv2+
  • 19.10.2008: ясно, что, с одной стороны, делать CXv4 будет долго, а с другой -- можно добавить в CXv2 некоторые фичи из будущей версии, создав гибрид, пригодный "уже сейчас" для использования и для Зеленограда, и для Снежинска, всяким Роговским.

    19.10.2008: Список требуемых доделок/фич примерно таков (главное -- БД!):

    1. Поддержка layer'ов.
    2. Сервер должен мочь спокойно реагировать на каналы "out of range" -- всегда отдавать "нэту такога", что ли? (Это нужно, чтобы ненайденные каналы могли занимать позиции в списке для запроса).
    3. SMP4TD.
    4. На его основе -- новый парсер БД для сервера, который бы позволял добавлять символьные имена блокам и каналам внутри них.
    5. И -- чтоб мочь указывать physinfo каналов прямо серверу.
    6. Наследование -- экземпляром блока от определения типа блока: а) имен каналов и б) physinfo каналов.
    7. Добавить в CX-протокол: а) запрос номера канала по имени и б) запрос physinfo канала по номеру (но оно при запросе по имени должно возвращаться автоматом - для одношаговости поиска канала).
    8. Глобальной таблицы псевдонимов, значится, пока не будет.
    9. На основе SMP4TD парсер текстовых файлов описания подсистем -- как Cdr_fromtext.c в CXv4.

    Все равно остаются вопросы (они касаются, возможно, и идеологии в CXv4 тоже):

    1. А что, если при живой программе канал, найденный по глобальному псевдониму, переезжает в другой сервер?
    2. А если просто канал внутри сервера переезжает? Ну да, мы повторим резолвинг, но... Надо для каждого канала помнить его "textual reference", и при поиске - зарегистрирован ли уже такой - сравнивать именно по ним, а не по номерам. Каналы же, указываемые номерами, можно считать за текстовые ссылки, у которых вместо имени канала указан номер.
    3. И, кстати, надо в "Knob properties" иметь ДВА отдельных поля - собственно ссылка, и уже разрезолвленный номер (показывать при наличии как "\n(%d)", чтоб оно шло 2-й строкой?).

    20.10.2008: насчет проблемы под номером 2 ("мочь спокойно реагировать...") -- а решение-то очевидно:

    1. надо ввести проверку на тему выхода за границы в MarkRangeForIO() -- чтоб он "втискивал" запрос в рамки имеющегося набора каналов, плюс
    2. аналогичную проверку (точнее -- guard) в FillDataPacket(), чтобы он таким "несуществующим" каналам ставил что-то, приводящее к CDA_FLAG_NOTFOUND. Вопрос только -- что? Поскольку это именно cda'шный флаг... CXRF_UNSUPPORTED+CXRF_INVAL, плюс age=255?

      14.11.2012: давно уж вызрела идея -- в v4 маппировать "несуществующие" каналы на 0-й, а у него будут и должные флаги, и бесконечно старый возраст. Дальнейшие рассуждения на эту тему -- уже в bigfile-0002.html, в разделе про libcxsd.

    3. Проверки в CheckFork() с результатом CXT_EINVCHAN просто выкинуть нафиг.
    4. В обработке больших каналов -- что-то аналогичное...

    21.10.2008: во-первых, забыл упомянуть в "амбициозном списке" о требующихся для адресации-по-именам добавлениях в cda и Cdr.

    А во-вторых -- глядя на пункт 9 ("парсер текстовых файлов описания..."), становится опять очевидно, что надобно будет просто реализовать эту штуку в CXv4, а сюда сделать back-port. "Эта штука" включает, естественно, и CXv4'шные Cdr+KnobsCore+MotifKnobs, и новые структуры дерева -- datatree*. А вот cda останется практически старым, просто с модификацией -- поддержкой адресации-по-именам.

  • 21.02.2009: очередной вариант идеи -- перейти на однопроцессный сервер. Ведь cxscheduler+fdiolib уже есть.

    22.02.2009: Некоторые детали/комментарии:

    1. Сразу, конечно, начинает хотеться сделать тогда "всё по-новому". Но стоит ограничиться разумным минимумом, перечисленным ниже.
    2. Из еще несделанного понадобятся:
      1. cxloader;
      2. cxlogger;
      3. smp4td (для начала -- можно будет обойтись простым эмулятором).
    3. Поддержка МНОЖЕСТВЕННЫХ больших каналов в одном соединении.
    4. Новый API драйверов (как в CXv4/dbody).

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

Плавное изменение уставок CANDAC
  • 31.01.2009: А ведь можно сделать плавное изменение значения в ЦАПе -- прямо на уровне драйвера, просто вместо записи-сразу включать временнОй цикл и писать следующий шаг, например, каждые 100мс.

    31.01.2009: детальное обсуждение:

    • Число шагов или их размер можно задавать в доп.каналах драйвера (по 1 каналу на каждый канал ЦАПа). Например, 0 -- ставить сразу, <0 -- число шагов, >0 -- размер шага.
    • Как конкретно обходиться с этим "плавным изменением" -- некоторый вопрос. В частности:
      1. Видимо, надо ВСЕГДА иметь некоторый "heartbeat" с частотой 10Гц, который бы, если в переменной "число каналов заказанных на плавное измерение" записано >0, пробегался бы по списку всех каналов, и те, что заказаны, обрабатывал бы.
      2. Как именно обрабатывать: видимо, каналам НЕ делать уставку сразу, а лишь программировать заказ для плавного-изменения. Поскольку неизвестно, сколько осталось времени до heartbeat'а, и следующий шаг может быть сделан слишком быстро.
      3. При записи -- видимо, как и при обычно записи, стараться дождаться ответа от устройства; если ответ не пришел -- то и незачем пока слать дальше.
      4. И отдавать ли эти промежуточные ответы наверх? Возможные минусы -- что возьмет, гад, и пришлет следующий запрос из очереди. Но:
        • Во-первых, пусть шлет -- перезакажем плавность (более того -- иначе длинный цикл никак и не прервешь).
        • А во-вторых -- приятно будет видеть, как циферки бегут. (Хотя и хреново, что тем самым OTHEROP зажжется...)

        Так что -- отдавать!

    • Эти доп.каналы надо бы разместить ПОСЛЕ обычных -- чтобы хоть какое-то подобие совместимости сохранилось...
    • В linmag'е убрать выключатели и [R], а вместо них поставить кнопки [...], и уж в тех окошках иметь и выключатели, и кнопки сброса, и управление шагами.

    Но не всё так просто --

    1. Для этого понадобится заиметь >1 таймаута на драйвер! Что -- вводим расширенный API, позволяющий регистрировать, например, до 10 таймаутов?
    2. И вообще -- не перевести ли сервер на cxscheduler, а? (а вот на fdiolib не удастся -- sendmsg(), мать его...)
    3. ...а систему сборки -- на GeneralRules.mk? :-)

    Замечание на будущее, для 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".

  • 27.03.2009: в связи с недавними разборками с квантами (см. bigfile.html за 23-03-2009) возникают серьезные сомнения в работоспособности этой архитектуры с плавным изменением.

    Проблема в том, что "удержание юзерского значения", обеспечивающее круглые числа (1800, а не -1799), работает только при ОДИНОЧНОЙ операции. Если же затребованное значение будет получаться не сразу, а через сколько-то шагов, то логика с квантом просто отключит "удержание", и в конечном итоге отобразится именно некруглое число. А в магнитной системе это малоприемлемо.

    Ну и что -- весь проект "плавного изменения уставок" накрывается медным тазом?!

    27.03.2009: некоторая последовательность выводов и размышлений:

    • Как минимум, один просто вывод: схема с передачей данных между сервером и клиентом в ЦЕЛЫХ числах нормально работает только при простых и однозначных операциях чтения/записи, а при всякой интерполяции и т.п. это всё ломается.
    • Хотя, с другой стороны -- а как бы нас спасли вещественные числа, раз там всё равно производилось бы "округление" до ближайшего достижимого устройством, а оно ВСЁ РАВНО отличается от запрошенного юзером...

      Разве что "квант" поддерживать и в вещественночисленном драйвере -- но как? Неа, это плохая идея :-).

    • О -- а что, если в железку писать не просто val_to_candac16(val), а val_to_candac16(val+quant/2)?

      Вопросов 2:

      1. Надо ли как-то химичить с НАПРАВЛЕНИЕМ округления в зависимости от знака?

        А хбз, но, вероятно, достаточно вставить прибавление quant/2 прямо в val_to_candac16().

        Видимо -- НАДО химичить, учитывая, что ВНАЧАЛЕ делается умножение/деление, а ПОТОМ сдвиг.

      2. Где каким боком это может вылезти?

    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: история:

    • При изготовлении xcdac20_src.c заново задумывался о том, как должно отражаться плавное изменение на каналах CHAN_OUT_n_BASE+n, и, не заглянув сюда, решил, что оно текущее значение должно отражаться в этом канале СРАЗУ (кстати, казалось, что где-то это записывал, но найти -- не могу).
    • В результате -- жалоба от Феди Еманова: ручки вырываются!!!
    • А дело в том, как такое сразу-отражение-в-канале-уставки взаимодействовало со стрелками: если обычно человек стрелками "добивает" уставку до нужного значения, то тут -- оно отбрыкивало поле ввода на первоначальное_значение+шаг_в_секунду, и следующее нажатие стрелки прибавляло уже к нему.
    • В ответ на аргумент "а как же ты будешь видеть настоящее значение" было сказано, что вполне достаточно видеть Iизм.

    Короче -- убрал то "|| 1" из проверки перед ReturnChanGroup() в _fd_p(), помеченной "This includes table-driven channels".

    05.03.2012: уж и не знаю, что там ТОГДА было откуда убрано, но вот сейчас "|| 1" в xcdac20_drv.c стояло, так что:

    • Поле уставки менялось вместе с OUT_CUR.
    • А вот при УБИРАНИИ -- т.е., чтобы на ch_isc-каналы оно не возвращало текущее значение в уставку -- получается хрень: сначала (через 1 цикл) оно отпрыгивает к предыдущему значению, потом через 5 секунд посиневает, и только по окончании изменения ставилось в запрошенное.
    • И еще: в сейчас-плавно-меняющий канал нельзя записать другое значение -- в _rw_p() стоит проверка, что НЕ пытаться что-то писать при ch_isc[l] ("No mangling with now-changing channels..."). 06.03.2012: был неправ. См. ниже.

    И что, спрашивается, с этим всем делать?

    1. При инициировании записи придётся ТУТ ЖЕ делать ReturnChanGroup() с запрошенным значением -- чтоб не менялось на предыдущее. А это очень гадостно -- т.к.
      • При отсутствии блока оно будет показывать якобы прописанность, а реально ничего не запишется.
      • ...и такое поведение радикально отличается от обычного, со сразу-записью.
    2. Со сменой цели: это в любом случае надо разрешить.

      Но не только убрать проверку, а еще учесть, что:

      1. В случае перехода на модель "сразу показываем trg-значение" надо здесь же возвращать уже ЕГО.
      2. Если мы меняем цель на ту, что около текущей -- то надо это проверить и сделать "завершение плавного изменения" (т.е., в "Just do write?..." сделать if(ch_isc[l]){ch_isc[l]=0;num_isc--;}).
      3. И, заодно -- тут же сделать сразу-возврат попрошенного (всё из-за отличия поведения при плавном и резком изменениях!).

    Гадко это всё выглядит, ОЧЕНЬ гадко...

    • Вот если бы как-нибудь так извернуться, чтобы оно работало как если бы число в ЦАП ушло, а именно какое-то там оконечное устройство долго реагирует...
    • А частичное решение -- ввести "в пару" к ch_fin[] еще и ch_fst[] (First STep), который вначале ставить в =1, а по первому получению ответа на уставку делать его =0 и возвращать trg-значение.

    06.03.2012: пробуем это сделать...

    • Что при отсутствии блока такие фокусы будут сильно неправильный результат показывать -- скорее нет, т.к.
      1. Оно не станет слать кучу последовательных записей подряд -- пока не придёт ответ на предыдущую, следующий шаг не делается (функционал ch_nxt[]).
      2. При использовании запланированного 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..." означает, что с уже-меняющимися каналами мы НЕ пытаемся "оптимизировать" и уставлять в них значение одним скачком (зачем?), и в любом случае программируем плавное изменение; а вовсе не запрет менять цель.

    • 06.09.2013: это всё было только в xcdac20 и порождённом из него xcandac16, а в xcac208/xceac124 -- забыто. Сделал.

    А вообще уже напрашивается сделать "общее место", выполняющее стандартные операции (как предусмотрено для 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: решаем две описанные выше проблемы.

    1. При блокировании t_aff-каналов делается проверка, не ch_isc-ли они сейчас, и если да -- то оно отключается (ch_isc[l]=0; num_isc--).
    2. В _hbt() вставлена проверка "если ch_spd[l]==0 то сразу переходим к цели".

    (Следующим утром) Проверил, работает и то, и другое.

  • 20.03.2012: обнаружился позорный ляп, существовавший, видимо, еще с момента создания cac208_src.c: были перепутаны 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.

  • 06.09.2013: замечена некоторая неприятность: плавное изменение занимает несколько больше времени, чем должно бы -- процентов так на 10; это хорошо заметно на долгих процессах, где, например, вместо 50с выходит 56-57.

    Причина понятна -- в _hbt() используется запрос таймаута в варианте "after" (а не "at" с точным указанием), так что оно потихонечку съезжает как из-за просто подтормаживания (таймаут запрашивается чуть позже истечения предыдущего), так и из-за квантования (100Hz у ядра при желаемой частоте таймаута 10Hz как раз и дадут 10% неточности).

    С одной стороны, можно б было перейти и на "at"-указание, но не стоит. Плавное изменение -- это чтоб "не быстрее, чем", а для точного должны использоваться таблицы. Зато нынешний вариант безопасен, поскольку оно адаптируется к производительности проца.

  • 27.02.2017: вылезло (уже на v4, но там архитектура плавного изменения от v2), что каналы, запущенные на плавное изменение, игнорируют 0xFF:is_a_reset, а просто продолжают ехать.

    Насколько это правильно?

    Если нет, то надо по 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, то совсем не факт, что нужно прерывать плавное изменение.

    1. В случае просто проблем со связью -- если девайс НЕ сбрасывался! -- надо бы как раз именно продолжить движение (ну и пофиг, что была пауза).
    2. А вот если устройство само меняло значения -- например, нулило по [Reset], то смысла в продолжении нет.

    Главный вопрос -- как отличить первое от второго. Ведь ТОЧНО можно понять, лишь сравнив текущие значения с "надлежащими" (причём учитывая, что последнее отправленное могло не дойти), а текущие будут получены намного позже, когда от юзера может успеть придти еще толпа заказов...

  • 28.02.2017: и еще косяк найден, опять же заложенный еще в 2009-м: в _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'шных драйверах везде исправлено.

3rd generation CAN drivers
  • 19.02.2009: пора начинать задумываться о следующем поколении CAN-драйверов, с учетом полученного за эти годы опыта.

    19.02.2009: краткий обзор их предполагаемых характеристик:

    • Построены на основе layer'ов, сами же -- все отдельными модулями.
    • Поддержка понимания ошибок типа bus-off на границе layer<->HAL. (@13.12.2009)
    • Очередь -- на основе sendqlib'а. (@23.02.2009)
    • И с использованием механизма "callbacks" -- чтобы с глобальных ответов, типа 0xFD/0xFE, отдавались бы только нужные данные, а не все. (@22.04.2009)
    • В самой очереди -- все три вида отправки пакетов (enqueue, enq_ons, send_frame) должны делаться одним вызовом, с параметром "how"={enq,ons,snd}, а данные чтоб можно было указывать и прямыми, и va-параметрами. (@05.05.2009)
    • Поддержка "плавного изменения" уставок у ВСЕХ ЦАП-драйверов.
    • Поддержка "emergency pings" у всех АЦП-драйверов раз в минуту -- см. за 08-06-2006. (@22.04.2004)
    • Поддержка табличного режима у ВСЕХ ЦАП-драйверов, через большие каналы (а вот тут идеологию -- таблиц-то может быть несколько! -- надо еще придумать; плюс, групповые запуски, как -- отдельным драйвером с командным каналом?).
    • Поддержка осциллографического режима у всех АЦП -- либо по проекту от 08-02-2006, либо чтобы данные отдавались поштучно с большой скоростью через большой канал (как у impacis10).
    • Также всем АЦП уметь работать как "время назад" -- видимо, с остановом по какому-то еще командному каналу; или же иметь дополнительный драйвер фиктивного устройства с единственным каналом "общий стоп". (@10.04.2009)

      (Реально это, по словам Козака, пока нигде не требовалось. Так что можно и забить.)

    • (А групповые запуски АЦП? Там что-то мутное, Козак рассказывал...)
    • Поддержка калибровки ЦАПов (CDAC20, CEAC51).
    • Поддержка ВСЕГО ассортимента козачиных устройств, включая евромеханические CEAC51, 124, 121.
    • Явно надо придумать еще какую-то "библиотечку", чтобы поддержка почти-одинаковых устройств делалась бы одним исходником. Функционал этой "библиотечки":
      1. Работа с ЦАП и АЦП (у Козака чуть ли не одинаковые команды и форматы для всех этих устройств).
      2. "Плавное изменение" уставок.
      3. Табличный режим.
      (чем-то это напоминает cm5307_fastadc_common.h -- видимо, надо будет делать так же, include-файлом, которому "описание" устройства отдается #define'ами).
  • 22.04.2009: О поддержке калибровки ЦАПов...

    22.04.2009: детали проекта:

    • "Внешний вид" -- канал-команда, и/или специальный псевдодевайс, с одним каналом, который приводит к отсылке broadcast-пакета.
    • Плюс к каждому такому каналу -- r-канал со временем последней калибровки.
    • Как-то поддерживать там "метки": то ли прямо значениями каналов-команд, то ли отдельными каналами.
    • И дать всем таким драйверам auxinfo-параметры "setcallabelto=ID" (у реальных драйверов это б приводило к отправке пакета 0x7 по первому 0xFF, у виртуального -- просто к запоминанию числа).
  • 29.04.2009: надо бы позаменять используемые сейчас в *_drv_i.h имена каналов АЦП и ЦАПов -- 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".

  • 12.07.2009: начаты работы по первому "правильному драйверу" -- xcac208.

    13.07.2009: highlights:

    1. Предполагается возможность не только указывать beg,end,magn,time в auxinfo, но и менять их на ходу, плюс делать reset к начальному состоянию -- в соответствии с задумкой от 10-07-2009.
    2. В _drv_i.h предусмотрены ВСЕ возможные каналы, включая управление калибровкой (которая пока в CAC208 отсутствует) и не-групповое управление исполнением таблиц.

      Общая идея -- чтобы ВСЕ CAN/ADC/CAC-драйверы козачиных блоков поддерживали унифицированный набор управляющих каналов (как это уже сделано в fastadc).

    3. Эти управляющие каналы помещены в начало списка, за ними идут АЦП, потом ЦАП, а в конце -- регистры.

    22.07.2009: еще: поскольку там некоторое количество внутренних функций, которым помимо privrec'а нужен также и magicid, то, чтобы не передавать постоянно лишний параметр, создано поле privrec_t.magicid, идущее сразу за iface, перед handle.

    Видимо, надо сделать такую раскладку стандартной на будущее.

    Только мучает меня один вопрос: вроде ж, кажется, где-то неявно использовалось, что некое поле (iface? или handle?) где-то (в cankoz?) идёт по некоему стандартному смещению в privrec'е. Где именно -- хрен бы помнил, не не здесь ли?

    12.08.2009: попробовал -- плавное изменение работает!!!

    Теперь надо разбираться сдеталями:

    • Как обходиться с каналами OUT_n во время самого плавного изменения?
    • DoSoftReset() -- в каких случаях и как её вызывать, и как она должна взаимодействовать с SendMULTICHAN() (или прочими, в зависимости от режима)?
    • По каналу RESET -- надо останавливать и всю работу: плавное изменение, осциллографичности, ...

    31.08.2009: Перевел xcac208_drv.c на принцип "указатель на приватную структуру называется me" (как в CXv4).

    Далее, принято соглашение, что запросы с ninfo==0 -- это "чтение" (также принудительно запись игнорируется при активированной или запущенной таблице). Вообще, нынешняя схема работы больших каналов СОВЕРШЕННО НЕАДЕКВАТНА для таблиц:

    1. Сама "временная диаграмма" -- постоянное вычитывание, а для табличных каналов вычитывание нужно только:
      1. При запуске программы.
      2. При модификации другой программой.
      3. При записи таблицы (тестовое чтение).
      4. При сбросе таблицы.
      Последние 3 требования были бы удовлетворены механизмом мониторирования.
    2. Во-вторых, в cda сейчас напрочь отсутствует поддержка ОТПРАВЛЯЕМЫХ ДАННЫХ.

    02.09.2009: за прошедшие несколько дней собственно bigc_p() допилена. Она принимает/возвращает данные больших каналов в удобном-для-клиента виде, сами же данные хранятся в виде общей таблицы в privrec'е. Принимает она данные в локальные буфера, и лишь убедившись, что они корректны, копирует в privrec.

    Обработка большей части DO_TABLE_* также сделана -- она несложна: эти команды шлют нужные пакеты и меняют "состояние" при помощи SetTmode(), каковая также уставляет значения (0/2/3) в каналах, связанных с этими командами.

    Теперь осталось: во-первых, собственно ACTIVATE, а во-вторых -- реакция на табличные пакеты в _in().

    06.09.2009: да, предварительную проверку провёл -- по минимуму функционирует. Детали:

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

      Но это было только для тестов, а реально загрузку надо явно переделать -- чтобы оно насчитанные данные старалось грузить ПЛАВНО.

      Кроме того, надо сделать корректную реакцию/проверку по пакету "CLOSE" -- чтобы объем совпадал.

    • Команды START/STOP/PAUSE/RESUME работают -- хотя последние 3 и приходится эмулировать бродкастами.
    • А вот пакет 0xFD, присылаемый блоком для уведомления о завершении исполнения таблицы, сделан у Козака халтурно -- там по-прежнему горит битик 0x01 "EXECING" (да еще и 0x10 "CONT_RQ" почему-то).

    06.09.2009: вечером: сделал НОРМАЛЬНУЮ загрузку таблицы, любого размера.

    Формулировка проблемы:

    1. То, какое количество пакетов можно отправить в интерфейс -- неясно.
    2. С какой скоростью блок сможет переваривать -- тоже неясно (на 125Кбит успеет, а на 1Мбит -- нет).

    Введен слегка жульнический способ регулирования нагрузки: после каждого пакета 0xF4 отправляется также и 0xFE (GETDEVSTAT), и все эти пакеты шлются не напрямую, а через очередь -- 0xFE обычным q_enqueue(), а 0xF4 -- q_enq_ons().

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

    • Естественно, размер кольцевого буфера, заказываемого при add(), увеличен на (макс.размер.таблицы+6)/7*2.
    • За компанию и отправка пакетов записи в ЦАП переведена с send_frame() на q_enq_ons() -- ведь за ней всегда следует обычное чтение.
    • Таблица из 918 байт в тесте (Kontron CP6000, TEWS TPMC810) ушла за 359 миллисекунд (возможно, ситуацию подтормозил fsync()'нутый логгинг).

    Всё работает очень надёжно, но проблема может возникнуть при одновременной загрузке таблиц в НЕСКОЛЬКО блоков -- она ведь пойдет независимо, и выходной буфер чипа всё равно может переполниться. Видимо, придется в layer'е/HAL'е предусматривать какую-то проверку готовности к отправке (уж реакцию на ошибку при забитом буфере -- точно).

    02.11.2009: переделываем карту каналов под стандарт v4:

    • Вначале 20 управляющих каналов, потом 30 -- под регистры (там есть место и под "маску прерываний"), а уж за ними каналы АЦП и ЦАП.
    • Порядок (номера) управляющих каналов теперь точно такие же.
    • А вот порядок регистровых каналов пока старый -- поскольку он обеспечивается cankoz_pre_lyr'ом, и в v4 эта под-карта совсем другая.
    • Имена/названия сменены -- "TIME" стал "TIMECODE", а "MAGN" превратился в "GAIN" (включая и все внутренние переменные/константы и имя auxinfo-параметра).
    • "CS" (ChangeSpeed) заменено на "RATE".
  • 05.09.2009: надо ввести правило: времена для козачиных таблиц указываются в МИКРОсекундах.

    Резоны:

    1. У большинства блоков размер временнОго кванта -- 10 миллисекунд, и поэтому первоначально в качестве единиц были выбраны МИЛЛИсекунды.
    2. Но у CEAC121 квант -- 100 микросекунд, а его б надо поддерживать унифицированно с остальными.
    3. Если взять МИКРОсекунды, то всё укладывается, т.к.: число шагов на ступень у всех блоков 16-битное, что даёт 65535/100=655 секунд у большинства, и 65535/10000=6.5 секунд у CEAC121; в знаковое же 32-битное целое упхать до 2^31~=2*10^9микросекунд=2000секунд. (А 100-миллисекундные шаги вряд ли у какого блока будут, т.к. их -- всего 10Hz -- уже вполне можно делать и программно.)

    02.11.2009: да, ввел XCAC208_USECS_IN_STEP=10000 и позаменял все пересчетные 10-ки на него. Пока, правда, не проверял.

  • 02.11.2009: создаем второй драйвер -- CEAC124, xceac124_src.c. Поскольку CEAC124 -- это "половинка" CAC208/CEAC208, то и драйвер сделан практически копированием и контекстной заменой.

    02.11.2009: за компанию сменен подход к функциям-конвертерам:

    1. Преобразование для 24-битных АЦП делается единой функцией kozadc_decode_le24(), по указанному куску байт из пакета и gain-фактору отдающей code и val, а возвращающей rflags -- это взамен парочки cac208_24_to_code()+cac208_code24_to_val() с сопутствующей логикой в самой _in().
    2. Функции преобразования для 16-битных АЦП переименованы в нейтральные относительно типа устройства 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 oscilloscope drivers
  • 15.04.2009: сюда будем записывать фичи, которые хочется иметь в следующих поколениях осциллографоподобных драйверов. В том числе -- чтобы обеспечивать унифицированный интерфейс у всех таких девайсов.

    (Этот раздел расположен тут, а не двумя разделами ниже, как по хронологии, чтобы быть поближе к разделу "3rd generation CAN drivers".)

    15.04.2009: собственно общий список:

    • Режим "самописец"/"время назад".
    • Страничный режим у adc200/adc200me.
    • Стандартизовать параметр "сколько прошло с начала измерения".
  • 15.04.2009: ВСЕМ осциллографоподобным, чье железо имеет режим "мерять в кольцевой буфер до стопа", стоит иметь поддержку этого на уровне драйверов.

    15.04.2009: отдельный вопрос -- как этим УПРАВЛЯТЬ (какими параметрами включать такой режим, как указывать его свойства), и как сочетать его с обычной работой. Смахивает на проблему "как реализовать осциллографический режим у CAN-АЦП".

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

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

    • Старостенко: у нас на комплексе это не нужно никогда, а теоретически может быть полезно в случае всяких внешних блокировок, чтобы именно сигнал блокировки работал стоповым импульсом, и можно было бы посмотреть осциллограмму за время, ПРЕДШЕСТВУЮЩЕЕ блокировке.
    • Карнаев: на ВЭПП-3 это используется в одном месте -- для контроля за ВЧ-генератором. Время от времени пучок у них сбрасывается, и есть девайс, следящий за ним и при проблемах генерящий сигнал, по которому ADC101* стопится и потом Одренком вычитывается. Т.е., то же самое -- срабатывание по непредсказуемому внешнему сигналу.
    • Козак: Собственно "время назад" предназначено для расследования аварий. Для старых АЦП это было при разбирательствах с пробоями на Б-4. После пробоя какой-то датчик давал СТОП, потом считывались картинки и по ним разбиралось что в какой последовательности сыпалось. В CANbus есть два применения- измерение пульсаций (осциллограммы)- это можно делать поочередно и программно, и отлавливание спорадических бросков. В последнем случае нужно одновременно писать несколько каналов и если они через 1 мсек, то никакая линия не пропустит всю информацию. Пока "время назад" еще нигде не требовалось.
    • Батраков прислал выдержку из какой-то статьи, где рассказывается о применении этого режима в ADC333 для тренировки и тестирования магнитных структур вигглера. Там постепенно задирается поле, пока где-то не произойдет срыв, а потом определяется, где и как он случился.

    16.04.2009: продолжение банкета:

    • Беркаев: внешний СТОП в CAMAC-АЦП у них не используется. Козачиные АЦП иногда работают со складыванием данных во внутренний буфер, это сделано исторически, а сейчас стараются переходить на высылку в линию сразу.

      П.Шатунов: не очень ясно, но вроде как практически нет. И вообще у них с CAN-АЦП сделано махарайчато, местами используется многоканальный режим, местами осциллографический, а если надо переключаться -- то работают двумя программами по очереди.

    Короче -- с одной стороны, оно пока не особо нужно, но сделать по большому счету несложно, т.к. ответ на вопрос "как этим УПРАВЛЯТЬ" очень прост: вводим еще один параметр -- "РЕЖИМ_САМОПИСЦА" (у nadc4 он будет =-1), на который чуток по-другому реагируют как сами драйверы, так и fastadc_common.

    • А все остальные параметры ведут себя точно так же (NUMPTS, PTSOFS, ...).
    • Отличие же -- просто драйвер по-другому вычитывает память.
    • SHOT работает как программный останов -- практически у всех АЦП NAF, делающий программный старт, в режиме "самописец" делает "останов измерений и готовность".
    • Ну и в GUI надо будет во-первых иметь ручку "режим самописца", а во-вторых -- уметь рисовать подписи к оси X "задом наперед".

    02.03.2010: еще поговорил с Батраковым, на тему «почему в ADC200ME нету бита "переполнение"/"счетчик хоть раз перешел через ноль"».

    1. Режим "самописец" -- это ВСЕГДА для разбора аварийных ситуаций. И там ВСЕГДА будет смотреть глазами именно человек (а не программа), и уж он разберется.
    2. В старых CAMAC'овских блоках такой битик был, но потом на него решили забить, просто за ненадобностью.
    3. Вероятность, что не наберется хотя бы одного полного круга -- практически нулевая. Ну еще бы -- с такими-то условиями использования (стоит и крутит часами/сутками, пока не взбрыкнёт авария).
    4. Практически всегда стоповый импульс приходит со специально добавленной задержкой -- чтобы видеть не только ПРЕДШЕСТВУЮЩЕЕ аварии, но и немножко сразу ПОСЛЕ неё. Поэтому на экране обычно момент аварии бывает где-то в серединке графика.
    5. Если уж хочется иметь железобетонный способ определять "неполное кольцо" -- то можно перед запуском измерений заполнять всю память каким-нибудь заведомо невозможным/маловероятным числом. Например, для ADC200ME -- в идеале с единицами в 8 старших битах, а коли туда не пишется -- то просто все единицы (это даст верхнюю границу диапазона, а с учётом, что на ОБОИХ каналах сразу -- гарантия почти железная).
  • 15.04.2009: а у некоторых -- adc200, adc200me -- есть еще СТРАНИЧНЫЙ режим, когда оно записывает N страниц по M измерений. ЭТО поддерживать будем? И если да, то как это будет управляться?

    15.04.2009: а главное -- КАК это будет поддерживаться в *fastadc_common.*? Оно-то рассчитано на режим "запустили, померялось -- получили LAM, прочитали, отдали".

    16.04.2009: дятел!!! Ведь LAM-то (или IRQ) возникнет после измерения ВСЕХ страниц, а не после каждой.

    А означенная "проблема" решается просто -- надо по LAM'у не безусловно считать "всё готово!", а вызывать некую функцию из драйвера, которая проверит (при надобности -- увеличит счетчик полученных страниц), и если она вернет 1 -- значит, готово; 0 -- не готово, ничего не делать, -1 -- ошибка, отвалить.

    06.06.2014: еще одна более хитрая потребность: мочь отдавать "наверх" осциллограмму не целиком, а вырезками.

    Конкретно на ЛИУ-2: есть 2 импульса, обмеряемые ADC200ME; они отстоят друг от дружки довольно далеко, и участок между ними совершенно не нужен. Вот вычитывать бы прямо из ОЗУ блока именно эти 2 кусочка...

    Как? Например, так:

    1. Завести еще пару параметров -- "NUMPTS второго сегмента" и "PTSOFS второго сегмента", и чтоб драйвер
      • программировал измерение не PTSOFS+NUMPTS, а PTSOFS+PTSOFS2+NUMPTS2,
      • наверх отдавал бы два участка, вычитанные из разных мест в ОЗУ, но склеенные вместе в один массив.

      (А "реальный NUMPTS первого сегмента" будет считаться равным NUMPTS-NUMPTS2.)

    2. pzframes/adc200me_data.c::adc200me_x2xs() посмотрит, что NUMPTS2!=0 и при x>=TRUE_NUMPTS1 прибавит к нему еще и PTSOFS2.

    ...ох и криво же это!!!

    • Ладно еще, что в adc200me_drv_i.h остался один-единственный незанятый параметр номер 16 -- карту можно изменить на 100-канальную в стиле fastadc_common.devtype.
    • Криво оно именно архитектурно. Жутко некрасиво.
    • Да и искусственное ограничение в 2 сегмента тоже страннО. А если больше 2 -- то это уже мега-навороты...

    10.08.2014@обдирание-обоев: а чё б 2-импульсный режим не делать страницами в adc200me?

    12.08.2014@утро: а то, что никто нам триггер перед вторым импульсом не даст. Ибо не предусмотрено.

    09.10.2014@Снежинск-каземат-11: в продолжение темы:

    • Вариант -- сделать специальный inserver-драйвер, наружу выглядящий как adc200me, но в реальности "паразитирующий" на настоящем драйвере железки, и отдающий "вырезку" данных оттуда. Останется лишь вопрос "как указывать сдвиг", но как раз под это можно задействовать PTSOFS.

      17.10.2014: дальнейшее обсуждение драйвера -- adcslice -- в его собственном разделе.

    • А в v4 действительно можно как-то выпендриваться с "каналами-отражениями".

      ...хотя, возможно, лучше подойдёт эта же модель -- например, из-за отсутствия ограничения на количество "сегментов-вырезок": тут они делаются сколько угодно, просто указанием в конфиге, а с "отражениями" придётся закладывать ограничение (да и вообще ПРЕДУСМАТРИВАТЬ, в драйвере; в то время как драйвер-паразит можно натравить на любой реальный драйвер).

    • Минус схемы с "драйверами-прилипалами" в том, что нет единой точки, могущей решать, какой результирующий NUMPTS выставить (максимум из требуемых PTSOFSn+NUMPTSn). Эту задачу придётся возложить на клиентскую программу -- впрочем, у неё и просто с отображением результатов забот выше крыши.
    • ...хотя в v4 можно, в принципе, перенаправить от всех "прилипал" запись {PTSOFS,NUMPTS} отдельному драйверу, который бы это помнил и передавал бы реальному драйверу должным образом подсчитанный максимум.

      Но оно так будет только расти -- значит, нужен еще канал "забудь".

    10.10.2014@Соболь-из-Толмачево-на-Большевистской: PTSOFS удобен тем, что и времена будут показываться правильно (при условии, конечно, что в реальном устройстве стоит PTSOFS=0).

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

    12.10.2014@Быстроном: экранные "дирижеры" надо делать контейнерами (подэлементами). Тогда у них будет возможность прилепляться к "дирижируемым" гарантированно уже после их создания.

  • 15.04.2009: также надо формализовать параметр "сколько времени прошло с момента запуска измерения" (т.е. -- сколько мы уже ждем). А то сейчас он в cm5307_fastadc_common.h реализован явочным порядком -- отдаётся при запросе любого параметра n>=NUM_PARAMS.

    09.06.2014: да, в pzframe_drv сделано -- param_elapsed.

  • 16.04.2009: может возникнуть потребность виртуально объединять несколько одинаковых блоков -- adc502 -- в группу, чтобы создавать виртуальный более многоканальный блок -- adc50v4. Конкретно -- для замены adc4 на пару adc502 (они больше умеют, в т.ч. "самописец" поддерживают).

    Но ведь cm5307_fastadc_common.h-то рассчитан на получение ОДНОГО сигнала LAM, а тут перед вычитыванием надо будет дождаться НЕСКОЛЬКИХ.

    P.S. А вот более общий "adc50vx" (с указанием числа блоков в конфиге) так просто не сделаешь -- там ведь всякие retdata/outbuf/... указываются статически... Ну да ладно -- можно сразу параметризовать, и потом "vN" генерить по мере надобности средствами препроцессора.

    16.04.2009: насчет "нескольких" LAM'ов -- всё тривиально, то же решение, что и для страничных измерений: функция в драйвере, которая return-числом скажет common-уровню, что все измерения готовы.

  • 16.04.2009: а еще -- у нас же cm5307_fastadc_common.h предназначен именно для CM5307, а в adc200me_drv.c придется воспроизводить/дублировать всю эту инфраструктуру.

    А надо -- после, пока продублировав-таки для опыта -- сделать некий "общий" кусок кода, который бы подходил как для cm5307-драйверов, так и для обычных. Ведь API у них почти одинаковый.

    02.03.2010: да, сделал для adc200me_drv.c (и будущего adc812me_drv.c) кусок cpci_fastadc_common.h.

    • С одной стороны, он очень похож на своего cm5307-родственника, и в значительной степени сделан просто копированием.
    • С другой же -- отличаются 2 ключевых аспекта:
      1. Среда исполнения -- в-серверная, полагающаяся на серверов основной цикл в плане таймаутов (параметр WAITTIME) и получения-IRQ-по-select.

        Но тут вопрос скорее к нынешнему cm5307_dbody.[ch] -- его недотягивающесть до API сервера уже отмечалась 12-01-2009.

      2. Интерфейс к железу -- на базе pci4624.[ch], что накладывает некоторый отпечаток и на сам _fastadc_common.h. (В основном это касается всё-таки init_b()/term_b() и способа получения IRQ.)

    03.03.2010: да, очень напрашивается в CXv4 сделать "dbody" прямо на основе cxscheduler/fdiolib -- пусть и на один дескриптор. Оно как бы некоторый overkill, но на "лишние накладные расходы" можно забить, зато будет унификация с canserver'ом. 11.08.2011: да, в CXv4 изначально так, и новый cm5307_sl_dbody тоже сделан так же.

    08.11.2011: поскольку

    1. в CM5307 вскорости LAM будет доставляться через select();
    2. появляется еще один fastadc-подобный драйвер -- для DIMEX
    то явно пора делать унифицированный xapi_fastadc_common.h, который бы и поддерживал семантику таких АЦП-подобных драйверов (в т.ч. все те соглашения типа "если в команде STOP присутствует бит DISABLE, то ничего наверх не отдавать, а молча рестартовать измерения").

    Предполагаемые фичи:

    • Тип API (v2/v4-alike-sl) будет определяться ifdef'ом в зависимости от некоего -D.
    • За основу возьмём cpci_fastadc_common.h, как наиболее современный.
    • Вероятно, потребуются как бы hook'и для специфической инициализации (типа ON_CLOSE). 11.11.2011: неа, НЕ потребуются.
    • ...отдельный прикол -- что надо будет это как-то подружить с РАЗНЫМИ подстилающими API: в cPCI -- pci4624 (pci4624_irq_proc), в cm5307 -- что еще вылезет...

    11.11.2011: назвать надо даже не "fastadc", а как-то со словом "frame" -- поскольку ровно эта же модель отлично подходит и для всяких камер.

    А нынешняя ДРАЙВЕРНАЯ модель "fastadc_common" -- это в первую очередь связка {cur_args,nxt_args}[NUM_PARAMS] плюс параметры SHOT, ISTART, WAITTIME, STOP; PTSOFS же и NUMPTS -- там не используются (присутствуют в закомментированном и давно ненужном отладочном коде).

    И еще, касательно общей архитектуры:

    • Никакие "hook'и" не потребуются, поскольку...
    • ...методы INIT_B() и TERM_B() вообще НЕ относятся к этому xapi_*_common-куску -- инициализация и завершение никак (кроме вызова InitParams()) не принадлежат к сей модели, а должны относиться к компетенции "прослойки для конкретной архитектуры".
    • Т.о., получается 3-слойная архитектура:
      1. Драйвер;
      2. адаптер для конкретного стандарта электроники (cpci, cm5307, bivme2, ...);
      3. xapi_*_common.h.

    Единственная "тонкость", могущая возникнуть -- это для не-локальной аппаратуры, типа CAN и DIMEX: поскольку там

    • Вычитывание параметров при инициализации -- совсем не мгновенное, и происходит "задним числом".
    • ReadMeasurements() -- тоже совсем не мгновенная, тут-же-завершающаяся операция.
    • Девайс не "всегда-есть", а может отключаться (обрыв кабеля, ...) и затем появляться вновь.

    Придётся проапгрейдить модель, введя 2 свойства:

    1. "Текущее состояние" -- в котором, помимо обычного "готов" и "меряем", уметь отражать продолжительно-ждущие состояния -- неготов, читаем измерения, выполняем stop, ...

      Следствие: драйверы, конечно же, НЕ будут иметь права самостоятельно уставлять DRVS_NOTREADY -- а только говоря об этом framework'у, который соответственно проапдейтит состояние, и уже сам вызовет SetBlockStatus().

    2. Операции. могущие вот так "зависнуть" -- AbortMeasurements() и ReadMeasurements() -- должны мочь возвращать результат "в процессе...".
      • Собственно, сейчас именно такая модель и используется для StartMeasurements().
      • И это сильно напоминает v4'шную модель "init_d: >0:operating, ==0:notready, <0:error/disconnect".
      • Осложнение лишь в том, что ReadMeasurements() уже СЕЙЧАС возвращает 0:restart,else:return.

    13.11.2011@душ: название можно взять "parframe" -- parametrized frame.

    09.06.2014: оно всё уже реализовано, в основном как описано выше (хотя и с изменениями касательно "зависнуть" -- не потребовалось), как pzframe_drv плюс *_fastadc_common.h.

API для быстрого/оперативного доступа к каналам
  • 30.03.2009: несколько дней назад Федя-jr. пришел с такой потребностью: ему надо сканировать пучком по сеточному датчику, в цикле, шагов по 50 каждым магнитом/корректором.

    А с циклом 1Гц одно-единственное сканирование растягивается на 50*2 секунд (*2 -- поскольку надо проконтролировать, что уставилось, да потом еще и померять). А весь цикл, по всем магнитам/корректорам, растянется тогда на час -- это совершенно неприемлемо; во-первых -- просто долго, а во-вторых -- за час сама установка может успеть "уплыть".

    Так что -- надо как-то мочь достигать частот намного выше 1Гц, а частоту цикла поднимать низзя, иначе совершенно незачем начнет жрать процессор linmag.

    Ну-с, и что делать, как можно решить имеющуюся проблему?

    30.03.2009: с пирометром-то решение оказалось в большом канале. Но вот тут -- ну ОЧЕНЬ не хочется вводить что-то в драйверы; тем более, что сами-то CAN-драйверы и так выжимают максимум того, на что способны устройства.

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

    А вот это будет включать в себя несколько аспектов:

    1. Введение интерфейса "callback'ов по получению значения" в сервер, и использование его (в cx-server_channels.c?).
    2. Соответствующее расширение cxlib'а.
    3. Ключевой аспект! -- а как это будет идти в протоколе? В частности -- делать это "синхронными" пакетами (тем самым запрещая одновременную "просто работу" с каналами), или же "асинхронными"?
    4. Поддержка в cda.

    API самих драйверов, естественно, затрагиваться НЕ будет.

    P.S. А вообще -- как попёрли-то недостатки/тупики: сначала вылезла проблема из-за только-целочисленности, теперь -- принудительная цикличность... Знак свыше? :-)

"Информационная система комплекса"
  • 08.04.2009: Федя-jr. попросил о такой вещи: чтобы если есть некая подсистема -- например, тренировщик, дегауссер, и т.п. -- то можно было бы узнать её текущее состояние: простаивает, работает, если работает, то в какой стадии, ...

    Вообще-то что-то подобное вроде есть на ВЭПП-4.

    Вопрос -- а как это сделать? Формально -- у нас же есть консольная команда "clients"; а что, если также ввести протокольное сообщение "уставить статус", и чтобы в cda имелся соответствующий вызов, который бы отправлял бы такое сообщение по ВСЕМ aux-соединениям такого-то sid'а...

  • 26.02.2013@совещание по каналу в мультовой ВЭПП-2000: опять зашла речь об отражении глобального состояния системы. На ВЭПП-2000 такое есть -- обычный сервер, только вместо реальной аппаратуры обслуживает некоторое количество "почтовых ящиков".

    А не маппировать ли такой информационный сервер везде на :0?

Отладочная печать, управляемая getenv()
  • 03.07.2009: еще один серьезный принцип касательно отладочной печати: надоело, что для её включения приходится рыться в коде, раскомментируя/комментируя обратно fprintf(stderr)'ы.

    Идея такая: пусть отладочная печать наличествует ВСЕГДА, но отрабатывает только если некая переменная окружения определена и имеет значение "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: насчет оперативного изменения параметров и возврата их в начальные состояния...

    10.07.2009: несколько исходных соображений:

    1. Во многих случаях надо при загрузке сервера сразу прописывать заренее определенные значения в некоторые каналы -- например, прескалер у CGVI. В настоящий же момент приходится уставлять значения руками.
    2. У CAN-АЦП давно напрашивается возможность менять настроечные параметры -- beg, end, magn -- оперативно. Т.е. -- их надо сделать каналами.
    3. В некоторых блоках параметров, управляющих режимом -- опухнуть сколько (DDS300...), и если где-то не то поставишь, то вряд ли сможешь вернуть на место. Так что надо бы иметь спец. канал [Reset].

    Что надо делать:

    1. Иметь возможность указывать значения для таких каналов в auxinfo. Если значение обязательно -- как в CAN-АЦП -- то надо делать default. Если же это именно режимный канал, который можно и "прочитать текущее значение при запуске", то выбирать default так, чтобы он пропускал уставку.
    2. У CAN-АЦП параметры beg, end, magn -- делать каналами, и по записи в них а) заменять текущие помнимые значения; б) тут же пере-посылать пакет "старт циклических измерений".
    3. Иметь дополнительный командный канал "RESET", в который если записывается некое специальное число, то выполняется уставка в настроечные каналы "начальных" значений. "Начальные" тут -- это те, что указаны в auxinfo.

      Технология очень простая -- иметь список пар (номер_канала,значение), и по "RESET"'у идти по этому списку и вызывать саму себя функцию записи.

      Следствие: надо ОТДЕЛЬНО помнить:

      1. текущие-уставленные значения настроечных каналов -- их использовать при высылке конфиг-пакета;
      2. указанные в auxinfo -- их никогда не трогать, а по "RESET"'у именно их использовать для сброса.

    Например, CAC208:

Модификации с целью приближения CXv2 к CXv4
  • 06.06.2012: модификация API драйверов для приближения к стандартам v4: шаг 1 -- имена/названия параметров и переменных.

    06.06.2012:

    1. "magicid" & Co.:
      • 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
      • Имевшийся в v2hw/drivers/serial/ "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: продолжаем:

    1. Название приватной структуры -- [*_]privrec_t.
    2. Название "self"-указателя -- me, вместо info, rec, ...
      • Прошерстены все драйверы.
      • lib/Knobs/:
        1. TYPE_privaterec_t->TYPE_privrec_t
        2. ..._privrec_t *info->*meinfo там осталось для call_data)
        3. Избавляемся от #define-аксессоров вида zzz(ki) в пользу прямого обращения me->field.
      • lib/Chl/:
        1. TYPE_privaterec_t->TYPE_privrec_t
        2. rec,info->me (кроме того, что касается диалоговых окон -- там вроде как не совсем объекты, ибо они единственные; но как минимум histplot в будущем радикально изменится, под множественность)
      • Индивидуальны xmclients/knobplugins/elemplugins по директориям.

    09.06.2012: далее:

    1. devptr вместо privptr.
      • cxsd_driver.h, remsrv_cxsd_driver.h
      • programs/server/: d_privptr->d_devptr
      • remsrv_drvmgr.c
      • cm5307_dbody.[ch]; cm5307_fastadc_common.h
      • (А вот remdrvlet_[ch].h не пришлось -- там всё изначально сделано. Как и в cpci_fastadc_common.h.)
      • cankoz_pre_lyr.[ch]
      • ТУЧА драйверов.
    2. privptr вместо usrptr/usrptr. МОРЕ мест.

    ВСЁ!

  • 23.07.2012: модификация API драйверов для приближения к стандартам v4: шаг 2 -- смена имён и списков параметров.

    24.07.2012: приступаем (пока в основном переименования):

    • Версии:
      • CXSRV_DRIVERREC_VERSION_MAJOR продвинута до 8.
      • 4cx'ная CXSD_DRIVER_MODREC_VERSION_MAJOR -- до 10.
      • REMDRVP_VERSION_MAJOR продвинута до 3.
      • 4cx'ная REMDRV_PROTO_VERSION_MAJOR -- до 4.
    • Удалена RegisterDriverTimeout():
      • И определение, и сама.
      • И d_timoutrec[].
      • В remsrv -- аналогично, только там поле devrec_t.trec осталось для подчистки semi-connected.
    • Никогда не использовавшиеся CxsrvDrvBigcCmpFunc() и CxsrvBlkBigcReprFunc() тоже выкинуты.
    • Переименовываем:
      • В именах per-device вызовов *Driver*->*Dev*: RegisterDriver*()/DeregisterDriver*() в RegisterDev*()/DeregisterDev*().
      • В типах CxsrvBlk*{Func,Proc} "Blk" заменено на "Dev".
      • ...а сам префикс Cxsrv -- на Cxsd.

        (Потребовались изменения и в паре Makefile'ов, т.к. таблицы драйверов для remsrv-серверов генерятся.)

      • Еще махинации с blk/block->dev, drv->mod:
        • Аналогично поля init_blk, term_blk стали init_dev, term_dev.
        • cx-server_dbase.c: 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.
        • Словцо "Block" превратилось в "Dev" везде, где касалось устройств (а не блокировки или блоков/кусков); case-insensitive.
      • ...за компанию некоторое украсивливание внутренностей cx-server'а:
        • CX_MAX_BIGCHANS стало CX_MAX_BIGCS (для унификации).
        • Четвёрка drivercallbacks[], driverwrcallbks[], driverexcallbks[], driverfd2devids[] превратилась в devfdcallbks[], devwrcallbks[], devexcallbks[], devfd2devids[].
      • Касательно DevStat*:
        • 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: продолжаем -- теперь больше по параметрам:

    • fd_p: устраняем правило, что если вёрнутое от init_b() число >0, то это файловый дескриптор.
      • Полагание на это убрано из нескольких драйверов, где оно еще оставалось, и заменено на явный RegisterDevFD().
      • Убрано и использование оного из cxsd_drvmgr.c::InitDev() и remsrv_drvmgr.c::ProcessPacket(), и переменная в них из dev_fd стала state.
      • Само "fd_io" удалено из:
        1. CxsdDriverRec.
        2. Определения DEFINE_DRIVER_REC().
        3. Использования DEFINE_DRIVER_REC() в драйверах.
    • Значения DEVSTATE_nnn, и их использование:
      • Сами значения сменены на -1/0/+1 в обоих файлах (cxsd_driver.h, remsrv_cxsd_driver.h).
      • Собственно реакция в *drvmgr*:
        • Кроме былой проверки на state<0 добавлено также:
          1. по ==DEVSTATE_NOTREADY делается SetDevState(,DEVSTATE_NOTREADY,0),
          2. иначе -- SetDevState(,DEVSTATE_OPERATING,0).
        • ...и вот тут вопрос -- не акунется ли такое где-нибудь, как уже было с DRVS_OFFLINE 21-03-2012...

          Ведь ПО-ХОРОШЕМУ -- по умолчанию устройство должно рождаться (т.е., ставиться ДО вызова 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):

    • Сервер и remsrv.
      1. Переставляем devptr 2-м параметром (после devid) во ВСЕХ методах.
      2. Убираем bid отовсюду, кроме CxsdDevInitFunc.
      3. FD:
        • Убираем fd отовсюду, кроме CxsdFDProc.
        • ...и еще к CxsdFDProc добавлен параметр mask -- на будущее, когда API "FD" будет на cxscheduler'е. Пока в нём отдаются константы 1, 2, 4.
        • Для устранения конфликтов с будущим API переименовываем RegisterDevFD() в RegisterDevRdFD().

    27.07.2012:

    • Общее для сервера/remsrv и драйвлетов: добавляем privptr к CxsdFDProc и CxsdToutProc.
      1. В последних драйвлетах -- remdrvlet_[ch].h-based cm5307_sl_dbody.[ch] и bivme2_dbody.[ch] -- оно уже было в полном объёме.
      2. А в СТАРОМ cm5307_dbody.[ch] хоть и были определены CxsdFDProc и CxsdToutProc, но отсутствовали Register*(), так что использоваться оно никак никем не могло.

        Поскольку всё равно этот и предыдущий пункты будут меняться на libremdrvlet -- то определения просто выкинуты, и вопрос закрыт.

      3. А вот в сервере/remsrv -- делаем.
        • Параметр privptr к методам добавлен.
        • К функциям RegisterDevTtFD() (кстати, в remsrv реализации WrFD не было, так что прототип убран) и RegisterDevTout*() -- тоже.
        • Модификация структур данных:
          1. Сервер: добавлено поле cxsd_devtinfo_t.privptr, массивы devfdprivptrs[], devwrprivptrs[], devexprivptrs[] (возможно, стоило бы сделать ОДИН массив, а не три, чтобы приблизиться к будущему sl-based API, ну да ладно...).
          2. remsrv_drvmgr.c: добавлено поле 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], как в сервере.

        • Ну и заиспользованы они в реализации API.

    30.07.2012: вносим изменения в CxsdDriverRec и DEFINE_DRIVER_REC(), для унификации их с v4'шными.

    Есть также мысль попробовать сделать "гибридную" схему, чтоб некий промежуточный вариант сервера (v2.5, v3?) мог грузить и использовать ОБА варианта драйверов. Т.е.:

    • В самом сервере чтоб внутренности были в стиле v4 (с cxdtype), но с дополнительным функционалом "больших каналов", существующим параллельно (как сейчас они существуют параллельно обычным каналам).
    • При загрузке драйвера сервер смотрит дополнительное поле "variant", и если оно говорит "v4", то используется нативный интерфейс (CxsdDevChanProc), если же "v2" -- то своя функция-адаптер, транслирующая v4'шный API в CxsdDevRWProc.
    • "Обратные" же интерфейсы -- возврата данных -- поддерживаются оба:

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

    Теперь собственно действия:

    1. Переход на cx_module.h:
      • Берем 4cx/'ный cx_module.h (БЕЗ cxldr'а, просто структуру).
      • cxsd_driver.h::CxsdDriverRec: устраняем поля magicnumber, version, name, init_mod, term_mod, заменяя их cx_module_rec_t mr в начале. И соответствующие исправления в коде сервера.

        И типы CxsdModInitFunc, CxsdModTermProc устранены.

      • remsrv_cxsd_driver.h -- аналогично. Правда, там никакие lib/rem/*.c править не пришлось -- ибо все эти поля никем не используются.
      • remdrvlet_h.h: excerpt из cx_module.h выкинут в пользу #include оного.
    2. Добавляем к DEFINE_*DRIVER* поле comment.
      • DEFINE_DRIVER_REC() в cxsd_driver.h и remsrv_cxsd_driver.h.
      • cm5307_dbody.h::DEFINE_DRIVER() -- добавлен неиспользуемый параметр.
      • cm5307_sl_dbody.h::DEFINE_DRIVER() -- добавлен параметр, передаваемый дальше в DEFINE_CXSD_DRIVER() (где он присутствовал изначально).
      • bivme2_dbody.h::DEFINE_DRIVER() -- аналогично. Только этот макрос нигде не используется, всё сразу на _CXSD-версии.
    3. Переименовываем CxsdDriverRec в CxsdDriverModRec.

    31.07.2012: продолжаем:

    1. Перетряска порядка и добавление нового: с содержимым CxsdDriverModRec и со списком параметров DEFINE_*DRIVER*() -- всё для приведения к более-менее единому виду:
      1. Серверный API:
        • Добавлено layerver в CxsdDriverModRec и в DEFINE_DRIVER_REC(). (В прочих DEFINE[_CXSD]_DRIVER() оно уже было.)
        • Древнее layerinfo переименовано в просто layer.
        • Параметры init_m, term_m перетащены в начало, сразу после name, comment.
        • Параметр privdsize и поле privdatasize переименованы в privrecsize и перенесен из середины в начало после {init,term}_m.
        • ...после него добавлены paramtable, min_businfo_n, max_businfo_n.
      2. Драйвлетные API:
        • DEFINE_DRIVER() (cm5307_dbody.h, cm5307_sl_dbody.h, bivme2_dbody.h):
          • min_busid_n, max_busid_n переименованы в min_businfo_n, max_businfo_n (но оно всё равно реально не используется).
          • Парочка init_m, term_m перенесена в начало, сразу после comment.
      3. ПОВСЕМЕСТНО -- в т.ч. в 4cx/ -- пара layer+layerver перенесена почти к началу, сразу за businfo_n (в v2-серверном -- из конца, в остальных -- из середины).

        В параметрах, плюс work/drivers/.

    2. Переименовываем сокращенные параметры DEFINE_*DRIVER*() -- init_m, term_m, init_d, term_d -- в init_mod, term_mod, init_dev, term_dev. Цель -- чтобы имена параметров были идентичны именам полей метрик.

    Всё, с данного момента у нас есть всего несколько вроде разных

    • DEFINE_*DRIVER*():
      1. Серверный DEFINE_DRIVER_REC() и ставший ему идентичным драйвлетный DEFINE_DRIVER().
      2. v4'шный DEFINE_CXSD_DRIVER(), отличающийся специфичными для v4 фишками:
        1. Вместо двух таблиц main_nsegs:main_info и bigc_nsegs:bigc_info плюс методов rdwr_p и bigc_p, есть таблица chan_nsegs:chan_info и список chan_namespace.
        2. Также добавлена таблица команд cmd_table.
      3. Драйвлетный (_sl-версий) DEFINE_CXSD_DRIVER(), почти идентичный предыдущему, но с добавлением bigc_p.
    • ...и CxsdDriverModRec()'ов:
      1. Серверный (и идентичный remsrv'шный).
      2. v4'шный, также отличающийся определениями касательно каналов (вместо обычные+большие -- "вообще") плюс наличием cmd_table.
      3. _sl-драйвлетный, вообще не содержащий таблиц определения каналов, а еще -- отсутствует cmdtable. Зато есть do_big.

    Теперь задача -- постараться как-то это всё максимально сблизить.

    01.08.2012: вариантов сближения просматривается 2:

    1. Сделать ОДНУ структуру, с доп. полем "variant", в которой будут ВСЕ поля, просто часть из них будет неиспользована для v2-варианта (v4'шные таблица каналов и do_rw), а другая -- для v4-варианта (v2'шные таблицы каналов и do_rw+do_big).
    2. Сделать РАЗНЫЕ структуры, у которых начала совпадают, и, в зависимости от mr.version трактовать их по-разному (v2=8, v4=10).

      При этом в рамках одной версии (v2) структуры всё же должны совпадать.

    Пожалуй, стоит выбрать 2-й подход -- он хоть и потенциально менее-всеподходящий, но зато проще в реализации и в будущем, при полном переходе на v4, позволит полностью избавиться от старого наследия, а не тянуть его до второго пришествия.

    Поехали:

    • Переименовываем DEFINE_DRIVER_REC() в DEFINE_DRIVER().
    • Убираем DEFINE_CXSD_DRIVER() из cm5307_sl_dbody.h и bivme2_dbody.h, переводя функционал в обычный DEFINE_DRIVER().

      Ибо -- пусть уж v2'шные будут сами собой, не пытаясь выглядеть v4'шными.

    • remdrvlet_h.h: унифицируем с обычным серверным:
      1. v4-подобные имена CxsdDevChanProc и CxsdDevBigcProc сменены на CxsdDevRWProc и CxsdDevBigProc.
      2. В CxsdDriverModRec добавлены поля таблиц {main,bigc}_{nsegs,info}. (Они реально никем не используются (и не могут), но зато теперь всё совпадает.)

    02.08.2012: переход от единственного busid/bid к businfo[]+businfocount (это, пожалуй, самое заморочное из всех изменений -- оно затрагивает очень много уровней).

    1. Первым делом меняем порядок "businfo[],businfocount" на более естественный (количество,данные) и приближённый к argc,argv[] -- "businfocount,businfo[]". Заодно, кстати, "*businfo" сменен на более адекватный "businfo[]".
      • 4cx/: очень немного, только прототип CxsdDevInitFunc, пара драйверов да вызов из cxsd_hw.c; плюс в drivers/can/src/.
      • drivers/cm5307/common/ -- море файлов. 20.08.2012: была халтура: i_cm5307.c::do_init() брал businfocount[{1,2}] вместо {0,1}. Драйвер SIGSEGV'ился при обращении к N=0.
      • remdrvlet_[ch].h.
      • Собственно драйверы
    2. Сервер:
      • cx-server_data.h::d_busid[] заменён на пару d_businfocount[] и d_businfo[][].
      • cx-server_dbase.c: переделаны AddDev(), и парсинг в SimulateDatabase(). 06.08.2012: была халтура. Исправлено.
      • cx-server_execcmd.c: выдача businfo[businfocount] -- копия из AddDev().
      • cxsd_drvmgr.c -- вызов init_dev(), d_businfo[0].

    04.08.2012: продолжение -- меняем уже собственно API "DevInit".

    1. Изменено в cxsd_driver.h и remsrv_cxsd_driver.h.

      Заодно -- сейчас проще всего -- добавлен параметр typename, для унификации с v4.

      05.08.2012: немного поразмыслив, выкинул его нафиг. Нужен он лишь нескольким драйверам, остальным же лишь захламляет список параметров. Желающие (реально -- только remdrv) смогут добыть искомое через свежевведённую cxsd_driver.c::GetDevTypename().

    2. Смена в cxsd_drvmgr.c -- тривиальна, всё уже было подготовлено. В качестве typename передаётся devname из blklist'а.
    3. В драйверах:
      • Самое противное было с serial -- разбираться, где там port_id, а где unit_id (они там перепутаны, т.к. исторически сначала они всегда работали с /dev/ttyS0; а в nkshd485 -- уже исправлены (копия из CAN)).
      • В остальном -- всё довольно просто, тупая работа, за исключением...
      • ...remdrv и прочего, связанного с ним:
        1. remdrv_proto.h: протокол изменён по схеме из v4 -- в фиксированной части пакета указывается businfocount (на месте былого busid, чтоб сохранить совместимую структуру), а ПОСЛЕ заголовка (с data[]) -- сначала идёт businfo[], а после него auxinfo.
        2. remdrv_drv.c: да, переделано.

          Выглядит кривовато, можно б оптимизировать -- прямо в init_d() после privrec'а сразу отводить блок businfo[]+auxinfo, там всё подготавливать, и в запуске драйвлета уже сразу слать этот готовый блок. Ну да фиг с ним -- в 4cx/ всё равно код уже другой; но, может, там потом поменяем.

        3. "Другая сторона":
          • remsrv_drvmgr.c: вставлена расшифровка. Изменился способ добычи auxinfo.
          • cm5307_dbody.c: скопировано из предыдущего пункта, с минимальной адаптацией.
          • cm5307_drvletbody.c: а тут скопировано уже из предыдущего.

            Заодно обновлён интерфейс cm5307d_init_f

          • ...что повлияло на i_cm5307.c.
          • remdrvlet_c.h: сюда также взято из dbody.

            И все танцы с REMDRVLET_C_H_BUSINFO_MODE устранены (и года не прожили :-)).

          • Кстати, поскольку тип businfo[] в API у нас указан как int, а в remdrv_proto -- int32, то в/из пакета делается не memcpy(), а именно поэлементное копирование. Хотя и была мыслишка и в API постулировать int32...

    Уф, теперь -- всё!!! Самая муторная и критичная фаза закончена.

    Дальше надо проверять, а затем начинать пользоваться новыми фичами и двигать реализацию API. Конкретно -- изготавливать libremdrvlet, libpzframe, sl-based cxlib, новое ядро сервера.

    04.08.2012: заюзываем CxsdDriverModRec.paramtable.

    1. Вставлен psp_parse() в:
      • cxsd_drvmgr.c::InitDev().
      • remsrv_drvmgr.c::ProcessPacket().
      • remdrvlet_c.h::ProcessPacket().
      • ...и даже в 4cx/'ный cxsd_hw.c::InitDevice().
      • А в cm5307_dbody.c::HandleDriveletInput() -- нет, поскольку там архитектура другая (БЕЗ метрики) и ему всё равно жить осталось недолго.
    2. В самих драйверах ручной парсинг убран в пользу указания таблицы в метрике.

      Но не во всех -- в нескольких, старых (типа *kshd485 и impacis10), оно оставлено, чтоб не возиться.

    3. И, кстати, изо всех драйверов выкинут #include "paramstr_parser.h", ставший излишним.

    06.08.2012: кстати, было забыто psp_free()'ние. Добавлено (в серверные варианты, в драйвлетных удаления устройства нету).

    Также добавлена проверка (во ВСЕ, кроме dbody), что заданный businfocount попадает в указанный в метрике диапазон [min,max], и если нет -- то автоотлуп.

    Оставался один недоправленый API: SetDevState() -- отсутствовал v4'шный параметр "строка объяснения". Добавлен. Но пока вообще никак не используется, да и по протоколу удалённых драйверов не передаётся.

    09.08.2012: еще некоторые трепыхания по приближению:

    • paramstr_parser.c перетащен в lib/misc/.
    • Из 4cx/ вселён cxldr (в основном на будущее).
    • Выкидываем из сервера всё, связанное со старыми "layer'ами" (которые всё равно неработоспособны).
    • Заиспользовываем privptr в fd_p():
      • pci4626.c::pci4624_irq_fd_p() -- убран поиск нужного handle сравнением по всем.
      • cankoz_pre_lyr.c::cankoz_fd_p() выкинута fd2line().
      • nkshd485_drv_common.c::piv485_fd_p() -- аналогично (ибо копия).
    • Радикально переделана схема с "разными" cxsd_driver.h: вместо алхимии с TRUE_CXSD_DRIVER_H, когда подменялся ВЕСЬ файл, теперь может быть указан символ 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: еще приближение к стандартам v4: общее -- имена некоторых .h-файлов и местоположение флагов.

    23.07.2012(реально 22.07.2012, ближе к полуночи, из дому): часть 1 -- файлы:

    • cx.h переименован в cxlib.h.
    • cx_types.h переименован в cx.h. Это пришлось отразить и в mkclient.sh.
    • Содержимое cxdtype.h перенесено в cx.h.
    • Сам cxdtype.h удалён (прожил недолго -- с 01.04.2011).

    23.07.2012: часть 2 -- флаги:

    • В младшей группе (серверные) блок 10..15 заменён на оный из v4.
    • CXRF_SERVER_HWERR_MASK тоже скопирована, теперь это перечисление флагов, а не байт.
    • Добавлен блок CXCF_ (16..31).

      Там существенное отличие -- положение WEIRD в группе CDR (CXCF_FLAG_COLOR_MASK), а не CDA.

    • Содержимое cxlib.c::_cx_rflagslist[] также заменено и теперь содержит все 32 бита.

    23.07.2012: добиваем.

    • При вышеописанном переходе возник конфликтец: поскольку каждый более высокий уровень в полученных от предыдущего оставляет только "тогойные" флаги, срезая свои, то при расположении COLOR_WEIRD в Cdr'ной группе он бы перестал работать вовсе -- поскольку выставиться может только cda'шной формулой.

      Посему -- проведена перетряска, исходно в 4cx/ -- ...

    • ...CXCF_FLAG_COLOR_WEIRD переведён в группу cda.

      (Первоначально была мысль просто переклассифицировать его в ту группу/маску, физически оставив на месте (ну и ладно, что группы стали б не-непрерывными). Но потом это показалось некрасивым. Минус же нынешнего решения -- что уже группа COLOR_MASK стала прерывной; ну и фиг с ней.)

    • Самое глобальное -- избавляемся от CDA_FLAG_* и CDR_FLAG_*, переводя весь код на CXCF_FLAG_*.
      1. Удалены сами определения в cda.h и Cdr.h.
      2. Удалены функции стрингификации флагов.
      3. ...единственным их использованием оказалось cda_strrflag_long() в Chl_knobprops.c; заменено на cx_strrflag_long().
      4. И ссылки на все CD[AR]_FLAG_ заменены на CXCF_FLAG_.
    • В связи с переездом FLAG_DEFUNCT в блок cda и его уставка (плюс сброс) также переехали в cda.c::DecodeData().

      ...Интересно, не вылезет ли это где-нибудь? В simple-Knobs всё равно пока не применяется.

      22.08.2012: ну да -- вылезло :-): оказывается, перестало посиневать по неприходу данных от сервера и обрыву связи. Пофиксено.

      30.08.2012@Снежинск-каземат-11: еще одно вылезло: на серверах с cyclesize<1000000 каналы стали посиневать при мелком возрасте -- -b200000 (5Гц) давало посинение при 1. Тривиально -- оно сравнивало еще НЕмасштабированный возраст. Исправлено перетаскиванием условия погусения в точку после масштабирования.

    • Заодно уж в cx-starter добавлена поддержка WEIRD.

    09.08.2012: поскольку всё собирается, то можно было вообще сразу ставить "done".

  • 04.08.2012: битым текстом -- что надо делать для адаптации старых исходников драйверов к свежемодифицированной системе:
    • Повсеместно заменить #include с "cx_types.h" на "cx.h" (для клиентов -- не забыв предварительно сменить "cx.h" на "cxlib.h").
    • Внимательно поменять содержимое DEFINE_DRIVER_REC() и DEFINE_CXSD_DRIVER() на DEFINE_DRIVER() -- проще это сделать "с нуля", чем перестановкой.
    • Сменить прототипы драйверных методов -- тоже проще скопировать из cxsd_driver.h, чем править.
    • Основное -- обратить внимание на параметры метода init_dev(), где вместо bid'а теперь businfocount+businfo[].
    • Теперь можно не psp_parse()'ить auxinfo вручную, а указывать в метрике таблицу, и среда исполнения выполнит парсинг сама.
    • У таймаутов и fd-proc'ев доабвлен privptr, могущий оказаться полезным.
    • Ну и названия некоторых API-функций изменились, но это надо смотреть на warning'и, да и просто драйвер не загрузится если чё.
  • 13.08.2012: всякости-мелочи задним числом.

    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 "DevFD" -- чтоб fd использовались только при регистрации, а уж дальше чтоб адресация была по внутренним дескрипторам ("handle" -- аналогично tid'ам, io-handle, и работе собственно cxscheduler'а (в обновлённом его варианте)).

    04.01.2013: итак, обновляем файловый API драйверов: добавляем к CxsdFDProc параметр fdhandle -- ПЕРЕД fd.

    1. v2'шная CXSRV_DRIVERREC_VERSION_MINOR продвинута до 1 (теперь 8.1).
    2. Добавлен сам параметр в оба cxsd_driver.h.
    3. И во всех драйверах тоже.
    4. В вызовы fd-proc'ев в средах исполнения:
      • v2: cx-server.c, remsrv_drvmgr.c;
      • v4: cxsd_driver.c.

      Пока в качестве "fdhandle" передаётся просто сам fd.

    06.01.2013: продолжаем:

    1. В операциях с дескриптором -- дерегистрация, смена маски -- параметр переименован из "fd" в "fdhandle".
    2. А функции регистрации теперь этот дескриптор возвращают (и пока тоже просто fd).
    3. Операции с дескриптором в драйверах (дерегистрация, смена маски) теперь отдают именно fdhandle (который им приходится хранить).
    4. Для большей читабельности введены типы cxsd_fdh_t, cxsd_tid_t, cxsd_fio_t (а то уже в глазах рябило от разных handle'ов).
    5. 07.01.2013: И всё переведено на них.

      (Ох и маятная же была работка! Кстати, для упрощения собирабельности пришлось эту троицу 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".

YEARS:
  • 06.01.2013: микро-раздельчик для общих результатов, достигнутых за год.
  • 2012:
    • Переход на новую систему сборки (GeneralRules.mk от v4).
    • Переход на новый, унифицированный API драйверов (также от v4).
    • Внедрение SLOTARRAY (стало ключевым для следующих пунктов).
    • Переход на новый cxlib -- select/cxscheduler-based (без-SIGIO!).
    • Обновление cxscheduler'а -- избавление от trec'ов в пользу tid'ов; адресация и fd по присваиваемым fdh'ам.
    • Создание pzframe_drv и перевод на него многих драйверов (хотя zcpci и camac заиспользовались лишь летом 2013).
    • Добавление в sendqlib реально работающей поддержки "портов".

    Следующими кандидатами на пути к v4 выглядят однопроцессный сервер (на унифицированной lib/srv/) и Knobs/Cdr/datatree.

  • 2013:
    • Обновление API cxscheduler'а и fdiolib'а -- добавление uniq и privptr1 (ключевое для следующих пунктов).
    • Однопроцессный сервер -- zzz/cxsd.
    • Перевод таймаутов/дескрипторов с API RegisterDev*() на cxscheduler, через uniq.
    • Внедрение libremdrvlet и обновлённого libremsrv, оба на общей базе remcxsd.
    • Создание ppf4td.
    • Все парсеры файлов в 4cx/ переведены на ppf4td, а старый код удалён.
  • 2014:
    • Клиентская часть pzframe -- pzframe_{data,gui,knobplugin}. На нём сделана новая версия fastadc, а также vcamimg.
    • Отлов скачков времени вперёд и назад.
    • Редактирование диапазонов в самом окне KnobProps, без под-окон.
    • CXv4:
      • Концепция "контекстов" вместо primary-серверов, переход на адресацию исключительно по каналам, со скрытием sid'ов от клиентов.
      • Timestamp'ы -- разработка концепции и внедрение.
      • Реализация cda_core на основе контекстов.
      • cda_d_v2cx.c и обкатка cda+datatree+Cdr+Knobs на нём.
      • Реализация ядра виртуальной машины формул в cda_f_fla.c -- по функционалу уже сейчас превосходит v2'шную.
      • Наполнение cxsd_hw функционалом работы с каналами (включая самое сложное, реализующее "мозги").
      • cda_d_insrv.c и обкатка на нём модели взаимодействия клиент-сервер.
      • Реализация преобразования типов (в 4 точках, "DATACONV").
      • Реализация основы CX-протокола версии 4 (cxlib_client, cxsd_fe_cx).
  • 2015
    • Базовый функционал CXv4:
      • Реализован обмен данными по протоколу CXv4 -- запинаны cxlib, cxsd_fe_cx, cda_d_cx.
      • Пересмотрена планировавшаяся концепция работы с "параметризованными" каналами (в сторону упрощения) и сделана пара pzframe-драйверов.
      • Консольные утилиты: cdaclient, das-experiment и dataset-save доведены до широкофункционального состояния, на основе общей библиотеки.
      • Реализована система имён: 1) просто имена каналов в устройствах (devtype и channels); 2) виртуальные каналы -- cpoints; 3) каналы состояния _devstate*.
    • Обвязка, необходимая для реального использования:
      • remdrv доведён до рабочего состояния, дореализован протокол remdrv_proto, сделаны и запущены limremdrvlet для CM5307-PPC и libremsrv для CANGW и MOXA.
      • Портированы драйверы из v2 -- большая часть CAMAC'овских, основные CAN'овские (БЕЗ поддержки таблиц ЦАПов...).
      • vdev и драйверы на нём (ist_cdac20, v1k_cdac20, v3h_a40d16).
      • Сделан histplot -- в виде общей библиотеки, на которой и окошко по Shift+MB3, и knobplugin'чик, и отдельная программа histplot.
      • Ре-имплементированы server-leds -- по другой архитектуре, с обновлением на основе событий и умеющие расти по мере добавления серверов.
      • Модульный cx-starter -- пока неидеально модульный, но с возможностью в будущем пересчитывать конфиг на ходу.
    • СУ ВЭПП-5 наполовину переведена на v4 (магнитные системы линака и накопителя, вакуум, термостабилизация, стюфиные пикапы). На v2 остались векторные (fastadc, ottcam), ИПП и всякие селекторы.
  • 2016:
    • Поддержка вариабельных каналов (CXDTYPE_UNKNOWN) в cxsd и cda (cda только на чтение -- в cda_snd_ref_data() явный запрет (а в cda_process_ref() нету!)).
    • Сделан v4-стек pzframe (ре-имплементированы fastadc и vcamimg, добавлен wirebpm).
    • Система управления на пульту ВЭПП-5 полностью переведена на v4.
    • В т.ч. все лебединости реализованы драйверами.
    • ВЭПП-4'шные (ГИД-25 в первую очередь) тоже переведены на v4.
    • Сделаны и проверены все v4'шные драйверы, необходимые для ЛИУ-2.
    • Ре-реализованы тулбары и сохранение/чтение режимов, в виде по-строчных текстовых файлов, совместимо с cdaclient.
    • ---почти--- конструктор из кубиков-модулей.
    • Написан изрядный кусок документации.
    • Не-застреливание CAN-драйверов при получении 0xFF с неверным кодом устройства, а ожидание появления верного.
  • 2017:
    • Уволена концепция CX_TIME_SEC_TRUSTED.
    • Задеплоен logrotate логов v4'шного cxsd.
    • Добавления в тулбар: BANNER и LOGGIA.
    • Решена проблема бесконечнорастущих буферов сервера/fdiolib (и тормозов при их опустошении).
    • "Кинетическое" плавное изменение в CAN-ЦАП -- advdac_slowmo_kinetic_meat.h (неидеально, косячновата арифметика завершения, но приемлемо).
    • Поддержка таблиц в козачиных ЦАПостях -- advdac_cankoz_table_meat.h.
    • Поддержка VME (пока bivme2) в hw4cx/ (на примере ADC4X250, который сам неидеален, но инфраструктура готова).
    • Внедрена поддержка гроханья всех ресурсов в cda.
    • UDP-резолвинг.
  • 2018:
    • "Умная" буферизация записи в cda: при отправке {r,d}-конверсия теперь откладывается до получения калибровок.
    • Инфраструктура histplot стала полной: a) утилита теперь умеет читать сохранённые данные из файла, ей можно указывать диапазоны и метки; b) компонент MotifKnobs_histplot оные данные в сохранённые файлы добавляет; c) das-experiment теперь пишет в том же формате.
    • Внедрение нового типа узлов -- DATAKNOB_VECT.
    • На ВЭПП-5 весь CAN переведён с CANGW на CAN-сервера (socketcan); сначала через sktcanserver.
    • На ВЭПП-5 весь гусиный EPICS-софт (клистроны, rfsyn, впуск/выпуск, задающий генератор, конверсионная система) заменён на CX'ный.
    • Мелкий улучшайзинг для билдеров интерфейсов: cda научена отдавать информацию о "нативном" (серверовом) типе канала
    • Возможность передачи аппаратных диапазонов -- от драйверов до клиентов: SetChanRange()...cda_range_of_ref().
    • В fdiolib добавлено гибкости: "плагины" -- FDIO_STREAM_PLUG для бинарных данных, плюс возможность просить STRING-потоки прочитать блок бинарных данных -- fdio_string_req_binary().
    • Фичи для отлаживаемости драйверов:
      1. Возможность управлять состоянием драйвера через запись в ._devstate (-1, 0, +1 -- отключение, рестарт, включение).
      2. Возможность по-устройственно включать симуляцию, указанием '-' перед именем драйвера.
      3. Управление logmask: возможность указания в -l, в опциях (:log=mask), динамическое через канал _logmask.

      Консольный интерфейс после пунктов 1 и 3 практически не нужен.

    • В fastadc добавлена фича "сохранить текущее измерение как хорошее и показывать его серым цветом".
  • 2019:

    Много работы со сторонними СУ (EPICS, Tango) и с разной электроникой (NI DAQmx, VME):

    • Решена "проблема RESCALE_VALUE()", когда нулевые значения на графиках рисовались на 1 пиксел выше нулевой оси.
    • В cxsd_hw "массивы" cxsd_hw_{layers,devices,channels}[] переведены с реально статических массивов на аллокирование.
    • ?Запинана поддержка автоматического запуска и останова CX-серверов под systemd?
    • (Семёновская сварка переведена на v4)
    • Научились работать с NI DAQmx, на примере PXI6363 даже сделан прототип общего модуля (будущего daqmx_lyr).
    • cda_d_epics.
    • cxdtype_t переведён с 8 на 32 бита.
    • Введена динамическая загрузка dat-плагинов.
    • Консольная утилита pipe2cda.
    • Много для VME:
      • Новая архитектура для VME -- на layer'ах.
      • Поддержка динамической загрузки драйверов в libremsrv.
      • В BIVME2'шный драйвер vmei добавлена буферизация векторов (теперь может поддерживать несколько блоков на IRQ).
    • Возможность указывать "свойства" каналов (R,D, строки, диапазон) прямо в devtype.
    • Симуляция и noop_drv теперь учитывают диапазоны при записи.
  • 2020:
    • Поддержка CAEN A3818.
    • Векторные операции в libvmedirect -- libvmedirect_vect_io.h.
    • bridge_drv.c
    • Возможность указывать в devlist'ах по-канальные свойства dbprops и drvinfo.
    • Оптимизация при обновлении ручек: теперь на экране обновляются только при изменении значений.
    • Сделана совместимость между INT и TEXT совпадающего размера.
    • img878 - возможность видеостриминга.
    • Управление доступом в сервере - cxsd_access.
    • ppf4td_get_double(), умеющий парсить выражения (по алгоритму Дийкстры (сортировочной станции)).

      И Cdr_via_ppf4td c cxsd_db_via_ppf4td на неё переведены.

    • Локинг каналов (пока простая версия, без гарантий атомарности групп).
    • mt_cxscheduler -- надстройка над cxscheduler'ом, позволяющая использовать всё на его основе, включая cda, в multithreaded-окружении.
    • Реализация CX-протокола "через handle" вместо прямой адресации по gcid'ам.
    • ---замечание---: осенью возился с разной "подчисткой хвостов": и по новому протоколу, и с VME ("-O0" для vmedirect), и начало с L_TIMER'ом; а в декабре пилил CAENVMElib и драйвер, но до конца не дошло из-за потерь связи. Так что возни было много, а результатов сюда записать -- нет.
  • 2021:
    • Драйвер bridge_drv наделён умением полностью миррорить указываемые устройства.
    • Возможность по-устройственно включать readonly, указанием '!' перед именем драйвера. (Для bridge_drv -- чтоб можно было использовать для readonly-мирроринга устройств их же devtype.)
    • Менеджмент буфера отправки в cxsd_fe_cx.c теперь централизован -- rpycn и replydatasize хранятся прямо в v4clnt_t, что позволяет "мгновенные" ответы от драйверовых _fd_p() добавлять в текущий формируемый пакет, а не отбрасывать.
    • Дореализована поддержка select()/poll() для получения IRQ от CAENVMElib и драйвера a3818.c.
    • Поддержка режимов RUN_MODE в pzframe_drv.
    • Сделан прототип kernel_hw/mvme3100/vmeli.c.
    • Сделан прототип mvme3100_hal.h (заняло около месяца, а с предыдущим -- полтора).
    • ---замечание---: бОльшая часть работы в этом году была связана с VME. В основном -- крайне неприятная и трудоёмкая работа, где даже мелочи требуют совершенно неадекватных трудозатрат. Ощущение бездарно потраченного времени: собственно времени ушло много, а результат невелик.
  • 2022:
    • Допилена lab6_vme_firmware_common.c -- менеджер прошивок.
    • В сервер добавлена концепция "binbuf" и на её основе возможность указывать каналам defval.
    • cxsd_fe_epics
    • UDP-резолвинг в cxlib переведён с 255.255.255.255 на "directed broadcasts" и возможность указания списка в $CX_GURU_LIST.
    • (18.09.2022) чисто для отчёта: поддержка источников MPS6 через CEAC208. (23.10.2022) и для отчёта же: поддержка каких-то измерений с модуляторов Бака через CDAC20.
    • (НАДЕЮСЬ, что сделаю) поддержка cda_d_epics'ом полиморфизма через CXDTYPE_UNKNOWN -- в интересах epics2cda.
    • (10.12.2022) опять чисто для отчёта: фиксенье косяка в canipp_drv.c (некорректная реакция на 0xFF -- нет пересканирования); маета с PLL-PRESET'ами в adc250; поддержка прореживания в ADC250; поддержка ADC1000 (в виде общего adc4x250_meat.h); в adc200_drv.c добавлена поддержка J5/J6, новые драйверы vector_minmaxavg_drv.c и const_drv.c.
    • ---замечание---: первые полгода возился с cxsd_fe_epics, а вторые -- с epics2cda. Весьма выматывающая и неблагодарная работа, будто в густом киселе пытаешься идти: очень уж libCAS плохо сделан.
  • 2023:
    • Чисто для отчёта: драйверы GIN25 и TIME24K4.
    • В систему сборки добавлена поддержка E2K (Elbrus), в т.ч. E2K-128.

      Заодно запинана сборка под E2K и E2K-128 и EPICS (и Tango, но он c -128 несовместим из-за целочисленности "thread ID" в их библиотеке).

    • Наконец-то доделана возможность смены типа канала на лету -- cda_set_type().
    • epics2cda (начатый в прошлом году) доведён до юзабельного состояния.
    • Парочка rdreg_drv.c и wrreg_drv.c, позволяющая представить любой целочисленный (до 32 бит) канал как битовый с индивидуальным доступом к чтению/записи битов.
    • CDA_DATAREF_OPT_PASSIVEMON: возможность пассивной работы с каналами, когда сервером постоянное чтение не ведётся, но клиент сам спрашивает в нужные моменты посредством cda_req_ref_read(). Работает и для клиентов (cda_d_cx+cxsd_fe_cx), и для драйверов (cda_d_insrv).
    • Синхронный режим обновления ON_CYCLE-данных на ветке cxsd_fe_cx-cxlib-cda, включаемый $CDA_D_CX_SYNC_CYCLES, чтобы GUI-клиенты лучше работали при плохой связи.
    • Поддержка Modbus (в основном -TCP) -- полгода с июня по ноябрь делал:
      • Собственно modbus_tcp_drv.c,
      • все "потроха" работы с Modbus -- в modbus_conv.h,
      • и на основе последнего -- утилита командной строки modbus_mon.c, она умеет и с Modbus-RTU и Modbus-ASCII, в т.ч. и через COM-порт, и over-TCP.
    • cda_d_tango "начала дышать" -- научилась получать данные, пока только скаляров. Также и запись скаляров была сделана, но проверена лишь в 2024-м.
  • 2024:
    • Драйверы-симуляторы simkoz_drv.c (для козачиных ЦАП/АЦП) и sim_dir_drv.c (манипулирующий каналами другого устройства).
    • Корректная подчистка формул с помощью being_processed/being_destroyed для возможности формулам "самоубиваться" (в интересах sim_dir_drv.c, чтоб формулы могли делать STOP самим себе (в т.ч. "косвенно") и "._devstate=0").
    • Возможность в devtype указывать имя типа-"предка", от которого будет взят список имён каналов. В интересах симуляции, чтоб можно было описания симулируемых устройств основывать на реальных, добавляя имён.
    • Узнал как реально в Linux работает SIGCHLD в связке с wait() и SIG_IGN (26.05.2024).
    • vdev исправлен так, чтоб правильно работать с "локальными" целевыми устройствами -- у которых значения готовы ДО завершения vdev_init() (05.06.2024).
    • В cda_d_tango добавлена поддержка векторов, строк и специфичных типов (VOID, STATE, ENUM, BOOLEAN, LONG64; всё, кроме DEV_ENCODED и векторов строк) -- чтение и запись, а также работа с командами (@c, @r, @s).
    • Создана основная часть epics2tango_gw.cpp (с использованием полученного опыта и кода) -- нативно сочетающего libCAS с libtango (с поддержкой многопоточности путём множества mutex'ов) и НЕблокирующимися начальными соединениями, посредством вынесения их в отдельные thread'ы.
    • Сделан fdManager_cxscheduler.cpp -- реализация API cxscheduler на fileDescriptorManager.
  • 20??:
  • ...хотелки на далее:
    • 2017:
    • Перевести сварку/Семёнова на v4. 1. Layerinfo в remdrv! 2. LAPPROX в формулах. 3. (23.04.2018) cxsd_fe_starogate.
    • Утилитки "per-device-stand": чтоб в командной строке указывать адрес CAN-блока, а оно б само генерило конфиг (утилита, сочетающая в себе сервер и скрин).
    • (22.05.2017) исчо локинг каналов (самое сложное -- clientside).
    • (22.05.2017) и еще по драйверам: ottucc, iset_walker чтоб работал с РАЗНОТИПНЫМИ каналами (в т.ч. с вещественными; квант ему указывать).
    • (13.08.2017) поддержка VME (пока bivme2) в hw4cx/. Неясно, до конца ли, ибо adc4x250* дурят.

      Также собственно ADC4X250 и ADC1000 -- практически готово, добиться только стабильной работы (будет также vme_fastadc_common).

    • 2018:
    • (26.01.2018) прошеная Роговским и еще ранее Емановым фича «указание "непересекаемых границ"» минимума и максимума отображения для fastadc.
    • (20.02.2018) изготовить первый новый frontend -- cxsd_fe_starogate.c, иначе не перевести сварку/Семёнова на v4. (23.04.2018) давно уж сделан.

      (Кстати, УЖЕ протормозил -- оно скоро распространится на чёмскую сварку, и это "оно" будет всё ещё v2.)

    • (16.03.2018): возможность драйверам указывать не 1 пару R,D, а цепочку -- необходимо для туннелирования свойств.
    • (23.04.2018): иметь возможность указывать в devlist'ах R и D не константами, а выражениями.

      Для этого: ppf4td_get_double() чтоб умел парсить выражения -- если начинается со скобки '(' -- и перевести на него ParseCpointProps().

    • Софт для клистронов/впуска-выпуска/rfsyn/задающего.

      И для этого -- драйверы frolov_ie4, f4226, еще...?

    • (07.11.2018): НАСТОЯЩАЯ возможность получать evproc'ы от формул.

      А то сейчас архитектура такова, что это де-факто не пашет (на примере trig_exec'а).

    • 2019:
    • (29.01.2019) Чтоб cdaclient'у не обязательно б было указывать тип канала, а он определял бы сам и как минимум ПЕЧАТАЛ бы значения как надо (а не nan при подключении к текстовому каналу).
    • (09.07.2019) daqmx-драйверы
    • (09.07.2019) cda_d_tango?
    • (08.11.2019) поддержка MVME3100.
    • (14.11.2019) добавить бы работающую ppf4td_get_double() с поддержкой выражений в "(...)", через алгоритм Дийкстры. И перевести cxsd_db_via_ppf4td.c и Cdr_via_ppf4td.c на неё.
    • 2020:
    • (28.04.2020) Перевести бы протокол v4 с прямой адресации по hwid'ам на "сначала регистрируем канал и получаем handle этой регистрации, а потом все действия уже с этим handle".
    • (30.04.2020) Добавить в cda_dat_p_rec_t ещё пару методов, пока неиспользуемых -- srv_ioctl() и chan_ioctl(), плюс ещё пару пустых полей. Смысл srv_ioctl() -- чтоб можно было принудительно заставить выполнить реконнект; это для случаев, когда клиент был отпинут по правилам контроля доступа, а потом правила поменялись и надо без рестарта повторить коннект.
    • (27.11.2020) Поддержка select()/poll() в CAENVMElib (и драйвере).

      (07.01.2021) и ловля отпада/возврата крейта тоже.

    • 2021:
    • (07.01.2021) исправить косяк формирования ответных пакетов в cxsd_fe_cx -- чтобы rpycn и replydatasize хранились бы прямо в v4clnt_t, чтоб работали мгновенные ответы из драйверовых _rw_p() -- добавлялись бы прямо в текущий формируемый пакет, а не отбрасывались бы.
    • (10.01.2021) изготовить спецификацию протокола SPECIE. А из комментариев/обоснования можно и целую статью сварганить (вопрос только -- куда?), т.к. там явно наклёвываются все надлежащие 5 разделов.
    • (25.02.2021) разобраться с причиной косячной передачи клиенту диапазонов значений каналов.
    • (18.03.2021) bridge_drv -- когда он научится САМ брать имена target-каналов, то станет серьёзным достижением.
    • (18.03.2021) флаг "readonly" per-device
    • 16.04.2021: режимы RUN_MODE для pzframe_drv.
    • 19.04.2021: "processer'ы" для обработки/проверки получаемых от драйверов значений, с поддержкой плагинов.
    • 19.04.2021: "инкарнации" cpoint'ов -- для возможности навешивать processer'ы даже на cpoint'ы.
    • 29.12.2021: переделать API serial_hal.h, чтобы можно было одному простому варианту указывать разные имена устройств (ttyS*, ttyUSB*, ttyAP*, ...). И соответствующие переделки в его "юзерах".
    • 2022:
    • epics2cda
    • epics2tango
    • "cxsd_fe_simple" (вот какое имя хотел дать?! (05.01.2023: "cxsd_sfi") -- см. 0002 от 22-06-2022) -- чтоб на его основе были и cxsd_fe_starogate, и cxsd_fe_epics.
    • 2023:
    • Переходник в CX-сервер для python3-драйверов.
    • ...и на нём сделать обсчёт данных для пикапов от Беркаева.
    • 07.06.2023: modbus_tcp_drv, rdreg_drv, wrreg_drv

      Для отчёта: "Конфигурирование Modbus-драйвера сравнительно простое, за счёт возможности получать информацию о свойствах каналов через внутрисерверный API интроспекции, введённый в 2020г."

    • 07.07.2023: режим PASSIVEMON и cda_req_read.
    • 15.09.2023: синхронный режим обновления ON_CYCLE-данных, включаемый $CDA_D_CX_SYNC_CYCLES.
    • 2024:
    • 11.01.2024: Драйвер-симулятор "CDAC20" для имитации работы источников вроде IST -- нужно для отладки ЕманоФединого софта. Чтоб можно было натравить драйверы ist_cdac20 (или хотя бы mps20_ceac124) в паре с iset_walker на такой псевдо-девайс и они бы корректно с ним работали.

      Число-то перекладывать из уставки в измеренно несложно, но: 1) нужно двигаться с ограниченной скоростью; 2) надо имитировать битики включенности (перекладывать из бита "включиться"?) и т.п.

      04.06.2024: сделан simkoz_drv.c.

    • 29.04.2024: а ещё "драйвер-симулятор вообще" sim_drv.c -- для указания произвольного списка пар "ИЗМЕНЁННЫЙ_КАНАЛ:ДЕЙСТВИЕ", манипулирующий с каналами ДРУГОГО устройства, с которым он связывается по insrv::, и то устройство, скорее всего, в режиме суперсимуляции для возможности писать в каналы чтения.

      13.05.2024: сделан sim_dir_drv.c.

    • 02.05.2024: корректная подчистка формул с помощью being_processed/being_destroyed для возможности формулам "самоубиваться".
    • 04.05.2024: возможность в devtype указывать имя типа-"предка", от которого будет взят список имён каналов.
    • 05.06.2024: а ещё, видимо, исправления в vdev.c, чтоб он корректно работал с sodc-каналами, чьи значения готовы к моменту vdev_init()'а (т.е., с ЛОКАЛЬНЫМИ каналами, а не сетевыми).
    • 30.01.2024: драйверы для шаговиков electroprivod_smsd_xxlan_drv.c (TCP) и servotechnica_spsh_nncan_drv.c; последний через CAN-bus, что повлечёт за собой некоторую переделку инфраструктуры сборки CAN-драйверов, т.к. появится реальный ВТОРОЙ layer.

      И вообще этот год (или хотя бы остаток зимы) имеет шанс пройти под знаком шаговиков.

      (Кстати, с 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'ной, в т.ч. поддержки разных версий нет), собралось без проблем.

    • 06.07.2024: cda_d_tango: добавлена поддержка векторов и строк, а также работа с командами.
    • 05.09.2024: epics2tango_gw
    • 06.10.2024: добавить в cda_d_tango поддержку "массивов строк": читаемые превращать в символьные массивы, разделяя строки NUL'ами, а при записи сканировать строковые массивы на NUL'ы и превращать их в векторы строк.
    • 06.10.2024: добавить в cda_d_epics поддержку "массивов строк" аналогично предыдущему.
    • 06.10.2024: ...а в epics2cda? А тогда уж в cxsd_fe_epics?
    • 09.11.2024: разбить modbus_tcp_drv.c на modbus_drv.c+modbus_lyr.c -- чтоб добавить поддержку работы через COM-порты, с Modbus-RTU и с несколькими устройствами на одном порте.
    • 2025:
    • 21.02.2025: сделать trainer_drv
    • Отделить-таки уровни "установление соединения" и "открыватели" из modbus_lyr.c в отдельные "слои", чтоб оно было доступно в других местах.
    • UNLIMITED_MODBUS
    • MODBUS_CONV_FLOAT16_10E6M10
    • Умение устанавливать нестандартную скорость COM-порту
    • Сборка и запуск 4cx+hw4cx+cxsd_fe_epics на "контроллере Торнадо"
    • Поддержка vip45 (ради которого FLOAT16_10E6M10) и dicom_ip_mrn20 (ради которого UNLIMITED_MODBUS).
    • 202?:
???

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, а там весёлое:

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.
(и подобного там от других бриджей, вроде k500_bpm -- выше крыши). Смотрим прямее:
[oper@cxout]~% host cxhw
Host cxhw not found: 3(NXDOMAIN)
Т.е., были косяки с резолвингом (как потом оказалось, у этой машины шибко много интерфейсов, и, похоже, последний оказался институтским, и именно его DNS стал первым в списке, так что в конечном итоге имя "cxhw" попросту не резолвилось.

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 (и отрицательные тоже, из-за кодировки), как-то обрезает.