DAQ PASCAL API

Введение
DaqPascal StdLibrary template.pas включаемые файлы стандартная библиотека опции компилятора ограничения компилятора
DAQ-Объекты Устройства Кривые Теги Тексты Задачи Потоки Базы Данных Каналы pipe tcp com Хеш-таблицы Окна Регулярные выражения Конечные Автоматы Планирование Переменные окружения URL-кодирование строк техника Cleanup
Основные типы данных
char boolean integer real string record array
Предопределенные константы
true false sizeofchar sizeofreal sizeofboolean sizeofinteger daq_cmd_init daq_cmd_free daq_cmd_poll tag_ref_min tag_ref_max task_ref_min task_ref_max rfreplaceall rfignorecase quotemark apostrophe isunix islinux iswindows sizeofpointer defaultsystemcodepage cp_utf8 cp_none cp_1251 cp_866
Операторы
+ - * / div mod () [] not ~ and & or | := = <> < <= > >= : ; {},(**),// begin end program procedure function type var const if then else case for to downto do while repeat until
Функции арифметические, общего назначения
abs sqr sqrt odd chr ord upcase locase succ pred round trunc int frac floor ceil deg rad sin cos tan asin acos atan arctan sinh cosh tanh exp power ln log _nan isnan _minusinf _plusinf _inf isinf _nil pi macheps maxint gamma sign hypot rand random min max rmin rmax imin imax eq ne lt le gt ge bitnot inot bitor ior bitxor ixor bitand iand rshift ishift isbit getbitmask hasflags
Функции строковые, общего назначения
maxavail stackavail length strfetch copy pos posex poseol str strfix strfmt strtimefmt hexb hexw hexl inttostrbase strtointbase val rval upcasestr locasestr worddelims wordcount extractword skipwords phrasequotes phrasecount extractphrase skipphrases phraselisttotextlines crlf eol lineending slinebreak directoryseparator pathdelim pathseparator pathsep adjustlinebreaks stringreplace ansiquotedstr ansidequotedstr ansiskipquotedstr extractfirstparam skipfirstparam isoption getoptionvalue ansiquotedifneed issametext islexeme trim trimleft trimright cookiescan stringofchar igetdump isetdump rgetdump rsetdump eval evar global
Функции строковые, связанные с регулярными выражениями (RegExp)
regexp_init regexp_free regexp_ref regexp_ctrl regexp_test regexp_exec regexp_replace regexp_matchnum regexp_matchpos regexp_matchlen regexp_matchstr regexp_escape
Функции строковые, связанные с кодированием
strconv base16_encode base16_decode base32_encode base32_decode base32_alphabet base64_encode base64_decode hex_encode hex_decode url_packed url_encode url_decode mime_encode mime_decode crypt_encode crypt_decode crypt_ctrl getmd5fromstr getmd5fromtext getmd5fromfile dump dump2b dump2c dump2i dump2r dump2f dumpf hashindexof backslash_encode backslash_decode backslash_encoder_ctrl pct_encode pct_decode pct_encoder_ctrl percent_encode percent_decode percent_encoder_ctrl
Функции строковые, связанные с Unicode, UTF8
utf8_encode_ansi utf8_decode_ansi utf8_uppercase utf8_lowercase utf8_length utf8_copy utf8_ord utf8_chr utf8_bom
Функции строковые, связанные с файлами
daqfileref defaultextension forceextension defaultpath forcepath makerelativepath fexpand addbackslash addpathdelim dropbackslash droppathdelim extractfilepath extractfilename extractfileext iswildcard isrelativepath hasextension direxists fileexists fileerase filerename filecopy getfattr setfattr doserror mkdir dirlist createtempfile adaptfilename adaptexefilename adaptdllfilename adaptlnkfilename adaptfilenamemode
Функции конфигурирования и идентификации
paramstr readini readinisection getenv expenv setenv getpid progname devname reffind refinfo enumerate
Функции консольного/файлового ввода-вывода и обмена сообщениями
echo debugout reset rewrite append read readln write writeln ioresult eof eoln f_reset f_rewrite f_read f_write f_size f_seek f_close devmsg devsend devpost devsendmsg devpostmsg readfiletobuffer writebuffertofile getfileproperties
Функции ведения системного журнала syslog
syslognotable
syslognote
Функции для работы с текстами
text_new text_free text_getln text_putln text_insln text_addln text_delln text_numln text_tostring
Функции, связанные с временем
runcount sleep awakeflag wdt_reset wdt_timeout secnow msecnow mksecnow getticks getclockres setclockres vdpm_opcount time timebase timeunits ms2year ms2month ms2day ms2hour ms2min ms2sec datetime2ms tm_new tm_free tm_gettime tm_start tm_stop tm_isstart tm_event tm_curint tm_numint tm_addint tm_getint tm_setint cpu_count cpu_start cpu_clock cpu_mhz
Функции для работы с устройствами
numais numaos numdis numdos refai refdi refao refdo putev putao putdo calibr numcals refcalibr getai getdi getai_n getdi_n getai_xn getdi_xn getai_yn getdi_yn getai_xi getdi_xi getai_yi getdi_yi getai_par getao_par getdi_par getdo_par aimap dimap aomap domap diword fixerror registererr geterrcount voice action clear cleardevice start stop clearcurve savecrw irqinit irqfree isisrnow
Функции для работы с кривыми
crvfind crvname crvlen crvx crvy crvget crvput crvins crvdel crvwhere crvinteg crvlock crvunlock crvgetln crvputln crvinsln crvaddln crvdelln crvnumln
Функции для работы с тегами
createtag inittag freetag findtag typetag nametag igettag isettag rgettag rsettag sgettag ssettag taglock iatomictagop ratomictagop gettagcolor settagcolor gettagparam settagparam gettagtimer settagtimer
Функции для работы с окнами и диалогами
clickbutton clicksensor clicktag clickparams clickwhat clickread clickwrite clickwrote clickfilter clickawaker shortcut2str str2shortcut windraw winshow winhide winselect add3d plot3d specmark specmarkl specmarkr specroil specroir edit editstate guard_check
Функции для работы с RS-232/RS-485/Ethernet(NamedPipe)
comopen comclose comclear comcount comspace comwrite comread adam_status adam_get adam_request adam_reqtime
Функции для работы с портами ввода-вывода
inportb inportw inportl outportb outportw outportl
Функции для работы c задачами (процессами)
task_init task_free task_ref task_pid task_run task_wait task_send task_recv task_txcount task_txspace task_rxcount task_rxspace task_result task_kill task_ctrl getcomspec getshell shellexecute pidlist pidkill pidaffinity devaffinity
Функции для работы с каналами (task,pipe,tcp,com)
pipe_init pipe_free pipe_ref pipe_pid pipe_run pipe_wait pipe_send pipe_recv pipe_txcount pipe_txspace pipe_rxcount pipe_rxspace pipe_result pipe_kill pipe_ctrl pipe_txclear pipe_rxclear pipe_count pipe_stream pipe_connected
Функции для межпроцессного взаимодействия (EasyIpc)
easyipc_init easyipc_free easyipc_poll easyipc_send easyipc_recv easyipc_ctrl
Функции для работы DLL (внешними подпрограммами на Delphi)
daqdllinit daqdllfree daqdllcall
Функции для работы с хеш-таблицами
hashlist_init hashlist_free hashlist_count hashlist_getkey hashlist_delete hashlist_indexof hashlist_getdata hashlist_setdata hashlist_getlink hashlist_setlink hashlist_getpara hashlist_setpara
Функции для работы с Конечными Автоматами (Finite State Machine)
fsm_new fsm_free fsm_ref fsm_root fsm_type fsm_parent fsm_name fsm_path fsm_ctrl fsm_count fsm_items fsm_get_iparam fsm_set_iparam fsm_get_fparam fsm_set_fparam fsm_get_sparam fsm_set_sparam fsm_add fsm_find fsm_get_state fsm_set_state fsm_link fsm_modified fsm_name_rule
Функции для работы с СУБД (Базами Данных)
db_create db_connection db_recordset db_command db_free db_ref db_root db_type db_parent db_engineid db_active db_state db_close db_open db_ctrl db_bugscount db_bugsclear db_errors db_errorscount db_errorsclear db_execute db_cancel db_update db_cancelupdate db_begintrans db_committrans db_rollbacktrans db_bof db_eof db_movefirst db_movelast db_movenext db_moveprevious db_fieldscount db_fieldsnames db_fieldstypes db_fieldsasint db_fieldsasfloat db_fieldsasstring db_addnew db_delete db_requery db_resync db_supports db_save
Функции для работы с Общей Памятью (Shared Memory)
shm_init shm_ref shm_free shm_init shm_delink shm_iop shm_rop shm_fop shm_ctrl shm_ioresult
Daq Pascal Api -> daqpascal

Описание:

Daq Pascal - компилятор языка Pascal для виртуальной DAQ-машины. На этом компиляторе основано большинство автоматизированных систем, работающих под управлением системы сбора данных CRW-DAQ. См. также опции компилятора.

Каждое Устройство типа Program содержит простой компилятор для выполнения программ на языке, условно названном DAQ PASCAL. Этот компилятор не ставит целью как-либо конкурировать с другими версиями языков Pascal, он служит для строго определенных целей. Язык DAQ PASCAL – это узкоспециализированная версия компилятора языка Паскаль для решения задач сбора данных в системе CRW-DAQ. Его основным достоинством является простота и скорость создания программ для автоматизации измерений, “прозрачность” и ясность получаемых при этом программ, повышенная степень защищенности DAQ-программ от сбоев, а также наличие в системе CRW-DAQ полной среды разработки этих программ. Встроенный язык позволяет сильно ускорить разработку и отладку программ автоматизации измерений.

Система CRW-DAQ компилирует файл с программой на языке DAQ PASCAL в промежуточный P-код, который можно условно назвать DAQ-программой, который затем выполняется при помощи интерпретатора P-кода, который можно условно назвать виртуальной DAQ-машиной. Интерпретатор P-кода работает намного быстрее, чем интерпретаторы типа Basic, хотя и уступает машинному коду, который генерируют обычные компиляторы. Например, для PII-350 производительность составляет около 1.100.000 простых операторов Daq Pascal в секунду. Здесь следует заметить, что даже самый простой оператор Pascal на самом деле содержит довольно значительное число низкоуровневых команд процессора и поэтому накладные расходы на интерпретацию играют сколько-нибудь заметную роль только для самых элементарных операций. Для подавляющего числа задач производительности кода DAQ PASCAL оказывается достаточно. Немаловажным фактором является то, что программы на встроенном языке Daq Pascal могут быть отредактированы, скомпилированы и сразу же выполнены в рамках системы CRW-DAQ и не требуют отдельного компилятора или среды разработки.

Компилятор DAQ PASCAL не создает каких-либо промежуточных файлов, а сразу генерирует P-код программы в памяти из исходного текста при загрузке конфигурационного файла. Поэтому в качестве “исполняемой” программы хранятся исходные тексты на языке DAQ Pascal, обычно расположенные в каталоге ..\DaqPas.

Файлы с программами на языке DAQ PASCAL должны содержать разделенные на строки символы ASCII, строки не должны быть длиннее 241 символа. Модули в данной версии не поддерживаются. Допускаются включаемые файлы, то есть каждая DAQ-программа может быть разбита на несколько файлов - главный и включаемые. Комментарии помещаются в фигурные скобки {}. Вложение комментариев не допускается. Идентификаторы могут содержать буквы английского алфавита a…z,A…Z, цифры 0…9 и знак подчеркивания _, но могут начинаться только с буквы или символа _. Идентификаторы имеют в данной версии не более 40 значащих символов, причем заглавный/прописной регистр не играет роли. Однако в строковых константах и выражениях регистр символов играет роль.

Компилятор содержит следующие зарезервированные слова и операторы, которые не могут использоваться как пользовательские идентификаторы:

and, array, begin, case, const, div, do, downto, else, end, file, for, function, goto, if, in, label, mod, nil, not, of, or, packed, procedure, program, record, repeat, set, then, to, type, until, var, while, with, '+', '-', '*', '/', ')', '=', ',', '[', ']', '~', '&', ';', '|'

В приведенном списке слова file, goto, in, label, nil, set не используются и введены только для совместимости со стандартом языка.


Daq Pascal Api -> включаемые файлы

Описание:

Текст программы на языке Daq Pascal может включать в себя вложенные файлы с уровнем вложенности до 9 (в текущей версии). Вложенные файлы включаются директивой:
          {$I FileName}
              или
          (*$I FileName*)
         

Для имени файла действуют такие правила:

  • Имя включаемого файла имеет расширение "по умолчанию" .INC, если оно не указано явно.
  • Имя может содержать только символы a..z A..Z 0..9 _ : \ / . ~. Национальные и специальные символы не допускаются.
  • Если указан абсолютный путь файла, то это конкретный файл. Путь тоже не должен содержать символов кроме указанных.
  • Если указан относительный путь, то путь вычисляется относительно главного компилируемого файла.
    Если файл не найден относительно главного компилируемого файла, то делается попытка найти его относительно включаемого файла предыдущего уровня вложенности. В пути допустимо использовать ссылки: .\ = текущий каталог, ..\ - родительский каталог, ~\ - домашний каталог пользователя, ~~\ - домашний каталог основной программы (Crw32.exe).
  • Если путь совсем не указан, поиска файла идет (до первого успеха) в такой последовательности:
    1. В каталоге главного компилируемого файла.
    2. В списке путей, заданных переменной CRW_DAQ_CONFIG_PATH, которая задается при загрузке текущей конфигурации переменными [DAQ] SearchPath=..., см. также ParamStr('AddSearchPath').
    3. В списке путей, заданных переменной CRW_DAQ_INCLUDE_PATH, которая задается при загрузке текущей конфигурации из файла Crw32.ini, секции [DaqSys] IncludePath=.... Там находится системная библиотека включаемых файлов. Эти подробности мало касаются прикладного программиста - системная библиотека для него есть константа. Там находятся "эталонная" версия файлов библиотеки, редактировать которые запрещено.
    4. В каталоге включаемого файла предыдущего уровня вложенности.

Такая сложная система поиска включаемых файлов призвана облегчить разработку и сопровождение программ. Предполагается, что в подавляющем большинстве случаев будет указываться простое имя включаемого файла, без пути и расширения (которое, напомню, по умолчанию .INC). Если система найдет файл в текущем каталоге компилируемой программы, она включит его. Если этого файла нет, будет продолжен поиск по каталогам, заданным в [DAQ] SearchPath=..., затем в каталоге системной библиотеки и наконец в пути предыдущего по уровню включаемого файла. Такая система позволяет ссылаться на системные библиотечные файлы по короткому имени, в то же время системные библиотеки легко "перекрыть", если поместить их в путь поиска [DAQ] SearchPath=..., либо в текущий каталог программы.

При включении файла выполняется простая подстановка текста файла в месте включения. За процессом включения можно следить по отладочному выводу в *.LST файл в каталоге [DAQ] TempPath, который также доступен в панели свойств DAQ-устройств. Это позволяет точно выяснить, какой именно файл был включен в программу и увидеть полный текст программы, а также время, затраченное на его компиляцию.

Включение файлов имеет ограничения:

  1. Нельзя помещать во включаемый файл опции компилятора ([COMPILER.OPTIONS]).
    Опции компилятора должны находиться только в основном файле.
    Во включаемых файлах они будут просто проигнорированы.
  2. При использовании стандартных библиотек возможно придется увеличить размер таблиц компилятора. Воспользуйтесь шаблоном опций компилятора.
  3. Не допустимы рекурсивные включения (это приведет к ошибке компиляции).
  4. Нельзя включать файлы с уровнем включения выше 9.

Создана стандартная библиотека включаемых файлов для прикладных программ DAQ PASCAL. Есть описание и шаблон программы, поясняющий, как правильно ее использовать. Предполагается, что постепенно большинство прикладных программ будут создаваться на основе этой библиотеки путем включения в программу нужных библиотек. Это резко (в 2-3 раза) сократит объем прикладного кода, а также понизит вероятность ошибок в прикладных программах за счет отделения стандартного, хорошо провереного кода от прикладного.


Daq Pascal Api -> [COMPILER.OPTIONS] - опции компилятора

Описание:

Daq Pascal - компилятор языка Pascal для виртуальной DAQ-машины. Компилятор и виртуальная DAQ-машина имеют внутренние таблицы, определяющие максимальный размер кода, данных, идентификаторов и т.д. В данном разделе описано, как конфигурировать эти параметры.

Сначала приведем шаблон для копирования:

{
[Compiler.Options]             ; Size of table for ...
Compiler.dtabmax = 1024*16     ; -Data segment ( number of virtual machine data items )
Compiler.stabmax = 1024*4      ; -Strings      ( number of available string var+const )
Compiler.dtabmin = 1024*1      ; Min stack  space at start ( error if free space less )
Compiler.stabmin = 512*1       ; Min string space at start ( error if free space less )
Compiler.slenmax = 1024*1024*2 ; Max string length limit   ( error at runtime if more )
[]
}
         
Здесь указаны наиболее часто употребляемые значения по умолчанию, умноженные на единицу - как заготовка. Вместо *1 можно вставить свой множитель, можно вещественный.

А теперь обещанное описание.

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

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

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

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

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

Размеры таблиц задаются в файле с исходным кодом программы. Для задания размеров таблиц в комментарии программы помещается секция [Compiler.Options], обязательно завершающаяся пустой секцией []. Например:

         program demo;
         {
         [Compiler.Options]
         Compiler.dtabmax = 4096
         []
         }
         begin
         end.
         
Если размеры таблиц не найдены в файле с исходным кодом программы, их поиск продолжается сначала в секции [Compiler.Options] в основном конфигурационном файле загружаемой DAQ системы, а затем в секции [Compiler.Options] системного файла настроек Crw32.ini. Это позволяет задавать прикладным программам опции "по умолчанию". Если же размер таблицы явно нигде не описан, принимается разумное значение по умолчанию, зашитое в программе (оно указано в следующем описании) и пригодное для большинства простых программ. Все это сокращает трудозатраты на конфигурирование устройств.

Пример можно посмотреть в конце раздела.

Итак, какие же таблицы имеет компилятор и DAQ-машина? Вот они:

  • Compiler.itabmax

    Значение по умолчанию = 1280.
    Этот параметр задает размер таблицы идентификаторов, то есть имен переменных, констант, типов, массивов, процедур, функций - всех поименованных объектов прикладной программы. Таблица может переполниться, если в программе много переменных, функций и т.д. При переполнении таблицы идентификаторов компилятор выдает ошибку компилятора №63 с сообщением:
                  Compiler table overflow - identifiers
                  
    При возникновении этой ошибки надо увеличить значение itabmax.

    Начиная с версии 17.02.2012 переменная Compiler.itabmax задает лишь начальное значение таблицы itab, которая может расти по мере необходимости. Это решение было принято потому что таблица itab заполняется в момент компиляции и не может расти в процессе работы программы, то есть не может снизить надежность работы программы. Поэтому ошибка переполнения itab не должна более возникать.

  • Compiler.btabmax

    Значение по умолчанию = 60.
    Этот параметр задает размер таблицы блоков, то есть процедур и функций - блоков кода прикладной программы. Таблица может переполниться, если в программе много процедур и функций. При переполнении таблицы блоков компилятор выдает ошибку компилятора №64 с сообщением:
                  Compiler table overflow - procedures
                  
    При возникновении этой ошибки надо увеличить значение btabmax.

    Начиная с версии 17.02.2012 переменная Compiler.btabmax задает лишь начальное значение таблицы btab, которая может расти по мере необходимости. Это решение было принято потому что таблица btab заполняется в момент компиляции и не может расти в процессе работы программы, то есть не может снизить надежность работы программы. Поэтому ошибка переполнения btab не должна более возникать.

  • Compiler.atabmax

    Значение по умолчанию = 60.
    Этот параметр задает размер таблицы массивов, то есть различных переменных array [ .. ] of .. и record. Таблица может переполниться, если в программе много массивов и записей. При переполнении таблицы блоков компилятор выдает ошибку компилятора №66 с сообщением:
                  Compiler table overflow - arrays
                  
    При возникновении этой ошибки надо увеличить значение atabmax.

    Начиная с версии 17.02.2012 переменная Compiler.atabmax задает лишь начальное значение таблицы atab, которая может расти по мере необходимости. Это решение было принято потому что таблица atab заполняется в момент компиляции и не может расти в процессе работы программы, то есть не может снизить надежность работы программы. Поэтому ошибка переполнения atab не должна более возникать.

  • Compiler.rtabmax

    Значение по умолчанию = 100.
    Этот параметр задает размер таблицы вещественных констант, то есть различных констант типа real. Таблица может переполниться, если в программе много вещественных констант. Целочисленные константы компилируются непосредственно в сегменте кода, их число не ограничено значением rtabmax. При переполнении таблицы вещественных констант компилятор выдает ошибку компилятора №65 с сообщением:
                  Compiler table overflow - reals
                  
    При возникновении этой ошибки надо увеличить значение rtabmax.

    Начиная с версии 17.02.2012 переменная Compiler.rtabmax задает лишь начальное значение таблицы rtab, которая может расти по мере необходимости. Это решение было принято потому что таблица rtab заполняется в момент компиляции и не может расти в процессе работы программы, то есть не может снизить надежность работы программы. Поэтому ошибка переполнения rtab не должна более возникать.

  • Compiler.ctabmax

    Значение по умолчанию = 8192.
    Этот параметр задает размер таблицы исполняемого кода, то есть откомпилированных инструкций виртуальной машины. Таблица может переполниться, если программа имеет большой объем и в ней много операторов. При переполнении таблицы исполняемого кода компилятор выдает ошибку компилятора №68 с сообщением:
                  Compiler table overflow - code segment
                  
    При возникновении этой ошибки надо увеличить значение ctabmax.

    Начиная с версии 17.02.2012 переменная Compiler.ctabmax задает лишь начальное значение таблицы ctab, которая может расти по мере необходимости. Это решение было принято потому что таблица ctab заполняется в момент компиляции и не может расти в процессе работы программы, то есть не может снизить надежность работы программы. Поэтому ошибка переполнения ctab не должна более возникать.

  • Compiler.dtabmax

    Значение по умолчанию = 8192.
    Этот параметр задает размер сегмента данных, то есть стека виртуальной машины, который служит для хранения всех глобальных и локальных переменных, а также временных данных, нужных для выполнения операций. Таблица может переполниться, если в программе много переменных, массивов, записей. При переполнении таблицы сегмента данных компилятор выдает ошибку компилятора №20 с сообщением:
                  Compiler table overflow - data segment
                  
    При возникновении этой ошибки надо увеличить значение dtabmax.
    Переполнение таблицы возможно также во время выполнения программы, если ей не хватает стека для размещения временных данных или параметров и локальных переменных при вызове процедур. Поэтому значение dtabmax надо брать с запасом.

  • Compiler.dtabmin

    Значение по умолчанию = 256.
    Это минимальное допустимое значение свободного пространства в стеке (сегменте данных) после завершения компиляции программы. Во время компиляции программы глобальные переменные размещаются в сегменте данных статически, а оставшееся стековое пространство используется для размещения локальных переменных процедур/функций и временных данных для вычислений. Если свободное стековое пространство меньше указанного предела, компилятор генерирует ошибку, так как нормальное выполнение DAQ-программы при недостаточном объеме памяти в стеке невозможно.

  • Compiler.stabmax

    Значение по умолчанию = 1024.
    Этот параметр задает размер таблицы строк, то есть таблицы, которая служит для хранения всех глобальных и локальных строк, а также временных строк, нужных для выполнения строковых операций. Таблица может переполниться, если в программе много строковых констант и переменных. При переполнении таблицы строк компилятор выдает ошибку компилятора №69 с сообщением:
                  Compiler table overflow - strings
                  
    При возникновении этой ошибки надо увеличить значение stabmax.
    Переполнение таблицы возможно также во время выполнения программы, если ей не хватает таблицы для размещения временных строк или локальных строковых переменных при вызове процедур. Поэтому значение stabmax надо брать с запасом. См. также раздел string.

  • Compiler.stabmin

    Значение по умолчанию = 256.
    Это минимальное допустимое значение свободного пространства в таблице строк после завершения компиляции программы. Во время компиляции программы строковые константы размещаются в сегменте данных статически, а оставшееся строковое пространство используется для размещения строковых переменных. Если свободное строковое пространство меньше указанного предела, компилятор генерирует ошибку, так как нормальное выполнение DAQ-программы при недостатке места в строковой таблице невозможно.

  • Compiler.slenmax

    Значение по умолчанию = 1024*1024*2.
    Этот параметр задает максимальное значение допустимой длины строк. Если программа пытается работать со строками длиннее этого предела, виртуальная машина генерирует ошибку времени исполнения. В настоящей версии максимальную длину можно менять от 32KB до 256 MB.

Следует особенно подчеркнуть, что

  • Размер таблиц itabmax, btabmax, atabmax, rtabmax, ctabmax играет роль только во время компиляции программы. То есть если компиляция прошла успешно, значение этих таблиц не оказывает влияния на этапе исполнения DAQ-программы. В этом смысле указанные таблицы "безопасны".
  • Размер таблиц dtabmax, stabmax играет роль не только во время компиляции программы, но и на этапе ее исполнения. То есть успешная компиляция еще не означает, что все в порядке. Памяти может не хватить в процессе исполнения программы. Причины могут быть разными:
    • Не хватает памяти в стеке для временных данных, нужных для выполнения программы.
    • Не хватает памяти в стеке для вызова процедур и функций, особенно при большом объеме локальных данных или при глубокой рекурсии.
    • Не хватает таблиц для размещения постоянных или временных строковых данных. См. также раздел string.
По указанной причине следует оценивать предположительный объем сегмента кода и таблицы строк и брать значения этих таблиц с запасом.

Для оценки требуемых размеров таблиц надо учитывать, что каждая переменная, элемент массива или записи любого типа занимает одну ячейку сегмента данных. Сегмент данных можно контролировать при помощи функции stackavail. Кроме того, каждая строковая константа, переменная или элемент массива требует ячейки в таблице строк. Таблицу строк можно контролировать при помощи функции maxavail.

Надо также отметить, что задании размеров таблиц можно использовать арифметические выражения, например,

         Compiler.dtabmax = 1024*8+256
         Compiler.ctabmax = 8192*1.5
         

Пример.

         program demo;
         {
         [Compiler.Options]
         Compiler.itabmax = 1024*2  ; Size of table for identifiers  ( var, const, procedure, function etc  )
         Compiler.btabmax = 256*1   ; Size of table for blocks       ( number of procedures and functions   )
         Compiler.atabmax = 64*1    ; Size of table for arrays       ( number of arrays and records         )
         Compiler.rtabmax = 128*1   ; Size of table for real const   ( number of real constants             )
         Compiler.ctabmax = 1024*16 ; Size of table for code segment ( number of virtual machine operators  )
         Compiler.dtabmax = 1024*16 ; Size of table for data segment ( number of virtual machine data items )
         Compiler.stabmax = 1024*4  ; Size of table for strings      ( number of available string var+const )
         Compiler.dtabmin = 1024*1  ; Min stack  space at startup    ( generate error if free space less    )
         Compiler.stabmin = 512*1   ; Min string space at startup    ( generate error if free space less    )
         Compiler.slenmax = 1024*1024*2 ; Max string length ( generate runtime error if string length more  )
         []
         }
         begin
          writeln('hello);
         end.
         

Daq Pascal Api -> ограничения компилятора

Описание:

Daq Pascal - компилятор языка Pascal для виртуальной DAQ-машины. Он создан для специальных целей - создание прикладных программ для измерительных систем - и не претендует на статус языка общего назначения, как C++ или Object Pascal. Он имеет ограничения, которые, вероятно, никогда не будут сняты, так как важнее сохраненить простоту и надежность языка. Другие ограничения, возможно, со временем исчезнут. Вот список основных ограничений:
  • Классов нет и не будет.
  • Указателей нет и не будет.
  • Процедурных типов нет и не будет.
  • Структура программы жесткая, см. program. Вряд ли это изменится.
  • В языке используются статические таблицы, см. раздел опции компилятора. Из соображений сохранения надежности так будет и дальше.
  • При объявлении процедур допускается вложенность не более 7 уровней. Пока не возникало ситуаций, где бы это чему-то мешало. Константа может быть увеличена в будущем, если потребуется.
  • Идентификаторы имеют 40 значимых символов. Это значение может быть увеличено в будущем, если потребуется.
  • Строки исходного текста программы могут иметь длину не более 241 символа. Это значение может быть увеличено в будущем, если потребуется.
  • Длина строк не более [Compiler.Options] Compiler.slenmax байт. По умолчанию - 1024*1024*2 = 2 MB.
  • Многие строковые функции работают со строками длиной не более 255 символов (обрезают их).
  • Оператор case ограничен не более чем 256 альтернативами, не имеет секции else, не поддерживает диапазоны значений (а только перечисления).


Daq Pascal Api -> DAQ-Объекты

Описание:

Daq Pascal не является объектно-ориентированным языком, это язык сценариев для измерительных задач. Однако все же можно говорить о DAQ-объектах в том смысле, что работа Daq Pascal с различными структурами ядра CRW-DAQ происходит как с неделимыми (атомарными) объектами через специальный набор функций.

Список основных типов DAQ-объектов:

  • Device - устройство, то есть программа или драйвер. Это объект, содержащий исполняемый код и выполняющий какую-то работу в измерительной системе.
  • Curve - кривая, динамический массив для хранения данных.
  • Tag - тег, скалярная переменная.
  • Text - текст, упорядоченный набор строк.
  • Task - задача, ссылка на внешнюю, параллельно работающую программу.
  • Polling - поток контролируемого периодического опроса (polling).
  • Timer - таймер, для организации циклических процессов.
  • Window - окно, для отображения графической информации.
  • RegExp - мастер обработки регулярных выражений (TRegExpMaster).
  • Fsm - средства обработки конечных автоматов (FiniteStateMachine).
  • DB - средства работы с базами данных (DataBase).

DAQ-объекты идентифицируются именами и ссылками, а также имеют тип. В ряде функций Daq Pascal доступ к объектам осуществляется по имени, однако в большинстве случаев работа с объектами идет при помощи ссылок. Программа получает ссылку объекта по имени объекта и далее работает со ссылкой для быстрого доступа к нему - таков общий принцип работы с объектами в CRW-DAQ. Зачем нужно было делать такое разделение?

Имя объекта - это строка символов, которая служит для идентификации объектов в конфигурационном файле, а также именующая объект с точки зрения пользователя. Имя объекта существует, даже если самого объекта нет (программа не запущена). Поэтому имя объекта - его постоянный и неизменный идентификатор. Однако доступ к объекту по имени приводит к деградации производительности системы, так как доступ по имени связан с поиском объекта в таблице объектов, что занимает много времени. Для ускорения доступа и нужны ссылки.

Ссылка (reference) объекта - это число, которое служит для идентификации объекта с точки зрения исполнительной системы. Смысл ссылки известен только системе. Прикладной программе известно только следующее:

  • Корректная ссылка является целым положительным числом.
  • Нулевая ссылка является признаком ошибки или несуществующего объекта.
  • Ссылки задач Task занимают диапазон 1..255.
  • Для каждого типа объектов существует свой набор функций.
Ссылка обеспечивает высокую скорость работы с объектом, однако она существует только в момент работы системы и не является постоянной характеристикой объекта. Так, при каждом запуске программы ссылка может иметь различное значение. Программа не вправе рассчитывать, что ссылка будет иметь заранее известное значение. Поэтому для получения корректной ссылки используется имя и тип объекта.

Тип объекта определяет, какие операции к нему применимы. Тип объекта также является постоянным атрибутом объекта, как и его имя. Тип объекта ref можно узнать вызовом refinfo(ref,'Type'). Для каждого типа объекта существует набор функций, которые к нему применимы. Например, теги обслуживаются функциями типа iGetTag,iSetTag и т.д.

Daq Pascal задуман как защищенная от сбоев среда для DAQ-систем. По этой причине в языке нет и не будет указателей, они небезопасны. Идентификация объектов CRW-DAQ ссылками позволяет повысить защиту программ. Система поддерживает таблицу ссылок для всех объектов программы, поэтому всегда можно проверить, существует ли объект с данной ссылкой, какой он имеет тип, имя и т.д. Для указателей такой возможности нет.

Благодаря высокой степени защиты, ошибочное использование ссылки в неверном контексте (скажем, вычисление crvlen(reffind('tag button')) не приведет к сбою системы, будет возвращено разумное значение (в данном примере - ноль). В худшем случае будет прекращено выполнение DAQ программы, но это все же лучше, чем сбой всей системы.

См. также reffind, refinfo.

Пример:

          {работа с тегами}
          tag:=findtag('button')                       {получить ссылку тега по имени}
          tag:=reffind('tag button')                   {еще один способ сделать это}
          if typetag(tag)=tag_type_int                 {проверить тип тега}
          if IsSameText(refinfo(tag,'type'),'tag')     {проверить что это тег}
          i:=igettag(tag);                             {использование тега}
          ...
          {работа с кривыми}
          crv:=refai(0);                               {получить ссылку кривой по входу}
          crv:=crvfind('temp1');                       {получить ссылку кривой по имени}
          crv:=reffind('curve temp1');                 {еще один способ сделать это}
          if IsSameText(refinfo(crv,'type'),'curve')   {проверить что это кривая}
          y:=crvx(crv,crvlen(crv));                    {использование кривой}
          ...
          {работа с устройствами}
          dev:=reffind('device &driver');              {получить ссылку устройства по имени}
          if IsSameText(refinfo(dev,'type'),'device')  {проверить что это устройство}
          r:=devsend(dev,'Hello'+EOL);                 {послать устройству сообщение}
          ...
          {работа с задачами}
          tid:=task_init('CmdLine=cmd.exe')            {создать задачу и получить ссылку}
          if IsSameText(refinfo(tid,'type'),'task')    {проверить что это задача}
          b:=task_run(tid)                             {запустить на исполнение}
          b:=task_free(tid)                            {уничтожить задачу}
          ...
          {работа с таймерами}
          tm:=tm_new                                   {создать таймер и получить ссылку}
          if IsSameText(refinfo(tid,'type'),'task')    {проверить что это таймер}
          b:=tm_event(tm)                              {использовать для генерации событий}
          b:=tm_free(tm)                               {уничтожить таймер}
          ...
         

Daq Pascal Api -> Устройство (device)

Описание:

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

Устройства описываются в конфигурационном файле и создаются системой при загрузке DAQ-конфигурации. Динамического создания устройств в процессе исполнения не предусмотрено.

Можно представить устройство CRW-DAQ как “черный ящик”, имеющий цифровые и аналоговые входы и выходы, который может по какому-то алгоритму преобразовывать данные, взяв нужную информацию из базы данных реального времени и поместив результат обработки в эту же базу данных. База данных реального времени - это список кривых и тегов, в которых хранятся измеряемые величины и вспомогательные переменные.

Кривые обычно поключаются к входам и выходам устройства, так что реально устройство обычно берет данные с входов и помещает результат на выходы. Устройства также могут использовать для хранения и обмена данными общие скалярные переменные - теги. Каждое устройство работает автономно, то есть не зависит явно от других устройств, так как выполняется в своем программном потоке. Входы и выходы устройств – это на самом деле массивы ссылок на кривые, которые, как и теги, хранятся в общей базе данных реального времени, описанной в секциях [DataStorage] и [TagList]. Под чтением входа или записью выхода понимается чтение или запись данных в кривую, на которую ссылается этот выход или выход. Таким образом, все потоки данных централизованы и проходят через общую базу данных, целостность которой поддерживает ядро CRW-DAQ.

Входы и выходы устройств делятся на цифровые и аналоговые. Это деление достаточно условно, так как и цифровые и аналоговые данные хранятся в одинаковых кривых в общей базе данных в виде чисел double (64-битные с плавающей точкой). Разница аналоговых и цифровых входов состоит в методах обработки данных, которую можно производить с данными на этом входе или выходе. Можно считать, что аналоговые входы и выходы ориентированы на гладкие непрерывные данные, в то время как цифровые – на дискретные целочисленные данные. Поэтому параметры, связанные с входами или выходами, различны:

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

Вызов устройств CRW-DAQ происходит периодически, каждое устройство работает в своем потоке. Частота опроса определяется файлом конфигурации, максимальная частота опроса определяется квантом времени системы и составляет порядка 100 герц под Windows-NT/2000/XP, что бывает достаточно для многих систем реального времени. Работа устройства фактически заключается в том, чтобы брать входные данные из общей базы данных (кривых и тегов), обрабатывать их и помещать результат в эту же базу данных.

Устройства CRW-DAQ не могут непосредственно вызывать друг друга или передавать друг другу какие-либо данные. Передача данных между устройствами возможна только через входы-выходы, то есть через кривые, так как входы и выходы связаны с кривыми, теги и сообщения. При обмене данными между устройствами устройство - “писатель” записывает данные в базу данных, а устройство - “читатель” читает их из базы данных, причем запись и чтение асинхронны, а запись в кривые буферизована через механизм событий (см. описание putev).

Набор свойств или атрибутов устройства CRW-DAQ определяется его типом. Однако есть минимальный набор атрибутов, присущих всем устройствам:

  • Name - Имя идентифицирует устройство и служит для ссылок на устройство в файле конфигурации, в программах на встроенном языке DAQ PASCAL и т.д. Имя всегда преобразуется в верхний регистр, поэтому в описаниях и ссылках регистр символов имени устройства не играет роли. Имя должно быть уникальным в пределах данной конфигурации, объявление двух устройств с одним именем является ошибкой. По сложившейся традиции имя устройств начинается с символа &, хотя это и необязательно.
  • Family - Семейство характеризует принадлежность устройства к некоторому классу однородных по свойствам устройств. В настоящее время реализованы такие семейства устройств:
    • SOFTWARE - Это функциональные или программные устройства. Устройства этого класса не связаны с какой-то аппаратной спецификой. Чаще всего это устройства для преобразования данных, не связанные с аппаратурой, хотя они могут реализовывать и аппаратный драйвер. Например, к этому семейству относятся программы на встроенном языке DAQ PASCAL, устройства, реализующие диалоги и т.д.
    • ADAM - Это драйверы измерительных модулей, ориентированных на шину RS-485. Такие модули подключаются к последовательному порту RS-232 компьютера через преобразователь интерфейсов RS-232/RS-485. Использование разделяемого ресурса – последовательного порта и шины RS-485 – приводит к необходимости введения специальной дисциплины опроса этих устройств, с чем и связано их выделение в одно семейство. Например, к этому семейству относятся модули серии ADAM-4000 фирмы Advantec или модули серии I7000 фирмы ICP DAS.
    • PCL - Это драйверы измерительных карт, подключаемые к ISA-шине компьютера. Общая шина является разделяемым ресурсом для этих устройств, с чем и связано их выделение в одно семейство. Например, к этому семейству относятся PC Lab-карты фирмы Advantec.
  • Model - Модель характеризует принадлежность устройства к какому-то конкретному типу, например, модуль ADAM-4018, карта ввода PCL-818L и т.д. Тип устройства определяет его свойства, например, число цифровых или аналоговых входов или выходов, допустимые режимы набора и т.д. Поддерживаемые в настоящее время устройства приводятся в этом документе.
  • AnalogInputs - Это массив ссылок на кривые, через которые устройство получает аналоговые данные. Размер массива определяется типом устройства и в принципе не ограничен. С каждым аналоговым входом также связаны параметры сглаживания, позволяющие сглаживать данные в процессе набора. Индексация массива начинается с 0.
  • AnalogOutputs - Это массив ссылок на кривые, в которые устройство помещает аналоговые данные, полученные в результате работы устройства. Размер массива определяется типом устройства и в принципе не ограничен. С каждым аналоговым выходом также связаны параметры округления и длина хранимой истории, чтобы сократить объем хранимых данных. Индексация массива начинается с 0. В случае наличия мультиплексора второго уровня индексация двумерная (задается основной и дочерний канал).
  • DigitalInputs - Это массив ссылок на кривые, через которые устройство получает цифровые данные. Размер массива определяется типом устройства и в принципе не ограничен. С каждым цифровым входом также связан номер бита и флаг инверсии, чтобы указать, из каких битов формируются цифровые данные. Индексация массива начинается с 0.
  • DigitalOutputs - Это массив ссылок на кривые, в которые устройство помещает цифровые данные. Размер массива определяется типом устройства и в принципе не ограничен. Индексация массива начинается с 0.
  • AnalogFifo - Это буфер fifo для хранения аналоговых событий. При поступлении аналоговых данных они упаковываются в события и помещаются в fifo. Выборка fifo и диспетчеризация данных идет в фоновом режиме. Это позволяет легко реализовать сбор в разных режимах (программный опрос, прерывания, прямой доступ (DMA) и т.д.) единым образом.
  • DigitalFifo - Это буфер fifo для хранения цифровых событий. При поступлении цифровых данных они упаковываются в события и помещаются в fifo. Выборка fifo и диспетчеризация данных идет в фоновом режиме. Это позволяет легко реализовать сбор в разных режимах (программный опрос, прерывания, прямой доступ (DMA) и т.д.) единым образом.
  • InquiryPeriod - Это требуемый период опроса устройств в миллисекундах. Заметим, что реальный период опроса может отличаться от запрошенного, например, если аппаратура не может его обеспечить. Минимальный период опроса зависит от частоты переключения потоков в системе и составляет около 10 ms для Windows-NT/2000/XP.
  • Comment - Это строка до 255 символов с кратким описанием назначения устройства или другой информации для пользователя.
  • Calibration - Это массив калибровок, служащих для преобразования аналоговых сигналов. Калибровки хранятся в отдельных файлах с расширением *.cal, которые обычно помещаются в каталоге calibr. Конкретный смысл каждой калибровке в массиве калибровок зависит от типа устройства.

См. также reffind, refinfo.


Daq Pascal Api -> Кривая (curve)

Описание:

Кривая - один из основных объектов системы сбора данных CRW-DAQ. Кривая есть поименованный динамический буфер для хранения величин, для которых играет роль история их изменения. Кривую можно представить как динамический массив упорядоченных пар (x,y) длиной N, которые представляют дискретный аналог функции y(x). Кривая также содержит паспорт (комментирующий текст произвольного объема) и экранные атрибуты (цвет, стиль и т.д.).

Кривые описываются в конфигурационном файле и создаются системой при загрузке DAQ-конфигурации. Динамического создания кривых в процессе исполнения не предусмотрено.

См. также reffind, refinfo.


Daq Pascal Api -> Тег (tag)

Описание:

Тег - один из основных объектов системы сбора данных CRW-DAQ. Тег есть поименованная скалярная переменная для хранения величин, для которых не играет роль история их изменения.

Система CRW-DAQ поддерживает базу данных (список) тегов. Каждый тег имеет:

  • Имя - строка, идентифицирующая тег в конфигурациях и программах.
  • Тип - целое число, указывающее, на тип значения тега, 1=Integer, 2=Real, 3=String.
  • Значение - собственно содержимое тега.
  • Ссылку - Integer число, однозначно идентифицирующее тег в системе. В отличие от имени, ссылка может меняться от одного запуска программы к другому, она вообще имеет смысл только в момент выполнения программы. Ссылки используются для ускорения доступа к значению тега.

Теги описываются в конфигурационном файле и создаются системой при загрузке DAQ-конфигурации, в секции [TagList]. Например:

          [TagList]
          StartBtn = Integer 0
          Voltage  = Real    0.0
          WinName  = String  Кривые
         
Динамическое создание тегов в процессе исполнения также возможно (createtag/freetag), хотя надо пользоваться этим очень осторожно.

Обычно DAQ программы в начале выполнения инициализируют ссылку на тег по имени при помощи findtag, а затем используют эту ссылку для доступа к тегу, например, вызовом igettag/isettag. Использование ссылок позволяет сделать обращение к тегам весьма быстрым, в то время как имя тега используется для его идентификации при создании конфигураций и программ.

См. также findtag, reffind, refinfo.


Daq Pascal Api -> Текст (text)

Описание:

Текст - один из основных объектов системы сбора данных CRW-DAQ. Это упорядоченный набор строк, применяемый для работы с текстами. Строки нумеруются начиная с нуля и разделяются (при записи в файл) символами crlf. Строка представляет логический элемент текста и имеет (в текущей версии) длину до 255 символов ASCII. Число строк текста ограничено только доступной памятью.

Тексты создаются динамически вызовом text_new.

См. также refinfo.


Daq Pascal Api -> Задача (task)

Описание:

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

Задачи создаются вызовом task_init.

См. также reffind, refinfo.


Daq Pascal Api -> Поток опроса (polling)

Описание:

Поток опроса - один из основных объектов системы CRW-DAQ. Это контролируемый системой и сторожевым таймером программный поток, выполняющий какие-либо задачи периодического опроса (polling) в рамках процесса CRW-DAQ. Список потоков можно посмотреть в Главной Консоли командой @polling list или в окне Монитор Ресурсов.

Программные потоки создаются системой для общесистемных целей (например, поток System.Uart обслуживает COM-порты), а также для каждого устройства, канала или порта - для его обслуживания. Как правило, потоки опрашиваются автоматически, по таймеру, с определенным периодом. Для досрочного пробуждения потоков служит функция devsend(ref,''). Досрочное пробуждение может потребоваться, например, для ускорения реакции на какие-то события.

См. также reffind, refinfo.


Daq Pascal Api -> Объект DbApi для Баз Данных

Описание:

Это объекты connection, command, recordset, которые служат для работы с Базами Данных (СУБД) через набор функций DbApi.

Интерфейс DbApi дает универсальный доступ к распространенным СУБД, таким как Firebird, MySQL, MS SQL и другим - при условии установки необходимых клиентских драйверов.

См. также reffind, refinfo.


Daq Pascal Api -> Канал (pipe) Канал (tcp) Канал (com)

Описание:

Канал - один из основных объектов системы сбора данных CRW-DAQ. Это абстрактное устройство буферизованной приемо-передачи данных с дисциплиной доступа FIFO. Именованный сетевой канал pipe, канал связи через TCP сокет, COM - порт и задача task, связанная с CRW-DAQ анонимным каналом - различные реализации каналов. Каждый канал имеет передатчик (обозначаемый часто Tx от transmitter) и приемник (обозначаемый часто Rx от receiver).

В системе CRW-DAQ канальный ввод-вывод буферизован. Все функции pipe_xxxx для работы с каналами - неблокирующие, они просто помещают данные в FIFO буфер передатчика или берут их из FIFO буфера приемника. Собственно ввод-вывод осуществляет система в отдельных программных потоках.

Каналы создаются вызовом pipe_init.

См. также reffind, refinfo.


Daq Pascal Api -> Хеш-таблицы

Описание:

Хеш-таблицы - динамические массивы данных (списки) для хранения данных, индексируемых строковыми ключами (key). Обычные массивы индексируются целыми числами, однако не всегда это подходит для решения задач. Например, при работе с массивами кривых, окон, файлов, тегов и других поименованных объектов часто требуется быстрый доступ с индексацией по строковому ключу (обычно имени объекта). Для работы с такими структурами и служат хеш-таблицы HashList. Они обеспечивают быстрый доступ к данным по строковому ключу (имени) со скоростью, практически не зависящей от размера массива.

Согласно Википедии, Хэш-таблица или хеш-таблица — это структура данных, реализующая интерфейс ассоциативного массива, а именно, она позволяет хранить пары (ключ, значение) = (key,value) и выполнять три операции: операцию добавления новой пары, операцию поиска и операцию удаления пары по ключу.

В библиотеке DaqPascal реализованы хеш-таблицы, которые позволяют хранить в качестве значений записи из трех элементов - вещественного числа data, целого числа link и строкового параметра para, поэтому каждому ключу соответствует запись (key, data, link, para). Кроме того, реализованные хеш-таблицы позволяют независимо менять значения данных (data,link,para).

Хеш-таблицы создаются вызовом hashlist_init.

См. также reffind, refinfo.


Daq Pascal Api -> Таймер (timer)

Описание:

Таймер - один из основных объектов системы сбора данных CRW-DAQ. Этот объект позволяет организовывать циклические события в программах CRW-DAQ.

Таймеры создаются вызовом tm_new.

См. также reffind, refinfo.


Daq Pascal Api -> Окно (window)

Описание:

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

Окна описываются в конфигурационном файле и создаются системой при загрузке DAQ-конфигурации. Динамического создания окон в процессе исполнения не предусмотрено.

См. также reffind, refinfo.


Daq Pascal Api -> Регулярное выражение (regexp)

Описание:

RegExp - объект для обработки регулярных выражений в CRW-DAQ. Они полезны для поиска/замены строк в тексте и могут применяться при обработке потока ввода, чтении и анализе файлов, обработки данных в разных протоколах связи.

Регулярные выражения создаются вызовом regexp_init.

См. также refinfo.


Daq Pascal Api -> Конечные Автоматы (FSM)

Описание:

FSM - средства поддержки Конечных Автоматов для реализации алгоритмов управления на основе концепции Конечных Автоматов.

Конечные Автоматы создаются вызовом fsm_new.

См. также refinfo.


Daq Pascal Api -> Планирование

Описание:

Система CRW-DAQ предназначена для сбора данных в "мягком" реальном времени. По этой причине исключительное значение имеет понимание временных характеристик работы системы, а также способов настройки этих характеристик.

Прежде всего, планирование задач CRW-DAQ основано на механизмах планировщика потоков операционной системы. Поэтому полезно посмотреть файл об оптимизации Windows.

Программы Daq Pascal выполняются каждая в своем отдельном потоке. Потоки имеют различные приоритеты и частоты опроса. На частоту опроса также влияют внешние события. Для планирования опроса устройства CRW-DAQ в конфигурационном файле, в секции описания устройства задаются такие характеристики:

          [DeviceList]
          MyDevice = device software program
          [MyDevice]
          InquiryPeriod = aPeriod                       ; Задает номинальный период опроса
          DevicePolling = aDelay, aPriority             ; Задает период и приоритет потока опроса
         
Параметры aPeriod, aDelay, aPriority задают временные характеристики опроса устройства. Времена указаны в миллисекундах.

Цикл опроса программных устройств CRW-DAQ можно иллюстрировать таким псевдокодом:

          procedure DeviceThread.Execute;               // Поток опроса устройства (Polling)
          begin                                         // Программных устройств (DAQPascal)
           Priority:=aPriority;                         // Задаем приоритет потока
           while not Terminated do begin                // Выполняем цикл опроса с выходом по внешнему сигналу:
            WaitForSingleObject(aWakeEvent,aDelay);     // Ждем пробуждающее событие aWakeEvent или событие таймера
            if GotEvents(aPeriod) then Polling;         // Если есть события для обработки, выполняем опрос Polling
            ResetWatchDogTimer; Statistics;             // Сбрасываем Watchdog и накапливаем статистику
           end;
          end;
          function GotEvents(aPeriod):Boolean;          // Есть какие-нибудь события?
          begin                                         // Это может быть:
           Result:=InquiryTimer.Event(aPeriod)          // Событие таймера опроса с периодом aPeriod
                   or (ClickButton<>0)                  // Нажатие клавиатуры, сенсора на мнемосхеме
                   or (StdIn.Count>0)                   // Поступление данных в FIFO буфер консоли ввода
                   or aWakeEvent;                       // Пробуждение от внешнего события, например, devSend()
          end;                                          // Тогда TRUE - сигнал о наличии событий для обработки
         

Параметр aPeriod задает номинальный период опроса устройства Polling в миллисекундах. Этот период, например, нужен для устройств, которые не имеют своего собственного программного потока. Так, он определяет период опроса устройств ADAM, которые обслуживают обмен через шину RS-232/485 и выполняются в потоке, обслуживающем все устройства ADAM. Следует учитывать, что (в силу свойств функций времени) aPeriod квантуется величиной около 10 ms для одноядерных или 15 ms для многоядерных систем. При этом, например, указание aPeriod=1 или aPeriod=10 оказываются эквивалентными. Это может привести к тому, что опрос будет идти существенно реже ожидаемого. Поэтому для программных устройств часто используется значение aPeriod=0. В этом случае выполнение программы будет происходить при каждой активизации потока, связанного с устройством, т.е. с периодом aDelay. Если точнее, активизация потока программы будет происходить либо по таймеру с периодом aDelay, либо досрочно, по событию WakeEvent, нажатию сенсора, поступлению данных в консоль ввода. Во всяком случае, для устройств, которые работают по внешним событиям, то есть обрабатывают сообщения, поступающие в консоль ввода, иногда есть смысл устанавливать aPeriod=0, чтобы событие было обработано как можно скорее.
Обычно нет смысла использовать значения aPeriod > aDelay, так как в этом случае поток будет активизироваться чаще, чем надо для опроса устройства. В некоторых случаях, однако, имеет смысл использовать aPeriod > aDelay, так как при каждой активизации поток, как минимум, сбрасывает сторожевой таймер ResetWatchDogTimer и набирает статистику. Сторожевой таймер используется для регистрации подвисания потоков. Поэтому, например, если устройство должно опрашиваться редко, скажем, раз в 10 секунд, то установка значения aDelay=10000 привела бы к активизации сторожевого таймера . В этом случае лучше задать параметры aDelay=1000,aPeriod=10000. Это значит, раз в секунду поток будет активизироваться и сбрасывать сторожевой таймер, а раз в 10 секунд будет также выполняться опрос.

Параметр aDelay задает период активизации потока опроса устройства в миллисекундах. Этот параметр применим только для устройств, которые имеют свой собственный программный поток. С целью снижения загрузки процессора рекомендуется регулировать частоту опроса именно этим параметром, установив aPeriod=0. Тогда не будет ненужных для опроса активизаций потока устройства. Надо учитывать, что реальная частота опроса определяется (в обычном состояниии) квантом времени Windows, который составляет для Win-NT/2K/XP/7/8/10 около 10 ms для одноядерной и 15 ms для многоядерной системы. Это значит, при задании 1 или 10 миллисекунд период опроса будет один и тот же - 10 миллисекунд. При этом частота опроса может быть увеличена до 1 kHz путем задания специального таймера. Это можно сделать консольной командой Главной Консоли SetClockRes(p), где p - желаемый период опроса, ms. Эту команду можно вызвать и через функцию Eval:

Eval('@System @Async SetClockRes(1)'); // Установить период опроса Windows 1 ms

Параметр aPriority задает приоритет потока и влияет на гарантию его своевременной активизации. Параметр принимает значения

  • tpIdle - самый низший приоритет. Рекомендуется для вычислительных потоков, которые выполняют длительные вычисления. Чтобы они не мешали выполнению других потоков, им дается самый низший приоритет. Низкоприоритетный поток будет выполняться, только пока система не занята. При высокой загрузке процессора выполнение потока может задерживаться на неопределенное время.
  • tpLowest - низкий приоритет.
  • tpLower - приоритет ниже среднего.
  • tpNormal - средний приоритет. Такой приоритет имеет основной поток программы, где выполняются команды пользователя и отображение графики.
  • tpHigher - выше среднего.
  • tpHighest - высокий.
  • tpTimeCritical - самый высокий. Потоки этого приоритета обязаны освобождать процессор в самые минимальные сроки. Если для низкоприоритетных потоков циклы ожидания не рекомендуются, то для высокоприоритетных потоков они просто запрещены, так как могут подвесить систему. Потоки с приоритетом tpTimeCritical с высокой вероятностью будут выполняться строго периодически, примерно каждые 10 миллисекунд. При высокой загрузке процессора выполнение высокоприоритетного потока будет также с большой вероятностью происходить вовремя.

Сама процедура опроса Polling должна быть быстрой. Длительные процессы надо отслеживать не замкнутым циклом while ... do ..., а путем разбиения обработки на этапы, выполняемые при многократном входе в процедуру в разное время. Это требует введения переменных состояния измеряемого процесса, значение которых между вызовами сохраняется. Вызов WaitForSingleObject(aWakeEvent,aDelay) гарантирует, что в течение указанного времени поток не будет активизирован, если не возникает внешнего события aWakeEvent. Поток будет находиться в спящем состоянии, то есть не будет отнимать процессорное время, освобождая его для других потоков.

Событие aWakeEvent введено для досрочного выполнения программы в случае возникновения внешнего события. Таким событием может быть:

  • Щелчек мышью на сенсоре мнемосхемы, связанном с данным устройством.
  • Посылка сообщения devSendMsg(...) устройству. При посылке сообщения его текст помещается в буфер консоли устройства - приемника, а потом активизируется событие аWakeEvent, чтобы программа могла как можно скорее выполнить обработку посланого сообщения.
  • Ввод данных через консольное окно устройства. Вводимый текст помещается в буфер консоли устройства, а потом активизируется событие аWakeEvent, чтобы программа могла как можно скорее выполнить обработку введенных данных.

Windows обеспечивает, по крайней мере высокоприоритетным потокам, возобновление работы во время очередного кванта времени. При выборе времен надо учесть, что квант времени Windows в нормальном состоянии составляет около 10 ms для одноядерной и 15 ms для многоядерной системы. Это значит, при задании 1 или 10 миллисекунд период опроса будет один и тот же - 10 миллисекунд. При этом частота опроса может быть увеличена до 1 kHz путем задания специального таймера. Это можно сделать консольной командой Главной Консоли SetClockRes(p), где p - желаемый период опроса, ms.

Не следует делать высокоприоритетными все потоки устройств CRW-DAQ. Отнюдь не все устройства требуют жесткого времени опроса. Лучше дать им низкий приоритет, чтобы облегчить работу тех устройств, где режим времени действительно жесткий.

Система CRW-DAQ имеет консоль монитора ресурсов , через которую можно наблюдать за работой всех потоков программы.

Пример:

          [FastDevice]                                  ; Обработка быстрых измерений
          InquiryPeriod = 0                             ; Опрос при каждой активизации потока, т.к. aPeriod=0
          DevicePolling = 1, tpTimeCritical             ; Частота активизации потока до 1 кГц

          [HandlerDevice]                               ; Обработка сообщений/событий
          InquiryPeriod = 1                             ; Опрос при каждой активизации потока, т.к. aPeriod << aDelay
          DevicePolling = 1000, tpNormal                ; Раз в секунду или по событиям

          [BackgroundDevice]                            ; Обработка данных в фоновом режиме
          InquiryPeriod = 1                             ; Опрос при каждой активизации потока, т.к. aPeriod << aDelay
          DevicePolling = 100, tpIdle                   ; Низкий приоритет для фоновых операций

          [SlowDevice]                                  ; Обработка данных раз в минуту
          InquiryPeriod = 60000                         ; Задаем длительный период таймера опроса, aPeriod >> aDelay
          DevicePolling = 1000, tpNormal                ; Но раз в секунду все равно надо сбросить Watchdog
         

Daq Pascal Api -> Переменные окружения

Описание:

При старте CRW32.EXE устанавливает набор переменных окружения, содержащих сведения о себе, любимом. Это нужно для того, чтобы программы DaqPascal, а также внешние программы, вызываемые через функции task_xxx, могли легче взаимодействовать с основным процессом. Переменные окружения доступны через вызов getenv.
  • CRW_DAQ_SYS_HOME_DIR - домашний каталог CRW32.EXE, например, c:\crw32exe.
  • CRW_DAQ_SYS_EXE_FILE - полное имя файла CRW32.EXE, например, c:\crw32exe\crw32.exe.
  • CRW_DAQ_SYS_INI_FILE - полное имя файла CRW32.INI, например, c:\crw32exe\crw32.ini. Зная имя файла настроек, можно прочитать множество системных параметров CRW-DAQ.

  • CRW_DAQ_SYS_PATH - список путей поиска библиотеки утилит CRW-DAQ. Он содержит разделенный точкой с запятой список путей, в которых содержатся разные полезные утилиты. Эту переменную можно применять для поиска утилит по короткому имени. Например, в командных файлах можно применять поиск типа:
                rem Найти hidcon.exe в библиотеке утилит, в списке путей поиска CRW_DAQ_SYS_PATH
                for %%i in ( hidcon.exe ) do ( set hidcon=%%~$CRW_DAQ_SYS_PATH:i)
                if -%hidcon% == - ( echo Не найден ) else ( echo Найден %hidcon%)
                
    Кроме того, можно использовать эту переменную с помощью paramstr:
                hidcon:=ParamStr('FileSearch hidcon.exe CRW_DAQ_SYS_PATH');
                
  • CRW_DAQ_SYS_LANG - идентификатор языка:
                CRW_DAQ_SYS_LANG=RUSSIAN,CP1251,CP866,$419,$419,$419,$419
                                    \       \      \    \    \    \    \__ GetUserDefaultLCID     - идентификатор локали пользователя
                                     \       \      \    \    \    \______ GetUserDefaultLangID   - идентификатор языка  пользователя
                                      \       \      \    \    \__________ GetSystemDefaultLCID   - идентификатор локали системы
                                       \       \      \    \______________ GetSystemDefaultLangID - идентификатор языка  системы
                                        \       \      \__________________ OEM  CodePage - кодовая страница консоли DOS
                                         \       \________________________ ANSI CodePage - кодовая страница программ ANSI
                                          \_______________________________ Язык интерфейса CRW-DAQ: (RUSSIAN,ENGLISH)
                
    Позволяет дочерним программам узнать язык интерфейса процесса CRW32.EXE.

  • CRW_DAQ_SYS_EXE_PID - идентификатор процесса CRW32.EXE. Например, автономная программа, вызванная через task_run, может узнать, используя PID (Process ID), жив ли еще родительский процесс и завершить работу, если родительский процесс уже завершен.

  • CRW_DAQ_SYS_EXE_TID - идентификатор основного потока CRW32.EXE. Например, автономная программа, вызванная через task_run, может посылать, используя TID (Thread ID), сообщения родительскому процессу.

  • CRW_DAQ_SYS_CLASS - имя класса главного окна (в данной версии - константа TFormCrw32).
  • CRW_DAQ_SYS_TITLE - текст заголовка главного окна (формируется при старте из идентификатора процесса).
  • CRW_DAQ_SYS_HANDLE - идентификатор главного окна (целое число, используется для посылки сообщений).
  • CRW_DAQ_SYS_MESSAGE - уникальное сообщение MsgId, связанное с данным экземпляром CRW32.EXE. При посылке этого сообщения через вызов типа:
    var BSMR:DWORDS;
    BSMR:=BSM_APPLICATIONS;
    BroadCastSystemMessage(BSF_IGNORECURRENTTASK or BSF_POSTMESSAGE, @BSMR, MsgId, 0, 0);

    приложение CRW32.EXE активизируется. Это чтобы дочерняя программа могла активизировать родительское приложение.

  • CRW_DAQ_CONFIG_FILE - полное имя текущего конфигурационного файла CRW-DAQ.

  • CRW_DAQ_CONFIG_HOME_DIR - домашний каталог текущего конфигурационного файла CRW-DAQ.

  • CRW_DAQ_CONFIG_PATH - список путей поиска текущей конфигурации DAQ-системы. Он содержит разделенный точкой с запятой список путей, в которых содержатся различные файлы текущей конфигурации. Эту переменную можно применять для поиска файлов по короткому имени. Например, в командных файлах можно применять поиск типа:
                rem Найти hidcon.exe в списке путей поиска CRW_DAQ_CONFIG_PATH
                for %%i in ( hidcon.exe ) do ( set hidcon=%%~$CRW_DAQ_CONFIG_PATH:i)
                if "%hidcon%" == "" ( echo Не найден ) else ( echo Найден %hidcon%)
                
    Кроме того, можно использовать эту переменную с помощью paramstr:
                hidcon:=ParamStr('FileSearch hidcon.exe CRW_DAQ_CONFIG_PATH');
                
  • CRW_DAQ_INCLUDE_PATH - список путей поиска включаемых файлов стандартной библиотеки включаемых файлов.

  • CRW_DAQ_CONFIG_DATA_PATH - полный путь текущего каталога данных CRW-DAQ.

  • CRW_DAQ_CONFIG_TEMP_PATH - полный путь текущего временного каталога CRW-DAQ.

  • CRW_DAQ_CONFIG_LOAD_TIME - время загрузки текущей конфигурации CRW-DAQ в миллисекундах.

  • CRW_DAQ_CONFIG_BASE_TIME - базовое время текущей конфигурации CRW-DAQ в миллисекундах.

  • CRW_DAQ_CONFIG_TIME_UNITS - единицы времени текущей конфигурации CRW-DAQ в миллисекундах.

  • CRW_DAQ_CONFIG_START_TIME - время запуска текущей конфигурации CRW-DAQ в миллисекундах.

Daq Pascal Api -> URL-кодирование строк

URL кодировка (от Universal Resource Location) была первоначально создана для кодирования имен Web. Однако ее можно успешно использовать для других случаев, когда надо передавать произвольные двоичные данные по текстовому каналу связи.

URL кодировка применяется в случаях когда

  • Строка (например, имя файла) содержит пробелы или непечатные символы, но должна представляться одним словом, состоящим из печатных символов.
  • Строка (например, небольшой текст) содержит непечатные символы (например, перенос строки), но должен представляться текстом, состоящим только из печатных символов (например, для передачи по каналу связи).

URL кодировка очень проста. Для кодирования текста в URL кодировку:

  1. непечатные символы надо заменить на %hh, где hh - двухсимвольный HEX-код символа
  2. символ + надо заменить на %2b
  3. символ % надо заменить на %% или %25
  4. символ пробела можно заменить на + или на %20
  5. остальные символы не меняются
Например,
    текст:

         Строка 1
         Строка 2+
         Строка 3%

    может быть записан строками

         Строка 1%0d%0aСтрока 2%2b%0d%0aСтрока 3%25

    или

         Строка+1%0d%0aСтрока+2%2b%0d%0aСтрока+3%%
   

Заметьте, что обязательно надо заменять CR на %0d, LF на %0a, символ % на %% или %25, символ + на %2b, а пробел можно заменить на + или %20. Остальные символы нет необходимости менять, они будут прочитаны "как есть".

Замена пробелов на + или %20, табуляций на %09, хотя она необязательна, имеет то достоинство, что при этом строку, содержащую пробелы и табуляции, можно представить одним непрерывным словом, что иногда существенно (например, если слово служит элементом списка). Это позволяет передавать в утилиты, содержащие список параметров - слов, произвольные строки, не нарушая при этом логики разбора слов. По этой причине URL кодировка часто используется для задания списка параметров в файлах конфигураций.

Кодировку текста в URL кодировку можно сделать вызовом URL_Packed(Text) или URL_Encode(Text).

Исходный текст, заданный URL кодировкой можно получить вызовом URL_Decode(Text).

Декодер URL_Decode(Text) заменяет символ + на пробел, строку %% на символ %, строку %hh на символ chr(hh), где hh - шестнадцатеричный код символа. Никакие другие символы строки Text декодер не меняет. Поэтому URL-кодированные строки удобно применять в случае, когда надо передавать в процедуры\функции список строковых параметров, разделенных пробелами, но при этом сами параметры могут содержать пробелы. Если параметр не содержат пробелов и символов +,% - можно передавать его "как есть", это наиболее частый случай. Если указанные символы есть, применяется функция URL_Packed(Text) или URL_Encode(Text). Таким образом, в большинстве случаев можно передавать URL-строки без всякого изменения.


Daq Pascal Api -> char sizeofchar boolean sizeofboolean integer sizeofinteger real sizeofreal string length

Определение:

type char - символьный тип размера sizeofchar
type boolean - логический тип размера sizeofboolean
type integer - целый тип размера sizeofinteger
type real - вещественный тип размера sizeofreal
type string - строка символов длиной length

Описание:

char - символьный тип данных. Фактически это 8-битный байт, интерпретируемый как ASCII код символа. Принимает значения chr(0)..chr(255). Константа sizeofchar возвращает размер типа char в байтах (1).
boolean - логический тип данных. Принимает значения FALSE,TRUE. Константа sizeofboolean возвращает размер типа boolean в байтах (1).
integer - целочисленный тип данных. В настоящей версии это 32-битное целое. Принимает значения от -2147483648 до 2147483647. Константа sizeofinteger возвращает размер типа integer в байтах (4).
real - вещественный тип данных. В настоящей версии это 64-битное вещественное, то есть double. Константа sizeofreal возвращает размер типа real в байтах (8).
string - строковый тип данных. Все строки Daq-Pascal - динамические, то есть память под них выделяется по мере необходимости. Длина строки length в текущей версии может достигать slenmax, однако многие строковые функции реально работают с строками не более 255 символов.

Надо иметь в виду, что изначально строки не инициализированы и не могут быть использованы для получения значения, пока им не будет что-нибудь присвоено. При попытке использования неинициализированной строки генерируется ошибка и выполнение Daq-Pascal программы завершается. Чтобы избежать этого, надо придерживаться таких правил:

  • Строки, объявленные в основной программе, должны быть проинициализированы при первом старте программы, то есть когда runcount=1. Инициализация производится присвоением строке пустой строки, строковой константы или значения строковой функции.
  • Строки, объявленные в подпрограмме, должны быть проинициализированы перед первым использованием в этой подпрограмме. Инициализация производится присвоением строке пустой строки, строковой константы или значения строковой функции.
  • Строки, передаваемые как аргументы функции, не нуждаются в инициализации.

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

  • Строки, объявленные в основной программе, должны быть освобождены при завершающем вызове программы, то есть когда isinf(runcount)=true. Инициализация производится присвоением строке пустой строки.
  • Строки, объявленные в подпрограмме, должны быть освобождены перед выходом из подпрограммы. Освобождение производится присвоением строке пустой строки.
  • Строки, передаваемые как аргументы функции, не нуждаются в освобождении.
Таким образом, работа со строками выглядит примерно так:
      program example;
         var s1:string;
         procedure DoSomething;
         var s2:string;
          begin
          s2:='';       {initialize s2}
          writeln(s2);  {use s2}
          s2:='';       {finalize s2}
         end;
         begin
          if runcount=1 then begin
           s1:='';      {initialize s1}
          end else
          if isinf(runcount) then begin
           s1:='';      {finalize s1}
          end else begin
           writeln(s1); {use s1}
          end;
         end.
         

Пример:

          var c:char; b:boolean; i,j:integer; x:real; s:string;
          begin
           for i:=48 to 57 do begin c:=chr(i); writeln(i,'->',c); end;
           i:=1; j:=2; b:=(i=j); {b=false}
           i:=1; j:=1; b:=(i=j); {b=true}
           for i:=1 to 100 do writeln(i);
           x:=exp(pi/2); writeln(x);
           s:='';
           readln(s);
           writeln('Read:',s);
           s:='';
          end;
         

Daq Pascal Api -> record

Определение:

type x=record field1:type1; field2:type2; ... fieldN:typeN; end

Описание:

Записные типы данных - один из способов генерации новых типов данных в Pascal.
field1..fieldN - имена полей
type1..typeN - типы полей
Доступ к полям записи - x.field1, x.field2, ... x.fieldN.

Пример:

          type point=record x:real; y:real; end;
          var p:point;
          begin
           p.x:=1;
           p.y:=2;
           writeln(p.x,p.y);
          end;
         

Daq Pascal Api -> array

Определение:

type x=array[min..max] of typex;

Описание:

Массивные типы данных - один из способов генерации новых типов данных в Pascal.
min..max - диапазон индексов
typex - тип элементов массива
Доступ к элементам массива - x[i].

Пример:

          type
           point=record x:real; y:real; end;
           points=array[0..9] of point;
          var i:integer; p:points;
          begin
           for i:=0 to 9 do begin
            p[i].x:=1;
            p[i].y:=2;
            writeln(p[i].x,p[i].y);
           end;
          end;
         

Daq Pascal Api -> + - * / div mod () [] not ~ and & or | := = <> < <= > >= : ; {},(**),//

Описание:

  • a + b возвращает сумму чисел a,b.
  • s + t сцепляет строки s,t.
  • a - b возвращает разность чисел a,b.
  • a * b возвращает произведение чисел a,b.
  • a / b возвращает результат деления a на b.
  • i div j возвращает результат целочисленного деления i на j.
  • i mod j возвращает остаток целочисленного деления i на j.
  • () круглые скобки меняют порядок вычислений. При отсутствии скобок вычисления идут в соответствии с приоритетом. Сначала выполняются операции с более высоким приоритетом, потом с более низким.
  • [] квадратные скобки служат для индексации элементов массивов.
  • not b, ~b логическое отрицание логического выражения b.
  • a and b, a & b логическое И логических выражений a,b.
  • a or b, a | b логическое неисключающее ИЛИ логических выражений a,b.
  • a := b присваивает переменной a значение выражения b. Переменная должна быть совместима по типу с выражением. Тип переменной совместим с типом выражения, если эти типы совпадают, например, real:=real, либо если тип выражения является подмножеством типа переменной, например, real:=integer. Если тип выражения является подмножеством типа переменной, при присвоении выполняется неявное преобразование типа.
  • a = b проверяет "а равно b".
  • a <> b проверяет "а не равно b".
  • a < b проверяет "а меньше b".
  • a <= b проверяет "а меньше или равно b".
  • a > b проверяет "а больше b".
  • a >= b проверяет "а больше или равно b".
  • a : b объявляет переменную a типа b.
  • a ; b отделяет оператор a от оператора b.
  • { a } комментарий a, игнорируется компилятором.
  • (* a *) комментарий a, игнорируется компилятором.
  • // a комментарий a (до конца текущей строки), игнорируется компилятором.

Пример:

          x:=(a+b)*c/d-e;
          s:='one'+' '+'two';
          z[i,j]:=x;
          if( (i>1) and (i<10) ) then ...
         

Daq Pascal Api -> begin end

Описание:

Операторы begin .. end - операторные скобки, аналогично скобкам {} языка C. Они нужны для того, чтобы:

  • Обозначить тело (исполняемый код) программы, процедуры или функции.
  • Обозначить тело (исполняемый код) составного оператора. Дело в том, что многие операторы, такие как if .. then .. else .., предполагают выполнение одного оператора. Если действий надо сделать больше, вместо одного оператора подставляют составной оператор. Составной оператор - группа операторов, помещенных в операторные скобки begin .. end. Составной оператор, с точки зрения внешнего по отношениею к нему кода, ведет себя как один оператор.

Операторные скобки begin и end, как и обычные скобки, всегда идут в паре. Нескомпенсированность операторных скобок (разное число begin и end) является ошибкой.

Пример:

          procedure max(a,b:integer; var c:integer);
          begin
           if a>b then begin
            c:=a;
            writeln(c);
           end;
          end;
         

Daq Pascal Api -> program

Описание:

program name; - Оператор объявления программы с именем name. Имя программы доступно в тексте программы через вызов функции progname.

В языке DAQ PASCAL программы имеют жесткую структуру:

          program name;
          const ... объявление констант ...
          type  ... объявление констант ...
          var   ... объявление переменных ...
           procedure ... объявление процедур и функций ...
           function  ... объявление процедур и функций ...
          begin
           ... тело программы ...
          end.
         
Описания const, type, var могут отсутствовать, однако их порядок фиксирован. Описания процедур или функций могут чередоваться, но должны идти после констант, типов и переменных.

Особенностью DAQ программ является то, что

  • Они выполняются не однократно, а многократно, то есть вызываются планировщиком в режиме опроса до 100 раз в секунду. Частота опроса задается переменными InquiryPeriod и DevicePolling, см. awakeflag, wdt_reset.
  • Все глобальные (то есть объявленные в основной программе) переменные типа Integer, Real при первом вызове имеют значения 0, а переменные типа String не инициализированы.
  • При последующих вызовах все глобальные (то есть объявленные в основной программе) переменные сохраняют значения между вызовами. Это свойство используется практически во всех программах сбора и обработки данных Daq Pascal. Его ценность в том, что как раз для задач реального времени характерно то, что они обычно отслеживают изменения состояния измеряемых объектов во времени, поэтому сохранение предыдущего состояния переменных в таких задачах просто необходимо.

Пример:

          program test;
          const
           msg = 'c = ';
          type
           abc : record a,b,c:integer;
          var
           x : abc;
           {Пример процедуры}
           procedure pmax(a,b:integer; var c:integer);
           begin
            if a>b then c:=a else c:=b;
            writeln(msg,c);
           end;
           {Пример рекурсивного вызова}
           function gamma(n:integer):integer;
           begin
            if n=1 then gamma:=1 else gamma:=n*gamma(n-1);
           end;
          begin
           x.a:=1;
           x.b:=2;
           pmax(x.a,x.b,x.c);
           writeln(gamma(10));
          end.
         

Daq Pascal Api -> procedure function

Описание:

procedure p(arg); - Оператор объявления процедуры с именем p и списком параметров arg.

function f(arg):t; - Оператор объявления функции типа t с именем f и списком параметров arg. Функция обязательно должна содержать присвоение f:=...; иначе результат функции будет неопределенным.

Список параметров arg = [var] x1:t1; [var] x2:t2; ... содержит имена параметров x1,x2,... типа t1,t2,..., разделенные точкой с запятой. Параметры могут передаваться по значению и по ссылке. Параметры, передаваемые по значению, не содержат объявление var. При передаче этих параметров в стек записывается текущее значение параметра в момент вызова. Вся работа идет с этой локальной копией данных, так что значение внешней переменной не изменяется. Параметры, передаваемые по ссылке, содержат объявление var. При передаче этих параметров в стек записывается адрес переменной, где хранится параметр. Вся работа идет с внешней переменной, на которую ссылается адрес, поэтому значение переменной после вызова в общем случае изменяется.

Допустим рекурсивный вызов процедур и функций. Уровень рекурсии ограничен размером стека программы.

В языке DAQ PASCAL подпрограммы имеют жесткую структуру:

          procedure p(x:real; var s:string);
          const ... объявление констант ...
          type  ... объявление констант ...
          var   ... объявление переменных ...
           procedure ... объявление процедур и функций ...
           function  ... объявление процедур и функций ...
          begin
           ... тело подпрограммы ...
          end;
         
Описания const, type, var могут отсутствовать, однако их порядок фиксирован. Описания "вложенных" процедур или функций могут чередоваться, но должны идти после констант, типов и переменных.

Пример:

см. program

Daq Pascal Api -> type var const

Описание:

type t:expr; - Оператор объявления нового типа t, заданного выражением expr. Выражение содержит базовые типы char, integer, real, string, а также конструкции array, record, позволяющие создавать новые типы данных.

var x1:t1; x2:t2; ... - Оператор объявления переменных x1,x2,... типа t1,t2,.... Объявления разделяются точкой с запятой, возможно перечисление нескольких переменных одного типа, то есть x1,x2:t1. Возможно также неявное определение типа переменной, например:

          var x,y:array[0..10] of integer;
         

const c1 = v1; c2 = v2; ... - Оператор объявления констант c1,c2,... со значением v1,v2,.... Тип константы определяется автоматически, по значению. Объявления разделяются точкой с запятой.

Целочисленные константы могут задаваться

  • в обычной десятичной (decimal) нотации, например 12345;
  • в шестнадцатеричной (hexadecimal) нотации, например $1FE;
  • в восьмеричной (octal) нотации, например &755;
  • в двоичной (binary) нотации, например %10101.
Символы ($,&,%) служат признаком констант по основанию (16,8,2) соответственно.
При этом константы не должны превышать диапазон целых чисел (integer).

В языке DAQ PASCAL объявления констант, типов и переменных имеют жесткую структуру, см. program.

Пример:

см. program

Daq Pascal Api -> true false sizeofchar sizeofreal sizeofboolean sizeofinteger daq_cmd_init daq_cmd_free daq_cmd_poll tag_ref_min tag_ref_max task_ref_min task_ref_max rfreplaceall rfignorecase quotemark apostrophe isunix islinux iswindows cpubitness sizeofpointer defaultsystemcodepage cp_utf8 cp_none cp_1251 cp_866

Описание:

В языке DAQ PASCAL есть ряд предопределенных констант:

    Имя                     Тип     Значение        Комментарий
    ===========================================================
    true                    boolean истина          и так ясно
    false                   boolean ложь            и так ясно
    sizeofchar              integer (1)             размер char
    sizeofreal              integer (8)             размер real
    sizeofboolean           integer (1)             размер boolean
    sizeofinteger           integer (4)             размер integer
    daq_cmd_init            integer (1)             см. daqdllinit
    daq_cmd_free            integer (2)             см. daqdllinit
    daq_cmd_poll            integer (3)             см. daqdllinit
    TAG_REF_MIN             integer (runtime)       нижний  диапазон ссылок тегов
    TAG_REF_MAX             integer (runtime)       верхний диапазон ссылок тегов
    TASK_REF_MIN            integer (runtime)       нижний  диапазон ссылок task
    TASK_REF_MAX            integer (runtime)       верхний диапазон ссылок task
    rfReplaceAll            integer (1)             см. StringReplace
    rfIgnoreCase            integer (2)             см. StringReplace
    QuoteMark               char    (")             двойные кавычки, определенные  в Unicode как "Quotation Mark"
    Apostrophe              char    (')             одинарные кавычки, определенныые в Unicode как "Apostrophe"
    IsUnix                  boolean (runtime)       работает OS Unix?        (для поддержки многоплатформенности)
    IsLinux                 boolean (runtime)       работает OS Linux?       (для поддержки многоплатформенности)
    IsWindows               boolean (runtime)       работает OS Windows?     (для поддержки многоплатформенности)
    CpuBitness              integer (runtime)       разрядность CPU, 32/64   (для поддержки многоплатформенности)
    SizeOfPointer           integer (runtime)       размер указателя, байт   (для поддержки многоплатформенности)
    defaultsystemcodepage   integer (runtime)       кодовая страница системы (для поддержки многоплатформенности)
    CP_UTF8                 integer (65001)         кодовая страница UTF8
    CP_NONE                 integer (65535)         кодовая страница отсутствует
    1251                    integer (1251)          кодовая страница windows-1251
    866                     integer (866)           кодовая страница dos-866

    Примечание:
     1) Константы, помеченные как runtime, определяются на этапе выполнения.
     2) Ряд других констант (например, EOL), реализованы в виде функций.
     3) Ряд системных констант и параметров доступен через ParamStr.
     4) Ряд констант введен для поддержки многоплатформенности. Например, IsUnix,IsWindows.
     5) SizeOfPointer равно 4/8 на 32/64 битных платформах.
         

Daq Pascal Api -> if then else case

Описание:

if c then a else b; - Оператор ветвления или условный оператор. Если логическое выражение c истинно, выполняется оператор a, иначе выполняется оператор b. В качестве выполняемых операторов можно использовать пустой оператор (нет оператора) или составной оператор begin .. end. Надо иметь в виду, что при вычислении логического выражения вычисления всегда идут полностью, например при выполнении if f(x) or g(x) then ... вызов g(x) произойдет, даже если f(x)=true, хотя это, строго говоря избыточно, так как значение выражения уже определено.

case n of n1:a1; n2:a2; .. end; - Оператор альтернативы (выбора) из списка значений. Выполняет операторы a1,a2,.., если значение целочисленного выражения n равно n1,n2,.. В качестве значения n1,n2,.. допустимо использовать перечисление (через запятую) вида n1,n2,n3. В Daq Pascal оператор case имеет особенности:

  • Допустимо не более 256 альтернатив.
  • Не поддерживается альтернатива else.
  • Поскольку диапазоны (вида n..m) не поддерживаются, их надо заменять явным перечислением, например, вместо диапазона 1..4 использовать перечисление 1,2,3,4.
  • Если значение n не равно одной из альтернатив, выполение программы продолжается с оператора, следующего после case. То есть в этом случае case ничего (кроме проверки значения) не делает.
  • Проверка альтернатив идет путем линейного поиска в порядке, указанном в коде программы, до первого найденного совпадения. Поэтому с точки зрения производительности наиболее частые (типичные) случаи (альтернативы) лучше указывать первыми.

Пример:

          if a>b then x:=a else x:=b;
          case i of
           0,1,2,3,4,5,6,7,8,9: Make1;
           10,11,12,13,14,15,16,17,18,19: Make2;
           20: Make3;
          end;
         

Daq Pascal Api -> for to downto do while repeat until

Описание:

while с do p; - Оператор цикла с пред-условием. Выполняет оператор p, пока истинно условие c. Обычно оператор p содержит вычисления, влияющие на значение условия c, иначе выхода из цикла не произойдет. Заметим, что оператор p не будет выполнен ни разу, если условие c изначально не выполнено.

repeat p until c; - Оператор цикла с пост-условием. Выполняет оператор p, затем проверяет условие c. Если оно истинно, прерывает цикл, если ложно - продолжает итерации. Обычно оператор p содержит вычисления, влияющие на значение условия c, иначе выхода из цикла не произойдет. Заметим, что оператор p будет выполнен хотя бы один раз, даже если условие c изначально выполнено.

for n:=n1 to n2 do p; - Оператор восходящего цикла. Присваивает переменной цикла n значение n1, затем, пока n<=n2, выполняет в цикле оператор p, увеличивая счетчик цикла в конце каждой итерации на 1. Это аналог выражения

          n:=n1; while n<=n2 do begin p; n:=n+1; end;
         
В случае n1>n2 оператор p не будет выполняться совсем.

for n:=n2 downto n1 do p; - Оператор нисходящего цикла. Присваивает переменной цикла n значение n2, затем, пока n>=n1, выполняет в цикле оператор p, меньшая счетчик цикла в конце каждой итерации на 1. Это аналог выражения

          n:=n2; while n>=n1 do begin p; n:=n-1; end;
         
В случае n1>n2 оператор p не будет выполняться совсем.

Пример:

          for i:=1 to 10 do writeln(i);
          for i:=10 downto 1 do writeln(i);
          i:=1; while i<=10 do begin writeln(i); i:=i+1; end;
          i:=1; repeat writeln(i); i:=i+1; until i=10;
         

Daq Pascal Api -> abs

Определение:

function abs(x:real):real
function abs(x:integer):integer

Аргументы:

  • x - целое или вещественное число.

Результат:

Возвращает абсолютное значение числа x.

Описание:

Возвращает абсолютное значение числа x.

Пример:

          var x,y:real;
          begin
           x:=random(-1,1);
           y:=abs(x);
           writeln('x=',x,' y=',y);
          end;
         

Daq Pascal Api -> sqr

Определение:

function sqr(x:real):real
function sqr(x:integer):integer

Аргументы:

  • x - целое или вещественное число.

Результат:

Возвращает квадрат числа x.

Описание:

Возвращает квадрат числа x, эквивалентно sqr(x)=x*x.

Пример:

           x:=sqr(2); {x=4}
         

Daq Pascal Api -> sqrt

Определение:

function sqrt(x:real):real

Аргументы:

  • x - подкоренное выражение, вещественное число.

Результат:

Возвращает квадратный корень числа x.

Описание:

Возвращает квадратный корень числа x, или NAN для отрицательных значениях x.

Пример:

           y:=sqrt(100); {y=10}
         

Daq Pascal Api -> odd

Определение:

function odd(x:integer):boolean

Аргументы:

  • x - целое число.

Результат:

Возвращает true, если число x нечетное.

Описание:

Возвращает true, если число x нечетное или false, если четное.

Пример:

          var i:integer;
          begin
           for i:=1 to 10 do writeln('i=',i,' odd=',odd(i));
          end;
         

Daq Pascal Api -> chr

Определение:

function chr(x:integer):char

Аргументы:

  • x - целое число.

Результат:

Возвращает символ с ASCII кодом x.

Описание:

Возвращает символ с ASCII кодом x. Значение x должно быть в диапазоне 0..255.

Пример:

          var i:integer;
          begin
           for i:=48 to 57 do writeln('i=',i,' chr=',chr(i));
          end;
         

Daq Pascal Api -> ord

Определение:

function ord(x:char):integer

Аргументы:

  • x - символ.

Результат:

Возвращает ASCII код символа x.

Описание:

Возвращает ASCII код символа x.

Пример:

           writeln(ord('a'));
         

Daq Pascal Api -> upcase locase

Определение:

function upcase(c:char):char
function locase(c:char):char

Аргументы:

  • c - символ для преобразования (8 бит).

Результат:

Меняет регистр символа с на верхний/нижний (upcase/locase).

Описание:

Возвращает символ с в верхнем (upcase) или нижнем (locase) регистре. Преобразование делается только для символов ASCII, т.е. в диапазоне a-z или A-Z.

Пример:

          if UpCase(StrFetch(s,1))='A' then writeln('Start from A');
          if LoCase(StrFetch(s,1))='b' then writeln('Start from b');
         

Daq Pascal Api -> succ

Определение:

function succ(x:char):char

Аргументы:

  • x - символ.

Результат:

Возвращает ASCII код следующего символа после x.

Описание:

Возвращает ASCII код следующего символа после x.

Пример:

           writeln(succ('a')); {return 'b'}
         

Daq Pascal Api -> pred

Определение:

function pred(x:char):char

Аргументы:

  • x - символ.

Результат:

Возвращает ASCII код предыдущего символа перед x.

Описание:

Возвращает ASCII код предыдущего символа перед x.

Пример:

           writeln(pred('b')); {return 'a'}
         

Daq Pascal Api -> round

Определение:

function round(x:real):integer

Аргументы:

  • x - вещественное число.

Результат:

Возвращает округленное значение x.

Описание:

Возвращает округленное до ближайшего целого значение числа x, преобразованное к типу integer. Число должно лежать в диапазоне -2147483648..2147483647.

Пример:

           i:=round(1.2); {i=1}
           j:=round(1.6); {j=2}
         

Daq Pascal Api -> trunc

Определение:

function trunc(x:real):integer

Аргументы:

  • x - вещественное число.

Результат:

Возвращает целую часть x.

Описание:

Возвращает округленное в сторону нуля значение числа x, преобразованное к типу integer. Число должно лежать в диапазоне -2147483648..2147483647.

Пример:

           i:=trunc(1.2); {i=1}
           j:=trunc(1.6); {j=1}
         

Daq Pascal Api -> int

Определение:

function int(x:real):real

Аргументы:

  • x - вещественное число.

Результат:

Возвращает целую часть x.

Описание:

Возвращает целую часть числа x, то есть отсекает дробную часть числа. Это аналог trunc, только без преобразования в целое число.

Пример:

           x:=int(1.2); {x=1.0}
           y:=int(1.6); {y=1.0}
         

Daq Pascal Api -> frac

Определение:

function frac(x:real):real

Аргументы:

  • x - вещественное число.

Результат:

Возвращает дробную часть x.

Описание:

Возвращает дробную часть числа x.

Пример:

           x:=frac(1.2); {x=0.2}
           y:=frac(1.6); {y=0.6}
         

Daq Pascal Api -> floor

Определение:

function floor(x:real):real

Аргументы:

  • x - вещественное число.

Результат:

Возвращает округленное в сторону нуля значение x.

Описание:

Возвращает округленное в сторону нуля значение x. Синоним int.

Пример:

           x:=floor(1.2); {x=1.0}
           y:=floor(1.6); {y=1.0}
         

Daq Pascal Api -> ceil

Определение:

function ceil(x:real):real

Аргументы:

  • x - вещественное число.

Результат:

Возвращает округленное в сторону бесконечности значение x.

Описание:

Возвращает округленное в сторону бесконечности значение x.

Пример:

           x:=ceil(1.2); {x=2.0}
           y:=ceil(1.6); {y=2.0}
         

Daq Pascal Api -> deg rad

Определение:

function deg(r:real):real
function rad(d:real):real

Аргументы:

  • r - угол в радианах.
  • d - угол в градусах.

Результат:

Переводит радианы в градусы и наоборот.

Описание:

deg переводит угол r в радианах в градусы.
rad переводит угол d в градусах в радианы.

Пример:

           x:=rad(30); {x=pi/6}
           x:=deg(pi); {x=180}
         

Daq Pascal Api -> sin cos tan

Определение:

function sin(x:real):real
function cos(x:real):real
function tan(x:real):real

Аргументы:

  • x - угол в радианах.

Результат:

Возвращает синус,косинус,тангенс x.

Описание:

sin возвращает значение синуса числа x, заданного в радианах.
cos возвращает значение косинуса числа x, заданного в радианах.
tan возвращает значение тангенса числа x, заданного в радианах.

Пример:

           x:=sin(pi/6); {x=0.5}
           x:=cos(pi/3); {x=0.5}
           x:=tan(pi/4); {x=1.0}
         

Daq Pascal Api -> asin acos atan arctan

Определение:

function asin(x:real):real
function acos(x:real):real
function atan(x:real):real
function arctan(x:real):real

Аргументы:

  • x - вещественное число.

Результат:

Возвращает арксинус,арккосинус,арктангенс x.

Описание:

asin возвращает значение обратного синуса числа x в радианах.
acos возвращает значение обратного косинуса числа x в радианах.
atan возвращает значение обратного тангенса числа x в радианах.

Пример:

           x:=asin(0.5); {x=pi/6}
           x:=acos(0.5); {x=pi/3}
           x:=atan(1.0); {x=pi/4}
         

Daq Pascal Api -> sinh cosh tanh

Определение:

function sinh(x:real):real
function cosh(x:real):real
function tanh(x:real):real

Аргументы:

  • x - вещественное число.

Результат:

Возвращает гиперболический синус,косинус,тангенс x.

Описание:

sin возвращает значение гиперболического синуса числа x.
cos возвращает значение гиперболического косинуса числа x.
tan возвращает значение гиперболического тангенса числа x.

Пример:

           x:=sinh(1);
           x:=cosh(1);
           x:=tanh(1);
         

Daq Pascal Api -> exp power

Определение:

function exp(x:real):real
function power(b,x:real):real

Аргументы:

  • b - основание.
  • x - степень.

Результат:

exp возвращает натуральную экспоненту числа x.
power возвращает число b в степени x.

Описание:

exp(x) возвращает значение натуральной экспоненты числа x, то есть e в степени x.
power(b,x) возвращает значение числа b в степени x. Для недопустимых значений основания (<=0) возвращается NAN или INF.

Пример:

           v:=exp(1);      {v=e}
           v:=power(10,2); {v=100}
         

Daq Pascal Api -> ln log

Определение:

function ln(x:real):real
function log(b,x:real):real

Аргументы:

  • b - основание.
  • x - логарифмируемое число.

Результат:

ln возвращает натуральный логарифм x.
log возвращает логарифм x по основанию b.

Описание:

ln(x) возвращает натуральный логарифм числа x по основанию e.
log(n,x) возвращает логарифм числа x по основанию b.
Для недопустимых значений аргументов (<=0) функции возвращают NAN или INF.

Пример:

           v:=ln(sqr(e));  {v=2}
           v:=log(10,100); {v=2}
           v:=ln(-1);      {v=NAN}
           v:=ln(0);       {v=INF}
         

Daq Pascal Api -> _nan isnan

Определение:

function _nan:real
function isnan(x:real):boolean

Аргументы:

  • x - проверяемое число.

Результат:

_nan возвращает NAN.
isnan проверяет, имеет ли x значение NAN.

Описание:

_nan возвращает значение NAN, "not a number" или "не число".
isnan(x) возвращает true, если x равно NAN, то есть "не число". Нечисловые значения могут возникать при некорректных операциях с вещественными числами. Например, выражения sqrt(-1), ln(-1), rval('abc') вернут значение NAN. Нечисловые значения можно использовать также как флаги для маркировки тех или иных особых ситуаций (значение не инициализировано, недоступно, некорректно и т.д.).

Пример:

           x:=_nan;   if isnan(x) then writeln('x=NAN'); {yes}
           x:=ln(-1); if isnan(x) then writeln('x=NAN'); {yes}
           x:=ln(+1); if isnan(x) then writeln('x=NAN'); {no}
         

Daq Pascal Api -> _inf _plusinf _minusinf isinf

Определение:

function _inf:real
function _plusinf:real
function _minusinf:real
function isinf(x:real):boolean

Аргументы:

  • x - проверяемое число.

Результат:

_minusinf возвращает -INF.
_inf или _plusinf возвращает +INF.
isinf(x) проверяет, имеет ли x значение INF.

Описание:

_minusinf возвращает значение -INF, "-infinity" или "-бесконечность".
_inf или _plusinf возвращает значение +INF, "+infinity" или "+бесконечность".
isinf(x) возвращает true, если x равно ±INF. Бесконечные значения могут возникать при некорректных операциях с вещественными числами. Например, выражения 1/0, ln(0) вернут значение INF. Бесконечные значения можно использовать также как флаги для маркировки тех или иных особых ситуаций (значение не инициализировано, недоступно, некорректно и т.д.).

Пример:

           x:=_minusinf; if isinf(x) then writeln('x=INF'); {yes}
           x:=_plusinf;  if isinf(x) then writeln('x=INF'); {yes}
           x:=1/0;       if isinf(x) then writeln('x=INF'); {yes}
           x:=1/1;       if isinf(x) then writeln('x=INF'); {no}
         

Daq Pascal Api -> _nil

Определение:

function _nil:integer

Аргументы:

Нет.

Результат:

Возвращает пустую ссылку.

Описание:

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

Пример:

           if cref=_nil
           then writeln('Curve is not initialized')
           else writeln('Curve length = ',crvlen(cref));
         

Daq Pascal Api -> pi

Определение:

function pi:real

Аргументы:

Нет.

Результат:

Возвращает значение числа "пи".

Описание:

Возвращает "пи"=3.14...

Пример:

           x:=sin(pi/3);
         

Daq Pascal Api -> macheps

Определение:

function macheps:real

Аргументы:

Нет.

Результат:

Возвращает точность представления вещественных чисел.

Описание:

Возвращает точность представления вещественных чисел. Более конкретно, это наименьшее положительное число eps, такое, что
1+eps>1.

Пример:

           x:=macheps;
         

Daq Pascal Api -> maxint

Определение:

function maxint:integer

Аргументы:

Нет.

Результат:

Возвращает наибольшее челое число.

Описание:

Возвращает наибольшее целое число. В данной версии это 2147483647.

Пример:

           i:=maxint;
         

Daq Pascal Api -> gamma

Определение:

function gamma(x:real):real

Аргументы:

  • x - вещественное число.

Результат:

Возвращает значение гамма-функции x.

Описание:

Возвращает значение гамма-функции x. Для целочисленных положительных значений x гамма-функция совпадает с факториалом, то есть справедливо равенство gamma(x)=(x-1)!. Для отрицательного аргумента функция вернет NAN.

Пример:

           x:=gamma(4); {x=3!=6}
         

Daq Pascal Api -> sign

Определение:

function sign(x:real):integer

Аргументы:

  • x - вещественное число.

Результат:

Возвращает знак числа x.

Описание:

Возвращает значение знака числа x.
  • +1 для x>0
  • 0 для x=0
  • -1 для x<0

Пример:

           x:=sign(+pi); {x=+1}
           x:=sign(0);   {x=0}
           x:=sign(-pi); {x=-1}
         

Daq Pascal Api -> hypot

Определение:

function hypot(x,y:real):real

Аргументы:

  • x,y - катеты.

Результат:

Возвращает гипотенузу x,y.

Описание:

Возвращает гипотенузу x,y. Эквивалентно sqrt(sqr(x)+sqr(y)), но быстрее и точнее.

Пример:

           x:=hypot(3,4); {x=5}
         

Daq Pascal Api -> rand random

Определение:

function rand:real
function random(a,b:real):real

Аргументы:

  • a,b - интервал.

Результат:

Возвращает случайное равномерно распределенное число.

Описание:

rand возвращает случайное число, равномерно распределенное в интервале (0,1).
random(a,b) возвращает случайное число, равномерно распределенное в интервале (a,b).

Пример:

           x:=random(-1,1);
         

Daq Pascal Api -> min max rmin rmax imin imax

Определение:

function min(a,b:real):real
function max(a,b:real):real
function rmin(a,b:real):real - синоним min (real min)
function rmax(a,b:real):real - синоним max (real max)
function imin(a,b:integer):integer - вариант min для целых чисел (integer min)
function imax(a,b:integer):integer - вариант max для целых чисел (integer max)

Аргументы:

  • a,b - аргументы.

Результат:

Возвращает минимальное или максимальное из чисел a,b.

Описание:

min(a,b) возвращает минимальное из чисел a,b.
max(a,b) возвращает максимальное из чисел a,b.

Пример:

           x:=min(3,4); {x=3}
           x:=max(3,4); {x=4}
         

Daq Pascal Api -> eq ne lt le gt ge

Определение:

function eq(a,b:real):boolean
function ne(a,b:real):boolean
function lt(a,b:real):boolean
function le(a,b:real):boolean
function gt(a,b:real):boolean
function ge(a,b:real):boolean

Аргументы:

  • a,b - сравниваемые числа.

Результат:

Возвращает результат сравнения чисел a,b.

Описание:

eq(a,b) возвращает a = b (equal).
ne(a,b) возвращает a <> b (not equal).
lt(a,b) возвращает a < b (less).
le(a,b) возвращает a <= b (less or equal).
gt(a,b) возвращает a > b (greater).
ge(a,b) возвращает a >= b (greater or equal).

Пример:

           if eq(3,4) then ... {false}
           if ne(3,4) then ... {true}
           if lt(3,4) then ... {true}
           if le(3,4) then ... {true}
           if gt(3,4) then ... {false}
           if ge(3,4) then ... {false}
         

Daq Pascal Api -> bitnot inot bitor ior bitxor ixor bitand iand rshift ishift isbit getbitmask hasflags

Определение:

function bitnot(a:real):real
function inot(i:integer):integer
function bitor(a,b:real):real
function ior(i,j:integer):integer
function bitxor(a,b:real):real
function ixor(i,j:integer):integer
function bitand(a,b:real):real
function iand(i,j:integer):integer
function rshift(a:real;j:integer):real
function ishift(i,j:integer):integer
function isbit(a:real;j:integer):boolean
function getbitmask(j:integer):real
function hasflags(i,m:integer):boolean

Аргументы:

  • a,b - вещественные аргументы.
  • i,j - целочисленные аргументы.
  • m - маска флагов.

Результат:

Возвращает результат битовых операций с числами a,b,i,j,m.

Описание:

Данная группа функций реализует базовый набор битовых операций с числами. В функциях bitnot,bitor,bitxor,bitand,isbit аргументы a,b преобразуются в 32-битные целые. Числа в этих функциях имеет вещественный тип по причине того, что
  1. Целые числа часто хранятся в вещественных переменных, например, в кривых.
  2. В старой версии CRW-DAQ тип integer имел 16 бит
Функции реализуют следующие операции:
  • bitnot(a) возвращает побитно инвертированное число a. Число a предварительно преобразуется в 32-битное целое.
  • inot(i) возвращает побитно инвертированное целое число i.
  • bitor(a,b) возвращает побитное логическое неисключающее ИЛИ для чисел a,b. Числа a,b предварительно преобразуются в 32-битное целое.
  • ior(i,j) возвращает побитное логическое неисключающее ИЛИ для чисел i,j.
  • bitxor(a,b) возвращает побитное логическое исключающее ИЛИ для чисел a,b. Числа a,b предварительно преобразуются в 32-битное целое.
  • ixor(i,j) возвращает побитное логическое исключающее ИЛИ для чисел i,j.
  • bitand(a,b) возвращает побитное логическое И для чисел a,b. Числа a,b предварительно преобразуются в 32-битное целое.
  • iand(i,j) возвращает побитное логическое И для чисел i,j.
  • rshift(a,j) сдвигает число a на j разрядов, что эквивалентно умножению на 2 в степени j: rshift(a,j)=a*power(2,j). Однако данный способ вычисления быстрее и точнее. В отличие от других функций этой группы, число a не преобразуется в 32-битное целое.
  • ishift(i,j) сдвигает число i на j разрядов, что эквивалентно умножению на 2 в степени j: ishift(i,j)=i*power(2,j). Однако данный способ вычисления быстрее и точнее.
  • isbit(a,j) возвращает true, если бит j числа a равен 1. Число a предварительно преобразуются в 32-битное целое.
  • getbitmask(j) возвращает число, в котором бит j равен 1, а остальные биты равны 0. Эквивалентно rshift(1,j).
  • hasflags(i,m) проверяет, есть ли в числе i биты флагов m. Эквивалентно hasmask(i,m):=(iand(i,m)<>0).

Пример:

           x:=inot(x);              {побитно инвертировать}
           i:=iand(j,15);           {выделить 4 младших бита}
           x:=rshift(x,+2);         {сдвинуть на 2 разряда влево  (умножить на 4)}
           x:=rshift(x,-2);         {сдвинуть на 2 разряда вправо (поделить на 4)}
           if isbit(x,0) then       {проверить наличие 0 бита}
           if hasflags(mode,3) then {проверить наличие битов 0 или 1, входящих в маску 3}
         

Daq Pascal Api -> htonl ntohl htons ntohs

Определение:

function htonl(hl:integer):integer
function ntohl(nl:integer):integer
function htons(hs:integer):integer
function ntohs(ns:integer):integer

Аргументы:

  • hl - 32-битное число (long) в машинном представлении (host).
  • nl - 32-битное число (long) в сетевом представлении (net).
  • hs - 16-битное число (short) в машинном представлении (host).
  • ns - 16-битное число (short) в машинном представлении (net).

Результат:

Возвращает результат перестановки байтов числа в машинном (host) или сетевом (net) представлении.

Описание:

Данная группа функций реализует перестановку байтов числа в машинном (host) или сетевом (net) представлении и используется при организации потоковой приемо-передаче данных в сети.
В машинном (host) представлении порядок байтов числа зависит от текущей архитектуры процессора. Для процессоров архитектуры x86 - порядок байтов от младшего к старшему. Такой порядок не может быть использован для потоковой передачи данных в сетевой гибридной системе, т.к. другие компьютеры в сети могут иметь иной порядок байтов.
В сетевом (net) представлении порядок байтов числа фиксирован - от старшего к младшему, независимо от архитектуры процессора.
Сетевой порядок байтов используется для передачи числовых данных в сети TCP/IP. Независимость сетевого порядка от архитектуры обеспечивает правильную работу сети на любой аппаратной платформе. Поэтому при передаче потоковых данных (через сеть, канал, файл и т.д.) желательно преобразовывать целочисленные данные в сетевое предcтавление, а при приеме - преобразовывать принятые данные обратно в машинное представление.

Функции реализуют следующие операции:
  • htonl(hl) возвращает 32-битное целое число hl, преобразованное в сетевое представление.
  • ntohl(nl) возвращает 32-битное целое число nl, преобразованное в машинное представление.
  • htons(hs) возвращает 16-битное целое число hs, преобразованное в сетевое представление.
  • ntohs(ns) возвращает 16-битное целое число ns, преобразованное в машинное представление.

Пример:

  procedure TestHtonl;
  var hl,nl,hs,ns,ne:Integer;
  begin
   ne:=0; writeln('Test htonl/ntohl:');
   hl:=Val('$D0C0B0A0'); nl:=htonl(hl);  writeln(' $',hexl(hl):8,' $',hexl(nl):8,' - ',(hl=ntohl(nl)):1); ne:=ne+ord(hl<>ntohl(nl));
   nl:=Val('$D0C0B0A0'); hl:=ntohl(nl);  writeln(' $',hexl(hl):8,' $',hexl(nl):8,' - ',(nl=htonl(hl)):1); ne:=ne+ord(nl<>htonl(hl));
   hs:=Val('$DCBA');     ns:=htons(hs);  writeln(' $',hexw(hs):8,' $',hexw(ns):8,' - ',(hs=ntohs(ns)):1); ne:=ne+ord(hs<>ntohs(ns));
   ns:=Val('$DCBA');     hs:=ntohs(ns);  writeln(' $',hexw(hs):8,' $',hexw(ns):8,' - ',(ns=htons(hs)):1); ne:=ne+ord(ns<>htons(hs));
   writeln(ne:1,' error(s) found');
  end;

  Вывод процедуры:

  Test htonl/ntohl:
   $D0C0B0A0 $A0B0C0D0 - true
   $A0B0C0D0 $D0C0B0A0 - true
   $    DCBA $    BADC - true
   $    BADC $    DCBA - true
  0 error(s) found
         

Daq Pascal Api -> maxavail stackavail

Определение:

function maxavail:integer
function stackavail:integer

Аргументы:

Нет.

Результат:

Возвращает число свободных ячеек под динамические строки (maxavail) и переменные (stackavail).

Описание:

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

Все скалярные переменные в Daq Pascal, независимо от типа, занимают одну ячейку в стеке данных. Функция StackAvail возвращает число свободных ячеек стека данных, то есть элементов данных которые может создать программа динамически. Глобальные переменные, то есть переменные, объявленные в начале программы, получают место в стеке еще при компиляции, поэтому функция возвращает свободное место в сегменте данных, исключая глобальные переменные. Стек используется для хранения временных данных, локальных переменных при вызове функций и т.д. Для нормального функционирования программы в стеке должно быть достаточно свободного пространства. Если его мало, надо увеличить сегмент данных, как описано в разделе опции компилятора.

Все строки в Daq Pascal динамические, память под них выделяется по мере необходимости. Все строки хранятся в таблице строк, которую имеет каждая программа DAQ Pascal. Функция MaxAvail возвращает число свободных ячеек таблицы строк, то есть число строк, которые может создать программа. Для нормального функционирования программы в таблице строк должно быть достаточно свободного пространства. Если его мало, надо увеличить таблицу строк, как описано в разделе опции компилятора.

Как описано в разделе string, все используемые строки должны инициализироваться в начале программы и освобождаться в конце программы путем присвоения пустой строки. Неправильное использование строк будет приводить к ошибкам. Если попытаться прочитать неинициализированную строку, DAQ программа аварийно завершит работу. Если не освобождать строки, таблица строк будет постепенно заполняться, пока не произойдет ее переполнение. После переполнения DAQ программа также аварийно завершит работу.

Функция maxavail позволяет контролировать корректность работы со строками. При корректной работе со строками значение функции при старте программы (runcount=1) и при ее завершении (isinf(runcount)) совпадают. Несовпадение говорит о том, что не все строки освобождены и происходит "утечка ресурсов".

Пример:

          {Пример программы с контролем утечки строковых ресурсов}
          program test;
          var s,w:string; errors,errorcode,fixavail:integer; b,Ok:boolean;
           procedure ClearStrings;
           begin
            s:='';
            w:='';
            if runcount=1 then fixavail:=maxavail;
            if isinf(runcount) then
            if maxavail<>fixavail then begin
             writeln('String resource leak found!');
             b:=fixerror(errorcode);
            end;
           end;
          begin
           if runcount=1 then begin
            errors:=0;
            errorcode:=registererr(devname);
            ClearStrings;
            Ok:=(errors=0);
            if errors<>0 then fixerror(errorcode);
           end else
           if isinf(runcount) then begin
            ClearStrings;
           end else
           if Ok then begin
            if not eof then begin
             readln(s);
             s1:=extractword(1,s);
             writeln(s1);
            end;
           end;
          end;
         

Daq Pascal Api -> length

Определение:

function length(s:string):integer

Аргументы:

  • s - строка.

Результат:

Возвращает длину строки.

Описание:

Возвращает длину строки, то есть число символов в строке.

Пример:

          {Подсчет числа пробелов в строке}
          function numspaces(s:string):integer;
          var i,n:integer;
          begin
           n:=0;
           for i:=1 to length(s) do if s[i]=' ' then n:=n+1;
           numspaces:=n;
          end;
         

Daq Pascal Api -> strfetch

Определение:

function strfetch(s:string; i:integer):char

Аргументы:

  • s - строка.
  • i - индекс.

Результат:

Безопасно извлекает (fetch) символ строки с заданным индексом s[i] (или возвращает ноль).

Описание:

Это безопасный аналог индексации строки s[i], который возвращает нулевой символ chr(0), если индекс вышел за допустимый диапазон [1..length(s)] (а не генерирует ошибку, как s[i]). Функцию удобно использовать для безопасной проверки определенных символов строки (например, для её идентификации). Также strFetch удобно использовать в сложных алгоритмах обработки строк, чтобы быть полностью уверенным, что не будет ошибок индексации. Поведение функции (возврат нуля за пределами длины) можно использовать в алгоритмах для прерывания цикла обработки (вспомним про строки в стиле C). В общем, функция полезная.

Тесты показали, что производительность strFetch(s,i) примерно совпадает с индексацией s[i]. Поэтому функцию можно смело использовать в алгоритмах любой сложности как безопасную замену индексации.

Пример:

          { Обработка команд, начинающихся с @ }
          procedure HandleCommand(var arg:string);
          begin
           if strFetch(arg,1)='@' then begin // А раньше для безопасной проверки надо было еще проверять длину строки
            // Обработка команды
           end;
          end;
         

Daq Pascal Api -> copy

Определение:

function copy(s:string;p,n:integer):string

Аргументы:

  • s - строка.
  • p - позиция копируемой подстроки.
  • n - длина копируемой подстроки.

Результат:

Возвращает подстроку строки s, начиная с позиции p длиной n символов.

Описание:

Возвращает подстроку строки s, начиная с позиции p, длиной n символов.
Если s - пустая строка, возвращается пустая строка.
Если позиция p выходит за границы строки 1..length(s), возвращается пустая строка.
Если длина n больше длины остатка строки length(s)-p+1, строка обрезается по этой фактической длине.

Пример:

          s:='1234567890';
          t:=copy(s,1,length(s));       {t='1234567890'}
          t:=copy(s,1,3);               {t='123'}
          t:=copy(s,4,255);             {t='4567890'}
          t:=copy(s,3,3);               {t='345'}
          t:=copy(s,33,10);             {t=''}
         

Daq Pascal Api -> pos posex

Определение:

function pos(sub,str:string):integer
function posex(sub,str:string; startpos:integer):integer

Аргументы:

  • sub - искомая подстрока.
  • str - строка текста, в которой ищется подстрока.
  • startpos - начальный индекс поиска (1 = начало строки).

Результат:

Возвращает позицию подстроки sub в строке str.

Описание:

Функция pos и её расширенный вариант posex осуществляет поиск подстроки в строке. Функция возвращает позицию подстроки sub в строке str. Позиция может принимать значения от 1 до length(str)-length(sub)+1. Функция возвращает ноль, если подстрока sub в строке str не найдена. При сравнении строки рассматриваются как массив байтов, т.е. регистр символов играет роль.

Вызов pos(sub,str) эквивалентен вызову posex(sub,str,1).

Функция posex позволяет итерационно найти все вхождения подстроки в строку, т.к. поиск начинается с указаной позиции.
Основой поиска служит цикл вида
         i:=0;
         repeat
          i:=PosEx(Sub,Str,i+1);
          if (i>0) then writeln('Found '+Sub+' at position '+Str(i));
         until (i<=0);
         
В этом цикле поиск на каждой итерации начинается со следующей позиции после ранее найденной.

Пример:

          s:='1234567890';
          p:=pos('34',s);             {p=3}
          p:=pos('0',s);              {p=10}
          p:=pos('a',s);              {p=0}
          p:=posex('a','abcdabcd',1); {p=1}
          p:=posex('a','abcdabcd',2); {p=5}

          procedure ProcessLines(Lines:String);
          var i,p,n:Integer; Line:String;
          begin
           i:=0; p:=1; n:=0;
           repeat
            i:=PosEx(EOL,Lines,i+1);
            if (i>0) then begin
             Line:=Copy(Lines,p,i-p);
             n:=n+1; Writeln(StrFmt('Line[%d] = ',n)+Line);
             p:=i+Length(EOL);
            end else begin
             if (p<=Length(Lines)) then begin
              Line:=Copy(Lines,p,Length(Lines)-p+1);
              n:=n+1; Writeln(StrFmt('Line[%d] = ',n)+Line);
             end;
            end;
           until (i<=0);
          end;
         

Daq Pascal Api -> poseol

Определение:

function poseol(buf:string; startpos,skiplines:integer):integer

Аргументы:

  • buf - буфер текста, в котором ищется маркер конца строки EOL.
  • startpos - начальный индекс поиска (1 = начало буфера).
  • skiplines - число пропускаемых строк (0 = не надо).

Результат:

Возвращает позицию (pos) маркера конца строки EOL (end of line) в буфере buf, либо пропускает разделители строк и возвращает позицию после разделителей (т.е. начало следующей строки).

Описание:

Функция poseol служит для обработки текстов, разделенных на строки маркерами конца строки EOL.
В качестве маркеров применяются:

  • CRLF в Windows.
  • LF в Unix/Linux.
  • CR в MAC OS.
  • Любые варианты (CR,LF,CRLF,LFCR) в разного рода сетевых контроллерах.

При анализе EOL считается, что последовательность (CR,LF) или (LF,CR) - это один маркер, в то же время (CR,CR) или (LF,LF) считаются последовательностью из двух маркеров с пустой строкой между ними.

Поскольку тексты могут передаваться по сети между различными устройствами, возникает задача анализа строк (в первую очередь разбиения на строки) в такой гибридной среде. Ориентация на вызовы Pos(CRLF,buf) или Pos(LineEnding,Buf) крайне ненадежна, т.к. сетевое устройство может иметь другой маркер EOL.

Функция PosEol(buf,startpos,skiplines) позволяет анализировать строки с любыми выше перечисленными маркерами конца строки EOL.

Вызов PosEol(buf,startpos,0) находит в буфере маркер EOL (любого типа), начиная с заданной позиции поиска (1 соответствует началу буфера), и возвращает его позицию в диапазоне (1..length(buf)), либо возвращает 0, если маркер не найден, буфер пуст или стартовая позиция задана неверно (не входит в диапазон допустимых значений). Этот вызов используется для поиска в буфере маркера конца строки (любого типа).

Вызов PosEol(buf,startpos,1) пропускает одну строку, то есть сначала ищет в буфере маркер EOL (любого типа), начиная с заданной позиции поиска (1 соответствует началу буфера), затем (если EOL найден), пропускает его и возвращает позицию ПОСЛЕ маркера EOL - то есть позицию начала следующей строки. Особый случай - когда в строке нет маркера EOL - возвращает (length(buf)+1), т.е. позицию после текста (таким образом, если в буфере нет маркера EOL, пропускается весь буфер).

Вызов PosEol(buf,startpos,n) при n>1 пропускает n строк, так что в конце позиция соответствует началу n+1 строки (либо позиции после буфера).

Многократный вызов PosEol может применяться для движения по строкам текста:
          s:=1;                         // Это начало буфера
          p:=poseol(buf,s,0);           // Ищем конец строки
          if (p>0)  then begin          // Найден маркер EOL
           line:=copy(buf,s,p-s);       // Выделяем эту строку
           s:=poseol(buf,p,1);          // Идем в начало следующей строки
          end else line:=buf;           // Берем весь текст, если нет eol
          ... и так далее ...
         
Шаблон для построения цикла обработки буфера по строкам приведен ниже.

Таким образом, функция PosEol возвращает такие значения:
  • Ноль (0), если пропуск строк не задан, а в буфере нет маркера EOL, или буфер пуст, или стартовая позиция недопустима.
  • Позицию маркера EOL (1..length(buf)), если пропуск строк не задан, а в буфере найден EOL после стартовой позиции.
  • Позицию следующей стоки (1..length(buf)+1), если задан пропуск строк и найдены маркеры EOL после стартовой позиции.
  • Позицию после текста (length(buf)+1), если задан пропуск строк и маркеры EOL не найдены после стартовой позиции.
  • При задании пропуска строк результат вызова может лежать за пределами текста (length(buf)+1) - за этим надо следить.

Использование функции PosEol повысит надежность работы прикладных программ в гибридной сетевой среде, где текстовые данные могут передаваться с разными маркерами конца строки. Однако при программировании надо быть внимательным, т.к. функция PosEol может возвращать результат в диапазоне (0..length(buf)+1), т.е. возможен его выход за пределы индексации исходного текста. Поэтому результат надо тщательно проверять, прежде чем использовать его для индексации строк.

Функция PosEol отличается высокой скоростью работы, особенно при работе с строками - переменными: стоимость её вызова примерно такая же как у Pos (около 80 ns на i7-4700MQ-2.4GHz). Её рекомендуется использовать для функций анализа и обработки различных потоков данных, где требуется высокая надежность и скорость.

Пример (шаблон анализа строк в буфере):

        //
        // Procedure to handle line number n.
        //
        procedure HandleLine(n:Integer; line:String);
        begin
         writeln('Line ',n:1,' = ',line);
        end;
        //
        // Handle lines in buffer, return num.lines.
        //
        function HandleLines(var buf:String):Integer;
        var p,s,n,l:Integer;
        begin
         p:=1;s:=1;n:=0;l:=Length(buf);
         while (p>=1) and (p<=l) do begin
          p:=PosEol(buf,s,0); // Find EOL
          if (p>0) then begin // EOL found
           HandleLine(n,Copy(buf,s,p-s));
           s:=PosEol(buf,p,1); // Skip EOL
           n:=n+1; // inc line counter
          end else // handle last line
          if (s<=l) then begin // Has tail
           HandleLine(n,Copy(buf,s,l-s+1));
           n:=n+1; // inc line counter
          end;
         end;
         HandleLines:=n; // return number of lines
        end;
         

Daq Pascal Api -> str strfix strfmt

Определение:

function str(i:integer):string - преобразование целого числа в строку
function str(r:real):string - преобразование вещественного числа в строку
function strfix(r:real;w,d:integer):string - преобразование вещественного числа в строку с форматированием
function strfmt(f:string; p:string/char/integer/real):string - преобразование параметра в строку с форматированием

Аргументы:

  • i - целое число.
  • r - вещественное число.
  • w - ширина поля, число знаков.
  • d - число знаков после запятой.
  • f - формат преобразования для параметра p.
  • p - преобразуемый параметр типа string, char, integer или real.

Результат:

Преобразует число в строку (в десятичной системе счисления, либо согласно формату).

Описание:

str(i) - возвращает строковое представление целого числа i в десятичной системе счисления.

str(r) - возвращает строковое представление вещественного числа r в десятичной системе счисления. Преобразование идет в свободном формате, то есть %g. В свободном формате выбирается представление либо в форме с фиксированной точкой, либо в экспоненциальной форме, в зависимости от того, какая будет короче. Точность представления около 15 значащих цифр, незначащие нули справа и слева подавляются.

strfix(r,w,d) - возвращает строковое представление вещественного числа r в десятичной системе счисления. Преобразование идет в формате с фиксированной точкой, не менее w знаков, d знаков после запятой, то есть %w.df. При необходимости число дополняется пробелами слева до нужной ширины поля w.

strfmt(f,p) - возвращает строковое представление параметра p в соответствии с форматом f. Преобразуемый параметр p может иметь тип string, char, integer или real. Строка формата f соответствует функции Format() языка ObjectPascal или sprintf() языка C/C++. Отличие в том, что преобразуемый параметр может быть только один.
В общем случае строка формата - произвольная строка с содержащимся в ней описателем формата.
Описатель формата начинается с символа % и заканчивается индикатором типа данных (d,u,x,m,f,e,g,s):
         %d = Десятичное целое число      ( decimal     )  например:  %10d
         %u = Десятичное число без знака  ( unsigned    )  например:  %12u
         %x = Шестнадцатеричное целое     ( hexadecimal )  например:  %8.8x
         %m = Денежный формат             ( money       )  например:  %10m
         %f = Фиксированный вещественный  ( fixed       )  например:  %11.5f
         %e = Научный (с экспонентой)     ( exponent    )  например:  %11.5e
         %g = Автоматический выбор        ( general     )  например:  %11.5g
         %s = Строка                      ( string      )  например:  %-20.15s
         
Общий вид описателя формата следующий:

%[-][Width][.Precision]Type, где Type={d,u,x,m,f,e,g,s} - индикатор типа, например %-11.5g

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

При использовании функции StrFmt надо быть внимательным, т.к. при неверном формате или несоответствии формата и типа параметра будет зафиксирована ошибка (которая попадет в журнал исключений), а функция вернет пустую строку.

Пример:

          s:=str(1/3);              {s='0.333333333333333'}
          s:=str(0.3);              {s='0.3'              }
          s:=strfix(1/3,11,4);      {s='     0.3333'      }
          s:=strfmt('%11.4f',1/3);  {s='     0.3333'      }
          s:=strfmt('pi=%7.5f',pi); {s='pi=3.14159'       }
          {
          Get string like 2006.09.21-00:12:30
          }
          function GetDateTime(ms:Real):String;
          begin
           GetDateTime:=StrFmt('%4.4d.',ms2year(ms))
                       +StrFmt('%2.2d.',ms2month(ms))
                       +StrFmt('%2.2d-',ms2day(ms))
                       +StrFmt('%2.2d:',ms2hour(ms))
                       +StrFmt('%2.2d:',ms2min(ms))
                       +StrFmt('%2.2d',ms2sec(ms));
          end;
          {
          Процедура для тестирования StrFmt().
          }
          procedure testStrFmt;
          begin
           writeln('testStrFmt');
           // StrFmt(string, integer)
           writeln(StrFmt('maxint  = %14.8x',maxint));
           // StrFmt(string temporary, integer)
           writeln(StrFmt('maxint  '+'= %14.12d',maxint));
           // StrFmt(string, real)
           writeln(StrFmt('pi = %11.5e',pi));
           writeln(StrFmt('pi = %11.5f',pi));
           // StrFmt(string temporary, real)
           writeln(StrFmt('pi '+'= %11.5g',pi));
           // StrFmt(string,string)
           writeln(StrFmt('str = %20.14s','ComputerName'));
           // StrFmt(string,string temporary)
           writeln(StrFmt('str = %20.14s',ParamStr('ComputerName')));
           // StrFmt(string temporary,string)
           writeln(StrFmt('str = %20.14s','Host'+'Name'));
           // StrFmt(string temporary,string temporary)
           writeln(StrFmt('str '+'= %20.14s',ParamStr('HostName')));
           // StrFmt(string, char)
           writeln(StrFmt('char  = %14.8s','a'));
           // StrFmt(string temporary, integer)
           writeln(StrFmt('char  '+'= %s','b'));
           //writeln(StrFmt('%q',pi)); // bug
           //writeln(StrFmt('%g',pi));
          end;
         

Daq Pascal Api -> strtimefmt

Определение:

function strtimefmt(fmt:string; ms:real):string - преобразование времени в строку с форматированием

Аргументы:

  • ms - время в миллисекундах от начала эры (msecnow).
  • fmt - формат преобразования времени ms.

Результат:

Преобразует время в миллисекундах от начала эры (ms) в строку, согласно формату (fmt).

Описание:

strfmt(fmt,ms) - возвращает строковое представление времени ms в соответствии с форматом fmt. Функция рааботает аналогично FormatDateTime, только время задается в миллисекундах от начала эры (см. msecnow). При ошибке в строке описания формата или при недопустимых данных (ms<MSecRangeMin или ms>MSecRangeMax) функция возвращает пустую строку. Функция имеет относительно высокую скорость и работает существенно быстрее, чем ручное форматирование через разложение времени на компоненты и затем вызовы Str,StrFix,StrFmt.
Описатель формата fmt включает выражения:
         yyyy       год полный                как 2022
         yy         год короткий              как   22
         m          месяц              1..12  как    4
         mm         месяц             01..12  как   04
         mmm        месяц (имя)               как  апр
         mmmm       месяц (имя)               как  апрель
         d          день               1..31  как    1
         dd         день              01..31  как   01
         ddd        день недели               как   пт
         dddd       день недели               как   пятница
         h          часы               0..23  как    3
         hh         часы              00..23  как   03
         n          минуты             0..59  как    3
         nn         минуты            00..59  как   03
         s          секунды            0..59  как    7
         ss         секунды           00..59  как   07
         z          миллисекунды      0..999  как   57
         zzz        миллисекунды    000..999  как  057
         
Например: s:=StrTimeFmt('yyyy-mm-dd hh:nn:ss.zzz',msecnow);

Пример:

          s:=StrTimeFmt('yyyy.mm.dd-hh:nn:ss.zzz',msecnow); // s = 2022.12.25-12:15:30.123
         

Daq Pascal Api -> hexb hexw hexl

Определение:

function hexb(b:integer):string
function hexw(w:integer):string
function hexl(r:real):string

Аргументы:

  • b - целое число, 8 бит.
  • w - целое число, 16 бит.
  • r - число, преобразуемое в 32-битное целое.

Результат:

Преобразует число в строку в шестнадцатеричной системе счисления.

Описание:

hexb(i) - возвращает 2-x символьное строковое представление 8-битного целого числа b в шестнадцатеричной системе счисления.
hexw(i) - возвращает 4-x символьное строковое представление 16-битного целого числа w в шестнадцатеричной системе счисления.
hexl(r) - возвращает 8-x символьное строковое представление 32-битного целого числа r в шестнадцатеричной системе счисления. Аргумент r имеет вещественный тип, но преобразуется в 32-битное целое. Это наследие старых версий CRW-DAQ, где был 16-битный тип integer.

Пример:

          s:=hexb(15);         {s='0F'}
          s:=hexb(15*16);      {s='F0'}
          s:=hexw(15*256);     {s='0F00'}
          s:=hexl(15*65536.0); {s='000F0000'}
         

Daq Pascal Api -> strtointbase inttostrbase

Определение:

function strtointbase(s:string; b,d:integer):integer
function inttostrbase(v,b,w:integer):string

Аргументы:

  • s - строка для преобразования в целое число.
  • b - база (base) для преобразования, из набора [2,8,10,16].
  • d - значение по умолчанию (default) в случае если преобразование не удалось.
  • v - целое число (value) для преобразования в строку.
  • w - желаемая ширина (width) строки или 0.

Результат:

Преобразует строку в целое число (strtointbase) или число в строку (inttostrbase) в различных системах счисления (двоичной, восьмеричной, десятичной, шестнадцатеричной).

Описание:

strtointbase(s,b,d) - преобразует строку s в целое число в системе счисления по основанию (базе) b из набора [2,8,10,16]. Если преобразование не удалось, возвращается значение по умолчанию d. База системы счисления может быть также задана первым символом строки: [$,&,%] соответствует базе [16,8,2].

inttostrbase(v,b,w) - возвращает символьное строковое представление целого числа v в системе счисления по базе (основанию) b из набора [2,8,10,16]. Если длина строки превышает заданную ширину w, то удаляются лишние (незначимые) нули слева (в начале строки).

Примечания:
  1. Если строка s начинается с символов [$,&,%], то преобразование (strtointbase) идет по основанию [16,8,2] независимо от аргумента b.
  2. Функции strtointbase, inttostrbase работают несколько медленнее, чем их аналоги с фиксированной базой (например str и val). Поэтому их следует применять только по необходимости - например, если система счисления может меняться или если нет аналога для данной системы счисления (например, двоичной или восьмеричной).
  3. Если требуется выяснить возможность преобразования строки в число, то можно выполнить два вызова strtointbase с разными значениями по умолчанию d и сравнить результаты. Если они совпадают, то преобразование успешно.

Пример:

          s:=inttostrbase(15,16,2);        {s='0F'}
          s:=inttostrbase(15,8,0);         {s='17'}
          i:=strtointbase('0F',16,0);      {i=15}
          i:=strtointbase('17',8,0);       {i=15}
          i:=strtointbase('$0F',0,0);      {i=15}
          i:=strtointbase('&17',0,0);      {i=15}
         

Daq Pascal Api -> val rval

Определение:

function val(s:string):integer
function rval(s:string):real

Аргументы:

  • s - преобразуемая строка.

Результат:

Преобразует строку в число.

Описание:

val(s) - возвращает целое число, представленное строкой s. Если строка начинается с символа "$", она интерпретируется как представление целого числа в шестнадцатеричной системе счисления. Если строка начинается с любого другого символа, то преобразование идет в десятичной системе счисления. Если строка содержит нечисловое значение, возвращается ноль.
rval(s) - возвращает вещественное число, представленное строкой s. Если строка начинается с символа "$", она интерпретируется как представление целого числа в шестнадцатеричной системе счисления. Если строка начинается с любого другого символа, то преобразование идет в десятичной системе счисления. Если строка содержит нечисловое значение, возвращается NAN.

Пример:

          i:=val('15');        {i=15}
          i:=val('$F');        {i=15}
          i:=val('abc');       {i=0}
          r:=rval('15');       {r=15}
          r:=rval('$F');       {r=15}
          r:=rval('1.23');     {r=1.23}
          r:=rval('abc');      {r=NAN}
         

Daq Pascal Api -> upcasestr locasestr

Определение:

function upcasestr(s:string):string
function locasestr(s:string):string

Аргументы:

  • s - преобразуемая строка.

Результат:

Преобразует строку в верхний/нижний регистр.

Описание:

upcasestr(s) - преобразует строку s в верхний регистр.
locasestr(s) - преобразует строку s в нижний регистр.
Преобразование дополнительных (не английских) символов в настоящей версии идет в кодировке 1251, принятой в русскоязычных версиях Windows.

Пример:

          s:=upcasestr('Case'); {s='CASE'}
          s:=locasestr('Case'); {s='case'}
         

Daq Pascal Api -> worddelims wordcount extractword skipwords phrasequotes phrasecount extractphrase skipphrases phraselisttotextlines

Определение:

function worddelims(d:string):string
function wordcount(s:string):integer
function extractword(n:integer;s:string):string
function skipwords(n:integer;s:string):string
function phrasequotes(q:string):string
function phrasecount(s:string):integer
function extractphrase(n:integer;s:string):string
function skipphrases(n:integer;s:string):string
function phraselisttotextlines(s:string):string

Аргументы:

  • d - строка символов - разделителей слов или фраз.
  • n - номер слова или фразы в строке, начиная с 1.
  • s - анализируемая строка для выделения из неё слов или фраз.
  • q - строка символов - кавычек для выделения фраз.

Результат:

Это группа функций для анализа строки, для разбиения её на слова (word) или фразы (phrase).

Описание:

Данный набор функций используется для анализа строк и разбиения строк на слова или фразы.

Словом считается непрерывная цепочка символов, отличных от символов - разделителей, узнать или задать которые можно функцией worddelims. Слова отделены друг от друга символами - разделителями. Функция extractword(n,s) выделяет из строки, содержащей wordcount(s) слов, слово номер n. Функция skipwords(n,s) пропускает в строке n слов, и возвращает остаток строки.

Фразой считается либо слово (без кавычек), либо цепочка символов, заключенных в кавычки. Фразы отделены друг от друга символами - разделителями, однако внутри кавычек разделители допускаются (кроме нуля и EOL), а для передачи кавычек используется удвоение кавычек. Таким образом, Фразы являются расширением понятия Слов, с учетом кавычек. Символы кавычек узнаются/задаются вызовом phrasequotes. Функция extractphrase(n,s) выделяет из строки, содержащей phrasecount(s) фраз, фразу номер n. Функция skipphrases(n,s) пропускает в строке n фраз, и возвращает остаток строки.

worddelims(d) - возвращает строку, состоящую из текущего набора символов - разделителей слов или фраз. Кроме того, функция задает новый набор символов - разделителей слов, заданных непустой строкой d. Если же строка d пустая, набор символов - разделителей не меняется. Поэтому вызовом worddelims('') можно проверять текущий набор символов - разделителей без его изменения. По умолчанию в набор символов - разделителей слов входят:
  • chr(32) - Space - пробел.
  • chr(09) - Tab - табуляция.
  • chr(13) - CR - возврат каретки.
  • chr(10) - LF - перевод строки.
  • chr(44) - ',' - запятая.
  • chr(59) - ';' - точка с запятой.
  • chr(61) - '=' - знак равенства.

Это соответствует регулярному выражению /[\t\n\r ,;=]/.

Значение d=dump(0) используется как маркер для сброса разделителей в начальное значение "по умолчанию". Для сброса набора символов разделителей слов в приведенное выше значение "по умолчанию" используйте вызов типа s:=worddelims(dump(0)); или sNul(worddelims(dump(0)));.

Обратите внимание, при изменении символов разделителей слов всегда надо обязательно в начале сохранять, а в конце восстанавливать стандартные символы разделителей. Иначе может нарушиться логика работы стандартных процедур.
    Первый способ:
        s:=worddelims('');          // Сохранили текущие разделители
        sNul(worddelims('|'));      // Задали новые разделители
        n:=WordCount('1|2|3');      // Поработали с ними
        sNul(worddelims(s));        // Восстановили прежние разделители

    Второй способ:
        sNul(worddelims('|'));      // Задали новые разделители
        n:=WordCount('1|2|3');      // Поработали с ними
        sNul(worddelims(dump(0)));  // Восстановили стандартные разделители
         
Не забывайте делать это при работе с worddelims.

wordcount(s) - находит число слов, содержащихся в строке s. Если строка s пустая или состоит только из разделителей, возвращается 0.

extractword(n,s) - выделяет из строки s слово номер n. Значение n лежит в интервале 1..wordcount(s). Если строка s пустая или состоит только из разделителей, или если значение n не лежит в интервале 1..wordcount(s), возвращается пустая строка.

skipwords(n,s) - пропускает в строке s заданное число n слов, а также лишние разделители, и затем возвращает остаток строки (без пропущенных слов). Значение n лежит в интервале 1..wordcount(s). Если строка s пустая или состоит только из разделителей, или если значение n больше wordcount(s), возвращается пустая строка.

phrasequotes(q) - возвращает строку, состоящую из текущего набора символов - кавычек для выделения фраз. Кроме того, функция задает новый набор символов - кавычек, заданных непустой строкой q. Если же строка q пустая, набор символов - кавычек не меняется. Поэтому вызовом phrasequotes('') можно проверять текущий набор символов - кавычек без его изменения. По умолчанию в набор символов - кавычек входят:
  • chr(34) - двойная кавычка (") или знак цитаты (QuoteMark).
  • chr(39) - одинарная кавычка (') или апостроф (Apostrophe).

Это соответствует регулярному выражению /[\"\']/.

Значение q=dump(0) используется как маркер для сброса кавычек в начальное значение "по умолчанию". Для сброса набора символов кавычек в приведенное выше значение "по умолчанию" используйте вызов типа s:=phrasequotes(dump(0)); или sNul(phrasequotes(dump(0)));.

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

phrasecount(s) - находит число фраз, содержащихся в строке s. Если строка s пустая или состоит только из разделителей, возвращается 0. Число фраз отличается от числа слов, если в строке используются кавычки. Если же кавычек нет, число фраз совпадает с числом слов.

extractphrase(n,s) - выделяет из строки s фразу номер n. Фраза может быть либо обычным словом, либо строкой в кавычках, при этом внутри кавычек допускаются разделители и (удвоенные) кавычки. Значение n лежит в интервале 1..phrasecount(s). Если строка s пустая или состоит только из разделителей, или если значение n не лежит в интервале 1..phrasecount(s), возвращается пустая строка. При выделении фразы кавычки из фразы убираются, а удвоенные кавычки внутри кавычек преобразуются в одинарные кавычки, как принято в большинстве библиотек и языков программирования.

skipphrases(n,s) - пропускает в строке s заданное число n фраз, а также лишние разделители, и затем возвращает остаток строки (без пропущенных фраз). Значение n лежит в интервале 1..phrasecount(s). Если строка s пустая или состоит только из разделителей, или если значение n больше phrasecount(s), возвращается пустая строка.

phraselisttotextlines(n,s) - разбирает строку s на фразы, и возвращает текст, состоящий из фраз (уже без кавычек), разделенный на строки с помощью EOL. Эта функция позволяет быстро преобразовать список фраз в текст для последующего анализа.

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

Следует иметь в виду, что в текущей реализации функции разбора слов wordcount, extractword, skipwords работают с короткими строками (до 255 символов). Поэтому для работы с длинными строками надо использовать другие функции. Функции работы с фразами работают с длинными строками и не имеют этого недостатка.

Пример:

          d:=worddelims('');           {Узнать текущий набор разделителей}
          d:=worddelims(' =');         {Сохранить текущий и задать новый набор разделителей}
          n:=wordcount('x = 123');     {n=2}
          s:=extractword(1,'x = 123'); {s='x'}
          s:=extractword(2,'x = 123'); {s='123'}
          if extractword(1,s)='x' then x:=rval(extractword(2,s)); {простой интерпретатор}
          s:='one "two three" four';   { строка содержит 4 слова или 3 фразы }
          n:=PhraseCount(s);           { 3 фразы }
          p:=ExtractPhrase(2,s);       { p='two three' }
         

Daq Pascal Api -> crlf eol lineending slinebreak directoryseparator pathdelim pathseparator pathsep

Определение:

function crlf:string
function eol:string
function lineending:string
function slinebreak:string
function directoryseparator:char
function pathdelim:char
function pathseparator:char
function pathsep:char
См. также функцию poseol.

Аргументы:

Нет.

Результат:

Возвращает разделители для строк текста, каталогов (директорий) и путей поиска.

Описание:

CRLF - возвращает разделитель строк текста для Windows.
EOL, LineEnding или sLineBreak - возвращает разделитель строк (EOL = End Of Line) для текущей операционной системы.
Это наиболее предпочтительный способ разделения строк текста, т.к. он учитывает тип операционной системы.
В операционной системе Windows принят разделитель строк CR,LF=chr(13),chr(10).
В операционной системе Linux принят разделитель строк LF=chr(10).
В операционной системе MacOS принят разделитель строк CR=chr(13).
См. также функцию poseol.

DirectorySeparator и PathDelim - возвращает разделитель каталогов для текущей операционной системы.
В операционной системе Windows принят разделитель каталогов \.
В операционной системе Unix принят разделитель каталогов /.

PathSeparator и PathSep - возвращает разделитель путей для текущей операционной системы.
В операционной системе Windows принят разделитель путей ;.
В операционной системе Unix принят разделитель путей :.

Пример:

          b:=echo('x=1'+CRLF+'y=2');       {Напечатает y с новой строки - под Windows}
          b:=echo('x=1'+LineEnding+'y=2'); {Напечатает y с новой строки - для всех OC}
          b:=echo('x=1'+EOL+'y=2');        {Напечатает y с новой строки - для всех OC}
         

Daq Pascal Api -> adjustlinebreaks

Определение:

function adjustlinebreaks(str:string):string

Аргументы:

str - строка исходного текста для обработки.

Результат:

Возвращает строку с исправленными разделителями строк текста CR/LF.

Описание:

Стандартизует разделители строк текста в CR/LF. В операционной системе Windows принят разделитель строк CR/LF=chr(13)/chr(10). AdjustLineBreaks настраивает все разрывы строк в данной строке str, чтобы они были истинными последовательностями CR/LF. Функция изменяет любые символы CR, за которыми не следует LF (как принято в MAC), и любые символы LF, которым не предшествует CR (как принято в Linux), на пары CR/LF. Она также преобразует пары LF/CR в пары CR/LF. Пара LF/CR часто встречается в текстовых файлах Unix.

Пример:

          b:=echo(AdjustLineBreaks('x=1'+cr+'y=2'+lf)); {Напечатает y с новой строки}
         

Daq Pascal Api -> stringreplace rfreplaceall rfignorecase

Определение:

function stringreplace(str,old,new:string; flags:integer):string

Аргументы:

str - строка исходного текста для обработки.
old - старая строка, которую надо заменить на новую.
new - новая строка, которая заменяет старую.
flags - флаги опций замены.

Результат:

Возвращает строку текста str с заменой строки old на строку new.

Описание:

Выполняет замену строк в тексте str, при которой старая old заменяется на новую new.
Параметр flags задает битовую маску опций замены:
  • rfReplaceAll = 1 - заменять все строки old на new (а не только первую).
  • rfIgnoreCase = 2 - игнорировать разницу в регистре символов при замене.

Пример:

          s:=StringReplace('a1 b1 c1','1','2',rfReplaceAll+rfIgnoreCase); // s='a2 b2 c2'
         

Daq Pascal Api -> ansiquotedstr ansidequotedstr ansiskipquotedstr

Определение:

function ansiquotedstr(s:string; q:char):string
function ansidequotedstr(s:string; q:char):string
function ansiskipquotedstr(s:string; q:char):string

Аргументы:

s - строка исходного текста для обработки.
q - символ кавычки, который надо добавить или убрать.

Результат:

Возвращает строку добавленными или удаленными кавычками. В качестве кавычек обычно используются константы QuoteMark (") или (реже) Apostrophe (').

Описание:

Функции позволяют добавлять или убирать кавычки в строках.

ansiquotedstr(s,q) добавляет кавычки q в начале и конце строки s. Кавычки внутри строки, если они там есть, удваиваются. Будьте внимательны - функция не проверяет, строка уже закавычена или еще нет. Повторное применение функции приведет к удвоению уже имеющихся кавычек.

ansidequotedstr(s,q) выделяет из текста s строку, заключенную в кавычки q. Кавычки должны быть первым символом текста. Если строка не начинается с кавычки, возвращается исходная строка. Если после закавыченной строки есть остаток, он отбрасывается. Двойные кавычки внутри строки преобразуются в одинарные. Фактически функция выделяет первое закавыченное слово.

ansiskipquotedstr(s,q) выделяет из текста s остаток строки, следующий после строки, заключенной в кавычки q. Кавычки должны быть первым символом текста. Если строка не начинается с кавычки, возвращается исходная строка. Фактически функция пропускает первое закавыченное слово.

Пример:

          writeln(AnsiQuotedStr('Привет мир.',QuoteMark));       // Напечатает "Привет мир."
          writeln(AnsiDeQuotedStr('"Привет мир."',QuoteMark));   // Напечатает  Привет мир.
          writeln(AnsiDeQuotedStr('"Привет" мир.',QuoteMark));   // Напечатает  Привет
          writeln(AnsiSkipQuotedStr('"Привет" мир.',QuoteMark)); // Напечатает   мир.
         

Daq Pascal Api -> extractfirstparam skipfirstparam isoption getoptionvalue ansiquotedifneed

Определение:

function extractfirstparam(s:string; q:char):string
function skipfirstparam(s:string; q:char):string
function isoption(s,o:string):boolean
function getoptionvalue(s:string):string
function ansiquotedifneed(s:string; q:char):string

Аргументы:

s - строка исходного текста для обработки.
q - символ кавычки, который используется при обработке.
o - список опций для проверки (например, '-h,--help').

Результат:

Функции для разбора и формирования командной строки, содержащей кавычки и опции.

Описание:

Функции позволяют выполнять разбор или формирование командной строки, которая может содержать кавычки для передачи параметров с пробелами, табуляциями, запятыми и т.д., а также опции разного вида. В качестве кавычек обычно используются константы QuoteMark (") или (реже) Apostrophe ('). Для разделения параметров между собой используются также пробелы (разделители), используемые для разбора слов, см. WordDelims.

ExtractFirstParam(s,q) выделяет из строки s первый параметр, при этом допустимо использовать кавычки q. Если кавычек нет, выделяется первое слово. Если кавычки есть, выделяется параметр, заключенный в кавычки. Результат возвращается без кавычек. Следует учитывать, что при выделении параметра используются пробелы (разделители), используемые для разбора слов, см. WordDelims.

SkipFirstParam(s,q) пропускает первый параметр текста из строки s и возвращает остаток строки (после удаления пробелов слева), при этом параметр может быть заключен в кавычки q. Следует учитывать, что при выделении параметра используются пробелы (разделители), используемые для разбора слов, см. WordDelims.

IsOption(s,o) проверяет, является ли строка аргумента s опцией. Если задан список опций o, то проверяется также равенство аргумента с одной из этих опций.
Виды опций:
          /V         короткие опции в стиле Windows
          /VERBOSE   длинные  опции в стиле Windows
          //V        короткие опции в стиле WScript
          //VERBOSE  длинные  опции в стиле WScript
          -v         короткие опции в стиле Linux
          -verbose   длинные  опции в стиле Linux
          --verbose  длинные  опции в стиле GNU
         
При разборе опций допускаются все виды опций, а регистр не учитывается, т.е. опции /help, /HELP, -help, -HELP будут эквивалентными. В опциях также допускается параметр, отделенный знаком равно (=), например, --inifile=params.ini.

GetOptionValue(s) выделяет из строки с опцией s параметр после знака =. Например, GetOptionValue('--inifile=params.ini') вернет 'params.ini'.

AnsiQuotedIfNeed(s,q) заключает строку s параметра в кавычки q, если в это нужно. Строку надо заключать в кавычки, если в ней есть пробелы (разделители), используемые для разбора слов, см. WordDelims. Если в строке есть пробелы, она помещается в кавычки, если нет - передается "как есть".

Примечания:
  1. Набор пробелов по умолчанию (WordDelims) содержит запятую (,) и знак равно (=), что может помешать разбору опций с параметрами (например, --users=bob,fred,max). В этом случае надо задать нужные пробелы вызовом WordDelims, при этом надо не забывать восстановить исходный набор пробелов парным вызовом WordDelims с сохраненными пробелами.
  2. Набор функций содержит выделение только первого параметра, однако это не является проблемой, т.к. параметры командной строки обычно обрабатываются в цикле, один за другим. Приведенный ниже пример показывает, как это делать.

Пример:

            {
            Test 3.
            Check Command Line parsing functions.
            }
            procedure Test3;
             procedure Test(cmdline:String);
             var args,arg,del:String; parnum,optnum:Integer;
             begin
              del:=WordDelims(' '+CRLF);
              parnum:=0; optnum:=0;
              args:=cmdline; arg:='';
              writeln('Process Command Line: ',args);
              while not IsEmptyStr(args) do begin
               arg:=ExtractFirstParam(args,QuoteMark);
               args:=SkipFirstParam(args,QuoteMark);
               if IsOption(arg,'') then begin
                optnum:=optnum+1;
                writeln(' Found option[',optnum:1,']: ',arg);
                if IsOption(arg,'-h,--help')  then writeln(' Option is --help');
                if IsOption(arg,'--verbose')  then writeln(' Option is --verbose');
                if IsOption(arg,'--filename') then writeln(' Option is --filename with param '+GetOptionValue(arg));
               end else begin
                writeln(' Found parameter[',parnum:1,']: '+AnsiQuotedIfNeed(arg,QuoteMark));
                parnum:=parnum+1;
               end;
              end;
              del:=WordDelims(del);
              args:=''; arg:=''; del:='';
             end;
            begin
             writeln;
             writeln;
             Test('demo /h');
             Test('demo --help');
             Test('demo --version');
             Test('demo --verbose --filename=note.txt "Test is OK" "Life is good" Bye!');
            end;

            Результат вызова:

            Process Command Line: demo /h
             Found parameter[0]: demo
             Found option[1]: /h
             Option is --help
            Process Command Line: demo --help
             Found parameter[0]: demo
             Found option[1]: --help
             Option is --help
            Process Command Line: demo --version
             Found parameter[0]: demo
             Found option[1]: --version
            Process Command Line: demo --verbose --filename=note.txt "Test is OK" "Life is good" Bye!
             Found parameter[0]: demo
             Found option[1]: --verbose
             Option is --verbose
             Found option[2]: --filename=note.txt
             Option is --filename with param note.txt
             Found parameter[1]: "Test is OK"
             Found parameter[2]: "Life is good"
             Found parameter[3]: Bye!
         

Daq Pascal Api -> strconv

Определение:

function strconv(how,data:string):string

Аргументы:

  • how - строка метода преобразования.
  • data - строка данных для преобразования.

Результат:

StrConv (от String Convert) посимвольно преобразует строку data методом, заданным в строке how.

Описание:

Возвращает строку data, посимвольно преобразованную по методу how. Метод how имеет вид НАЧАЛО РАЗДЕЛИТЕЛЬ КОНЕЦ, где НАЧАЛО - предполагаемая начальная кодировка строки (OEM, ANSI, UTF8 и т.д.), КОНЕЦ - желаемая конечная кодировка строки после преобразования, РАЗДЕЛИТЕЛЬ - пробел, знак подчеркивания (_) или тире (-). Метод how не чувствителен к регистру симолов и имеет несколько эквивалентных значений, например, 'ansi utf8' эквивалентно 'ansi_utf8' или 'ansi-utf8'. То есть разделитель между левым (начальным) и правым (конечным) кодом преобразования может быть символом пробела, подчеркивания (_) или тире (-). Поэтому в следующей таблице указываются только значения метода how с тире, например, 'ansi-utf8' означает "преобразовать строку ANSI в кодировку UTF8".

С учетом вышеуказанного замечания, метод how принимает следующие значения:

  • oem-ansi - преобразование OEM -> ANSI.
  • ansi-oem - преобразование ANSI -> OEM.
  • dos-win - преобразование DOS CP866 -> WIN CP1251.
  • win-dos - преобразование WIN CP1251 -> DOS CP866.
  • koi-win - преобразование KOI8 -> WIN1251.
  • win-koi - преобразование WIN1251 -> KOI8.
  • utf8-ansi - преобразование UTF8 -> ANSI.
  • ansi-utf8 - преобразование ANSI -> UTF8.
  • ascii-uppercase - преобразование ASCII строки в верхний регистр.
  • ascii-lowercase - преобразование ASCII строки в нижний регистр.
  • ansi-uppercase - преобразование ANSI строки в верхний регистр.
  • ansi-lowercase - преобразование ANSI строки в нижний регистр.
  • utf8-uppercase - преобразование UTF8 строки в верхний регистр.
  • utf8-lowercase - преобразование UTF8 строки в нижний регистр.

Также допустимо (но не рекомендовано) использовать устаревшие значения how:
  • oem2ansi - преобразование OEM -> ANSI (устаревшая форма).
  • ansi2oem - преобразование ANSI -> OEM (устаревшая форма).
  • dos2win - преобразование DOS CP866 -> WIN CP1251 (устаревшая форма).
  • win2dos - преобразование WIN CP1251 -> DOS CP866 (устаревшая форма).
  • koi2win - преобразование KOI8 -> WIN1251 (устаревшая форма).
  • win2koi - преобразование WIN1251 -> KOI8 (устаревшая форма).

Если метод how не равен одному из указанных, функция возвращает исходную строку data.

Ограничений на длину строки data нет.

При использовании преобразования ansi-utf8 и utf8-ansi появляется возможность преобразования текста в Unicode. При этом следует учитывать, что длина строки может измениться, а некоторые символы могут быть замещены "заглушками" (если текст Unicode не может быть преобразован в ANSI). Кроме того, результат преобразования utf8-ansi может быть пустой строкой, если преобразование невозможно из-за нарушения формата UTF8.

Пример:

          s:=strconv('oem ansi',s);   // Convert DOS to Windows code page
          s:=strconv('ansi oem',s);   // Convert Windows to DOS code page
          s:=strconv('ansi-utf8',s);  // Convert ANSI to UTF8 code page
          s:=strconv('utf8-ansi',s);  // Convert UTF8 to ANSI code page
         

Daq Pascal Api -> base16_encode base16_decode base32_encode base32_decode base32_alphabet base64_encode base64_decode hex_encode hex_decode nice_encode nice_decode mime_encode mime_decode url_packed url_encode url_decode crypt_encode crypt_decode crypt_ctrl getmd5fromstr getmd5fromtext getmd5fromfile dump dump2b dump2c dump2i dump2r dump2f dumpf

Определение:

function base16_encode(s:string):string
function base16_decode(s:string):string
function base32_encode(s:string):string
function base32_decode(s:string):string
function base32_alphabet(a:string):string
function base64_encode(s:string):string
function base64_decode(s:string):string
function hex_encode(s:string):string
function hex_decode(s:string):string
function nice_encode(s:string):string
function nice_decode(s:string):string
function mime_encode(s:string):string
function mime_decode(s:string):string
function url_packed(s:string):string
function url_encode(s:string):string
function url_decode(s:string):string
function crypt_encode(s,k:string):string
function crypt_decode(s,k:string):string
function crypt_ctrl(p:string):string
function getmd5fromstr(s:string):string
function getmd5fromtext(t:integer):string
function getmd5fromfile(f:string):string
function dump(b:boolean):string
function dump(c:char):string
function dump(i:integer):string
function dump(r:real):string
function dump2b(s:string):boolean
function dump2c(s:string):char
function dump2i(s:string):integer
function dump2r(s:string):real
function dump2f(s:string):real
function dumpf(r:real):string

Аргументы:

  • s - преобразуемая строка.
  • k - ключ для шифрования.
  • p - параметры шифрования.
  • t - преобразуемый текст.
  • f - имя файла.
  • a - алфавит преобразования.

Результат:

Группа функций выполняет кодирование/декодирование двоичных данных и текста.

Описание:

Эта группа функций выполняет кодирование/декодирование двоичных данных и текста. Эти функции сделаны для обмена двоичными данными через текстовый канал связи. Текстовый канал связи может быть реализован в виде COM-порта, анонимного или именованного канала, текстового файла и т.д. Текстовые каналы связи удобны тем, что позволяют легко наблюдать сообщения через консоль, интерпретировать сообщения, записывать и считывать данные обычными процедурами writeln,readln. Однако двоичные данные через текстовый канал связи передавать напрямую нельзя - ведь в произвольных двоичных данных могут присутствовать специальные управляющие символы, например, возврат каретки, пробел. Эти символы при приемо\передаче потеряются, так как будут восприняты как символы форматирования текста. В результате двоичные данные будут искажены. Можно, конечно, передавать данные в виде десятичных чисел, но тогда могут возникать ошибки округления, что не желательно.

По перечисленным причинам для передачи двоичных данных по текстовому каналу эти двоичные данные лучше всего закодировать в BASE16 (синоним HEX), BASE32 (синоним NICE), BASE64 (синоним MIME), чтобы там были только отображаемые (печатные) символы, а при приеме декодировать обратно. Эти методы кодирования отличаются битностью (4,5,6 бит) и таблицами алфавита, то есть набора символов (16,32,64 символа), используемых для кодирования данных. Для Base32 есть несколько распространенных вариантов алфавитов (при этом алгоритм кодирования остается одинаковым).

Формальные описания алгоритмов кодирования Base-16/32/64 даны в документах: rfc4648.txt, rfc3548.txt, rfc2938.txt, crockford-base32-encoding.htm, human-oriented-base-32-encoding.txt.

Таблица сравнения алгоритмов

  Алгоритм  Битность  Алфавит     Коэффициент Регистр символов  Генерация имен  Передача текста  Читабельность
  base16    4 бита    16 символа  в 2 раза    Не чувстителен    Пригоден        Возможна         Приемлемая
  base32    5 бит     32 символа  в 8/5 раз   Не чувствителен   Пригоден        Возможна         Хорошая
  base64    6 бит     64 символа  в 4/3 раз   Чувствителен      Не пригоден     Возможна         Плохая
         

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

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

Другой проблемой является то, что в Daq Pascal нет указателей и поэтому нет прямого доступа к двоичным данным. Вместо этого есть функции dump,dump2i,dump2r, позволяющие преобразовать двоичное содержимое переменной в строку и наоборот. Поэтому при кодировании - декодировании двоичных данных эти функции также используются.

base16_encode(s) и его синоним hex_encode(s) кодирует строку текста s по алгоритму BASE16,HEX. Входная строка может содержать произвольный набор двоичных данных. На выходе после кодирования получается строка, содержащая только набор 16 отображаемых символов из алфавита: '0123456789ABCDEF'. Размер закодированной строки увеличивается в 2 раза по сравнению с исходным. Поскольку закодированная строка содержит только отображаемые (печатные) символы, ее можно без искажений хранить в текстовых файлах и передавать через текстовые каналы связи, записывать и считывать процедурами writeln,readln. Можно применять к строкам функции преобразования регистра locasestr,upcasestr, данные при этом не будут искажены. Данные в шестнадцатеричном формате поэтому можно хранить в виде переменных в конфигурационном файле. Надо не забывать, что длина данных при кодировании возрастает в 2 раза. Поскольку многие функции DaqPascal работают только с короткими строками длиной до 255 символов, не следует кодировать слишком длинные строки - желательно чтобы длина исходной строки не превышала 127 символов.

base16_decode(s) и его синоним hex_decode(s) декодирует строку текста s, закодированную по алгоритму BASE16,HEX. Входная строка может содержать произвольный набор двоичных данных, но при декодировании игнорируются все символы, кроме набора 16 отображаемых символов из алфавита: '0123456789ABCDEF' без учета регистра. Это значит, что закодированная строка может при приемо-передаче быть подвергнута некоторым преобразованиям, не влияющим на результат декодирования. Например, добавление или вставка пробелов и преобразование регистра символов не влияют на декодирование, так как эти символы игнорируются. Однако не допускается изменение порядка символов и т.д. Результатом декодирования является строка, содержащая исходные двоичные данные. Процесс кодирования/декодирования можно представить так:

          1) s1                исходные данные
          2) sx=hex_encode(s1) данные для передачи в канал
          3) ...sx...          передача данных через канал
          4) s2=hex_decode(sx) восстановление, s2=s1
         
Имеет место тождество s=hex_decode(hex_encode(s)).

base32_encode(s) и его синоним nice_encode(s) кодирует строку текста s по алгоритму BASE32 (название nice "приятный" взято потому что при кодировании получается читабельный, т.е. приятный текст). Входная строка может содержать произвольный набор двоичных данных. На выходе после кодирования получается строка, содержащая только набор 32 отображаемых символов, входящих в алфавит, см. описание функции base32_alphabet(). Размер закодированной строки увеличивается примерно в 8/5=1.6 раза по сравнению с исходным. Поскольку закодированная строка содержит только отображаемые символы, ее можно без искажений хранить в текстовых файлах и передавать через текстовые каналы связи, записывать и считывать процедурами writeln,readln. Можно применять к строкам функции преобразования регистра locasestr,upcasestr, данные при этом не будут искажены. Данные в base32 формате поэтому можно хранить в виде переменных в конфигурационном файле. Надо не забывать, что длина данных при кодировании возрастает в 1.6 раза. Поскольку многие функции DaqPascal работают только с короткими строками длиной до 255 символов, не следует кодировать слишком длинные строки - желательно чтобы длина исходной строки не превышала 150 символов. Заметим, что Base32 очень хорошо подходит для генерации читабельных идентификаторов, имен файлов и других именованных объектов.

base32_decode(s) и его синоним nice_decode(s) декодирует строку текста s, закодированную по алгоритму BASE32. Входная строка может содержать произвольный набор двоичных данных, но при декодировании игнорируются все символы, кроме набора 16 отображаемых символов, содержащихся в алфавите (см. base32_alphabet) без учета регистра. Это значит, что закодированная строка может при приемо-передаче быть подвергнута некоторым преобразованиям, не влияющим на результат декодирования. Например, добавление или вставка пробелов и преобразование регистра символов не влияют на декодирование, так как эти символы игнорируются. Однако не допускается изменение числа и порядка символов и т.д. Результатом декодирования является строка, содержащая исходные двоичные данные. Процесс кодирования/декодирования можно представить так:

          1) s1                исходные данные
          2) sx=base32_encode(s1) данные для передачи в канал
          3) ...sx...          передача данных через канал
          4) s2=base32_decode(sx) восстановление, s2=s1
         
Имеет место тождество s=base32_decode(base32_encode(s)).

base32_alphabet(a) возвращает текущий и (если указан) задает новый алфавит (набор символов кодирования) а для кодирования по алгоритму BASE32. Если задана пустая строка а, то функция не меняет алфавит, а только возвращает текущий алфавит.
Алфавит может быть задан в строке а непосредственно или по номеру/имени.
Если строка а имеет длину 32 символа, она задает алфавит непосредственно. 33-й символ в этом случае может также задать символ заполнения блока (обычно это = знак равенства).
Также доступно несколько распространенных алфавитов по номерам или именам (псевдонимам):
'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=' - алфавит 0 или base32 или rfc4648.3.
'0123456789ABCDEFGHIJKLMNOPQRSTUV=' - алфавит 1 или base32hex или rfc4648.4.
'0123456789ABCDEFGHJKMNPQRSTVWXYZ' - алфавит 2 или crockford.
'ybndrfg8ejkmcpqxot1uwisza345h769' - алфавит 3 или zooko или zbase32 или human или nice.
Текущий алфавит можно узнать или задать вызовом base32_alphabet('').
В качестве алфавита по умолчанию в DAQ Pascal принят алфавит 3 = zooko, так как это самый читабельный из известных в Сети base32 алфавитов. Название алфавита zooko берется от имени предложившего его автора (Zooko O'Whielacronx). Этот автор также предложил псевдоним алфавита zbase32. Псевдоним human происходит от характеристики human-readable (человечный,читабельный). Псевдоним nice происходит от слова nice - приятный, удобный.

         abc:=base32_alphabet('3');         сохранить текущий алфавит и задать алфавит 3=Zooko
         d:=base32_encode(s)                закодировать данные
         s:=base32_decode(d)                декодировать данные
         sNul(base32_alphabet(abc));        восстановить исходный алфавит
         //
         // Test Base16/32/64 functions.
         //
         procedure TestBase32;
         var a,a0,a1,a2,a3,s:string; i,n:Integer;
         begin
          n:=16; a:=base32_alphabet('');
          sNul(base32_alphabet('0')); a0:=base32_alphabet(''); sNul(base32_alphabet('1')); a1:=base32_alphabet('');
          sNul(base32_alphabet('2')); a2:=base32_alphabet(''); sNul(base32_alphabet('3')); a3:=base32_alphabet('');
          for i:=0 to 16 do begin
           s:=copy('ABCDEFGHIJKLMNOPQRSTUVWXYZ',1,i);
           writeln(s:n,' ',Ord(s=base16_decode(base16_encode(s))):1,' ',hex_encode(s) :n*2,' ',base16_encode(s));
           sNul(base32_alphabet(a0));
           writeln(s:n,' ',Ord(s=base32_decode(base32_encode(s))):1,' ',nice_encode(s):n*2,' ',base32_encode(s));
           sNul(base32_alphabet(a1));
           writeln(s:n,' ',Ord(s=base32_decode(base32_encode(s))):1,' ',nice_encode(s):n*2,' ',base32_encode(s));
           sNul(base32_alphabet(a2));
           writeln(s:n,' ',Ord(s=base32_decode(base32_encode(s))):1,' ',nice_encode(s):n*2,' ',base32_encode(s));
           sNul(base32_alphabet(a3));
           writeln(s:n,' ',Ord(s=base32_decode(base32_encode(s))):1,' ',nice_encode(s):n*2,' ',base32_encode(s));
           writeln(s:n,' ',Ord(s=base64_decode(base64_encode(s))):1,' ',mime_encode(s):n*2,' ',base64_encode(s));
          end;
          writeln('base32_alphabet(0): ',a0,'  also named base32, rfc4648.3');
          writeln('base32_alphabet(1): ',a1,'  also named base32hex, rfc4648.4');
          writeln('base32_alphabet(2): ',a2,'   also named crockford');
          writeln('base32_alphabet(3): ',a3,'   also named zooko, human, nice, zbase32');
          sNul(base32_alphabet(a)); a0:=''; a1:=''; a2:=''; a3:=''; a:=''; s:='';
         end;
         

base64_encode(s) и его синоним mime_encode(s) кодирует строку текста s по алгоритму BASE64,MIME. Входная строка может содержать произвольный набор двоичных данных. На выходе после кодирования получается строка, содержащая только набор 64 отображаемых символов: 'a'..'z','A'..'Z','0'..'9','/','+'. Размер закодированной строки увеличивается примерно в 4/3 раза по сравнению с исходным. Поскольку закодированная строка содержит только отображаемые символы, ее можно без искажений хранить в текстовых файлах и передавать через текстовые каналы связи, записывать и считывать процедурами writeln,readln. Только не надо применять к строкам функции преобразования регистра locasestr,upcasestr, иначе данные будут искажены. Надо также не забывать, что длина данных при кодировании возрастает примерно в 4/3 раз. Поскольку многие функции DaqPascal работают только с короткими строками длиной до 255 символов, не следует кодировать слишком длинные строки - желательно чтобы длина исходной строки не превышала 190 символов.

base64_decode(s) и его синоним mime_decode(s) декодирует строку текста s, закодированную по алгоритму BASE64,MIME. Входная строка может содержать произвольный набор двоичных данных, но при декодировании игнорируются все символы, кроме набора 64 отображаемых символов: 'a'..'z','A'..'Z','0'..'9','/','+'. Это значит, что закодированная строка может при приемо-передаче быть подвергнута некоторым преобразованиям, не влияющим на результат декодирования. Например, добавление или вставка пробелов не влияют на декодирование, так как эти символы игнорируются. Однако не допускается изменение порядка символов, преобразование регистра и т.д. Результатом декодирования является строка, содержащая исходные двоичные данные. Процесс кодирования/декодирования можно представить так:

          1) s1                 исходные данные
          2) sx=mime_encode(s1) данные для передачи в канал
          3) ...sx...           передача данных через канал
          4) s2=mime_decode(sx) восстановление, s2=s1
         
Имеет место тождество s=mime_decode(mime_encode(s)).

url_packed(s) кодирует строку текста s по "нестрогому" алгоритму URL-encode. "Нестрогость" заключается в том, что набор "непечатных" символов уменьшен и отличается от стандартного (используемого в url_encode в соответствии со стандартом HTTP). Это значит, что в строку вносятся только минимальные изменения, в отличие от url_encode, которая кодирует также русские буквы и другие полезные символы. Кроме того, url_packed будет давать чаще всего более компактную строку, чем url_encode. Работает url_packed так:

  • Непечатными символами считаются #0..#32 (в том числе CR, LF, пробел, табуляция), символ +, символ %, запятая, символ ;, символ =.
  • Все остальные сиволы считаются печатными.
  • Все печатные символы передаются без изменений.
  • Символ пробела заменяется на +.
  • Символ + заменяется на %2B.
  • Символ % заменяется на %%.
  • Остальные непечатные символы преобразуются в %xx где xx - шестнадцатеричное представление числа. Например, CRLF преобразуется в %0D%0A.
Коэффициент увеличения данных в этом методе кодирования может составлять от 1 (для обычных текстовых данных) до 3 (если в данных много непечатных символов). "Нестрогий" алгоритм URL-encode используется для передачи списков параметров, когда произвольные строки надо упаковать в слова, которые можно разбирать через ExtractWord. После кодирования строка данных может передаваться по текстовому каналу без искажений.

url_encode(s) кодирует строку текста s по "строгому" алгоритму URL-encode. Этот метод кодирования соответствует стандарту, используемому протоколом HTTP для передачи адресов URL (Universal Resource Location) и других данных. Работает url_encode так:

  • Печатными символами считаются A..Z a..z * @ . _ - 0..9 $ ! ' ( ).
  • Все остальные сиволы считаются непечатными.
  • Все печатные символы передаются без изменений.
  • Символ пробела заменяется на %20.
  • Символ + заменяется на %2B.
  • Символ % заменяется на %25.
  • Остальные символы преобразуются в %xx где xx - шестнадцатеричное представление числа. Например, CRLF преобразуется в %0D%0A.
Коэффициент увеличения данных в этом методе кодирования может составлять от 1 (для обычных текстовых данных) до 3 (если в данных много непечатных символов). Алгоритм URL-encode используется протоколом HTTP для передачи URL-адресов, запросов QUERY_STRING, а также содержимого CONTENT. После кодирования строка данных может передаваться по текстовому каналу без искажений.

url_decode(s) декодирует строку текста s, закодированную по алгоритму URL-encode функциями url_packed, url_encode. Эта функция может применяться для декодирования данных HTTP - запросов, таких как URL-адреса, запросы QUERY_STRING, содержимое форм CONTENT.
Интересно, что декодер вообще не меняет строки, в которых нет символов + и %. Если точнее, + заменяется на пробел, %% на %, %hh на chr(hh), а остальной текст передается без изменений. Так, декодер не меняет имен файлов или тегов, ведь в них обычно нет символов + и %. Поэтому многие функции используют URL-encode-кодированные параметры. Например, оба вызова

          filecopy(file1+' '+file2)
          filecopy(URL_Packed(file1)+' '+URL_Packed(file2))
         
допустимы, если имена файлов не содержат пробелов, + и %. Однако второй вызов позволяет копировать также и файлы с пробелами в именах.

crypt_encode(s,k) кодирует (шифрует) строку текста s с ключем k по алгоритму, заданному вызовами crypt_ctrl. Входная строка может содержать данные в форматах Bin,Hex,Mime. По умолчанию входная строка в формате Bin, то есть в исходной двоичной форме. Перед шифрованием данные декодируются в Bin формат. Затем применяется шифрование по алгоритму, заданному вызовами crypt_ctrl. В результате шифрования получается строка, которую можно безопасно передавать по открытому каналу связи, так как без знания ключа понять ее содержание затруднительно. Для восстановления исходной строки надо знать ключ (пароль) k и параметры шифрования. После шифрования строка кодируется для передачи по открытому каналу связи. Результат может выдаваться в форматах Bin,Hex,Mime. По умолчанию результат выдается в формате Mime.

crypt_decode(s) декодирует и дешифрует строку текста s, зашифрованную с ключем k по алгоритму, заданному вызовами crypt_ctrl. На входе дешифратора имеется зашифрованная строка, принятая по открытому каналу связи. Для ее восстановления надо знать ключ (пароль) k и параметры шифрования. Входная строка может содержать данные в форматах Bin,Hex,Mime. Поэтому строка сначала декодируется в Bin в формат. По умолчанию предполагается, что данные даны в формате Mime, симметрично с выходным форматом crypt_encode. Затем строка дешифруется с паролем k по алгоритму, заданному вызовами crypt_ctrl. Результат может выдаваться в форматах Bin,Hex,Mime. По умолчанию результат выдается в формате Bin, то есть восстанавливается исходная строка. Процесс кодирования/декодирования можно представить так:

          1) s1                    исходные данные
          2) sx=crypt_encode(s1,k) данные для передачи в открытый канал
          3) ...sx...              передача данных через открытый канал
          4) s2=crypt_decode(sx,k) восстановление, s2=s1
         
Имеет место тождество s=crypt_decode(crypt_encode(s,k),k).

crypt_ctrl(p) задает/читает параметры, используемые для шифрования, по заданному выражению p.
Выражение x возвращает текущее значение параметра x.
Выражение x=y присваивает параметру x значение y и возвращает новое значение x.
Выражение x=* присваивает параметру x значение по умолчанию и возвращает новое значение x. Можно задавать такие переменные (в таблице указаны значения по умолчанию):

  • crypt_ctrl('') - выдает все текущие параметры.
  • crypt_ctrl('*') - задает все значения по умолчанию.
  • crypt_ctrl('Kind=Blowfish') - алгоритм шифрования Blowfish/Gost/RC2/RC4/RC5/RC6
  • crypt_ctrl('Mode=CBC') - режим шифрования CBC/OFB/CFB/OFBC
  • crypt_ctrl('IV=') - начальный вектор шифрования crypt_encode, должен быть задан в Mime кодировке. По умолчанию пустая строка, а вообще может использоваться как второй (открытый) ключ шифрования. Значимая длина вектора 8(Blowfish,Gost,RC2,RC5) или 16 байт (RC6).
  • crypt_ctrl('Ei=Bin') - входной формат crypt_encode, Bin/Hex/Mime
  • crypt_ctrl('Eo=Mime') - выходной формат crypt_encode, Bin/Hex/Mime
  • crypt_ctrl('Di=Mime') - входной формат crypt_decode, Bin/Hex/Mime
  • crypt_ctrl('Do=Bin') - выходной формат crypt_decode, Bin/Hex/Mime
Краткая сводка по алгоритмам шифрования:
   Method    BlockSize   Key,bytes   IV,bytes   Modes
   Blowfish  64 bit      1..56       8          ECB,CBC,CFB,OFB,OFBC
   Gost      64 bit      32          8          ECB,CBC,CFB,OFB,OFBC
   RC2       64 bit      1..128      8          ECB,CBC,CFB,OFB,OFBC
   RC4       None        1..256      None       Stream only
   RC5       64 bit      1..256      8          ECB,CBC,CFB,OFB,OFBC
   RC6       128 bit     1..256      16         ECB,CBC,CFB,OFB,OFBC
         
Обычно достаточно использовать значения по умолчанию. При задании других параметров надо иметь в виду следующее. Восстановление данных произойдет только при совпадении секретных паролей k1=k2 на стороне передатчика(1) и приемника(2), а также при при правильных настройках параметров шифрования, то есть при одинаковых значениях параметров Kind1=Kind2, Mode1=Mode2, IV1=IV2 и симметричных значениях форматов Ei1=Do2, Eo1=Di2. Параметры шифрования могут передаваться по каналу открыто, это не влияет на степень защиты. Скрытым должен быть только секретный пароль k.

Группа функций GetMd5FromXXX

  • GetMd5FromStr(s) - контрольная сумма строки s.
  • GetMd5FromText(t) - контрольная сумма текста со ссылкой t.
  • GetMd5FromFile(f) - контрольная сумма файла с именем f.
вычисляет 16-байтную контрольную сумму строки, файла или текста по алгоритму MD5. Результат выдается в Bin формате, для передаче по каналу его надо преобразовывать в Hex,Mime. Контрольные суммы можно использовать для таких целей как
  1. Контроль сохранности строк/текстов/файлов по их контрольной сумме при передаче/хранении.
  2. Идентификация строк/текстов/файлов по их контрольной сумме при предоставлении пользователю прав на использование этих объектов.
  3. Генерация паролей шифрования по строкам/текстам/файлам по их контрольной сумме.

dump(x) извлекает двоичное содержимое x в виде строки. Длина строки будет равна размеру данных x, то есть 1 для boolean,char, 4 для integer, 8 для real. Заметим, что Real эквивалентно типу IEEE Double (вещественное 8 байт).

dump2b(s),dump2c(s),dump2i(s),dump2r(s) возвращает значение переменной, заданной своей двоичной строкой. Если длина строки больше размера данных данного типа, лишние байты игнорируются. Если длина строки меньше размера данных данного типа, недостающие байты зануляются.

Функции dump2f(s) и dumpf(r) работают с типом float или IEEE-754 Single (вещественное 4 байта). Поскольку в Daq Pascal нет типа float, в функцию dumpf(r) данные передаются в виде числа r типа Real. Функция преобразует число к типу float и возвращает его дамп. Наконец, функция dump2f(s) извлекает из строки s число float, преобразует в Real и возвращает его значение.

Пример:

      { Программа - писатель. }
      { Кодирует двоичные данные и записывает в текстовый канал связи. }
      program writer;
      var x,y,z:real; i:integer; s:string;
      begin
       s:=dump(x)+dump(y)+dump(z)+dump(i); {binary dump of (x,y,z,i) record}
       s:=mime_encode(s);                  {now dump converted to MIME}
       writeln(s);                         {write it to text file...}
      end;

      { Программа - читатель. }
      { Читает данные из текстового канала связи и декодирует. }
      program reader;
      var x,y,z:real; i:integer; s:string;
      begin
       readln(s);                          {read data from text file...}
       s:=mime_decode(s);                  {decode data from MIME to binary}
       x:=dump2r(copy(s,1,8));             {extract x value}
       y:=dump2r(copy(s,9,8));             {extract y value}
       z:=dump2r(copy(s,17,8));            {extract z value}
       i:=dump2i(copy(s,25,4));            {extract i value}
      end;
         

Daq Pascal Api -> hashindexof

Определение:

function hashindexof(key:string; size,method:integer):integer

Аргументы:

  • key - строка данных - ключ.
  • size - размер хэш-таблицы или 0.
  • method - метод расчета хэша 0..93.

Результат:

Вычисляет целочисленный хэш-индекс для строки текста key, предназначенный для индексирования в хэш-таблице размера size, методом method.
Также вычисляет контрольные суммы (CRC-8/16/32 и т.д.), которые тоже являются разновидностью хешей.

Описание:

Возвращает по ключевой строке key хэш-индекс, взятый по модулю size (т.е. в диапазоне 0..size-1).
Если указан нулевой размер size=0, то операция "по модулю" не применяется и хэш-индекс будет распределен на всем диапазоне integer:
 if size > 0 then hash:=hash mod size;
Расчет хэша ведется по методу method. Этот параметр определяет алгоритм хеширования, т.е. способ вычисления хеша. В настоящее время в библиотеке есть 94 метода хеширования, включая много вариантов контрольных сумм CRC-8/16/32. Выбор метода определяется целью. Например, для индексирования строк лучше использовать быстрые методы, а для расчета контрольных сумм используются стандартизованные алгоритмы, хотя они работают несколько медленнее. Для индексации строк рекомендуется использовать быстрые методы 0..46 (метод 0 является основным). Для расчета контрольных сумм по разным алгоритмам используются методы 47..92.

Для выбора метода method можно использовать функцию paramstr('HasherCode ...'), которая позволяет определить код (номер) метода по его имени (и наоборот):
          // Вычисление контрольной суммы MODBUS/RTU

          hid:=iValDef(ParamStr('HasherCode Crc16Modbus'),-1);  // получить код хеш-функции CRC-16/MODBUS по имени
          if hid<0 then Trouble('Алгоритм не найден');          // проверить допустимость кода хеш-функции
          checksum:=HashIndexOf(data,0,hid);                    // вычислить контрольную сумму
         
Слово хэш происходит от английского hash - рубить, крошить, мешанина, путаница. Это слово отражает цель хэш-функции - хорошенько перемешать символы ключа key таким образом, чтобы разные строки ключей давали в качестве хэш-индекса числа, которые с высокой вероятностью не совпадают и желательно равномерно распределены по диапазону значений. Хеш-функции часто используются для ускорения поиска строк в ассоциативных массивах, а также в криптографии для хранения или сравнения паролей, а также в качестве контрольных сумм для контроля целостности данных. Например, вместо пароля может запоминаться или передаваться по сети его криптографическая хэш-сумма, чтобы нельзя было узнать пароль даже перехватив сообщение. К криптографическим хэш-функциям (таким как md5, crc32 и т.д.) предъявляются особые требования, в первую очередь криптографической стойкости (невозможности восстановления ключа по значению хэша). Однако такие хэш-суммы сложны и трудоемки в вычислениях. Функция hashindexof реализует в основном не криптографические хэш-функции. Это значит, что предпочтение отдается скорости вычисления хэша, а не его криптографической стойкости. Быстро вычисляемый хэш используется в качестве индекса объекта со строковым ключем key в массиве размера size. Функция hashindexof должна использоваться там, где криптографическая стойкость не важна, но требуется высокая скорость расчета хэша. Её можно использовать для индексирования ассоциативных массивов или проверки обновления некритических данных или метода расчета контрольной суммы, но не стоит использовать в качестве метода для хранения паролей.
Хэш-функция также удобна при обработке и интерпретации строковых выражений. Например, чтобы отслеживать изменения в строковых данных, необязательно хранить дубликаты строк - вместо этого можно хранить значения хэш-сумм. Это следует иметь в виду при написании серверов, которые должны обновлять строковые данные по мере их изменения.
При работе с хэш-индексами всегда надо помнить, что ЛЮБОЙ алгоритм хэширования содержит вероятность коллизий, то есть ситуаций, когда две разные строки дают один хэш. Это значит, что любой алгоритм работы с хэшами должен учитывать вероятность коллизий и правильно их обрабатывать.

Кроме алгоритмов индексирования строк в библиотеке алгоритмов есть также большой набор функций расчета контрольных сумм, такие как CRC-8/16/32 (много вариантов), HART, DCON и т.д. Эти алгоритмы в том или ином виде стандартизованы и применяются в различных протоколах хранения и приемопередачи данных. Эти алгоритмы можно использовать в драйверах соответствующих протоколов.

Ограничений на длину и содержимое строки key нет. Размер size должен быть неотрицательным или нулем. Значение method должно быть в диапазоне 0..93, рекомендуемый метод 0.

Пример:

            // Индексация строк

            key:='ключ для поиска';          - взять ключ для поиска
            hash:=hashindexof(key,1024,0);   - вычислить хэш-индекс для таблицы data[0..1023] по алгоритму 0
            data[hash]:=getdata(key);        - использовать его для индексирования в таблице

            // Вычисление контрольной суммы MODBUS/RTU

            hid:=iValDef(ParamStr('HasherCode Crc16Modbus'),-1);  // получить код хеш-функции CRC-16/MODBUS по имени
            if hid<0 then Trouble('Алгоритм не найден');          // проверить допустимость кода хеш-функции
            checksum:=HashIndexOf(data,0,hid);                    // вычислить контрольную сумму

            // Получение списка имен хеш-функций
            i:=0;
            while (i<256) do begin
             s:=ParamStr('HasherName '+Str(i));
             if s='' then i:=256 else writeln(i:3,' ',s);
             i:=i+1;
            end;

         
В настоящее время в библиотеке есть такие алгоритмы:
              //////////////////////// Алгоритмы хеширования строк:
              0 RS                     основной алгоритм индексирования строк (используется в системе по умолчанию)
              1 RS_W2                  основной алгоритм для индексирования длинных имен (например для имен файлов)
              2 RS_W4                  основной алгоритм для индексирования длинных имен (например для имен файлов)
              3 SDBM                   запасной алгоритм индексирования строк
              4 SDBM_W2                запасной алгоритм для индексирования длинных имен
              5 SDBM_W4                запасной алгоритм для индексирования длинных имен
              6 SDBM_ASM
              7 SDBM_SHF
              8 ROT13                  запасной алгоритм индексирования строк
              9 ROT13_ASM
             10 LY                     запасной алгоритм индексирования строк
             11 FNV
             12 FNV0
             13 FNV1                   запасной алгоритм индексирования строк
             14 FNV1A                  запасной алгоритм индексирования строк
             15 JENKINS
             16 AP
             17 ALSHA1
             18 ALSHA2
             19 MURMUR3
             20 MURMUR3_ASM
             21 BKDR131
             22 BKDR31
             23 BKDR1313
             24 BKDR13131
             25 BKDR131313
             26 DJB
             27 DJB_SHF
             28 JS
             29 LUA
             30 H37
             31 TDH17
             32 TDPJW
             33 PJW
             34 PJW32
             35 ELF
             36 DEK
             37 BP
             38 LOSER
             39 JCL
             40 GOULBURN
             41 ONEATTIME
             42 SBOX
             43 CRAP8
             44 CRAPWOW
             45 SUPERFAST
             46 Murmur2
             ///////////////////////// Алгоритмы расчета контрольных сумм:
             47 Crc8                   см. crccalc.com, CRC RevEnv
             48 Crc8Cdma2000           см. crc_calc, CRC
             49 Crc8Darc
             50 Crc8Dvbs2
             51 Crc8Ebu
             52 Crc8Icode
             53 Crc8Itu
             54 Crc8Maxim
             55 Crc8Rohc
             56 Crc8Wcdma
             57 Crc16CcittFalse
             58 Crc16Arc
             59 Crc16AugCcitt
             60 Crc16Buypass
             61 Crc16Cdma2000
             62 Crc16Dds110
             63 Crc16DectR
             64 Crc16DectX
             65 Crc16Dnp
             66 Crc16En13757
             67 Crc16Genibus
             68 Crc16Maxim
             69 Crc16Mcrf4xx
             70 Crc16Riello
             71 Crc16T10dif
             72 Crc16Teledisk
             73 Crc16Tms37157
             74 Crc16Usb
             75 Crc16A
             76 Crc16Kermit            KERMIT protocol
             77 Crc16Modbus            MODBUS/RTU protocol
             78 Crc16X25
             79 Crc16Xmodem            XMODEM protocol
             80 Crc32
             81 Crc32Bzip2
             82 Crc32C
             83 Crc32D
             84 Crc32Jamcrc
             85 Crc32Mpeg2             CRC-32/MPEG-2
             86 Crc32Posix
             87 Crc32Q
             88 Crc32Xfer
             89 ModbusLrc              MODBUS/ASCII protocol
             90 ModbusCrc              MODBUS/RTU   protocol
             91 DCON                   DCON protocol
             92 HART                   HART protocol
             93 ZERO                   нулевой хеш (только для отладки\калибровки)
         

Daq Pascal Api -> utf8_encode_ansi utf8_decode_ansi utf8_uppercase utf8_lowercase utf8_length utf8_copy utf8_ord utf8_chr utf8_bom

Определение:

function utf8_encode_ansi(s:string):string
function utf8_decode_ansi(s:string):string
function utf8_uppercase(s:string):string
function utf8_lowercase(s:string):string
function utf8_length(s:string):integer
function utf8_copy(s:string; i,n:integer):string
function utf8_ord(s:string; i:integer):integer
function utf8_chr(c:integer):string
function utf8_bom:string

Аргументы:

  • s - исходная строка для обработки.
  • i - индекс интересующего символа в строке.
  • n - число символов для копирования.
  • c - код символа Unicode.

Результат:

Группа функций обработки строк Unicode в формате UTF8.

Описание:

Функции utf8_xxx служат для обработки строк Unicode в формате UTF8. Кодировка UTF8 подробно описана в rfc3629. Ограничений на длину строки s у функций этой группы нет.

function utf8_encode_ansi(s:string):string
Вызов эквивалентен strconv('ansi-utf8',s).
Функция преобразует строку ANSI в строку UTF8 в соответствии с текущей кодовой страницей ANSI. Длина строки результата может быть больше, чем длина исходной строки ANSI, т.к. в кодировке UTF8 символ Unicode может занимать от 1 до 4 байт. В случае ошибки преобразования результатом будет пустая строка.

function utf8_decode_ansi(s:string):string
Вызов эквивалентен strconv('utf8-ansi',s).
Функция преобразует строку UTF8 в строку ANSI в соответствии с текущей кодовой страницей ANSI. Длина строки результата может быть меньше, чем длина исходной строки UTF8, т.к. в кодировке UTF8 символ Unicode может занимать от 1 до 4 байт. В случае ошибки преобразования результатом будет пустая строка. Перед преобразованием строки полезно проверить её вызовом IsLexeme(s,lex_utf8).

function utf8_uppercase(s:string):string
Вызов эквивалентен strconv('utf8-uppercase',s).
Функция преобразует строку UTF8 в верхний регистр. Длина строки результата может отличаться от длины исходной строки, т.к. в кодировке UTF8 символ Unicode может занимать от 1 до 4 байт. В случае ошибки преобразования результатом будет пустая строка.

function utf8_lowercase(s:string):string
Вызов эквивалентен strconv('utf8-lowercase',s).
Функция преобразует строку UTF8 в нижний регистр. Длина строки результата может отличаться от длины исходной строки, т.к. в кодировке UTF8 символ Unicode может занимать от 1 до 4 байт. В случае ошибки преобразования результатом будет пустая строка.

function utf8_length(s:string):integer
Функция возвращает длину строки UTF8 в символах. Символьная длина строки UTF8 может отличаться от байтовой длины строки length(s), т.к. в кодировке UTF8 символ Unicode может занимать от 1 до 4 байт. В случае ошибки преобразования результатом будет ноль.

function utf8_copy(s:string; i,n:integer):string
Функция копирует часть UTF8-строки s начиная с индекса i длиной в n символов Unicode. Параметры i,n могут меняться в пределах 1..utf8_length(s). В случае ошибки преобразования результатом будет пустая строка.

function utf8_ord(s:string; i:integer):integer
Функция возвращает код символа UTF8-строки s по индексу i. Параметр i может меняться в пределах 1..utf8_length(s). В случае ошибки преобразования результатом будет -1.

function utf8_chr(c:integer):string
Функция возвращает строку UTF8 по коду символа c. Параметр c может меняться в пределах 0..$10FFFF=1114111. В случае ошибки преобразования результатом будет пустая строка.

function utf8_bom:string
Функция возвращает строку UTF8 BOM (byte order mark). Эта строка, помещенная в начале файла или текста, служит признаком кодировки UTF8.

1) Примечание по применению
Строки UTF8 являются байт-ориентированными строками, частично совместимыми с строками ANSI. В силу того, что пробелы, кавычки и управляющие символы ASCII в кодировке UTF8 не меняются, для обработки строк UTF8 применима значительная часть обычных строковых функций, например, trim, extractword и т.д. При аккуратном использовании эти функции можно смешивать с функциями обработки строк UTF8.

2) Важное примечание по применению
Формат UTF8 предназначен в основном для переносимого хранения данных в файлах и обмена данными по сети и не очень хорошо подходит для оперативной обработки строк в силу переменного числа байтов на символ, что затрудняет индексацию символов в строках. Текущая реализация функций utf8_xxx довольно ресурсоемка, особенно при обработке длинных строк, поэтому ими не следует злоупотреблять, ограничив их применение сохранением/чтением данных из файлов или сети. При необходимости обработки текстов UTF8 рекомендуется обрабатывать их небольшими частями, например, построчно или по словам. Это значительно снизит нагрузку процессора.

Пример:

 {
 Demo for UTF8 functions.
 Example: demo_utf8('Привет Мир!');
 }
 procedure demo_utf8(test:string);
 var su,sa:string; i:Integer;
 begin
  su:=''; sa:='';
  writeln('Execute utf8_demo("'+test+'"):');
  su:=utf8_encode_ansi(test);
  sa:=utf8_decode_ansi(su);
  writeln('Test string = ',test);
  writeln('UTF8 encode = ',su);
  writeln('UTF8 decode = ',sa);
  writeln('Test Status = ',(test=sa):1);
  writeln('Test Length = ',length(test):1);
  writeln('UTF8 Length = ',length(su):1);
  writeln('Char Length = ',utf8_length(su):1);
  writeln('UTF8 Upper  = ',strconv('utf8-ansi',utf8_uppercase(ExtractWord(1,su))));
  writeln('UTF8 Lower  = ',strconv('utf8-ansi',utf8_lowercase(ExtractWord(2,su))));
  writeln('Test Copy   = ',utf8_decode_ansi(utf8_copy(su,2,utf8_length(su)-2)));
  writeln('UTF8 BOM    = ',utf8_bom);
  for i:=1 to length(test) do begin
   write(' Index ',i:3);
   write(' Ansi ',StrFetch(test,i));
   write(' Code ',Ord(StrFetch(test,i)):3);
   write(' UTF8 ',utf8_copy(su,i,1):4);
   write(' Code ',utf8_ord(su,i):5);
   write(' Char ',utf8_chr(utf8_ord(su,i)):4);
   write(' Test ',(StrFetch(test,i)=utf8_decode_ansi(utf8_chr(utf8_ord(su,i)))):1);
   writeln;
  end;
  su:=''; sa:='';
 end;
         

Daq Pascal Api -> issametext

Определение:

function issametext(s1,s2:string):boolean

Аргументы:

  • s1 - сравниваемая строка 1.
  • s2 - сравниваемая строка 2.

Результат:

Сравнивает две строки текста.

Описание:

Возвращает true, если две строки текста равны. При сравнении регистр символов не играет роли. В этом состоит отличие данной функции от выражения s1=s2. Функция удобна при интерпретации строковых выражений.

Ограничений на длину строк s1,s2 нет.

Пример:

          b1:=issametext('Var','VAR'); b2:=('Var'='VAR'); {true,  false}
          b1:=issametext('Var','Var'); b2:=('Var'='Var'); {true,  true}
          b1:=issametext('Var','xxx'); b2:=('Var'='xxx'); {false, false}
         

Daq Pascal Api -> islexeme

Определение:

function islexeme(arg:string; typ:Integer):boolean

Аргументы:

  • arg - строка аргумента для анализа лексемы.
  • typ - проверяемый тип лексемы.

Результат:

Проверяет строку (arg) как строковое выражение (лексему) заданного типа (typ).

Описание:

Возвращает true, если строка (arg) удовлетворяет определению лексемы заданного типа (typ). Для пустой строки (arg) всегда возвращается false. Ограничений на длину строки (arg) нет. Обычно выясняется принадлежность всех символов строки определенному классу символов (см. далее). Функция позволяет максимально быстро узнать, содержит ли проверяемая строка нужную лексему. Функция удобна для интерпретации строковых выражений или для лексического анализа текста. Функция "заточена" на максимальную скорость работы, т.е. позволяет быстро классифицировать обрабатываемую строку, чтобы в дальнейшем обрабатывать её тем или иным способом.

Тип проверяемой лексемы (typ) может принимать такие значения:

  1. lex_ansi - строка содержит символы ANSI т.е. фактически любые символы. То есть IsLexeme(arg,lex_Ansi) эквивалентно (Length(arg)>0) или (arg<>'').

  2. lex_utf8 - строка является допустимой строкой UTF8, т.е. её можно использовать в функциях работы со строками Unicode UTF8.

  3. lex_name - строка является именем, т.е. содержит символы [0-9A-Z_a-z] букв, цифр и знак подчеркивания и не начинается с цифры. Это основном соответствует классу символов [:word:], с тем отличием, что первый символ не может быть цифрой. Такие строки могут служить именами (идентификаторами) в языке Pascal, C/C++ и в большинстве других языков программирования.

  4. lex_word - строка является словом и содержит только символы класса [:word:], т.е. буквы, цифры и знак подчеркивания [[:alnum:]_].

  5. lex_blank - строка содержит только символы класса [:blank:], т.е. пробелы и табуляции [ \t].

  6. lex_space - строка содержит только символы класса [:space:], т.е. пробельные символы [ \t\v\r\n\f].

  7. lex_cntrl - строка содержит только символы класса [:cntrl:], т.е. управляющие символы [\x00-\x1F\x7F].

  8. lex_alpha - строка содержит только символы класса [:alpha:], т.е. латинские буквы [A-Za-z].

  9. lex_lower - строка содержит только символы класса [:lower:], т.е. латинские буквы [a-z].

  10. lex_upper - строка содержит только символы класса [:upper:], т.е. латинские буквы [A-Z].

  11. lex_digit - строка содержит только символы класса [:digit:], т.е. цифры [0-9].

  12. lex_alnum - строка содержит только символы класса [:alnum:], т.е. латинские буквы и цифры [A-Za-z0-9].

  13. lex_xdigit - строка содержит только символы класса [:xdigit:], т.е. символы для шестнадцатеричных чисел [0-9A-Fa-f].

  14. lex_punct - строка содержит только символы класса [:punct:], т.е. символы пунктуации [-!"#$%&'()*+,./:;<=>?@[\\\]_`{|}~].

  15. lex_print - строка содержит только символы класса [:print:], т.е. печатные символы (все кроме контрольных) [\x20-\x7E].

  16. lex_graph - строка содержит только символы класса [:graph:], т.е. графические (отображаемые) символы (все кроме контрольных и пробела) [\x21-\x7E].

  17. lex_ascii - строка содержит только символы класса [:ascii:], т.е. все символы таблицы ASCII (7 бит) [\x00-\x7F].

  18. lex_iparam - строка содержит целочисленный параметр (integer parameter), т.е. целое число в виде 123 или $1FF, которое может быть успешно преобразовано функцией val.

  19. lex_fparam - строка содержит вещественный параметр (float parameter), т.е. вещественное число, которое может быть успешно преобразовано функцией rval.

  20. lex_sparam - строка содержит строковый параметр (string parameter), т.е. стороку, которую, вероятно, можно использовать в качестве параметра командной строки. Это значит, что строка содержит только печатные символы ([:print:]) и пробелы ([:blank:]), но не содержит других управляющих символов ([:cntrl:]). Если строка содержит пробелы, она заключена в кавычки, т.е. первый и последний символ - кавычки ("). Такие строки могут быть использованы для передачи строковых параметров в командной строке или по каналу связи. Например, строка (abcd123) или ("abcd 123") - допустимые параметры, а (abcd 123) - нет, т.к. нет кавычек и есть пробел. Исключение управляющих символов гарантирует, что при интерпретации потока данных строка не будет разбита на разные строки.

  21. lex_base64 - строка содержит символы алфавита Base64, т.е. содержит только символы [0-9A-Za-z\+/=], принятые в кодировке Base64, так то строка, весьма вероятно, может быть успешно преобразована функцией base64_decode.

  22. lex_fsmname - строка может использоваться в качестве имени объекта/состояния/действия конечного автомата FSM. Правила аналогичны lex_name, но также допускаются символы [&:-], а имя может начитаться с &.

  23. lex_sminame - строка может использоваться в качестве имени объекта/состояния/действия конечного автомата SMI. Правила аналогичны lex_name, но также допускаются символы [&:-], а имя может начитаться с &.

  24. lex_dimname - строка может использоваться в качестве имени сервиса DIM. Правила описаны в документации DIM. Имена сервисов DIM не должны содержать управляющих и пробельных символов, символов разделителей [|@], а также не должны начинаться с символов [\+\-!].

  25. lex_sqlname - строка содержит идентификатор SQL по правилам ГОСТ Р ИСО/МЭК 9075-93. Начинается с буквы и содержит буквы, цифры и знак подчеркивания (_). Определение: [a-zA-Z][0-9_a-zA-Z]*.

  26. lex_fbdname - строка содержит идентификатор SQL по правилам СУБД Firebird. Начинается с буквы и содержит буквы, цифры, знак доллара ($) и подчеркивания (_). Определение: [a-zA-Z][0-9_\$a-zA-Z]*.

  27. lex_section - строка содержит имя [секции]. По правилам DAQ системы имя секции заключено в квадратные скобки [], т.е. начинается с символа [, завершается символом ] и не содержит управляющих символов [:cntrl:] внутри. В начале и конце строки не должно быть пробелов. Определение: ^[\[][^[:cntrl:]]*[\]]$. Например: [CRW-DAQ Data File].

  28. lex_atcall - строка содержит вызов @команды с символом @ (At sign) в начале, за которым идет хотя бы один символ, отличный от пробелов [:space:] и управляющих символов [:cntrl:]. Проверка фильтром lex_AtCall очень быстрая, т.к. проверяется только 1 и 2 символ. Её рекомендуется использовать в потоке обработки данных для быстрой фильтрации команд. Определение: ^[@][^[:space:][:cntrl:]]. Например: @send message.

  29. lex_atcall - строка содержит вызов @команды с символом @ (At sign) в начале. По правилам DAQ системы команды начинаются с символа @ (At), за которым идет хотя бы один символ, отличный от пробелов [:space:] и управляющих символов [:cntrl:], и не содержит управляющих символов [:cntrl:] внутри (за исключением разрешенной табуляции). Фильтр lex_AtCmnd несколько более строгий, чем lex_AtCall, т.к. проверяется вся строка, но работает несколько медленнее. Его рекомендуется использовать при предварительном анализе команд (например, при чтении из файла). В потоке обработки данных лучше использовать lex_AtCall. Определение: ^[@][^[:space:][:cntrl:]][\t^[:cntrl:]]*$. Например: @send message.

  30. lex_lquote - строка начинается с кавычки - одинарной Apostrophe ['] или двойной QuoteMark ["].
    Название lex_lquote берется от left quote (левая кавычка).

  31. lex_rquote - строка заканчивается кавычкой - одинарной Apostrophe ['] или двойной QuoteMark ["].
    Название lex_rquote берется от right quote (правая кавычка).

  32. lex_quotes - строка начинается с кавычки - одинарной Apostrophe ['] или двойной QuoteMark ["], а также содержит другую (закрывающую) кавычку того же типа.
    Название lex_quotes берется от quotes (кавычки).
    Наличие кавычек говорит о том, что строка, предположительно, содержит список фраз в кавычках, которые надо разбирать функциями обработки фраз.

  33. lex_quoted - строка начинается с кавычки - одинарной Apostrophe ['] или двойной QuoteMark ["], а также заканчивается такой же кавычкой.
    Название lex_quoted берется от quoted (закавыченный).
    Наличие этой лексемы означает, что строка, предположительно, является строго закавыченной строкой.

  34. lex_domname - строка является простым доменным именем (domain name), т.е. содержит символы [a-zA-Z0-9\-] букв, цифр и тире (минуса) и начинается и заканчивается буквой или цифрой. Такие имена могут представлять (локальные) имена компьютеров (hostname) или доменов. Заметьте, в доменных именах (по стандарту) символ подчеркивания _ не используется. Длина доменных имен может быть от 1 до 63 (заметим, что в разных источниках есть разнобой по этому вопросу). Например, bilbo или bilbo-baggins.
    Сетевые имена регламентируются стандартами RFC-921, RFC-952, RFC-1123.
    Следует заметить, что из-за обновления стандартов и разного их толкования в источниках есть разночтения в реализации этих стандартов.

  35. lex_dnshost - строка является полным доменным именем в стиле DNS, т.е. содержит простые доменные имена (lex_DomName), разделенные точкой. При этом точка не должна быть в начале и конце. Длина доменных имен может быть от 1 до 253. Такие имена могут представлять сетевые имена компьютеров в домене. Например, bilbo.shire или bilbo-baggins.shire.middle-earth.world.
    Сетевые имена регламентируются стандартами RFC-921, RFC-952, RFC-1123.
    Следует заметить, что из-за обновления стандартов и разного их толкования в источниках есть разночтения в реализации этих стандартов.

  36. lex_adduser - строка является именем пользователя Linux (с политикой по умолчанию). Политику имен пользователей задает переменная NAME_REGEX в файле конфигурации /etc/adduser.conf. Эту политику использует программа adduser, команда для создания пользователей Linux. По умолчанию переменная NAME_REGEX имеет значение регулярного выражения ^[a-z][-a-z0-9_]*\$?$ Обратите внимание - имена пользователей Linux задаются в нижнем регистре и могут содержать символы -_ и символ $ в конце. Например, bilbo, bilbo-baggins, frodo_baggins, evel_sauron$.

  37. lex_domuser - строка является доменным именем пользователя. Оно удовлетворяет регулярному выражению /^[a-zA-Z][-a-zA-Z0-9_]*$/ Обратите внимание - доменные имена пользователей (в отличие от пользователей Linux) задаются в любом регистре, могут содержать символы -_ и не могут содержать символы $.. Например, Gandalf, bilbo, bilbo-baggins, frodo_baggins.

  38. lex_usrhost - строка имеет вид username@hostname в стиле ssh. Здесь username - доменное имя (lex_DomName), hostname - сетевое имя компьютера (lex_DnsHost). Например, bilbo@localhost или bilbo-baggins@shire.middle-earth.world.

  39. lex_ip4addr - строка имеет вид IP-адреса версии IPv4 по типу n.m.i.j, где n,m,i,j - целые числа в диапазоне [0-255]. Например, 192.168.0.1. Адреса IPv4 описаны, например, в стандарте RFC-791, Для проверки соответствия используется регулярное выражение ^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$.

  40. lex_ip6addr - строка имеет вид IP-адреса версии IPv6. Адреса IPv4 регламентируются стандартами RFC-2460, RFC-4193, RFC-8200.
    Например, 2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d. Для проверки соответствия используется сложное регулярное выражение ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ .

  41. lex_macaddr - строка имеет вид MAC-адреса сетевой карты по стандарту IEEE-802 MAC-48. Такие адреса состоят из 6 групп 2-значных HEX чисел, разделенных символами [:-]. Например, 8e:26:55:54:e6:c7. Адреса IPv4 описаны, например, в стандарте RFC-7042, Для проверки соответствия используется регулярное выражение ^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$.

  42. lex_dotname - строка имеет вид составного имени с разделителем-точкой (dot), т.е. строка состоит из имен (lex_name), разделенных точками (.), при этом точка не должна стоять в начале или конце строки, а также повторяться дважды подряд. Например, daq.system.devices или PIPE.PRESSURE.P_1.

  43. lex_uriaddr - строка имеет вид URI-адреса сетевого ресурса стандарта RFC-3986. Такие адреса имеют вид scheme://authority/path?query#fragment. Например, ib://SYSDBA:masterkey@localhost:3035/employee.fdb?ctype=windows-1251&pagesize=4096#example. Адреса URI описаны, например, в стандарте RFC-3986, Для проверки соответствия используется регулярное выражение ^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?, приведенное в RFC-3986, Appendix B, Errata 2624.

  44. lex_pctchar - строка содержит (хотя бы один) символ процента (%). Это может указывать на то, что строка (в зависимости от контекста) закодирована алгоритмом pct-encode, алгоритмом url-encode или содержит подстановку переменной окружения в стиле Windows (%VAR%).

  45. lex_pctcode - строка содержит ноль или более корректных кодов %xx алгоритма pct-encode. Это значит, что строка может быть корректно декодирована алгоритмом pct-decode без ошибок (см. pct_decode). Условие lex_pctcode можно применять для проверки входных данных, если ожидается, что они закодированы алгоритмом pct-encode или алгоритмом url-encode.

  46. lex_pctdata - строка имеет вид корректно закодированной алгоритмом pct-encode строки. Это значит, что она содержит только корректные коды %xx алгоритма pct-encode (см. lex_pctcode), а также разрешенные символы, НЕ входящие в набор управляющих, пробельных и зарезервированных RFC-3986 символов - разделителей. Другими словами, такая строка может быть корректным компонентом адреса URI. Условие lex_pctdata можно применять при формировании адресов URI, чтобы проверять корректность компонентов адреса URI. А также для проверки при анализе данных, если их предполагается передавать по текстовым каналам связи с использованием алгоритмов кодирования pct-encode или url-encode.

  47. lex_pctneed - строка требует кодирования pct-encode. Это значит, что она содержит запрещенные символы, входящие в набор управляющих, пробельных и зарезервированных RFC-3986 символов - разделителей: [0x00-0x20\%:,/?#\[\]@!$&\'\(\)*+,;=]. Это значит, что строка требует применения кодирования pct-encode, чтобы стать допустимым компонентом адреса URI или быть переданной по текстовому каналу связи. Условие lex_pctneed можно применять при формировании адресов URI, чтобы проверять необходимость кодирования pct-encode по отношении к компонентам адреса URI. А также для данных, передаваемых по текстовым каналам связи.

Кроме того, в качестве типа проверяемой лексемы (typ) также можно использовать ссылку на регулярное выражение regexp_init(..). В этом случае для проверки используется regexp_test(..), например:
         rex:=regexp_init(0,'/^[_a-zA-Z]+[_a-zA-Z0-9]*$/i');    // Создать объект - регулярное выражение
         if IsLexeme(s,rex) then ...                            // Проверка выражения s
         if regexp_test(rex,s) then ...                         // Проверка выражения s (эквивалент)
         bNul(regexp_free(rex));                                // Не забываем освободить регулярное выражение
         
Это расширение IsLexeme(..) позволяет использовать регулярные выражения единообразным способом вместе с предопределенными константами, делая эту функцию универсальным инструментом синтаксического анализа теста.

Классы символов, часто используемые в лексемах (регулярных выражениях), приведены ниже:
    POSIX-класс   Эквивалент (регулярные выражения)       Значение
    [:upper:]     [A-Z]                                   Символы верхнего регистра
    [:lower:]     [a-z]                                   Символы нижнего  регистра
    [:alpha:]     [[:upper:][:lower:]]                    Буквы (латинские)
    [:digit:]     [0-9]                                   Цифры
    [:xdigit:]    [[:digit:]A-Fa-f]                       Шестнадцатеричные цифры
    [:alnum:]     [[:alpha:][:digit:]]                    Буквы и цифры
    [:word:]      [[:alnum:]_]                            Символы, образующие «слово»
    [:punct:]     [!"#$%&'()*+,-./:;<=>?@[\\]_`{|}~]      Знаки пунктуации
    [:blank:]     [ \t]                                   Пробел и табуляция
    [:space:]     [[:blank:]\v\r\n\f]                     Пробельные символы
    [:cntrl:]     [\x00-\x1F\x7F]                         Управляющие символы
    [:graph:]     [\x21-\x7E]                             Печатные символы
    [:print:]     [\x20-\x7E], т. е. [[:graph:] ]         Печатные символы с пробелом
    [:ascii:]     [\x00-\x7F]                             Все символы ASCII (7 бит)
         

Пример:

          if islexeme('Var',lex_name) then writeln('arg is name');
         

Daq Pascal Api -> dadfileref

Определение:

function daqfileref(n,e:string):string

Аргументы:

  • n - имя файла.
  • e - расширение по умолчанию.

Результат:

Возвращает полное имя файла по ссылке n с расширением e по умолчанию.

Описание:

Возвращает полное имя файла по ссылке n с расширением e по умолчанию. Функция используются для формирования полных имен файлов по коротким (относительным) ссылкам. Расширением считается часть имени файла начиная от последней точки. Например, файл "c:\tmp\x.bmp" имеет расширение ".bmp".

Конфигурации DAQ - систем используют преимущественно относительные имена файлов в качестве файловых ссылок (file reference). Отосительные ссылки хороши тем, что позволяют конфигурировать систему, не заботясь о конкретном расположении каталога как самого пакета CRW-DAQ, так и конкретной DAQ системы.

Функция формирует имя файла по следующим правилам:

  • Имя файла имеет длину не более 255 символов.
  • Если имя файла n и расширение e - пустая строка, возвращается текущий каталог.
  • Если имя файла n имеет полный путь и/или расширение, результат имеет такой же путь и/или расширение.
  • Если имя файла n не имеет расширения, результат имеет расширение e.
  • Если имя файла n начинается с символов ~~, вместо этих символов подставляется домашний каталог CRW-DAQ, то есть каталог, где находится основной исполняемый файл Crw32.exe.
  • Если имя файла n начинается с символа ~, вместо этого символа подставляется домашний каталог текущего пользователя, то есть каталог, где находится профиль текущего пользователя, типа C:\Documents and Settings\User.
  • Если имя файла n содержит относительный (неполный) путь, то путь отсчитывается от домашнего каталога текущей DAQ-системы, то есть каталога, где находится основной конфигурационный файл загруженной конфигурации DAQ.
  • Путь файла может содержать относительные ссылки, при этом точка . - ссылка на текущий каталог, двоеточие .. - ссылка на родительский каталог.
  • После всех подстановок имя файла нормализуется, то есть все относительные ссылки, заданные точкой . и двумя точками .., преобразуются в абсолютное имя файла.

Система CRW-DAQ также использует при формировании имен указанные выше договоренности.

Пример:

          Пусть

           текущий каталог  c:\daq
           конфигурация     c:\daq\demo\config\test.cfg
           пакет            c:\Crw32exe\Crw32.exe
           пользователь     Somebody

          Тогда:

           daqfileref('','')                       =  c:\daq
           daqfileref('.','')                      =  c:\daq\demo\config
           daqfileref('..','')                     =  c:\daq\demo
           daqfileref('~','')                      =  c:\Documents and Settings\Somebody
           daqfileref('~~','')                     =  c:\Crw32exe
           daqfileref('c:\daq\clear','.exe')       =  c:\daq\clear.exe
           daqfileref('c:\daq\clear.bat','.exe')   =  c:\daq\clear.bat
           daqfileref('.\main.cfg','')             =  c:\daq\demo\config\main.cfg
           daqfileref('~\params','.ini')           =  c:\Documents and Settings\Somebody\params.ini
           daqfileref('..\bitmaps\button','.bmp')  =  c:\daq\demo\bitmaps\button.bmp
           daqfileref('~~\Resource\Button','.bmp') =  c:\Crw32exe\Resource\button.bmp
         

Daq Pascal Api -> defaultextension forceextension

Определение:

function defaultextension(n,e:string):string
function forceextension(n,e:string):string

Аргументы:

  • n - имя файла.
  • e - расширение.

Результат:

defaultextension - задает имя файла с расширением по умолчанию.
forceextension - задает имя файла с новым расширением.

Описание:

Функции используются для формирования расширений имен файлов. Расширением считается часть имени файла начиная от последней точки. Например, файл "c:\tmp\x.bmp" имеет расширение ".bmp".

defaultextension - задает имя файла с расширением по умолчанию.
Если имя файла n имеет расширение, возвращает просто исходное имя файла.
Если имя файла n не имеет расширения, возвращает исходное имя файла с расширением e.

forceextension - задает имя файла с новым расширением.
Возвращает имя файла n с новым расширением e, независимо от расширения исходного файла.

Имена файлов имеют не более 255 символов.

Пример:

          s:=defaultextension('c:\Crw32exe\Crw32.exe','.ini'); {c:\Crw32exe\Crw32.exe}
          s:=defaultextension('c:\Crw32exe\Crw32',    '.ini'); {c:\Crw32exe\Crw32.ini}
          s:=forceextension('c:\Crw32exe\Crw32.exe','.ini');   {c:\Crw32exe\Crw32.ini}
          s:=forceextension('c:\Crw32exe\Crw32',    '.ini');   {c:\Crw32exe\Crw32.ini}
         

Daq Pascal Api -> defaultpath forcepath

Определение:

function defaultpath(n,p:string):string
function forcepath(p,n:string):string

Аргументы:

  • n - имя файла.
  • p - путь (каталог).

Результат:

defaultpath - задает имя файла с путем по умолчанию.
forcepath - задает имя файла с новым путем.

Описание:

Функции используются для формирования путей (каталогов) имен файлов. Путем считается часть имени файла до последнего разделителя каталогов "/" или "\". Например, файл "c:\tmp\x.bmp" имеет путь "c:\tmp". Путь файла считается абсолютным, если он начинается с имени диска, корневого каталога или имени сервера, например: c:\tmp, \tmp, \\server\tmp. Путь файла считается относительным, если он не абсолютный. Относительный путь файла может содержать символы текущего каталога "." или родительского каталога "..", например: tmp, .\tmp, ..\tmp.

defaultpath - задает имя файла с путем по умолчанию.
Если имя файла n имеет абсолютный путь, возвращает просто исходное имя файла.
Если имя файла n не имеет пути или имеет относительный путь, возвращает имя файла p\n, то есть путь прибавляется к началу имени файла.

forcepath - задает имя файла с новым путем.
Возвращает имя файла n с новым путем p, независимо от пути исходного файла.

Имена файлов имеют не более 255 символов.

Пример:

          s:=defaultpath('c:\Crw32exe\Crw32.exe','c:\tmp'); {c:\Crw32exe\Crw32.exe}
          s:=defaultpath('Crw32exe\Crw32.exe',   'c:\tmp'); {c:\tmp\Crw32exe\Crw32.exe}
          s:=forcepath('c:\tmp','c:\Crw32exe\Crw32.exe');   {c:\tmp\Crw32.exe}
          s:=forcepath('c:\tmp','Crw32exe\Crw32.exe');      {c:\tmp\Crw32.exe}
         

Daq Pascal Api -> makerelativepath

Определение:

function makerelativepath(n,b:string):string

Аргументы:

  • n - имя файла.
  • b - имя опорного файла.

Результат:

makerelativepath - возвращает имя файла относительно опорного файла.

Описание:

Функция используются для формирования имен файлов. Довольно часто на практике используются относительные имена файлов. Путем считается часть имени файла до последнего разделителя каталогов "/" или "\". Например, файл "c:\tmp\x.bmp" имеет путь "c:\tmp". Путь файла считается абсолютным, если он начинается с имени диска, корневого каталога или имени сервера, например: c:\tmp, \tmp, \\server\tmp. Путь файла считается относительным, если он не абсолютный. Относительный путь файла может содержать символы текущего каталога "." или родительского каталога "..", например: tmp, .\tmp, ..\tmp.

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

Если имя опорного файла неизвестно, а используется просто опорный каталог, допустимо указывать вместо имени файла маску "*", например, c:\tmp\*.

Имена файлов имеют не более 255 символов.

Пример:

          {начало пути c:\tmp удаляется из имени файла:}

          s:=makerelativepath('c:\tmp\Crw32exe\Crw32.exe','c:\tmp\*');     {Crw32exe\Crw32.exe}
          s:=makerelativepath('c:\tmp\Crw32exe\Crw32.exe','c:\tmp\tst\*'); {..\Crw32exe\Crw32.exe}
         

Daq Pascal Api -> trim trimleft trimright

Определение:

function trim(s:string):string
function trimleft(s:string):string
function trimright(s:string):string

Аргументы:

  • s - преобразуемая строка.

Результат:

Удаляет незначащие пробелы в строке.

Описание:

Функции используются при обработке текста. Они служат для удаления незначащие пробелов в начале и конце теста. Пробелами считаются символы chr(0)..chr(20), то есть пробел и все что ниже его.

trim(s) - удаляет незначащие пробелы справа и слева.

trimleft(s) - удаляет незначащие пробелы только слева.

trimright(s) - удаляет незначащие пробелы только справа.

Ограничений на длину строки s нет.

Пример:

          s:=' string ';
          s:=trim(s);      { 'string' }
          s:=trimleft(s);  { 'string '}
          s:=trimright(s); {' string' }
         

Daq Pascal Api -> cookiescan

Определение:

function cookiescan(buff,name:string; mode:integer):string - сканирование текстового буфера для поиска значения "куки".

Аргументы:

  • buff - текстовый буфер, содержащий строки, разделенные EOL и, возможно, разделителем chr(mode).
  • name - имя "куки" (печения), то есть искомого значения value выражения "name=value" с заданным именем name.
  • mode - задает символ-разделитель chr(mode) (биты 0..7) + опции сканирования (биты 8..10) + маркер "не найден" (биты 16..23).

Результат:

Возвращает по заданному имени name искомое "печенье", т.е. строку value выражения name=value, если таковое выражение присутствует в буфере buff. Если выражение не найдено, возвращает пустую строку или маркер "не найден".

Описание:

CookieScan (от Cookie Scan - "печенье искать") сканирует текстовый буфер buff, содержащий строки текста, разделенные EOL и, возможно, разделителем chr(mode). Нулевой символ chr(0), если он есть в строке, служит "терминатором", завершающим поиск. В каждой строке текста функция ищет выражение name=value с заданным именем name. Если такое выражение найдено, функция возвращает value. Если выражение не найдено, возвращается пустая строка, либо маркер "не найден", если он задан в mode.

Комментарий:
Как известно, Web-обозреватели используют "куки" (cookies) для хранения информации о сеансе. В HTML используются "куки" с разделителем ";". В конфигурационных файлах (*.ini, *.cfg) используются разделители EOL. Возможны и другие варианты. Общее у них одно - в строках содержатся выражения "Имя=Значение". Все данные такого рода можно анализировать функцией CookieScan.

Параметр buff содержит текстовый буфер, состоящий из строк, разделенных EOL. Допускается также использование дополнительного символа разделителя, который указывается в битах 0..7 параметра mode (т.е. chr(mode)). Нулевой символ (если он есть в буфере) служит "терминатором", т.е. маркирует конец буфера и прерывает поиск.

Параметр name задает имя искомого "печенья", т.е. выражения name=value. Имя не должно быть пустым или содержать незначащих пробелов справа и слева от имени.

Параметр mode задает необязательный разделитель строк (в младшем байте) и опции сканирования (поиска). Биты параметра mode принимают такие значения:

  • биты 0..7 = ord(d) - задают символ разделителя d, например, оrd(';') задает разделитель ";".
  • бит 8 = 256*1 - если есть, то сравнивать имена с учетом регистра символов (case sensitive).
  • бит 9 = 256*2 - если есть, то НЕ удалять незначащие пробелы в имени слева и справа от имени (no trim name).
  • бит 10 = 256*4 - если есть, то НЕ удалять незначащие пробелы в значении слева и справа от значения (no trim value).
  • биты 16..23 = 256*256*ord(m) - задают символ маркера m признака "имя не найдено", например, 256*256*оrd(_CR) задает маркер CR в случае, если в буфере не найдено выражение "name=value" с заданным именем.


Заметим, что значением "по умолчанию" является mode=0, что соответствует разделителю EOL, сравнению имен без учета регистра и удалению незначащих пробелов в имени и значении (case insensitive, trim name, trim value). Это наиболее часто используемый режим работы функции.

Для формирования других значений mode рекомендуется использовать арифметические выражения, например
         ord(';')               задать разделитель строк ";" (вдобавок к EOL)
         256*7                  задать опции (case sensitive, no trim name, no trim value)
         256*256*ord(_CR)       задать символ CR в качестве признака "выражение не найдено"
         
Специально следует оговорить маркер "выражение не найдено" в битах 16..23 параметра mode. Это символ, который возвращается в случае, если в буфере не найдено выражение "name=value" с заданным именем. Этот случай следует отличать от ситуации, когда выражение найдено, но значение value отсутствует (равно пустой строке). В обоих случаях функция (без маркера) вернула бы пустую строку. Маркер позволяет эти случаи различать. В качестве маркера надо выбирать CR=chr(13) или LF=chr(10), которые в нормальном значении присутствовать не могут.

Пример:

          buff:='read=book'+EOL+'write=pen';            // Буфер строк с разделителем EOL
          s:=cookiescan(buff,'Read',0);                 // book
          buff:='read = book; write = pen';             // Буфер строк с разделителем ; и с пробелами
          s:=cookiescan(buff,'Write',Ord(';'));         // pen
         

Daq Pascal Api -> stringofchar

Определение:

function stringofchar(fill:char; leng:integer):string - возвращает строку заданных символов.

Аргументы:

  • fill - символ, которым заполняется строка, например, '*' или chr(0).
  • leng - длина строки, заполненной заданным символом.

    Результат:

    Возвращает строку символов fill заданной длины leng.

    Описание:

    StringOfChar - самый быстрый способ получить строку, заполненную заданным символом fill заданной длины leng. Это бывает нужно для форматирования строк или для выделения памяти под какие-то двоичные данные, которые могут храниться в виде дампа (содержимого строки).

    Пример:

    
              writeln(StringOfChar('*',10)); => напечатает строку '**********'
             

Daq Pascal Api -> igetdump isetdump rgetdump rsetdump

Определение:

function igetdump(var dump:string; offset:integer):integer - чтение целого числа из буфера dump со смещением offset.
function isetdump(var dump:string; offset:integer; data:integer):boolean - запись целого числа в буфер dump со смещением offset.
function rgetdump(var dump:string; offset:integer):real - чтение вещественного числа из буфера dump со смещением offset.
function rsetdump(var dump:string; offset:integer; data:real):boolean - запись вещественного числа в буфер dump со смещением offset.

Аргументы:

  • dump - строковый буфер для размещения данных (строковая переменная).
  • offset - смещение от начала буфера в байтах. Нулевое смещение соответствует началу буфера.
  • data - данные для записи в буфер.

    Результат:

    Возвращает данные из буфера (ноль при ошибке) или статус записи в буфер.

    Описание:

    Перечисленные функции служат для работы с данными, расположенными в строковом буфере. В языке DaqPascal нет динамических массивов, однако есть длинные строки, которые могут служить динамическим контейнером для хранения любых двоичных данных. Перечисленные функции служат для чтения и записи данных в буфер строки dump со смещением offset (нулевое смещение соответствует началу буфера). Функции являются быстрыми (скорость сопоставима с индексацией массива) и безопасными (при недопустимом смещеннии offset возвращается ноль при чтении или false при записи). Для выделения памяти по строку можно использовать функцию StringOfChar, а для расчета размера и смещения - константы SizeOfInteger, SizeOfReal.

    Пример:

             procedure testDump;
             var s:string; i,n:Integer; b:Boolean;
             begin
              n:=9;
              writeln('testDump ',SizeOfBoolean,SizeOfChar,SizeOfInteger,SizeOfReal);    // Размер типов
              s:=StringOfChar(chr(0),SizeOfInteger*n);                                   // Выделение памяти
              for i:=0 to n-1 do b:=iSetDump(s,i*SizeOfInteger,i+1);                     // Запись в буфер
              for i:=0 to n-1 do write(iGetDump(s,i*SizeOfInteger):5,', '); writeln;     // Чтение из буфера
              s:=StringOfChar(chr(0),SizeOfReal*n);                                      // Выделение памяти
              for i:=0 to n-1 do b:=rSetDump(s,i*SizeOfReal,i+1.0);                      // Запись в буфер
              for i:=0 to n-1 do write(rGetDump(s,i*SizeOfReal):5:3,', '); writeln;      // Чтение из буфера
             end;
             

Daq Pascal Api -> eval evar global

Определение:

function eval(expr:String):Real
function evar(name:String;data:Real):Boolean
function global(expr:String):Real

Аргументы:

  • expr - формульное выражение для вычисления.
  • name - имя переменной.
  • data - значение переменной.

Результат:

eval(expr) вычисляет формульное выражение expr в локальном калькуляторе.
evar(name,data) присваивает переменной name значение data.
global(expr) вычисляет формульное выражение expr в системном калькуляторе.

Описание:

Функции eval,evar,global используются для вычисления формульных выражений при помощи встроенного интерпретатора. Каждая программа Daq Pascal содержит локальный (приватный) экземпляр интерпретатора формульных выражений, который позволяет вычислять довольно сложные математические выражения, которые по тем или иным причинам не могут быть формализованы на этапе компиляции. Например, если пользователь вводит формулу для построения графика через поле ввода, для ее выполнения нужен интерпретатор.

Кроме того, в программе есть глобальный (общий для всей программы) интерпретатор, который доступен также через системную консоль (окно ГЛАВНАЯ КОНСОЛЬ). Он может использоваться для хранения общих для всей системы переменных, для взаимодействия между DAQ-программами, а также для выполнения специфических операций системного характера.

eval(expr) вычисляет формульное выражение expr при помощи локального интерпретатора. Интерпретатор поддерживает единственный (вещественный) тип данных, константы, переменные, встроенные функции и команды, операторы +,-,*,/,^,скобки(). Например:

          var x,y                  создает переменные х=0, y=0, возвращает 0
          y=1                      создает(если надо) и присваивает y=1, возвращает 1
          y                        возвращает значение переменной y
          (x+y*10)/2-sin(pi)^2     вычисляет выражение
          r=@voice 123             выполняет команду @voice 123, возвращает результат в r
          @system pid=@run cmd.exe выполняет команду pid=@run cmd.exe в системном калькуляторе
         
Более подробно узнать об интерпретаторе можно через меню Инструменты/Калькулятор.

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

  • eval('@global expr') - вычисляет выражение expr в системном калькуляторе. Вызов фактически работает как global('expr'), но перед вычислением в системном калькуляторе строка типа %x заменяется фактическим значением локальной переменной x.
  • eval('@system expr') - вычисляет выражение expr в системном калькуляторе. Вызов фактически работает как global('expr'), выражение expr передается в системный калькулятор без изменений.
Почувствуйте разницу:
          eval('@global @echo %x') - распечатает локальную переменную x (работает подстановка)
          eval('@system @echo %x') - распечатает системную переменную x
         

evar(name,data) присваивает переменной локального интерпретатора с именем name значение data. Если переменная с таким именем не существовала, она создается. Функция возвращает true в случае успешного выполнения, false в случае ошибки (например, если указано пустое имя).
Конечно, присвоение можно также делать и в выражении типа

          r:=eval(name+'='+str(data));
         
Однако присвоение через evar выстрее и точнее, так как при этом нет преобразования в строковый вид и обратно и поэтому нет ошибок округления.

global(expr) вычисляет формульное выражение expr при помощи системного интерпретатора, связанного с системной консолью. Этот вызов следует считать устаревшим, вместо него лучше использовать выражение

          eval('@system expr') или
          eval('@global expr')
         

В отличие от локального интерпретатора, пространство переменных, констант, функций у системного калькулятора общее, что позволяет использовать эти переменные для взаимодействия между разными DAQ-программами. Для изучения возможностей global можно использовать системную консоль, потому что она делает в точности то же, что и функция global. Есть только одна существенная разница, связанная с потоками. Многие команды системной консоли (все, связанные с рисованием или окнами) могут выполняться только в основном потоке программы. Такие команды, вызванные из других потоков, по умолчанию ничего не делают и молча возвращают ноль. В консольном режиме это получается автоматически, так как анализ консольного ввода идет в основном потоке. А вот при вызове функции global выполнение происходит в потоке DAQ - программы. Чтобы устранить проблему, используется команда @async, которая помещает следующую за ней строку в очередь для последующего выполнения в основном потоке программы. Выполнение асинхронных команд делается по таймеру, с частотой примерно 18 Hz. Вместо результата команда @async возвращает длину помещенной в очередь команды. Если надо узнать результат самой команды, надо использовать присвоение переменной, например:

          r:=eval('@system @async pid=@run cmd.exe'); или просто
          r:=eval('@async pid=@run cmd.exe');
         
В этом примере eval вернет длину команды "pid=@run cmd.exe", а для того, чтобы узнать результат команды, надо проверять системную переменную pid вызовом eval('@system pid'). Реально она изменится с задержкой, так как будет выполнена в другом потоке. Отметим, что вызов @system @async эквивалентен просто вызову @async.

Кроме стандартных функций, системный калькулятор содержит такие полезные команды:

  • @help - справка.
  • @list - смотреть список доступных переменных, функций, команд и т.д.
  • @async - асинхронное выполнение команд в основном потоке.
  • @system - выполнение команд в системном калькуляторе.
  • @global - выполнение команд в системном калькуляторе (с подстановкой локальных переменных).
  • @guard - проверка\установка прав доступа.
  • @run - запускать новые процессы.
  • @pid - смотреть и убивать процессы.
  • @polling - смотреть выполняющиеся потоки.
  • @menu - выполнять команды меню CRW32.
  • @view - работать с визуальными элементами (показывать\прятать).
  • @echo - вывод сообщения в консоль.
  • @sysinfo - выводит краткую информацию о системе.
  • @memory - узнать\задать параметры памяти.
  • @sleep - заснуть на заданное время [ms].
  • @mmtimer - прочитать\задать период быстрого мультимедийного таймера [ms].
  • @speak - говорит фразу при помощи речевого синтезатора (см. также тут).
  • @speech - управляет включением\выключением и параметрами речевого синтезатора.
Команды @help, @list, @polling, @menu, @view @speak, @speech, могут выполняться только в основном потоке, поэтому для их вызова всегда используется @async. Остальные команды могут выполняться везде. Указанные команды в совокупности позволяют, например, останавливать\перезагружать DAQ, CRW, Windows, менять внешний вид программы, говорить и многое другое. Изучить поведение команд можно в системной консоли. Как правило, команда без параметров выводит ее синтаксис.

Кроме того, есть ряд предопределенных переменных и имен:

  • _Crw_Force_Exit_ - переменная подавляет диалог при завершении CRW.
  • _Daq_Force_Exit_ - переменная подавляет диалог при завершении DAQ.
  • _Daq_Force_Stop_ - переменная подавляет диалог при остановке DAQ.
  • _Daq_Force_Start_ - переменная принуждает делать старт DAQ после следующей загрузки.
  • FormCrw32 - идентификатор основного окна CRW.
  • FormCrw32.MainMenu - идентификатор основного меню CRW.
  • FormCrw32.ToolBar - идентификатор панели инструментов CRW.
  • FormCrw32.StatusBar - идентификатор статусной строки CRW.
  • FormDaqControlDialog - идентификатор окна DAQ СИСТЕМА.
  • FormConsoleWindow - идентификатор окна ГЛАВНАЯ КОНСОЛЬ.
Указанные идентификаторы используются в перечисленных выше командах.

Длина строк expr,name ограничена 255 символами.

Интерпретаторы не следует употреблять без необходимости, так как скорость интерпретации на порядок ниже, чем скорость выполнения обычных DAQ программ.

Пример:

см. также demo_edit
          --Примеры вычисления выражений:
            b:=evar('x',pi);
            r:=eval('x');
            r:=eval('sin(x/3)');
            r:=eval('x=1');

          --Включить\выключить речевой синтезатор, сказать фразу
          --Перед использованием надо установить spchapi.exe и lhttsrur.exe
          --которые можно найти в меню Web\Home
            r:=eval('@async @speech 1');           {включить}
            r:=eval('@async @speak Привет, Мир!'); {сказать фразу}
            r:=eval('@async @speech 0');           {выключить}

          --Речевой систезатор поддерживает теги.
          --Вот примеры основных тегов:
            r:=eval('@speak \Rst\Сброс всех тегов на значения по умолчанию.');
            r:=eval('@speak Пауза \Pau=1000\в речи на 1000 миллисекунд.');
            r:=eval('@speak Громкость речи \Vol=65535\по максимуму \Vol=0\по нулям.');
            r:=eval('@speak \Spd=100\Скорость речи 100 слов в минуту.');
            r:=eval('@speak Тон (тембр) речи в герцах \Pit=100\низкий \Pit=200\высокий.');
            r:=eval('@speak Читать \Pro=1\с интонацией или \Pro=0\без интонации (монотонно).');
            r:=eval('@speak Читать слова \RmS=1\по буквам \rms=0\нормальными словами.');
            r:=eval('@speak Подчеркнуть интонацией выражение \Emp\СЛЕДУЮЩЕГО слова.');
            r:=eval('@speak Не надо подчеркивать интонацией \Dem\СЛЕДУЮЩЕЕ слово.');

          --Узнать\включить\выключить быстрый таймер
            t:=eval('@system @mmtimer');   {узнать}
            r:=eval('@system @mmtimer 1'); {включить}
            r:=eval('@system @mmtimer 0'); {выключить}

          --Узнать размер полной\доступной физической\виртуальной памяти
            r:=eval('@system @memory TotalPhys');
            r:=eval('@system @memory AvailPhys');
            r:=eval('@system @memory TotalVirtual');
            r:=eval('@system @memory AvailVirtual');

          --Узнать\задать макс\мин размер рабочей области памяти процесса
            r:=eval('@system @memory max');
            r:=eval('@system @memory max 1024*1024*64');
            r:=eval('@system @memory min');
            r:=eval('@system @memory min 1024*1024*16');

          --Сохранить/установить/восстановить права доступа
            r:=eval('@system @async SaveGuard=@gurd');
            r:=eval('@system @async @gurd lock');
            r:=eval('@system @async @gurd guest');
            r:=eval('@system @async @gurd user');
            r:=eval('@system @async @gurd root');
            r:=eval('@system @async @gurd %SaveGuard');

          --Подавить/разрешить диалог подтверждения при завершении CRW32
            r:=eval('@system _Crw_Force_Exit_=1');
            r:=eval('@system _Crw_Force_Exit_=0');

          --Подавить/разрешить диалог подтверждения при остановке  DAQ
            r:=eval('@system _Daq_Force_Stop_=1');
            r:=eval('@system _Daq_Force_Stop_=0');

          --Подавить/разрешить диалог подтверждения при завершении DAQ
            r:=eval('@system _Daq_Force_Exit_=1');
            r:=eval('@system _Daq_Force_Exit_=0');

          --Форсировать\отменить автостарт при следующей загрузке DAQ
            r:=eval('@system _Daq_Force_Start_=1');
            r:=eval('@system _Daq_Force_Start_=0');

          --Выполнить "@run mspaint.exe" непосредственно или асинхронно, в основном потоке
          --Системная переменная p будет содержать PID запущенной программы mspaint.exe
            r:=eval('@system p=@run mspaint.exe');
            r:=eval('@system @async p=@run mspaint.exe');

          --Вложенная конструкция @async: будет выведено
          -- Two
          -- One
          --так как выполнение One задержано вложенным вызовом @async
            r:=eval('@system @async @async @echo One');
            r:=eval('@system @async @echo Two');

          --Спрятать/показать основное окно программы в трей
            r:=eval('@system @async @view min FormCrw32');
            r:=eval('@system @async @view max FormCrw32');

          --Спрятать/показать окно DAQ SYSTEM
            r:=eval('@system @async @view min  FormDaqControlDialog');
            r:=eval('@system @async @view norm FormDaqControlDialog');

          --Спрятать/показать окно ГЛАВНАЯ КОНСОЛЬ
            r:=eval('@system @async @view min  FormConsoleWindow');
            r:=eval('@system @async @view norm FormConsoleWindow');

          --Спрятать/показать основное меню
            r:=eval('@system @async @view hide FormCrw32.MainMenu');
            r:=eval('@system @async @view show FormCrw32.MainMenu');

          --Спрятать/показать панель инструментов
            r:=eval('@system @async @view hide FormCrw32.ToolBar');
            r:=eval('@system @async @view show FormCrw32.ToolBar');

          --Спрятать/показать статусную строку
            r:=eval('@system @async @view hide FormCrw32.StatusBar');
            r:=eval('@system @async @view show FormCrw32.StatusBar');

          --Выполнить команду File/Exit
            r:=eval('@system @async @menu run FormCrw32.ActionFileExit');

          --Выполнить команду Daq/Start
            r:=eval('@system @async @menu run FormDaqControlDialog.ActionDaqStart');

          --Выполнить команду Daq/Stop
            r:=eval('@system @async @menu run FormDaqControlDialog.ActionDaqStop');

          --Выполнить команду Daq/Init (загрузка конфигурации)
            r:=eval('@system @async @menu run FormDaqControlDialog.ActionDaqInit');

          --Выполнить команду Daq/Done (выгрузка конфигурации)
            r:=eval('@system @async @menu run FormDaqControlDialog.ActionDaqStart');

          --Выполнить перезагрузку конфигурации
          --Фактически запускается второй экземпляр Crw32 и ему передается имя *.cfg файла
            r:=eval('@system @async @run '+ParamStr('ProgName')+' '+ParamStr('DaqConfigFile'));

          --Выполнить загрузку новой конфигурации
          --Фактически запускается второй экземпляр Crw32 и ему передается имя *.cfg файла
            r:=eval('@system @async @run '+ParamStr('ProgName')+ ' c:\daq\test.cfg');

          --Выполнить команду завершения Windows
            r:=eval('@system @async @run -Hide %ComSpec% /c shutdown -l');       - Выполнить Logout
            r:=eval('@system @async @run -Hide %ComSpec% /c shutdown -r');       - Выполнить Restart
            r:=eval('@system @async @run -Hide %ComSpec% /c shutdown -s');       - Выполнить Stop(Halt)
            r:=eval('@system @async @run -Hide %ComSpec% /c shutdown -s -f');    - Форсировать Stop
            r:=eval('@system @async @run -Hide %ComSpec% /c shutdown -s -t 15'); - Выполнить Stop через 15 сек

          --Запустить и затем через секунду убить процесс
            r:=eval('@system @async p=@run mspaint.exe');
            r:=eval('@system @async sleep 1000');
            r:=eval('@system @async @async @pid kill %p');

          --Скрипт, который завершает работу конфигурации, а затем перезагружает конфигурацию
          --и выполняет старт загруженной конфигурации DAQ
          --Обратите внимание - права доступа временно повышаются до ROOT, а затем восстанавливаются
          --Используется вложенный вызов @async, т.к. следующие команды должны выполняться с задержкой
          --Перед выполнением команд окно FormDaqControlDialog активизируется т.к. у скрытого окна
          --команды запрещены
            r:=eval('@system @async _Daq_Force_Stop_=1');
            r:=eval('@system @async _Daq_Force_Exit_=1');
            r:=eval('@system @async SaveGuard=@gurd');
            r:=eval('@system @async @gurd root');
            r:=eval('@system @async @view max FormCrw32');
            r:=eval('@system @async @view norm FormDaqControlDialog');
            r:=eval('@system @async @menu run FormDaqControlDialog.ActionDaqStop');
            r:=eval('@system @async @menu run FormDaqControlDialog.ActionDaqDone');
            r:=eval('@system @async @run '+ParamStr('ProgName')+' '+ParamStr('DaqConfigFile'));
            r:=eval('@system @async @sleep 500');
            r:=eval('@system @async @async @view norm FormDaqControlDialog');
            r:=eval('@system @async @async @menu run FormDaqControlDialog.ActionDaqStart');
            r:=eval('@system @async @async @gurd %SaveGuard');

          --Скрипт, который завершает работу конфигурации, выходит из программы
          --и завершает работу Windows через 15 секунд
            r:=eval('@system @async @gurd root');
            r:=eval('@system @async _Daq_Force_Stop_=1');
            r:=eval('@system @async _Daq_Force_Exit_=1');
            r:=eval('@system @async _Crw_Force_Exit_=1');
            r:=eval('@system @async @view max FormCrw32');
            r:=eval('@system @async @view norm FormDaqControlDialog');
            r:=eval('@system @async @menu run FormDaqControlDialog.ActionDaqStop');
            r:=eval('@system @async @menu run FormDaqControlDialog.ActionDaqDone');
            r:=eval('@system @async @run -Hide %ComSpec% /c shutdown -s -t15');
            r:=eval('@system @async @menu run FormCrw32.ActionFileExit');

          --Скрипт, который прячет меню, панель инструментов, статусную строку, окно DAQ SYSTEM
            r:=eval('@system @async @view hide FormCrw32.MainMenu');
            r:=eval('@system @async @view hide FormCrw32.ToolBar');
            r:=eval('@system @async @view hide FormCrw32.StatusBar');
            r:=eval('@system @async @view min  FormDaqControlDialog');

          --Скрипт, который показывает меню, панель инструментов, статусную строку, окно DAQ SYSTEM
            r:=eval('@system @async @view show FormCrw32.MainMenu');
            r:=eval('@system @async @view show FormCrw32.ToolBar');
            r:=eval('@system @async @view show FormCrw32.StatusBar');
            r:=eval('@system @async @view norm FormDaqControlDialog');
         

Daq Pascal Api -> regexp_init regexp_free regexp_ref regexp_ctrl regexp_test regexp_exec regexp_replaace regexp_matchnum regexp_matchpos regexp_matchlen regexp_matchstr regexp_escape

Определение:

function regexp_init(eng:Integer; pat:string):integer
function regexp_free(ref:integer):boolean
function regexp_ref(ref:integer):integer
function regexp_ctrl(ref:Integer; par:string):string
function regexp_test(ref:integer; inp:string):boolean
function regexp_exec(ref:integer; inp:string):integer
function regexp_replace(ref:integer; inp,rep:string):string
function regexp_matchnum(ref,i:integer):integer
function regexp_matchpos(ref,i,j:integer):integer
function regexp_matchlen(ref,i,j:integer):integer
function regexp_matchstr(ref,i,j:integer):string
function regexp_escape(arg:string):string

Аргументы:

  • eng - номер движка (engine), используемого для вычислений RegExp.
  • pat - шаблон (pattern) поиска/замены, т.е. регулярное выражение, которое надо искать (test,exec) или заменять (replace).
  • ref - ссылка (reference) на объект мастера регулярных выражений (TRegExpMaster).
  • par - строка для чтения/записи параметра (parameter) в процедуре управления (control).
  • inp - входная строка для обработки, в которой надо выполнять поиск или замену.
  • rep - заменитель (replacer), на который заменяется шаблон при выполнении замены (replace).
  • arg - строка аргумента для экранирования (escape), используется для подготовки шаблона для поиска.
  • i - номер найденного соответствия (match) при выполнении (exec) поиска.
  • j - номер найденного субсоответсвия (submatch) при выполнении (exec) поиска.

Результат:

Функции реалезуют обработку регулярных выражений (regular expression), сокращенно regexp.

Описание:

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

Функция regexp_init(eng,pat) создает экземпляр объекта regexp для регулярного выражения, заданного шаблоном (pattern) pat, с помощью движка (engine) eng. Движок - это фактически библиотека (программный код), реализующий regexp. В настоящее время доступны движки:
  1. regexp_def - основной движок "по умолчанию" (default). Это значение является ссылкой, которая заменяется при вызове на реальный номер движка, значение которого задается в конфигурационном файле Crw32.ini [System] regexp_Default_Options. Это позволяет менять основной движок через конфигурацию, не меняя прикладные программы.
  2. regexp_pas - движок TRegExpr, разработанный Андреем Сорокиным на языке Object Pascal (отсюда и название) и включенный в дистрибутив FPC. Движок regexp_pas не зависит от внешних библиотек и является самым надежным (в смысле доступности). Поэтому этот движок считается основным (по умолчанию) и резервным (fallback) на случай недоступности других движков. При указании недействительного номера движка по умолчанию для него будет принято резервное значение. Среди особенностей движка можно отметить большой набор модификаторов (igmrsx), наличие нестандартного модификатора (r) для расширенной поддержки русского языка, нестандартную реализацию модификатора (g). Обычно (для большинства движков) модификатор (g) имеет значение (Global - глобальный), т.е. глобальный поиск во входной строке всех соответствий шаблону (а не только первого). В движке TRegExpr модификатор (g) имеет значение (Greedy - жадный), т.е. включение в соответствие максимального числа символов (жадный режим). При значении (Greedy=0) включается ленивый режим, при котором .* работает как .*?, а .+ работает как .+?, т.е. включает в соответствие минимальное число символов. При вызове regexp_ctrl(rex,'Modifiers=g') для движка TRegExpr меняется Global, чтобы была совместимость с другими движками. Однако при задании модификатора в шаблоне (?g) будет изменено значение Greedy. По причине этой неоднозначности рекомендуется всегда использовать модификатор Greedy=1 (по умолчанию) и задавать модификатор (g) только в вызове regexp_ctrl(rex,'Modifiers=g'), а вместо (?g) использовать явные конструкции .*? или .+?.
  3. regexp_vbs - движок VBScript.RegExp от Microsoft, включенный в состав Visual Basic Script и обычно входящий в дистрибутив Windows. Этот движок считается запасным, т.к. он реализует не все нужные функции, а также (в принципе) может быть недоступен или запрещен администратором. Он полезен для сравнения, а также контроля (как хорошо проверенный эталон). Среди особенностей движка можно отметить ограниченный набор модификаторов (igm), отсутствие режима (UseSubst=0,Greedy=0), т.е. режимы подстановки и жадности всегда включены. Кроме того, движок имеет ограниченный набор возможностей и не поддерживает многие конструкции, например, задание модификаторов (?ig) или (?#комментарий).
  4. regexp_pcrelib - движок TPerlRegEx в загружаемой библиотеке pcrelib.dll. Этот движок считается запасным, и в каком-то смысле эталонным, т.к. он основан на широко используемой библиотеке PCRE, на которой работает язык Perl.
Шаблон pat должен содержать корректное регулярное выражение, которое будет использоваться для поиска или замены. При наличии флага UseSlash=1 (по умолчанию включен) допустимо использовать выражения вида /Шаблон/m с явным заданием модификаторов (m).
Функция возвращает ссылку, которая используется для работы с данным регулярным выражением.
Если задан неверный движок или ошибочный шаблон, будет сгенерировано исключение, а функция вернет 0.
После выполнения требуемой работы необходимо удалять (вызовом regexp_free) все ненужные экземпляры, созданные ранее вызовом regexp_init.

Начальное состояние объекта после создания (если в шаблоне явно не заданы модификаторы) соответсвует строке параметров:
 Engine=1,Modifiers=-igmrsx,ExecMax=0,UseSubst=1,UseSlash=1,Greedy=1,SavePar=0 
Задание в шаблоне модификаторов (типа /Шаблон/igm) устанавливает явно указанные модификаторы в соответствии с заданными.

Функция regexp_free(ref) удаляет экземпляр объекта regexp, созданный вызовом regexp_init. Она используется для удаления ненужных экземпляров regexp при завершении работы с ними.

Функция regexp_ref(ref) возвращает по ссылке (ref) объекта regexp ненулевое значение (обычно ту же ссылку), если исходная ссылка (ref) корректна, либо ноль, если ссылка недействительна. Вызов if (regexp_ref(ref)<>0) then ... является стандартной проверкой корректности ссылки объекта.

Функция regexp_ctrl(ref,par) читает или задает параметры (par) объекта regexp, заданного ссылкой (ref).
Параметр par может быть просто именем (name) параметра для чтения, либо выражением (name=value, без лишних пробелов) для записи параметра.
Допускаются такие параметры:
  • Engine - номер движка (только чтение) или -1, если движок недоступен. Движок задается при создании объекта (regexp_init) и не может быть изменен после создания.
  • Engines - список имен доступных движков (только чтение) - сейчас это 'TRegExpr,VBScript.RegExpr'.
  • Version - версия движка (только чтение). Например, 'TRegExpr 9.52'. Первое слово версии - название движка. Номер движка (Engine) соответствует положению в списке (Engines).
  • Errors - счетчик ошибок (только чтение). Ненулевое значение может означать недоступность движка или ошибку в шаблоне. При возникновении ошибки генерируется исключение с записью в журнал. Используйте только проверенные (например, в встроенном RegExp Калькуляторе) шаблоны.
  • Pattern - шаблон поиска или замены (чтение/запись). Будьте внимательны, при задании некорректного шаблона будет сгенерировано исключение с записью в журнал.
    При флаге (UseSlash=1) для шаблона допускается конструкция /Pattern/Modifiers, (например, /test/igm). При этом все модификаторы сбрасываются и устанавливаются только те, которые явно указаны после завершающего слеша.
  • Input - обрабатываемая строка (только чтение) при последнем вызове (test,exec,replace). Недоступна при параметре SavePar=0.
  • Replacer - строка замены (только чтение) при последнем вызове (replace). Недоступна при параметре SavePar=0.
  • IgnoreCase - (чтение/запись) флаг режима "игнорировать регистр символов". Соответствует модификатору i.
  • Global - (чтение/запись) флаг режима "глобальный поиск". Соответствует модификатору g.
  • Greedy - (чтение/запись) флаг режима "жадный поиск". По умолчанию 1. Поддерживается только в TRegExpr, в других движках всегда 1. В движке TRegExpr не рекомендуется использовать (?g), т.к. в нем модификатор g имеет смысл Greedy, а не Global, как обычно.
  • Multiline - (чтение/запись) флаг режима "многострочный текст". Соответствует модификатору m.
  • Singleline - (чтение/запись) флаг режима "однострочный текст". Соответствует модификатору s.
  • Russian - (чтение/запись) флаг режима "расширение поддержки русского языка". Соответствует модификатору r.
  • Extended - (чтение/запись) флаг режима "расширенный синтаксис". Соответствует модификатору x.
  • AllModifiers - список всех доступных модификаторов для данного движка. Например, 'igmrsx'. Список доступных модификаторов зависит от используемого движка.
  • Modifiers - список состояний для доступных модификаторов данного движка. Это строка вида 'rsg-imx', в которой до минуса идут включенные (=1) модификаторы, а после минуса - отключенные (=0) модификаторы. Модификаторы обозначаются буквами (например, igmrsx) и влияют на способ обработки регулярных выражений.
    • Модификатор i или флаг IgnoreCase. Включает режим "игнорировать регистр символов".
    • Модификатор r или флаг Russian. Включает расширенную поддержку русского языка, в которой в диапазон [а-я] включается ё, в [А-Я] - Ё, а в [а-Я] - все русские буквы. Это нестандартный модификатор, который есть только в движке regexp_pas (TRegExpr).
    • Модификатор m или флаг Multiline. Включает многострочный режим, в котором ^ и $ обозначают начало и конец строки (а не всего текста).
    • Модификатор s или флаг Singleline. Включает однострочный режим, в котором точка . обозначает любой символ, включая разделители строк.
    • Модификатор g или флаг Global. Включает глобальный поиск ("жадный", greedy), в котором ищутся все возможные соответствия (а не только первые найденные).
    • Модификатор x или флаг Extended. Включает расширенный синтаксис выражений, который разрешает комментарии и пробелы в шаблоне.
  • ExecMax - предел (чтение/запись) для числа искомых соответствий при вызове (exec). По умолчанию равен 0 (нет ограничений), но может быть задан для ускорения работы. При положительном значении выполнение поиска соответствий (exec) прерывается по достижении заданного числа найденных соответствий.
  • SavePar - флаг (0/1) (чтение/запись) для сохранения параметров (Input,Pattern,Replacer,Modifiers) при вызове (test,exec,replace). По умолчанию равен 0 (параметры не сохраняются) для экономии ресурсов и ускорения работы, но может быть включен для удобства программирования. В этом случае будут доступны вызовы типа regexp_ctrl(ref,'Input') (строка входных данных для обработки при последнем вызове test,exec,replace).
  • UseSubst - флаг (0/1) (чтение/запись) для режима "использовать подстановку (substitute) при вызове replace". По умолчанию 1 (использовать подстановки). Если установлен этот флаг перед вызовом Replace, сработает режим подстановки при выполнении Replace. Подстановки заменяют $1,$2,.. на содержимое соответствий (..)..(..).. шаблона регулярного выражения. Отключение подстановок может немного ускорить работу Replace.
  • UseSlash - флаг (0/1) (чтение/запись) для режима "использовать шаблон со слешем". По умолчанию 1 (использовать слеш). Если установлен этот флаг, то позволяется использовать шаблоны в стиле JavaScript/Perl вида /Pattern/Modifiers, например, /test/igm. При задании такого шаблона все модификаторы сбрасываются и устанавливаются только те, которые были явно указаны после завершающего слеша.
  • Errors - счетчик ошибок при работе RegExp - например, попытка задания неверного шаблона.
  • DumpData - строка (только чтение) буфера списка внутренних переменных (в основном для отладки).
  • DumpCode - строка (только чтение) буфера P-кода интерпретатора RegExpr (в основном для отладки).
  • WordChars - строка (только чтение) символов для слов (\w), обычно [0-9a-zA-Z_].
  • SpaceChars - строка (только чтение) символов для пробелов (\s), обычно [ \t\r\n\f].
  • LineSeparators - строка (только чтение) символов разделения строк, обычно CR,LF.
  • LinePairSeparator - строка (только чтение) парного разделителя строк, обычно CRLF.
  • Options - позволяет читать и записывать основные параметры в виде одной строки параметров вида:
     Engine=1,Modifiers=-igmrsx,ExecMax=0,UseSubst=1,UseSlash=1,Greedy=1,SavePar=0 
    в которой можно записывать все перечисленные параметры кроме Engine.
  • * - текст (только чтение) содержащий список имен всех доступных параметров.
Вызовы regexp_ctrl(ref,par) используются для чтение или записи параметров объекта regexp. Все параметры (включая численные и логические) читаются или записываются в виде строки.

Функция regexp_test(ref,inp) возвращает по ссылке (ref) объекта regexp значение результата поиска строки шаблона (pattern) во входной строке (input). Шаблон обычно задается при создании объекта regexp_init(regexp_def,pattern), либо (реже) задается вызовом regexp_ctrl(ref,'Pattern='+pattern). Функция выполняет поиск только до первого вхожнения искомого шаблона. Она служит для (быстрой) проверки того, что во входной строке есть искомый шаблон.

Функция regexp_exec(ref,inp) возвращает по ссылке (ref) объекта regexp значение результата поиска строки шаблона (pattern) во входной строке (input). При поиске ищутся все соответствия (если не указано ограничение ExecMax). Функция возвращает число найденных соответствий или 0, если их не найдено. Кроме того, в буфере хранилища (storage) сохраняется таблица всех найденных соответствий, которые можно получить функциями regexp_matchnum, regexp_matchpos, regexp_matchlen, regexp_matchstr. Хранилище очищается при следующем вызове (test,exec,replace), поэтому анализ найденных соответсвий надо делать вовремя. Для больших текстов и сложных шаблонов вызов функции может занимать большое время, т.к. в цикле ищутся все соответствия (которых может быть много). Это надо учитывать при вызове функции. При необходимости длительность поиска можно ограничить параметром ExecMax, который ограничивает число найденных соответствий указанным числом (число 0 означает поиск без ограничений).

Функция regexp_replace(ref,inp,rep) заменяет во входной строке (input) строку шаблона (pattern) на строку заменителя (replacer). Работа функции зависит от модификаторов (Modifiers). На нее также влияет параметр (флаг) UseSubst. Если это флаг активен (=1), то при замене будут выполняться подстановки субсоответствий, например, $1 заменится на найденное субсоответсвие (..). Подробности см. в описании языка регулярных выражений.

Функции regexp_matchnum(ref,i), regexp_matchpos(ref,i,j), regexp_matchlen(ref,i,j), regexp_matchstr(ref,i,j), используются после вызова поиска всех соответствий (exec) для их анализа и обработки. Данные для анализа берутся из хранилища (storage), куда их помещает вызов regexp_exec. Анализ должен быть выполнен до следующего вызова методов поиска/замены (test,exec,replace). Это связано с тем, что перед каждым поиском/заменой хранилище очищается для сохранения нового поиска.
regexp_matchnum(ref,0) возвращает число (number) N найденных соответствий (как и функция regexp_exec).
regexp_matchnum(ref,i) возвращает число (number) M найденных субсоответствий для соответствия номер i=1..N.
regexp_matchpos(ref,i,0), regexp_matchlen(ref,i,0), regexp_matchstr(ref,i,0) возвращает положение (pos), длину (len) и строку (str) соответствия номер i=1..N.
regexp_matchpos(ref,i,j), regexp_matchlen(ref,i,j), regexp_matchstr(ref,i,j) возвращает положение (pos), длину (len) и строку (str) субсоответствия j=1..M для соответствия номер i=1..N.
Таким образом, в цикле можно извлечь все найденные соответствия и субсоответствия.
Cубсоответствия в шаблонах задаются скобками (..). Например, для времени (типа 15:30) можно взять шаблон (\d{2}):(\d{2}), тогда "соответствием" будет найденное время '15:30', а двумя субсоответствиями будут часы '15' и минуты '30'.

Функция regexp_escape(arg) обрабатывает аргумент (arg), экранируя (escape) все специальные символы RegExp с помощью символа \, так что после обработки строка будет пригодна для шаблона поиска. Эту функцию рекомендуется использовать для подготовки шаблона поиска RegExp.

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

Обычно для ускорения работы поиска/замены происходит внутренняя компиляция шаблона regexp в промежуточный исполняемый код (того или иного вида, зависящего от используемого движка). Поэтому регулярные выражения чаще всего используют постоянный шаблон (заданный при вызове regexp_init) для различного набора данных (input,replacer), что позволяет избежать многократной компиляции кода regexp. При необходимости использования нескольких шаблонов создается несколько экземпляров regexp.

Из соображений производительности рекомендуется создавать экземляры regexp в начале работы и использовать их многократно (не меняя шаблона) по мере необходимости, а удалять только в конце работы. Это сэкономит время на создание объектов regexp и компиляцию шаблонов, которую можно выполнять однократно при инициализации программы. Если надо использовать несколько шаблонов, лучше создать несколько экземпляров regexp, чем каждый раз создавать новый regexp или менять шаблоны одного экземпляра. Число доступных для создания экземпляров regexp не ограничено.

Пример:

          ref:=regexp_init(regexp_def,'\s*input\s*');                 // init regexp
          sNul(regexp_ctrl(ref,'IgnoreCase=1'));                   // ignore case mode
          inp:='Demo Input Line.';                              // set data to process
          if regexp_test(ref,inp) then writeln('Found.');          // check inp is match
          if regexp_exec(ref,inp)>0 then begin                     // find all matches:
           for i:=1 to regexp_matchnum(ref,0) do begin             // for all matches
            writeln('match ',i,' str ',regexp_matchstr(ref,i,0));  // print match[i] string
            for j:=1 to regexp_matchnum(ref,i) do                  // for each submatch
            writeln('submatch ',j,' = ',regexp_matchstr(ref,i,j)); // print submatch[j] of match[i]
           end;
          end;
          rep:=' Test ';                                        // set replacer
          out:=regexp_replace(ref,inp,rep);                        // replace pattern to replacer
          writeln(out);                                         // out = 'Demo Test Line'
         

Daq Pascal Api -> backslash_encode backslash_decode backslash_encoder_ctrl

Определение:

function backslash_encode(s:string):string
function backslash_decode(s:string):string
function backslash_encoder_ctrl(p:string):string

Аргументы:

  • s - данные для кодирования/декодирования.
  • p - строка чтения/задания параметров кодировщика.

Результат:

Кодирование/декодирование данных с помощью экранирования обратным слешем (\) - backslash escaping.

Описание:

Функции кодируют/декодируют данные с помощью экранирования с обратным слешем (\).
Такой способ кодирования принят, например, в языках C/C++ для задания строк в тексте программного кода.
Кодирование с обратным слешем (\) часто называют экранированием (escape), т.к. символ обратного слеша (\) как бы экранирует следующий за ним символ.
Кодирование с обратным слешем удобно применять для строк, состоящих в основном из читабельных символов, т.к. после кодирования текст в значительной степени сохраняет читабельность. В то же время этот кодированный текст можно передавать по текстовым каналам связи, т.к. в нем уже нет управляющих символов (они заменяются печатными кодами). Это делает кодирование с обратным слешем (\) удобным для отладочного вывода или передачи обычных текстов (не двоичных данных) по каналам связи. Кодированный текст также можно использовать для хранения текста в строках, которые не предполагают разрыва строки. Для передачи произвольных двоичных данных кодирование с обратным слешем (\) тоже подходит, но хуже чем base64, т.к. размер данных может сильно (до 4 раз) возрасти при кодировании. При этом для обычных (читабельных) текстов коэффициент увеличения размера данных будет небольшим (около 1). Гибкая настройка кодировщика позволяет управлять набором символов, которые будут экранироваться, что позволяет использовать кодировщик для передачи данных по каналам связи с ограничениями на набор символов в полосе пропускания.

Примечание: из-за отсутствия единого стандарта на \-экранирование здесь принят минимальный набор правил, обеспечивающий взаимооднозначное преобразование строк (чтобы кодер/декодер работали без искажений). Некоторые последовательности (например, \e для символа ESC \x1B или \0 для символа NUL \x00) здесь не включены, т.к. они не везде поддерживаются или неоднозначно интерпретируются. Также отсутствует поддержка octal кодировки (\nnn) из-за неоднозначности интерпретации (этот метод кодирования считается устаревшим и не рекомендуется стандартом EcmaScript5, вместо него предлагается использовать hex-коды). При этом, например, \0 интепретируется здесь как символ цифры 0 (а не \x00). Для передачи нулевого символа (NUL \x00) используется hex-код. Кроме того, здесь не поддерживается кодирование символов Unicode (\unnnn), которое также можно заменить последовательностью hex-кодов. Кроме того, длина hex-кода принимается всегда равной 4 (\xnn), а не продолжается до появления любого нецифрового символа, как описывают некоторые руководства. Надо заметить, что последовательность (\n) интерпретируется всегда как (LF \x0A), хотя некоторые руководства утверждают, что (\n) может означать (CRLF). Последовательность (CRLF) здесь передается как (\r\n). Таким образом, данная реализация кодера генерирует код, который поймет большинство декодеров, но другие \-декодеры могут генерировать код, который данный декодер поймет неверно. Причина - в отсутствии единого стандарта \-кодирования и неоднозначной интерпретации правил кодирования в разных реализациях кодера/декодера. Поэтому лучше использовать данный кодер на обеих сторонах приема/передачи.

Функция backslash_encode(s) кодирует строку данных s.

Функция backslash_decode(s) декодирует строку данных s. При ошибке декодирования (типа неверного hex-кода) возвращает пустую строку.

Функция backslash_encoder_ctrl(p) управляет через команду p способом кодирования backslash_encode.
Строка параметра p имеет вида name (чтение) или name=value (запись).
Функция возвращает предыдущее значение параметра (до изменения).
Допустимые имена параметров:
esclist - список дополнительных символов, экранируемых обратным слешем (\).
hexlist - список дополнительных символов, передаваемых в HEX-кодировке (\xnn).
Не забывайте восстанавливать значения по умолчанию после завершения кодирования. По умолчанию списки esclist, hexlist пустые.

    // список esclist задает дополнительные символы, экранируемые обратным слешем \
    ///////////////////////////////////////////////////////////////////////////////
    s:=backslash_encoder_ctrl('esclist');       // прочитать список esclist
    s:=backslash_encoder_ctrl('esclist=()/|');  // задать    список esclist
    s:=backslash_encode('xy(1)=ab/cd');         // s='xy\(1\)=ab\/cd'
    s:=backslash_encoder_ctrl('esclist=');      // очистить  список esclist

    // список hexlist задает дополнительные символы, экранируемые HEX-кодом \xnn
    ////////////////////////////////////////////////////////////////////////////
    s:=backslash_encoder_ctrl('hexlist');       // прочитать список hexlist
    s:=backslash_encoder_ctrl('hexlist=()/|');  // задать    список hexlist
    s:=backslash_encode('xy(1)=ab/cd');         // s='xy\x281\x29=ab\x2Fcd'
    s:=backslash_encoder_ctrl('hexlist=');      // очистить  список hexlist
         

Алгоритм работает так.

  • Символы (BEL,BS,HT,LF,VT,FF,CR,",',?,\) кодируются с помощью специальных последовательностей (\a,\b,\t,\n,\v,\f,\r,\",\',\?,\\).
  • Символы управления (\x00..\x1F,\x7F) кроме указанных выше всегда передаются в виде кода \xnn, где nn - шестнадцатеричный код символа.
  • Остальные символы передаются без изменений "как есть".
  • При помещении символа в список esclist он экранируется обратным слешем (\).
  • При помещении символа в список hexlist он передается шестнадцатеричным кодом \xnn.
  • По умолчению списки esclist, hexlist пусты, т.е. экранирование дополнительных символов (кроме вышеуказанных) по умолчанию не используется.

Для справки еще такая таблица:
 C/C++ - style encoding with backslash (\) escaping.
 Escape sequence    Description     Representation
 \a                 audible bell    byte 0x07 in ASCII encoding
 \b                 backspace       byte 0x08 in ASCII encoding
 \t                 horizontal tab  byte 0x09 in ASCII encoding
 \n                 line feed       byte 0x0a in ASCII encoding
 \v                 vertical tab    byte 0x0b in ASCII encoding
 \f                 form feed       byte 0x0c in ASCII encoding
 \r                 carriage return byte 0x0d in ASCII encoding
 \"                 double quote    byte 0x22 in ASCII encoding
 \'                 single quote    byte 0x27 in ASCII encoding
 \?                 question mark   byte 0x3f in ASCII encoding
 \\                 backslash       byte 0x5c in ASCII encoding
 \xnn               hexadecimal     byte nn
 Notes:
  1)    control chars \x00..\x1F,\x7F always passed as hex \xnn
  2)    any printable char (c) except of (a,b,t,n,v,f,r,",',?,\)
        may be passed as \c (use esclist to apply this pass).
  3)    any char (c) may be passed as hex \xnn (use hexlist).
  4)    current version don`t use octal codes, use hex instead.
         
Списки esclist, hexlist, задаваемые backslash_encoder_ctrl, позволяют экранировать дополнительный набор символов (кроме стандартных). Это может быть удобно для хранения или передачи данных по каналам связи. Например, если хочется передавать текст как одно слово (без пробелов), можно использовать экранирование пробелов, поместив пробел в список hexlist. Независимо от содержимого списков декодер backslash_decode сумеет восстановить исходную строку.

Пример:

          s:=backslash_encode('Line1'+CRLF+'Line2'+CRLF);  // s='Line1\r\nLine2\r\n'
          s:=backslash_decode(s);                          // return original string
          sNul(backslash_encoder_ctrl('hexlist= '));       // add space to hexlist
          s:=backslash_encode('word1 word2');              // s='word1\x20word2'
         

Daq Pascal Api -> pct_encode pct_decode pct_encoder_ctrl percent_encode percent_decode percent_encoder_ctrl

Определение:

function pct_encode(s:string):string
function pct_decode(s:string):string
function pct_encoder_ctrl(p:string):string
function percent_encode(s:string):string
function percent_decode(s:string):string
function percent_encoder_ctrl(p:string):string

Аргументы:

  • s - данные для кодирования/декодирования.
  • p - строка чтения/задания параметров кодировщика.

Результат:

Кодирование/декодирование данных с помощью процентного кодировщика, согласно стандарту RFC-3986.
Этот кодировщик называют pct-encode/decode или percent-encode/decode.
Функции pct_xxx и percent_xxx являются синонимами.

Описание:

Функции кодируют/декодируют данные с помощью экранирования с процентом [%], согласно стандарту RFC-3986.
Такой способ кодирования принят, например, в протоколе HTTP для задания адресов ресурсов URI.

Кодирование pct-encode заключается в том, что символы из определенного набора защищенных (reserved) символов заменяются на строку %xx, где xx - двухсимвольный шестнадцатеричный HEX-код заменяемого символа. Символ процента [%] входит в набор защищенных символов ВСЕГДА и замещается строкой %25. Стандарт определяет набор и других защищенных символов, однако этот набор может быть произвольно расширен или сужен без потери возможности декодирования. В сущности, набор защищенных символов определяется полосой пропускания среды передачи данных, в контексте которой используется кодировщик. Набор защищенных символов кодировщика можно менять по желанию, это не повлияет на возможность декодирования. При этом начальный набор защищенных символов соответствует стандарту RFC-3986.

Процентная кодировка (pct-encode или percent-encode) очень похожа на (считающуюся устаревшей) URL кодировку, но отличается набором правил кодирования. В корировке некоторые символы заменяются другими (например, пробел на плюс), что делает кодировку URL более сложной и запутанной. Кодировка pct-encode намного проще и понятнее - в ней, по сути, есть лищь одно универсальное правило замены %xx. Поэтому в будущем предпочтение следует отдавать кодировщику pct-encode.

Кодирование pct-encode удобно применять для строк, состоящих в основном из читабельных символов, т.к. после кодирования текст в значительной степени сохраняет читабельность. В то же время этот кодированный текст можно передавать по текстовым каналам связи, т.к. в нем уже нет управляющих символов (они заменяются печатными кодами). Это делает кодирование pct-encode удобным для отладочного вывода или передачи обычных текстов (не двоичных данных) по каналам связи. Кодированный текст также можно использовать для хранения текста в строках, которые не предполагают разрыва строки. Для передачи произвольных двоичных данных кодирование pct-encode тоже подходит, но чуть хуже чем base64, т.к. размер данных может сильно (до 3 раз) возрасти при кодировании. При этом для обычных (читабельных) текстов коэффициент увеличения размера данных будет небольшим (чуть больше 1). Гибкая настройка кодировщика позволяет управлять набором защищенных (reserved) символов, которые будут экранироваться, что позволяет использовать кодировщик для передачи данных по каналам связи с ограничениями на набор символов в полосе пропускания.

Функция percent_encode(s) кодирует строку данных s. При этом используется набор защищенных символов, который изначально соответствует стандарту RFC-3986, но может быть изменен вызовом percent_encoder_ctrl.

Функция percent_decode(s) декодирует строку данных s. При наличии ошибки в кодированной строке декодируется то, что возможно (что соответствует шаблону %xx), а остальные символы остаются неизменными. При ошибке декодирования (типа неверного hex-кода) функция возвращает частично декодированную строку, но при этом увеличивается внутренний счетчик ошибок, который можно прочитать вызовом percent_encoder_ctrl('Errors'). Этот счетчик можно использовать для обработки ошибок.
Ошибкой декодирования считается, если в закодированной строке найден "висящий" символ процента %, за которым в двух следующих символах не содержится корректный HEX-код %xx. Строго говоря, "висящий" символ процента не может встречаться в закодированной строке. При (правильной) кодировке символ процента должен быть преобразован в %25, поэтому даже при наличии в исходной строке символов процента закодированная строка будет корректно декодироваться. Однако при "ручном" кодировании программисты могут не заметить "висящий" процент и не заменить его корректным кодом. Отсюда и могут быть ошибки.

Функция percent_encoder_ctrl(p) управляет через команду p способом кодирования percent_encode.
Строка параметра p имеет вид name (чтение) или name=value (запись).
Функция возвращает предыдущее значение параметра (до изменения).
Допустимые имена параметров:
Reserved - список защищенных символов, экранируемых процентом.
Errors - счетчик ошибок декодирования.
Не забывайте восстанавливать значения по умолчанию после завершения кодирования. Это делвется вызовом percent_encoder_ctrl('reserved='); По умолчанию набор Reserved соответствует стандарту RFC-3986, а счетчик ошибок Errors равен нулю.

    // список Reserved задает защищенные символы, заменяемые на %xx
    ///////////////////////////////////////////////////////////////////////////////
    s:=percent_encoder_ctrl('Reserved');       // прочитать список Reserved
    s:=percent_encoder_ctrl('Reserved=( )/|'); // задать    список Reserved
    s:=percent_encode('Это пример');           // s='Это%20пример'
    s:=percent_encoder_ctrl('Reserved=');      // сбросить Reserved к стандартному значению

    // счетчик Errors содержит число ошибок декодирования
    ////////////////////////////////////////////////////////////////////////////
    s:=percent_encoder_ctrl('Errors');        // прочитать счетчик
    s:=percent_encoder_ctrl('Errors=0');      // прочитать и сбросить в ноль
         
Набор защищенных символов Reserved, задаваемый через percent_encoder_ctrl, позволяют экранировать любой желаемый набор символов для передачи данных по каналам связи. Например, если хочется передавать текст как одно слово (без пробелов), можно использовать экранирование пробелов, поместив пробел в список Reserved. Независимо от содержимого набора защищенных символов декодер percent_decode сумеет восстановить исходную строку.

Пример:

          s:=percent_encode('Line1'+CRLF+'Line2'+CRLF);  // s='Line1%0D%0ALine2%0D%0A'
          s:=percent_decode(s);                          // return original string
         

Daq Pascal Api -> fexpand

Определение:

function fexpand(s:string):string

Аргументы:

  • s - ненормализованное имя файла.

Результат:

Нормализует имя файла, делает его полным.

Описание:

Функция используются при формировании имен файлов. Она нормализует имя файла, делает путь файла полным.

Если указано имя файла без пути (только имя и расширение), прибавляется путь текущего каталога. В частности, fexpand('') возвращает просто текущий каталог.

Если указано имя файла с относительным путем, путь преобразуется в абсолютный относительно текущего каталога.

Если указано имя файла с абсолютным путем, путь нормализуется, приводится к каноническому виду. Например, если имя было 'c:\tmp\picture\2005\..\family.bmp', оно преобразуется к эквивалентному имени 'c:\tmp\picture\family.bmp'.

Имена файлов в каноническом виде можно сравнивать. То есть выражение IsSameText(FExpand(f1),FExpand(f2)) говорит о том, что f1 и f2 указывают на один файл.

Имена файлов имеют не более 255 символов.

Пример:

          s:=fexpand('c:\tmp\picture\2005\..\family.bmp'); {'c:\tmp\picture\family.bmp'}
          s:=fexpand('c:\tmp\picture\.\family.bmp');       {'c:\tmp\picture\family.bmp'}
          s:=fexpand('');                                  {текущий каталог}
          s:=fexpand('data.txt');                          {полный путь файла 'data.txt' в текущем каталоге}
         

Daq Pascal Api -> addbackslash addpathdelim dropbackslash droppathdelim

Определение:

function addbackslash(s:string):string
function addpathdelim(s:string):string
function dropbackslash(s:string):string
function droppathdelim(s:string):string

Аргументы:

  • s - преобразуемая строка.

Результат:

Добавляет или удаляет символ разделителя каталогов в конце строки.

Описание:

Функции используются при формировании имени файла или каталога.

addbackslash(s), addpathdelim(s) - добавляет в конец строки символ разделителя каталогов (для Windows это \ - обратный слеш), если его там нет. Применяется для формирования имен файлов. Предпочтительнее использовать addpathdelim(s), в названии которого используется мультиплатформенное название PathDelim.

dropbackslash(s), droppathdelim(s) - удаляет из конца строки символ разделителя каталогов (для Windows это \ - обратный слеш), если он там есть. Применяется для формирования имен каталогов. Предпочтительнее использовать droppathdelim(s), в названии которого используется мультиплатформенное название PathDelim.

Имена файлов имеют не более 255 символов.

Пример:

          s:=addbackslash('c:\tmp');             {'c:\tmp\' }
          s:=addbackslash('c:\tmp\');            {'c:\tmp\' }
          s:=dropbackslash('c:\tmp');            {'c:\tmp' }
          s:=dropbackslash('c:\tmp\');           {'c:\tmp' }
          s:=addbackslash('c:\tmp')+'file.txt';  {'c:\tmp\file.txt' }
         

Daq Pascal Api -> extractfilepath extractfilename extractfileext

Определение:

function extractfilepath(s:string):string
function extractfilename(s:string):string
function extractfileext(s:string):string

Аргументы:

  • s - имя файла.

Результат:

Выделяет из полного имени файла путь, имя и расширение.

Описание:

Функции используются при формировании имени файла или каталога.

extractfilepath(s) - выделяет путь файла, то есть каталог, где он расположен. Путем считается часть имени файла до последнего разделителя каталогов "/" или "\". Например, файл "c:\tmp\x.bmp" имеет путь "c:\tmp".

extractfilename(s) - выделяет имя файла, без каталога и расширения.

extractfileext(s) - выделяет расширение файла. Расширением считается часть имени файла начиная от последней точки. Например, файл "c:\tmp\x.bmp" имеет расширение ".bmp".

Имена файлов имеют не более 255 символов.

Пример:

          s:='c:\tmp\data.txt');
          s:=extractfilepath(s); {'c:\tmp\' }
          s:=extractfilename(s); {'data' }
          s:=extractfileext(s);  {'.txt' }
          s:=addbackslash(extractfilepath(s))+extractfilename(s)+extractfileext(s);
         

Daq Pascal Api -> iswildcard isrelativepath hasextension direxists fileexists

Определение:

function iswildcard(s:string):boolean
function isrelativepath(s:string):boolean
function hasextension(s:string):boolean
function direxists(s:string):boolean
function fileexists(s:string):boolean

Аргументы:

  • s - строка с именем файла или каталога.

Результат:

Проверяет имя файла по различным признакам.

Описание:

Функции используются для проверки имен файлов.

iswildcard(s) - возвращает true, если строка содержит маску файла, заданную символами * или ?.

isrelativepath(s) - возвращает true, если строка содержит относительное имя файла, а не полное (абсолютное) имя файла.

hasextension(s) - возвращает true, если строка содержит имя файла с расширением.

direxists(s) - возвращает true, если существует каталог s.

fileexists(s) - возвращает true, если существует файл s.

Имена файлов имеют не более 255 символов.

Пример:

          b:=iswildcard('*.tx?');                {true}
          b:=iswildcard('a.txt');                {false}
          b:=isrelativepath('.\bin\bp.exe');     {true}
          b:=isrelativepath('c:\bp\bin\bp.exe'); {false}
          b:=hasextension('data.txt');           {true}
          b:=hasextension('data');                 {false}
         

Daq Pascal Api -> fileerase filerename filecopy getfattr setfattr doserror mkdir dirlist createtempfile

Определение:

function fileerase(fname:String):Boolean
function filerename(src2dst:String):Boolean
function filecopy(src2dst:String):Boolean
function getfattr(fname:String):Integer
function setfattr(fname:String;attr:Integer):Boolean
function doserror:Integer
function mkdir(folder:String):Boolean
function dirlist(txt,maxlevel:Integer;folder,pattern:String):Boolean
function createtempfile(template:String):String

Аргументы:

  • fname - строка с именем файла.
  • src2dst - пара имен файлов "источник, приемник".
  • attr - атрибуты файла.
  • folder - строка с именем каталога.
  • pattern - строка с шаблоном поиска файлов.
  • txt - ссылка на текст.
  • maxlevel - максимальный уровень рекурсии.
  • template - строка с шаблоном имени временого файла (XXX.TMP).

Результат:

Функции манипуляции с файлами и каталогами.

Описание:

Функции для манипуляции с файлами и каталогами, не связанные с пересылкой данных.

fileerase(s) - удаляет файл с именем fname.

filerename(s) - переименовывает файл согласно аргументу s.
Аргумент содержит пару имен файлов s='СтароеИмя НовоеИмя', разделенных пробелом или маркером EOL.
Имена файлов передаются в одной из трех форм:

  1. В исходном виде, с разделителем EOL, например:
    filerename('source file.txt'+EOL+'target file.txt')
    Признаком этого варианта служит наличие в строке маркера EOL.

  2. В закавыченном виде с разделителем - пробелом, например:
    filerename('"source file.txt" "target file.txt"')
    Признаком этого варианта служит наличие в строке кавычек.

  3. В URL-кодировке (необходимость в ней есть только если имена файлов содержат пробелы). Наприме:
    filerename(URL_Pack('c:\Program Files\demo.txt')+' '+URL_Pack('c:\temp\demo.txt'))
    Этот вариант выбирается, если в строке нет ни маркера EOL, ни кавычек.

Рекомендуется использовать второй вариант (с кавычками), как наиболее понятный и переносимый.
Заключать имена файлов в кавычки можно вызовом AnsiQuotedStr(FileName,QuoteMark).

filecopy(s) - копирует файл согласно аргументу s.
Аргумент содержит пару имен файлов s='Оригинал Копия', разделенных пробелом или маркером EOL.
Имена файлов передаются таким же способом, как в функции filerename.
Рекомендуется использовать имена файлов в кавычках, разделенные пробелом. Например:
filerename('"c:\Program Files\demo.txt" "c:\temp\demo.txt"')
Заключать имена файлов в кавычки можно вызовом AnsiQuotedStr(FileName,QuoteMark).

getfattr(fname) - возвращает атрибуты файла fname:

  • $01 = ReadOnly
  • $02 = Hidden
  • $04 = SysFile
  • $08 = VolumeID
  • $10 = Directory
  • $20 = Archive
Возвращает -1, усли файл не существует.

setfattr(fname,attr) - устанавливает новые атрибуты attr файла fname.

doserror - возвращает ошибку DOS при выполнении последней файловой операции. В CRW32 это системный вызов GetLastError.

mkdir(folder) - создает каталог folder. При надобности создает родительские каталоги, если они не существуют. Например, mkdir('c:\tmp\data') может создать каталог c:\tmp, а затем c:\tmp\data.

dirlist(txt,maxlevel,folder,pattern) - создает список файлов каталога и его подкаталогов по данному шаблону поиска. Возвращает в качестве значения аргумент txt, чтобы можно было использовать конструкции типа txt:=dirlist(text_new,0,'c:\daq','*.cfg').

  • txt - ссылка на текст, в который будет записан (точнее, добавлен) список искомых файлов. Например, это может быть результат вызова text_new. Список найденных файлов добавляется в конец текста, не затрагивая уже имеющихся в тексте строк. Поэтому можно делать несколько вызовов dirlist для добавления файлов в один и тот же текст. Функция возвращает в качестве результата значение аргумента txt (на практике это удобно).
  • maxlevel - максимальный уровень рекурсии при просмотре подкаталогов. Например, если задать maxlevel=0, будет добавлен список файлов только данного каталога, без подкаталогов. Если задать maxlevel=1, будут включены также файлы подкаталогов, на один уровень рекурсии. И так далее.
  • folder - каталог, в котором ищутся файлы. При положительном уровне рекурсии в поиск включаются также подкаталоги каталога folder.
  • pattern - шаблон (маска) для поиска файлов. Это список разделенных запятыми или пробелами шаблонов имени искомых файлов, включая символы - заменители * и ?. Например, можно задать шаблон типа '*.exe, *.com, data??????.dat' и т.д. Шаблон *.* означает любой файл.
Надо иметь в виду, что в список dirlist включаются не только имена найденных обычных файлов, отвечающих шаблону поиска, но также имена всех каталогов, в которых производился поиск. Каталоги можно отличить от обычных файлов по такому признаку: их имя заканчивается обратным слэшем \, в то время как имя обычного файла не может заканчиваться слэшем. Например, C:\DAQ\ - каталог, а C:\DAQ - простой файл. Кроме того, при указании нескольких масок файлов каждая маска ищется заново, как если бы для каждой маски делался свой вызов dirlist. Поэтому одни и те же каталоги поиска могут быть включены в список несколько раз, по числу элементов в шаблоне поиска pattern. По указанной причине после чтения каталога dirlist может потребоваться анализ списка и удаление лишних имен (например, каталогов, если они не нужны).

createtempfile(template) - создает временный файл с уникальным именем по заданному шаблону template типа XXX.TMP, где XXX - префикс файла (3 символа), .TMP - расширение. Функция создает файл нулевой длины с уникальным именем и применяется для создания временных файлов для промежуточных операций с данными. После использования временные файлы надо удалять.

Имена файлов имеют не более 255 символов.
В текущей версии могут быть проблемы с именами файлов и каталогов, содержащими пробелы.

Пример:

          b:=fileerase('data.txt');
          b:=filerename('from.txt to.txt');
          b:=filecopy('from.txt to.txt');
          i:=getfattr('data.txt');
          b:=setfattr('data.txt',ior(i,$20));
          b:=mkdir('c:\data');
          s:=createtempfile('dat.tmp');

          Пример чтения файлов *.cfg, *.bmp и *.crc из каталога DAQ:

          dir:=paramstr('daqconfigpath')+'\..';
          t:=dirlist(text_new,maxint,dir,'*.bmp,*.crc,*.cfg');
          for i:=0 to text_numln(t)-1 do begin
           s:=text_getln(t,i);
           if s<>'' then if s[length[s]]<>'\' then writeln(s); {исключить каталоги}
          end;
          b:=text_free(t);

          Результатом будет что-то вроде

            D:\DAQ\TESTS\BITMAPS\LED12025.BMP
            D:\DAQ\TESTS\BITMAPS\LED60X18.BMP
            D:\DAQ\TESTS\CIRCUITS\HEATS.CRC
            D:\DAQ\TESTS\CONFIG\TEST.CFG
            D:\DAQ\TESTS\CONFIG\HEATDEMO.CFG
         

Daq Pascal Api -> adaptfilename adaptexefilename adaptdllfilename adaptlnkfilename adaptfilenamemode

Определение:

function adaptfilename(s:string):string
function adaptexefilename(s:string):string
function adaptdllfilename(s:string):string
function adaptlnkfilename(s:string):string
function adaptfilenamemode(m:integer):integer

Аргументы:

  • s - имя файла.
  • m - режим работы (mode).

Результат:

Функции для (первичной) адаптации (adapt) имени файла под данную операционную систему.

Описание:

Функции используются при формировании имени файла или каталога.
Они нужны для (первичной) адаптации (приведения формата) имени файла к правилам текущей операционной системы.
  • Под Unix:
    • используются разделители каталогов "/"
    • имена файлов не содержат имени диска (C:)
    • имена исполняемых файлов не имеют расширений
    • сценарии оболочки (обычно, по соглашению) имеют расширение .sh
    • библиотечные файлы (обычно, по соглашению) имеют имя вида lib*.so
    • файлы ярлыков Рабочего Стола имеют расширение .desktop
    • имена файлов (обычно, по соглашению) заданы в нижнем регистре
    • имена файлов задаются в кодировке UTF8
  • Под Windows:
    • используются разделители каталогов "\"
    • имена файлов могут содержать имя диска (C:)
    • имена исполняемых файлов имеют расширение .exe
    • сценарии оболочки имеют расширение .bat или .cmd
    • библиотечные файлы имеют расширение .dll
    • файлы ярлыков Рабочего Стола имеют расширение .lnk
    • регистр имен файлов не играет роли
    • имена файлов задаются в кодировке ANSI

Задача, собственно, состоит в адаптации имен файлов под эти правила.

adaptfilename(s) - адаптирует имя обычного файла, в зависимости от флагов режима работы.
Удаляются лишние пробелы, исправляются разделители каталогов ("/" или "\"). Под Unix имя преобразуется в нижний регистр, из него удаляется диск ("C:"), если он задан. Дублирующиея разделители ("//", "\\") преобразуются в одинарные. Например, имя файла "resource//bitmap/x.bmp" может быть преобразовано в "resource\bitmap\x.bmp" (под Unix).
Предполагается, что adaptfilename является первым этапом обработки имени файла (например, сразу после чтения из конфигурации). Эта функция не затрагивает каталог или расширение файла, она лишь корректирует имя файла в соответствии с правилами, принятыми в текущей операционной системе, для приведения имени файла к каноническому виду. На втором этапе обработки имени файла применяются другие функции, например, daqfileref.

adaptexefilename(s) - адаптирует имя исполняемого файла, в зависимости от флагов режима работы.
Сначала вызывает adaptfilename(s), затем корректирует расширение файла.
Под Unix удаляет расширение для *.exe файла, а расширения .bat,.cmd заменяет на .sh.
Под Windows добавляет расширение .exe для файла без расширения, а расширение .sh заменяет на .cmd.
Предполагается, что adaptexefilename является первым этапом обработки имени исполняемого файла (например, сразу после чтения имени файла из конфигурации). Эта функция не затрагивает каталог, она лишь корректирует имя и расширение файла в соответствии с правилами, принятыми в текущей операционной системе, для приведения имени исполняемого файла к каноническому виду. На втором этапе обработки имени файла применяются другие функции, например, daqfileref.

adaptdllfilename(s) - адаптирует имя библиотечного файла, в зависимости от флагов режима работы.
Сначала вызывает adaptfilename(s), затем корректирует расширение файла и (возможно) префикс его имени.
Под Unix добавляет расширение .so для файла без расширения, а расширение .dll заменяет на .so. Также добавляет к имени файла префикс lib, если его нет. Таким образом, получается имя файла lib*.so.
Под Windows добавляет расширение .dll для файла без расширения, а расширение .so заменяет на .dll.
Предполагается, что adaptdllfilename является первым этапом обработки имени библиотечного файла (например, сразу после чтения имени файла из конфигурации). Эта функция не затрагивает каталог, она лишь корректирует имя и расширение файла в соответствии с правилами, принятыми в текущей операционной системе, для приведения имени библиотечного файла к каноническому виду. На втором этапе обработки имени файла применяются другие функции, например, daqfileref.

adaptlnkfilename(s) - адаптирует имя файла ссылки - ярлыка, в зависимости от флагов режима работы.
Сначала вызывает adaptfilename(s), затем корректирует расширение файла и (возможно) префикс его имени.
Под Unix добавляет расширение .desktop для файла без расширения, а расширение .lnk заменяет на .desktop.
Под Windows добавляет расширение .lnk для файла без расширения, а расширение .desktop заменяет на .lnk.
Предполагается, что adaptlnkfilename является первым этапом обработки имени файла ярлыка (например, сразу после чтения имени файла из конфигурации). Эта функция не затрагивает каталог, она лишь корректирует имя и расширение файла в соответствии с правилами, принятыми в текущей операционной системе, для приведения имени библиотечного файла к каноническому виду. На втором этапе обработки имени файла применяются другие функции, например, daqfileref.

adaptfilename(m) - возвращает текущий и задает новый режим адаптации имен файлов, скомбинированный из флагов:
afnm_trim=1 - удалять лишние пробелы справа/слева.
afnm_delim=2 - исправлять разделители каталогов ("/","\"), заменяя их на pathdelim.
afnm_drive=4 - (только под Unix) удалять из имени указание диска, например, C:
afnm_lower=8 - (только под Unix) приводит имя к нижнему регистру
afnm_utf8=16 - (только под Unix) обрабатывать имя как UTF8
afnm_nodup=32 - удалять лишние разделители каталогов (типа path///name), оставляя только нужные (path/name).
При вызове adaptfilename(-1) устанавливается режим работы по умолчанию.

При использовании функций adaptfilename предполагается, что в многоплатформенных конфигурациях исполняемые программы (например /path/demo и c:\path\demo.exe) и сценации оболочки (например /path/test.sh и c:\path\test.cmd) лежат в одноименных каталогах. Это позволяет задавать файлы на одной платформе, а использовать (адаптировать) на всех других. Например:
         [&Demo]
         Program = ..\Utility\Demo.exe
         Script  = ..\Utility\Test.cmd
         ...
         exe:=AdaptFileName(ReadIni('[&Demo] Program'));
         cmd:=AdaptFileName(ReadIni('[&Demo] Script'));
         // Unix:    exe = ../utility/demo      cmd = ../utility/test.sh
         // Windows: exe = ..\Utility\Demo.exe  cmd = ..\Utility\Test.cmd
         
         [&Demo]
         Program = ../utility/demo
         Script  = ../utility/test.sh
         ...
         exe:=AdaptFileName(ReadIni('[&Demo] Program'));
         cmd:=AdaptFileName(ReadIni('[&Demo] Script'));
         // Unix:    exe = ../utility/demo      cmd = ../utility/test.sh
         // Windows: exe = ..\utility\demo.exe  cmd = ..\utility\test.cmd         
         

Пример:

          s:='c:\opt\crwdaq\CrwDaq.exe');
          s:=adaptexefilename(s);
          // Под Unix:    /opt/crwdaq/crwdaq
          // Под Windows: c:\opt\crwdaq\CrwDaq.exe
          
          //Установка режима
          m:=adaptfilenamemode(afnm_trim+afnm_delim+afnm_drive+afnm_lower+afnm_utf8+afnm_nodup); 
          
          //Установка режима по умолчанию
          m:=adaptfilenamemode(-1); 
         

Daq Pascal Api -> paramstr

Определение:

function paramstr(arg:string):string

Аргументы:

  • arg - строка запроса.

Результат:

Возвращает параметры системы, заданные строкой запроса arg.

Описание:

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

Если строка arg состоит из слов w1, w2, w3, w4, w5, w5, w6, разделенных пробелом или знаком табуляции, то первое слово w1 задает имя параметра или функции, а w2...w6 – аргументы этой функции. Результат вызова paramstr в зависимости от w1 приведен в таблице:

w1 Описание
0..9 Возвращает параметр командной строки, заданный номером w1.
paramstr('0') возвращает имя запущенной программы.
paramstr('1') возвращает первый аргумент и т.д.
[Compiler.Options] Возвращает текущие настройки компилятора DaqPascal. Параметр w2 принимает значения: Compiler.itabmax, Compiler.btabmax, Compiler.atabmax, Compiler.rtabmax, Compiler.dtabmax, Compiler.dtabmin, Compiler.stabmax, Compiler.stabmin, Compiler.slenmax, которые описаны в разделе опции компилятора.
SessionNumber,
SessionNb
Возвращает номер текущей сессии crwdaq. Это натуральный номер (начиная с 1), используемый Менеджером Сессий для идентификации экземпляров программы.
Функция не имеет параметров.
SessionName Возвращает имя текущей сессии crwdaq. Это имя имеет вид crwdaq_Nb, где Nb - номер сеанса. Например, crwdaq_1.
Функция не имеет параметров.
SessionTitle Возвращает заголовок текущей сессии crwdaq. Этот заголовок имеет вид имеет вид crwdaq#Nb/Pid@Host, где Nb - номер сеанса, Pid - номер процесса, Host - имя компьютера. Например, crwdaq#1/5146@crwbox. Этот заголовок также является заголовком главного окна программы и хранится в переменной окружения CRW_DAQ_SYS_TITLE.
Функция не имеет параметров.
HOMEDIR Возвращает путь домашнего каталога, то есть каталог основной программы (CRW32.EXE).
Функция не имеет параметров.
STARTUPPATH Возвращает путь домашнего каталога, то есть каталог основной программы (CRW32.EXE).
Функция не имеет параметров.
TEMPPATH Возвращает путь временных файлов CRW-DAQ.
Он задается переменной Crw32.ini [System] TempDir = ...
Обычно это каталог Crw32exe\Temp.
Функция не имеет параметров.
DAQCONFIGPATH Возвращает каталог основного файла конфигурации CRW-DAQ.
Функция не имеет параметров.
DAQCONFIGFILE Возвращает имя основного файла конфигурации CRW-DAQ.
Функция не имеет параметров.
DAQDATAPATH Возвращает путь каталога данных DAQ.
Он задается переменной [DAQ] DataPath = ...
Обычно это каталог ..\Data.
Функция не имеет параметров.
DAQBACKUPFILE Возвращает имя файла автосохранения CRW-DAQ.
Он задается переменной [DAQ] BackFile = ...
Функция не имеет параметров.
GETCURDIR
GETCURRDIR
Возвращает текущий каталог.
Функция не имеет параметров.
DEVICENAME Возвращает имя устройства CRW-DAQ.
Необязательный параметр w2 задает номер устройства. Номера устройств начинаются с 0. Если устройства с данным номером нет, функций возвращает пустую строку. Таким образом, функция позволяет просмотреть всю таблицу устройств CRW-DAQ, при этом условием завершения цикла будет возврат пустой строки.
Если параметр w2 не указан, функция возвращает имя текущего устройства CRW-DAQ. Это позволяет идентифицировать текущее устройство.
DEVICEMODEL Возвращает модель устройства CRW-DAQ.
Необязательный параметр w2 задает номер устройства. Номера устройств начинаются с 0. Если устройства с данным номером нет, функций возвращает пустую строку. Таким образом, функция позволяет просмотреть всю таблицу устройств CRW-DAQ, при этом условием завершения цикла будет возврат пустой строки.
Если параметр w2 не указан, функция возвращает модель текущего устройства CRW-DAQ. Это позволяет идентифицировать текущее устройство.
DEVICEFAMILY Возвращает семейство устройства CRW-DAQ.
Необязательный параметр w2 задает номер устройства. Номера устройств начинаются с 0. Если устройства с данным номером нет, функций возвращает пустую строку. Таким образом, функция позволяет просмотреть всю таблицу устройств CRW-DAQ, при этом условием завершения цикла будет возврат пустой строки.
Если параметр w2 не указан, функция возвращает семейство текущего устройства CRW-DAQ. Это позволяет идентифицировать текущее устройство.
PROGNAME Возвращает полное имя основной программы CRW32.EXE.
Функция не имеет параметров.
SYSINIFILE Возвращает полное имя основного INI файла программы, CRW32.INI.
Функция не имеет параметров.
DAQPROGRAM Возвращает имя текущей программы Daq-Pascal (то, что написано в program ...).
Функция не имеет параметров.
USERNAME Возвращает имя текущего пользователя на компьютере, имя котогого задан параметром w2. Если параметр w2 не задан или это точка (т.е. localhost), то имеется в виду локальный компьютер.
COMPUTERNAME Возвращает имя компьютера.
Функция не имеет параметров.
HOSTNAME Возвращает сетевое имя компьютера.
Небязательный параметр w2 принимает значения 0/1 (по умолчанию w2=0).
При w2=0 возвращается короткое имя компьютера, обычно совпадающее с COMPUTERNAME, например, crwbox.
При w2=1 возвращается полное имя компьютера, включающее домен, например, crwbox.abbey.ru.
USERDOMAIN Возвращает доменное имя по имени пользователя.
Небязательный параметр w2 задает имя пользователя. Если имя пользователя не указано, возвращает доменное имя текущего пользователя.
USERLIST Возвращает список пользователей компьютера или домена.
Функция имеет параметры w2..w5. Все параметры необязательные.

Параметр w2 (по умолчанию w3=.) задает имя компьютера в URL-кодировке. По умолчанию это точка (т.е. localhost), обозначающая текущий компьютер. Использование URL-кодировки имени имеет свои достоинства. Например, для задания пустого имени достаточно указать + или %20.

Параметр w3 (по умолчанию w3=0) задает уровень детализации.
При w3=0 выдается простой (в один столбец) список пользователей.
При w3=1 выдается список пользователей вида

            UserName AccountType OnOff Comment
            oleg     User        On    Пользователь по имени oleg
            alex     Root        On    Администратор по имени alex
            Гость    Guest       Off   Гость по имени Гость, отключен
            
Следует иметь в виду, что 1 уровень детализации работает существенно медленнее, чем 0 и может привести к подвисанию потока программы. Поэтому использовать этот уровень детализации надо очень осторожно.

Параметр w4 (по умолчанию w4=$02) задает фильтр:

             $01 = Enumerates local user account data on a domain controller.
             $02 = Enumerates global user account data on a computer.
             $04 = Enumerates proxy accounts.
             $08 = Enumerates domain trust account data on a domain controller.
             $10 = Enumerates workstation or member server account data on a domain controller.
             $20 = Enumerates domain controller account data on a domain controller.
            

Параметр w5 (по умолчанию w5=10000) задает TimeOut в ms.

HOSTLIST Возвращает список серверов в сети.
Функция имеет параметры w2..w6. Все параметры необязательные.

Параметр w2 (по умолчанию w3=.) задает имя компьютера в URL-кодировке. По умолчанию это точка (т.е. localhost), обозначающая текущий компьютер. Использование URL-кодировки имени имеет свои достоинства. Например, для задания пустого имени достаточно указать + или %20.

Параметр w3 задает имя домена в URL-кодировке. По умолчанию это пустая строка (то есть +), обозначающая текущий домен. Использование URL-кодировки имени имеет свои достоинства. Например, для задания пустого имени домена достаточно указать + или %20.

Параметр w4 (по умолчанию w4=0) задает уровень детализации.
При w4=0 выдается простой (в один столбец) список компьютеров/доменов.
При w4=1 выдается список компьютеров вида

            HostName IP            Flags            Comment
            crwbox   174.21.4.131  \WSt\Srv\WinNt\  Сервер DaqGroup
            
Следует иметь в виду, что 1 уровень детализации работает существенно медленнее, чем 0 и может привести к подвисанию потока программы. Поэтому использовать этот уровень детализации надо очень осторожно.

Параметр w5 (по умолчанию w5=$FFFFFFFF=-1) задает фильтр:

             Значение    Flags    Имя                Расшифровка
             $00000001 - WSt      WORKSTATION        All LAN Manager workstations
             $00000002 - Srv      SERVER             All LAN Manager servers
             $00000004 - SQL      SQLSERVER          Any server running with Microsoft SQL Server
             $00000008 - DomCtrl  DOMAIN_CTRL        Primary domain controller
             $00000010 - DomBack  DOMAIN_BAKCTRL     Backup domain controller
             $00000020 - TimSrc   TIME_SOURCE        Server running the Timesource service
             $00000040 - AFP      AFP                Apple File Protocol servers
             $00000080 - Novell   NOVELL             Novell servers
             $00000100 - DomMemb  DOMAIN_MEMBER      LAN Manager 2.x Domain Member
             $40000000 - LocList  LOCAL_LIST_ONLY    Servers maintained by the browser.
             $00000200 - Prn      PRINT              Server sharing print queue
             $00000400 - Deal     DIALIN             Server running dial-in service
             $00000800 - Unix     UNIX               Xenix server
             $00004000 - MFPN     MFPN               Microsoft File and Print for Netware
             $00001000 - WinNt    NT                 Windows NT (either Workstation or Server)
             $00002000 - WinFW    WFW                Server running Windows for Workgroups
             $00008000 - SrvNt    SERVER_NT          Windows NT Non-DC server
             $00010000 - PBrow    POTENTIAL_BROWSER  Server that can run the Browser service
             $00020000 - BBrow    BACKUP_BROWSER     Server running a Browser service as backup
             $00040000 - MBrow    MASTER_BROWSER     Server running the master Browser service
             $00080000 - DomMast  DOMAIN_MASTER      Server running the domain master Browser
             $80000000 - DomEnum  DOMAIN_ENUM        Primary Domain enumerator
             $00400000 - Win9x    WINDOWS            Windows 95 or later
             $FFFFFFFF                               All servers
            
При вызове указывается численная маска w5, показывающая, какие сервера интересуют. Соответственно, у каждого сервера будут установлены свои флаги, указанные в символьном виде, например, \WSt\WinNt\PBrow\MBrow\.

Параметр w6 (по умолчанию w6=10000) задает TimeOut в ms.

DOMAINLIST Возвращает список доменов в сети.
Функция имеет параметры w2..w4. Все параметры необязательные.

Параметр w2 (по умолчанию w3=.) задает имя компьютера в URL-кодировке. По умолчанию это точка (т.е. localhost), обозначающая текущий компьютер. Использование URL-кодировки имени имеет свои достоинства. Например, для задания пустого имени достаточно указать + или %20.

Параметр w3 (по умолчанию w3=0) задает уровень детализации.
При w3=0 выдается простой (в один столбец) список доменов (включая точку и ComputerName).
При w3=1 выдается список компьютеров вида

            DomainName HostName  IP
            .          localhost 127.0.0.1
            kouriakine localhost 174.21.4.33
            abbey      abbot     174.21.4.21
            ...
            
Следует иметь в виду, что 1 уровень детализации работает существенно медленнее, чем 0 и может привести к подвисанию потока программы. Поэтому использовать этот уровень детализации надо очень осторожно.

Параметр w4 (по умолчанию w4=10000) задает TimeOut в ms.

IPADDRESS Возвращает IP адрес компьютера, имя которого задано параметром w2.
Если параметр не указан, имеется в виду локальный компьютер.
MACADDRESS Возвращает MAC адрес сетевой карты компьютера, имя которого задано параметром w2.
Если параметр не указан, имеется в виду локальный компьютер.
COMPORTLIST Возвращает список COM-портов на локальном компьютере, например, "COM1,COM2,COM3".
Функция не имеет параметров.
ADAMTRAFFIC Возвращает траффик устройств ADAM в формате TxPolls TxBytes RxPolls RxBytes.
TxPolls - счетчик запросов передатчика.
TxBytes - счетчик переданных байтов передатчика.
RxPolls - счетчик ответов, успешно полученных приемником.
RxBytes - счетчик байтов, успешно полученных приемником.
Параметр w2 задает источник: COM-порт (например, COM1) или имя конкретного устройства. Если источник не указан или указан источник * (звездочка=любой), то выдается суммарная статистика для всех устройств.
ColorCode Возвращает по имени w2 цвета RGB код цвета $GGBBRR. Например ColorCode Red = $0000FF.
ColorName Возвращает по RGB коду w2 цвета $GGBBRR его имя. Например ColorName $0000FF = Red.
CharsetCode Возвращает по имени w2 кодировки символов её код. Например CharsetCode Russian = 204.
CharsetName Возвращает по коду w2 кодировки символов её имя. Например CharsetName 204 = Russian.
PitchCode Возвращает по имени w2 ширины фонта его код. Например PitchCode Default = 0.
PitchName Возвращает по коду w2 ширины фонта его имя (Default/Fixed/Variable). Например PitchName 0 = Default.
HasherCode Возвращает по имени w2 хеш-функции её кодовый номер, который можно использовать при вызове функции HashIndexOf Например HasherCode Crc16Modbus = 77.
HasherName Возвращает по коду хеш-функции w2 имя алгоритма хеширования, cм. функцию HashIndexOf. Например HasherName 77 = Crc16Modbus.
DaqSysInfo Возвращает по идентификатору w2 значение параметра DAQ системы:
  Tag.Count                           - Счетчик всех тегов
  Curve.Count                         - Счетчик всех кривых
  Device.Count                        - Счетчик всех устройств
  Sensor.Count                        - Счетчик всех сенсоров
  Tab_Window.Count                    - Счетчик всех окон Tab_Window
  Tab_Window.DrawView.Count           - Счетчик рисований окна
  Tab_Window.MonitorCall.Count        - Счетчик вызовов   монитора окон
  Tab_Window.MonitorDraw.Count        - Счетчик рисований в мониторе окон
  Curve_Window.Count                  - Счетчик всех окон Curve_Window
  Curve_Window.DrawView.Count         - Счетчик рисований окна
  Curve_Window.DrawCurve.Count        - Счетчик рисований кривых
  Curve_Window.DrawPoint.Count        - Счетчик рисований точек
  Curve_Window.MonitorCall.Count      - Счетчик вызовов   монитора окон
  Curve_Window.MonitorDraw.Count      - Счетчик рисований в мониторе окон
  Curve_Window.MonitorCurve.Count     - Счетчик рисований кривых в мониторе
  Curve_Window.MonitorPoint.Count     - Счетчик рисований точек  в мониторе
  Spectr_Window.Count                 - Счетчик всех окон Spectr_Window
  Circuit_Window.Count                - Счетчик всех окон Circuit_Window
  Circuit_Window.DrawView.Count       - Счетчик рисований окна
  Circuit_Window.MakeBitmap.Count     - Счетчик создания  изображений  сенсора
  Circuit_Window.DrawSensor.Count     - Счетчик рисований (реальных)   сенсора
  Circuit_Window.PollSensor.Count     - Счетчик опросов для обновления сенсора
  Circuit_Window.TagEvalCall.Count    - Счетчик вызовов формулы   TagEval(v)
  Circuit_Window.LedEvalCall.Count    - Счетчик вызовов формулы   LedEval(v)
  Circuit_Window.PainterCall.Count    - Счетчик вызовов сценариев Painter(v)
  Circuit_Window.PainterApiCall.Count - Счетчик вызовов функций   Painter
  Circuit_Window.PainterApiDraw.Count - Счетчик рисований внутри  Painter
  Circuit_Window.MonitorCall.Count    - Счетчик вызовов монитора окон
  Circuit_Window.MonitorDraw.Count    - Счетчик рисований в мониторе
            
Чтение счетчиков позволяет узнавать важную информацию о системе и мониторировать её производительность.
SYSTEM Возвращает системные параметры, по категории (w2) и имени (w3): ParamStr('SYSTEM w2 w3')
Параметр w2 задает категорию: OS, RAM, PROCESS, METRICS, SCREEN.
Параметр w3 задает имя интересующего параметра в данной категории.

  • SYSTEM OS PLATFORM - Платформа операционной системы, например: 3x,9x,NT,??.
  • SYSTEM OS VERSION - Версия операционной системы, например: 5.1.2600.
  • SYSTEM OS SYSROOT - Каталог операционной системы, например: C:\WINDOWS.
  • SYSTEM OS COMSPEC - Имя командного процессора, например: C:\WINDOWS\SYSTEM32\CMD.EXE.
  • SYSTEM OS SHELL - Имя командного процессора, например: /bin/bash.
  • SYSTEM OS SHELLS - Список допустимых программ - оболочек командного процессора.
  • SYSTEM RAM TOTALPHYS - Общая физическая память в байтах.
  • SYSTEM RAM AVAILPHYS - Свободная физическая память в байтах.
  • SYSTEM RAM MEMORYLOAD - Занятая память в процентах.
  • SYSTEM RAM TOTALVIRTUAL - Общая виртуальная память в байтах.
  • SYSTEM RAM AVAILVIRTUAL - Свободная виртуальная память в байтах.
  • SYSTEM RAM TOTALPAGEFILE - Общая память файла подкачки в байтах.
  • SYSTEM RAM AVAILPAGEFILE - Свободная память файла подкачки в байтах.
  • SYSTEM PROCESS ID - Идентификатор текущего процесса.
  • SYSTEM PROCESS ParentID - Идентификатор родительского процесса.
  • SYSTEM PROCESS ParentExe - Имя файла родительского процесса.
  • SYSTEM PROCESS GDIOBJECTS - Счетчик захваченных ресурсов GDI (графические объекты - изображения, палитры, фонты и т.д.).
  • SYSTEM PROCESS USEROBJECTS - Счетчик захваченных ресурсов USER (окна, курсоры, меню и т.д.)
  • SYSTEM PROCESS KERNELOBJECTS - Счетчик захваченных ресурсов ядра KERNEL (файлы, потоки, мютексы и т.д.).
  • SYSTEM PROCESS GDIOBJECTSQUOTA - Квота на число ресурсов GDI, которые можно захватить.
  • SYSTEM PROCESS USEROBJECTSQUOTA - Квота на число ресурсов USER, которые можно захватить.
  • SYSTEM PROCESS KERNELOBJECTSQUOTA - Квота на число ресурсов ядра KERNEL, которые можно захватить.
  • SYSTEM PROCESS AllocMemCount - Cчетчик числа захваченных блоков памяти.
  • SYSTEM PROCESS AllocMemSize - Cчетчик объема захваченной памяти, байт.
  • SYSTEM PROCESS Times - Список временных параметров процесса и текущего потока.
    1. Время когда был запущен процесс от 1601.01.01-00:00:00 по Гринвичу.
    2. Время когда был остановлен процеесс. Должен быть 0, т.к. процесс работает.
    3. Время работы текущего процесса в режиме ядра.
    4. Время работы текущего процесса в режиме пользователя.
    5. Время когда был запущен поток от 1601.01.01-00:00:00 по Гринвичу.
    6. Время когда был остановлен поток. Должен быть 0, т.к. поток работает.
    7. Время работы текущего потока в режиме ядра.
    8. Время работы текущего потока в режиме пользователя.
    Все времена заданы в 100-ns единицах. Время работы в режиме ядра и пользователя - интегральное, его можно использовать, например, для вычисления средней загрузки процесса и потока. Можно, например, ограничивать объем расчетов, если загрузка стала слишком большой, чтобы дать другим потокам возможность что-то делать.
  • SYSTEM METRICS SM_CMONITORS - Число доступных мониторов.
  • SYSTEM METRICS SM_SAMEDISPLAYFORMAT - Флаг одинакового формата мониторов, если их несколько.
  • SYSTEM METRICS SM_CXSCREEN - Ширина экрана в пикселях.
  • SYSTEM METRICS SM_CYSCREEN - Высота экрана в пикселях.
  • SYSTEM METRICS SM_CXFULLSCREEN - Ширина клиентской области в режиме "полного экрана" в пикселях.
  • SYSTEM METRICS SM_CYFULLSCREEN - Высота клиентской области в режиме "полного экрана" в пикселях.
  • SYSTEM METRICS SM_XVIRTUALSCREEN - Координата X Рабочего Стола в пикселях.
  • SYSTEM METRICS SM_YVIRTUALSCREEN - Координата Y Рабочего Стола в пикселях.
  • SYSTEM METRICS SM_CXVIRTUALSCREEN - Ширина Рабочего Стола в пикселях.
  • SYSTEM METRICS SM_CYVIRTUALSCREEN - Высота Рабочего Стола в пикселях.
  • SYSTEM METRICS SM_CXICON - Ширина нормальных "ярлыков".
  • SYSTEM METRICS SM_CYICON - Высота нормальных "ярлыков".
  • SYSTEM METRICS SM_CXSMICON - Ширина маленьких "ярлыков".
  • SYSTEM METRICS SM_CYSMICON - Высота маленьких "ярлыков".
  • SYSTEM METRICS SM_CXICONSPACING - Ширина ячейки при отображении "ярлыков" на Рабочем Столе.
  • SYSTEM METRICS SM_CYICONSPACING - Высота ячейки при отображении "ярлыков" на Рабочем Столе.
  • SYSTEM METRICS SM_CXMIN - Минимальная ширина окна.
  • SYSTEM METRICS SM_CYMIN - Минимальная высота окна.
  • SYSTEM METRICS SM_CXMINIMIZED - Стандартная ширина "минимизированного" окна.
  • SYSTEM METRICS SM_CYMINIMIZED - Стандартная высота "минимизированного" окна.
  • SYSTEM METRICS SM_CXMAXIMIZED - Стандартная ширина "максимизированного" окна.
  • SYSTEM METRICS SM_CYMAXIMIZED - Стандартная высота "максимизированного" окна.
  • SYSTEM METRICS SM_CXMINTRACK - Минимальная ширина "резиновой рамки" при изменении размера окна.
  • SYSTEM METRICS SM_CYMINTRACK - Минимальная высота "резиновой рамки" при изменении размера окна.
  • SYSTEM METRICS SM_CXMAXTRACK - Максимальная ширина "резиновой рамки" при изменении размера окна.
  • SYSTEM METRICS SM_CYMAXTRACK - Максимальная высота "резиновой рамки" при изменении размера окна.
  • SYSTEM METRICS SM_CYCAPTION - Высота нормального заголовка окна.
  • SYSTEM METRICS SM_CYSMCAPTION - Высота маленького заголовка окна.
  • SYSTEM METRICS SM_CXSIZE - Ширина нормальных кнопок на заголовке окна.
  • SYSTEM METRICS SM_CYSIZE - Высота нормальных кнопок на заголовке окна.
  • SYSTEM METRICS SM_CXSMSIZE - Ширина маленьких кнопок на заголовке окна.
  • SYSTEM METRICS SM_CYSMSIZE - Высота маленьких кнопок на заголовке окна.
  • SYSTEM METRICS SM_CYMENU - Высота нормальной строки меню.
  • SYSTEM METRICS SM_CXMENUSIZE - Ширина кнопок в строке меню.
  • SYSTEM METRICS SM_CYMENUSIZE - Высота кнопок в строке меню.
  • SYSTEM METRICS SM_CXMENUCHECK - Ширина "флажка" отмеченного пункта меню.
  • SYSTEM METRICS SM_CYMENUCHECK - Высота "флажка" отмеченного пункта меню.
  • SYSTEM METRICS SM_MENUDROPALIGNMENT - Флаг выравнивания всплывающего меню 0:Left, 1:Right.
  • SYSTEM METRICS SM_CXBORDER - Ширина рамки нормального окна.
  • SYSTEM METRICS SM_CYBORDER - Высота рамки нормального окна.
  • SYSTEM METRICS SM_CXFRAME - Ширина рамки окна, за которую можно "хватать" мышкой для изменения размеров.
  • SYSTEM METRICS SM_CYFRAME - Высота рамки окна, за которую можно "хватать" мышкой для изменения размеров.
  • SYSTEM METRICS SM_CXDLGFRAME - Ширина рамки диалогового окна.
  • SYSTEM METRICS SM_CYDLGFRAME - Высота рамки диалогового окна.
  • SYSTEM METRICS SM_CXEDGE - Ширина рамки объемного окна.
  • SYSTEM METRICS SM_CYEDGE - Высота рамки объемного окна.
  • SYSTEM METRICS SM_ARRANGE - флаги способа выравнивания окон при минимизации.
  • SYSTEM METRICS SM_CXMINSPACING - ширина ячеек при выстраивании минимизированных окон.
  • SYSTEM METRICS SM_CYMINSPACING - высота ячеек при выстраивании минимизированных окон.
  • SYSTEM METRICS SM_CXHSCROLL - Ширина "стрелки" горизонтальной полосы прокрутки.
  • SYSTEM METRICS SM_CYHSCROLL - Высота "стрелки" горизонтальной полосы прокрутки.
  • SYSTEM METRICS SM_CXVSCROLL - Ширина "стрелки" вертикальной полосы прокрутки.
  • SYSTEM METRICS SM_CYVSCROLL - Высота "стрелки" вертикальной полосы прокрутки.
  • SYSTEM METRICS SM_CXHTHUMB - Ширина "движка" полосы прокрутки.
  • SYSTEM METRICS SM_CYVTHUMB - Высота "движка" полосы прокрутки.
  • SYSTEM METRICS SM_MOUSEPRESENT - Флаг наличия мыши, 0 или 1.
  • SYSTEM METRICS SM_CMOUSEBUTTONS - Число доступных кнопок мыши или 0 если нет мыши.
  • SYSTEM METRICS SM_MOUSEWHEELPRESENT - Флаг наличия "колесика" мыши, 0 или 1.
  • SYSTEM METRICS SM_SWAPBUTTON - Флаг замены левой и правой кнопки мыши (для левшей).
  • SYSTEM METRICS SM_CXCURSOR - Ширина стандартного курсора мыши.
  • SYSTEM METRICS SM_CYCURSOR - Высота стандартного курсора мыши.
  • SYSTEM METRICS SM_CXDOUBLECLK - максимальная ширина области для индикации "двойного щелчка" мыши.
  • SYSTEM METRICS SM_CYDOUBLECLK - максимальная высота области для индикации "двойного щелчка" мыши.
  • SYSTEM METRICS SM_CXDRAG - ширина минимальной области для перехода в режим "drag and drop".
  • SYSTEM METRICS SM_CYDRAG - высота минимальной области для перехода в режим "drag and drop".
  • SYSTEM METRICS SM_SECURE - Флаг наличия системы прав доступа, 0 или 1.
  • SYSTEM METRICS SM_NETWORK - Флаг наличия сети, 0 или 1.
  • SYSTEM METRICS SM_DEBUG - Флаг отладочной версии Windows.
  • SYSTEM METRICS SM_CLEANBOOT - Флаг загрузки: 0="Normal boot", 1="Fail-safe boot", 2="Fail-safe boot with network".
  • SYSTEM METRICS SM_SLOWMACHINE - Флаг "медленной" машины со "слабым" процессором, 0 или 1.
  • SYSTEM METRICS SM_MIDEASTENABLED - Флаг поддержки восточных языков, 0 или 1.
  • SYSTEM SCREEN Width - Ширина экрана (в пикселях).
  • SYSTEM SCREEN Height - Высота экрана (в пикселях).
  • SYSTEM SCREEN ColorDepth - глубина цвета экрана (разрядность цвета, т.е. число бит цвета).
  • SYSTEM SCREEN PixelsPerInch - метрика экрана - число пиксель на дюйм.
  • SYSTEM SCREEN DesktopTop - координата верхнего края Рабочего Стола.
  • SYSTEM SCREEN DesktopLeft - координата левого края Рабочего Стола.
  • SYSTEM SCREEN DesktopWidth - ширина Рабочего Стола (в пикселях).
  • SYSTEM SCREEN DesktopHeight - высота Рабочего Стола (в пикселях).
  • SYSTEM SCREEN WorkTop - координата верхнего края рабочей области экрана (с учетом Панели Задач).
  • SYSTEM SCREEN WorkLeft - координата левого края рабочей области экрана (с учетом Панели Задач).
  • SYSTEM SCREEN WorkWidth - ширина рабочей области экрана.
  • SYSTEM SCREEN WorkHeight - высота рабочей области экрана.
  • SYSTEM SCREEN MonitorCount - число подключенных мониторов.
  • SYSTEM SCREEN MonitorNumber - номер текущего монитора, где работает программа.
  • SYSTEM SCREEN MonitorPrimatry - номер основного монитора (в диапазоне 0..MonitorCount-1).
  • SYSTEM SCREEN MonitorNtop - координата верхнего края монитора N (в диапазоне 0..MonitorCount-1).
  • SYSTEM SCREEN MonitorNleft - координата левого края монитора N.
  • SYSTEM SCREEN MonitorNwidth - ширина экрана монитора N.
  • SYSTEM SCREEN MonitorNheight - высота экрана монитора N.
  • SYSTEM SCREEN MonitorNworkTop - координата верхнего края рабочей области монитора N.
  • SYSTEM SCREEN MonitorNworkLeft - координата левого края рабочей области монитора N.
  • SYSTEM SCREEN MonitorNworkWidth - ширина рабочей области монитора N.
  • SYSTEM SCREEN MonitorNworkHeight - высота рабочей области монитора N.
  • SYSTEM SCREEN MonitorNflags - флаги монитора N.
GUARD Возвращает текущий уровень доступа Lock, Guest, User, Root.
Функция не имеет параметров.
CURVENAME Возвращается имя кривой с номером w2 или пустая строка, если кривой с таким номером нет.
Номера кривых начинаются с 0. Функция позволяет просмотреть всю таблицу кривых CRW-DAQ.
CURWINNAME Возвращается имя окна-кривых Curve_Window с номером w2 или пустая строка, если окна с таким номером нет.
Номера окон начинаются с 0. Функция позволяет просмотреть всю таблицу окон-кривых CRW-DAQ.
CIRWINNAME Возвращается имя окна-мнемосхем Circuit_Window с номером w2 или пустая строка, если окна с таким номером нет.
Номера окон начинаются с 0. Функция позволяет просмотреть всю таблицу окон-мнемосхем CRW-DAQ.
TABWINNAME Возвращается имя окна-таблицы Tab_Window с номером w2 или пустая строка, если окна с таким номером нет.
Номера окон начинаются с 0. Функция позволяет просмотреть все окна-таблицы CRW-DAQ.
SPEWINNAME Возвращается имя окна-спектра Spectr_Window с номером w2 или пустая строка, если окна с таким номером нет.
Номера окон начинаются с 0. Функция позволяет просмотреть всю таблицу окон-спектров CRW-DAQ.
MAINCONSOLE Возвращает имя главной консоли, это ГЛАВНАЯ КОНСОЛЬ или MAIN CONSOLE, в зависимости от текущего языка.
Функция не имеет параметров.
КОНСОЛЬ
CONSOLE
Возвращает имя консоли для устройства с именем w2, это КОНСОЛЬ ... или CONSOLE ..., в зависимости от текущего языка.
GETENV Возвращает переменную окружения с именем w2 для текущего процесса.
FILESEARCH Поиск файла с коротким (без пути) именем w2 в списке путей, содержащихся в списке переменных окружения, заданных в параметре w3. Возвращает полное имя найденного файла или пустую строку, если файл не найден.
Имя файла w2 задано в URL-кодировке (обычные имена пишутся "как есть"). Список имен переменных окружения w3 для поиска также задан в URL-кодировке, что позволяет разделять переменные окружения в списке символом +. Если параметр w3 отсутствует, то по умолчанию задается параметр Path+CRW_DAQ_SYS_PATH+CRW_DAQ_CONFIG_PATH, то есть поиск осуществляется сначала в списке путей, заданных системной переменной Path (общесистемные пути Windows), затем в списке путей, заданных переменной CRW_DAQ_SYS_PATH (пути поиска утилит пакета CRW-DAQ), а затем в списке путей, заданных переменной CRW_DAQ_CONFIG_PATH (пути поиска текущей конфигурации CRW-DAQ). Каждая из переменных окружения в списке w3 должна содержать разделенный точкой с запятой список путей, по которым будет осуществляться поиск файла.
Например:
            // Поиск ping.exe в системных каталогах Windows
            ping:=ParamStr('FileSearch ping.exe Path');
            // Поиск rmtshare.exe в библиотеке утилит CRW-DAQ
            rmtshare:=ParamStr('FileSearch rmtshare.exe CRW_DAQ_SYS_PATH');
            // Поиск arj.exe во всех списках Windows и CRW-DAQ
            arj:=ParamStr('FileSearch arj.exe');
            

ADDSEARCHPATH Добавление пути поиска в переменную окружения. В переменную окружения с именем w2, содержащую разделенный точкой с запятой список путей поиска, добавляется путь, заданный в параметре w3. Возвращает переменную окружения w2 после добавления пути или пустую строку, если добывление прошло неудачно. Список не изменяется, если путь уже есть в списке. Имя каталога может задаваться относительно оснновного конфигурационного файла.
Например:
            // Добавить c:\temp в переменную Path с путями поиска Windows
            s:=ParamStr('AddSearchPath Path c:\Temp');
            

REMSEARCHPATH Удаление пути поиска из переменной окружения. Из переменной окружения с именем w2, содержащей разделенный точкой с запятой список путей поиска, удаляется путь, заданный в параметре w3. Возвращает переменную окружения w2 после удаления пути или пустую строку, если удаление прошло неудачно. Список не изменяется, если пути не было в списке. Имя каталога может задаваться относительно оснновного конфигурационного файла.
Например:
            // Удалить c:\temp из переменной Path с путями поиска Windows
            s:=ParamStr('RemSearchPath Path c:\Temp');
            

FEXPAND Возвращает полное имя файла по имени w2.
Полное имя файла имеет полный путь и нормализовано. Например, имена со ссылками типа .\ и ..\ преобразуются в нормальное имя. Функция гарантирует, что имена файлов после вызова можно сравнивать, например:
            IsSameText(ParamStr('fexpand c:\data\temp\..\x.dat'),ParamStr('fexpand c:\data\x.dat'))=True
            

Имя файла w2 задано в URL-кодировке. Это позволяет обрабатывать имена файлов, содержащие пробелы.
GETEXEBYFILE Возвращает исполняемый файл программы, которая ассоциирована с открытием файла или документа по имени w2.
Файл документа должен существовать, а имя файла должно быть полным (с путем и расширением). Например, для файла *.htm исполняемым файлом будет iexplore.exe, для файлов *.doc - Word.exe и т.д. Полученное имя исполняемого файла позволяет запустить этот файл с указанием исходного файла-документа в качестве параметра в командной строке. В результате появляется возможность открывать файлы документов при помощи внешних программ.
Имя файла w2 задано в URL-кодировке. Это позволяет обрабатывать имена файлов, содержащие пробелы.
EXTRACTFILEPATH Возвращает каталог файла по его полному имени w2.
Имя файла w2 задано в URL-кодировке. Это позволяет обрабатывать имена файлов, содержащие пробелы.
EXTRACTFILENAME Возвращает только имя файла w2, без каталога и расширения.
Имя файла w2 задано в URL-кодировке. Это позволяет обрабатывать имена файлов, содержащие пробелы.
EXTRACTFILEEXT Возвращается расширение по полному имени файла w2.
Имя файла w2 задано в URL-кодировке. Это позволяет обрабатывать имена файлов, содержащие пробелы.
ADDBACKSLASH Добавляет, если надо, обратный слэш '\' в конец имени каталога w2. Используется для формирования имен файлов.
Имя файла w2 задано в URL-кодировке. Это позволяет обрабатывать имена файлов, содержащие пробелы.
AdaptFileName Адаптирует имя файла, см. AdaptFileName.
Имя файла w2 задано в URL-кодировке. Это позволяет обрабатывать имена файлов, содержащие пробелы.
AdaptExeFileName Адаптирует имя исполняемого файла, см. AdaptExeFileName.
Имя файла w2 задано в URL-кодировке. Это позволяет обрабатывать имена файлов, содержащие пробелы.
AdaptDllFileName Адаптирует имя файла динамической библиотеки. Сначала адаптирует имя файла, см. AdaptFileName. Затем корректирует расширение (.dll/.so для Windows/Unix). А также удаляет/добавляет префикс lib в именах библиотек для Windows/Unix.
Имя файла w2 задано в URL-кодировке. Это позволяет обрабатывать имена файлов, содержащие пробелы.
AdaptLnkFileName Адаптирует имя файла ярлыка Рабочего Стола. Сначала адаптирует имя файла, см. AdaptFileName. Затем корректирует расширение (.lnk/.desktop для Windows/Unix).
Имя файла w2 задано в URL-кодировке. Это позволяет обрабатывать имена файлов, содержащие пробелы.
Читает содержимое файла ярлыка Рабочего Стола в виде текста. Файл должен иметь расширение (.lnk/.desktop для Windows/Unix). Возвращает текст из строк (полей) вида Name=Value, разделенные EOL. Исполняемая команда имеет имя поля Exec. Другие поля зависят от системы. Под Windows доступны поля TargetPath, Arguments, WorkingDirectory, WindowStyle, IconLocation, Hotkey, Description. Под Unix поля соответствуют спецификации .desktop файлов.
Имя файла w2 задано в URL-кодировке. Это позволяет обрабатывать имена файлов, содержащие пробелы.
MAKERELATIVEPATH Возвращает относительный путь файла w2, заданный относительно файла w3.
Имена файлов w2, w3 заданы в URL-кодировке. Это позволяет обрабатывать имена файлов, содержащие пробелы.
FORCEPATH Задать (насильно) путь w2 к файлу w3. Каталог результата берется из w2, а имя с расширением - из w3.
Имена файлов w2, w3 заданы в URL-кодировке. Это позволяет обрабатывать имена файлов, содержащие пробелы.
DEFAULTPATH Задать (если не задан) путь w3 к файлу w2. Если имя файла w2 - полное (с каталогом), ничего не делается. Если имя файла w2 - не полное (без каталога), каталог берется из w3.
Имена файлов w2, w3 заданы в URL-кодировке. Это позволяет обрабатывать имена файлов, содержащие пробелы.
FORCEEXTENSION Присвоить (насильно) файлу w2 расширение w3. Каталог и имя файла результата берется из w2, а расширение - из w3.
Имена файлов w2, w3 заданы в URL-кодировке. Это позволяет обрабатывать имена файлов, содержащие пробелы.
DEFAULTEXTENSION Присвоить (если не задано) файлу w2 расширение w3. Если имя файла w2 - полное (с расширением), ничего не делается. Если имя файла w2 - не полное (без расширения), расширение берется из w3.
Имена файлов w2, w3 заданы в URL-кодировке. Это позволяет обрабатывать имена файлов, содержащие пробелы.
REGISTRY Прочитать из реестра Windows данные.
Функция имеет обязательные параметры w2, w3, w4, которые заданы в URL-кодировке (это позволяет работать с произвольными строками).

Параметр w2 (ROOTKEY) может принимать значения:

            HKCR  или HKEY_CLASSES_ROOT
            HKLM  или HKEY_LOCAL_MACHINE
            HKCU  или HKEY_CURRENT_USER
            HKU   или HKEY_USERS
            HKCC  или HKEY_CURRENT_CONFIG
            HKDD  или HKEY_DYN_DATA
            HKPD  или HKEY_PERFORMANCE_DATA
            

Параметр w3 (PATH) задает путь переменной реестра.

Параметр w4 (NAME) задает имя переменной реестра.

Данные возвращаются в виде двоичной строки. Для строковых данных это просто строка, для целых чисел (DWORD) и двоичных данных (BINARY) это dump.
При ошибке возвращается пустая строка.

REGISTRY Записать в реестр Windows данные.
Функция имеет обязательные параметры w2, w3, w4, w5 которые заданы в URL-кодировке (это позволяет работать с произвольными строками).

Параметр w2 (ROOTKEY) может принимать значения:

            HKCR  или HKEY_CLASSES_ROOT
            HKLM  или HKEY_LOCAL_MACHINE
            HKCU  или HKEY_CURRENT_USER
            HKU   или HKEY_USERS
            HKCC  или HKEY_CURRENT_CONFIG
            HKDD  или HKEY_DYN_DATA
            HKPD  или HKEY_PERFORMANCE_DATA
            

Параметр w3 (PATH) задает путь переменной реестра.

Параметр w4 (NAME) задает имя переменной реестра.

Параметр w5 (DATA) задает записываемые данные. Для строковых переменных это просто строка, а для целых чисел (DWORD) и двоичных данных (BINARY) это что-то вроде url_packed(dump(123)).

Функция возвращает данные после записи (при успехе это будет просто DATA). Данные возвращаются в виде двоичной строки. Для строковых данных это просто строка, для целых чисел (DWORD) и двоичных данных (BINARY) это что-то вроде dump.
При ошибке возвращается пустая строка.

GetSystemAssoc Возвращает тип файла, связанный с расширением w2.
Например, для .lm9 это VisualTech.DieselPascal.CrossMachine.
GetSystemFType Возвращает командную строку, открывающую тип файла, заданный в w2.
Например, для VisualTech.DieselPascal.CrossMachine это будет "C:\Program Files\VisualTech\DieselPascal\exewindows\CrossMachine.exe" "%1" %*
GetSystemAssocExe Возвращает исполянемый файл, связанный с расширением w2 (открывающий его).
Например, для .lm9 это C:\Program Files\VisualTech\DieselPascal\exewindows\CrossMachine.exe.
GetSystemFTypeExe Возвращает исполянемый файл, открывающий тип файла, заданный в w2.
Например, для VisualTech.DieselPascal.CrossMachine это C:\Program Files\VisualTech\DieselPascal\exewindows\CrossMachine.exe.
CreateGUID
CreateClassID
Генерирует и возвращает "глобально уникальный идентификатор" GUID (Globally Unique Identifier) в формате, заданном словом w2, из списка (str, bin, hex, nice). Если слово w2 не указано, принимается формат по умолчанию str.
CreateGUID str - возвращает GUID в виде строки канонического вида {6B7826DD-B281-4BA9-B32F-AF78A24DEDA6}.
CreateGUID bin - возвращает GUID в виде строки с двоичным содержимым 16-байтного значения GUID.
CreateGUID hex - возвращает GUID в виде строки вида DD26786B81B2A94BB32FAF78A24DEDA6 в кодировке hex_encode.
CreateGUID nice - возвращает GUID в виде строки вида jc8gbwozo19w7cheja66q55e4y в кодировке nice_encode.
SpecFolder Возвращает системный каталог (special folder) с идентификатором w2. Если слово w2 не указано или равно '*', возвращается список системных каталогов. Слово w2 принимает одно из приведенных значений:
            Пример:
            paramstr('specfolder *')          // Список
            paramstr('specfolder desktop');   // Рабочий стол пользователя

            DESKTOP                 = Папка пользователя Рабочий Стол
            INTERNET                =
            PROGRAMS                = Папка пользователя меню Программы
            CONTROLS                =
            PRINTERS                =
            PERSONAL                = Папка пользователя Мои Документы
            FAVORITES               = Папка пользователя Избранное
            STARTUP                 = Папка пользователя Автозагрузка
            RECENT                  = Папка пользователя Недавнее
            SENDTO                  = Папка пользователя  Послать...
            BITBUCKET               =
            STARTMENU               = Папка пользователя меню Пуск
            MYDOCUMENTS             =
            MYMUSIC                 = Папка пользователя Музыка
            MYVIDEO                 = Папка пользователя Видео
            DESKTOPDIRECTORY        = Папка пользователя Рабочий Стол
            DRIVES                  =
            NETWORK                 =
            NETHOOD                 = Папка пользователя Сетевое Окружение
            FONTS                   = Папка системная со шрифтами
            TEMPLATES               = Папка пользователя с шаблонами
            COMMON_STARTMENU        = Папка общая меню Пуск
            COMMON_PROGRAMS         = Папка общая меню Программы
            COMMON_STARTUP          = Папка общая меню Автозагрузка
            COMMON_DESKTOPDIRECTORY = Папка общая Рабочий Стол
            APPDATA                 = Папка пользователя для файлов приложений
            PRINTHOOD               =
            LOCAL_APPDATA           = Папка пользователя для настроек приложений
            ALTSTARTUP              =
            COMMON_ALTSTARTUP       =
            COMMON_FAVORITES        = Папка общая Избранное
            INTERNET_CACHE          =
            COOKIES                 =
            HISTORY                 =
            COMMON_APPDATA          = Папка общая для файлов приложений
            WINDOWS                 = Папка системная где установлена Windows
            SYSTEM                  = Папка системная Windows\System32
            PROGRAM_FILES           = Папка системная %ProgramFiles%
            MYPICTURES              = Папка пользователя Изображения
            PROFILE                 = Папка пользователя домашняя (профиль)
            SYSTEMX86               = Папка системная Windows\System32 для 32-разрядной системы
            PROGRAM_FILESX86        = Папка системная %ProgramFiles(x86)%
            PROGRAM_FILES_COMMON    = Папка системная %CommonProgramFiles%
            PROGRAM_FILES_COMMONX86 = Папка системная %CommonProgramFiles(x86)%
            COMMON_TEMPLATES        = Папка общая Шаблоны
            COMMON_DOCUMENTS        = Папка общая Документы
            COMMON_ADMINTOOLS       = Папка общая для команд администрирования
            ADMINTOOLS              = Папка пользователя для команд администрирования
            CONNECTIONS             =
            COMMON_MUSIC            = Папка общая Музыка
            COMMON_PICTURES         = Папка общая Изображения
            COMMON_VIDEO            = Папка общая Видео
            RESOURCES               = Папка системная с ресурсами
            RESOURCES_LOCALIZED     =
            COMMON_OEM_LINKS        =
            CDBURN_AREA             = Папка системная для создания CD/DVD
            COMPUTERSNEARME         =
            
AnsiCodePage Возвращает кодовую страницу ANSI (GetACP) для программ Windows. Для русской версии Windows это 1251.
OemCodePage Возвращает кодовую страницу OEM (GetOEMCP) для программ DOS. Для русской версии Windows это 866.
AppFormBounds Возвращает абсолютные экранные координаты Left,Top,Right,Bottom формы приложения, т.е. главного окна CRW-DAQ.
AppClientBounds Возвращает абсолютные экранные координаты Left,Top,Right,Bottom клиентской области приложения, т.е. клиентской области главного окна CRW-DAQ, которое содержит в себе остальные окна. Координаты клиентской области приложения нужны потому, что в функциях WinDraw, WinSelect координаты окон задаются относительно клиентской области приложения, в то время как в функции ClickParams координаты объектов (мыши, окна мнемосхемы, сенсора) выдаются в абсолютных экранных координатах.
OleDbProviderNames Возвращает список провайдеров OLEDB или ADO, т.е. компонентов для доступа к базам данных через интерфейс ADO. Программный интерфейс ADO используется в пакете для доступа к СУБД. Список провайдеров позволяет проверить наличие в системе нужного провайдера.
OdbcDriverNames Возвращает список драйверов ODBC, т.е. библиотек для доступа к базам данных через интерфейс ODBC. Программный интерфейс ADO используется в пакете для доступа к СУБД. При этом провайдеры ADO могут использовать драйверы ODBC. Список драйверов позволяет проверить наличие в системе нужного драйвера.
MainInstance Возвращает идентификатор (handle) экземпляра приложения.
MainThreadId Возвращает идентификатор (id) главного потока приложения.
CurrentThreadId Возвращает идентификатор (id) текущего потока программы.
CurrentProcessId Возвращает инентификатор текущего процесса (PID).
ApplicationHandle Возвращает инентификатор (handle) окна приложения. Как правило, это скрытое окно, нужное для обработки событий.
MainFormHandle Возвращает инентификатор (handle) главной формы (окна) приложения.
Polling.UseMsgPump Возвращает флаг (0/1) использования (Use) потоком опроса (Polling) очереди сообщений (Message Pump). Если это флаг установлен, то поток программы использует очередь сообщений (PeekMessage/DispatchMessage). Если задан параметр (w2=0/1), то задается новое значение режима опроса очереди сообщений. Очередь сообщений нужна в отдельных случаях, например, при использовании COM объектов (таких как ADO, см. DbApi). Использование очереди событий всеми потоками по умолчанию задается в параметре Crw32.ini [System] TPolling.DefMsgPump файла настроек. Режим очереди сообщений рекомендуется задавать однократно при старте программы.
Polling.ThreadID Возвращает идентифиукатор потока опроса (Polling).
Polling.Delay Возвращает задержку (delay) потока опроса (Polling) в миллисекундах (ms). Фактически этот параметр определяет период опроса потока, т.к. цикл опроса основан на задержке (засыпании) потока. После очередного опроса в цикле поток засыпает на заданную величину задержки, чтобы освободить процесссор для других задач. После (заданной) задержки поток просыпается и выполняет очередной опрос, обеспечивая периодический опрос. Поток может проснуться и досрочно, если случились важные события (нажатие кнопки, получение сообщения devsend и другие). При задании параметра (w2) в диапазоне 1..5000 можно динамически менять частоту потока опроса (Polling).
Polling.Priority Возвращает приоритет потока опроса (Polling) из набора ( tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest, tpTimeCritical ). При задании параметра (w2) можно динамически менять приоритет потока опроса (Polling).
[:digit:] Возвращает набор символов Posix [:digit:] - десятичных цифр '0123456789'. Наборы символов используются при анализе строк.
[:xdigit:] Возвращает набор символов Posix [:xdigit:] - шестнадцатеричных цифр '0123456789ABCDEFabcdef'. Наборы символов используются при анализе строк.
[:upper:] Возвращает набор символов Posix [:upper:], т.е. букв верхнего регистра 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. В набор включен только минимальный (латинский) алфавит, т.к. он одинаков во всех кодировках. Наборы символов используются при анализе строк.
[:lower:] Возвращает набор символов Posix [:lower:], т.е. букв нижнего регистра 'abcdefghijklmnopqrstuvwxyz'. В набор включен только минимальный (латинский) алфавит, т.к. он одинаков для всех кодировок. Наборы символов используются при анализе строк.
[:blank:] Возвращает набор символов Posix [:blank:], т.е. пустых символов (пробел и табуляция). Наборы символов используются при анализе строк.
[:space:] Возвращает набор символов Posix [:space:], т.е. пробельные символы, включая (пробел SP, HT и VT табуляцию, символы разрыва строки CR,LF,FF). Наборы символов используются при анализе строк.
[:cntrl:] Возвращает набор символов Posix [:cntrl:], т.е. управляющие символы. Это символы с кодами Chr(0)..Chr(32) и Chr(127). Символы [:blank:] и [:space:] входят в набор [:cntrl:]. Управляющие символы считаются неграфическими (не имеют начертания) и служат для управления потоком данных при использовании текстовых протоколов связи. Наборы символов используются при анализе строк.
[:alpha:] Возвращает набор символов Posix [:alpha:], т.е. всех буквенных символов, включая [:upper:] и [:lower:]. Наборы символов используются при анализе строк.
[:alnum:] Возвращает набор символов Posix [:alnum:], т.е. цифр и букв - [:digit:] и [:alpha:]. Наборы символов используются при анализе строк.
[:punct:] Возвращает набор символов Posix [:punct:], т.е. символов пунктуации '!"#$%&''()*+,-./:;<=>?@[\]^_`{|}~'. Наборы символов используются при анализе строк.
[:print:] Возвращает набор символов Posix [:print:], т.е. печатных символов, включая [:alnum:], [:punct:] и пробел. Наборы символов используются при анализе строк.
[:graph:] Возвращает набор символов Posix [:graph:], т.е. графических (имеющих начертание) символов, включая [:alnum:] и [:punct:]. Наборы символов используются при анализе строк.
[:word:] Возвращает набор символов Posix [:word:], т.е. символов для слов (идентификаторов), включая [:alnum:] и символ подчеркивания '_'. Наборы символов используются при анализе строк.
FetchUriScheme w2 Выделяет из универсального адреса URI (w2) компонент scheme. При этом декодирование pct-decode к компоненту не применяется, при необходимости (если в строке есть символ процента) делайте это самостоятельно.
Универсальные адреса ресурсов URI согласно RFC-3986 имеют формат scheme://authority/path?query#fragment.
FetchUriAuthority w2 Выделяет из универсального адреса URI (w2) компонент authority. При этом декодирование pct-decode к компоненту не применяется, при необходимости (если в строке есть символ процента) делайте это самостоятельно.
Универсальные адреса ресурсов URI согласно RFC-3986 имеют формат scheme://authority/path?query#fragment.
FetchUriPath w2 Выделяет из универсального адреса URI (w2) компонент /path. При этом декодирование pct-decode к компоненту не применяется, при необходимости (если в строке есть символ процента) делайте это самостоятельно. Обратите ввнимание, что ведущий символ-разделитель / включается в компонент /path.
Универсальные адреса ресурсов URI согласно RFC-3986 имеют формат scheme://authority/path?query#fragment.
FetchUriQuery w2 Выделяет из универсального адреса URI (w2) компонент query. При этом декодирование pct-decode к компоненту не применяется, при необходимости (если в строке есть символ процента) делайте это самостоятельно.
Универсальные адреса ресурсов URI согласно RFC-3986 имеют формат scheme://authority/path?query#fragment.
FetchUriFragment w2 Выделяет из универсального адреса URI (w2) компонент fragment. При этом декодирование pct-decode к компоненту не применяется, при необходимости (если в строке есть символ процента) делайте это самостоятельно.
Универсальные адреса ресурсов URI согласно RFC-3986 имеют формат scheme://authority/path?query#fragment.
FindServicePort w2 w3 Ищет и возвращает номер порта для сетевой службы w2 и протокола w3. Например, службе ssh соответствует порт 22. По умолчанию (если не задан) используется протокол tcp. Таблица служб берется из файла /etc/services.
ServicePortName w2 w3 w4 Ищет и возвращает имя сетевой службы для порта номер w2 и протокола w3. Например, порту 22 соответствует служба ssh. По умолчанию (если не задан) используется протокол tcp. Параметр w4 (равный 1 по умолчанию) задает номер имени (1 - основное имя, 2+ - псевдонимы). Таблица служб берется из файла /etc/services.
ServicePortInfo w2 w3 Ищет и возвращает описание сетевой службы для порта номер w2 и протокола w3. Например, порту 22 соответствует описание ssh # SSH Remote login protocol. В описании сетевой службы идет список имен (псевдонимов) службы, и комментарий ( # ... ). По умолчанию (если не задан) используется протокол tcp. Таблица служб берется из файла /etc/services.
ServicePortList Возвращает (большой) список известных сетевых служб. Таблица служб берется из файла /etc/services.
EtcServicesFile Возвращает имя файла c таблицей сетевых служб. Таблица служб обычно находится в файле /etc/services или %SystemRoot%\system32\drivers\etc\services.
ListOf
GetListOf
Функция чтения (системных) таблиц с возможностью выборки данных по значениям ключей в стиле SQL запросов.

Общий формат запроса:

ListOf TABLE [where key1=value1,[key2=value2[,…]]]

Здесь TABLE - имя таблицы, а дальше необязательный запрос, например:
s:=ParamStr('ListOf tables'); // Список доступных таблиц
s:=ParamStr('ListOf keys'); // Список ключей для выборки таблиц
s:=ParamStr('ListOf pids'); // Список всех работающих процессов
s:=ParamStr('ListOf pids where pid=4321'); // Процесс по PID
s:=ParamStr('ListOf pids where name=dns.exe'); // Процесс по имени
s:=ParamStr('ListOf colors where name=lime,format=hex'); // Код цвета по имени
s:=ParamStr('ListOf colors where code=$00ff00,format=str'); // Имя цвета по коду
            
Результат (таблица или выборка из неё) возвращается в виде текста из строк, разделенных EOL. Формат вывода зависит от таблицы. Формат при выборке из таблицы может отличаться от формата при выводе всей таблицы и зависит от ключей. Формат можно уточнить в консоли командой @paramstr listof ....

Краткое описание:
 -------------------------------------------------------------------------------
 ParamStrListOf(arg) и ParamStr('ListOf '+arg) - получить список параметров из
 запрошенной системной таблицы (например, pids - список работающих процессов).
 -------------------------------------------------------------------------------
 Вызов  Таблица     Ключи where key=value  Комментарий
 -------------------------------------------------------------------------------
 ListOf tables                             Список доступных для чтения таблиц
 ListOf keys        (name)                 Список ключей для таблицы (name)
 ListOf pids        (pid,ppid,name)        Список процессов PID,PPID,PRIO,NAME
 ListOf processes   (pid,ppid,name)        Список п-в PID,PPID,PRIO,NAME,CMDLINE
 ListOf modules     (pid)                  Список модулей для процесса PID
 ListOf windows     (pid,class,title)      Список окон на Рабочем Столе
 ListOf threads     (name)                 Список потоков Polling
 ListOf tasks       (tid,pid)              Список задач task_xxx
 ListOf colors      (name,code,format)     Список цветов с выборкой по коду/цвету
 ListOf netifs      (mode)                 Список сетевых интерфейсов
 ListOf specfolders (name)                 Список специальных именованных каталогов
 ListOf services    (name,port,protocol)   Список сетевых сервисов (портов tcp,udp)
 ListOf comports    (name,path)            Список COM-портов
 ListOf users       (name)                 Список пользователей (users)
 ListOf hosts       (name)                 Список известных компьютеров (hosts)
 ListOf groups      (name)                 Список членства в группах (groups)
 ListOf environs    (name)                 Список переменных окружения
 ListOf ftypes      (name)                 Список ftype(Windows)/mime(Unix)
 ListOf assocs      (name)                 Список assoc(Windows/Unix)
 ListOf daq_tags    (name)                 Список тегов DAQ системы
 ListOf daq_curves  (name)                 Список кривых DAQ системы
 ListOf daq_devices (name,family,model)    Список устройств DAQ системы
 ListOf daq_windows (name,type)            Список окон DAQ системы
 -------------------------------------------------------------------------------
 Например:
  s:=paramstr('ListOf pids where name=firebird');
  s:=paramstr('ListOf colors where code=$FF0000');
 -------------------------------------------------------------------------------
            
Пример работы функции:
///////////////////////////////////////////////////////////////////
// Выполнено в консоли &TESTBENCH демо-конфигурации demo_testbench:
///////////////////////////////////////////////////////////////////

// Список всех доступных таблиц
@paramstr listof tables
 specfolders,colors,comports,tables,threads,services,keys,pids,processes,tasks,windows,netifs,modules

// Список ключей для всех доступных таблиц
@paramstr listof keys
 pids keys: pid,ppid,name
 processes keys: pid,ppid,name
 modules keys: pid
 windows keys: pid,class,title,mode
 threads keys: name
 tasks keys: tid,pid
 colors keys: name,code,format
 netifs keys: mode
 specfolders keys: name
 services keys: name,port,protocol
 comports keys: name,path
 users keys: name
 hosts keys: name
 groups keys: name
 environs keys: name
 ftypes keys: name
 ftypes keys: name
 daq_tags keys: name
 daq_curves keys: name
 daq_devices keys: name,family,model
 daq_windows keys: name,type
 keys keys: name
 tables keys: name

// Процесс по имени
@paramstr listof pids where name=dns
 793241, 1, 120, dns

// COM-порт по имени
@paramstr listof comports where name=com1
 COM1  /dev/ttyUSB0  pl2303

// Имя цвета по коду
@paramstr listof colors where code=$00ff00,format=str
 Lime
            
DetectBlobImageType Функция DetectBlobImageType w2 w3 пытается определить (detect) тип изображения (image type) для блоба (blob), заданного строкой w3 в кодировке w2. Кодировка w2 может быть одной и списка b64,hex,pct,url. Предполагается, что блоб (из базы данных) передается в закодированном виде. Функция пытается по ряду признаков (например, по сигнатуре) определить, может ли этот блоб содержать изображение. Если признаки изображения обнаружены, возвращается строка, одна из списка bmp,gif,png,pbm,pgm,ppm,jpg,xpm,tif,pcx.
Например: s:=ParamStr('DetectBlobImageType b64 '+base64_encode(blob));
SysLogSeverityList Возвращает список кодов и имен уровней значимости (Severity) системного журнала (SysLog).
SysLogSeverityCode w2 Возвращает числовой код уровня значимости (Severity) системного журнала (SysLog) по имени w2.
SysLogSeverityName w2 Возвращает имя (строку идентификации) уровня значимости (Severity) системного журнала (SysLog) по его числовому коду w2.

Пример:

          s:=paramstr('0');                            {s='c:\crw32exe\crw32.exe'}
          s:=paramstr('daqconfigfile');                {s='c:\daq\triton\config\triton.cfg'}
          s:=paramstr('daqconfigpath');                {s='c:\daq\triton\config'}
          s:=paramstr('daqdatapath');                  {s='c:\daq\triton\data'}
          s:=paramstr('defaultextension x .txt');      {s='x.txt'}
          s:=paramstr('defaultextension x.bat .txt');  {s='x.bat'}
          s:=paramstr('forceextension x.bat .txt');    {s='x.txt'}
          s:=paramstr('userlist');                     {s='alex'+CRLF+... on localhost}
          s:=paramstr('userlist crwbox 1');            {s='alex Root On'+CRLF+'vinogradov User On'+CRLF+...}
          s:=paramstr('registry HKCU Software\Microsoft\Windows\CurrentVersion\Explorer\Shell+Folders Desktop');
          {s='c:\Documents and Settings\Alex\Рабочий Стол'}
          s:=ParamStr('FileSearch ping.exe Path');     {s='c:\Windows\system32.ping.exe'}
          s:=ParamStr('FileSearch gzip.exe CRW_DAQ_SYS_PATH'); {s="c:\Crw32exe\Resource\Tools\UnixUtils\add\bin\gzip.exe"}
          s:=ParamStr('FileSearch arj.exe');           {s="c:\Crw32exe\Resource\Tools\UnixUtils\add\bin\arj.exe"}
          s:=ParamStr('CreateGUID str');               {s="{6B7826DD-B281-4BA9-B32F-AF78A24DEDA6}"}
          s:=ParamStr('CreateGUID hex');               {s="DD26786B81B2A94BB32FAF78A24DEDA6"}
          s:=ParamStr('ListOf Colors where name=Lime,format=web'); {s="#00ff00"}
         

Daq Pascal Api -> readini

Определение:

function readini(arg:string):string

Аргументы:

  • arg - строка запроса.

Результат:

Возвращает переменную, прочитанную из конфигурационного файла по строке запроса arg.

Описание:

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

В случае, если строка состоит из трех слов

  • arg=config section varname
то
  • config - имя файла конфигурации
  • section - имя секции
  • varname - имя переменной

В случае, если строка состоит из двух слов

  • s=section varname
то в качестве config берется основной файл конфигурации, из которого загружена текущая DAQ-система.

В случае, если строка состоит из одного слова

  • s=varname
то в качестве config берется основной файл конфигурации, а в качестве section берется имя секции с описанием устройства, то есть [&Name], где &Name – имя устройства, которому принадлежит программа.

Функция читает строковое значение value – слово, следующее за именем переменной:

  • varname = value
Слово value преобразуется к верхнему регистру. Если за выражением varname = … следует строка из несколько слов, функция вернет только первое слово. Такой способ чтения был выбран потому, что он удобен для чтения чисел или имен, служащих для идентификации тегов, кривых или устройств. Если из конфигурации надо прочитать число, то нужно использовать преобразования val,rval, например,
x:=rval(readini(‘x’));.

Пример:

          [Device]
          s = SomeString
          x = 1.5

          s:=readini('s');       {s='SOMESTRING'}
          x:=rval(readini('x')); {x=1.5}
         

Daq Pascal Api -> readinisection

Определение:

function readinisection(txt,how:integer;cfg,sec:string):integer

Аргументы:

  • txt - ссылка на текст, см. text_new.
  • how - флаги, управляющие разбором конфигурации.
  • cfg - имя конфигурационного файла.
  • sec - имя секции.

Результат:

Читает секцию конфигурационного файла cfg в текст txt и возвращает ссылку txt.

Описание:

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

В параметре txt передается ссылка на текст, куда будет записана секция. Поскольку функция возвращает этот параметр, допустима конструкция вида

          txt:=readinisection(text_new,...);
         
в которой совмещается создание текста и чтение конфигурации.

В параметре how передается битовый флаг, влияющий на разбор конфигурации. Основные параметры такие:

  • 0 - текст передается как есть (as is), без изменений.
  • 1 = risModeUpper - текст преобразуется в верхний регистр.
  • 2 = risModeLower - текст преобразуется в нижний регистр.
  • 4 = risModeTrimL - удаление незначащих пробелов слева.
  • 8 = risModeTrimR - удаление незначащих пробелов справа.
  • 16 = risModeRemComm - удаление комментария, отделяемого точкой с запятой.
Надо заметить, что readini использует флаги risModeDefault=29=16+8+4+1.

В параметре cfg передается имя файла конфигурации. Если это имя - пустая строка, разбирается основной конфигурационный файл, что равносильно paramstr('DaqConfigFile'). Если имя не содержит расширения, применяется расширение по умолчанию .cfg. Если имя файла содержит относительный путь, считается, что путь указан относительно основного конфигурационного файла.

В параметре sec передается имя файла секции. Если это имя - пустая строка, разбирается секция данного устройства, что равносильно '['+devname+']'. Имя секции можно указывать без скобок, регистр роли не играет. Например, '[DAQ]' эквивалено 'Daq'.

Пример:

          var txt,i:integer;
          txt:=readinisection(text_new,16,'','[Daq]');
          for i:=0 to text_numln(txt)-1 do writeln(text_getln(txt,i));
          b:=text_free(txt);
         

Daq Pascal Api -> getenv expenv setenv

Определение:

function getenv(n:string):string
function expenv(e:string):string
function setenv(n,v:string):boolean

Аргументы:

  • n - имя (name) переменной окружения процесса.
  • e - выражение (expression) для подстановки переменных окружения процесса.
  • v - значение (value) переменной окружения процесса.

Результат:

getenv(n) позволяет прочитать значение переменной окружения с именем n (get environment).
expenv(e) позволяет выполнить подстановку переменных окружения в выражении e (expand environment).
setenv(n,v) позволяет записать значение v переменной окружения с именем n (set environment).

Описание:

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

Вот основные виды применения переменных окружения:

  • Переменные окружения позволяют узнать многие системные параметры операционной системы, такие как имя пользователя или имя компьютера.
  • Переменные окружения позволяют хранить общие для потоков данного процесса переменные.
  • Переменные окружения позволяют передавать параметры дочерним процессам, так как обычно дочерние процессы наследуют переменные окружения своего родительского процесса. Для передачи параметра надо вызвать setenv перед запуском дочернего процесса, (см. task_init) чтобы он унаследовал требуемые переменные окружения.

Пример:

          s:=getenv('username');                {узнать имя пользователя}
          b:=setenv('daq','c:\daq');            {задать переменную окружения daq=c:\daq}
          s:=expenv('%daq%');                   {узнать переменную daq через подстановку}
          tid:=task_init(getcomspec+' /k set'); {подготовить вызов командного процессора}
          b:=task_run(tid);                     {запустить командный процессор на выполнение}
          b:=task_free(tid);                    {процесс унаследовал переменную daq}
         

Daq Pascal Api -> getpid

Определение:

function getpid:integer

Аргументы:

  • нет.

Результат:

getpid возвращет идентификатор текущего процесса.

Описание:

Функция предназначена для получения идентификатора текущего процесса. Этот идентификатор может служить, например, для организации межпроцессного взаимодействия (IPC, inter process communication) с другими процессами. Например, идентификатор текущего процесса можно передать порожденному процессу для того, чтобы порожденный процесс по завершении обработки послал результат обработки данному процессу.

Пример:

          writeln('Current process identifier is ',getpid);
          ShowTooltip('text Demo preset stdOk btn1 Hello cmd1 "'
          +ParamStr('HomeDir')+'\Resource\Shell\send2crwdaq.exe -p '+Str(getpid)+' -c @echo Hello"');
         

Daq Pascal Api -> progname devname

Определение:

function progname:string
function devname:string

Аргументы:

Нет.

Результат:

progname позволяет прочитать имя программы.
devname позволяет прочитать имя устройства.

Описание:

progname позволяет прочитать имя программы, указанной в операторе program. Это может помочь при генерации отладочных сообщений, имен файлов или использоваться в алгоритмах анализа (например, для различения несколько похожих программ).
devname позволяет прочитать имя текущего устройства. Это может помочь при генерации отладочных сообщений, имен файлов, кодов ошибок, для генерации сообщений и т.д.

Эти функции дублируются в paramstr('DaqProgram'),paramstr('DeviceName'), но дают более короткий путь для получения указанных параметров.

Пример:

         [DeviceList]
         &testdev = device software program
         ...
         program test;
         begin
          writeln(progname); {'test'}
          writeln(devname);  {'&testdev'}
         end.
         

Daq Pascal Api -> reffind

Определение:

function reffind(arg:string):integer

Аргументы:

  • arg - строка запроса для поиска.

Результат:

Ищет объект по типу и имени, заданным в строке запроса arg.

Описание:

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

Ссылка (reference) на объект - это положительное число, по которому система CRW-DAQ идентифицирует объект. Нулевая ссылка используется как признак несуществующиего объекта или ошибки. Не надо путать ссылку с указателем, то есть с адресом памяти, где расположен объект. В Daq Pascal указателей нет и не будет, так как указатели небезопасны, а Daq Pascal задуман как язык для сценариев измерений с высокой степенью защиты. Ссылки Daq Pascal больше похожи на Handle в Windows. Объекту сопоставляется ссылка, смысл которой известен только системе. Ссылки используются только для доступа к объектам через функции Daq Pascal и не имеют какого-либо другого смысла для клиента.

Система ведет реестр всех объектов программы, сопоставляя каждому объекту некое положительное целое число, по которому к нему можно быстро получить доступ. Это и есть ссылка, возвращаемая функцией reffind. Система знает, как по ссылке найти указатель объекта для работы с ним. Сами же указатели скрыты от клиента и недоступны ему. Работать с объектами пользователь CRW-DAQ может только через ссылки и функции Daq Pascal.

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

  • Корректная ссылка есть положительное число (не ноль).
  • Корректная ссылка на любой объект, при вызове refinfo(ref,'ClassName') вернет непустую строку - имя класса объекта.
  • Корректная ссылка на тег при вызове typetag(ref) вернет положительное число (не ноль).

В настоящей версии реализованы такие поисковые запросы:

  • reffind('Tag name') - вернет ссылку на тег с именем name. Аналог findtag('name').
  • reffind('Curve name') - вернет ссылку на кривую с именем name. Аналог findcrv('name').
  • reffind('Device') - вернет ссылку на текущее устройство.
  • reffind('Device name') - вернет ссылку на устройство с именем name.
  • reffind('Window name') - вернет ссылку на окно с именем name.
  • reffind('Polling name') - вернет ссылку на поток опроса с именем name.

Пример:

          tag:=reffind('Tag Button');
          s:=refinfo(tag,'ClassName'); {'Tag'}
          s:=refinfo(tag,'Type');      {'Tag'}
          s:=refinfo(tag,'Name');      {'Button'}
          b:=isettag(tag,0);           {Использование ссылки}
          dev:=reffind('Device &Main');
          s:=refinfo(tag,'ClassName'); {'TDaqPascalDevice'}
          s:=refinfo(tag,'Type');      {'Device'}
          s:=refinfo(tag,'Name');      {'&Main'}
          r:=devsend(dev,'Hello');     {Использование ссылки}
         

Daq Pascal Api -> refinfo

Определение:

function refinfo(ref:real;arg:string):string

Аргументы:

  • ref - ссылка на интересующий объект.
  • arg - строка запроса.

Результат:

Возвращает для объекта ref свойства, заданные строкой запроса arg.

Описание:

Возвращает для объекта ref свойства, заданные строкой запроса arg. Ссылку ref на объект можно узнать по его типу и имени вызовом reffind. Ссылка передается как вещественное число, так как ссылки кривых и таймеров были когда-то для старых систем сделаны вещественными. Строка запроса arg задает свойство, которое пользователь хочет узнать. В настоящее время доступны такие запросы:
  • ClassName - возвращает имя класса ref. Пустая строка результата служит признаком того, что ссылка ref неверна. Непустая строка результата дает:
    • Если ref - ссылка на объект, возвращает имя класса объекта. Имя класса объекта понимается в смысле класса Delphi. Например, кривая будет иметь имя класса TCurve.
    • Если ref - ссылка на тег, возвращает строку Tag.
    • Если ref - ссылка на задачу, возвращает строку Task.


  • Type - возвращает тип объекта DAQ системы. Пустая строка служит признаком того, что ссылка не указывает на какой-либо объект DAQ системы. Отличие от ClassName состоит в том, что:
    • Далеко не все объекты CRW являются объектами DAQ системы. Поэтому возможно, что ClassName вернет непустую строку, а Type - нет.
    • Объекты DAQ системы на самом деле являются семействами классов Delphi. Поэтому возможно, что различные значения ClassName будут соответствовать одному значению Type.
    Может возвращать значения:
    • Device - если ref ссылается на устройство DAQ.
    • Polling - если ref ссылается на поток опроса DAQ.
    • Curve - если ref ссылается на кривую.
    • Window - если ref ссылается на окно.
    • Timer - если ref ссылается на таймер, см. tm_new.
    • Text - если ref ссылается на текст, см. text_new.
    • Task - если ref ссылается на задачу, см. task_init.
    • RegExp - если ref ссылается на регулярное выражение, см. regexp_init.
    • Pipe - если ref ссылается на именованный канал, см. pipe_init.
    • Tcp - если ref ссылается на канал TCP, см. pipe_init.
    • Com - если ref ссылается на COM порт, см. pipe_init.
    • HashList - если ref ссылается на хеш-таблицу, см. hashlist_init.
    • RegExp - мастер обработки регулярных выражений (TRegExpMaster), см. regexp_init.
    • Fsm - если ref ссылается на элемент Конечного Автомата, см. fsm_new.
    • DB - если ref ссылается на элемент Базы Данных, см. db_connection.
    • Tag - если ref ссылается на тег.


  • Name - возвращает имя объекта DAQ системы или тега.
    Имена имеют объекты Device, Polling, Curve, Window и Tag.
    В случае тегов результат аналогичен вызову nametag(ref).

  • Bounds - возвращает координаты (Left Top Right Bottom) окна DAQ системы (или пустую строку).
    Вызов RefInfo(ref,'Bounds') возвращает координаты окна со ссылкой ref, которую можно получить по имени (заголовку) окна вызовом ref:=RefFind('Window '+WindowName).
    Координаты окна задаются относительно клиентской области экрана, см. ParamStr('AppClientBounds').

Пример:

Смотри reffind.

Daq Pascal Api -> enumerate

Определение:

function enumerate(ref:integer;arg:string):integer

Аргументы:

  • ref - ссылка на текст.
  • arg - строка запроса.

Результат:

Перечислитель объектов, возвращает в списке ref имена объектов, имеющих тип, заданный строкой запроса arg.

Описание:

Перечислитель объектов, возвращает в списке со ссылкой ref имена объектов, имеющих тип, заданный строкой запроса arg. Как результат возвращает исходную ссылку ref на объект - текст со списком. Строка запроса arg задает тип объектов, которые пользователь хочет перечислить. В настоящее время доступны такие запросы:
  • Tag - тег.
  • Task - задача. В этом случае возвращается номер задачи tid, так как имен у задач нет.
  • Curve - кривая DAQ. Кривые, не входящие в DAQ, не перечисляются.
  • Device - устройство DAQ.
  • Polling - поток опроса DAQ.
  • Window - окно DAQ любого типа. Также можно нумеровать окна только типа Circuit_Window, Curve_Window, Tab_Window, Spectr_Window. Окна, не входящие в DAQ, не перечисляются.

Пример:

          {
          Enumerate tags,devices, curves, etc...
          }
          procedure EnumerateAll;
          var t,i:integer; b:Boolean;
           procedure EnumerateType(What:string);
           begin
            b:=text_addln(t,'--- '+What+' --- list:');
            t:=enumerate(t,What);
           end;
          begin
           t:=text_new;
           EnumerateType('Tag');
           EnumerateType('Task');
           EnumerateType('Curve');
           EnumerateType('Device');
           EnumerateType('Polling');
           EnumerateType('Window');
           EnumerateType('Tab_Window');
           EnumerateType('Curve_Window');
           EnumerateType('Spectr_Window');
           EnumerateType('Circuit_Window');
           for i:=0 to text_numln(t)-1 do writeln(text_getln(t,i));
           b:=text_free(t);
          end;
         

Daq Pascal Api -> echo debugout reset rewrite append read readln write writeln ioresult eof eoln

Определение:

function echo(msg:String):Boolean
function debugout(msg:String):Boolean
function reset(fname:String):Integer
function rewrite(fname:String):Integer
function append(fname:String):Integer
procedure read([var1,..varN])
procedure readln([var1,..varN])
procedure write([out1,..outN])
procedure writeln([out1,..outN])
function ioresult:Integer
function eof:Boolean
function eoln:Boolean

Аргументы:

  • msg - сообщение для вывода на экран.
  • fname - имя файла.
  • var1,..varN - список переменных для чтения.
  • out1,..outN - список переменных и выражений для записи.

Результат:

Группа функций виртуальной консоли:
echo - выводит сообщение в Главную консоль.
debugout - выводит сообщение в файл отладки, обычно в Temp\Debug.Out.
Имя файла отладки задано в crw32.ini [System] DebugFile.
reset - открывает файл виртуальной консоли для ввода.
rewrite - создает файл виртуальной консоли для вывода.
append - открывает файл виртуальной консоли для вывода.
read/readln - чтение виртуальной консоли ввода.
write/writeln - запись в виртуальную консоли вывода.
ioresult - возвращает статус ошибки консольного ввода-вывода.
eof - возвращает статус "конец файла" (end of file).
eoln - возвращает статус "конец строки" (end of line).

Описание:

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

Виртуальная консоль - это как бы два текстовых файла, один для чтения (консоль стандартного ввода - StdIn), другой для записи (консоль стандартного вывода - StdOut). Оговорка "как бы" не случайна - файл консоли может быть связан как с регулярным файлом (то есть дисковым или сетевым файлом операционной системы), так и с виртуальным файлом - буфером FIFO, который на самом деле файлом не является, а есть просто буфер в памяти, через который производится ввод данных, введенных в консольном окне или прием сообщений, посланных вызовом devsend или devpost, а также вывод в консольное окно. Оговорка "текстовых" тоже не случайна - все функции работы с консолью ориентированы на текстовую информацию, с разделением на строки, между которыми стоит разделитель crlf. Например, при чтении чисел предполагается, что числа представлены в символьном виде, в десятичной системе счисления.

Другой отличительной чертой консоли является то, что это файл с последовательным (а не произвольным) доступом. В сущности, консоль реализует дисциплину FIFO.

Наличие виртуального файла FIFO делает консоль одним из важнейших средств Daq Pascal для обмена сообщениями между разными программами Daq Pascal. Не забывайте, все программы Daq Pascal выполняются в отдельных программных потоках и нуждаются в механизме обмена данными. Консоль как раз и реализует очередь сообщений для обмена данными между Daq-программами.

Надо отметить, что в DAQ Pascal вообще поддерживается только один текстовый файл для ввода и один текстовый файл для вывода, то есть виртуальная консоль. Не следует считать это серьезным ограничением, так как программ в конфигурации может быть сколько угодно, каждая программа может заниматься своим файлом. Кроме того, файлы можно открывать и закрывать, если надо читать или записывать несколько файлов. Вообще, долго держать несколько открытых дисковых файлов одновременно - плохая практика. Например, при сбое питания данные в открытых файлах могут пропасть. Поэтому при необходимости записи порции данных в дисковый файл лучше открыть его, записать порцию данных и немедленно закрыть. В таком стиле можно работать с многими файлами, несмотря на то, что консоль всего одна.

echo(msg) выводит сообщение msg в окно Главная консоль.
Вывод буферизован через FIFO, поэтому управление возвращается немедленно, без задержек. Реальное отображение сообщения происходит асинхронно, по таймеру, в другом потоке программы. То есть вызов echo не помешает высокоприоритетному измерительному потоку продолжать выполнение. Функция вернет False, если буфер FIFO переполнен и не может принять сообщение.
Аналогичный результат будет, если открыть виртуальный консольный файл с именем CON: при помощи append('CON:'). В этом случае консольный вывод направляется в окно Главная консоль.

reset(fname) открывает файл консольного ввода.
Функция возвращает ноль при успехе или ненулевой код ошибки.
Функция закрывает консольный файл ввода, если он был открыт, и открывает для ввода виртуальный файл с именем fname.

  • В качестве имени файла можно указать имя регулярного файла, например, локального дискового файла (типа d:\demo\readme.txt), или имя сетевого файла (типа \\crwbox\public\readme.txt). В этом случае чтение будет происходить из указанного файла стандартными средствами операционной системы.
  • В качестве имени файла можно указать пустую строку. Пустая строка соответствует буферу FIFO для ввода. Ввод в этом случае осуществляется не из файла, а из буфера в памяти.
    Данные могут попасть в FIFO консоли ввода двумя путями:
    1. При вводе данных клавиатуры в консольном окне устройства.
    2. При посылке устройству сообщения devsend или devpost или devmsg. Это дает возможность использовать консоль ввода как очередь сообщений для взаимодействия разных программами Daq Pascal. Ведь все программы Daq Pascal выполняются в разных программных потоках и нуждаются в механизме обмена данными.
      Размер буфера FIFO задается в переменной StdInFifo в секции описания устройства.
      StdinFifo=n - задает размер nкилобайтах) буфера FIFO стандартной консоли ввода для данного устройства. Стандартный размер по умолчанию StdinFifo=16. Размер StdInFifo можно уменьшить для экономии памяти, если данное устройство не использует консоль ввода, то есть никогда не делает чтения консоли вызовом read или readln. Размер StdInFifo можно увеличить, если программа интенсивно использует консоль ввода, например, если другие программы присылают данной программе сообщения через devsend или devpost или devmsg, а программа читает консоль вызовом read или readln.
По умолчанию при старте программы консоль ввода связана с FIFO, поэтому обычно открывать ее специально не надо. При работе с регулярными файлами обычно открывается файл, делается чтение данных, а потом снова открывается файл FIFO вызовом reset('') (регулярный файл при этом закрывается). Вообще, поскольку функции закрытия консольного файла не существует, всегда открыт либо файл, либо FIFO. То есть консоль никогда не "болтается в воздухе", она всегда доступна для чтения.

rewrite(fname) создает файл консольного вывода.
append(fname) открывает файл консольного вывода.
Функции возвращают ноль при успехе или ненулевой код ошибки.
Функции закрывают консольный файл вывода, если он был открыт, и открывают для вывода виртуальный файл с именем fname. Различие функций в том, что rewrite всегда создает новый файл, уничтожая существующий (или очищая FIFO), в то время как append открывает существующий файл для записи в конец этого файла (или сохраняет содержимое FIFO).

  • В качестве имени файла можно указать имя регулярного файла, например, локального дискового файла (типа d:\demo\readme.txt), или имя сетевого файла (типа \\crwbox\public\readme.txt). В этом случае запись будет происходить в указанный файл стандартными средствами операционной системы.
  • В качестве имени файла можно указать пустую строку. Пустая строка соответствует буферу FIFO для вывода. Вывод в этом случае осуществляется не в файл, а в буфер в памяти. Буфер затем отображается в консольном окне (если оно открыто).
    Размер буфера FIFO задается в переменной StdOutFifo в секции описания устройства.
    StdoutFifo=n - задает размер nкилобайтах) буфера FIFO стандартной консоли вывода для данного устройства. Стандартный размер по умолчанию StdoutFifo=32. Размер StdoutFifo можно уменьшить для экономии памяти, если данное устройство не использует консоль вывода, то есть никогда не делает записи консоли вызовом write или writeln. Размер StdOutFifo можно увеличить, если программа интенсивно использует консоль вывода, например, для отображения отладочных сообщений в консоль вызовом write или writeln.
  • В качестве имени файла можно указать 'CON:'. Тогда вывод будет делаться в окно Главная Консоль.
По умолчанию при старте программы консоль вывода связана с FIFO, поэтому обычно открывать ее специально не надо. При работе с регулярными файлами обычно открывается файл, делается запись данных, а потом снова открывается файл FIFO вызовом append('') (регулярный файл при этом закрывается). Вообще, поскольку функции закрытия консольного файла не существует, всегда открыт либо файл, либо FIFO. То есть консоль никогда не "болтается в воздухе", она всегда доступна для записи.

read(var1,..varN) - чтение переменных var1..varN без учета crlf.
readln(var1,..varN) - чтение переменных var1..varN с учетом crlf.
Обе процедуры делают чтение переменных из текстовых файлов, отличие в том, что read читает переменные до конца файла, игнорируя разделители crlf, а readln читает до конца строки, то есть до появления crlf. Обычно текст разбит на строки и используется readln (см. также eoln).
Переменные var1..varN могут иметь тип char, integer,real,string.
Надо заметить, что числовые переменные считываются не как двоичные данные, а как данные в символьном представлении, в десятичной системе счисления. Эта особенность связана с тем, что консоль - текстовый файл, там все данные предполагаются символьными. Если надо передавать двоичные данные, можно воспользоваться функциями кодирования, например, dump, mime_encode/decode, hex_encode/decode.

write(out1,..outN) - запись выражений out1..outN без завершающего crlf.
writeln(out1,..outN) - запись выражений out1..outN с завершающим crlf.
Обе процедуры делают запись списка выражений в текстовый файл, отличие в том, что write не записывает в конце разделитель строки crlf, а writeln записывает. Обычно текст разбит на строки и используется writeln.
Выражения out1..outN могут иметь тип char, integer,real,string. Это могут быть константы, переменные, результаты функций или формульных выражений. Каждый элемент списка может также сопровождаться спецификатором формата:

          out:w
           или
          out:w:d
          out - выводимое выражение
          w   - минимальная ширина поля вывода
          d   - число цифр после запятой (для вещественных чисел)
          например:
          writeln('Значение pi=',pi:7:4); { Значение pi= 3.1459 }
         

Надо заметить, что числовые данные записываются не как двоичные данные, а как данные в символьном представлении, в десятичной системе счисления. Эта особенность связана с тем, что консоль - текстовый файл, там все данные предполагаются символьными. Если надо передавать двоичные данные, можно воспользоваться функциями кодирования, например, dump, mime_encode/decode, hex_encode/decode.

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

  • В случае регулярного файла ошибка ввода-вывода может возникать из-за сбоя дисковой системы или сетевого обмена.
  • В случае FIFO ошибка ввода-вывода может возникать из-за переполнения буфера FIFO. Размеры буферов задаются переменными StdInFifo, StdOutFifo при описании устройства.
  • В обоих случаях ошибка ввода может возникать при попытке чтения файла при достижении конца файла (см. eof). В случае FIFO концом файла считается пустой буфер ввода.
  • В обоих случаях ошибка ввода может возникать при попытке чтения данных в неверном формате (читается число, а в файле записаны нечисловые данные).

eof - возвращает и признак "конец файла" ("end of file") консоли ввода.
В случае FIFO концом файла считается пустой буфер ввода. Поскольку данные в буфере могут появиться как с клавиатуры, так и через сообщения devmsg, состояние eof может в любой момент измениться. Поскольку чтение файла при достижении конца файла считается ошибкой, при чтении данных из консоли всегда надо проверять eof. Обычно цикл чтения данных из консоли выглядит примерно так:

          while (ioresult=0) and not eof do begin
           readln(s);
           ... do something with s ...
          end;
         

eoln - возвращает и признак "конец строки" ("end of line") консоли ввода.
Конец строки - это появление во входном потоке разделителя строк CrLf.
eoln применяется при чтении данных c помощью read, чтобы фиксировать достижение конца строки. Это бывает нужно в случае, если число читаемых переменных в строке неизвестно, например:

          while not eoln do begin
           read(data[i]);
           i:=i+1;
          end;
         
При чтении данных c помощью readln конец строки учитывается автоматически, однако при этом число переменных в строке должно быть известно.

Последнее замечание: если работа с регулярными файлами может вызвать при операциях ввода-вывода блокировку потока, то есть приостановку выполнения потока на некоторое время, при работе с FIFO все вызовы - не блокирующие, то есть программа всегда немедленно получает управление после выполнения ввода-вывода. Это связано с тем, что ввод-вывод в FIFO - это просто пересылка данных в памяти, которая выполняется быстро или не выполняется вообще. Кстати, при необходимости записи данных в регулярный файл из критичного по времени потока лучше всего создать программу - сервер, которая будет принимать от клиента сообщения в консоль ввода и записывать их в файл через консоль вывода. Таким образом, при правильной организации работа с консолью в системе CRW-DAQ никак не противоречит принципам организации RealTime систем.

Пример:

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

         [DeviceList]
         &client = device software program
         &server = device software program
         [&server]
         StdInFifo = 64  ; сделаем буфер ввода побольше...
         StdOutFifo = 64 ; сделаем буфер вывода побольше...
         ...
         program client;
         var b:boolean; s:string;
         begin
          s:=str(runcount);             {формирование сообщения серверу}
          b:=devmsg('&server '+s+CrLf); {посылка сообщения серверу}
          writeln(s);                   {вывод в консольное окно}
         end.
         ...
         program server;
         var b:boolean; i:integer; s:string;
         begin
          {Start actions}
          if runcount=1 then begin
           b:=echo('Hello, user '+ParamStr('UserName')); {вывод в Главную Консоль}
           writeln('Hello, user '+ParamStr('UserName')); {вывод в локальную консоль}
          end else
          {Stop actions}
          if isinf(runcount) then begin
           b:=echo('Goodbye, user '+ParamStr('UserName')); {вывод в Главную Консоль}
           writeln('Goodbye, user '+ParamStr('UserName')); {вывод в локальную консоль}
          end else
          {Polling}
          begin
           if ioresult<>0                                {проверить статус ввода-вывода}
           then b:=echo('I/O error found!');             {сообщить об ошибке в Главную Консоль}
           while not eof do begin                        {пока есть данные в FIFO ввода}
            readln(s);                                   {прочитать строку из FIFO ввода}
            if rewrite('c:\server.log')=0                {открыть файл для вывода}
            then writeln(s);                             {вывод в файл}
            if append('')                                {закрыть файл=открыть FIFO}
            then writeln(s);                             {вывод в консольное окно}
           end;
          end;
         end.
         

Daq Pascal Api -> f_reset f_rewrite f_read f_write f_size f_seek f_close

Определение:

function f_reset(fname:String;mode:Integer):Integer
function f_rewrite(fname:String;mode:Integer):Integer
function f_read(n:Integer):String
function f_write(data:String):Integer
function f_size:Real
function f_seek(pos:Real):Real
function f_close:Boolean

Аргументы:

  • fname - имя файла.
  • mode - режим открытия файла.
  • n - число байт при чтении.
  • data - данные для записи.
  • pos - позиция в файле.

Результат:

Функции манипуляции с двоичными файлами.

Описание:

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

f_reset(fname,mode) - функция открытия существующего файла. Закрывает файл, если он был открыт. Открывает существующий файл с именем fname, в режиме mode (0-ReadOnly,1-WriteOnly,2-Read/Write). Возвращает 0 при успехе или ненулевой код ошибки, как IOResult.

f_rewrite(fname,mode) - функция создания файла. Закрывает файл, если он был открыт. Создает новый файл именем fname, в режиме mode (0-ReadOnly,1-WriteOnly,2-Read/Write). Если файл существовал, он уничтожается перед созданием нового файла. Возвращает 0 при успехе или код ошибки, как IOResult.

f_read(n) - функция чтения файла. Читает из файла n байт и возвращает прочитанное в виде строки дампа (двоичных данных). Длина строки результата равна числу прочитанных байтов. Возвращает статус через функцию IOResult. Для анализа прочитанных данных можно использовать функции dump2i и т.д.

f_write(data) - функция записи файла. Записывает данные, заданные строкой дампа data в файл. Возвращает число записанных байтов. Возвращает статус через функцию IOResult. Двоичные данные для записи можно получить функцией dump.

f_size - размер файла. Возвращает текущий размер открытого файла или -1 при ошибке. Возвращает статус через функцию IOResult.

f_seek(pos) - функция позиционирования файла. Позиционирует указатель открытого файла в положение pos, если p >=0 , или -1 при ошибке. При позиционировании 0 соответствует началу файла, f_size - концу. Возвращает позицию указателя после выполнения операции. Вызов f_seek(-1) просто вернет текущую позицию указателя. Возвращает статус через функцию IOResult.

f_close - закрывает файл, если он был открыт. Файл также автоматически закрывается при остановке программы или при открытии другого файла. Возвращает статус через функцию IOResult.

Пример:

          program Example;
          var x,y:real; i,io:integer; fname,s:string;
          begin
           fname:='c:\test.bin';
           if fileexists(fname)
           then io:=f_reset(fname,2)
           else io:=f_rewrite(fname,2);
           if io=0 then begin
            s:=dimp(i)+dump(x)+dump(y);
            if f_write(s) <> length(s) then writeln('Error ',IoResult);
            writeln('File size ',f_size);
            if f_seek(0)<>0 then then writeln('Error ',IoResult);
            s:=f_read(4+8+8);
            if length(s)=20 then begin
             i:=dump2i(Copy(s,1,4));
             x:=dump2i(Copy(s,5,8));
             i:=dump2i(Copy(s,13,8));
            end;
           end else begin
            writeln('Error ',io);
            b:=f_close;
           end;
          end.
         

Daq Pascal Api -> readfiletobuffer writebuffertofile getfileproperties

Определение:

function readfiletobuffer(filename:String;count,offset:Integer):String
function writebuffertofile(filename,buffer:String;offset:Integer):Integer
function getfileproperties(filename,properties:String):String

Аргументы:

  • filename - имя файла для чтения/записи.
  • buffer - буфер данных для записи в файл.
  • count - число байтов для чтения/записи.
  • offset - смещение от начала файла.
  • properties - список интересующих свойств файла.

Результат:

Функции чтения/записи в/из файл(а), а также получение различных свойств файла.

Описание:

Это функции атомарного (в смысле одной операцией) чтения файла в буфер памяти, либо записи буфера памяти в файл. Буфер памяти - это (большая) строка, которая считается просто массивом байтов (или символов). Файл читается как двоичный (просто массив байтов) и копируется в буфер без какой-либо обработки. Предполагается, что данные считываются из файла большим блоком (например, все сразу), а затем обрабатываются (приклодной программой) уже в буфере памяти. При работе с большими файлами надо иметь в виду ограничения на длину строки slenmax. Если файлы совсем большие, обработку можно вести блоками со смещением.

ReadFileToBuffer(filename,count,offset) - функция чтения (двоичного) файла в буфер.
Параметр filename задает имя файла для чтения.
Параметр count задает счетчик байтов - т.е. максимальное число байтов для чтения, при этом значение 0 означает, что размер файла определяется автоматически, т.е. чтение идет до обнаружения конца файла. Это удобно, когда размер файла заранее неизвестен.
Параметр offset задает смещение от начала файла в байтах. Нулевое смещение соответствует началу файла.
Для чтения ВСЕГО файла можно применять вызов ReadFileToBuffer(filename,0,0).
При этом желательно предварительно проверять размер файла.
При успешном чтении функция возвращает (двоичную) строку данных из файла. Длина строки может быть меньше запрошенной, если был достигнут конец файла. При ошибке доступа или чтения возвращается пустая строка.

WriteBufferToFile(filename,buffer,offset) - функция записи буфера в файл. Создает файл, если он не существовал. Записывает буфер в файл с заданным смещением. Возвращает число записанных байтов или ноль (или -1) при ошибке.
Параметр filename задает имя файла для записи.
Параметр buffer задает буфер (двоичных) данных для записи.
Параметр offset задает смещение от начала файла в байтах. Нулевое смещение соответствует началу файла.

GetFileProperties(filename,properties) - функция чтения (списка) свойств файла.
Свойства возвращаются в виде куки-списка key=value, разделенного EOL.
Свойства извлекаются по именам функцией CookieScan.
Параметр filename задает имя файла для чтения свойств.
Параметр properties задает CSV список ключей (имен) для интересующих свойств файла.
Это могут быть ключи:
  • Exists - флаг (1/0) "файл существует".
  • Permissions - разрешения доступа - строка вида -rwxr--r--, как принято в Unix.
  • Owners - владельцы файла - строка вида user:group uid:gid, как принято в Unix.
    Первое слово - владельцы в виде имен, второе - в виде номеров (используйте ExtractWord).
    Например, alex:users 1001:500.
  • IsExecutable - флаг (1/0) "файл исполняемый".
  • IsSymLink - флаг (1/0) "файл символической ссылки".
  • IsHardLink - флаг (1/0) "файл жесткой ссылки".
  • IsReadable - флаг (1/0) "файл доступен для чтения".
  • IsWritable - флаг (1/0) "файл доступен для записи".
  • Size - размер файла в байтах.
  • Attr - атрибуты файла (целое число).
  • Time - время обновления файла в миллисекундах от начала Новой Эры.
  • FileExt - расширение имени файла (как он был передан).
  • DirName - каталог файла (как он был передан).
  • BaseName - базовое имя файла (без каталога и расширения).
  • FileName - унифицированное (обработанное) имя файла.
  • RealPath - реальное имя файла на диске (с учетом регистра и симлинков).
В случае передачи пустого параметра properties возвращается список всех перечисленных свойств.
Извлечение свойств делается в стиле: size:=CookieScan(GetFileProperties(filename,''),'Size',0).
Свойства возвращаются в строковом виде, при желании сами преобразуйте их в числа.

Пример:

  procedure TestBufferRW;
  var fname,buff:string;
  begin
   fname:=''; buff:='';
   writeln('Test ReadFileToBuffer/WriteBufferToFile:');
   fname:=AdaptFileName(ExpEnv('$CRW_DAQ_SYS_TMP_DIR/mymodules.txt'));
   if FileExists(fname) then bNul(FileErase(fname));
   buff:=ParamStr('ListOf modules');
   //writeln(buff);
   if WriteBufferToFile(fname,buff,0)>0
   then writeln('Luck write '+fname)
   else writeln('Fail write '+fname);
   if fileexists(fname) then begin
    buff:=ReadFileToBuffer(fname,0,0); // Read all file
    if (buff<>'')
    then writeln('File readback:',EOL,buff)
    else writeln('Error read file '+fname);
    writeln('File properties:',EOL,GetFileProperties(fname,''));
   end;
   fname:=''; buff:='';
  end;
         

Daq Pascal Api -> devsend devpost

Определение:

function devsend(ref:integer;msg:string):real
function devpost(ref:integer;msg:string):real

Аргументы:

  • ref - ссылка на объект - устройство (а также для devsend(ref,'') - поток, канал, порт).
  • msg - строка сообщения.

Результат:

devsend посылает сообщение устройству ref, заданное строкой msg, с синхронизацией.
devpost посылает сообщение устройству ref, заданное строкой msg, без синхронизации.
Обе функции возвращают длину посланного сообщения или 0 при ошибке.

Описание:

Функции devsend и devpost посылают сообщение устройству со ссылкой ref, заданное строкой msg. Функции отличаются только синхронизацией - devsend при посылке сообщения "пробуждает" устройство-приемник, в то время как devpost - только передает устройству-приемнику сообщение, не выполняя его синхронизацию (пробуждение). Нулевой результат при непустой строке сообщения - признак ошибки. Ссылку ref на устройство по имени name можно узнать вызовом reffind('Device name').

Действие сообщения и возвращаемый результат зависит от типа устройства - приемника. В настоящее время:

  • Для объектов Device Software Program сообщения помещаются в буфер FIFO стандартной консоли ввода. Функция возвращает число символов, помещенных в буфер или ноль, если буфер устройства - приемника переполнен. Программа - приемник может читать сообщения вызовом readln. При этом надо не забывать вставлять CrLf для разделения сообщений. Кроме того, поскольку консоль readln является текстовым каналом связи, при пересылке двоичных данных рекомендуется использовать mime_encode/mime_decode.
  • Для объектов Device Software ConstGenerator и Device Software BitSetGenerator:
    • ConstName=name - задает имя константы name.
    • ConstValue=val - задает значение константы val.
  • Для объектов Device Software Dialog:
    • Confirm=n - команда 1=Ok, 2=Cancel.

Надо также иметь в виду, что посылка сообщения devsend устройству - приемнику приводит к его досрочному пробуждению, то есть к активизации потока устройства, даже если время опроса по таймеру еще не пришло. Можно даже посылать пустое сообщение devsend, чтобы только "разбудить" устройство - приемник, не посылая ему данных. После посылки сообщения devsend функция awakeflag устройства-приемника вернет true при очередном опросе. Это признак того, что устройство-приемник было "разбужено" досрочно посылкой сообщения devsend.

Выбор функции devsend или devpost зависит от задачи. Если после посылки сообщения требуется немедленная обработка со стороны приемника, то следует использовать синхронное сообщение devsend, чтобы досрочно пробудить устройство-приемник для выполнения обработки сообщения. Однако при большой частоте сообщений есть риск создания большой нагрузки для процессора из-за частого переключения контекста потоков. Если сообщения передается часто, содержат много данных, а время задержки их обработки приемником не критично, то лучше использовать асинхронные сообщения devpost. В этом случае сообщения будут записываться в очередь приемника без его досрочного пробуждения, что снижает частоту переключения потоков и уменьшает нагрузку на процессор. Приемник будет работать в привычном для него темпе, заданном таймером. При этом задержка между посылкой сообщения и его обработкой будет определяться периодом опроса устройства-приемника.

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

Длина посылаемого сообщения может быть любой, допустимой типом string. Однако рекомендуется посылать сообщения, в которых строки, разделенные переводом строки CrLf, не длиннее 200-250 символов, так как многие функции обработки строк ориентированы на строки длиной не более 255 символов.

Функция devsend в случае пустой строки данных принимает также ссылки на поток (polling), задачу (task), канал (pipe), которые создаются вызовом pipe_init или возвращаются вызовом reffind. В этом случае поведение функции следующее:

  • Для устройства Device поведение обычное - передача сообщения и пробуждение потока устройства.
  • Для потока Polling и канала Pipe функция devSend выполняется только в случае передачи пустой строки данных, т.е. msg=''. При этом devSend(ref,'') выполняет досрочное пробуждение потока, связанного с объектом и возвращает 0. Для канала любого типа ссылка создается функцией pipe_init, для потоков - возвращается вызовом RefFind('Polling name'). Досрочное пробуждение потока, связанного с каналом, позволяет ускорить процесс обмена данными через канал.


Таким образом, вызов devSend(ref,'') служит общим способом досрочного пробуждения потока для устройства, задачи, потока, канала.

Пример:

          {Server send message:}
          dev:=reffind('Device &Client');
          if devsend(dev,'Hello'+CrLf)=0 then writeln('Fifo overflow');

          {Client handle messages:}
          while not eof do begin
           readln(s);
           if IoResult=0
           then writeln('Server talk: ',s)
           else writeln('Error');
          end;

          ref:=RefFind('Polling System.Uart');  // Получить ссылку на поток драйвера COM-порта
          r:=devSend(ref,'');                   // Досрочно пробудить этот поток
         

Daq Pascal Api -> devmsg devsendmsg devpostmsg

Определение:

function devmsg(arg:string):real
function devsendmsg(arg:string):real
function devpostmsg(arg:string):real

Аргументы:

  • arg - строка сообщения dev msg с именем устройства dev и текстом сообщения msg.

Результат:

Посылает сообщение устройству, заданное строкой arg.
devmsg - устаревший синоним devsendmsg, сохранен для совместимости.
devsendmsg - аналог devsend с заданием имени устройства вместо ссылки.
devpostmsg - аналог devpost с заданием имени устройства вместо ссылки.

Описание:

Функции devmsg, devsendmsg, devpostmsg посылают сообщение устройству, заданное строкой arg. Они отличаются методом синхронизации - devmsg, devsendmsg синхронизируют ("пробуждают") устройство-приемник, а devpostmsg синхронизацию не выполняет. Строка arg имеет вид arg='dev msg', где dev - имя устройства, msg текст сообщения. Функции можно считать аналогом вызовов
          devmsg     = devsend(reffind('Device dev'),'msg')
          devsendmsg = devsend(reffind('Device dev'),'msg')
          devpostmsg = devpost(reffind('Device dev'),'msg')
         
где вместо ссылки устройства используется его имя.

Есть отличия от devsend и devpost:

  • Вызов devsend и devpost быстрее, так как устройство там идентифицируется ссылкой, а не именем.
  • devsend, devpost могут также работать с каналами и потоками.
  • devmsg, devsendmsg, devpostmsg имеет ограничение на длину строки length(arg)<=255. При этом devsend и devpost не имеют такого ограничения.

Таким образом, devmsg, devsendmsg, devpostmsg удобно применять в случае редких сообщений небольшого размера. При большом потоке сообщений devsend и devpost предпочтительнее.

Пример:

          {Server send message:}
          if devmsg('&Client Hello'+CrLf)=0 then writeln('Fifo overflow');

          {Client handle messages:}
          while not eof do begin
           readln(s);
           if IoResult=0
           then writeln('Server talk: ',s)
           else writeln('Error');
          end;
         

Daq Pascal Api -> syslognotable syslognote

Определение:

function syslognotable(severity:Integer):Boolean
function syslognote(severity:Integer; data:String):Integer

Аргументы:

  • severity - уровень значимости события.
  • data - данные для записи события.

Результат:

Функции записи событий в системный журнал syslog.

Описание:

Это базовые функции для записи событий в системный журнал syslog.
Имеется в виду системный журнал пакета crwdaq, а не системный журнал linux/syslog операционной системы.

Примечание: В прикладном коде рекомендуется использовать функции-"обертки" из стандарной библиотеки, такие как trouble, problem, success. При прямом использовании будьте внимательны к выбору уровня значимости severity, т.к. неверный выбор этого параметра приводит либо к игнорированию события, либо к "спаму", т.е. избыточному "информационному шуму", который будет только мешать работе.

Ещё прочитайте про системный журнал syslog.

SysLogNotable(severity) - функция проверки значимости события severity. У системного журнала syslog есть текущий уровень срабатывания TriggerLevel, который задается командой @syslog -t … в окне Главная Консоль, например, @syslog -t INFO/NOTIFY. События с уровнем значимости ниже порога игнорируются и не попадают в журнал. Это нужно для снижения нагрузки на систему в штатном режиме работы - лишние отладочные сообщения, нужные лишь в процессе разработки, в нормалном режиме игнорируются, т.к. они (больше) не нужны.
Параметр severity задает уровень значимости в диапазоне 0..50.
Функция возвращает флаг "событие значимое", то есть может записываться в журнал. Предварительная проверка уровня значимости позволяет исключить ненужные операции записи в журнал, т.к. незначимые события все равно не будут записаны. Например:
if SysLogNotable(sev_info) then n:=SysLogNote(sev_info,'Это демонстрационное событие.');
Рекомендуется всегда проверять уровень значимости событий перед записью, чтобы исключить ненужную работу программы.

SysLogNote(severity,data) - функция записи (Note) события с уровнем значимости severity, заданного строкой data, в системный журнал syslog. Событие записывается в системный журнал, только если уровень значимости severity достаточен (не ниже порога). Это условие проверяется вызовом SysLogNotable.
Параметр severity задает уровень значимости записываемого события в диапазоне 0..50.
Параметр data задает строку данных для записи.
Строка данных - это содержимое события с описанием "что случилось".
Функция возвращает длину записанного в журнал сообщения (включая метку времени и другие данные). В случае ошибки формата строки данных или незначимого уровня события (ниже порога) возвращается ноль, а событие игнорируется.
При записи события фиксируется текущее время в качестве метки времени. Поэтому запись в журнальный файл надо делать сразу, когда произошло событие, чтобы не нарушать хронометраж.

Для удобства работы с журналом для severity определены такие константы:
  • sev_DEBUG = 0 - базовый уровень для отладочных сообщений.
  • sev_Remark - отладочная пометка.
  • sev_Comment - отладочный комментарий.
  • sev_Notice - отладочная заметка.
  • sev_Mark - отладочная отметка.
  • sev_Details - отладочные подробности (детали) DAQ.
  • sev_ViewExp - отладочный ввод из потока данных DAQ.
  • sev_ViewImp - отладочный вывод потока данных DAQ.
  • sev_Report - отладочный отчет.
  • sev_Attention - обратить внимание.
  • sev_INFO = 10 - базовый уровень для информационных сообщений.
  • sev_Voice - событие звуковой системы.
  • sev_Tooltip - всплывающее сообщение.
  • sev_Input - ввод в Главной Консоли.
  • sev_Print - печать в Главной Консоли.
  • sev_Notify - уровень уведомления (это триггерный уровень по умолчанию).
  • sev_Success - успешное действие.
  • sev_Succeed - успешное завершение операции.
  • sev_Great - отлично выполненное действие.
  • sev_Perfect - безупречно выполенное действие.
  • sev_WARN = 20 - базовый уровень для предупредительных сообщений.
  • sev_Worry - есть повод для беспокойства.
  • sev_Hazard - возник риск неприятностей.
  • sev_Disturb - это раздражает и мешает.
  • sev_Hardship - возникли трудности.
  • sev_Problem - возникли проблемы (некритические ошибки DAQ).
  • sev_Mistimes - рассинхронизация времени.
  • sev_Exception - программное исключение.
  • sev_Watchdog - сработал сторожевой таймер.
  • sev_Alert - режим повышенной готовности.
  • sev_ERROR = 30 - базовый уровень для сообщений об устранимых ошибках.
  • sev_Bug - программная ошибка.
  • sev_Glitch - программный "глюк" (непредвиденное поведение).
  • sev_Fail - операция прошла неудачно.
  • sev_Fault - программный или аппаратный сбой.
  • sev_Trouble - серьезная неприятность - ошибка DAQ.
  • sev_Alarm - тревога (красная лампа).
  • sev_Danger - тревога (опасная ситуация).
  • sev_Siren - тревога (звучит сирена).
  • sev_Critical - критическая ошибка.
  • sev_FATAL - базовый уровень для сообщений о неустранимых ошибках.
  • sev_Failure - неустранимый программный или аппаратный сбой.
  • sev_Abort - аварийное завершение программы (по алгоритму).
  • sev_Crash - аварийное завершение программы (непредсказуемое).
  • sev_Misfortune - "фортуна нас оставила".
  • sev_Emergency - чрезвычайная ситуация.
  • sev_Accident - несчастный случай.
  • sev_Catastrophe - катастрофа.
  • sev_Disaster - "нам капец".
  • sev_Doomsday - Судный День.
  • sev_OFF = 50 - журнал отключен.

Пример:

  procedure TestSysLog;
  var severity,code,leng:Integer; name:String;
  begin
   name:='';
   severity:=sev_Notify;
   name:=ParamStr('SysLogSeverityName '+Str(severity));
   code:=Val(ParamStr('SysLogSeverityCode '+name));
   writeln('Имя уровня значимости ',name);
   writeln('Код уровня значимости ',code);
   if SysLogNotable(severity)
   then writeln('Уровень ',severity,' ЗНАЧИМЫЙ')
   else writeln('Уровень ',severity,' НЕ_ЗНАЧИМЫЙ');
   leng:=SysLogNote(severity,'Это тестовая запись в Журнал.');
   if (leng>0)
   then writeln('Событие записано в журнал')
   else writeln('Событие проигнорировано');
   name:='';
  end;
         

Daq Pascal Api -> text_new text_free text_getln text_putln text_insln text_addln text_delln text_numln text_tostring

Определение:

function text_new:integer;
function text_free(ref:integer):boolean
function text_getln(ref:integer;i:integer):string
function text_putln(ref:integer;i:integer;s:string):boolean
function text_insln(ref:integer;i:integer;s:string):boolean
function text_addln(ref:integer;s:string):boolean
function text_delln(ref:integer;i:integer):boolean
function text_numln(ref:integer):integer
function text_tostring(ref:integer):string

Аргументы:

  • ref - ссылка на текст.
  • i - номер строки от 0 до text_numln(ref)-1.
  • s - строка для внесения в текст.

Результат:

text_new - создает текст и возвращает ссылку на этот текст.
text_free - уничтожает текст ref. Возвращает true при успешном выполнении.
text_getln - возвращает строку номер i текста ref.
text_putln - заносит строку s на место строки номер i в тексте ref. Возвращает true при успешном выполнении.
text_insln - вставляет строку s перед строкой номер i текста ref. Возвращает true при успешном выполнении.
text_addln - вставляет строку s в конец текста ref. Возвращает true при успешном выполнении.
text_delln - удаляет строку номер i из текста ref. Возвращает true при успешном выполнении.
text_numln - возвращает число строк в тексте ref.
text_tostring - возвращает текст ref в виде длинной строки с разделителями LineEnding.

Описание:

Группа операций с текстом. Дает возможность организовать хранение и обработку текстов в памяти. Текст представляется в виде набора строк, которые в текущей версии имеют длину не более 255 символов. Строки нумеруются начиная с нуля и разделяется символами crlf.

Тексты идентифицируются ссылками, см. refinfo.

Пример:

          var txt:integer;
          txt:=text_new;
          b:=text_addln(txt,'Add line');
          b:=text_putln(txt,0,'Put line 0');
          b:=text_insln(txt,0,'Insert line 0');
          while text_numln(ref)>0 do text_delln(txt,text_numln(txt)-1); {clear text}
          b:=text_free(txt);
         

Daq Pascal Api -> runcount

Определение:

function runcount:Real

Аргументы:

Нет.

Результат:

Возвращает счетчик вызовов DAQ программы.

Описание:

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

Каждая DAQ-программа имеет счетчик вызовов программы с момента старта DAQ. При первом вызове программы, который происходит при старте DAQ системы, runcount=1. Затем при каждом последующем вызове программы значение runcount приращивается на 1. Наконец, при остановке DAQ системы программа вызывается со значением runcount=+INF, что можно проверить вызовом isinf(runcount).

Указанное поведение активно используется для организации вычислений.
Обычно:

  • По runcount=1 делается инициализация, устанавливается признак успешного старта Ok.
  • По isinf(runcount) делается завершение, освобождаются ресурсы.
  • При других значениях runcount идет обычный опрос, если флаг Ok указывает на успешный старт.
Подавляющее большинство DAQ программ используют указанные соглашения.

Пример:

Данная программа иллюстрирует каноническую структуру большинства DAQ программ.
         program example;
         var Ok:Boolean;
         begin
          if runcount=1 then begin
           writeln('Start program');
           Ok:=true;
          end else
          if isinf(runcount) then begin
           writeln('Stop  program');
          end else
          if Ok then begin
           writeln(’Poll program, RunCount is ', runcount:1:0);
          end;
         end.
         

Daq Pascal Api -> sleep

Определение:

function sleep(t:integer):boolean

Аргументы:

  • t - задержка, миллисекунд.

Результат:

Выполняет задержку выполнения потока на срок не менее t миллисекунд. Возвращает всегда TRUE.

Описание:

Вызов sleep(t) приостанавливает выполнение потока программы Daq Pascal на срок не менее t миллисекунд, всегда возвращая true. Приостановленный поток переводится в спящий режим и не занимает времени процессора, пока не истечет срок ожидания. Функция служит для снижения загрузки процессора при организации небольших циклов ожидания, но не более нескольких секунд.
Надо иметь в виду следующеее:
  • При вызове sleep(t) при t>0 процессор передает управление другим потокам, а возврат происходит во время выделения потоку очередного кванта времени.
  • Управление может быть возвращено через время, превышающее запрошенное t. Это связано по-первых с квантованием времени, во вторых, с возможностью передачи управления более приоритетным потокам.
  • Обычно квант времени под Win-NT/2K/XP составляет порядка 10 миллисекунд, поэтому, например, вызов sleep(1) или sleep(5) реально даст одну и ту же задержку 10 ms.
  • При вызове sleep(0) процессор передает управление другим потокам, только если в данный момент есть ожидающий исполнения поток с таким же или более высоким приоритетам. Если таких потоков нет, sleep(0) вернет управление немедленно.
  • Система CRW32 имеет программный сторожевой таймер (WatchDog), который детектирует подвисание потоков программы. Поэтому больших задержек (больше нескольких секунд) делать не следует, иначе сторожевой таймер сработает. Обычно WatchDog настроен на 5 сек.

Пример:

          var t:real;b:boolean;
          begin
           t:=msecnow;
           b:=sleep(1);
           writeln('Sleep ',msecnow-t,' ms');
          end;
         

Daq Pascal Api -> awakeflag

Определение:

function awakeflag:boolean

Аргументы:

Нет.

Результат:

Возвращает true, если поток программы был "разбужен" досрочно.

Описание:

Возвращает true, если поток программы был "разбужен" досрочно, то есть до исчерпания времени ожидания, заданного планировщиком. Поток программы может быть разбужен досрочно в следующих случаях:
  • При посылке сообщения устройству вызовом devmsg.
  • При вводе данных в консоль устройства, что эквивалентно посылке сообщения.
  • При нажатии сенсора мнемосхемы, связанного с устройством.

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

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

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

Надо также учитывать, что функция не регистрирует число событий, вызвавших пробуждение. Приход одного или десяти сообщений даст один результат - однократное пробуждение потокf. Можно твердо сказать только одно - если функция awakeflag вернула true, то с момента завершения предыдущего опроса программы что-то произошло.

Пример:

          if awakeflag then writeln('Awake');
         

Daq Pascal Api -> wdt_reset wdt_timeout

Определение:

function wdt_reset(flag:boolean):real
function wdt_timeout(timeout:integer):integer

Аргументы:

  • flag - нужен ли сброс Сторожевого Таймера Watchdog.
  • timeout - время ожидания Сторожевого Таймера Watchdog.

Результат:

wdt_reset(flag) возвращает время (ms) выполнения программы в текущем цикле опроса, а также принудительно (досрочно) сбрасывает Watchdog, если указан flag. Системный сброс сторожевого таймера происходит автоматически в конце очередного цикла опроса (при завершении программы), но при выполнении длительных операций может потребоваться выполнять досрочный сброс Сторожевого Таймера. Сброс Watchdog уведомляет систему, что работа потока программы продолжается нормально. После сброса Watchdog поток программы получает ещё один интервал времени для выполнения работы, прежде чем для него сработает сторожевой таймер. Этот интервал времени определяется максимальным значением из timeout (по умолчнию 0) и значения системного времени ожидания, которое задается в окне диалога СТОРОЖЕВОЙ ТАЙМЕР.
wdt_timeout(timeout) возвращает время ожидания сторожевого таймера Watchdog и затем задает новое время ожидания timeout. При этом timeout=0 означает "использовать системное значение по умолчанию". Интервал времени для работы программы определяется максимальным значением из timeout (по умолчнию 0) и значения системного времени ожидания, которое задается в диалоге СТОРОЖЕВОЙ ТАЙМЕР. Если программа не завершила работу в течение этого времени или не сбросила Watchdog, то сторожевой таймер фиксирует ошибку.

Описание:

Функция wdt_reset(flag) служит для контроля и сброса сторожевого таймера (WatchDog Timer) потока данного устройства. Возвращает время (в миллисекундах, как msecnow) с начала "системного" цикла опроса потока. Это фактически время выполнения прграммы в текущем цикле опроса.

Если указан flag=true, то функция выполняет также "принудительный" сброс Watchdog. В обычной ситуации срабатывает системный сброс Watchdog, который делается автоматически. Системный сброс Watchdog происходит в начале и конце каждого системного цикла опроса потока перед вызовом программы и после её завершения. Однако при длительных операциях требуется выполнять принудительный (досрочный) сброс.
Сброс Watchdog уведомляет систему, что работа потока программы продолжается нормально, т.е. поток не "завис". После сброса Watchdog поток программы получает ещё один интервал времени для выполнения работы, прежде чем для него сработает сторожевой таймер. Этот интервал времени определяется максимальным значением из timeout (по умолчнию 0) и значения системного времени ожидания, которое задается в диалоге СТОРОЖЕВОЙ ТАЙМЕР.

Функция wdt_timeout(timeout) возвращает старое и задает новое время ожидания для сторожевого таймера данного устройства. Время ожидания задается в миллисекундах. Нулевое значения timeout=0 соответствует значению по умолчанию. Время ожидания сторожевого таймера является наибольшим из времени ожидания для сторожевого таймера данного устройства и общесистемного времени ожидания, заданного в диалоге Сторожевой Таймер.

Функции Сторожевого Таймера применяются в трех случаях.

  1. Для контроля времени работы программы в цикле опроса.
    Вызов wdt_reset(false) без сброса Сторожевого Таймера позволяет измерять время работы программы в текущем цикле опроса, что дает возможность его контролироливать (управлять им). Например, длительные циклические операции (если они состоят из множества мелких операций) можно прервать, чтобы продолжить выполнение в следующем цикле опроса. Например:
                 Terminated:=False;
                 while not Terminated do begin
                  DoSomeWork;
                  if wdt_reset(false)>3000 then Terminated:=true;
                 end;
                 
    В данном случае выполняется некоторая циклическая работа (DoSomeWork), без сброса Watchdog, но не дольше 3 секунд в сумме (что меньше системного времени ожидания Сторожевого Таймера), после чего цикл прерывается до следующего вызова программы в системном цикле опроса. В этом примере время выполнения программы будет ограничено, поэтому сработает автоматический сброс Сторожевого Таймера.

  2. Для выполнения длительных итераций, состоящих их "мелких" вызовов.
    В цикле опроса много мелких операций, которые в сумме могут выполняться долго (больше стандартного интервала). В этом случае после выполнения очередной операции делается вызов wdt_reset(true) для сброса Watchdog. При этом можно также анализировать результат вызова (время выполнения программы в данной итерации цикла опроса) и (возможно) прерывать выполнения операций, чтобы продолжить их в следующем цикле. Например:
                 Terminated:=False;
                 while not Terminated do begin
                  DoSomeWork;
                  if wdt_reset(true)>15000 then Terminated:=true;
                 end;
                 
    В данном случае выполняется некоторая циклическая работа (DoSomeWork), со сбросом Watchdog после каждой итерации, но не дольше 15 секунд в сумме, после чего цикл прерывается до следующего вызова программы в системном цикле опроса. В этом примере время выполнения программы в цикле будет больше стандартного времени ожидания Сторожевого Таймера, поэтому необходимо применять принудительный сброс Сторожевого Таймера.

  3. Для вызова длительно выполняемых операций.
    В цикле опроса требуется выполнить длительную операцию (например, создание базы данных), для которой время ожидания может превышать стандартное время ожидания Сторожевого Таймера. В этом случае надо перед выполнением операции задать таймаут wdt_timeout(timeout), где timeout - предполагаемое значение времени ожидания, достаточное для выполнения долгой операции, а затем востановить таймаут. При этом также надо вызывать сброс Watchdog вызовом wdt_reset(true).
                 if NewDataBase then begin  // Если надо создать новую БД
                  bNul(wdt_reset(true));    // Сбрасываем Watchdog
                  tm:=wdt_timeout(30000);   // Задаем большой таймаут
                  CreateNewDataBase;        // Не спеша создаем новую БД
                  bNul(wdt_reset(true));    // Сбрасываем Watchdog
                  iNul(wdt_timeout(tmt));   // Восстанавливаем таймаут
                  end;                      // Сделано.
                  ...                       // Работаем дальше...
                 
    В этом примере требуется задать timeout, потому что длительно выполняемая операция CreateNewDataBase не позволяет сбросить Watchdog длительное время. Поэтому требуется уведомить систему о том, что в течение этого времени поток ожидает завершения операции, а не просто "висит". Система не будет фиксировать ошибку Сторожевого Таймера, пока запрошенный лимит времени не будет исчерпан.

Теперь более подробно о работе Сторожевого Таймера.
Во-первых, в целях повышения надежности измерительных систем и улучшения диагностики опроса потоков, система CRW-DAQ имеет сторожевой таймер (WatchDog Timer или просто Watchdog). Цель сторожевого таймера состоит в том, чтобы диагностировать "подвисание" потоков программы. В цикле опроса потока DAQ-системы, непосредственно перед очередным выполнением программы Daq Pascal, система сбрасывает Watchdog и запоминает время этого сброса (назовем такой сброс "системным", так как он генерируется системой автоматически). При нормальном выполнении программы Watchdog будет сбрасываться каждый квант времени, определяемый периодом опроса DevicePolling устройства и квантом времени операционной системы. Если в программе Daq Pascal возникает задержка (блокировка потока или длительный цикл вычислений), то сбос Watchdog будет задерживаться. Специальный сторожевой поток системы CRW-DAQ периодически взводит Watchdog для каждого потока программы и отслеживает, сколько времени Watchdog потока остается взведенным. Если это время превысило критическое (обычно около 5 секунд), значит за это время поток не имел возможности сбросить Watchdog, то есть скорее всего "повис". При этом в окне появляется окно с изображением
собаки собаки собаки
предупреждающим о подвисании программы.

Цикл опроса DAQ-устройства можно представить в виде такого псевдокода:

         DeviceThread.Execute;                       {функция потока устройства}
         begin
          while not Terminated do begin              {цикл опроса}
           t:=msecnow;                               {время сброса watchdog}
           ResetWatchDogTimer;                       {сброс watchdog таймера}
           if InquiryPeriod.Event                    {если подошло время опроса}
           then ExecuteDaqPascalProgram;             {то выполнить программу Daq Pascal}
           TimeOfLastWatchdogReset:=t;               {время последнего сброса watchdog}
           WaitForSingleObject(event,DevicePolling); {ожидать цикл опроса или событие}
          end;
         end;
        

Функция wdt_reset может быть представлена таким псевдокодом:

        function wdt_reset(flag:boolean):real;
        begin
         wdt_reset:=TimeOfLastWatchdogReset; {вернуть время системного сброса watchdog}
         if flag then ResetWatchDogTimer;    {если надо, принудительно сбросить watchdog}
        end;
        
При нормальном выполнении программы процедура ExecuteDaqPascalProgram будет выполняться быстро, а Watchdog будет сбрасываться примерно через каждые DevicePolling миллисекунд. Если при выполнении программы возникла задержка (блокировка потока или длительные вычисления), то сброса Watchdog не произойдет и система выдаст предупреждение.

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

msecnow-wdt_reset(false)
дает именно период опроса потока. Заметим, что период опроса программы и потока совпадают, если InquiryPeriod <= DevicePolling. По этой причине часто в конфигурации устанавливается InquiryPeriod=1, при котором период опроса DAQ Pascal программы и потока заведомо совпадают. Однако иногда имеет смысл делать опрос программы значительно реже, чем потока. Если программа должна выполняться раз в минуту, поток все же должен активизироваться раз в секунду, хотя бы для того, чтобы сбросить Watchdog. Сравните:
         program polling;
         var ms,lastms:real;
         begin
          ms:=msecnow;
          writeln('Период опроса потока = ',ms-wdt_reset(false),' ms');
          writeln('Период опроса программы = ',ms-lastms,' ms');
          lastms:=ms;
         end.
        

Функция wdt_reset может применяться в следующих целях:

  • Вызов wdt_reset(false) возвращает фактически время начала предыдущего выполнения потока программы Daq Pascal, не сбрасывая при этом Watchdog. В обычной ситуации (когда Daq программа выполняется быстро и выполнено условие InquiryPeriod <= DevicePolling) это будет просто период опроса, например, 10 миллисекунд (квант времени Windows). Таким образом, вызов wdt_reset(false) позволяет контролировать период опроса потока DAQ-устройства, а также позволяет правильно организовать вычислительный процесс, например, прерывая длительные вычисления, если они занимают слишком много времени:
                 while msecnow-wdt_reset(false) < timeout do Something;
                 
  • Вызов wdt_reset(true) позволяет организовать длительные вычисления или циклы ожидания без активизации предупреждения Watchdog, так как принудительно сбрасывает Watchdog и поэтому подавляет предупреждения Watchdog. Этой возможностью следует пользоваться с осторожностью, в виде исключения, только если это сильно упрощает код программы. Ведь отключение предупреждений Watchdog может привести к тому, что система не сможет отличить подвисший в бесконечном цикле поток от нормального выполнения программы.

Пример:

          {Get time of last polling}
          dt:=msecnow-wdt_reset(false);
          writeln('Last device polling was ',dt,' msec ago');

          {Sleep 10 second. WatchDog Timer warning disabled.}
          while msecnow-wdt_reset(true) < 10000 do b:=sleep(1);

          {Sleep 10 second. WatchDog Timer warning enabled.}
          while msecnow-wdt_reset(false) < 10000 do b:=sleep(1);
         

Daq Pascal Api -> secnow

Определение:

function secnow:real

Аргументы:

нет

Результат:

Возвращает текущее астрономическое время в секундах.

Описание:

Возвращает текущее астрономическое время в секундах. Функция введена просто для удобства, а ее реализация эквивалентна вызову
secnow=1000*msecnow.
Подробнее об измерении времени смотри описание msecnow.

Daq Pascal Api -> msecnow

Определение:

function msecnow:real

Аргументы:

нет

Результат:

Возвращает текущее астрономическое время в миллисекундах.

Описание:

Возвращает текущее время в миллисекундах от Рождества Христа, то есть от 0001.01.01. Это основная, наиболее часто употребимая функкция CRW-DAQ для измерения времени. Говоря о времени, следует иметь в виду, что в компьютере есть несколько независимых часов и несколько способов определения времени. Как минимум, в компьютере есть:
  1. Энергонезависимые часы CMOS, которые питаются от аккамулятора и служат для определения календарного времени. Однако их временное разрешение невелико, а доступ не быстрый. Для получения времени по часам CMOS используется системный вызов GetSystemTime. Системное время Windows также учитывает часовой пояс, летнее/зимнее время. Вызов GetSystemTime меняется скачком при смене пользователем системного времени, часового пояса или летнего/зимнего времени. Из-за этого нарушения непрерывности времени, а также из-за дороговизны вызова, функция GetSystemTime не может быть использована в измерительных системах напрямую.
  2. Кварцевые часы, которые тактируются от кварцевого генератора и служат для определения локального времени, то есть времени от старта системы. Собственно ядро системы Windows в основном построено на этих часах. Кварцевые часы энергозависимы, поэтому могут служить только для измерения локального времени. Для получения локального времени есть две основные функции:
    • GetTickCount - быстрая функция грубого локального времени, которая возвращает фактически счетчик прерываний таймера, на которых основан планировщик задач Windows. Функция имеет квант времени порядка 10 ms. Вызовы GetTickCount в рамках одного кванта времени планировщика возвращают одно и то же значение. Через 49.71 суток работы Windows 32-разрядный счетчик переполняется, что не позволяет напрямую использовать функцию для измерительных систем, которые должны работать неограниченное время непрерывно. Подробнее о кварцевых часах и квантовании времени смотри описание getclockres.
    • QueryPerformanceCounter - функция прецизионного локального времени, с квантом от 100 нс (обычно порядка 1 мкс). Вызов функции стоит сравнительно дорого, что не позволяет использовать ее как основную функцию для определения времени в измерительных системах. Кроме того, часто алгоритмы управления строятся так, что события в рамках одного кванта времени планировщика считаются одновременными, а данная функция возвращает при каждом вызове новое значение.
Из сказанного ясно, что подходящей функции, то есть непрерывной, монотонной, быстрой функции, дающей астрономическое время, в Windows нет. Поэтому был взят некий гибрид этих функций, который делает как надо. Алгоритм вычисления msecnow такой:
  1. При старте CRW-DAQ запоминает астрономическое время по часам CMOS, полученное вызовом GetSystemTime, с учетом временного пояса, и локальное время по кварцевым часам, полученное вызовом GetTickCount.
  2. При дальнейших вызовах к запомненному календарному времени прибавляется локальное время по кварцевым часам, полученное вызовом GetTickCount минус начальное значение GetTickCount, с контролем переполнения, которое наступает через 49.71 суток непрерывной работы Windows.
Из сказанного следует, что msecnow имеет следующие свойства:
  • Время по msecnow - астрономическое, монотонное и непрерывное, не испытывает разрыва при смене системного времени Windows, вызов функции быстрый.
  • Временное разрешение msecnow совпадает с разрешением GetTickCount, то есть с квантом времени планировщика заданий Windows, который составляет порядка 10 ms. Для получения микросекундного разрешения следует использовать mksecnow. Cм. также описание getclockres.
  • События по msecnow в рамках одного кванта времени планировщика задач Windows выглядят одновременными, то есть вызов msecnow возвращает одно и то же значение, пока не начнется новый квант времени. В отличие от этого, вызов mksecnow каждый раз возвращает новое значение.
  • В отличие от GetSystemTime, время по msecnow меняется непрерывно и монотонно при смене текущего времени Windows, часового пояса, переходе на летнее/зимнее время и т.д. Однако при этом возможно возникновение разницы между временами CRW-DAQ и Windows. Все алгоритмы управления обычно строятся на функции msecnow, поэтому при смене текущего времени Windows их работа не нарушится, однако возможны другие побочные эффекты, скажем, связанные с временами создания файлов и т.д. Поэтому при обнаружении рассинхронизации часов CRW-DAQ и Windows пользователю выдается предупреждение.
  • В отличие от GetTickCount, msecnow выдает астрономическое (календарное) время, а не локальное время от старта системы, а также меняется непрерывно и монотонно при длительной работе Windows, так как учитывается переполнение GetTickCount через 49.71 суток.
  • Вызов msecnow быстрый, занимает порядка 100 нс. Вызов mksecnow значительно дороже, порядка 2 мкс, поэтому msecnow и взята за основу, несмотря на сравнительно большой квант. С другой стороны, это разумно, так как при работе с потоками получается, что события в одном кванте времени выглядят одновременными, что обычно и предполагается. Функцию же mksecnow, из-за дороговизны вызова, следует использовать только тогда, когда это действительно надо и стараться минимизировать число этих вызовов.

Пример:

          var t:real;
          begin
           t:=msecnow;
           writeln('Time is ',t);
          end;
         

Daq Pascal Api -> mksecnow

Определение:

function mksecnow:real

Аргументы:

нет

Результат:

Возвращает текущее локальное время в микросекундах.

Описание:

Возвращает текущее локальное время CRW-DAQ в микросекундах. В качестве начала отсчета берется старт программы. Назначение функции – высокоточное определение коротких интервалов между событиями, когда глобальная привязка событий к астрономическому времени не нужна. Надо также иметь в виду, что вызов mksecnow занимает порядка 2 микросекунд против 0.1 микросекунд на вызов msecnow, поэтому если микросекундное разрешение не требуется, следует отдавать предпочтение вызовам msecnow.
Функция основана на системном вызове QueryPerformanceCounter. Основные свойства функции таковы:
  • Функция имеет высокое разрешение по времени, обычно порядка 1 mks.
  • Каждый вызов функции возвращает новое фактическое значение, то есть время не квантовано.
  • Вызов функции стоит дорого, порядка 2 mks, поэтому надо стараться минимизировать число вызовов функции и не использовать ее, если действительно не требуется высокое разрешение.
Подробнее об измерении времени смотри описание msecnow.

Пример:

Нет.

Daq Pascal Api -> cpu_count cpu_start cpu_clock cpu_mhz

Определение:

function cpu_count:Integer
function cpu_start:Boolean
function cpu_clock:Real
function cpu_mhz:Real

Аргументы:

нет

Результат:

Группа функций, связанных процессором и его часами.

Описание:

Группа функций, связанных процессором и его часами. Новые модели процессоров, начиная с Pentium, имеют регистр счетчика реального времени TSC (Time Stamp Counter). Фактически это счетчик тактов процессора. Этот счетчик имеет разрядность 64 bit, то есть время до переполнения, например, 1 GHz процессора, равное 2^64/(1e9*3600*24*365)=584 года. Поскольку счетчик возвращается как Real, время его переполнения еще меньше (месяцы), поэтому функцию чтения счетчика тактов процессора cpu_clock надо использовать в паре с функцией сброса счетчика cpu_start примерно так:
         if cpu_start then begin
          DoSomething;
          t:=cpu_clock;
          writeln('DoSomething takes ',t,' CPU clocks');
         end else writeln('cpu_clock is not supported');
         

function cpu_count:Integer - возвращает число логических процессоров в системе. Это могут быть

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

function cpu_start:Boolean - сбрасывает счетчик тактов в ноль (сброс происходит программно, а не аппаратно). Возвращает False, если счетчик тактов процессора недоступен. Функция используется в паре с cpu_clock для измерения тактов процессора. Необходимо периодически делать сброс счетчика, чтобы избежать его переполнения.

function cpu_clock:Real - возвращает счетчик тактов процессора, прошедших с последнего вызова cpu_start. Надо учитывать, что сам вызов функции тоже занимает несколько сот тактов и делать соответствующую коррекцию. Если функция возвратила 0, счетчик тактов недоступен.

function cpu_mhz:Real - возвращает частоту тактов процессора, в мегагерцах. Это измеренная в момент старта программы оценка, она может быть не вполне точной, так как надежного способа точно узнать частоту процессора не существует, ее можно измерить по счетчику тактов и кварцевым часам типа mksecnow. Если функция возвратила 0, счетчик тактов недоступен.

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

Однако использование функции cpu_clock имеет ограничения, не позволяющие считать ее очень надежной.

  1. Программа, использующая cpu_clock, должна иметь в виду, что команда может отсутствовать на данном процессоре и использовать в этом случае более надежный вызов msecnow или mksecnow.
  2. На мультипроцессорных системах cpu_clock в общем случае не дает надежного способа измерения времени. Причина в том, что никто не гарантирует синхронность счетчиков разных процессоров, а потоки могут в приципе выполняться на разных процессорах. То есть два вызова cpu_clock могут выполняться на разных процессорах и возвращать значение разных счетчиков. Расхождение счетчиков может появиться, например, при использовании функций энергосбережения, когда разные процессоры отключаются и просыпаются в разное время. Поэтому тайминг на основе cpu_clock будет надежно работать только если процесс (поток) привязан к одному процессору. Это можно сделать, например, функцией pidaffinity, devaffinity.
  3. Никто не гарантирует, что частота процессора стабильна. Частота процессора может измениться, если включены функции энергосбережения.

По перечисленным причинам применение cpu_clock ограничено случаями когда

  1. В системе один процессор или процесс/поток привязан к одному процессору.
  2. Функции энергосбережения отключены и частота процессора стабильна.
  3. Измеряются очень краткие интервалы времени, когда маловероятно переключение процессора или изменение его частоты.

Пример:

        program demo;
        var i:Integer; t:Real;
        begin
         writeln('System has ',cpu_count,' processors (CPUs).');
         writeln('CPU frequency is ',cpu_mhz,' MegaHertz.');
         {on multiprocessor systems set process affinity}
         if cpu_count>1 then i:=pidaffinity(0,1);
         if cpu_start then begin
          DoSomething;
          t:=cpu_clock;
          writeln('Procedure DoSomething takes ',t,' CPU clocks.')
         end;
        end.
        

Daq Pascal Api -> getticks

Определение:

function getticks:real

Аргументы:

нет

Результат:

Возвращает текущее суточное время в тиках.

Описание:

Возвращает текущее суточное время в “тиках” таймера BIOS. Один “тик” таймера составляет около 55 миллисекунд, а отсчет ведется от начала текущих суток. В полночь “тики” сбрасываются в ноль. Эти особенности функции надо учитывать при программировании. Обычно тики используются для организации периодических событий, где монотонность времени не играет роли. Функция getticks введена для совместимости со старыми версиями CRW-DAQ и считается устаревшей. Вместо нее следует использовать msecnow.
Подробнее об измерении времени смотри описание msecnow.

Пример:

Нет.

Daq Pascal Api -> getclockres setclockres

Определение:

function getclockres(what:integer):real
function setclockres(new_res:real):real

Аргументы:

  • what - определяет, что (какой параметр) надо прочитать.
  • new_res - (+)задает/(-)отменяет новое разрешение таймера, [ms].

Результат:

Функции чтения/задания разрешения (кванта) системного таймера, все в [ms].

Описание:

Планирование потоков в системе Windows основано на системном таймере, обычно это кварц на материнской плате. Таймер генерирует прерывания с определенной частотой (или периодом=квантом=разрешением). Во время каждого прерывания системного таймера происходит принудительное перепланирование потоков в соответствии с их приоритетами (на этом и основана вытесняющая приоритетная многозадачность). Поэтому временные параметры планировщика, такие как квант времени операционной системы, определяются системным таймером. Кроме того, во время прерываний таймера инкрементируется счетчик системного времени, поэтому системный таймер определяет разрешение системных часов, таких как GetTickCount, GetSystemTimeAsFileTime и основанной на них msecnow.

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

Функции GetClockRes, SetClockRes позволяют определить и задать некоторые параметры системного таймера и тем самым влияют на временные характеристики системы сбора данных, как и системы в целом. Все времена в этих функциях измеряются в [ms] (миллисекундах) и имеют квант (гранулярность) 100 ns. Обе функции возвращают ноль, если возникает какая-то ошибка. Например, если отменяется таймер, который не был разрешен, возвращается ноль. Кроме того, функции не работают в Windows-9x, поэтому там они всегда возвращают ноль. Впрочем, время Windows-9x прошло, к тому же для этой стистемы квант времени - понятие прпктически бесполезное, так как вытесняющая приоритетная мультизадачность там реализована из рук вон плохо.

GetClockRes(What) возвращает параметры системного таймера:

  • GetClockRes(0) - возвращает стандартное для данной системы разрешение (стандартный квант времени) системного таймера. Этот квант определяет точность измерения времени в функциях GetTickCount, GetSystemTimeAsFileTime и основанной на них msecnow. Этот квант также определяет стандартную частоту переключения потоков в системе. Для однопроцессорных систем это обычно около 10 ms, для многопроцессорных около 15 ms.
  • GetClockRes(1) - возвращает минимальное для данной системы разрешение (то есть максимальный квант времени) системного таймера. Обычно это значение порядка 10 ms.
  • GetClockRes(2) - возвращает максимальное для данной системы разрешение (то есть минимальный квант времени) системного таймера. Обычно это значение порядка 1 ms.
  • GetClockRes(3) - возвращает актуальное (текущее) значение разрешения (кванта времени) системного таймера. Именно это значение определяет текущую частоту переключения потоков в системе. Однако надо учитывать, что функции измерения времени по-прежнему используют стандартный квант времени, поэтому может оказаться так, что в течение нескольких квантов времени планировщика функции измерения времени (GetTickCount, GetSystemTimeAsFileTime, msecnow) возвращают одно и то же значение. Это возможно, если частота системного таймера выше стандартного значения. Этот факт надо учитывать при конфигурировании и разработке прикладного программного обеспечения (см. описание msecnow и mksecnow). В частности, для устройств с быстрым опросом (например, с периодом 1 ms) надо указывать:
                  InquiryPeriod = 0
                  DevicePolling = 1, tpTimeCritical
                  
    Если указать ненулевое значение InquiryPeriod, частота опроса потока не будет выше стандартной частоты (порядка 100 Герц), т.к. квант функций измерения времени равен стандартному кванту порядка 10 ms, а работа InquiryPeriod основана на вызове msecnow. Ну а высокий приоритет необходим для повышения стабильности частоты опроса потока.

SetClockRes(new_res) задает или отменяет новую частоту системного таймера. Положительное (разрешающее) значение таймера задает новую частоту таймера, а отрицательное (запрещающее) отменяет ее и возвращает стандартное значение. Функция возвращает новое актуальное значение таймера, подобно GetClockRes(3).

Важно понимать, что возвращаемое SetClockRes значение может существенно отличаться от заданного в аргументе желаемого значения. Ведь системный таймер - общесистемный ресурс и на него влияют запросы многих процессов. Система работает так, чтобы удовлетворять работе наиболее быстрых процессов. Например, если один процесс активизировал таймер с квантом 1 ms (например, вызвал SetClockRes(1)), а затем второй процесс активизировал таймер с квантом 5 ms (например, вызвал SetClockRes(5)), то функция вернет значение 1 ms, так как системный таймер должен удовлетворить требованиям первого процесса.

Другими словами, вызов GetClockRes(new_res) с положительным (разрешающим) значением аргумента может только уменьшить квант таймера (повысить частоту опроса). Если запрошенный квант больше текущего (частота опроса ниже текущей), значение кванта не изменится. Однако факт запроса будет зафиксирован, что важно при отмене таймера (см. далее).

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

          SetClockRes(+5); ... SetClockRes(-5);
         
Система действительно отменяет таймер, только когда все процессы, запросившие таймер, отменили запрос или завершились. Например, в приведенном выше примере, если второй процесс (с квантом 5 ms) вызвал SetClockRes(-5) для отмены таймера, квант времени останется равным 1 ms, пока первый процесс не отменит его или завершит работу.

Как показывают наблюдения, в рамках одного процесса очереди запросов на таймер не создается (длина очереди равна 1). Например, если вызвать SetClockRes(+5), затем SetClockRes(+2), то вызов SetClockRes(-2) отменяет таймер, хотя было два запроса. Однако рекомендуется соблюдать стековый порядок разрешения/отмены таймера (так, на будущее).

Наконец, надо напомнить, что в CRW-DAQ работает много потоков, каждый из них может разрешить/запретить таймер. Чтобы не создавать хаос, рекомендуется разрешать таймер в каком-то одном потоке (то есть в одной программе Daq Pascal).


Подробнее об измерении времени смотри описание msecnow.

Пример:

         program demo;
         begin
          writeln('Standard timer quantum is ',GetClockRes(0),' ms');
          writeln('Maximal  timer quantum is ',GetClockRes(1),' ms');
          writeln('Minimal  timer quantum is ',GetClockRes(2),' ms');
          writeln('Actual   timer quantum is ',GetClockRes(3),' ms');
          writeln('Now set clock about 1 ms (1000 Hz).');
          if SetClockRes(+1)=0 then writeln('Error!');
          writeln('Actual   timer quantum is ',GetClockRes(3),' ms');
          writeln('Now disable fast clock, go back to standard value.');
          if SetClockRes(-1)=0 then writeln('Error!');
          writeln('Actual   timer quantum is ',GetClockRes(3),' ms');
         end;
        

Daq Pascal Api -> vdpm_opcount

Определение:

function vdpm_opcount:real

Аргументы:

нет

Результат:

Возвращает счетчик числа выполненных операций Daq программы.

Описание:

Virtual Daq Pascal Machine OPeration COUNTer. Возвращает счетчик числа выполненных операций Daq программы, то есть число выполненных низкоуровневых инструкций интерпретатора Daq Pascal с момента старта Daq программы. Применяется для оценки производительности кода и его фрагментов. Хотя "вес" разных инструкций отличается, оценка числа инструкций дает некоторое представление о времени выполнения кода.

Пример:

x:=vdpm_count;

Daq Pascal Api -> time timebase timeunits

Определение:

function time:Real
function timebase:Real
function timeunits:Real

Аргументы:

Нет.

Результат:

time возвращает время по часам DAQ.
timebase возвращает начало отсчета времени по часам DAQ в миллисекундах.
timeunits возвращает величину единицы времени DAQ в миллисекундах.

Описание:

time возвращает текущее время по часам DAQ-системы в единицах timeunits, начиная отсчет от момента timebase.
timebase возвращает момент времени в миллисекундах, как msecnow, соответствующий нулю по часам DAQ-системы.
timeunits возвращает величину единицы времени DAQ-системы в миллисекундах.
Время, возвращаемое функцией time, отсчитывается от момента timebase. По умолчанию timebase - время загрузки DAQ-системы, однако его можно задавать (в календарной форме) в переменной timebase в секции [DAQ], например:
          [DAQ]
          TimeBase = 13.05.1999-18:35:00 ; Базовое время Day.Month.Year-Hour:Min:Sec
         
Время time измеряется в единицах timeunits миллисекунд, которое задается (в секундах) в переменной TimeUnit в секции [DAQ], например для измерения времени в минутах надо написать:
         [DAQ]
         TimeUnit = 60 ; Единица измерения времени time,[секунд]
         
Некая путаница связана с тем, что в конфигурации TimeUnit задается в секундах, а в программе timeunits заданы в миллисукундах. Это сделано потому, что в конфигурации секунды использовать удобнее, а в программе основной функцией измерения времени является все же msecnow.

Ноль времени timebase часов DAQ-системы, время msecnow в миллисекундах от Р.Х., время time и единицы измерения timeunits связаны соотношением:

          msecnow = timebase + time * timeunits
         

Данные, записываемые в кривые и отображаемые при работе системы на графиках, обычно измеряются по часам DAQ, через функцию time, так как это время наиболее удобно пользователю для наблюдения. Времена time обычно выражены в привычных для пользователя единицах (секунды, минуты, часы, сутки...). Работать с ними удобно, числа получаются разумные. Однако надо учитывать, что по времени DAQ нельзя узнать астрономическое (календарное) время без знания начала отсчета и единиц измерения. Поэтому данные разных сеансов работы DAQ, записанные в единицах времени time, трудно "сшивать", так как они отсчитаны от разных моментов времени. Правда, этот момент зафиксирован в паспорте кривой, но каждый раз сдвигать данные неудобно.

Время msecnow в миллисекундах от Р.Х. - более подходящая единица для сохранения данных в файлы, так как данные потом не надо сшивать, ведь они заданы в одной временной шкале. Однако пользователю работать с астрономическим временем не очень-то удобно, слишком большие числа получаются. Поэтому разумнее всего отображать данные в единицах time, а хранить в единицах msecnow.

Астрономическое время tms от Р.Х. связано с временем tDAQ по часам time формулой

          tms = timebase + tDAQ * timeunits
         

Пример:

          b:=putao(0,time,data);                               {запись данных в кривую}
          x:=getai_xn(0); y:=getai_yn(0);                      {извлечение данных из кривой}
          ms:=timebase+x*timeunits;                            {перевод времени кривой в ms}
          writeln(ms,' ',y);                                   {запись в файл}
         

Daq Pascal Api -> ms2year ms2month ms2day ms2hour ms2min ms2sec datetime2ms

Определение:

function ms2year(ms:Real):Integer
function ms2month(ms:Real):Integer
function ms2day(ms:Real):Integer
function ms2hour(ms:Real):Integer
function ms2min(ms:Real):Integer
function ms2sec(ms:Real):Integer
function datetime2ms(year,month,day,hour,min,sec,msec:Integer):Real

Аргументы:

  • ms - время, миллисекунд от Р.Х..
  • year,month,day,hour,min,sec,msec - календарная дата.

Результат:

Функции возвращают календарное время по астрономическому и наоборот.

Описание:

Функции служат для преобразования астрономического (линейного) времени по часам msecnow в календарное время и обратно. Назначение функций достаточно ясно выражается их названием:
  • ms2year - календарный год, 1 .. 9999
  • ms2month - календарный месяц, 1..12
  • ms2day - календарный день, 1..31
  • ms2hour - время суток, часы, 0..23
  • ms2min - время суток, минуты, 0..59
  • ms2sec - время суток, секунды, 0..59
  • datetime2ms - узнать астрономическое время по календарному

Пример:

          ms:=msecnow;
          writeln('Now ',ms2day(ms),'.',ms2month(ms),'.',ms2year(ms),'/',
                         ms2hour(ms),':',ms2min(ms),':',ms2sec(ms));
          {результат типа 12.6.2005/12:15:33}
         

Daq Pascal Api -> tm_new tm_free tm_gettime tm_start tm_stop tm_isstart tm_event tm_curint tm_numint tm_addint tm_getint tm_setint

Определение:

function tm_new:Integer
function tm_free(ref:Real):Boolean
function tm_gettime(ref:Real):Real
function tm_start(ref:Real):Boolean
function tm_stop(ref:Real):Boolean
function tm_isstart(ref:Real):Boolean
function tm_event(ref:Real):Boolean
function tm_curint(ref:Real):Integer
function tm_numint(ref:Real):Integer
function tm_addint(ref,int:Real):Boolean
function tm_getint(ref:Real;num:Integer):Real
function tm_setint(ref:Real;num:Integer;int:Real):Boolean

Аргументы:

  • ref - ссылка на таймер.
  • int - длительность интервала.
  • num - номер интервала.

Результат:

Группа функций для организации интервалов и периодических событий.

Описание:

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

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

Группа содержит 11 функций:

  • tm_new - функция создает таймер для организации периодических событий и возвращает ссылку на этот таймер. Вызов этой функции должен предшествовать вызовам всех других функций работы с таймером. Обычно таймер создается при старте программы и удаляется при ее остановке.
  • tm_free(ref) - удаление таймера, заданного ссылкой ref.
  • tm_gettime(ref) - время со старта таймера, заданного ссылкой ref, в миллисекундах.
  • tm_start(ref) - старт таймера, заданного ссылкой ref.
  • tm_stop(ref) - останов таймера, заданного ссылкой ref.
  • tm_isstart(ref) - позволяет узнать, был ли дан старт таймера, заданного ссылкой ref.
  • tm_event(ref) - событие таймера, заданного ссылкой ref. Эту функцию обязательно надо вызывать в цикле опроса. По окончании очередного интервала времени функция возвращает значение true. В этом и состоит событие. В этот момент можно что-то сделать...
  • tm_curint(ref) - номер текущего интервала времени таймера, заданного ссылкой ref. Отсчет интервалов ведется от нуля.
  • tm_numint(ref) - возвращает число интервалов таймера, заданного ссылкой ref.
  • tm_addint(ref,ms) - функция добавляет новый интервал длительности ms миллисекунд для таймера, заданного ссылкой ref.
  • tm_getint(ref,n) - возвращает длительность интервала номер n для таймера, заданного ссылкой ref. Отсчет интервалов ведется от нуля.
  • tm_setint(ref,n,ms) - изменить на значение ms миллисекунд интервал номер n для таймера, заданного ссылкой ref. Отсчет интервалов ведется от нуля.

Пример:

         program timer;
         var  tm:real; b:boolean;  n:integer;
         begin
          {start measure}
          if runcount=1 then begin
           writeln('Start timer test program');
           tm:=tm_new; {create new timer & add intervals 1,2 3 sec & start }
           if not( tm_addint(tm,1000) and
                   tm_addint(tm,2000) and
                   tm_addint(tm,3000) and
                   tm_start(tm) )
           then begin
            writeln('Timer failure!');
            b:=tm_free(tm);
            tm:=0;
           end;
          end else
          {stop measure}
          if isinf(runcount) then begin
           if tm<>0 then b:=tm_free(tm);
           tm:=0;
           writeln('Stop  timer test program');
          end else
          {polling}
          if tm<>0 then begin
           if tm_event(tm) then begin {event happened?}
            n:=tm_curint(tm);         {read interval №}
            {do something...}
            if n=0 then writeln('interval 0 finished');
            if n=1 then writeln('interval 1 finished');
            if n=2 then writeln('interval 2 finished');
           end;
          end;
         end.
         

Daq Pascal Api -> numais numaos numdis numdos

Определение:

function numais:integer
function numdis:integer
function numaos:integer
function numdos:integer

Аргументы:

Нет.

Результат:

Возвращает число аналоговых/цифровых входов/выходов устройства.

Описание:

Возвращает число аналоговых/цифровых входов/выходов устройства.
  • numais - Number of Analog Inputs, число аналоговых входов.
  • numdis - Number of Digital Inputs, число цифровых входов.
  • numaos - Number of Analog Outputs, число аналоговых выходов.
  • numdos - Number of Digital Outputs, число цифровых выходов.
Число входов и выходов устройства определяется его типом. Устройства - драйверы конкретного оборудования обычно имеют фиксированное число входов-выходов, которое определяется этим оборудованием. Например, устройство ADAM-7017 имеет 8 аналоговых выходов (значения АЦП). Программные устройства могут иметь различное число входов-выходов, в зависисмости от их конфигурации. Число аналоговых/цифровых входов/выходов задается для программных устройств переменными AnalogInputs, DigitalInputs, AnalogOutputs, DigitalOutputs в секции описания устройства. Например:
          [DataStorage]
          ai#0 = Curve 0 100 black 15 1
          ai#1 = Curve 0 100 black 15 1
          di#0 = Curve 0 100 black 15 1
          ao#0 = Curve 0 100 black 15 1
          do#0 = Curve 0 100 black 15 1
          [DeviceList]
          Adc = device software program
          [Adc]
          AnalogInputs = 2
          Link AnalogInput 0 with curve ai#0
          Link AnalogInput 1 with curve ai#1
          DigitalInputs = 1
          Link DigitalInput 0 with curve di#0 bit 0
          AnalogOutputs = 1
          Link AnalogOutput 0 with curve ao#0
          DigitalOutputs = 1
          Link DigitalOutput 0 with curve do#0
         

Пример:

Печать имен подключенных к входам-выходам кривых:
          for i:=0 to numais-1 do writeln('ai#',i,' -> ',crvname(refai(i)));
          for i:=0 to numdis-1 do writeln('di#',i,' -> ',crvname(refdi(i)));
          for i:=0 to numaos-1 do writeln('ao#',i,' -> ',crvname(refao(i)));
          for i:=0 to numdos-1 do writeln('do#',i,' -> ',crvname(refdo(i)));
         

Daq Pascal Api -> refai refao refdi refdo

Определение:

function refai(i:integer):integer
function refdi(i:integer):integer
function refao(i:integer):integer
function refdo(i:integer):integer

Аргументы:

  • i - номер аналогового/цифрового входа/выхода.

Результат:

Возвращает ссылку кривой, подключенной к аналоговому/цифровому входу/выходу i.

Описание:

Возвращает ссылку кривой, подключенной к аналоговому/цифровому входу/выходу номер i.
  • refai - AnalogInput, аналоговый вход, в диапазоне 0..numais-1.
  • refdi - DigitalInput, цифровой вход, в диапазоне 0..numdis-1.
  • refao - AnalogOutput, аналоговый выход, в диапазоне 0..numaos-1.
  • refdo - DigitalOutput, цифровой выход, в диапазоне 0..numdos-1.
Кривая должна быть объявлена в секции [DataStorage] и подключена директивой Link. Например:
          [DataStorage]
          ai#0 = Curve 0 100 black 15 1
          di#0 = Curve 0 100 black 15 1
          ao#0 = Curve 0 100 black 15 1
          do#0 = Curve 0 100 black 15 1
          [DeviceList]
          Adc = device software program
          [Adc]
          AnalogInputs = 1
          Link AnalogInput 0 with curve ai#0
          DigitalInputs = 1
          Link DigitalInput 0 with curve di#0 bit 0
          AnalogOutputs = 1
          Link AnalogOutput 0 with curve ao#0
          DigitalOutputs = 1
          Link DigitalOutput 0 with curve do#0
         

Пример:

          var ref,x,y:real;
          ref:=refai(0);
          x:=crvx(ref,1);
          y:=crvy(ref,1);
         

Daq Pascal Api -> putev putao putdo

Определение:

function putev(what,chan:integer;time,data,data1:real):boolean
function putao(chan:integer;time,data:real):boolean
function putdo(chan:integer;time,data:real):boolean

Аргументы:

  • what - битовые флаги, определяющие тип события.
  • chan - номер канала, т.е. аналогового или цифрового выхода.
  • time - время события.
  • data,data1 - данные события.

Результат:

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

Описание:

Записывает в FIFO устройства событие (what,chan,time,data,data1) для последующей обработки и записи в кривую, подключенную к аналоговому или цифровому выходу chan.

Сначала собственно описание функций упаковки событий.

putev(what,chan,time,data,data1) записывает в буфер FIFO устройства событие общего вида (what,chan,time,data,data1). Все остальные вызовы функций упаковки событий эквивалентны вызову этой функции с различными параметрами.

putao(chan,time,data) записывает событие (time,data) в аналоговый буфер FIFO, для последующей записи в кривую AnalogOutput[Chan]->(time,data). Эквивалентно вызову putev(4,chan,time,data,0). Вызов putao применяется обычно для записи аналоговых (непрерывных) сигналов.

putdo(chan,time,data) записывает событие (time,data) в цифровой буфер FIFO, для последующей записи в кривую DigitalOutput[Chan]->(time,data). Эквивалентно вызову putev(5,chan,time,data,0). Вызов putdo применяется обычно для записи цифровых (дискретных) сигналов.

Все три функции возвращают true при записи события в FIFO или false при переполнении FIFO. При переполнении FIFO также регистрируется ошибка DAQ. При обнаружении переполнения FIFO надо увеличить размер буферов FIFO в конфигурационном файле (переменные AnalogFifo, DigitalFifo).

Теперь немного теории.

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

При синхронной работе с кривой надо получить ссылку кривой (например, вызовом refai,..,reffind), и работать с кривой через функции типа crvx, crvy, crvput и т.д. При этом работа с кривой идет напрямую, а для корректной работы в многопоточной среде кривую надо к тому же блокировать и разблокировать (crvlock/crvunclock). Кроме того, надо самостоятельно заботиться об отображении кривой на экране после ее обработки. Правда, синхронная работа с кривой идет без задержек буферизации, а обработка делается в точности так, как задано в прикладной программе. Поэтому синхронная работа с кривой применяется при обработке больших блоков данных, а также при сложной или нестандартной обработке с кривой. Однако для стандартной, рутинной обработки данных, полученных в процессе измерений, синхронная обработка слишко громоздка.

Асинхронный стиль работы связан с механизмом событий, а также их буферизацией и диспетчеризацией. Механизм событий - упаковка каждого факта измерений в некую стандартную запись для последующей буферизации и диспетчеризации. Буферизация заключается в том, что запись данных в кривую идет не напрямую, сразу в момент измерений, а накапливается в буфере FIFO в виде событий для последующей асинхронной обработки. Событие, как правило, соответствует факту измерения сигнала, то есть получению известного значения y в определенный момент времени x. Каждое устройство имеет два буфера FIFO - аналоговый и цифровой, размер которых (в элементах) задаются переменными AnalogFifo, DigitalFifo в секции описания устройства. Диспетчеризация - это процедура, которая периодически обращается к FIFO, извлекает оттуда данные, выполняет их обработку по некоторому стандартному алгоритму, параметры которого задаются в конфигурации и затем записывает обработанные данные в кривые. Достоинство такого подхода в том, что при этом не надо заботиться о рутинных процедурах типа блокировки кривой, стандартной обработки данных и т.д. Кроме того, при асинхронной обработке обновление данных на экране идет автоматически. Асинхронная обработка обычно применяется для добавления в кривые небольших порций данных, в процессе измерений. Надо также отметить, что буферизация идет в измерительном потоке, при вызове функций упаковки событий, а диспетчеризация и отображение данных идут в других программных потоках, поэтому функции упаковки событий всегда работают быстро и не приводят к блокировке измерительного потока.

Необходимость введения механизма событий связана с тем, что набор данных может идти в разных режимах:

  • программном
  • по аппаратным прерываниям
  • в режиме прямого доступа к памяти (DMA)
Скорость регистрации данных в режиме прерываний или DMA определяется аппаратурой и может быть более высокой, хотя бы локально, чем скорость обработки. Во всяком случае, синхронная (по мере поступления данных) обработка при сборе данных по прерываниям и DMA, как правило, невозможна. Буфер fifo, в котором накапливаются события, позволяет сделать процессы регистрации и обработки данных асинхронными и одинаковыми для всех режимов набора. В самом деле, драйвер при регистрации данных просто упаковывает данные в fifo, не заботясь об их дальнейшей обработке, а диспетчер событий из ядра CRW-DAQ асинхронно читает события из fifo и обрабатывает их, не заботясь о том, откуда они взялись. Неизбежной платой за наличие fifo является то, что обработка данных может отставать от набора, что ухудшает время реакции систем с обратной связью. Поэтому для реализации высокоскоростных (с реакцией меньше 100 миллисекунд) систем управления с обратной связью необходимо регистрацию, анализ и управление (обратную связь) делать в рамках одного устройства CRW-DAQ, работающего по прерываниям, чтобы избежать задержек в fifo. Такая возможность в системе CRW-DAQ есть, хотя в большинстве задач, для которых создавалась программа, это излишне, так как для них реакция 100 миллисекунд вполне достаточна.

Таким образом, сбор данных в системе CRW-DAQ обычно построен на механизме событий. Это значит, что данные по мере поступления обычно не сразу записываются в кривые, а упаковываются в некоторые записи - события и буферизуются через FIFO. Событие имеет структуру, понятную из его описания, взятого из исходного текста программы, из файла _daqevnt.pas, входящего в дистрибутив:

         TDaqEvent=record      {запись - событие DAQ}
          What   : Integer;    {класс события}
          Chan   : Integer;    {канал события}
          Time   : Double;     {время события}
          Data   : Double;     {данные события (основные)}
          Data1  : Double;     {данные события (дополнительные)}
         end;
         
Событие можно обозначать кратко как запись (what,chan,time,data,data1).

Класс события What содержит слово, биты которого влияют на его обработку и диспетчеризацию. Биты имеют такой смысл:

  • 1 = Бит 0 - Значение 0/1 означает, что событие аналоговое/цифровое.
    В соответствии с этим битом событие передается в аналоговый/цифровой буфер FIFO. При диспетчеризации событие извлекается из FIFO и записывается в аналоговый/цифровой выход устройства.
  • 2 = Бит 1 - Значение 0/1 означает, что событие нормальное/критическое.
    Критические события всегда добавляются в кривую, независимо от их времени и данных. Нормальные события могут подвергаться сжатию и поэтому не добавляться в кривую в целях уменьшения объема хранимых данных. Например, если нормальное событие имеет такое же время и значение, как предыдущее событие, оно будет проигнорировано.
  • 4 = Бит 2 - Значение 0/1 означает, что событие нельзя/можно сжимать.
    Сжатие данных – это их округление до требуемой точности, применяемое для уменьшения объемов хранимых данных. При сжатии, после добавления очередной точки кривой, анализируются три последние точки. Если все три точки имеют одинаковое значение ординаты, предпоследняя точка удаляется из кривой, так как она не несет полезной информации о сигнале. Если сжатие запрещено, удаления незначимых точек не делается.
  • 8 = Бит 3 - Значение 0/1 означает, что событие динамическое/спектрометрическое. Динамическое событие означает, что надо добавить в конец кривой точку (time,data) и применяется для описания зависимостей величин от времени. Значение data1 в этом случае не используется.
    Спектрометрическое событие означает, что спектрометрический канал с номером data надо инкрементировать на величину data1 и применяется для накопления статистических данных в спектрометрии. Значение time в этом случае не используется.

Канал события Chan влияет на диспетчеризацию события. Фактически, это номер аналогового/цифрового выхода, куда передается событие при диспетчеризации.

Время события Time фиксирует момент, когда оно зарегистрировано. Это время необходимо, ведь за счет буферизации время обработки события может не совпадать со временем его возникновения, особенно если события генерирует аппаратный драйвер. Обычно время извлекается функцией time. Во всяком случае, время динамического события должно изменяться монотонно. Это значит, что при записи ряда событий время каждого события должно быть больше предыдущего. Алгоритм диспетчеризации существенно использует условие монотонности времени. Если какое-то событие нарушает условие монотонности времени, оно игнорируется.

Данные Data,Data1 зависят от класса события, как описано в поле What. Для динамических событий Data - это значение сигнала в момент Time. Для спектрометрических событий Data - это канал, Data1 - приращение в канале.

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

Пример:

          {Упаковка данных АЦП}
          x:=time;
          y:=GetADC;
          b:=putao(0,x,y); {эквивалентно b:=putev(4,0,x,y,0);}

          {Упаковка данных DI}
          x:=time;
          y:=GetDI;
          b:=putdo(0,x,y); {эквивалентно b:=putev(5,0,x,y,0);}

          {Упаковка события спектрометрии: инкремент канала y на 1 и запись в AnalogOutput 1}
          x:=time;
          y:=GetChan;
          b:=putao(8,1,x,y,1);
         

Daq Pascal Api -> calibr numcals refcalibr

Определение:

function numcals:integer
function refcalibr(i:integer):integer
function calibr(i:integer;dat,par:real):integer

Аргументы:

  • i - номер калибровки.
  • dat - данные для преобразования по калибровке.
  • par - параметр для преобразования по калибровке.

Результат:

Функции работы с калибровками.

Описание:

Функции работы с калибровками.

Функция numcals возвращает число калибровок данного устройства. Калибровки имеют номера в диапазоне 0..numcals-1.

Функция refcalibr(i) возвращает ссылку на калибровку номер i или ноль, если калибровки с таким номером нет. Сама калибровка задается в конфигурации устройства ссылкой на файл калибровки (*.cal), в котором содержится массив точек калибровки, а также описание метода аппроксимации этих точек.

Функция calibr(i,dat,par) преобразует данные dat с параметром par по калибровке номер i. Например, если dat - термоЭДС термопары, par - температура холодного спая, а калибровка - термопарная таблица, то функция вернет температуру в градусах.

Теперь немного теории.

Каждое устройство может иметь массив калибровок. Число калибровок устройства задается типом устройства, а для программных устройств - переменной Calibrations в секции описания устройств.

Калибровка - некоторое преобразование данных, например, переводящее милливольты АЦП тензодатчика в атмосферы давления. От обычной функции калибровка отличается тем, что она определяется не столько аналитически (формулой), сколько эмпирически (набором точек калибровки). Обычно используется та или иная аппроксимация массива точек калибровки некоторой параметрической функцией по методу наименьших квадратов (МНК).

Начнем с понятия точек калибровки. Пусть имеется величина x, измеряемая непосредственно, например, данные АЦП. Пусть также известно, что величина y зависит только от x и, возможно, параметра z, который также измеряется либо считается известным (параметр может и отсутствовать). Обычно предполагается, что зависимость y от x,z может быть описана некоторой параметрической функцией y=Fy(x,z,p) с неизвестными параметрами p, которые определяются процедурой МНК. Для определения функции Fy делается калибровочное измерение. Для этого проводится ряд измерений величины x, при этом величина y одновременно измеряется альтернативным методом, который считается достоверным и достаточно точным. Следует подчеркнуть этот принципиальный момент: калибровка всегда основана на применении альтернативного метода измерения (сравнения с эталоном). Если эталонный метод неточен, калибровка тоже будет неточной. Вторым принципиальным моментом является то, что калибровочные измерения должны охватывать как можно более широкий диапазон значений величины y. Это не всегда осуществимо, однако сужение диапазона точек калибровки снижает ее точность. Результатом калибровочного измерения является массив точек (xi,yi,zi),i=1..N, причем значения xi и yi измерены одновременно, xi - непосредственно, а yi - альтернативным методом. Значения параметра zi, если он есть, считаются известными или измеряются непосредственно. Это и есть массив точек калибровки.

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

Рассмотрим пример конфигурирования устройства, которое преобразует милливольты термоЭДС из кривой mvT1, подключенной к 0 входу устройства и температуру холодного спая TCJ с 1 входа с использованием калибровки с номером 0.

          [DeviceList]
          &T_CONV = device software program
          [&T_CONV]
          Comment       = Термопарные преобразования
          InquiryPeriod = 10
          DevicePolling = 10, tpNormal
          ProgramSource = ..\daqpas\_mv2pu.pas
          AnalogInputs  = 2
          AnalogOutputs = 1
          Calibrations  = 1
          Link AnalogInput  0  with curve mvT1
          Link AnalogInput  1  with curve TCJ
          Link AnalogOutput 0 with curve T1 tolerance 0.05 0.001 history 100
          Calibration#0 = ..\calibr\hral.cal U(mV) T(C) Tcj Line HrAl 0 10
         
Здесь
  • Calibration#0 - описание калибровки с индексом 0
  • ..\calibr\hral.cal - ссылка на файл калибровки,
  • U(mV) - имя (название) аргумента (непосредственно измеряемой величины) x
  • T(C) - имя (название) значения (зависимой физической величины) y
  • Tcj - имя (название) параметра (в данном случае температуры холодного спая) z
  • Line HrAl - тип шкалы TransformX, TransformY по умолчанию
  • 0 10 - диапазон аргумента по умолчанию
Файл hral.cal содержит следующее описание:
          [U(mV)-T(C) calibration] ; Заголовок калибровки
          FitMethod = Polynom      ; Метод аппроксимации
          TransformX = Line        ; Линеаризующее преобразование по X
          TransformY = HRAL        ; Линеаризующее преобразование по Y
          Power = 1                ; Степень полинома
          Center = 0               ; Сдвиг
          Scale = 1                ; Масштаб
          Bounds = -7 55           ; Диапазон изменения аргумента
          Data      U(mV)       T(C)     Weight        Tcj
                   -6.458       -270          1          0
                        0          0          1          0
                   54.875       1372          1          0
          End Data
          Notice Text
          Калибровка хромель-алюмель
          End Notice Text
         
Здесь описание калибровки помещено в секцию [U(mV)-T(C) calibration]. Обратите внимание, что имена аргумента U(mV) и значения T(C) входят в имя секции. Переменная FitMethod задает метод аппроксамации, в данном случае это полиномиальная аппроксимация по методу наименьших квадратов. Переменные TransformX, TransformY задают линеаризующие преобразования, в данном случае Line означает тождественное преобразование, HRAL - преобразование по термопарной таблице хромель-алюмель. Переменная Power задает степень полинома, используемого для аппроксимации. Переменные Center, Scale задают линейное преобразование аргумента t=(x-Center)/Scale, которое применяется для нормализации диапазона аргумента. Обычно они выбираются так, чтобы новая переменная менялась в диапазоне [0..1]. Далее, в разделе Data ... End Data содержится таблица точек калибровки. Таблица состоит из столбцов. Столбцы должны идти в том же порядке, как названия соответствующих величин U(mV), T(C), Weight, Tcj. Здесь Weight - статистический вес точки.

Аппроксимация делается так. Сначала к массиву точек (xi,yi,zi) применяются линеаризующие преобразования по X, Y: x'i=Tx(xi,zi), y'i=Ty(yi,zi). Предполагается, что после этих преобразований линеаризованные переменные x', y' зависят друг от друга примерно линейно и могут быть описаны полиномом некоторой степени. Затем линеаризованная переменная x' преобразуется в нормализованную переменную t'=(x'-Center)/Scale. Затем массив точек (t',y') аппроксимируется полиномом p(t') степени Power по МНК. Эти коэффициенты запоминаются для дальнейших вычислений.

При вычислении калибровки calibr(0,x,z) аргумент x преобразуется в t'=(Tx(xi,zi)-Center)/Scale. Затем вычисляется y'=p(t'). Затем применяется обратное линеаризующее преобразование y=T-1y(y',z).

Пример:

          {чтение АЦП, преобразование по калибровке и запись события}
          if refcalibr(0)<>0 then begin
           t:=time;
           dat:=getADC;
           par:=getColdJunktion;
           y:=calibr(0,dat,par);
           b:=putao(0,t,y);
          end;
         

Daq Pascal Api -> getai getdi getai_n getdi_n getai_xn getdi_xn getai_yn getdi_yn getai_xi getdi_xi getai_yi getdi_yi getai_par getao_par getdi_par getdo_par

Определение:

function getai(n:integer;t:real):real
function getdi(n:integer;t:real):real
function getai_n(n:integer):real
function getdi_n(n:integer):real
function getai_xn(n:integer):real
function getdi_xn(n:integer):real
function getai_yn(n:integer):real
function getdi_yn(n:integer):real
function getai_xi(n:integer;i:real):real
function getdi_xi(n:integer;i:real):real
function getai_yi(n:integer;i:real):real
function getdi_yi(n:integer;i:real):real
function getai_par(n:integer;id:integer):real
function getao_par(n:integer;id:integer):real
function getdi_par(n:integer;id:integer):real
function getdo_par(n:integer;id:integer):real

Аргументы:

  • t - время, в единицах timeunits.
  • n - номер входа или выхода, начиная с 0.
  • i - индекс точки кривой, начиная с 1.
  • id - идентификатор параметра, см. описание ниже.

Результат:

Группа функций для работы с входами и выходами устройства.

Описание:

Группа функций для работы с входами и выходами устройства CRW-DAQ. Каждое устройство имеет аналоговые и цифровые входы и выходы, то есть массивы кривых, используемые для чтения или записи аналоговых или цифровых данных. Каждый вход или выход имеет также набор атрибутов, которые позволяют выполнять стандартную обработку данных (сглаживание, округление, ограничение истории и т.д.).

getai(n,t) - аппроксимация значения кривой c аналогового входа номер n в момент времени t. Аналоговые входы ориентированы на обработку непрерывных сигналов, поэтому при аппроксимации используется сглаживание, указанное в файле конфигурации в конструкции

         Link AnalogInput n with curve c smoothing w p k1 k2
         
Здесь
  • w - полуширина окна сглаживания, в единицах timeunits,
  • p - метод сглаживания
    • -1 = взвешенное интегральное среднее
    • 0 = взвешенное среднее
    • 1..9 = подгонка полиномом степени p взвешенным МНК.
  • k1,k2 - задают веса, то есть ядро сглаживания (1-xk1)k2.
Если нет сглаживания, используется интерполяция кусочно-линейной функцией.

getdi(n,t) - аппроксимация значения кривой с цифрового входа номер n в момент времени t. В отличие от getai, в getdi используется интерполяция кусочно-постоянной функцией, так как цифровые входы ориентированы на дискретные сигналы.

getai_n(n) - возвращает число точек кривой, подключенной к аналоговому входу номер n. Возвращает ноль, если нет кривой или точек.

getdi_n(n) - возвращает число точек кривой, подключенной к цифровому входу номер n. Возвращает ноль, если нет кривой или точек.

getai_xn(n) - координата x последней точки кривой, подключенной к аналоговому входу номер n. Возвращает ноль, если нет кривой или точек на ней.

getdi_xn(n) - координата x последней точки кривой, подключенной к цифровому входу номер n. Возвращает ноль, если нет кривой или точек на ней.

getai_yn(n) - координата y последней точки кривой, подключенной к аналоговому входу номер n. Возвращает ноль, если нет кривой или точек на ней.

getdi_yn(n) - координата y последней точки кривой, подключенной к цифровому входу номер n. Возвращает ноль, если нет кривой или точек на ней.

getai_xi(n,i) - координата x точки номер i кривой, подключенной к аналоговому входу номер n. Возвращает ноль при отсутствии кривой или ошибке диапазона индекса.

getai_xi(n,i) - координата x точки номер i кривой, подключенной к цифровому входу номер n. Возвращает ноль при отсутствии кривой или ошибке диапазона индекса.

getai_yi(n,i) - координата y точки номер i кривой, подключенной к аналоговому входу номер n. Возвращает ноль при отсутствии кривой или ошибке диапазона индекса.

getai_yi(n,i) - координата y точки номер i кривой, подключенной к цифровому входу номер n. Возвращает ноль при отсутствии кривой или ошибке диапазона индекса.

getai_par(n,id) - возвращает значение параметра аналогового входа номер n с идентификатором id:

  1. len - длина кривой
  2. x1 - абсцисса первой точки кривой
  3. y1 - ордината первой точки кривой
  4. xn - абсцисса последней точки кривой
  5. yn - ордината последней точки кривой
  6. color - цветовой код кривой
  7. style - код стиля кривой
  8. limits.a.x - нижняя граница данных кривой по абсциссе
  9. limits.a.y - нижняя граница данных кривой по ординате
  10. limits.b.x - верхняя граница данных кривой по абсциссе
  11. limits.b.y - верхняя граница данных кривой по ординате
  12. w - полуширина окна сглаживания, см. описание getai()
  13. p - метод сглаживания, см. описание getai()
  14. k1 - параметр ядра сглаживания, см. описание getai()
  15. k2 - параметр ядра сглаживания, см. описание getai()
Если задать другие значения id, функция вернет значение _NaN.

getao_par(n,id) - возвращает значение параметра аналогового выхода номер n с идентификатором id:

  1. len - длина кривой
  2. x1 - абсцисса первой точки кривой
  3. y1 - ордината первой точки кривой
  4. xn - абсцисса последней точки кривой
  5. yn - ордината последней точки кривой
  6. color - цветовой код кривой
  7. style - код стиля кривой
  8. limits.a.x - нижняя граница данных кривой по абсциссе
  9. limits.a.y - нижняя граница данных кривой по ординате
  10. limits.b.x - верхняя граница данных кривой по абсциссе
  11. limits.b.y - верхняя граница данных кривой по ординате
  12. history - длина истории (предел на число точек кривой)
  13. analogfifo - размер буфера аналоговых событий
  14. abstol - абсолютный порог округления
  15. reltol - относительный порог округления
Если задать другие значения id, функция вернет значение _NaN.

getdi_par(n,id) - возвращает значение параметра цифрового входа номер n с идентификатором id:

  1. len - длина кривой
  2. x1 - абсцисса первой точки кривой
  3. y1 - ордината первой точки кривой
  4. xn - абсцисса последней точки кривой
  5. yn - ордината последней точки кривой
  6. color - цветовой код кривой
  7. style - код стиля кривой
  8. limits.a.x - нижняя граница данных кривой по абсциссе
  9. limits.a.y - нижняя граница данных кривой по ординате
  10. limits.b.x - верхняя граница данных кривой по абсциссе
  11. limits.b.y - верхняя граница данных кривой по ординате
  12. bit - номер бита, ассоциированный с входом
  13. inverted - 1/0 - флаг инверсии бита, ассоциированного с входом
Если задать другие значения id, функция вернет значение _NaN.

getdo_par(n,id) - возвращает значение параметра цифрового входа номер n с идентификатором id:

  1. len - длина кривой
  2. x1 - абсцисса первой точки кривой
  3. y1 - ордината первой точки кривой
  4. xn - абсцисса последней точки кривой
  5. yn - ордината последней точки кривой
  6. color - цветовой код кривой
  7. style - код стиля кривой
  8. limits.a.x - нижняя граница данных кривой по абсциссе
  9. limits.a.y - нижняя граница данных кривой по ординате
  10. limits.b.x - верхняя граница данных кривой по абсциссе
  11. limits.b.y - верхняя граница данных кривой по ординате
  12. history - длина истории (предел на число точек кривой)
  13. figitalfifo - размер буфера цифровых событий
Если задать другие значения id, функция вернет значение _NaN.

Пример:

          if getai_n(0)>0 then begin
           x:=getai_xn(0);
           y:=getai_yn(0);
           b:=putao(0,x,y);
          end;
         

Daq Pascal Api -> aimap dimap aomap domap diword

Определение:

function aimap(i,n:integer):real
function dimap(i,n:integer):real
function aomap(i,n:integer):real
function domap(i,n:integer):real
function diword(i,n:integer):real

Аргументы:

  • i - начальный номер аналогового/цифрового входа/выхода, начиная с 0.
  • n - число входов/выходов.

Результат:

Группа битовых функций для работы с входами и выходами устройства.

Описание:

Группа битовых функций для работы с входами и выходами устройства CRW-DAQ. Каждое устройство имеет аналоговые и цифровые входы и выходы, то есть массивы кривых, используемые для чтения или записи аналоговых или цифровых данных. Битовые функции позволяют быстро узнать, к каким входам подключены кривые, а также сформировать цифровые данные из отдельных битов.

aimap(i,n) - битовая карта наличия кривых на аналоговых входах (i…i+n-1). Соответствующий бит номер j выставлен, если к входу i+j подключена кривая. Функция применяется для быстрой проверки наличия подключенных к аналоговым входам кривых.

dimap(i,n) - битовая карта наличия кривых на цифровых входах (i…i+n-1). Соответствующий бит номер j выставлен, если к входу i+j подключена кривая. Функция применяется для быстрой проверки наличия подключенных к цифровым входам кривых.

aomap(i,n) - битовая карта наличия кривых на аналоговых выходах (i…i+n-1). Соответствующий бит номер j выставлен, если к выходу i+j подключена кривая. Функция применяется для быстрой проверки наличия подключенных к аналоговым выходам кривых.

domap(i,n) - битовая карта наличия кривых на цифровых выходах (i…i+n-1). Соответствующий бит номер j выставлен, если к выходу i+j подключена кривая. Функция применяется для быстрой проверки наличия подключенных к цифровым выходам кривых.

diword(i,n) - битовая карта входных битов (i..i+n-1). Эта функция рассматривает цифровые данные на входе устройства как массив битов, формирующийся из отдельных битов кривых, подключенных к цифровым входам. Если конфигурация устройства содержит конструкцию

          Link DigitalInput i with curve с bit j           или
          Link DigitalInput i with curve с inverted bit j
         
то для формирования бита номер i берется последняя точка кривой c, округляется до 32-битного целого и извлекается бит номер j этого числа. Обратите внимание, что функция getdi_yn(i) позволяет получать доступ ко всем битам последней точки кривой c, в то время как diword использует только один бит из каждой кривой. Основное применение функции – быстрое формирование логических сигналов из отдельных битов входных данных. Во многих случаях это позволяет формировать логические сигналы без дополнительного программирования.

Пример:

          if dimap(0,1)>0 then ...             к DigitalInput 0 подключена кривая
          if dimap(0,16)>0 then ...            к DigitalInput 0..15 подключены кривые
          i:=outport(addr,dimap(diword(8,8))); вывод в порт байта (8 бит) с входов 8..15
         

Daq Pascal Api -> fixerror registererr geterrcount

Определение:

function fixerror(n:integer):boolean
function registererr(s:string:)integer
function geterrcount(n:integer):real

Аргументы:

  • n - номер ошибки.
  • s - строка, идентифицирующая ошибку.

Результат:

Группа функций для регистрации ошибок устройства.

Описание:

Группа функций для регистрации ошибок устройства CRW-DAQ. Каждое устройство имеет массив счетчиков ошибок. Ошибка каждого типа имеет номер (код) для идентификации и строку для отображения. При возникновении ошибок в программе (например, сбои связи, ошибки ввода-вывода) программа может фиксировать ошибки, при этом счетчик ошибок данного типа увеличивается. Это позволяет оповещать пользователя об ошибках, не прерывая работу программы. В окне

DAQ-СИСТЕМА есть индикаторы состояния системы:

  • Когда все в порядке, нормальное состояние кнопки зеленое. Это значит, не было зарегистрировано новых ошибок.
  • Если в DAQ системе возникает ошибка, состояние меняется на желтое. Это состояние длится одну секунду, и если новых ошибок не возникло, оно переходит в красное.
  • Если новых ошибок не возникает, через секунду после последней ошибки состояние переходит в красное.
Чтобы сбросить красное состояние, надо нажать кнопку. При этом она возвращается в зеленое состояние, а в консольное окно помещается список ошибок. Таким образом, зеленая кнопка означает, что со времени последнего нажатия кнопки ошибок DAQ не было. Красная означает, что ошибки когда-то были, но потом прекратились. Желтая означает что ошибки сыплются прямо сейчас.

fixerror(n) - фиксирует ошибку номер n. Номер n лежит в диапазоне 0…255. Вызывается для оповещения пользователя об ошибке в программе, чтобы можно было анализировать работу программы.

registererr(s) - регистрирует в системе новый вид ошибок с текстом сообщения s. Возвращает код зарегистрированной ошибки, который можно использовать в функции fixerror. При запуске программы следующие коды ошибок регистрируются автоматически:

  • 0 - Переполнение fifo аналоговых событий
  • 1 - Переполнение fifo цифровых событий
  • 2 - Ошибка диспетчера аналоговых событий
  • 3 - Ошибка диспетчера цифровых событий
  • 4 - Синтаксическая ошибка в формуле
  • 5 - Watchdog: Host failure
  • 6 - Аппаратная ошибка при старте
  • 7 - Неверный спектрометрический канал
  • 8 - Ошибка при старте программы DAQ PASCAL
  • 9 - Ошибка при выполнения программы DAQ PASCAL
  • 10 - Ошибка устройства-диалога
  • 11 - Ошибка ввода-вывода pipe
  • 12 - Переполнение FIFO pipe
  • 13 - Неверный тип тега Get/SetTag
  • 14 - Проблемы при загрузке конфигурации
  • 100 - Ошибка формата сообщений ADAM
  • 101 - TimeOut при опросе ADAM
  • 102 - Неверный запрос ADAM
  • 103 - Ошибка при старте ADAM
  • 104 - Ошибка контрольной суммы ADAM
  • 105 - Ошибка инициализации DMA
  • 106 - Переполнение fifo буферов DMA
  • 107 - Ошибка чтения кода ADC
  • 108 - Перегрузка Camac по прерываниям
  • 109 - Переполнение счетчика
  • 110 - Перегрузка АЦП по входу
  • 111 - Ошибка мультиплексора
  • 255 - Неизвестная ошибка

geterrcount(n) - возвращает счетчик ошибок с кодом n. В зависимости от кода n:

  • n = -2 : счетчик ошибок всех типов для всех устройств.
  • n = -1 : счетчик ошибок всех типов для данного устройства.
  • n = 0…255 : счетчик ошибок данного устройства с кодом n.
  • n = 256…511 : счетчик ошибок всех устройств с кодом n-256.

Пример:

           if runcount=1 then begin
            errorcode:=registererr(devname);
           end else begin
            if ErrorFound then b:=fixerror(errorcode);
            writeln('Total errors:',geterrcount(-2));
            writeln('Device errors:',geterrcount(errcorcode));
           end;
         

Daq Pascal Api -> voice

Определение:

function voice(s:integer):boolean

Аргументы:

  • s - сообщение для озвучивания.

Результат:

Звуковое сообщение.

Описание:

Функция предназначена для вывода звукового сообщения, заданного стокой s.

Функция возвращает false, если очередь сообщений переполнена.

Строка s должна содержать список сообщений, которые будут озвучены. Каждое сообщение - это либо целое число в диапазоне -999999..+999999, либо имя библиотечного *.wav файла. Имена файлов указываются без расширения и пути. Числа также автоматически преобразуются в набор библиотечных *.wav файлов. Библиотечные *.wav файлы системы CRW-DAQ задаются в Crw32.ini [System] SoundLibrary. Библиотечные *.wav файлы конкретной DAQ системы задаются в секции [DAQ] SoundLibrary. Эти переменные ссылаются на каталог, где находятся *.wav файлы.

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

В окне

DAQ-СИСТЕМА есть индикатор звука: . Он позволяет включать/выключать озвучивание сообщений DAQ.

Командой в панели управления CRW-DAQ в верхней части окна программы можно вызвать окно Управление звуком. В нем можно отключить звук, а также посмотреть и попробовать библиотеку звуков.

Пример:

          if clickbutton>0 then begin
           if IsSameText(clicksensor,'Button1') then b:=voice('click 1');
           if IsSameText(clicksensor,'Button2') then b:=voice('click 2');
          end;
         

Daq Pascal Api -> action clear cleardevice start stop clearcurve savecrw

Определение:

function action(s:integer):boolean
function clear(s:integer):boolean
function cleardevice(s:integer):boolean
function start(s:integer):boolean
function stop(s:integer):boolean
function clearcurve(s:integer):boolean
function savecrw(s:integer):boolean

Аргументы:

  • s - строка аргументов.

Результат:

Группа функций для работы с устройствами.

Описание:

Группа функций для работы с устройствами CRW-DAQ.

action(s) - вызов метода action для списка устройств, заданного в строке s в виде названий устройств. Конкретное, действие action зависит от типа устройства. Например, устройства Dialog, ConstGenerator, BitSetGenerator отображают диалог на экране, а устройства других типов ничего не делают.

clear(s) - вызов метода clear для списка устройств, заданного в строке s в виде названий устройств. Метод clear в основном относится к устройствам – драйверам и должен делать очистку устройства. Для обычных устройств этот метод ничего не делает.

cleardevice(s) - вызов метода cleardevice для списка устройств, заданного в строке s в виде названий устройств. Метод cleardevice в основном относится к устройствам – драйверам и должен делать аппаратную очистку устройства. Для обычных устройств этот метод ничего не делает.

start(s) - вызов метода start для списка устройств, заданного в строке s в виде названий устройств. Этот метод приводит к началу работы устройства, если оно еще не работает. Функция считается устаревшей, не рекомендуется ее использовать. Вместо этого рекомендуется использовать сообщения devmsg, devsend.

stop(s) - вызов метода stop для списка устройств, заданного в строке s в виде названий устройств. Этот метод приводит к остановке работы устройства, если оно еще работает. Функция считается устаревшей, не рекомендуется ее использовать. Вместо этого рекомендуется использовать сообщения devmsg, devsend.

clearcurve(s) - очистка кривых, заданных в виде списка имен в строке s. Для динамических кривых очистка приводит к удалению точек кривой, а для статических кривых (то есть спектров) – к обнулению значений y всех точек кривой. В списке можно также задавать длину истории для последующих кривых. Например:

b:=clearcurve('100,crv1,200,crv2,crv3');
Следует иметь в виду, что функция выпролняется асинхронно, в другом потоке программы, а не сразу. Функция считается устаревшей, не рекомендуется ее использовать. Вместо этого рекомендуется использовать crvdel.

savecrw(s) - выполняет сохранение кривых в архивный файл формата *.CRW. Строка s имеет формат s='fname winname wintitle winlable curvelist'.

  • fname - имя файла crw-архива, можно без пути и без расширения. По умолчанию расширение *.crw, а пути задан переменной [DAQ] DataPath.
  • winname - имя окна, то есть имя, под которым оно попадет в crw-архив.
  • wintitle - заголовок в верхней части окна.
  • winlable - метка в нижней части окна.
  • curvelist - список имен сохраняемых кривых.
Каждое из выражений fname,winname,wintitle,winlable должно быть одним словом, а для задания wintitle,winlable можно применять символы форматирования ^L,^R,^C. Кроме того, любое из выражений fname,winname,wintitle,winlable,curvelist может быть именем секции, то есть помещенным в квадратные скобки именем секции конфигурационного файла. В этом случае предполагается, что значения выражений будут прочитаны из переменных FileName,Name,AxisY,AxisX,CurveList этой секции соответственно, причем в этом случае список кривых, заданный в секции, может быть произвольной длины. Например:
          If not savecrw('c:\daq\data.crw Data_Window ^CTitle^N^L_Y_Units ^RX_Units_^N^CLabel [CurvesToSave]')
          then writeln('Error!');

          [CurvesToSave]
          CurveList = curve1, curve2, curve3
         

Пример:

          if clicksensor='PRESET' then b:=action('&PresetDialog'); {вызов диалога}
         

Daq Pascal Api -> irqinit irqfree isisrnow

Определение:

function irqinit(irq:integer):boolean
function irqfree:boolean
function isisrnow:boolean

Аргументы:

  • irq - номер прерывания.

Результат:

Работа с прерываниями.

Описание:

В версии CRW32 обработка прерываний запрещена, в отличие от версии CRW16 для DPMI. Для совместимости версий функции обработки прерываний оставлены, но заглушены (всегда возвращают false.)

В версии CRW16 функции позволяют выполнять обработку прерываний.

irqinit(irq) устанавливает обработчик прерывания номер irq, в частности, 0 - прерывание таймера, каждые 55 ms. При этом программа активизируется только при возникновении прерывания.

irqfree - снимает обработчик прерывания. При этом программа вызывается в обычном цикле опроса.

isisrnow - возвращает true, если программа вызвана из обработчика прерывания. В обработчике прерывания надо действовать осторожно, чтобы не "повесить" программу. В частности, все файловые или другие длительные операции должны быть вынесены за пределы обработчика. Допустимы только асинхронные вызовы, такие как putev или echo.

Пример:

          if runcount=1 then begin
           if not irqinit(0) then writeln('Error!');
          end else
          begin
           if isrnow then b:=echo('IRQ 0 Runcount=',runcount);
          end;
         

Daq Pascal Api -> crvfind

Определение:

function crvfind(s:string):real

Аргументы:

  • s - имя искомой кривой.

Результат:

Возвращает ссылку кривой по имени s.

Описание:

Возвращает ссылку кривой по имени s в базе данных CRW-DAQ. Возвращает _nil, если кривой с таким именем не найдено.

Возможно также задание для поиска в виде s=name win,
где name - имя кривой, win - имя окна. Если win = *, то поиск идет в активном окне. Если win = **, то поиск в самом верхнем окне для кривых. Если name = *, то берется активная кривая в окне.

Кривая должна быть объявлена в секции [DataStorage]. Например:

          [DataStorage]
          ai#0 = Curve 0 100 black 15 1

          ref:=crvfind('ai#0');
         
Результат имеет тип real, так как на старых системах у типа integer был слишком малый диапазон. В текущей системе в этом нет необходимости, однако для совместимости тип сохранен.

Пример:

          var ref,x,y:real;
          ref:=crvfind('temperature');
          x:=crvx(ref,crvlen(ref));
          y:=crvy(ref,crvlen(ref));
         

Daq Pascal Api -> crvname

Определение:

function crvname(ref:real):string

Аргументы:

  • ref - ссылка на кривую.

Результат:

Возвращает имя кривой по ссылке ref.

Описание:

Возвращает имя кривой по ссылке ref. Аналогичный результат дает refinfo(ref,'Name'). Ссылка имеет тип real, так как на старых системах у типа integer был слишком малый диапазон. В текущей системе в этом нет необходимости, однако для совместимости тип сохранен.

Пример:

          c:=refai(0);
          name:=crvname(c);
         

Daq Pascal Api -> crvlen

Определение:

function crvlen(ref:real):real

Аргументы:

  • ref - ссылка на кривую.

Результат:

Возвращает длину кривой ref.

Описание:

Возвращает длину, то есть число точек, кривой со ссылкой ref. Ссылку на кривую можно получить функциями reffind, refai, refao, refdi, refdo, crvfind.

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

Ссылка и результат имеют тип real, так как на старых системах у типа integer был слишком малый диапазон. В текущей системе в этом нет необходимости, однако для совместимости тип сохранен.

Пример:

          for i:=1 to round(crvlen(refai(0))) do writeln(crvx(refao(0),i),crvy(refao(0),i));
         

Daq Pascal Api -> crvx crvy

Определение:

function crvx(ref,i:real):real
function crvy(ref,i:real):real

Аргументы:

  • ref - ссылка на кривую.
  • i - индекс, от 1 до crvlen(ref).

Результат:

Возвращает x,y-координаты точки номер i кривой ref.

Описание:

Возвращает x,y-координаты точки номер i кривой со ссылкой ref. Если действовать совсем правильно, рекомендуется извлекать данные в блоке crvlock..crvunlock, чтобы гарантировать правильную работу в многопоточной среде. Ссылку на кривую можно получить функциями reffind, refai, refao, refdi, refdo, crvfind.

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

Ссылка и индекс имеют тип real, так как на старых системах у типа integer был слишком малый диапазон. В текущей системе в этом нет необходимости, однако для совместимости тип сохранен.

Пример:

          var ref,x,y:real; b:boolean;
          ref:=refai(0);
          if crvlock(ref) then begin
           x:=crvx(ref,crvlen(ref));
           y:=crvy(ref,crvlen(ref));
           b:=crvunlock(ref);
          end;
         

Daq Pascal Api -> crvget

Определение:

function crvget(ref,x:real):real

Аргументы:

  • ref - ссылка на кривую.
  • x - координата, где надо вычислить кривую.

Результат:

Возвращает результат линейной интерполяции в точке x кривой ref.

Описание:

Возвращает результат линейной интерполяции в точке x кривой ref. Полагается, что точки кривой упорядочены по возрастанию x, то есть x[i+1]>=x[i]. Если это условие не выполнено, результат будет неверным. Обычно по оси x отложено время, которое всегда возрастает, поэтому в этом случае проблем не будет. Ссылку на кривую можно получить функциями reffind, refai, refao, refdi, refdo, crvfind.

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

Ссылка имеет тип real, так как на старых системах у типа integer был слишком малый диапазон. В текущей системе в этом нет необходимости, однако для совместимости тип сохранен.

Пример:

          y:=crvget(refai(0),x);
         

Daq Pascal Api -> crvput crvins crvdel

Определение:

function crvput(ref,i,x,y:real):boolean
function crvins(ref,i,x,y:real):real
function crvdel(ref,i,n:real):real

Аргументы:

  • ref - ссылка на кривую.
  • i - номер точки кривой.
  • x,y - координаны точки кривой.
  • n - число точек кривой.

Результат:

Функции для изменения/вставки/удаления точек кривой ref.

Описание:

Функции для изменения/вставки/удаления точек кривой ref.

Вызов crvput(ref,i,x,y) задает точке с индексом i кривой ref новое значение x,y. Индекс (номер) точки может быть в диапазоне 1..crvlen(ref).

Вызов crvins(ref,i,x,y) вставляет или добавляет точку с индексом i кривой ref и значением x,y. Индекс (номер) точки для вставки может быть в диапазоне 1..crvlen(ref). При значении i>crvlen(ref) (например, i=maxint) точка добавляется в конец кривой. Функция возвращает число вставленных точек (0 или 1).

Вызов crvdel(ref,i,n) удаляет n точек кривой ref, начиная с индекса i. Индекс (номер) точки может быть в диапазоне 1..crvlen(ref). Функция возвращает число удаленных точек.

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

Ссылка и индекс имеют тип real, так как на старых системах у типа integer был слишком малый диапазон. В текущей системе в этом нет необходимости, однако для совместимости тип сохранен.

Пример:

          Задать последней точке кривой значения x,y
          Блокировка гарантирует, что между вызовами crvlen и crvput кривая не изменится

          c:=refao(0);
          if crvlock(c) then begin
           b:=crvput(c,crvlen(c),x,y);
           b:=crvunlock(c);
          end;

          Добавить в конец кривой точку x,y

          r:=crvins(c,maxint,x,y);

          Вставить в начало кривой точку x,y

          r:=crvins(c,1,x,y);

          Удалить все точки кривой

          r:=crvdel(c,1,maxint);
         

Daq Pascal Api -> crvwhere

Определение:

function crvwhere(ref,x:real):real

Аргументы:

  • ref - ссылка на кривую.
  • x - координата, по которой ищется индекс.

Результат:

Возвращает индекс ближайшей к x точки кривой ref.

Описание:

Осуществляет быстрый двоичный поиск индекса точки, ближайшей к точке x кривой ref. Если точнее, в результате находится индекс i, такой, что x>=x[i],x[i+1]>x. Индекс меняется в диапазоне от 1 до crvlen(ref). Полагается, что точки кривой упорядочены по возрастанию x, то есть x[i+1]>=x[i]. Если это условие не выполнено, результат будет неверным. Обычно по оси x отложено время, которое всегда возрастает, поэтому в этом случае проблем не будет. Ссылку на кривую можно получить функциями reffind, refai, refao, refdi, refdo, crvfind.

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

Ссылка и результат имеют тип real, так как на старых системах у типа integer был слишком малый диапазон. В текущей системе в этом нет необходимости, однако для совместимости тип сохранен.

Пример:

          i:=round(crvwhere(refai(0),x));
         

Daq Pascal Api -> crvinteg

Определение:

function crvinteg(ref,a,b:real):real

Аргументы:

  • ref - ссылка на кривую.
  • a..b - пределы интегрирования.

Результат:

Возвращает интеграл кривой ref на интервале a..b.

Описание:

Возвращает значение интеграла кривой ref на интервале a..b, сосчитанное по методу трапеций. Ссылку на кривую можно получить функциями reffind, refai, refao, refdi, refdo, crvfind.

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

Ссылка имеет тип real, так как на старых системах у типа integer был слишком малый диапазон. В текущей системе в этом нет необходимости, однако для совместимости тип сохранен.

Пример:

          f:=crvinteg(refai(0),0,1);
         

Daq Pascal Api -> crvlock crvunlock

Определение:

function crvlock(ref:real):boolean
function crvunlock(ref:real):boolean

Аргументы:

  • ref - ссылка на кривую.

Результат:

Блокировка/разблокировка кривой ref.

Описание:

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

Вызов crvlock(ref) блокирует, а crvunlock(ref) разблокирует кривую ref. Эти вызовы идут всегда парно (сбалансированно), как begin и end. Вызов crvlock(ref) возвращает true, если блокировка прошла успешно. Если crvlock(ref) вернул false, то либо ссылка ref некорректна, либо переполнен стек блокировок (см. далее). Вызов crvunlock(ref) возвращает true, если разблокировка прошла успешно. Если crvunlock(ref) вернул false, то либо ссылка ref некорректна, либо блокировка не была выполнена.
Использование блокировок всегда идет по шаблону:

          if crvlock(crv) then begin
           ... действия с кривой crv ...
           b:=crvunlock(crv);
          end;
         

В целях защиты программ DAQ Pascal от подвисаний при ошибках в программе, DAQ система имеет внутренний стек блокировок, так что при несбалансированности вызовов crvlock/crvunlock программа DAQ Pascal не повиснет, система восстановит баланс, а затем сгенерирует ошибку времени выполнения и вы увидите печальную физиономию :(. Это, конечно, не оправдывает неряшливого использования блокировок, хотя и спасает от подвисаний при отладках.

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

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

Наконец, применение блокировок излишне, если

  • С кривой работает только один поток.
  • Нужен только одиночный вызов какой-либо функции работы с кривой.
    Например, crvins(c,maxint,x,y) - одиночный вызов, блокировка не нужна.
    А вот вызов crvins(c,crvlen(c),x,y) - двойной, блокировка нужна, так как между вызовами crvlen и crvins кривая может измениться.

Пример:

          Небезопасное извлечение последней точки кривой:

          x:=crvx(c,crvlen(c));
          y:=crvy(c,crvlen(c));

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

          Безопасное извлечение последней точки кривой:

          c:=refai(0);
          if crvlock(c) then begin
           x:=crvx(c,crvlen(c));
           y:=crvy(c,crvlen(c));
           b:=crvunlock(c);
          end;
         

Daq Pascal Api -> crvgetln crvputln crvinsln crvaddln crvdelln crvnumln

Определение:

function crvgetln(ref:real;i:integer):string
function crvputln(ref:real;i:integer;s:string):boolean
function crvinsln(ref:real;i:integer;s:string):boolean
function crvaddln(ref:real;s:string):boolean
function crvdelln(ref:real;i:integer):boolean
function crvnumln(ref:real):integer

Аргументы:

  • ref - ссылка на кривую.
  • i - номер строки от 0 до crvnumln(ref)-1.
  • s - строка для внесения в текст.

Результат:

crvgetln - возвращает строку номер i текста паспорта кривой ref.
crvputln - заносит строку s на место строки номер i в тексте паспорта кривой ref. Возвращает true при успешном выполнении.
crvinsln - вставляет строку s перед строкой номер i текста паспорта кривой ref. Возвращает true при успешном выполнении.
crvaddln - вставляет строку s в конец текста паспорта кривой ref. Возвращает true при успешном выполнении.
crvdelln - удаляет строку номер i из текста паспорта кривой ref. Возвращает true при успешном выполнении.
crvnumln - возвращает число строк в тексте паспорта кривой ref.

Описание:

Группа операций с текстом паспорта кривой. Дает возможность организовать хранение в паспорте сопроводительной информации.

Каждая кривая, помимо данных, имеет паспорт, то есть текст, сопровождающий или комментирующий эту кривую. Паспорт может содержать список дополнительных переменных, описывающих условия измерений, например, в виде текстовых выражений типа "name=value". Группа вышеуказанных функций дает полный доступ к паспорту кривой.

Ссылку на кривую можно получить функциями reffind, refai, refao, refdi, refdo, crvfind.

Пример:

          var ref:real;
          ref:=refai(0);
          for i:=0 to crvnumln(ref)-1 do writeln(crvgetln(ref,i));
          b:=crvputln(ref,0,'Put line 0');
          b:=crvinsln(ref,0,'Insert line 0');
          b:=crvaddln(ref,'Add line');
          while crvnumln(ref)>0 do crvdelln(ref,crvnumln(ref)-1); {clear comment}
         

Daq Pascal Api -> createtag inittag freetag findtag typetag nametag igettag isettag rgettag rsettag sgettag ssettag taglock iatomictagop ratomictagop gettagcolor settagcolor gettagparam settagparam gettagtimer settagtimer

Определение:

function createtag(name:String;typ:Integer):Integer
function inittag(name:String;typ:Integer):Integer
function freetag(tag:Integer):Boolean
function findtag(name:String):Integer
function typetag(tag:Integer):Integer
function nametag(tag:Integer):String
function igettag(tag:Integer):Integer
function isettag(tag:Integer;i:Integer):Boolean
function rgettag(tag:Integer):Real
function rsettag(tag:Integer;r:Real):Boolean
function sgettag(tag:Integer):String
function ssettag(tag:Integer;s:String):Boolean
function taglock(lock:Boolean):Integer
function iatomictagop(tag:Integer;op:Char;i:Integer):Boolean
function ratomictagop(tag:Integer;op:Char;r:Real):Boolean
function gettagcolor(tag:Integer):Integer
function settagcolor(tag:Integer;c:Integer):Boolean
function gettagparam(tag:Integer):Real
function settagparam(tag:Integer;p:Real):Boolean
function gettagtimer(tag:Integer):Real
function settagtimer(tag:Integer;t:Real):Boolean

Аргументы:

  • tag - ссылка на тег.
  • name - имя тега.
  • typ - тип тега.
  • i - Integer значение для записи в тег или операнд для атомарной операции.
  • r - Real значение для записи в тег или операнд для атомарной операции.
  • s - String значение для записи в тег.
  • lock - Boolean операция блокирования/разблокирования.
  • op - Char идентификатор для атомарной операции.
  • c - пользовательский атрибут тега - цвет (color).
  • p - пользовательский атрибут тега - параметр (param).
  • t - пользовательский атрибут тега - метка времени (timer).

Результат:

Функции манипуляции с тегами.

Описание:

Группа функций для манипуляции с тегами.
Теги - общие для всех DAQ-программ переменные, имеющие
  1. ссылку (tag) - целое число, фактически индекс в таблице тегов,
  2. имя (name) для идентификации и поиска ссылки по имени в таблице тегов,
  3. тип (type) для контроля доступа к данным 1/2/3=integer/real/string,
  4. значение заданного типа с контролем доступа к данным по типу,
  5. пользовательские атрибуты (color,param,timer) для различных нужд.
Общая схема работы с тегами такая. Сначала по имени (name) находится ссылка (tag), которая обеспечивает быстрый доступ к данным тега. Для работы с данными используются функции (iGetTag,rGetTag,sGetTag), в зависимости от типа (type). Пользовательские атрибуты (color,param,timer) доступны для всех тегов независимо от типа и служат для хранения локальных пользовательских параметров, а также для взаимодействия с графической системой Painter. В отличие от значений, атрибуты не задаются в конфигурации и должны явно инициализироваться в прикладной программе (если они вообще нужны).

findtag(name) - ищет в базе данных тег по имени name. В случае успеха возвращает ссылку на тег. В случае неудачи, если тег не найден, возвращает ноль. При поиске тегов имена тегов не чувствительны к регистру. Теги создаются обычно при загрузке DAQ, согласно описаниям в секции [TagList]. Для работы с тегом надо сначало узнать его ссылку вызовом findtag. Обычно это делается один раз, в начале программы, потому что поиск ссылки - не очень быстрая операция. Полученная ссылка на тег используется затем всеми остальными функциями для чтения/записи значения тега, проверки его имени и типа. Использование ссылки (вместо имени) для доступа к тегу позволяет резко ускорить работу системы.

Ссылки на теги лежат в диапазоне, заданным константами TAG_REF_MIN..TAG_REF_MAX. Эти константы позволяют организовать цикл по всем тегам, при этом надо проверять тип тега (typetag).

createtag(name,typ) или inittag(name,typ) - находит существующий или создает новый тег по имени name типа typ. В случае успеха возвращает ссылку на тег. В случае неудачи возвращает ноль. Неудача возможна по двум причинам:

  1. Тег существует, но имеет другой тип.
  2. В таблице тегов нет места для нового тега.
Теги создаются обычно при загрузке DAQ, согласно описаниям в секции [TagList]. Это основной, рекомендуемый путь создания тегов. Функция createtag дает другой путь для динамической генерации новых тегов. Если, например, надо создать массив тегов по какому-то правилу, используется createtag. Однако использовать эту функцию надо с осторожностью, чтобы не перепутать теги, созданные при загрузке системы (статические) и вновь создаваемые (динамические). Для этого перед созданием нового тега надо сначала убедиться при помощи findtag, что тега с интересующим именем нет, и только потом создавать новый.

freetag(tag) - уничтожает тег с данной ссылкой. Эту функцию надо использовать с большой осторожностью, и применять ее следует только к тегам, которые были созданы динамически вызовом createtag. Не следует уничтожать статические теги, созданные при загрузке DAQ, это может нарушить работу DAQ - программ, ведь программы обычно используют статические теги для своих нужд. Большинство DAQ-программ инициализируют ссылки на статические теги в начале программы и затем полагаются на их неизменность, то есть на то, что они не уничтожаются и не создаются вновь. Это позволяет существенно ускорить выполнение программ, однако требует корректной работы с функциями createtag/freetag.

typetag(tag) - возвращает тип тега:

  1. tag_type_nil - тег не инициализорован или его ссылка недействительна
  2. tag_type_int - тег умеет целочисленный тип Integer
  3. tag_type_real - тег умеет вещественный тип Real
  4. tag_type_string - тег умеет строковый тип String

Если вызов typetag(tag) вернул ноль (tag_type_nil), то ссылка tag некорректна, то есть тега с такой ссылкой не существует. Это основной способ проверки тегов (можно также использовать refinfo).

nametag(tag) - возвращает имя тега или пустую строку, если тега с такой ссылкой не существует. Имена тегов не чувствительны к регистру.

igettag(tag) - возвращает значение Integer тега tag. Если ссылка некорректна (несуществующий тег или тег другого типа), возвращается ноль. При ошибке типа тега также фиксируется ошибка в DAQ системе.

isettag(tag,i) - записывает в Integer тег tag значение i. Если ссылка некорректна (несуществующий тег или тег другого типа), возвращается false. При ошибке типа тега также фиксируется ошибка в DAQ системе.

rgettag(tag) - возвращает значение Real тега tag. Если ссылка некорректна (несуществующий тег или тег другого типа), возвращается ноль. При ошибке типа тега также фиксируется ошибка в DAQ системе.

rsettag(tag,r) - записывает в Real тег tag значение r. Если ссылка некорректна (несуществующий тег или тег другого типа), возвращается false. При ошибке типа тега также фиксируется ошибка в DAQ системе.

sgettag(tag) - возвращает значение String тега tag. Если ссылка некорректна (несуществующий тег или тег другого типа), возвращается ноль. При ошибке типа тега также фиксируется ошибка в DAQ системе.

ssettag(tag,s) - записывает в String тег tag значение s. Если ссылка некорректна (несуществующий тег или тег другого типа), возвращается false. При ошибке типа тега также фиксируется ошибка в DAQ системе.

taglock(lock) - блокирует/разблокирует доступ к тегам для других потоков. Вызов taglock(true) блокирует доступ и инкрементирует счетчик блокировок. Вызов taglock(false) разблокирует доступ и декрементирует счетчик блокировок. Функция возвращет текущий счетчик блокировок после выполнения инкремента/декремента. Блокировка применяется в особых случаях, когда надо выполнить атомарные (неделимые) операции над одним или несколькими тегами, чтобы другие потоки не нарушили логику работы алгоритма. Из соображений производительности следует максимально сокращать объем кода внутри заблокированной секции кода. В противном случае другие потоки могут оказаться на долгое время заблокированными при ожидании доступа к тегам. Функцию всегда надо применять парными вызовами, кратковременно блокируя, а затем разблокируя доступ. Например:

          procedure IncTag(tag:Integer);   // Атомарный инкремент тега
          var i:Integer; b:Boolean;
          begin
           i:=taglock(true);               // Блокировка
           b:=iSetTag(tag,iGetTag(tag)+1); // Инкремент тега
           i:=taglock(false);              // Разблокировка
          end;
         
Если баланс блокировок-разблокировок нарушается, фиксируется фатальная ошибка в DAQ-программе. Поэтому функцию надо использовать очень осторожно.

iAtomicTagOp(tag,op,i) - выполняет атомарную (неделимую) операцию op с тегом tag и операндом i.

         Значение op   Операция         Аналог
         +             сложение         iSetTag(tag, iGetTag(tag) + i)
         -             вычитание        iSetTag(tag, iGetTag(tag) - i)
         *             умножение        iSetTag(tag, iGetTag(tag) * i)
         /             div              iSetTag(tag, iGetTag(tag) div i)
         %             mod              iSetTag(tag, iGetTag(tag) mod i)
         &             AND              iSetTag(tag, iAnd(iGetTag(tag), i))
         |             OR               iSetTag(tag, iOr(iGetTag(tag), i))
         ^             XOR              iSetTag(tag, iXor(iGetTag(tag), i))
         <             сдвиг влево      iSetTag(tag, iShift(iGetTag(tag), i))
         >             сдвиг вправо     iSetTag(tag, iShift(iGetTag(tag), -i))
         
Отличие от приведенного в таблице аналога состоит в том, что операция выполненяется в защищенном блоке TagLock(true); ... TagLock(false); , что гарантирует невмешательство других потоков в процесс вычислений во время выполнения операции. Например:
         b:=iAtomicTagOp(tag,'+',1);       // Атомарный инкремент тега

         эквивалентно вызову

         i:=TagLock(true);                 // Блокировка
         b:=iSetTag(tag,iGetTag(tag)+1);   // Инкремент тега
         i:=TagLock(false);                // Разблокировка
         
Зачем нужны атомарные операции? Для начала заметим, что доступ к тегам, как и всем другим объектам в DaqPascal защищен и потокобезопасен. Это значит, что разные потоки (а их бывает много) могут безопасно считывать и записывать данные, не мешая друг другу. Другими словами, операции чтения iGetTag(tag) и записи iSetTag(tag,value) являются атомарными (неделимыми). Однако некоторые операции над данными тегов не являются атомарными. Это возникает в случае, если операция предполагает чтение value:=iGetTag(tag), изменение значения value, а затем запись результата iSetTag(tag,value). Поскольку чтение и запись - две разные операции, другой поток может вмешаться в промежутке между чтением и записью и изменить значение тега, в результате чего может произойти потеря данных. Рассмотрим пример с двумя потоками, которые работают с битами целого числа. Допустим, Поток 1 хочет установить 0-й бит целого тега tag, а Поток 2 - установить 1-й бит. Эти биты соответствуют значениям 1 и 2. Возможна такая ситуация:
         Время | Значение тега | Поток 1                            | Поток 2
         ------|---------------|------------------------------------|-----------------------------------
               |  начальное    | хочет выполнить                    | хочет выполнить
               |  значение 0   | iSetTag(tag,iOr(iGetTag(tag),1))   | iSetTag(tag,iOr(iGetTag(tag),2))
         ------|---------------|------------------------------------|-----------------------------------
         0     | 0             | вычисляет iOr(iGetTag(tag),1) = 1  | спит
         1     | 0             | засыпает                           | просыпается
         2     | 0             | спит                               | вычисляет iOr(iGetTag(tag),2) = 2
         3     | 2             | спит                               | вычисляет iSetTag(tag,2)
         4     | 2             | просыпается                        | засыпает
         5     | 1             | вычисляет iSetTag(tag,1)           | спит

         Результат = 1 - а должно быть 1+2 = 3, то есть 1-й бит (2) потерян из-за конфликта потоков
         
Описанная выше ошибка может возникать довольно редко, но она возможна.
Находить ошибки такого типа очень сложно, именно потому что они редкие.
Использование атомарных операций предотвращает возникновение такого рода ошибок.

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

rAtomicTagOp(tag,op,r) - выполняет атомарную (неделимую) операцию op с тегом tag и операндом r.

         Значение op  Операция         Аналог
         +            сложение         rSetTag(tag, rGetTag(tag) + r)
         -            вычитание        rSetTag(tag, rGetTag(tag) - r)
         *            умножение        rSetTag(tag, rGetTag(tag) * r)
         /            деление          rSetTag(tag, rGetTag(tag) / r)
         ^            степень          rSetTag(tag, Power(rGetTag(tag), r))

         например:    b:=rAtomicTagOp(tag,'+',1); - атомарный инкремент
         
Отличие от приведенного в таблице аналога состоит в том, что операция выполненяется в защищенном блоке TagLock(true); ... TagLock(false); , что гарантирует невмешательство других потоков в процесс вычислений во время выполнения операции.

gettagcolor(tag) - возвращает значение атрибута Color тега tag.
settagcolor(tag,c) - задает значение (с) атрибута Color тега tag.
gettagparam(tag) - возвращает значение атрибута Param тега tag.
settagparam(tag,p) - задает значение (p) атрибута Param тега tag.
gettagtimer(tag) - возвращает значение атрибута Timer тега tag.
settagtimer(tag,t) - задает значение (t) атрибута Timer тега tag.
Каждый тег имеет набор атрибутов (color, param, timer), которые доступны независимо от типа тега. Атрибуты предназначены для хранения локальных пользовательских данных, ассоциированных с тегом. Кроме того, атрибуты служат для взаимодействия с графической системой при использовании сценариев Painter. Сценарии рисования Painter позволяют прочитать атрибуты связанного с сенсором тега с помощью вызовов LinkedTagColor(), LinkedTagParam(), LinkedTagTimer(). Это позволяет передавать графической системе дополнительные параметры для рисования сложных изображений, в дополнение к значению тега. Атрибут цвета (color) может использоваться, например, для задания цвета сенсора. Атрибут параметра (param) может использоваться, например, для передачи сенсору какого-то дополнительного параметра. Атрибут метки времени (timer) может использоваться для действий, связанных с отсчетом времени, например, для того, чтобы сигнализировать критическую задержку (timeout).

Пример:

Данный пример-симулятор типичен в смысле использования тегов (в примере используется один тег). В начале программы инициализируется ссылка на тег по его имени. Затем в цикле опроса программы эта ссылка используется для чтения/записи значения тега. В данном случае, если тег кнопки ненулевой (кнопка нажата), на выход генерируются какие-то данные.
          [TagList]
          StartButton = integer 0

          program TagDemo;
          var
           b                 : Boolean; { Temporary                        }
           Ok                : Boolean; { Program initialization is Ok?    }
           errors            : Integer; { Program error counter            }
           errorcode         : Integer; { Error code for this device       }
           tagStart          : Integer; { Start button                     }
           {
           Initialize and check tag.
           }
           procedure InitTag(var tag:Integer; name:String; typ:Integer);
           begin
            tag:=findtag(name);
            if (typ>0) and (typetag(tag)<> typ) then errors:=errors+1;
           end;
          begin
           {
           Initialization actions on Start
           }
           if runcount=1 then begin
            {
            Initialize errors & strings, clear variables.
            }
            errors:=0;
            errorcode:=registererr(devname);
            {
            Initialize tags.
            }
            InitTag(tagStart,'StartButton',1);
            {
            Is it Ok?
            }
            if errors<>0 then b:=fixerror(errorcode);
            Ok:=(errors=0);
           end else
           {
           Finalization actions on Stop.
           }
           if isinf(runcount) then begin
            b:=iSetTag(tagStart,0);
           end else
           {
           Data acquisition actions on Poll.
           }
           if Ok then begin
            {
            If Start button clicked, similate data
            }
            if igettag(tagStart)<>0 then begin
             b:=putao(0,time,(1+sin(time))*0.01);
             b:=putao(1,time,(1+sin(time))*0.02);
            end;
           end;
          end.
         

Daq Pascal Api -> clickwhat clickread clickwrite clickwrote clickfilter clickawaker

Определение:

function clickwhat:integer - тип текущего обрабатываемого пользовательского события.
function clickread:integer - чтение следующего пользовательского события из очереди.
function clickwrite(data:string):integer - запись в очередь (симуляция) пользовательского события.
function clickwrote:integer - счетчик записи (симуляции) пользовательского события.
function clickfilter(filter):integer - считывает/задает фильтр пользовательских событий.
function clickawaker(awaker):integer - считывает/задает флаги пробуждения для событий.

Аргументы:

  • data - строка текста, задающая пользовательское событие.
  • filter - фильтр (битовая маска) для пользовательских событий.
  • awaker - флаги (битовая маска) для пробуждения по событиям.

Результат:

Группа функций для организации цикла опроса пользовательских событий (например, нажатий сенсоров мнемосхем).
Функции clickwhat, clickread, clickwrite возвращают тип текущего/причитанного/записанного события, clickwrote - счетчик записей, clickfilter - битовую маску фильтра событий, clickawaker - битовую маску флагов пробуждения по событиям.

Описание:

Вызов clickwhat возвращает тип текущего обрабатываемого пользовательского события из списка:
(0..8)=(NOTHING, MOUSEDOWN, MOUSEUP, MOUSEMOVE, KEYDOWN, KEYUP, MOUSEWHEEL, MOUSEWHEELDOWN, MOUSEWHEELUP).
Он как бы отвечает на вопрос: "Что случилось?".
Возможны такие значения clickwhat:
  1. NOTHING - ничего не произошло, нет событий для обработки.
  2. MOUSEDOWN - нажата кнопка мыши.
  3. MOUSEUP - отпущена кнопка мыши.
  4. MOUSEMOVE - курсор мыши сдвинулся.
  5. KEYDOWN - нажата кнопка клавиатуры.
  6. KEYUP - отпущена кнопка клавиатуры.
  7. MOUSEWHEEL - провернуто колесико мыши.
  8. MOUSEWHEELDOWN - нажато колесико мыши.
  9. MOUSEWHEELUP - отпущено колесико мыши.
Примечания:
  1. При нажатии/отпускании колесика мыши может генерироваться событие MouseDown/MouseUp вместо событий MouseWheelDown/MouseWheelUp, т.к. колесико мыши часто является одновременно еще и средней кнопкой мыши.
  2. Приращение колесика мыши обычно кратно 120 со знаком, зависящим от направления вращения при прокрутке. Можно использовать переменную WHEEL_DELTA для масштабирования.


Вызов clickread считывает из очереди событий очередное событие и возвращает его тип - такой же, как в функции clickwhat. Ноль служит признаком того, что в очереди больше нет событий для обработки. Функция clickread позволяет в цикле обрабатывать все доступные события из очереди, а не только одно текущее событие. Например:
         Цикл обработки событий, вариант 1:     Цикл обработки событий, вариант 2:

         if ClickWhat<>0 then                   while ClickWhat<>0 do begin
         repeat                                  if ClickButton=1 then
          if ClickButton=1 then                  ... Обработка .......
          ... Обработка .......                  iNul(ClickRead);
         until ClickRead=0;                     end;
         


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

Для записи события строка data должна содержать набор поименованных значений параметров события Name=Value, разделенных CRLF. Подобные данные часто называют "куками" (cookies, дословно "печенье"). Среди обязательных параметров должны быть указаны What, Where, Button. Остальные параметры указываются по необходимости. Для формирования строки с описанием события рекомендуется использовать вызов ClickParams(''), который формирует строку из текущего события. Это удобно, например, если текущее событие надо передать на обработку от клиента к серверу. К событию можно присоединить дополнительные параметры, если они нужны, например:
         Client pseudo-code:                        Server pseudo-code:
         // Получаем клик, передаем серверу:        // Клиентский клик записываем в очередь:
         if ClickWhat<>0 then begin                 if ReceiveFromClient(data) then
          data:=ClickParams('')+CRLF+               if ClickWrite(data) then Writeln('Client click received.');
                'NewValue=123.45'+CRLF+             // Обрабатываем события в обычном цикле обработки событий:
                'Time='+Str(Time)+CRLF;             if ClickWhat<>0 then
          SendToServer(data);                       repeat
         end;                                        if ClickWrote>0 then begin // Это событие просимулировано
                                                      writeln('Клиент прислал NewValue='+ClickParams('NewValue'));
                                                     end;
                                                    until ClickRead=0;
         
Длина присоединяемых к исходному событию данных (cookies) не должна быть очень большой. В настоящей версии максимальная длина cookies сотавляет около 3500 символов.

Вызов clickwrote возвращает счетчик записи текущего обрабатываемого события. Этот счетчик равен нулю для "исходного", "настоящего" события, которое генерируется клавиатурой или мышью. При каждой записи процедурой clickwrite счетчик clickwrote для этого события инкрементируется на единицу, что позволяет отличать "натуральные" события от "просимулированных". Стимуляция событий используется, например, в распределенных системах для передачи событий ("кликов") от клиента к серверу.

Вызов clickfilter(filter) возвращает текущий (до вызова) фильтр событий и затем устанавливает новое значение фильтра filter. Фильтр событий влияет на то, какие события будут приходить на обработку, а какие будут игнорироваться. По сути дела это битовая маска, в которой каждый бит отвечает за тип события ClickWhat:
  1. бит (маска 1) - не используется, т.к. события NOTHING никогда не наступают.
  2. бит (маска 2) - отвечает за прием событий MOUSEDOWN.
  3. бит (маска 4) - отвечает за прием событий MOUSEUP.
  4. бит (маска 8) - отвечает за прием событий MOUSEMOVE.
  5. бит (маска 16) - отвечает за прием событий KEYDOWN.
  6. бит (маска 32) - отвечает за прием событий KEYUP.
  7. бит (маска 64) - отвечает за прием событий MOUSEWHEEL.
  8. бит (маска 128) - отвечает за прием событий MOUSEWHEELDOWN.
  9. бит (маска 256) - отвечает за прием событий MOUSEWHEELUP.
Если соответствующий бит установлен, то события данного типа разрешены и будут доставляться для обработки. Если бит не установлен, события этого типа игнорируются.

Два значения filter имеют специальное значение:
  1. ClickFilter(0) - возвращает текущий фильтр, оставляя его без изменения.
  2. ClickFilter(1) - возвращает стандартный фильтр "по умолчанию" (MOUSEDOWN+KEYDOWN=18) .
Остальные значения filter устанавливают новый фильтр для пользовательских событий.

Стандартный фильтр (MOUSEDOWN+KEYDOWN=18) пропускает только нажатия кнопок, поэтому в этом случае возможна упрощенная обработка событий, при которой анализируется только значение ClickButton. Если фильтр отличается от стандартного, то при обработке обязательно надо анализировать значение типа события ClickWhat.

Вызов clickawaker(awaker) возвращает текущий (до вызова) набор флагов пробуждения для событий и затем устанавливает новое значение флагов awaker. Флаги пробуждения для событий влияют на то, по каким событиям будет приходить досрочное пробуждение потока. По сути дела это битовая маска, в которой каждый бит отвечает за тип события ClickWhat:
  1. бит (маска 1) - не используется, т.к. события NOTHING никогда не наступают.
  2. бит (маска 2) - отвечает за пробуждение потока для событий MOUSEDOWN.
  3. бит (маска 4) - отвечает за пробуждение потока для событий MOUSEUP.
  4. бит (маска 8) - отвечает за пробуждение потока для событий MOUSEMOVE.
  5. бит (маска 16) - отвечает за пробуждение потока для событий KEYDOWN.
  6. бит (маска 32) - отвечает за пробуждение потока для событий KEYUP.
  7. бит (маска 64) - отвечает за пробуждение потока для событий MOUSEWHEEL.
  8. бит (маска 128) - отвечает за пробуждение потока для событий MOUSEWHEELDOWN.
  9. бит (маска 256) - отвечает за пробуждение потока для событий MOUSEWHEELUP.
Если соответствующий бит установлен, то при возникновении события данного типа (с учетом фильтра) будет происходить досрочное пробуждение потока для скорейшей его обработки. Если бит не установлен, события этого типа не пробуждают поток досрочно. События при этом накапливаются в очереди на обработку.

Два значения awaker имеют специальное значение:
  1. ClickAwaker(0) - возвращает текущий набор флагов пробуждения, оставляя его без изменения.
  2. ClickAwaker(1) - возвращает стандартный набор флагов "по умолчанию" (MOUSEDOWN+KEYDOWN=18) .
Остальные значения awaker устанавливают новый набор флагов пробуждения для пользовательских событий.

Стандартный набор флагов (MOUSEDOWN+KEYDOWN=18) пробуждает поток только при нажатии кнопок, для их скорейшей обработки. Если флаги отличается от стандартного набора, то возможно возникновение "спама" - ситуации слишком частого пробуждения потока, что может сказаться на повышении нагрузки процессора.

Принцип обработки событий

Для лучшего понимания работы функций обработки пользовательских событий опишем общий принцип обработки событий. Каждая программа DAQ Pascal содержит FIFO очередь пользовательских событий, куда помещаются события нажатий кнопок мыши и клавиатуры вместе с сопроводительной информацией (сенсор, тег, кривая, окно, устройство и т.д. - см. функцию ClickParams). Фильтр событий ClickFilter разрешает или запрещает обрабатывать события в зависимости от их типа. Набор флагов ClickAwaker разрешает/запрещает досрочно пробуждать поток в зависимости от типа события. Перед каждым вызовом программы DAQ Pascal система извлекает одно событие из очереди для обработки (если в очереди есть события) и сохраняет его для обработки во внутренней буфер (буфер текущего события). Это и есть то самое текущее событие, которое сразу после вызова программы доступно для обработки функциями ClickWhat, ClickButton, ClickTag, ClickSensor, ClickParams. В принципе, события можно обрабатывать и без цикла, по одному событию на каждый вызов программы. Однако если событий много, лучше сразу обрабатывать все события в очереди, чтобы избежать потери событий. Для этого надо выполнять чтение событий из очереди в буфер текущего события функцией ClickRead. Функция ClickWhat позволяет быстро проверить наличие текущего события для обработки и его тип. При необходимости события можно генерировать искуственно (симулировать) и записывать в очередь событий функцией ClickWrite. Тогда их обработка будет идти так же, как обработка "настоящих" событий. Для различения "настоящих" и "просимулированных" событий служит функция ClickWrote, содержащая счетчик записи. Для "настоящих" событий он равен нулю, для "симулированных" - больше нуля.

Соображения производительности

При обработке событий необходимо учитывать, что вызовы функций обработки пользовательского ввода имеют разную степень "тяжести", т.е. занимают существенно разное время. Это необходимо учитывать при обработке, чтобы не перегружать процессор понапрасну. То есть сначала с помощью "легких" функций надо убедиться, что случилось ожидаемое событие, а потом уже с помощью "тяжелых" функций анализировать поступившие данные.
  • Функция ClickFilter позволяет изначально фильтровать (игнорировать) ненужные события, и тем самым повышать производительность. Значение фильтра "по умолчанию" пропускает только события нажатия кнопок (MOUSEDOWN+KEYDOWN).
  • Функция ClickAwaker позволяет управлять флагами досрочного пробуждения потока в зависимости от типа события, и тем самым влиять на производительность. Значение флагов "по умолчанию" пробуждают поток по событиям нажатия кнопок (MOUSEDOWN+KEYDOWN).
  • Функции ClickWhat, ClickButton, ClickTag, ClickRead, ClickWrote являются самыми быстрыми и должны использоваться в первую очередь для первичной идентификации события и организации цикла опроса.
  • Функция ClickSensor чуть медленнее (т.к. строковая) и используется во вторую очередь, после первичной идентификации.
  • Функция ClickParams является сравнительно тяжелой (медленной) и должна использоваться только после первичной идентификации событий, чтобы сократить нагрузку на процессор.
  • Функция ClickWrite является сравнительно тяжелой (медленной) и используется только в особых случаях (для обработки клиент-серверных событий с удаленного компьютера).


Типичная процедура обработки в цикле опроса должна анализировать события в таком порядке:
  1. ClickWhat - тип события (что случилось?).
  2. ClickButton - значение кнопки (что нажато?).
  3. ClickTag или ClickSensor - источник события (какой тег или сенсор?).
  4. ClickParams - остальные параметры, например, ClickParams('Curve') ( какая кривая?).

Пример:

            f:=ClickFilter(511);                                        // При инициализации задаем фильтр событий
            a:=ClickAwaker(511);                                        // а также флаги "пробуждения по событию"
            ...                                                         // В данном случае разрешаем все события
            procedure HandleClicks;                                     // Процедура обработки пользовательских событий
            begin                                                       // Вызывается в цикле опроса ApplicationPoll
             if ClickWhat<>0 then                                       // Что-то случилось?
             repeat                                                     // Начинаем цикл обработки событий...
              if ClickWhat=1 then begin                                 // Нажатие кнопки мыши?
               if ClickButton=1 then begin                              // Нажатие левой кнопки?
                if ClickSensor='DEMO' then ...                          // Выполняем обработку нажатия сенсора
                ...                                                     // И так далее
               end;                                                     //
              end;                                                      //
              if ClickWhat=3 then begin                                 // Движение мыши?
               writeln('Mouse move to: '+ClickParams('Where'));         // Печатаем координаты мыши
               ...                                                      // И так далее
              end;
              if ClickWhat=6 then begin                                 // Поворот колесика мыши?
               writeln('Mouse wheel: '+ClickParams('Wheel'));           // Печатаем приращение колесика
               ...                                                      // И так далее
              end;
             until ClickRead=0;                                         // Читаем следующие события, пока они есть
            end;
         

Daq Pascal Api -> clickbutton

Определение:

function clickbutton:integer

Аргументы:

Нет.

Результат:

Возвращает виртуальный код нажатой клавиши сенсора или ноль, если нажатия не было.

Описание:

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

Одним из основных элементов для организации интерфейсов в системе CRW-DAQ являются мнемосхемы. Мнемосхемы описываются при помощи *.CRC-файлов (от слова circuit). Мнемосхема представляет собой окно с изображением некоторой схемы, взятое из *.BMP-файла, на которой расположены сенсоры - чувствительные области для отображения и управления. Каждый сенсор – это поименованный объект, который содержит набор *.BMP-изображений, ссылку на кривую или тег и на устройство CRW-DAQ, а также горячую клавишу.

Изображение сенсора меняется в зависимости от значения кривой или тега, который с ним связан. Обновление изображений сенсоров ядро CRW-DAQ делает автоматически, по мере изменения состояния связанных с сенсорами кривых или тегов, поэтому заботиться об этом специально не надо. С точки зрения программы интерфейса как бы и нет – есть только набор переменных – тегов, значения которых надо правильно устанавливать.

Более сложной является анимация мнемосхемы. Под анимацией (“оживлением”) мнемосхемы понимается то, что с каждым сенсором связывается некоторое действие или процедура, выполняемая по нажатию правой и левой кнопок мыши на сенсоре, а также при нажатии горячей клавиши shortcut. Для анимации сенсора (например, Button) через устройство - программу (например, &Animate) на DAQ PASCAL надо выполнить следующие действия (смотри пример ниже):

  • Завести в секции [TagList] тег, связанный с сенсором, например, ButtonTag, типа integer или real.
  • Связать сенсор (в данном случае Button) с этим тегом через конструкцию
                    Link sensor Button with device &Animate tag ButtonTag shortcut KeyShortcut
                   
  • Описать устройство &Animate типа program в файле конфигурации.
  • Описать в программе Anamate действия, выполняемые при нажатии кнопок на сенсоре. В результате этих действий значение тега, связанного с сенсором, может измениться, что приведет к смене изображения сенсора.
При нажатии кнопки мыши на изображении сенсора или связанной с сенсором клавиши, ядро CRW-DAQ запоминает это событие, связанное с сенсором и помещает в FIFO устройства, связанного с сенсором. При очередном выполнении программы Daq Pascal событие извлекается из FIFO для обработки. Для доступа к событию существует несколько функций, которых вполне достаточно для организации довольно сложных интерфейсов.

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

  • 0 - Ничего не было нажато, не надо ничего анализировать.
  • 1 = LButton - нажата левая кнопка на одном из сенсоров
  • 2 = RButton - нажата правая кнопка на одном из сенсоров
  • 4 = MButton - нажата средняя кнопка на одном из сенсоров
  • Другие положительные значения появляются либо при нажатии горячей клавиши, заданной через атрибут shortcut, либо при щелчке мыши одновременно с удержанием Alt, Ctrl или Shift.
Подробнее о виртуальных кодах клавиш смотри str2shortcut, shortcut2str.

В программе анимации мнемосхемы всегда надо сначала вызывать clickbutton, чтобы узнать, был ли нажат какой-либо из сенсоров, и лишь при значении clickbutton>0 следует начинать анализ того, какой именно сенсор был нажат. Это позволяет исключить лишние проверки, что повышает скорость работы программ и способствует снижению загрузки цикла обработки данных.

Пример:

      Файл конфигурации:

      [Windows]
      Circuit = Circuit_Window
      [Circuit]
      Link sensor Button with device &Animate tag ButtonTag shortcut Alt+F5
      [TagList]
      ButtonTag = integer 0
      [DeviceList]
      &Animate = device software program
      [&Anamate]
      ProgramSource = Animate.pas

      Файл программы Animate.pas:

      program Animate;
      var b:boolean;
      begin
       if clickbutton>0 then begin
        if clickbutton=str2shortcut('Alt+F5') then writeln('Нажато Alt+F5');
                  writeln('Все параметры нажатия:',clickParams(''));
        if (clicksensor='BUTTON') then begin
         if igettag(clicktag)<>0   then begin
          b:=isettag(clicktag,0);
          b:=voice(‘кнопка отпущена’);
         end else begin
          b:=isettag(clicktag,1);
          b:=voice(‘кнопка нажата’);
         end;
        end;
       end;
      end.
         

Daq Pascal Api -> clicksensor

Определение:

function clicksensor:string

Аргументы:

Нет.

Результат:

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

Описание:

Вызов clicksensor возвращает имя сенсора в окне мнемосхемы или пустую строку, если нажатий какого-либо сенсора не было. Имена сенсоров задаются в *.CRC-файле описания мнемосхемы и всегда преобразуются в верхний регистр. Рекомендуется вызывать clicksensor только после проверки clickbuttont, так как вызов clickbuttont существенно быстрее.
Подробнее о мнемосхемах смотри clickbutton.

Пример:

Смотри clickbutton.

Daq Pascal Api -> clicktag

Определение:

function clicktag:integer

Аргументы:

Нет.

Результат:

Возвращает тег, связанный с нажатым сенсором или ноль.

Описание:

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

Рекомендуется вызывать clicktag только после проверки clickbuttont, так как вызов clickbuttont существенно быстрее.
Подробнее о мнемосхемах смотри clickbutton.

Пример:

Смотри clickbutton.

Daq Pascal Api -> clickparams

Определение:

function clickparams(arg:string):string

Аргументы:

  • arg - имя параметра, который надо узнать.

Результат:

Возвращает по имени значение параметра, связанного с нажатием сенсора.

Описание:

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

Функция дает доступ к большому числу перечисленных ниже параметров, однако ее вызов довольно медленный. Поэтому настоятельно рекомендуется вызывать clickparams только после проверки clickbutton, так как вызов clickbutton существенно быстрее.

  • clickparams('') - вернет все параметры одной длинной строкой. Это удобно для отладочного вывода.
  • clickparams('What') - что произошло:
    1. NOTHING - ничего не было.
    2. MOUSEDOWN - нажата кнопка мыши.
    3. MOUSEUP - отпущена кнопка мыши.
    4. MOUSEMOVE - движение курсора мыши.
    5. KEYDOWN - нажата клавиша на клавиатуре.
    6. KEYUP - отпущена клавиша на клавиатуре.
    7. MOUSEWHEEL - прокручено колесико мыши.
    8. MOUSEWHEELDOWN - нажато колесико мыши.
    9. MOUSEWHEELUP - отпущено колесико мыши.
  • clickparams('Key') - код клавиши без учета регистровых клавиш.
  • clickparams('Shift') - регистровые клавиши: ALT,CTRL,SHIFT...
  • clickparams('Where') - положение мыши x,y
  • clickparams('Wheel') - поворот колесика мыши n, обычно кратно 120
  • clickparams('When') - время (msecnow) в момент нажатия
  • clickparams('Button') - виртуальный код клавиши, смотри str2shortcut
  • clickparams('Window') - имя окна мнемосхемы
  • clickparams('Sensor') - имя сенсора
  • clickparams('Tag') - имя связанного с сенсором тега
  • clickparams('TagName') - имя связанного с сенсором тега
  • clickparams('Curve') - имя связанной с сенсором кривой
  • clickparams('Bounds') - границы сенсора: left,top,right,bottom относительно PaintBox
  • clickparams('ShortCut') - виртуальный код горячей клавиши, смотри str2shortcut
  • clickparams('Pixel') - цвет изображения в точке нажатия, в формате RGB, в HEX-виде
  • clickparams('Value') - значение тега или кривой в момент нажатия
  • clickparams('Bitmap') - имя файла текущего изображения сенсора
  • clickparams('Hint') - текст справочного комментария, ассоциированного с сенсором, в URL_Encode - кодировке. То есть текст справки извлекается как
    s:=URL_Decode(clickparams('Hint')); // Текст справки Hint
  • clickparams('CrcFile') - имя *.CRC файла, из которого загружен сенсор
  • clickparams('Section') - секция *.CRC файла, из которой загружен сенсор
  • clickparams('TagEval(v)') - формула для тега изображения (идентификатора картинки) вычисляемого сенсора, например isBit(v,0)
  • clickparams('LedEval(v)') - формула для циферблата (цифровой надписи) вычисляемого сенсора, например log(10,v)
  • clickparams('Painter(v)') - при наличии сценария рисования сенсора возвращается имя секции (иначе пустая строка). Сам сценарий может быть большим, поэтому его надо считывать из CRC файла по имени секции.
  • clickparams('LedValue') - текущая отображаемая на сенсоре надпись
  • clickparams('BookMark') - идентификатор текущего изображения (из таблицы Tag#1,Tag#2...)
  • clickparams('WindowRect') - абсолютные экранные координаты окна (Left,Top,Right,Bottom)
  • clickparams('PaintBoxRect') - абсолютные экранные координаты PaintBox (Left,Top,Right,Bottom)
  • clickparams('AppFormBounds') - абсолютные экранные координаты (Left,Top,Right,Bottom) главного окна приложения
  • clickparams('AppClientBounds') - абсолютные экранные координаты (Left,Top,Right,Bottom) клиентской области приложения
    Клиентская область приложения - это область (в главном окне), в которой размещаются окна приложения. Координаты WinDraw задаются относительно клиентской области приложения.

Подробнее о мнемосхемах смотри clickbutton.

Пример:

Смотри clickbutton.

Daq Pascal Api -> str2shortcut

Определение:

function str2shortcut(name:string):integer

Аргументы:

  • name - имя виртуальной клавиши.

Результат:

Возвращает числовой код виртуальной клавиши по ее имени или ноль.

Описание:

Вызов str2shortcut возвращает код виртуальной клавиши с заданным именем name. Если имя не распознано, возвращает ноль.

В качестве имен можно применять, например:

      Scroll_Lock   Caps_Lock   Right_Shift   Sys_Req      BkSp   Tab
      Num_5   Enter   Shift   Ctrl   Alt   Esc   Space   PgUp   PgDn   End
      Home   Left   Up   Right   Down   Ins   Del
      0   1   2   3   4   5   6   7   8   9
      A   B   C   D   E   F   G   H   I   J
      K   L   M   N   O   P   Q   R   S   T
      U   V   W   X   Y   Z   Num_*   Num_+   Num_-   Num_Del
      F1   F2   F3   F4   F5   F6   F7   F8   F9   F10
      F11   F12   F13   F14   F15   F16   F17   F18   F19   F20
      F21   F22   F23   F24   Pause   ;   =   ,   -   .
      `   '   [   ]   /   \
         
в комбинации с регистровыми клавишами Alt+, Ctrl+, Shift+.

Надо заметить, что с мышью связаны специальные виртуальные коды, возвращаемые clickbutton, которые также поддерживаются функциями str2shortcut,shortcut2str, хотя они и не являются клавиатурными клавишами. Это такие виртуальные коды:

  • LButton = 1 - левая кнопка мыши
  • RButton = 2 - правая кнопка мыши
  • MButton = 4 - средняя кнопка мыши
а также их производные с регистрами Alt+, Ctrl+, Shift+.
Подробнее о мнемосхемах смотри clickbutton.

Пример:

      if clickbutton>0 then begin
       writeln(shortcut2str(clickbutton));
       if clickbutton=str2shortcut('LButton') then LeftMouseButtonClick;
       if clickbutton=str2shortcut('RButton') then RightMouseButtonClick;
       if clickbutton=str2shortcut('Alt+F1') then Help;
       ...
      end;
         

Daq Pascal Api -> shortcut2str

Определение:

function shortcut2str(code:integer):string

Аргументы:

  • code - код виртуальной клавиши.

Результат:

Возвращает имя виртуальной клавиши по ее числовому коду или пустую строку.

Описание:

Вызов shortcut2str возвращает имя виртуальной клавиши по ее числовому коду или пустую строку для неизвестных кодов. Это обратная функция по отношению к функции str2shortcut.
Подробнее о мнемосхемах смотри clickbutton.

Пример:

Смотри str2shortcut.

Daq Pascal Api -> winshow

Определение:

function winshow(arg:string):boolean

Аргументы:

  • arg - имя окна и список атрибутов рисования.

Результат:

Показывает окно, заданное именем.

Описание:

Вызов winshow помещает в FIFO команду "показать окно с данным именем" и возвращает true, если команда помещена в очередь команд. Команда выполняется асинхронно в низкоприоритетном потоке рисования, чтобы не мешать измерениям. После имени окна можно указывать различные атрибуты рисования, разделяя их символом вертикальной черты |. Подробнее о атрибутах рисования смотри windraw.

Вызов winshow может также создать и открыть консольное окно устройства - программы:

          winshow('Консоль name')
         
где name - имя устройства.

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

  • практически не занимает время при вызове в высокоприоритетном потоке измерительной программы
  • выполняется с задержкой

Пример:

          Пример программы, открывающей свою консоль:

          b:=winshow('Консоль '+devname);
          b:=windraw('Консоль '+devname+'|Top=100|Left=200');
         

Daq Pascal Api -> windraw

Определение:

function windraw(arg:string):boolean

Аргументы:

  • arg - имя окна и список атрибутов рисования.

Результат:

Перерисовывает окно, заданное именем.

Описание:

Вызов windraw помещает в FIFO команду "перерисовать окно с данным именем" и возвращает true, если команда помещена в очередь команд. Команда выполняется асинхронно в низкоприоритетном потоке рисования, чтобы не мешать измерениям. После имени окна можно указывать различные атрибуты (команды) рисования, разделяя их символом вертикальной черты |. Общий формат вызова функции:
         WinDraw('WindowName|Fast|Attribute=Value|Attribute=Value|...)
         где
         WindowName - имя окна, которое надо (пере)рисовать
         Fast       - необязательный режим быстрого рисования (см. описание ниже)
         Attribute  - атрибут (команда), который надо изменить (исполнить)
         Value      - значение атрибута или аргументы команды
         
Чтобы уменьшить число прорисовок, рекомендуется все атрибуты передавать в одном вызове или использовать режим Fast. В то же время существует ограничение на длину строки аргументов (около 245 символов), поэтому возможны ситуации, когда из-за длинного списка аргументов вызов придется разбивать на несколько более коротких.

Режим быстрого рисования Fast (если он используется) должен идти первым, сразу после имени окна. Он позволяет менять отдельные элементы окна, не вызывая полную перерисовку всего окна, как делается обычно. Например:
         b:=WinDraw('Demo Window|Fast|Eval=(ItemColor=clRed)|DrawSensor=Item'); или
         b:=WinDraw('Demo Window|Fast|Eval=(ItemColor=clRed)|UpdateSensor=Item');
         1) находит окно (мнемосхемы) Demo Window
         2) вычисляет в этом окне выражение (ItemColor=clRed)
         3) рисует или обновляет сенсор с именем Item (предполагается, что цветом ItemColor)
         4) окно полностью не перерисовывается, потому что включен режим Fast

         b:=WinDraw('Demo.Plot|Fast|LegendX=Ось абсцисс|LegendY=Ось ординат');
         1) находит окно (график кривых) Demo.Plot
         2) меняет надписи на осях X, Y, заданные LegendX, LegendY
         3) окно полностью не перерисовывается, потому что включен режим Fast
         

Атрибуты рисования имеет вид Имя=Значение. Окна разных типов могут иметь разный набор атрибутов.

  • Для всех окон можно указывать такие параметры:
    • Top=y - координата y верхнего левого угла окна.
    • Left=x - координата x верхнего левого угла окна.
    • Width=w - ширина окна w.
    • Height=h - высота окна h.
    • Options=o1,o2,.. - задает список параметров (опций) окна o1,o2,.... Список опций может содержать такие элементы:
      • -Close - отключает системную кнопку закрытия окна (крестик в верхнем правом углу окна). Используется для создания окон, которые не могут быть случайно закрыты пользователем.
      • +Close - - включает системную кнопку закрытия окна (крескик в верхнем правом углу окна). Используется для создания окон, которые могут быть закрыты пользователем при помощи мыши или Alt-F4.
      • -Min - запретить кнопку минимизации окна.
      • +Min - разрешить кнопку минимизации окна.
      • -Max - запретить кнопку максимизации окна.
      • +Max - разрешить кнопку максимизации окна.
      • -Left - запретить менять горизонтальное положение X окна.
      • +Left - разрешить менять горизонтальное положение X окна.
      • -Top - запретить менять вертикальное положение Y окна.
      • +Top - разрешить менять вертикальное положение Y окна.
      • -Width - запретить менять ширину окна.
      • +Width - разрешить менять ширину окна.
      • -Height - запретить менять высоту окна.
      • +Height - разрешить менять высоту окна.
      • -HScroll - запретить горизонтальную прокрутку окна.
      • +HScroll - разрешить горизонтальную прокрутку окна.
      • -VScroll - запретить вертикальную прокрутку окна.
      • +VScroll - разрешить вертикальную прокрутку окна.
      • -StatusBar - запретить статусную строку в нижней части окна окна.
      • +StatusBar - разрешить статусную строку в нижней части окна окна.
      • -ToolBar - запретить строку инструментов в верхней части окна окна.
      • +ToolBar - разрешить строку инструментов в верхней части окна окна.
      • +ViewScrollLeft - показывать кнопку сдвиг ВЛЕВО в окне мнемосхемы.
      • -ViewScrollLeft - убрать(скрыть) кнопку сдвиг ВЛЕВО в окне мнемосхемы.
      • +ViewScrollRight - показывать кнопку сдвиг ВПРАВО в окне мнемосхемы.
      • -ViewScrollRight - убрать(скрыть) кнопку сдвиг ВПРАВО в окне мнемосхемы.
      • +ViewScrollUp - показывать кнопку сдвиг ВВЕРХ в окне мнемосхемы.
      • -ViewScrollUp - убрать(скрыть) кнопку сдвиг ВВЕРХ в окне мнемосхемы.
      • +ViewScrollDown - показывать кнопку сдвиг ВНИЗ в окне мнемосхемы.
      • -ViewScrollDown - убрать(скрыть) кнопку сдвиг ВНИЗ в окне мнемосхемы.
      Примечания:
      Опции LEFT,TOP зависят от флага UseFixedPosMdiChildren в файле Crw32.ini. При этом надо учитывать, что при указании -Left, -Top, -Width, -Height фиксируются текущие положение и размеры окна. То есть надо сначала указать положение и размеры, а потом только фиксировать их.
    • SaveBmp=n,f - сохранение изображения окна в файл *.bmp.
      Параметр f задает имя *.bmp файла. Расширение может отсутствовать, а путь может быть указан относительно основного конфигурационного файла.
      Параметр n задает формат изображения и принимает значения:
      • 1Bit - Черно-белое изображение, рекомендуемое для печати.
      • 4Bit - 16 цветное изображение.
      • 8Bit - 256 цветное изображение.
      • 16Bit - 16 битное изображение.
      • 24Bit - 24 битное изображение, RGB, рекомендуемое для цветных изображений.
      • 32Bit - 32 битное изображение.

  • Для окон-графиков CURVE_WINDOW можно также задавать:
    • LegendX=s - надпись s в нижней части окна.
    • LegendY=s - надпись s в верхней части окна.
    • При указании надписи используются символы форматирования:
      • ^L=Left
      • ^R=Right
      • ^C=Center
      • ^N=NewLine
    • Range=ex1;ey1;ex2;ey2 - диапазон ex1<=x<=ex2,ey1<=y<=ey2 окна.
    • Roi=ex1;ey1;ex2;ey2 - маркеры ex1,ey1 и ex2,ey2 области интереса ROI. Для отмены ROI надо задать значение _nan. Формульные выражения ex1,ey1,ex2,ey2, разделенные точкой с запятой, могут ссылаться на следующие переменные:
      • x1,y1,x2,y2 - текущий диапазон окна.
      • xmin,ymin,xmax,ymax - диапазон данных в окне.
      • roix1,roiy1,roix2,roiy2 - диапазон ROI в окне.
    • SelectCurve=s - выбор в окне кривой с именем s. Если нет такой кривой или указана пустая строка, выбор кривой отменяется, то есть отображаются все кривые.
    • SaveCrw=s - сохранение окна в *.CRW файл с именем s. Имя файла может указываться без расширения, в этом случае расширение по умолчанию .CRW. Путь файла может быть указан как полный, так и относительно основного конфигурационного файла. Например: "SaveCrw=..\data\tp20050116".

  • Для окон-мнемосхем CIRCUIT_WINDOW можно также задавать:
    • DrawSensor=s - рисует (только) сенсор с указанным именем s. Эту функцию рекомендуется использовать в режиме Fast, чтобы избежать перерисовки всего окна. Вызов этой функции требуется, например, если сенсор зависит от переменной, которая меняется вызовом windraw('WindowName|Fast|Eval=...|DrawSensor=...').
    • UpdateSensor=s - обновляет сенсор с указанным именем s. Обновление сенсора выполняется существенно быстрее, чем рисование, но работает только в случае, когда 1)сенсор использует функции Painter(v) и 2)к сенсору подключены данные (кривая/тег). Если сенсор не использует функции Painter(v), он и так будет обновлен автоматически. А если к сенсору не подключены данные, то он вообще не входит в цикл обновления. Обновление отличается от рисования тем, что при обновлении вместо непосредственного рисования устанавливается флаг обновления, а само рисование происходит позже, в цикле обновления сенсоров. За счет этого можно существенно снизить нагрузку системы при частом обновлении сенсора. Эту функцию рекомендуется использовать в режиме Fast, чтобы избежать перерисовки всего окна. Вызов этой функции требуется, например, если сенсор зависит от переменной, которая меняется вызовом windraw('WindowName|Fast|Eval=...|UpdateSensor=...').
    • Reload=s - заставляет перезагрузить изображения для указанного сенсора с именем s. Вызов Reload=All перезагружает все сенсоры. Функция предназначена для работы с сенсорами, изображения которых могут формироваться автоматически из программы. Например:
                        b:=windraw('Ip4.Viewer|Reload=Ip4.Bitmap');
                       
    • Scroll=sx;sy - задает сдвиг изображения, то есть положения скроллеров по X, Y. Функция предназначена для работы с большими мнемосхемами, изображения которых могут передвигаться командами из программы. Вызов функции эквивалентен ручному сдвигу скроллеров в заданные положения sx, sy. Каждое из положений может быть задано в формульном виде с использованием переменных:
      • xpos - текущее значение горизонтального скроллера.
      • ypos - текущее значение вертикального скроллера.
      • xmin - минимальное значение горизонтального скроллера.
      • ymin - минимальное значение вертикального скроллера.
      • xmax - максимальное значение горизонтального скроллера.
      • ymax - максимальное значение вертикального скроллера.
      При задании положений также можно использовать * для чтобы не изменять положение (эквивалентно xpos или ypos). Например:
                        // Фиксация положения окон:
                        b:=windraw('DemoPlot|Left=0|Options=-Left');   // Задать и зафиксировать горизонтальное положение окна
                        b:=windraw('DemoPlot|Top=50|Options=-Top');    // Задать и зафиксировать вертикальное   положение окна
                        // Скроллинг мнемосхем:
                        b:=windraw('DemoPlot|Scroll=100;*');           // Установить горизонтальный сдвиг 100, вертикальный не менять
                        b:=windraw('DemoPlot|Scroll=*;150');           // Установить вертикальный сдвиг 150, горизонтальный не менять
                        b:=windraw('DemoPlot|Scroll=100;50');          // Установить горизонтальный сдвиг 100, вертикальный 150
                        b:=windraw('DemoPlot|Scroll=xmin;ymin');       // Сдвинуться в левый  верхний угол  (это начальное положение)
                        b:=windraw('DemoPlot|Scroll=xmin;ymax');       // Сдвинуться в левый  нижний  угол  (пролистать вниз   до конца)
                        b:=windraw('DemoPlot|Scroll=xmax;ymin');       // Сдвинуться в правый верхний угол  (пролистать вправо до конца)
                        b:=windraw('DemoPlot|Scroll=xmax;ymax');       // Сдвинуться в правый нижний  угол  (пролистать вниз и вправо)
                        b:=windraw('DemoPlot|Scroll=xpos+10;ypos+20'); // Сдвинуться на 10 пикселей вправо и на 20 вниз
                       
    • Eval=e - выполнить выражение e в интерпретаторе окна. Например, вызов Eval=(alarm=100) задает значение переменной alarm=100. Для удобства присвоение (alarm=100) можно помещать в скобки. Скобки позволяют также задавать сразу несколько переменных. Например, выражение: Eval=(alarm=100)+(limit=20)+(count=10) задает сразу три переменные alarm=100, limit=20, count=10.
      Функция предназначена для работы с вычисляемыми сенсорами, содержащими формулы LedEval(v), TagEval(v) для расчета отображаемого состояния. Она позволяет задавать значения параметров, используемых в формулах LedEval(v), TagEval(v). Например:
                        [Circuit.StartupScript]                       initialization
                        alarm=10                                      initial value of "alarm" parameter
                        ...
                        TagEval(v) = gt(v,alarm)                      tag evaluation depends on "alarm"
                        ...
                        b:=windraw('Demo.Control|Eval=(alarm=100)');  change value of "alarm"
                       
      Из соображений производительности число вызовов windraw('..|Eval=..') для изменения параметров интерпретатора следует сократить до минимума, то есть делать вызов только при действительном изменении параметров. При этом, во избежание перерисовки всего окна, можно использовать режим Fast.
  • Для окон-поверхностей plot3d можно также задавать:
    • Legend=s - надпись s в нижней части окна.
    • Title=s - надпись s в верхней части окна.
    • Phi=s - азимутальный угол s в градусах.
    • Psi=s - аксиальный угол s в градусах.
    • ScaleX=s - масштаб s по оси X.
    • ScaleY=s - масштаб s по оси Y.
    • ScaleZ=s - масштаб s по оси Z.
    • Mode=s - режим s являющийся суммой битов
      • 1 : рисовать сечения X.
      • 2 : рисовать сечения Y.
      • 4 : рисовать цифры поверх графика.
    • ROI=x1;y1;x2;y2 - задает видимый регион x1,y1,x2,y2 в виде выражений, в которых можно использовать переменные:
      • (x1,y1,x2,y2) - текущие пределы ROI
      • (xmin,ymin,xmax,ymax) - максимально возможные пределы ROI
      Например, ROI=(x1+x2)/2;(y1+y2)/2;xmax;ymax
    • SliceX=n,c - заменить X-сечение номер n данными из кривой с именем c.
    • SliceY=n,c - заменить Y-сечение номер n данными из кривой с именем c.
    • SaveCrw=s - сохранение окна в *.CRW файл с именем s. Имя файла может указываться без расширения, в этом случае расширение по умолчанию .CRW. Путь файла может быть указан как полный, так и относительно основного конфигурационного файла. Например: "SaveCrw=..\data\tp20050116".
  • Для окон-спектров Spectr_Window можно также задавать:
    • LoadSpd=f - загрузка в окно спектра из SPD-файла с именем f. При задании имени файла можно использовать короткие имена (относительно основного конфиг-файла).
    • SaveSpd=f - сохранение спектра из окна в SPD-файл с именем f. При задании имени файла можно использовать короткие имена (относительно основного конфиг-файла).

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

  • практически не занимает время при вызове в высокоприоритетном потоке измерительной программы
  • выполняется с задержкой

Пример:

          b:=windraw('Температурные_кривые'+
                     '|Top=100|Left=200|Width=600|Height=400'
                     '|LegendY=^CTemperature in box^N^L  {Celsius}'+
                     '|LegendX=^RHours  ^N^CTime'+
                     '|Range=max(x1,xmax);y1;xmax+x2;ymax+y2'+
                     '|Roi=roix1;roiy1;_nan;_nan'+
                     '|SelectCurve=T#1'+
                     '|SaveCrw=..\data\tmp.crw');
          b:=windraw('Температурные_кривые|Options=-Min,-Max,-Close,-Width,-Height');
          b:=windraw('Температурные_кривые|SaveBmp=24bit,c:\data\image.bmp');
          b:=windraw('Spectrum|LoadSpd=..\Data\demo.spd');
         

Daq Pascal Api -> winhide

Определение:

function winhide(arg:string):boolean

Аргументы:

  • arg - имя окна.

Результат:

Прячет окно, заданное именем.

Описание:

Вызов winhide помещает в FIFO команду "спрятать окно с данным именем" и возвращает true, если команда помещена в очередь команд. При выполнении команды окно прячется (сворачивается в иконку), применяется для освобождения места на экране. Команда выполняется асинхронно в низкоприоритетном потоке рисования, чтобы не мешать измерениям.

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

  • практически не занимает время при вызове в высокоприоритетном потоке измерительной программы
  • выполняется с задержкой

Пример:

          Пример программы, закрывающей свою консоль:

          b:=winhide('Консоль '+devname);
         

Daq Pascal Api -> winselect

Определение:

function winselect(arg:string):boolean

Аргументы:

  • arg - имя окна.

Результат:

Активизирует окно, заданное именем.

Описание:

Вызов winselect помещает в FIFO команду "активизировать окно с данным именем" и возвращает true, если команда помещена в очередь команд. При выполнении команды окно активизируется, то есть помещается поверх всех других окон и получает фокус ввода. Команда выполняется асинхронно в низкоприоритетном потоке рисования, чтобы не мешать измерениям.

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

  • практически не занимает время при вызове в высокоприоритетном потоке измерительной программы
  • выполняется с задержкой

Пример:

          Пример программы, активизирующей свою консоль:

          b:=winshow('Консоль '+devname);
          b:=winselect('Консоль '+devname);
         

Daq Pascal Api -> add3d

Определение:

function add3d(x,y,z:real):boolean

Аргументы:

  • x,y,z - координаты точки в трехмерной Декартовой системе координат.

Результат:

Помещает в буфер plot3d точку и возвращает true при успехе.

Описание:

Функция add3d служит для добавления точек, по которым будет строится 3D график в виде поверхности, во внутренний буфер. Точки могут идти в произвольном порядке, причем они не обязаны лежать на регулярной сетке и могут располагаться случайно в плоскости (x,y). Точки, содержащие _nan,_inf в буфер не заносятся.
Надо иметь в виду, что внутренний буфер недоступен, пока не завершена предыдущая операция рисования трехмерного графика. В этом случае функция вернет false, а точка добавлена не будет. Чтобы узнать, свободен ли буфер, не занося в него точек, можно сделать так:
if add3d(_nan,0,0) then {буфер свободен}

При построении трехмерной поверхности рекомендуется по возможности использовать регулярную прямоугольную сетку (x,y), так как в этом случае рисование на порядок быстрее.

Пример:

Смотри plot3d.

Daq Pascal Api -> plot3d

Определение:

function plot3d(nx,ny:integer;x1,y1,x2,y2:real;opt:string):boolean

Аргументы:

  • nx,ny - задает число точек на графике по x,y.
  • x1,y1,x2,y2 - задает прямоугольную область рисования.
  • opt - атрибуты (опции) рисования.

Результат:

Рисует график трехмерной поверхности и возвращает true при успехе.

Описание:

Функция plot3d выполняет рисование трехмерного графика, заданного набором точек из внутреннего буфера, занесенных туда функцией add3d. При построении графика выполняется интерполяция набора точек (в общем случае случайного) из буфера на регулярную (прямоугольную) сетку в плоскости (x,y). Интерполяция делается с использованием триангуляции по методу Делоне (Delaunay).
Вызов plot3d также очищает буфер. Чтобы очистить буфер без рисования, можно вызвать plot3d(0,0,0,0,0,0,''). Если предыдущая операция рисования не завершена, функция вернет false, не производя никаких действий.

Допустимые атрибуты opt такие:

  • Caption,Title,Legend - заголовок и надписи.
  • top,left,width,height - положение и размеры окна в пикселях.
  • phi,psi - угол зрения в градусах.
  • scalex,scaley,scalez - масштабирующие факторы.
  • replace - 0/1 для того чтобы не/замещать окно графика, если окно с таким заголовком уже есть. То есть при новой прорисовке предыдущее окно уничтожается, если replace=1. Это чтобы не плодить много окон.
При построении трехмерной поверхности рекомендуется по возможности использовать регулярную прямоугольную сетку (x,y), так как в этом случае рисование на порядок быстрее.

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

  • практически не занимает время при вызове в высокоприоритетном потоке измерительной программы
  • выполняется с задержкой

Пример:

            procedure show3d(nx,ny:integer; x1,y1,x2,y2:real; formula:string);
            var ix,iy:integer; x,y,z:real;
            begin
             if add3d(_nan,0,0) then begin
              for ix:=0 to nx-1 do
              for iy:=0 to ny-1 do begin
               x:=x1+(x2-x1)*ix/(nx-1);
               y:=y1+(y2-y1)*iy/(ny-1);
               b:=evar('x',x);
               b:=evar('y',y);
               z:=eval(formula);
               b:=add3d(x,y,z);
              end;
              b:=plot3d(nx,ny,x1,y1,x2,y2,
                 'Caption=Plot3d'+
                 '|Title=Заголовок|Legend=Легенда'+
                 '|top=100|left=100|width=600|height=400'+
                 '|scalex=1|scaley=1|scalez=1'+
                 '|phi=60|psi=85'+
                 '|replace=1');
              b:=plot3d(0,0,0,0,0,0,'');
             end else writeln('Could not plot now!');
            end;
         

Daq Pascal Api -> specmark specmarkl specmarkr specroil specroir

Определение:

function specmark(win:string):real
function specmarkl(win:string):real
function specmarkr(win:string):real
function specroil(win:string):real
function specroir(win:string):real

Аргументы:

  • win - имя окна.

Результат:

Работа с маркерами спектрометрического окна.

Описание:

Работа с маркерами спектрометрического окна.

specmark(win) - функция возвращает текущий маркер спектрометрического окна с именем win.

specmarkl(win) - функция возвращает левый маркер спектрометрического окна с именем win.

specmarkr(win) - функция возвращает правый маркер спектрометрического окна с именем win.

specroil(win) - функция возвращает левый маркер ROI спектрометрического окна с именем win.

specroir(win) - функция возвращает правый маркер ROI спектрометрического окна с именем win.

Пример:

          Вычисление интеграла по ROI:
          a:=specroil('Spectr');
          b:=specroir('Spectr');
          integ:=crvinteg(refai(0),a,b);
         

Daq Pascal Api -> edit editstate

Определение:

function edit(params:String):String
function editstate:Integer

Аргументы:

  • params - параметры редактирования.

Результат:

edit используется для организации диалогов с пользователем.
editstate используется для проверки статуса диалогов.

Описание:

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

Проблема с модальными диалогами в том, что программа Daq Pascal выполняется в своем потоке и не может (да и не должна) напрямую вызывать какие-либо диалоги. Ведь в этом случае поток программы будет приостановлен на время выполнения диалога, что делать нельзя. Ведь неправильно приостанавливать поток измерительной DAQ-программы во время редактирования, так как редактирование - длительная операция, а измерения идут непрерывно. Поэтому логика работы диалогов DAQ Pascal другая - асинхронная. Или, фактически, логика Конечного Автомата (Finite State Machine). При этом алгоритм работы автомата основан на анализе переменных состояния в режиме опроса. Функция EditState служит для быстрого получения общего (сводного) состояния, а функция Edit('?..') - для получения наиболее подробного состояния.

Когда редактор свободен (EditState=0 или EditStateReady), программа может инициировать диалог. При инициировании с диалогом связывается некий идентификатор, который при завершении диалога используется для интерпретации результатов. Инициирование диалога состоит в следующем: программа при помощи вызовов edit формирует задание для редактирования и продолжает выполнение. В цикле опроса анализируется статус редактора, а само редактирование выполняется асинхронно, в другом потоке, в соответствии с заданием. После инициирования задания на редактирование через некоторое время, после ввода пользователем данных и нажатия Ok или Cancel, появится статус готовности ответа (EditState=1 или EditStateDone). По этому статусу можно читать результат редактирования. При чтении результата используется идентификатор завершившегося диалога, что позволяет различать разные диалоги, если в программе их несколько. Такова, вкратце, логика редактирования в DAQ Pascal.

function editstate:integer - возвращает статус редактирования. Это комбинация флагов:

  • 0 - редактор свободен (EditStateReady), можно инициировать новое редактирование.
  • 1 - редакторование успешно закончено (EditStateDone), можно читать результат.
  • 2 - редактор находится в состоянии редактирования (EditStateBusy), окно открыто.
  • 4 - редактор находится в состоянии подготовки к редактированию (EditStateBusy).
  • 8 - редактор обнаружил ошибку редактирования (EditStateError). Это либо ошибочные аргументы, либо в момент исполнения в программе уже открыто какое-то модальное окно. Чтобы не возникало проблем с множеством модальных окон, запрещено открывать более одного модального окна.
Временно могут возникать суммарные комбинации этих флагов, но не надолго.

function edit(params:string):string - основная функция редактирования. Редактирование получается за счет многократного вызова этой функции с разными аргументами. Перед вызовом edit надо проверять editstate.

Вызов edit('') используется, для начальной очистки редактора, при этом происходит сброс флагов 1,4,8 редактора, флаг 2 сбрасывается только самим редактором.

При вызове edit(params) с непустым значением params<>'', работа функции определяется первым символом params[1]:

  • '(' (открывающая скобка) - команда начать подготовку нового задания для редактирования (inp = input). После открывающей скобки идет строка, добавляемая в задание для редактора. Возвращается статус editstate или ? при ошибке.
  • ' ' - (пробел) продолжить подготовку задания для редактирования (inp = input). После пробела идет строка, добавляемая в задание для редактора. Возвращается статус editstate или ? при ошибке.
  • '@' - ("собака") - добавляет в задание редактора параметры настройки (set = setting). После "собаки" идет строка, добавляемая в список параметров настройки для редактирования. Например, параметрами могут быть описания шрифтов, размеры и координаты окна редактора. Возвращается статус editstate или ? при ошибке.
  • '!' - ("восклицательный знак") - добавляет в задание редактора список подтверждений (con = confirm). После знака "!" идет строка, добавляемая в список пользовательских подтверждений. Список пользовательских подтверждений доступен через вызов edit('?con n') по номеру n. Пользовательские подтверждения никак не интерпретируются и находятся целиком в распоряжении программиста. По замыслу, в этом списке хранятся строки для диалогов подтверждения, которые нужно будет запросить в конце редактирования, в зависимости от результата редактирования, введенного пользователем. Возвращается статус editstate или ? при ошибке.
  • '>' - ("знак больше") - добавляет в задание редактора пользовательские команды (cmd = command). После знака ">" идет строка, добавляемая в список пользовательских команд. Список пользовательских команд доступен через вызов edit('?cmd n') по номеру n. Пользовательские команды никак не интерпретруются и находятся целиком в распоряжении программиста. По замыслу, в этом списке хранятся команды, которые нужно будет запускать в конце редактирования, в зависимости от результата редактирования, введенного пользователем. Возвращается статус editstate или ? при ошибке.
  • ')' (закрывающая скобка) - закончить подготовку задания для редактирования и послать задание - строку запроса (request) редактору. После закрывающей скобки идет тип диалога и его идентификатор, который служит потом для того, чтобы верно интерпретировать результат редактирования. Если идентификатора нет, его заменяет тип диалога. Возвращается статус editstate или ? при ошибке.
  • '?' (знак вопроса) - прочитать данные. Дальше идет уточнение, что именно надо прочитать:
    • ? - прочитать статус, аналог editstate.
    • ?types - прочитать разделенный запятыми список доступных типов диалогов: Warning,YesNo,..,FileOpenDialog,SelectDirectoryDialog.
    • ?req - прочитать разделенную пробелами строку запроса (request) редактирования, включая тип диалога, его идентификатор и другие параметры.
    • ?ans count - прочитать число строк в буфере результата (answer) редактирования.
    • ?ans text - прочитать весь текст из буфера результата (answer) редактирования.
    • ?ans n - прочитать строку номер n из буфера результата (answer) редактирования.
    • ?inp count - прочитать число строк в буфере подготовки (input) задания для редактора.
    • ?inp text - прочитать весь текст из буфера подготовки (input) задания для редактирования.
    • ?inp n - прочитать строку номер n из буфера подготовки (input) задания для редактирования.
    • ?set count - прочитать число строк в буфере параметров (setting) редактирования.
    • ?set text - прочитать весь текст из буфера параметров (setting) редактирования.
    • ?set n - прочитать строку номер n из буфера параметров (setting) редактирования.
    • ?cmd count - прочитать число строк в буфере пользовательских команд (command) редактирования.
    • ?cmd text - прочитать весь текст из буфера пользовательских команд (command) редактирования.
    • ?cmd n - прочитать строку номер n из буфера пользовательских команд (command) редактирования.
    • ?con count - прочитать число строк в буфере пользовательских подтверждений (confirm) редактирования.
    • ?con text - прочитать весь текст из буфера пользовательских подтверждений (confirm) редактирования.
    • ?con n - прочитать строку номер n из буфера пользовательских подтверждений (confirm) редактирования.
    • ?ask count - прочитать число строк в буфере текущего редактируемного задания.
    • ?ask text - прочитать весь текст из буфера текущего редактируемного задания.
    • ?ask n - прочитать строку номер n из буфера текущего редактируемного задания.

В нулевой строке результата ?ans 0 возвращается строка Name=Result, где Name - идентификатор диалога, который был указан при создании диалога, Result - статус редактирования, это одна из констант:

Другие строки результата (?ans 0, ?ans 1,...), если они нужны, определяются типом диалога.
Типы диалога такие:

  • Warning - диалог-предупреждение.
    На входе задается заголовок (первая строка) и отображаемый текст (остальные строки), на выходе используется только статус (?ans 0)=(1=Ok/2=Cancel).
    При описании диалога Warning и аналогичных ему диалогов YesNo, YesNoCancel, Information, Error допустимо использовать настройки (setting) для задания координат (@set Form.Left ... и @set Form.Top ...) аналогичные диалогу MenuList. При этом другие параметры игнорируются.
    Пример инициирования диалога:
                   if editstate=0 then begin
                    if pos('?',edit('(Нет связи!')
                              +edit(' Проверьте провода!')
                              +edit('@set Form.Left '+ExtractWord(1,ClickParams('Bounds'))+' relative '+ClickParams('Window')+' PaintBox')
                              +edit('@set Form.Top  '+ExtractWord(4,ClickParams('Bounds'))+' relative '+ClickParams('Window')+' PaintBox')
                              +edit(')Warning NetworkWarning'))>0
                    then writeln('Ошибка инициирования диалога!');
                   end else writeln('В настоящий момент редактирование недоступно!');
                  
    Пример анализа результата:
                   {редактирование завершено?}
                   if editstate=1 then begin
                    if IsSameText(edit('?ans 0'),'NetworkWarning=1') then {Ok} else
                    if IsSameText(edit('?ans 0'),'NetworkWarning=2') then {Cancel};
                    s:=edit('');{Сброс}
                   end;
                   {найдена ошибка?}
                   if iand(editstate,8)<>0 then begin
                    writeln('Dialog error found!');
                    s:=edit('');{Сброс}
                   end;
                  
  • YesNo - диалог-подтверждение/отказ.
    На входе задается заголовок (первая строка) и отображаемый текст (остальные строки), на выходе используется только статус (?ans 0)=(6=Yes/7=No). Инициирование и анализ - аналогично диалогу Warning.
  • YesNoCancel - диалог-подтверждение/отказ/отмена.
    На входе задается заголовок (первая строка) и отображаемый текст (остальные строки), на выходе используется только статус (?ans 0)=(6=Yes/7=No/2=Cancel). Инициирование и анализ - аналогично диалогу Warning.
  • Information - диалог-справка.
    На входе задается заголовок (первая строка) и отображаемый текст (остальные строки), на выходе - только статус (1=Ok/2=Cancel). Инициирование и анализ - аналогично диалогу Warning.
  • Error - диалог-сообщение об ошибке.
    На входе задается заголовок (первая строка) и отображаемый текст (остальные строки), на выходе используется только статус (?ans 0)=(1=Ok/2=Cancel). Инициирование и анализ - аналогично диалогу Warning.
  • MenuList - меню выбора.
    На входе задается:
    • Строка № 1 = Заголовок окна меню
    • Строка № 2 = Комментарий объясняющий альтернативу
    • Строка № 3 = 1 пункт меню
    • Строка № 4 = 2 пункт меню
    • ...etc...
    В последней строке, кроме идентификатора диалога, можно указать номер пункта по умолчанию (нумерация с нуля).
    На выходе:
    • ?ans 0 - статус (1=Ok/2=Cancel).
    • ?ans 1 - если статус = 1=Ok, строка содержит номер выбранного пункта (нумерация с нуля), иначе возвращается пустая строка.
    При описании диалога MenuList допустимо использовать следующие параметры настроек (setting):
    • @set Panel.Font FFF - описание шрифта панели
    • @set ListBox.Font FFF - описание шрифта списка
    • @set Form.Left LLL - описание координаты левого края окна
    • @set Form.Top TTT - описание координаты верхнего края окна
    • @set Form.Width WWW - описание ширины окна
    • @set Form.Height HHH - описание высоты окна
    В описании шрифтов FFF используется формат, аналогичный тому, который используется в описании сенсоров мнемосхем.
    Например:
    edit('@set ListBox.Font Name:PT_Mono\Size:10\Color:Black\Style:[Bold]') - задать шрифт списка меню
    В описании координат LLL, TTT и размеров WWW, HHH используется число, вслед за которым может следовать ключевое слово relative (относительно) и указание идентификатора окна и (возможно) идентификатора визуального элемента в этом окне, относительно которого указывается координата или размер. Если ключевое слово relative не указано, координаты или размеры задаются в абсолютных единицах (пикселях на экране). При указании относительных координат их значения корректируются с учетом расположения указанного окна или элемента. При указании относительных размеров размер берется в процентах от указанного окна или элемента.
    В качестве идентификатора окна можно указывать:
    • Ссылку Screen на Экран или Desktop на Рабочий Стол или WorkArea на Рабочую Область Экрана.
    • Заголовок окна (например, мнемосхемы). Его можно узнать, например, вызовом ClickParams('Window') при обработке нажатий сенсоров мнемосхем.
    • Ссылку вида Class::Name на окно по имени класса (class) и имени окна (name). Например, главное окно пакета имеет ссылку TFormCrw32::FormCrw32. Имя класса или имя окна можно заменить маской *, если оно не имеет значения. Например, главное окно пакета можно описать как TFormCrw32::FormCrw32 или как *::FormCrw32 или как TFormCrw32::* (это возможно потому, что существует только одна форма класса TFormCrw32).
    В качестве идентификатора визуального элемента используется его имя (не путать с заголовком). Имена визуальных элементов для окна заданного класса фиксированы и не меняются. Имена окон и их элементов можно узнать командой Главной Консоли @view list. Например, для мнемосхем координаты удобно указывать относительно основного поля мнемосхемы с именем PaintBox. В этом случае при обработке нажатий сенсоров можно использовать координаты сенсора, полученные вызовом ClickParams('Bounds'), которые также указаны относительно основного поля мнемосхемы PaintBox.
    Например:
                  edit('@set Form.Left 200')                            - указание абсолютной координаты на экране
                  edit('@set Form.Width 80 relative Screen')            - ширина окна 80% относительно ширины экрана
                  edit('@set Form.Top 20 relative Desktop')             - координата относительно Рабочего Стола (зависит от настроек Windows)
                  edit('@set Form.Left 30 relative DemoEdit PaintBox')  - координата относительно окна с заголовком DemoEdit, элемента PaintBox
                  edit('@set Form.Width 40 relative *::FormCrw32')      - ширина 40% относительно ширины главного окна пакета
                  edit('@set Form.Height 50 relative TFormCrw32::*')    - высота 50% относительно высоты главного окна пакета
                  

    Пример инициирования диалога по нажатию сенсора, с заданием шрифта и координат окна:
                  if (ClickButton=1) then
                  if IsSameText(ClickSensor,'Demo') then begin
                    if editstate=0 then begin
                     if pos('?',edit('(Выбор термопары')
                               +edit(' Задайте термопару')
                               +edit(' Термопара 1') + edit('>@termocouple 1')  // Здесь задается текст
                               +edit(' Термопара 2') + edit('>@termocouple 2')  // пунктов меню, а также
                               +edit(' Термопара 3') + edit('>@termocouple 3')  // пользовательские команды
                               +edit('@set Panel.Font   Name:PT_Mono\Size:12\Color:Maroon\Style:[Bold]')
                               +edit('@set ListBox.Font Name:PT_Mono\Size:24\Color:Navy\Style:[Bold]')
                               +edit('@set Form.Left '+ExtractWord(1,ClickParams('Bounds'))+' relative '+ClickParams('Window')+' PaintBox')
                               +edit('@set Form.Top  '+ExtractWord(4,ClickParams('Bounds'))+' relative '+ClickParams('Window')+' PaintBox')
                               +edit('@set Form.Width 300')+edit('@set Form.Height 230')
                               // Другие варианты задания размеров:
                               //+edit('@set Form.Width  40 relative Desktop')+edit('@set Form.Height 50 relative Screen')
                               //+edit('@set Form.Width  50 relative '+ClickParams('Window'))+edit('@set Form.Height 50 relative '+ClickParams('Window'))
                               //+edit('@set Form.Width  40 relative *::FormCrw32')+edit('@set Form.Height 50 relative TFormCrw32::*')
                               +edit(')MenuList MENU_TC 2'))>0
                              // Здесь 2 - номер текущего пункта, начиная с 0
                     then writeln('Ошибка инициирования диалога!');
                    end else writeln('В настоящий момент редактирование недоступно!');
                   end;
                  
    Пример анализа результата:
                   {редактирование завершено?}
                   if editstate=1 then begin                                 // Если редактирование завершено
                    if IsSameText(edit('?ans 0'),'MENU_TC=1')                // Если статус результата = 1 = OK
                    then n:=val(edit('?ans 1')) else n:=-1;                  // Узнать номер n выбранного пункта
                    s:=Trim(edit('?cmd '+Str(n)));                           // Извлечь пользовательскую команду s
                    if Pos('@',s)=1 then b:=devpostmsg(devname+' '+s+CRLF);  // Если она есть, послать её в консоль
                    s:=edit('');                                             // Сброс редактора в исходное состояние
                   end;
                   {найдена ошибка?}
                   if iand(editstate,8)<>0 then begin
                    writeln('Dialog error found!');
                    s:=edit('');{Сброс}
                   end;
                  
  • SelectionList - список выбора.
    На входе задается:
    • Строка № 1 = Заголовок окна списка
    • Строка № 2 = Комментарий объясняющий смысл списка
    • Строка № 3 = 1 пункт списка
    • Строка № 4 = 2 пункт списка
    • ...etc...
    В последней строке, кроме идентификатора диалога, можно указать номер пункта по умолчанию (нумерация с нуля), а также список выделенных пунктов (в виде числа - битовой маски).
    На выходе:
    • ?ans 0 - статус (1=Ok/2=Cancel).
    • ?ans n - если статус = 1=Ok, строки содержат номера выбранных элементов списка (нумерация с нуля).
    При описании диалога SelectionList допустимо использовать параметры настроек (setting), аналогичные диалогу MenuList.
    Пример инициирования диалога:
                   if editstate=0 then begin
                    if pos('?',edit('(Выбор термопар')
                              +edit(' Задайте термопары')
                              +edit(' Термопара 1')
                              +edit(' Термопара 2')
                              +edit(' Термопара 3')
                              +edit(' Термопара 4')
                              +edit(' Термопара 5')
                              +edit('@set Panel.Font   Name:PT_Mono\Size:12\Color:Green\Style:[Bold]')
                              +edit('@set ListBox.Font Name:PT_Mono\Size:14\Color:Magenta\Style:[Bold]')
                              +edit('@set Form.Left 200 relative DemoEdit PaintBox')
                              +edit('@set Form.Top  250 relative DemoEdit PaintBox')
                              +edit('@set Form.Width 300')+edit('@set Form.Height 220')
                              +edit(')SelectionList SEL 0 5'))>0
                              // Здесь 0 - текущий пункт, 5 - битовая маска выбранных пунктов
                    then writeln('Ошибка инициирования диалога!');
                   end else writeln('В настоящий момент редактирование недоступно!');
                  
    Пример анализа результата:
                   {редактирование завершено?}
                   if editstate=1 then begin
                    if IsSameText(edit('?ans 0'),'SEL=1') then
                    for i:=1 to val(edit('?ans Count'))-1 do
                    writeln('Select element № ',edit('?ans '+str(i)));
                    s:=edit('');{Сброс}
                   end;
                   {найдена ошибка?}
                   if iand(editstate,8)<>0 then begin
                    writeln('Dialog error found!');
                    s:=edit('');{Сброс}
                   end;
                  
  • CheckBoxList - список выбора с флажками (CheckBox).
    На входе задается:
    • Строка № 1 = Заголовок окна списка
    • Строка № 2 = Комментарий объясняющий смысл списка
    • Строка № 3 = 1 пункт списка
    • Строка № 4 = 2 пункт списка
    • ...etc...
    В последней строке, кроме идентификатора диалога, можно указать номер пункта по умолчанию (нумерация с нуля), а также список отмеченных флажками пунктов (в виде числа - битовой маски).
    На выходе:
    • ?ans 0 - статус (1=Ok/2=Cancel).
    • ?ans n - если статус = 1=Ok, строки содержат номера отмеченных флажками элементов списка (нумерация с нуля).
    При описании диалога CheckBoxList допустимо использовать параметры настроек (setting), аналогичные диалогу MenuList. Пример инициирования диалога:
                   if editstate=0 then begin
                    if pos('?',edit('(Выбор термопар')
                              +edit(' Задайте используемые термопары')
                              +edit(' Термопара 1')
                              +edit(' Термопара 2')
                              +edit(' Термопара 3')
                              +edit(' Термопара 4')
                              +edit(' Термопара 5')
                              +edit('@set Panel.Font   Name:PT_Mono\Size:12\Color:Green\Style:[Bold]')
                              +edit('@set ListBox.Font Name:PT_Mono\Size:14\Color:Magenta\Style:[Bold]')
                              +edit('@set Form.Left 200 relative DemoEdit PaintBox')
                              +edit('@set Form.Top  250 relative DemoEdit PaintBox')
                              +edit('@set Form.Width 300')+edit('@set Form.Height 220')
                              +edit(')CheckBoxList SEL 0 5'))>0
                              // Здесь 0 - текущий пункт, 5 - битовая маска отмеченных флажками пунктов
                    then writeln('Ошибка инициирования диалога!');
                   end else writeln('В настоящий момент редактирование недоступно!');
                  
    Пример анализа результата:
                   {редактирование завершено?}
                   if editstate=1 then begin
                    if IsSameText(edit('?ans 0'),'SEL=1') then
                    for i:=1 to val(edit('?ans Count'))-1 do
                    writeln('Select element № ',edit('?ans '+str(i)));
                    s:=edit('');{Сброс}
                   end;
                   {найдена ошибка?}
                   if iand(editstate,8)<>0 then begin
                    writeln('Dialog error found!');
                    s:=edit('');{Сброс}
                   end;
                  
  • TextEdit - редактор текста.
    На входе задается:
    • Строка № 1 = Заголовок окна редактора
    • Строка № 2 = Текст, строка 1
    • Строка № 3 = Текст, строка 2
    • ...etc...
    На выходе:
    • ?ans 0 - статус (1=Ok/2=Cancel).
    • ?ans n - если статус = 1=Ok, строки содержат отредактированный текст.
    При описании диалога TextEdit допустимо использовать параметры настроек (setting), аналогичные диалогу MenuList, с той лишь разницей, что вместо ListBox.Font используется Editor.Font. Остальные параметры настроек одинаковы.
    Пример инициирования диалога:
                   if editstate=0 then begin
                    if pos('?',edit('(Отредактируйте комментарий')
                              +edit(' Эта калибровка')
                              +edit(' снята при температуре 25°C.')
                              +edit('@set Editor.Font Name:PT_Mono\Size:14\Color:Blue\Style:[Bold]')
                              +edit(')TextEdit EDIT_NOTE'))>0
                    then writeln('Ошибка инициирования диалога!');
                   end else writeln('В настоящий момент редактирование недоступно!');
                  
    Пример анализа результата:
                   {редактирование завершено?}
                   if editstate=1 then begin
                    if IsSameText(edit('?ans 0'),'EDIT_NOTE=1') then
                    for i:=1 to val(edit('?ans Count'))-1 do
                    writeln('Line ',i,' -> ',edit('?ans '+str(i)));
                    s:=edit('');{Сброс}
                   end;
                   {найдена ошибка?}
                   if iand(editstate,8)<>0 then begin
                    writeln('Dialog error found!');
                    s:=edit('');{Сброс}
                   end;
                  
  • StringGridEdit - редактор таблиц.
    На входе задается:
    • Строка № 1 = Заголовок окна
    • Строка № 2 = Описание строки 1
    • Строка № 3 = Описание строки 2
    • ...etc...
    Cтроки описания таблицы содержат элементы, которой разделены символом - разделителем |. Например, Row1|1.23|10|20 задает строку таблицы, содержащую 4 элемента: Row1, 1.23, 10 и 20. Первый элемент (столбец) таблицы всегда нередактируемый, он служит для комментария. Остальные пункты содержат редактируемые поля.

    На выходе:

    • ?ans 0 - статус (1=Ok/2=Cancel).
    • ?ans n - если статус = 1=Ok, строки содержат отредактированную таблицу в том же формате (столбцы разделены вертикальной чертой).
    При описании диалога StringGridEdit допустимо использовать параметры настроек (setting), аналогичные диалогу MenuList, с той лишь разницей, что вместо ListBox.Font используется StringGrid.Font. Остальные параметры настроек одинаковы.
    Пример инициирования диалога:
                   if editstate=0 then begin
                    if pos('?',edit('(Отредактируйте таблицу')
                              +edit(' Temperature|10|20|30')
                              +edit(' Pressure|100|200|300')
                              +edit('@set StringGrid.Font Name:PT_Mono\Size:14\Color:Red\Style:[Bold]')
                              +edit(')StringGridEdit EDIT_TAB'))>0
                    then writeln('Ошибка инициирования диалога!');
                   end else writeln('В настоящий момент редактирование недоступно!');
                  
    Пример анализа результата:
                   {редактирование завершено?}
                   if editstate=1 then begin
                    s:=worddelims('|');
                    if IsSameText(edit('?ans 0'),'EDIT_TAB=1') then
                    for i:=1 to 3 do begin
                     T[i]:=val(ExtractWord(i+1,edit('?ans 1')));
                     P[i]:=val(ExtractWord(i+1,edit('?ans 2')));
                    end;
                    s:=worddelims(s);
                    s:=edit('');{Сброс}
                   end;
                   {найдена ошибка?}
                   if iand(editstate,8)<>0 then begin
                    writeln('Dialog error found!');
                    s:=edit('');{Сброс}
                   end;
                  
  • FileOpenDialog - диалог выбора файла или списка файлов.
    На входе задается:
    • Строка № 1 = Заголовок диалога
    • Строка № 2 = Имя файла
    • Строка № 3 = Фильтр 1
    • Строка № 4 = Фильтр 2
    • ...etc...
    В качестве имени файла можно указать или реальное имя файла, или маску файла, вроде c:\daq\*.crw. Cтроки описания фильтров содержат пары элементов Описание|СписокРасширений|, которые разделены символом - разделителем | (вертикальная черта). Например, фильтр Тексты (*.txt)|*.txt| задает фильтр для файлов *.txt.

    На выходе:

    • ?ans 0 - статус (1=Ok/2=Cancel).
    • ?ans n - если статус = 1=Ok, строки содержат имена выбранных файлов.

    При описании диалога FileOpenDialog допустимо использовать настройки (setting) для задания координат (@set Form.Left ... и @set Form.Top ...) аналогичные диалогу MenuList. При этом другие параметры игнорируются.

    Пример инициирования диалога:
                   if pos('?',edit('(Please open file(s)')
                             +edit(' c:\daq\*.cfg')
                             +edit(' Text files (*.txt)|*.txt|')
                             +edit(' Config files (*.cfg)|*.cfg|')
                             +edit(' All files (*.*)|*.*|')
                             +edit('@set Form.Left '+ExtractWord(1,ClickParams('Bounds'))+' relative '+ClickParams('Window')+' PaintBox')
                             +edit('@set Form.Top  '+ExtractWord(4,ClickParams('Bounds'))+' relative '+ClickParams('Window')+' PaintBox')
                             +edit(')FileOpenDialog FOPEN'))>0
                    then writeln('Ошибка инициирования диалога!');
                   end else writeln('В настоящий момент редактирование недоступно!');
                  
    Пример анализа результата:
                   {редактирование завершено?}
                   if editstate=1 then begin
                    if IsSameText(edit('?ans 0'),'FOPEN=1') then
                    for i:=1 to val(edit('?ans Count'))-1 do
                    writeln('Selected file ',edit('?ans '+str(i)));
                    s:=edit('');{Сброс}
                   end;
                   {найдена ошибка?}
                   if iand(editstate,8)<>0 then begin
                    writeln('Dialog error found!');
                    s:=edit('');{Сброс}
                   end;
                  
  • SelectDirectoryDialog - диалог выбора существующих или создания новых каталогов.
    На входе задается:
    • Строка № 1 = Заголовок диалога
    • Строка № 2 = Начальное имя каталога
    • Строка № 3 = Список (необязательный) опций {sdAllowCreate, sdPerformCreate, sdPrompt}
    В качестве начального имени каталога указывается каталог, который будет выделен в начале диалога.
    Cтрока опций может отсутствовать, быть пустой или содержать список (набор) следующих опций:
    • sdAllowCreate - в диалоге разрешено задавать новые (не существующие) каталоги.
    • sdPerformCreate - если задан несуществующий каталог, создать его на выходе диалога.
    • sdPrompt - при создании нового каталога запрашивать подтверждение.
    Строка опций влияет на вид диалога,а также на возможность редактирования имени каталога.

    На выходе:

    • ?ans 0 - статус (1=Ok/2=Cancel).
    • ?ans 1 - если статус = 1=Ok, строка содержат имя выбранного или созданного каталога.

    При описании диалога SelectDirectoryDialog допустимо использовать настройки (setting) для задания координат (@set Form.Left ... и @set Form.Top ...) аналогичные диалогу MenuList. При этом другие параметры игнорируются.

    Пример инициирования диалога:
                   if pos('?',edit('(Please select directory')
                             +edit(' '+ParamStr('DaqConfigPath'))
                             +edit(' sdAllowCreate, sdPerformCreate, sdPrompt')
                             +edit('@set Form.Left '+ExtractWord(1,ClickParams('Bounds'))+' relative '+ClickParams('Window')+' PaintBox')
                             +edit('@set Form.Top  '+ExtractWord(4,ClickParams('Bounds'))+' relative '+ClickParams('Window')+' PaintBox')
                             +edit(')SelectDirectoryDialog SELDIR'))>0
                    then writeln('Ошибка инициирования диалога!');
                   end else writeln('В настоящий момент редактирование недоступно!');
                  
    Пример анализа результата:
                   {редактирование завершено?}
                   if editstate=1 then begin
                    if IsSameText(edit('?ans 0'),'SELDIR=1') then
                    writeln('Selected directory ',edit('?ans 1'));
                    s:=edit('');{Сброс}
                   end;
                   {найдена ошибка?}
                   if iand(editstate,8)<>0 then begin
                    writeln('Dialog error found!');
                    s:=edit('');{Сброс}
                   end;
                  

Пример:

Демонстрационная конфигурация demoedit.

Другой пример (программа редактирования строки):

          program editstr;
          var s:string; x:real;
          begin
           {инициирование диалога при нажатии сенсора EDIT_X}
           if (clickbutton=1) and (clicksensor='EDIT_X') then
           if editstate=0 then begin
            if pos('?',edit('(Отредактируйте переменную:')
                      +edit(' X=|10')
                      +edit(')StringGridEdit EDIT_X'))>0
            then writeln('Не могу инициировать диалог!');
           end;
           {анализ результата}
           if editstate=1 then begin
            if extractword(1,edit('?ans 0'))='EDIT_X' then begin
             if extractword(2,edit('?ans 0'))='1' then x:=rval(copy(edit('?ans 1'),4,255));
             s:=edit(''); {сброс}
            end;
           end;
           {сброс по ошибке};
           if editstate=8 then s:=edit('');
          end.
         

Daq Pascal Api -> guard_check

Определение:

function guard_check(level:string):integer

Аргументы:

  • level - название требуемого уровня доступа (Lock,Guest,User,Root).

Результат:

Сравнивает текущий уровень доступа с требуемым уровнем level.

Описание:

Функция применяется для определения уровня доступа текущего пользователя и применяется для организации защищенного интерфейса пользователя средствами DAQ Pascal (при обработке событий от мнемосхем).

Напомним, что система CRW-DAQ имеет систему безопасности (guard), которая опирается на понятие уровня доступа. В каждый момент времени система имеет некий текущий уровень доступа, который определяет набор доступных для оператора возможностей, причем смена уровней доступа защищена паролями.

В настоящее время предусмотрены такие уровни доступа:

  • Lock - блокировка программы. Запрещено почти все, кроме смены уровня доступа.
  • Guest - уровень постороннего человека, Гостя. Запрещено управлять измерительной системой (запуск/останов DAQ и т.д.), завершать работу программы, редактировать программы и конфигурации.
  • User - уровень оператора, выполняющего обычную работу на установке. Он имеет право на запуск/останов измерительных конфигураций, редактирование калибровок и текстовых файлов, но не может изменять сами программы или конфигурации, а также запускать внешние программы.
  • Root - уровень Администратора DAQ системы, имеющего все права на все операции.

Функция guard_check(level) сравнивает текущий уровень доступа с уровнем, заданным строкой level, и возвращает

  • -1 - текущий уровень доступа ниже уровня level, надо отказать в предоставлении прав этого уровня.
  • 0 - текущий уровень доступа равен уровню level, можно предоставить права доступа этого уровня.
  • +1 - текущий уровень доступа выше уровня level, надо предоставить права доступа этого уровня.
Таким образом, например,
         if guard_check('user') < 0 then ... отказать в правах уровня User
         if guard_check('user') >=0 then ... предоставить права уровня User
         

Пример:

          if clickbutton=1 then begin
           if clicksensor='START' then
           if guard_check('root') < 0
           then writeln('You need Root access level to START!')
           else Start;
           if clicksensor='STOP' then
           if guard_check('user') < 0
           then writeln('You need User access level to STOP!')
           else Stop;
          end;
         

Daq Pascal Api -> comopen

Определение:

function comopen(s:string):boolean

Аргументы:

  • s - имя секции с описанием порта.

Результат:

Открывает порт последовательной связи и возвращает true при успехе.

Описание:

Функция открывает порт последовательной связи (COM-порт), описанный в секции s. Под словом COM-порт здесь понимается логический последовательный порт. Это может быть реальный COM-порт типа RS-232,RS-485, или именованный канал связи (NamedPipe) для сетевого обмена по Ethernet, или канал связи через сетевой сокет (порт TCP/IP). Прикладной программе, в принципе, должно быть все равно, как реализован канал связи - через порт RS-232 или порт Ethernet. Как именно реализован канал связи, определяет содержимое секции s.

Группа функций comopen, comclose, comcount, comspace, comread, comwrite ориентирована на работу с портом последовательной связи, то есть связи с дисциплиной доступа типа FIFO. Функции поддерживают работу в дуплексном режиме, то есть с возможностью одновременной передачи данных в двух направлениях. В первую очередь это RS-232 для связи с измерительными устройствами и именованные каналы (NamedPipe) для сетевого обмена по Ethernet. Эта группа функций пригодна в принципе и для RS-485, однако, поскольку RS-485 поддерживает только полудуплексный режим работы (одновременно допустима только передача в одном направлении), а также имеет адресацию устройств, драйвер должен сам обеспечивать корректную реализацию полудуплексного режима и адресации всех устройств на шине. Но вообще лучше работать с RS-485 через функции Adam_XXX.

Программа Daq Pascal может обслуживать только один COM-порт. Для обслуживания нескольких портов параллельно запускается несколько DAQ-программ. Либо используются каналы связи, см. функции pipe_xxxx. При необходимости совместной работы параллельные программы обмениваются данными через теги, кривые или сообщения. Заметим, что возможна одновременная работа с RS-232 и RS-485, так как RS-485 имеет свой набор функций Adam_XXX.

Надо иметь в виду, что именованные каналы доступны только под Windows-NT/2K/XP/7/8/10/11. При сетевом обмене один экземпляр должен быть сервером, другой клиентом (возможно, на другой машине). Параметры порта считываются из указанной в строке s секции в файле конфигурации CRW-DAQ, например,

          b:=comopen('[SerialPort-COM2]');
         
или
          b:=comopen('[PipeServer]');
         

Надо учитывать, что процедура comopen монопольно захватывает порт, не проверяя, занят ли он другими устройствами. По этой причине нельзя подключать на этот порт другие устройства DAQ, иначе будет конфликт. Поэтому, в частности, нельзя использовать процедуры comXXX для того же порта, к которому подключены устройства серии ADAM или другое устройство program, использующее этот же порт. Однако можно бесконфликтно использовать устройства серии ADAM и Program на разных портах.

Следует иметь в виду, что все функции COM-порта реализованы асинхронно. Есть два FIFO-буфера, через которые принимаются и передаются данные, приемник и передатчик. Собственно чтение-запись осуществляет отдельный программный поток драйвера порта. При записи данных в порт данные немедленно помещаются в буфер передатчика и программа продолжает выполнение. Параллельно работающая программа драйвера порта читает данные из буфера передатчика и передает их в порт. Данные, прочитанные драйвером из порта, складываются в буфер приемника, откуда программа их забирает. Таким образом, во всех случаях вызовы функций являются неблокирующими, то есть не приводят к приостановке потока программы.

При реализации драйверов устройств RS-232 также необходимо стремиться к асинхронной работе программы. Пусть, например, устройству посылается сообщение msg и в течение 100 миллисекунд ожидается ответ из 3 символов. Самая простая реализация:

          t:=msecnow;
          b:=comwrite(msg);
          while (comcount<3) and (msecnow-t<100) do;
          ans:=comread(3);
         
приведет к тому, что процессор в течение 100 миллисекунд будет занят ненужными проверками в цикле ожидания, а поток будет стоять без дела. Загрузку процессора можно устранить при помощи Sleep:
          t:=msecnow;
          b:=comwrite(msg);
          while (comcount<3) and (msecnow-t<100) do b:=Sleep(1);
          ans:=comread(3);
         
однако и в этом случае поток программы будет заблокирован и ничего полезного сделать не сможет.

Правильным подходом будет введение переменной состояния, например, state. При записи данных в порт переменная состояния устанавливается в 1, при получении ответа или при превышении времени ожидания сбрасывается в 0.

         var t:real; state:integer;
         begin
          if state=0 then begin
           t:=msecnow;
           b:=comwrite(msg);
           state:=1;
          end else
          if state=1 then begin
           if comcount=3 then begin
            ans:=comread(3);
            state:=0;
           end;
           if msecnow-t>=100 then begin
            writeln('timeout');
            state:=0;
           end;
          end;
          DoSomethingElse;
         end;
         
Программа, конечно, несколько усложнилась, так как теперь запрос и ответ обрабатываются в разных вызовах программы, в разное время. Однако теперь программа никогда не блокируется, нет никаких задержек и циклов ожидания. Поэтому, независимо от состояния запроса, при каждом входе программа будет выполнять процедуру DoSomeThingElse, то есть делать что-то полезное помимо обмена данными с портом. Таков общий подход к построению DAQ программ, который можно обозначить термином асинхронное программирование.

Конфигурирование ComOpen(s) с именем секции s идет в таком порядке:
  1. Проверяется параметр PipeLine = ....
    Если этот параметр указан, открывается канал связи с помощью вызова pipe_init(...), которому передается строка параметров. Весь ввод-вывод направляется в этот канал связи. Это наиболее общий случай, позволяющий реализовать связь через последовательный порт (COM Port), именованный канал (Pipe) или сетевой сокет (TCP Port). Однако этот способ сложнее в конфигурировании, см. pipe_init(...).
  2. Проверяется параметр PipeName = ....
    Если этот параметр указан, открывается именованный канал с заданным именем.
  3. Проверяется параметр Port = COMn и BoudRate = ....
    Если этот параметр указан, открывается обычный COM порт с заданным номером n и скоростью обмена.

Конфигурирование сетевого канала (PipeLine).

  • PipeLine = ... - строка описания канала. Наличие описания PipeLine является признаком того, что порт открывается как сетевой канал. Параметры канала передаются в функцию pipe_init(...). В дальнейшем вся связь осуществляется через указанный канал, например:
       [SerialPort-COM1]                                                        ; Разные варианты канала связи:
       PipeLine = Com Port 1 Baudrate 9600 Parity NONE DataBits 8 StopBits 1    ; Обычный COM порт
       PipeLine = Pipe DEMO Polling 4 Priority tpHighest ListenPeriod 100       ; Именованный канал DEMO (сервер)
       PipeLine = Pipe .\DEMO Polling 4 Priority tpHighest ListenPeriod 100     ; Именованный канал DEMO (клиент)
       PipeLine = Tcp Port 1234 Server 1 Polling 4 Priority tpHighest           ; Сокет TCP:1234 (сервер)
       PipeLine = Tcp Port 1234 Client localhost Polling 4 Priority tpHighest   ; Сокет TCP:1234 (клиент)
      
При задании строки PipeLine можно использовать переменные окружения (например, %ComputerName%). Следует учитывать, что при создании серверного сетевого канала TCP поддерживается только один клиент, поэтому при указании параметров TCP сервера надо указывать Server 1 (один клиент). В принципе сервер TCP может обслуживать много клиентов. Однако в данном случае эта возможность не используется.

Конфигурирование именованного канала (NamedPipe).

  • PipeName = ... - имя (дентификатор) канала. Наличие описания PipeName является признаком того, что порт открывается как именовынный канал. Для сервера задается имя канала pipename, для клиента задается имя сервера и имя канала hostname\pipename. Наличие имени сервера служит признаком, что канал открывается как клиент. Клиент на локальной машине открывается как .\pipename, то есть с точкой в качестве имени сервера.
  • FifoSize = 16 - Размер буфера fifo в килобайтах
  • PipePolling = 10, tpNormal - Параметры потока опроса канадла
  • TimeOut = 1000 - Периодичность проверки сети при установке связи, мс
  • ListenPeriod = 500 - Периодичность проверки (accept,connect) при установке соединения, влияет на скорость установления связи при подключении клиента к каналу
Заметим, что значения по умолчанию FifoSize=16, PipePolling=10,tpNormal, TimeOut=1000, ListenPeriod = 500. Указание PipeName обязательно.

Конфигурирование RS-232/485.

  • [SerialPort-COM1] - имя секции COM-порта: COM1...COM32
  • Port = COM1 - Идентификатор COM-порта: COM1...COM32
  • BaudRate = 115200 - Скорость передачи, бод
  • Parity= NONE - Проверка на четность: NONE/ODD/EVEN/MARK/SPACE
  • DataBits = 8 - Число бит данных: 5/6/7/8
  • StopBits = 1 - Число стоп-бит: 1/1.5/2
  • XonXoff= 0 - Использовать ли протокол XOn-XOff: 0/1
  • BufSize = 8 - Размер буфера fifo в килобайтах
  • DcbFlags = 0 - Флаги, задающие режим COM-порта:
    • $0004 - CTS output flow control
    • $0008 - DSR output flow control
    • $0010 - DTR flow control type = software
    • $0020 - DTR flow control type = hardware hadshake
    • $0040 - DSR sensitivity
    • $0080 - XOFF continues Tx
    • $0100 - XON/XOFF out flow control
    • $0200 - XON/XOFF in flow control
    • $0400 - enable error replacement
    • $0800 - enable null stripping
    • $1000 - RTS flow control = software
    • $2000 - RTS flow control = hardware handshake
    • $3000 - RTS flow control = hardware handshake, toggle
Заметим, что значения по умолчанию Parity=NONE, DataBits=8, StopBits=1, XonXoff=0, BifSize=8. Указание Port, BaudRate обязательно.

Пример:


          Примеры описаний порта:

          [SerialPort-COM2]    ; Секция описания COM2-порта
          Port     = COM2      ; Идентификатор COM-порта номер 2
          BaudRate = 115200    ; Скорость передачи, бод
          Parity   = NONE      ; Проверка на четность: NONE/ODD/EVEN/MARK/SPACE
          DataBits = 8         ; Число бит данных: 5/6/7/8
          StopBits = 1         ; Число стоп-бит: 1/1.5/2
          XonXoff  = 0         ; Использовать ли протокол Xon-Xoff: 0/1
          BufSize  = 8         ; Размер буфера fifo в килобайтах
          DcbFlags = $3004     ; Флаги, задающие режим COM-порта: в данном случае CTS/RTS

          [PipeServer]                  ; секция описания именованного канала (сервер)
          PipeName     = postbox        ; идентификатор канала
          FifoSize     = 16             ; размер буфера, КБ
          PipePolling  = 10, tpNormal   ; поток опроса канала
          TimeOut      = 1000           ; тайм-аут для установки связи, миллисекунд
          ListenPeriod = 100            ; период опроса (accept), миллисекунд

          [PipeClient]                  ; секция описания именованного канала (клиент)
          PipeName     = host\postbox   ; компьютер\канал
          FifoSize     = 16             ; размер буфера, КБ
          PipePolling  = 10, tpNormal   ; поток опроса канала
          TimeOut      = 1000           ; тайм-аут для установки связи
          ListenPeriod = 100            ; период опроса (connect), миллисекунд

          [TcpServer]                   ; Секция описания сетевого сокета (сервер)
          PipeLine = tcp port 1234 server 1 polling 4 priority tpHighest

          [TcpClient]                   ; Секция описания сетевого сокета (клиент)
          PipeLine = tcp port 1234 client localhost polling 4 priority tpHighest


          Пример терминала RS-232:

          Файл _RS232.cfg

          [DeviceList]
          &RS232 = device software program
          [&RS232]
          Port = SerialPort-COM1 ; Секция описания порта
          ProgramSource = _RS232 ; Программа
          [SerialPort-COM1]      ; описание COM-порта номер 1
          Port     = COM1        ; идентификатор COM-порта номер 1
          BaudRate = 9600        ; скорость передачи, бод
          Parity   = NONE        ; проверка на четность - NONE, ODD, EVEN
          DataBits = 8           ; число бит данных
          StopBits = 1           ; число стоп-бит
          XonXoff  = 0           ; использовать ли протокол XON/XOFF
          BufSize  = 8           ; размер буфера fifo в килобайтах
          DcbFlags = 0           ; дополнительные флаги RS-232, при использовании CTS/RTS/DTR/DTS

          Файл _RS232.pas:

          program _RS232;
          var
           b,Ok:Boolean; errors,errorcode,n:Integer; inp,out:string;
          begin
           {
           Initialization actions on Start
           }
           if runcount=1 then begin
            errors:=0;
            errorcode:=registererr(devname);
            writeln(devname+': start');
            inp:='';
            out:='';
            if not comopen('['+readini('Port')+']') then begin
             writeln(devname+': Could not open COM port!');
             errors:=errors+1;
            end;
            b:=winshow('Консоль '+devname);
            if errors<>0 then b:=fixerror(errorcode);
            Ok:=(errors=0);
           end else
           {
           Finalization actions on Stop
           }
           if isinf(runcount) then begin
            writeln(devname+': stop');
            inp:='';
            out:='';
            b:=comclose;
           end else
           {
           Actions on Poll
           }
           if Ok then begin
            if not eof then begin
             readln(out);
             if comspace>length(out)+1 then begin
              if comwrite(out+chr(13))
              then writeln('SEND:':9,' ',out)
              else writeln('SEND ERROR');
             end else begin
              writeln('Tx overflow.');
              b:=comclear;
             end;
            end;
            if comcount>0 then inp:=inp+comread(comcount);
            n:=pos(chr(13),inp);
            if n>0 then begin
             writeln('RECEIVED:':9,' ',copy(inp,1,n-1));
             inp:='';
            end;
            if ioresult<>0 then b:=fixerror(errorcode);
           end;
          end.
         

Daq Pascal Api -> comclose

Определение:

function comclose:boolean

Аргументы:

Нет.

Результат:

Закрывает порт и возвращает true при успехе.

Описание:

Функция закрывает порт, если он был открыт при помощи comopen.

Пример:

Смотри comopen.

Daq Pascal Api -> comclear

Определение:

function comclear:boolean

Аргументы:

Нет.

Результат:

Очищает буфер передатчика COM-порта и возвращает true при успехе.

Описание:

Функция очищает FIFO-буфер передатчика порта, открытого при помощи comopen. Буфер приемника очищается при помощи чего-то вроде while comcount>0 do s:=comread(255).

Пример:

Смотри comopen.

Daq Pascal Api -> comcount

Определение:

function comcount:integer

Аргументы:

Нет.

Результат:

Возвращает число принятых символов в буфере приемника.

Описание:

Функция возвращает число доступных для чтения принятых из порта символов в буфере ввода COM-порта или значение –1, если порт не был открыт.

Пример:

Смотри comopen.

Daq Pascal Api -> comspace

Определение:

function comspace:integer

Аргументы:

Нет.

Результат:

Возвращает объем свободной памяти в буфере передатчика.

Описание:

Функция возвращает свободное пространство (в байтах) в буфере передатчика COM-порта или значение –1, если порт не был открыт. Функция дает возможность перед записью данных в порт проверить – есть ли место в буфере передатчика и, возможно, приостановить вывод, если места нет.

Пример:

Смотри comopen.

Daq Pascal Api -> comwrite

Определение:

function comwrite(s:string):boolean

Аргументы:

  • s - данные для передачи.

Результат:

Записывает данные s в буфер передатчика.

Описание:

Функция записывает данные, находящиеся в строке s, в буфер передатчика. Передача данных в порт выполняется асинхронно, в отдельном программном потоке.

Пример:

Смотри comopen.

Daq Pascal Api -> comread

Определение:

function comread(n:integer):string

Аргументы:

  • n - число байт для чтения.

Результат:

Считывает данные длиной не более n из буфера приемника.

Описание:

Функция считывает данные, длиной не более n, из буфера приемника. Прием данных из порта выполняется асинхронно, в отдельном программном потоке.

Пример:

Смотри comopen.

Daq Pascal Api -> adam_status adam_get adam_request adam_reqtime

Определение:

function adam_status:integer
function adam_get(what:string):string
function adam_request(request:string;timeout:integer):boolean
function adam_reqtime:real

Аргументы:

  • what - параметр для adam_get.
  • request - запрос, то есть данные для посылки удаленному модулю.
  • timeout - время ожидания ответа, msec.

Результат:

Функции для работы с устройствами в сети RS-485.

Описание:

Для работы в сети RS-485 функции прямой работы с портом типа comread не очень подходят, вместо них используются функции adam_xxx. Причина в том, что в случае RS-485 нужна специальная дисциплина опроса устройств - устройства должны опрашиваться последовательно, а не асинхронно, так как все устройства используют один канал связи. При этом в конфигурации должно присутствовать устройство типа Adam_Slot, а ссылка Adam_Slot_Device - указывать на это устройство. Устройство Adam_Slot служит для того, чтобы через него DAQ-программа получила корректный доступ к RS-485.

adam_status возвращает статус устройства Adam_Slot в сети RS-485:

  • 0 = NotAvail - Сеть RS-485 недоступна. Скорее всего это означает неверную ссылку в переменной Adam_Slot_Device. Либо COM-порт недоступен (отсутствует или занят другой программой).
  • 1 = NoRequest - Запрос не был возбужден. В этом состоянии можно вызывать функцию adam_request для возбуждения следующего запроса.
  • 2 = WaitQueue - Запрос возбужден и ожидает очереди. Это значит, что запрос передан в устройство Adam_Slot, но еще не передан в сеть из-за того, что она была занята.
  • 3 = WaitAnswer - Запрос возбужден и передан по линии связи, ожидается ответ.
  • 4 = Answer - Принят ответ на запрос. Его можно получить вызовом функции adam_get('answer').
  • 5 = TimeOut - Ошибка TimeOut. Это значит, что ответ на запрос не был принят в течение установленного времени.
Если статус был Answer или TimeOut, то после вызова функции статус автоматически сбрасывается в NoRequest, чтобы исключить возможность ошибочной повторной обработки уже обработанных запросов.

adam_get(what) - эта функция в зависимости от параметра what возвращает:

  • 'REQUEST' - Последний возбужденный вызовом adam_request запрос.
  • 'ANSWER' - Ответ на последний возбужденный запрос.
  • 'ADDRESS' - Строка - шестнадцатеричный адрес Adam_Slot.
  • 'PORT' - Порт, к которому подключено устройство Adam_Slot.
  • 'USESCHECKSUM' - Используются ли контрольные суммы в устройстве. Возвращает значение '0' или '1'.
Регистр символов what не играет роли, однако не должно быть лишних пробелов.

adam_request(request,timeout) - эта функция возбуждает запрос request, который передается в устройство Adam_Slot для последующей передачи в сеть RS-485, когда это станет возможным. Абсолютное значение timeout определяет время ожидания ответа на запрос в миллисекундах, причем если timeout=0, используется стандартное время, а если отрицательное - подавляется генерация ошибки TimeOut.

adam_reqtime - возвращает время передачи последнего запроса в сеть RS-485. Фактически это время вызова adam_request.

Пример:

Примитивный пример - опрос каналов ADAM-7018.
          [SerialPort-COM1]
          Port = COM1
          BaudRate = 115200
          Parity = NONE
          DataBits = 8
          StopBits = 1
          XonXoff = false

          [DeviceList]
          &Slot18 = device adam adam_slot
          [&Slot18]
          InquiryPeriod = 40
          Port = 1
          Address = $08
          UsesCheckSum = 1

          [DeviceList]
          &Adam18 = device software program
          [&Adam18]
          InquiryPeriod = 40
          DebugMode = 3
          ProgramSource = .\_slot18
          Adam_Slot_Device = &Slot18

          program _slot18;
          const                { Коды adam_status:                                       }
           rs_NotAvail   = 0;  { RS-485 недоступен, вероятно, неверен Adam_Slot_Device   }
           rs_NoRequest  = 1;  { Запрос очищен - можно слать новый запрос Adam_Request   }
           rs_WaitQueue  = 2;  { Запрос послан в слот, ждем его передачи на линию RS     }
           rs_WaitAnswer = 3;  { Запрос послан, передан на линию RS, ждем ответа с линии }
           rs_Answer     = 4;  { Пришел ответ Adam_Get('Answer') на запрос               }
           rs_TimeOut    = 5;  { TimeOut - не пришел ответ в течение заданного срока     }
          var b:boolean; i:integer; s,addr:string;
           procedure raiserequest;
           begin
            i:=i+1;
            s:='#'+addr+str(i mod 8);
            writeln('Request: ',s);
            if not adam_request(s+chr(13),100) then writeln('Error');
           end;
          begin
           if runcount=1 then begin
            s:='';
            addr:=adam_get('address');
            writeln('Address > ',adam_get('address'));
            writeln('Port    > ',adam_get('port'));
           end else
           if isinf(runcount) then begin
            writeln('STOP');
           end else
           begin
            case adam_status of
             rs_NotAvail   : writeln('COM port is not available.');
             rs_NoRequest  : raiserequest;
             rs_WaitQueue  : writeln('Wait queue...');
             rs_WaitAnswer : writeln('Wait answer...');
             rs_Answer     : begin
                              writeln('Request > ',adam_get('request'));
                              writeln('Time    > ',adam_reqtime:14:8);
                              writeln('Answer  > ',adam_get('answer'));
                              raiserequest;
                             end;
             rs_TimeOut    : begin
                              writeln('TimeOut');
                              raiserequest;
                             end;
            end;
           end;
          end.
         
   

Daq Pascal Api -> inportb inportw inportl outportb outportw outportl

Определение:

function inportb(addr:integer):integer
function inportw(addr:integer):integer
function inportl(addr:integer):integer
function outportb(addr,data:integer):integer
function outportw(addr,data:integer):integer
function outportl(addr,data:integer):integer

Аргументы:

  • addr - адрес порта ввода-вывода.
  • data - данные для вывода в порт.

Результат:

Функции для работы с портами ввода-вывода.

Описание:

Функции для работы с портами ввода-вывода. При работе под Windows-NT/2000/XP используют драйвер GIVEIO.SYS, при этом CRW-DAQ должен быть запущен с правами Администратора. Функции работы с портами применяются, например, при работе с устройствами на шине ISA или PCI.

Функции inportb, inportw, inportl предназначены для чтения данных из порта ввода.

  • inportb(addr) возвращает байт (byte) из порта ввода с адресом addr.
  • inportw(addr) возвращает слово (word) из двух байт из порта ввода с адресом addr.
  • inportl(addr) возвращает слово (long) из четырех байт из порта ввода с адресом addr.

Функции outportb, outportw, outportl предназначены для записи данных в порт вывода. Все три функции возвращают значение записываемых данных data.

  • outportb(addr,data) записывает байт (byte) data в порт вывода с адресом addr.
  • outportw(addr,data) записывает слово (word) из двух байт data в порт вывода с адресом addr.
  • outportl(addr,data) записывает слово (long) из четырех байт data в порт вывода с адресом addr.

Во всех случаях данные передаются через число Integer, только используется в нем 1, 2 или 4 младших байта.

Пример:

         addr:=val('$300');
         di:=inportw(addr);
         data:=GetData;
         do:=outportw(addr+2,data);
         

Daq Pascal Api -> task_init task_free task_ref task_pid task_run task_wait task_send task_recv task_txcount task_txspace task_rxcount task_rxspace task_result task_kill task_ctrl getcomspec getshell shellexecute pidlist pidkill pidaffinity devaffinity

Определение:

function task_init(cmdline:string):integer
function task_free(tid:Integer):Boolean
function task_ref(tid:Integer):Integer
function task_pid(tid:Integer):Integer
function task_run(tid:Integer):Boolean
function task_wait(tid,timeout:Integer):Boolean
function task_send(tid:Integer; data:String):Integer
function task_recv(tid,maxlen:Integer):String
function task_txcount(tid:Integer):Integer
function task_rxcount(tid:Integer):Integer
function task_txspace(tid:Integer):Integer
function task_rxspace(tid:Integer):Integer
function task_result(tid:Integer):Integer
function task_kill(tid,how,exit_code,timeout:Integer):Boolean
function task_ctrl(tid:Integer; param:String):String
function GetComSpec:String
function GetShell:String
function ShellExecute(args:String):Integer
function PidList(txt:Integer):Integer
function PidKill(pid,exit_code,childs:Integer):Integer
function PidAffinity(pid,mask:Integer):Integer
function DevAffinity(dev,mask:Integer):Integer

Аргументы:

  • cmdline - командная строка.
  • tid - task id, идентификатор задачи.
  • timeout - время ожидания, мс.
  • data - данные для передачи в канал.
  • maxlen - макс.число символов при чтении.
  • how - способ завершения процесса.
  • exit_code - код завершения процесса.
  • param - параметры процесса.
  • args - параметры ShellExecute.
  • txt - ссылка на текст.
  • pid - process id, идентификатор процесса в операционной системе.
  • childs - максимальный уровень рекурсии при убиении дочерних процессов.
  • mask - маска привязки процесса к процессорам.
  • dev - device id, идентификатор устройства, см reffind.

Результат:

Запуск, завершение и управление процессами (задачами, task).

Описание:

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

Ссылки на задачи (tid = task id) лежат в диапазоне, заданном константами TASK_REF_MIN..TASK_REF_MAX. Эти константы позволяют организовать цикл по всем задачам, при этом перед работой с задачей надо проверять ссылку задачи (task_ref(tid)) на отличие от нуля. Если ссылка нулевая, задача с номером tid не используется. Например:
    for tid:=TASK_REF_MIN to TASK_REF_MAX do
    if (task_ref(tid)<>0) then writeln('TASK ',tid,' has PID ',task_pid(tid));
         

Библиотека включает 20 функций:

  • task_init(cmdline:String):Integer - создает задачу, инициализирует ее командную строку, возвращает индекс задачи (task index, далее tid). Возвращает ноль, если таблица задач переполнена. Созданная задача не связана ни с каким процессом, пока не вызван task_run. Индекс задачи tid имеет значение 1..255. Это позволяет легко организовывать и хранить списки процессов в строках. Индекс задачи tid является также ссылкой, то есть его можно использовать в качестве ссылки при вызове refinfo. Это позволяет проверять корректность ссылок: if IsSameText(refinfo(tid,'type'),'task') then....
  • task_free(tid:Integer):Boolean - уничтожает задачу и освобождает элемент в таблице задач. Уничтожение задачи не обязательно означает уничтожение запущенного процесса. Процесс можно оставить в живых, если не вызывать task_kill. Однако, если делалось переназначение стандартного ввода-вывода в файл или анонимный канал связи, процесс убивается даже без вызова task_kill. Это связано с тем, что канал или файл закрываются, процесс уже все равно не имел бы к ним доступа и завершился бы по ошибке ввода-вывода.
  • task_ref(tid:Integer):Integer - возвращает ссылку задачи по ее индексу tid. Ненулевая ссылка означает, что задача с таким индексом существует, то есть индекс корректен.
  • task_pid(tid:Integer):Integer - возвращает идентификатор задачи pid, который используется операционной системой. Если индекс tid задачи некорректен или задача еще не создала процесс вызовом task_run, возвращается 0.
  • task_run(tid:Integer):Boolean - запускает задачу на исполнение. При успешном завершении задача связывается с процессом и получает свой pid. Связь остается даже после завершения процесса, пока не вызван task_free для уничтожения задачи. Перед запуском надо задать параметры задачи вызовом task_ctrl.
  • task_wait(tid,timeout:Integer):Boolean - Служит для ожидания завершения процесса. Возвращает true, если после таймаута процесс все еще жив. Вызов task_wait(tid,0) служит для проверки статуса процесса - работает ли он еще - без ожидания.
  • task_send(tid:Integer; data:String):Integer - посылка данных в канал stdin процесса. Возвращает число переданных байт. Передача данных из канала выполняется асинхронно, в отдельном программном потоке.
  • task_recv(tid,maxlen:Integer):String - чтение данных из канала stdout процесса. Прием данных из канала выполняется асинхронно, в отдельном программном потоке.
  • task_txcount(tid:Integer):Integer - возвращает счетчик данных в канале stdin процесса.
  • task_rxcount(tid:Integer):Integer - возвращает счетчик данных в канале stdout процесса.
  • task_txspace(tid:Integer):Integer - свободное место в буфере для посылки в канал stdin процесса.
  • task_rxspace(tid:Integer):Integer - свободное место в буфере для посылки в канал stdout процесса.
  • task_result(tid:Integer):Integer - возвращает код завершения процесса. Пока процесс выполняется, возвращает специальный код 259 = STILL_ACTIVE.
  • task_kill(tid,how,exit_code,timeout:Integer):Boolean - убивает процесс. Метод how может быть таким:
    0 = TerminateProcess - убить процесс жестко, без выполнения предусмотренных завершающих процедур
    1 = послать окну сообщение WM_CLOSE (для консольных окон - эквивалентно нажатию CTRL-C)
    2 = послать окну сообщение WM_QUIT (для графических окон - принудительное завершение)
    3 = убить дерево процессов, включая всех потомков данного процесса
    kill(tid,0...) - наиболее надежный способ убить задачу, однако это аварийное завершение. Посылка сообщений позволяет убить задачу более корректно (путем принуждения к самостоятельному завершению), но работает не всегда.
    Рекомендуется завершать процессы в два этапа - сначала "мягким" методом 1 (WM_CLOSE) в течение некоторого времени, а потом (для надежности) "жестким" методом 0 (TerminateProcess). То есть завершение процессов рекомендуется делать примерно так:
    if task_wait(tid,0) then                     // Если процесс работает
    if task_kill(tid,1,0,1000) then              // Пробуем убить процесс "мягко" (через WM_CLOSE) в течение 1000 ms
    Success('Kill(1) PID '+str(task_pid(tid)));  // Если получилось, то пишем сообщение
    if task_wait(tid,0) then                     // Если процесс все еще работает
    if task_kill(tid,0,0,1000) then              // Пробуем убить процесс "жестко" (через Terminate) в течение 1000 ms
    Success('Kill(0) PID '+str(task_pid(tid)));  // Если получилось, то пишем сообщение
                    
    При использовании метода завершения 1 (WM_CLOSE) следует иметь в виду код возврата. Если консольная задача завершается через ^C (CTRL-C), что эквивалентно посылке консольному окну WM_CLOSE, то кодом возврата процесса будет
     STATUS_CONTROL_C_EXIT = -1073741510; // Код завершения при CTRL-C = $C000013A 
    См. например NTSTATUS Error Code List.
  • task_ctrl(tid:Integer; param:String):String - задание и чтение параметров задачи. Работает, только пока не вызван task_run, до создания процесса. После создания процесса параметры можно только читать.
    Вызов task_ctrl('name=value') задает, а вызов task_ctrl('name') - читает параметры. Допустимы такие параметры:
    • AppName - имя файла программы. Параметр необязателен, если указана командная строка.
    • CmdLine - командная строка. Если имя программы не указано, первое слово командной строки должно содержать имя исполняемого файла.
    • HomeDir - стартовый каталог при запуске процесса.
    • Display - режим отображения 0=SW_HIDE, 1=SW_SHOWNORMAL, SW_SHOWMINIMIZED, SW_SHOWMAXIMIZED и т.д.
    • ProcessPriority - задает класс приоритета запускаемого процесса. Класс приоритет задается строкой из набора Idle, Lower, Normal, Higher, High, RealTime, либо числами 4=Idle, 6=Lower, 8=Normal, 10=Higher, 13=High, 24=RealTime.
    • ThreadPriority - задает приоритет основного потока запускаемого процесса. Приоритет задается строкой из набора tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest, tpTimeCritical.
    • StdInPipeSize - ненулевое значение приводит к переназначению stdin на чтение канала и задает размер канала. Теперь можно вызывать task_send.
    • StdInPriority - устанавливает приоритет потока, обслуживающего чтение канала stdin, если он используется, то есть если задано ненулевое значение StdInPipeSize. Приоритет задается строкой из набора tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest, tpTimeCritical. Введение отдельного потока, обслуживающего канал, позволяет сделать обмен данными асинхронным, не блокирующим выполнение программы.
    • StdInFileName - переназначает stdin на чтение файла.
    • StdOutPipeSize - ненулевое значение приводит к переназначению stdout на запись в канал и задает размер канала. Теперь можно вызывать task_recv.
    • StdOutPriority - устанавливает приоритет потока, обслуживающего запись канала stdout, если он используется, то есть если задано ненулевое значение StdOutPipeSize. Приоритет задается строкой из набора tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest, tpTimeCritical. Введение отдельного потока, обслуживающего канал, позволяет сделать обмен данными асинхронным, не блокирующим выполнение программы.
    • StdOutFileName - переназначает stdout на запись файла.
    • ExeName - возвращает имя исполняемого файла, как его распознала программа.
    • Encrypt - этот вызов используется для генерации зашифрованного ключа, позволяющего запускать данную задачу от имени другого пользователя. Формат вызова
      Encrypt=USER+CRLF+DOMAIN+CRLF+PASSWORD. При этом к моменту вызова уже долно быть задано имя исполняемого файла, так как ключ привязан к конкретному файлу и без него не может быть сгенерирован. Кроме того, имя исполняемого файла должно быть ПОЛНЫМ, то есть включать полный путь, имя файла и расширение файла, иначе программа откажется генерировать ключ.
    • Account - задает ключ учетной записи пользователя, от имени которого надо выполнять процесс. Ключ зашифрован, причем в генерации пароля шифрования участвует сам исполняемый файл, поэтому подмена файла не позволит запустить задачу. Ключ генерируется предварительно человеком, знающим пароль администратора, вызовом tast_ctrl('Encrypt='+'User'+CRLF+'Domain'+CRLF+'Password'). В рабочую версию программы ключ подставляется как константа, абракадабра, которая может использоваться только для запуска конкретной программы от имени конкретного пользователя. Кроме того, имя исполняемого файла должно быть ПОЛНЫМ, то есть включать полный путь, имя файла и расширение файла, иначе программа не сможет расшифровать ключ. Ведь ключ привязан к конкретному файлу.
    • ExitCode - возвращает код выхода процесса. Нулевой код обычно означает успешное завершение, ненулевой - код ошибки. Под Windows специальный код 259=$103=STILL_ACTIVE означает, что процесс всё еще работает.
    • ExitStatus - возвращает статус выхода процесса. Под Windows статус выхода и код выхода должны совпадать. Под Unix статус выхода может не совпадать с кодом выхода, если программа была прервана сигналом.
    • PID - возвращает идентификатор процесса.
  • GetComSpec:String
    GetShell:String
    - имя командного процессора: cmd.exe под Windows или bash под Unix. В принципе, функции GetComSpec и GetShell являются синонимами, однако рекомендуется (следуя традиции) использовать GetComSpec под Windows и GetShell под Unix. Это связано с тем, что имя командного процессора обычно хранится в переменной окружения COMSPEC под Windows и SHELL под Unix. Типичными значениями являются GetComSpec=C:\Windows\System32\cmd.exe под Windows и GetShell=/bin/bash под Unix.
  • ShellExecute(args:String):Integer - позволяет запускать программы или открывать документы или распечатывать их средствами Shell. При этом система сама найдет по расширению файла нужную для открытия документа или печати программу.
    Параметр args имеет вид:
    args = “cmd | exe | cmdline | dir | show”
    где
    • cmd – команда open, print, explore
    • exe - имя исполняемого файла программы, или документа, который надо открыть
    • cmdline - параметры командной строки программы
    • dir - начальный каталог программы либо каталог просмотра, если файл не указан. Допустимо указывать вместо него пробел или точку (текущий каталог).
    • show - необязательные параметры окна программы при запуске: HIDE, MAXIMIZE, MINIMIZE, RESTORE, SHOW, SHOWDEFAULT, SHOWMAXIMIZED, SHOWMINIMIZED, SHOWMINNOACTIVE, SHOWNA, SHOWNOACTIVATE, SHOWNORMAL (по умолчанию).
    Параметры, кроме cmd и exe или dir, необязательны и могут быть заменены пробелом.
    Функция возвращает число, значение от 0 до 32 означает ошибку, другие значения значают успех и дают дескриптор окна программы.
    Примеры допустимых вызовов:
                   i:=shellexecute('open | notepad.exe | c:\readme.txt | c:\ | SHOWNORMAL');
                   i:=shellexecute('open | notepad.exe | c:\readme.txt | .');
                   i:=shellexecute('explore | | | c:\paslib | showmaximized');
                   i:=shellexecute('open | readme.htm');
                  
    Надо отметить также, что вызов shellexecute возвращает управление сразу после создания процесса. Кроме того, поскольку функция не возвращает никакую ссылку на процесс, отследить его дальнейшее выполнение нельзя. Можно также сказать, что вызов shellexecute эквивалентен последовательности вызовов функций task_xxx, например:
                   Вызов
                    i:=shellexecute('open | notepad.exe | c:\readme.txt | c:\ | SHOWNORMAL');
                   аналогичен последовательности
                    tid:=task_init('');
                    s:=task_ctrl(tid,'CmdLine=notepad.exe c:\readme.txt');
                    s:=task_ctrl(tid,'HomeDir=c:\');
                    b:=task_run(tid);
                    b:=task_free(tid);
                  
    Удобство shellexecute в первую очередь в том, что можно открывать документы, не заботясь о поиске программы просмотра для этого типа файлов. Система сама найдет нужную программу. Альтернативой является вызов типа paramstr('GetExeByFile c:\readme.htm') для получения имени программы для данного типа документов и затем запуск этой программы с именем документа в командной строке. Этот путь сложнее, хотя возможности его больше. Вызов shellexecute дает более короткий путь для простых случаев.
  • PidList(txt:Integer):Integer - записывает в текст, заданный ссылкой txt, список выполняемых на локальной машине процессов и возвращает ссылку txt как результат. Список процессов возвращается в виде текста, каждая строка имеет вид:
                  Pid,ParentPid,Threads,Priority,FileName
                  
    где:
    • Pid - идентификатор процесса (в смысле операционной системы).
    • ParentPid - идентификатор родительского процесса.
    • Threads - число потоков в рамках этого процесса.
    • Priority - класс приоритета процесса.
    • FileName - имя исполняемого файла процесса (без пути).
    Возможность чтения списка процессов дает инструмент для построения систем слежения за действиями пользователей или операционной системы. Например, можно выявлять процессы, отнимающие слишком много процессорного времени и убивать их, чтобы не мешали измерениям. Или можно отслеживать запуск видео проигрывателя и предупреждать оператора, что делать этого не следует. И так далее.
  • PidKill(pid,exit_code,maxlevel:Integer):Integer - убивает (уничтожает) процесс с идентификатором pid и кодом возврата exit_code. Если указан ненулевой уровень рекурсии childs дочерних процессов, то будут также убиты все дочерние процессы до указанного уровня родства включительно. Этой функцией следует пользоваться весьма осторожно, иначе можно повредить операционную систему.
  • PidAffinity(pid,mask:Integer):Integer - позволяет прочитать и задать привязку процесса с идентификатором pid к процессорам в многопроцессорных системах. Если точнее, вызов PidAffinity(pid,mask) привязывает процесс с идентификатором pid к списку процессоров, заданных маской mask, если она отлична от нуля, а затем считывает и возвращает маску привязки к процессорам для указанного процесса. При этом идентификатор pid=0 интерпретируется как идентификатор текущего процесса, а pid=-1 - как система в целом. Маска mask задает список процессоров, к которым будет привязан данный процесс. Каждый бит от 0 до 31 соответствует процессору с соответствующим номером. Поскольку процесс без процессора не имеет смысла, задаваемая маска всегда ненулевая. Нулевое значение маски используется, если надо сделать только чтение маски привязки, не меняя ее содержимое. Таким образом:
    • PidAffinity(-1,0) - позволяет получить список всех доступных процессоров в системе.
    • PidAffinity(0,0) - позволяет получить список процессоров, доступных для данного процесса.
    • PidAffinity(0,mask) - позволяет задать список процессоров, доступных для данного процесса.
    • PidAffinity(pid,0) - позволяет получить список процессоров, доступных для процесса с известным идентификатором pid.
    • PidAffinity(pid,mask) - позволяет задать список процессоров, доступных для процесса с известным идентификатором pid.
  • DevAffinity(dev,mask:Integer):Integer - позволяет прочитать и задать привязку потока устройства с идентификатором dev к процессорам в многопроцессорных системах. Если точнее, вызов DevAffinity(dev,mask) привязывает поток устройства с идентификатором dev к списку процессоров, заданных маской mask, если она отлична от нуля, а затем считывает и возвращает маску привязки к процессорам для указанного потока. При этом идентификатор dev=0 интерпретируется как идентификатор текущего устройства. Маска mask задает список процессоров, к которым будет привязан данный поток. Каждый бит от 0 до 31 соответствует процессору с соответствующим номером. Поскольку поток без процессора не имеет смысла, задаваемая маска всегда ненулевая. Нулевое значение маски используется, если надо сделать только чтение маски привязки, не меняя ее содержимое. Таким образом:
    • DevAffinity(0,0) - позволяет получить список процессоров, доступных для данного потока.
    • DevAffinity(0,mask) - позволяет задать список процессоров, доступных для данного потока.
    • DevAffinity(dev,0) - позволяет получить список процессоров, доступных для потока устройства с известным идентификатором dev.
    • DevAffinity(pid,mask) - позволяет задать список процессоров, доступных для потока устройства с известным идентификатором dev.

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

Для генерации ключа используется вызов

         tid:=task_init('c:\Crw32exe\Crw32.exe');
         key:=task_ctrl(tid,'Encrypt='+'User'+CRLF+'Domain'+CRLF+'Password');
        
Разумеется, генерация ключа делается "дома", на недоступной для пользователя машине, так как пароль в этот момент прописывается явно. Для генерации ключа предварительно должно быть указано полное имя файла, с полным путем и расширением. Файл должен быть именно тот, который будет в рабочей версии, так как ключ привязан к конкретному файлу и без него никак не может быть использован. После генерации ключ заносится как константа в текст программы. Из этой абракадабры пользователь ничего не поймет.

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

Для запуска программы на исполнение используется вызов:

         tid:=task_init('c:\Crw32exe\Crw32.exe');
         s:=task_ctrl(tid,'Account='+key);
         tid:=task_run(tid);
        
Ключ заносится как константа в текст программы. Из этой абракадабры пользователь ничего не поймет. Для исполнения должно быть указано полное имя файла, с полным путем и расширением. Файл должен быть именно тот, который использовался при генерации пароля, так как ключ привязан к конкретному файлу и без него никак не может быть использован.

В отличие от запуска программ под текущим аккаунтом, где допустимы короткие имена файлов, при запуске под другим аккаунтом имя исполняемого файла должно быть обязательно ПОЛНЫМ, то есть включать полный путь, имя файла и расширение файла, иначе программа не сможет правильно расшифровать ключ. Ведь ключ привязан к конкретному файлу. Такая привязка обязательно нужна, чтобы нельзя было подменить исполняемый файл другим файлом и получить таким образом права администратора.

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

Разумеется, если защита пароля не нужна (для отладок), можно просто писать:

         tid:=task_init('c:\Crw32exe\Crw32.exe');
         s:=task_ctrl(tid,'Account='+task_ctrl(tid,'Encrypt='+'User'+CRLF+'Domain'+CRLF+'Password'));
         tid:=task_run(tid);
        
но в конечном варианте этого следует избегать.

Пример программирования: DEMO_TASK.

Пример:

          1)Вызвать программу и пусть ее живет...

            tid:=task_init(getcomspec+' /c example.bat');
            b:=task_run(tid);
            b:=task_free(tid);

          2)Вызвать программу, ждать 10 секунд и убить, если не завершилась сама.

            tid:=task_init(getcomspec+' /c example.bat');
            b:=task_run(tid);
            if task_wait(tid,10000) then task_kill(tid,0,0,1000);
            b:=task_free(tid);

          3)Вызвать программу c обменом данными по каналу и установкой приоритетов...

            tid:=task_init('');
            s:=task_ctrl('CmdLine=c:\example.exe');
            s:=task_ctrl('HomeDir=c:\');
            s:=task_ctrl('StdInPipeSize=1000');
            s:=task_ctrl('StdOutPipeSize=1000');
            s:=task_ctrl('StdInPriority=tpTimeCritical');
            s:=task_ctrl('StdOutPriority=tpTimeCritical');
            s:=task_ctrl('ThreadPriority=tpTimeCritical');
            s:=task_ctrl('ProcessPriority=RealTime');
            b:=task_run(tid);
            if task_wait(tid,0) then writeln('task is running');
            if task_rxcount>0 then writeln('Received:',task_recv(tid,100));
            if task_txspace>100 then writeln('Sent:',task_send(tid,s),' bytes');
            b:=task_free(tid);

          4)Вызвать программу от имени другого пользователя...

            tid:=task_init('');
            {--задание программы, должно предшествовать генерации ключа}
            {--имя файла программы должно быть полным}
            s:=task_ctrl('AppName=c:\Crw32exe\Crw32.exe');
            {--генерация ключа для запуска файла, делается на этапе отладки}
            {--ключ уникален для данного исполняемого файла и аккаунта}
            {--Encrypt=USER+CRLF+DOMAIN+CRLF+PASSWORD}
            key:=task_ctrl('Encrypt='+'Alex'+CRLF+'.'+CRLF+'eklmn');
            {--в рабочем режиме ключ подставляется как константа}
            key:=task_ctrl('Account='+key);
            b:=task_run(tid);
            b:=task_free(tid);

          5)Показать список выполняемых процессов...

            procedure ShowTaskList;
            var t,i:Integer; b:Boolean;
            begin
             writeln('Pid,ParentPid,Threads,Priority,FileName:');
             t:=pidlist(text_new);
             for i:=0 to text_numln(t)-1 do writeln(text_getln(t,i));
             b:=text_free(t);
            end;

            Результатом будет что-то вроде:

            Pid,ParentPid,Threads,Priority,FileName:
            0,0,1,0,[System Process]
            4,0,58,8,System
            400,4,3,11,smss.exe
            580,400,11,13,csrss.exe
            612,400,19,13,winlogon.exe
            656,612,16,9,services.exe
            ...
            3416,1752,12,8,CRW32.exe
            2948,3416,6,8,IEXPLORE.EXE
            2984,3416,1,8,cmd.exe
            3328,3416,1,8,cmd.exe

          6)Убить все процессы по известному имени exe-файла...

             {
             Kill process by given *.exe file name.
             }
             function KillPidByName(exe:string):Integer;
             var t,i,n,pid:Integer; b:Boolean;
              function npos(c:char;n:integer;s:string):integer;
              var i,j:integer;
              begin
               j:=0;
               npos:=0;
               for i:=1 to length(s) do
               if s[i]=c then begin j:=j+1; if j=n then npos:=i; end;
              end;
             begin
              n:=0;
              exe:=trim(exe);
              if length(exe)>0 then begin
               t:=pidlist(text_new);
               for i:=0 to text_numln(t)-1 do
               if IsSameText(exe,copy(text_getln(t,i),npos(',',4,text_getln(t,i))+1)) then begin
                pid:=val(extractword(1,text_getln(t,i)));
                if pid<>0 then n:=n+pidkill(pid,0,0);
               end;
              end;
              b:=text_free(t);
              KillPidByName:=n;
             end;

             n:=KillPidByName('cmd.exe'); {Убить все экземпляры cmd.exe, вернуть число убитых}

         

Daq Pascal Api -> pipe_init pipe_free pipe_ref pipe_pid pipe_run pipe_wait pipe_send pipe_recv pipe_txcount pipe_txspace pipe_rxcount pipe_rxspace pipe_result pipe_kill pipe_ctrl pipe_txclear pipe_rxclear pipe_count pipe_stream pipe_connected

Определение:

function pipe_init(args:string):integer
function pipe_free(pid:Integer):Boolean
function pipe_ref(pid:Integer):Integer
function pipe_pid(pid:Integer):Integer
function pipe_run(pid:Integer):Boolean
function pipe_wait(pid,timeout:Integer):Boolean
function pipe_send(pid:Integer; data:String):Integer
function pipe_recv(pid,maxlen:Integer):String
function pipe_txcount(pid:Integer):Integer
function pipe_rxcount(pid:Integer):Integer
function pipe_txspace(pid:Integer):Integer
function pipe_rxspace(pid:Integer):Integer
function pipe_result(pid:Integer):Integer
function pipe_kill(pid,how,exit_code,timeout:Integer):Boolean
function pipe_ctrl(pid:Integer; param:String):String
function pipe_txclear(pid:Integer):Boolean
function pipe_rxclear(pid:Integer):Boolean
function pipe_count(pid:Integer):Integer
function pipe_stream(pid,index:Integer):Integer
function pipe_connected(pid:Integer):Integer

Аргументы:

  • args - строка с описанием канала.
  • pid - pipe id, идентификатор канала.
  • timeout - время ожидания, мс.
  • data - данные для передачи в канал.
  • maxlen - макс.число символов при чтении.
  • how - способ завершения процесса (в случае task).
  • exit_code - код завершения процесса (в случае task).
  • param - параметры канала.
  • index - индекс дочернего канала.

Результат:

Работа с каналами связи разного типа (задачи task, именованные каналы pipe, сокеты tcp, последовательные порты com).

Описание:

Набор функций pipe_xxx предназначен для работы с каналами связи, то есть с устройствами последовательного доступа, куда можно записывать и откуда читать данные с дисциплиной доступа FIFO. Канал связи может иметь различную природу:
  • task - консольная задача, т.е. процесс, связь с которым осуществляется через анонимный канал, в который переназначается стандартный ввод-вывод stdin/stdout.
  • pipe - именованный сетевой канал.
  • tcp - канал связи через TCP сокет.
  • com - последовательный COM порт.

Канал связи с идентификатором pid может иметь один или несколько потоков pipe_stream(pid,i), через которые осуществляется ввод-вывод. Большинство каналов сами и являются потоками, то есть для них выполняется

         pipe_count(pid)    = 1
         pipe_stream(pid,0) = pid
         
Только сервер tcp не является потоком (через него нельзя делать ввод-вывод), но он имеет много потоков, каждый из которых соответствует соединению с каким-либо клиентом.

Библиотека включает 20 функций:

  • pipe_init(args:String):Integer - создает (открывает) канал связи, заданный строкой args, определяющей тип и параметры канала. Возвращает идентификатор канала (pipe identifier, далее pid). Возвращает ноль, если заданы неверные параметры или создание канала не удалось.

    • pipe_init('Task cmd') создает задачу task с заданной командной строкой cmd, а также задает фоновый режим выполнения (без видимого окна) и переназначает стандартный ввод-вывод в анонимный канал связи. То есть:
                           pid:=pipe_init('task cmd')
                          эквивалентно
                           pid:=task_init('cmd')
                           task_ctrl(pid,'Display=0')
                           task_ctrl(pid,'TxPipeSize=16384')
                           task_ctrl(pid,'RxPipeSize=32768')
                         
      После создания канала надо не забыть запустить задачу вызовом pipe_run(pid). После завершения работы с каналом надо не забыть его закрыть вызовом pipe_free(pid).

    • pipe_init('Pipe n Polling d Priority p TxPipeSize s RxPipeSize r Timeout t') открывает именованный канал pipe с именем n. Если имя имеет вид n=Name, то создается сервер канала по имени Name. Если имя имеет вид n=Host\Name, то создается клиент канала по имени Name, подключаемый к серверу Host.
      Остальные параметры необязательны.
      Параметр Polling задает период d опроса в миллисекундах, по умолчанию 1.
      Параметр Priority задает приоритет p опроса, по умолчанию tpTimeCritical.
      Параметр TxPipeSize задает буфер tx передатчика в байтах, по умолчанию 16384.
      Параметр RxPipeSize задает буфер rx приемника в байтах, по умолчанию 32768.
      Параметр Timeout задает время t ожидания для периодических попыток установления соединения в миллисекундах, по умолчанию 1000 миллисекунд.
      Параметр ListenPeriod задает период (интервал времени) t для периодических попыток установления соединения (accept,connect), по умолчанию 500 миллисекунд.

    • pipe_init('Tcp Port n Server m Polling d Priority p TxPipeSize s RxPipeSize r Timeout t Options o') и
      pipe_init('Tcp Port n Client h Polling d Priority p TxPipeSize s RxPipeSize r Timeout t Options o') -
      открывает серверный или клиентский канал связи через потоковый TCP/IP сокет на порте n.
      Параметр m задает максимальное число подключений, которые сможет обслуживать сервер. Именно это число вернется при вызове pipe_count(pid).
      Параметр h задает имя или IP адрес сервера, к которому подключается клиент.
      Остальные параметры - Polling, Priority, TxPipeSize, RxPipeSize, Timeout - задаются аналогично предыдущему случаю.
      Параметр o задает опции сокета (по умолчанию $08):
      • $01 - использовать "жесткое" (shutdown) закрытие сокета с потерей не переданных данных.
      • $02 - делать очистку буферу FIFO передатчика Tx при закрытии сокета (разрыве соединения).
      • $04 - делать очистку буфера FIFO приемника Rx при закрытии сокета (разрыве соединения).
      • $08 - отключение алгоритма Нагла (Nagle) (флаг SO_TCP_NODELAY). Этот алгоритм пытается собрать маленькие пакеты данных в пакеты побольше, что снижает трафик, но увеличивает задержку приемо-передачи.
      • $10 - использовать "жесткое" (LINGER,timeout=0) закрытие сокета с потерей не переданных данных.
      • $20 - использовать увеличенный (16384 byte) буфер передатчика сокета. Используется при большом трафике (более мегабайта в секунду).
      • $40 - использовать увеличенный (16384 byte) буфер приемника сокета. Используется при большом трафике (более мегабайта в секунду).

    • pipe_init('Com Port n Baudrate b Parity p DataBits d StopBits s XonXoff x BufSize z DcbFlags f') -
      открывает канал COM порта n.
      Остальные параметры необязательны.
      Параметр Baudrate задает скорость опроса b, по умолчанию 9600.
      Параметр Parity задает режим контроля четности p, по умолчанию NONE.
      Параметр DataBits задает число бит данных d, по умолчанию 8.
      Параметр StopBits задает число стоповых бит s, по умолчанию 1.
      Параметр BufSize задает размер буфера z в килобайтах, по умолчанию 4.
      Параметр XonXoff задает флаг управления потоком x, по умолчанию 0.
      Параметр DcbFlags задает флаги f, по умолчанию 0.
      Подробнее см. comopen.

    Пример:

                  pid:=pipe_init('task c:\daq32\demo.exe');            консольная задача
                  pid:=pipe_init('pipe test');                         серверный именованный канал
                  pid:=pipe_init('pipe crwbox\test');                  клиентский именованный канал
                  pid:=pipe_init('tcp port 1000 server 2');            TCP порт 1000, сервер на 2 клиента
                  pid:=pipe_init('tcp port 1000 client 192.168.0.1');  TCP порт 1000, клиент подключается к заданному IP адресу
                  pid:=pipe_init('com port 2 baudrate 115200');        COM порт 2, скорость 115200
                  

    После создания канала не мешает вызвать pipe_run для запуска задачи. Идентификатор канала pid далее используется во всех остальных функциях для работы с каналами, его можно также использовать в качестве ссылки при вызове refinfo.

  • pipe_free(pid:Integer):Boolean - уничтожает (закрывает) канал связи.
    Для task эквивалентно task_free(pid).
    Всегда следует завершать работу с каналом вызовом pipe_free(pid).

  • pipe_ref(pid:Integer):Integer - проверяет ссылку канала pid.
    Для task эквивалентно task_ref(pid).
    Ненулевой результат pipe_ref означает, что канал существует, то есть вызов pipe_init прошел успешно.

  • pipe_pid(pid:Integer):Integer - возвращает идентификатор процесса.
    Для task эквивалентно task_pid(pid).
    Для остальных типов каналов возвращает идентификатор текущего процесса.
    Ненулевой результат pipe_pid означает, что канал существует и вызов pipe_run прошел успешно.

  • pipe_run(pid:Integer):Boolean - запускает задачу канала на исполнение.
    Для task эквивалентно task_run(pid).
    Для остальных типов каналов возвращает True.

  • pipe_wait(pid,timeout:Integer):Boolean - Служит для ожидания завершения соединения.
    Для task эквивалентно task_wait(pid,timeout).
    Возвращает true, если после таймаута канал все еще подключен, см. pipe_connected.
    Вызов pipe_wait(pid,0) служит для проверки статуса (есть подключение?) без ожидания.

  • pipe_send(pid:Integer; data:String):Integer - посылка данных в канал.
    Для task эквивалентно task_send(pid,data).
    Возвращает число переданных в буфер канала байт данных. Реальная передача данных в канал выполняется асинхронно, в отдельном программном потоке.

  • pipe_recv(pid,maxlen:Integer):String - чтение принятых данных из буфера канала.
    Для task эквивалентно task_recv(pid,maxlen).
    Прием данных из канала выполняется асинхронно, в отдельном программном потоке.

  • pipe_txcount(pid:Integer):Integer - возвращает счетчик данных в буфере передатчика канала, byte.
    Для task эквивалентно task_txcount(pid).

  • pipe_rxcount(pid:Integer):Integer - возвращает счетчик данных в буфере приемника канала, byte.
    Для task эквивалентно task_rxcount(pid).

  • pipe_txspace(pid:Integer):Integer - свободное место в буфере передатчика для посылки в канал, byte.
    Для task эквивалентно task_txspace(pid).

  • pipe_rxspace(pid:Integer):Integer - свободное место в буфере приемника канала, byte.
    Для task эквивалентно task_rxspace(pid).

  • pipe_result(pid:Integer):Integer - возвращает код завершения процесса.
    Для task эквивалентно task_result(pid).
    Для остальных типов каналов возвращает специальный код 259 = STILL_ACTIVE.

  • pipe_kill(pid,how,exit_code,timeout:Integer):Boolean - убивает канальный процесс.
    Для task эквивалентно task_kill(pid,how,exit_code,timeout).
    Для остальных типов каналов возвращает pipe_wait(pid,timeout).

  • pipe_ctrl(pid:Integer; param:String):String - задание и чтение параметров канала. Зависит от типа канала.

    Вызов pipe_ctrl('name=value') задает, а вызов pipe_ctrl('name') - читает параметры.
    В случае неудачи функция возвращает строку '?'.

    Для канала task эквивалентно task_ctrl(pid,param).

    Для сетевых каналов pipe или tcp допустимы такие параметры:
    • Polling - читает/задает период опроса канала в миллисекундах.
    • Priority - задает приоритет потока опроса канала. Приоритет задается строкой из набора tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest, tpTimeCritical.
    • TxPipeSize - читает/задает размер буфера передатчика канала в байтах.
    • RxPipeSize - читает/задает размер буфера приемника канала в байтах.


    Для каналов типа tcp дополнительно можно считывать такие данные:
    • HostIP - читает IP адрес локального компьютера.
    • PeerIP - читает IP адрес удаленного компьютера, присоединенного к каналу.
    • HostName - читает доменное имя локального компьютера.
    • PeerName - читает доменное имя удаленного компьютера, присоединенного к каналу.
    • Target - читает целевое имя удаленнного компьютера (в случае клиента), которое было указано в вызове pipe_init('tcp port n client ...').


    Для каналов типа com (т.е. COM-портов) допустимы такие параметры:
    • BaudRate - читает/задает скорость обмена COM порта, бит/сек.
    • Parity - читает/задает режим четности COM порта - NONE,ODD,EVEN,MARK,SPACE.
    • DataBits - читает/задает число бит данных (размер байта), 5..8.
    • StopBits - читает/задает чило стоповых бит, (1,1.5,2).
    • DcbFlags - читает/задает флаги DCB, обычно это $4001.

  • pipe_txclear(pid:Integer):Boolean - очищает буфер передатчика канала.

  • pipe_rxclear(pid:Integer):Boolean - очищает буфер приемника канала.

  • pipe_count(pid:Integer):Integer - возвращает число потоков в канале. Каналы всех типов, кроме tcp server, имеют ровно 1 поток, совпадающий с самим каналом, поэтому для них pipe_count(pid)=1. Каналы типа tcp server сами по себе потоками не являются, но могут содержать несколько потоков pipe_stream(pid,index), через которые и делается ввод-вывод. Число потоков сервера указывается при вызове pipe_init. Каждый из потоков сервера обслуживает подключение своего отдельного клиента. В любом случае, следующий код должен работать корректно:
                  if pipe_connected(pid)>0 then
                  for i:=0 to pipe_count(pid)-1 do
                  if pipe_connected(pipe_stream(pid,i))>0 then begin
                   incomes:=pipe_recv(pipe_stream(pid,i),255);
                   sent:=pipe_send(pipe_stream(pid,i),outcomes);
                  end;
                  

  • pipe_stream(pid,index:Integer):Integer - возвращает идентификатор потока с номером Index в канале pid. Каналы всех типов, кроме tcp server, имеют ровно 1 поток, совпадающий с самим каналом, поэтому для них pipe_stream(pid,0)=pid. Каналы типа tcp server сами по себе потоками не являются, но могут содержать несколько потоков pipe_stream(pid,index), через которые и делается ввод-вывод. Это значит, что для tcp server вместо pipe_send(pid,data), например, надо использовать pipe_send(pipe_stream(pid,i),data), где индекс меняется от 0 до pipe_count(pid)-1. Каждый из потоков сервера обслуживает подключение своего отдельного клиента. В любом случае, следующий код должен работать корректно:
                  if pipe_connected(pid)>0 then
                  for i:=0 to pipe_count(pid)-1 do
                  if pipe_connected(pipe_stream(pid,i))>0 then begin
                   incomes:=pipe_recv(pipe_stream(pid,i),255);
                   sent:=pipe_send(pipe_stream(pid,i),outcomes);
                  end;
                  
    Надо заметить, что потоки серверного канала не являются самостоятельными единицами, их не надо закрывать вызовом pipe_free, так как они закрываются автоматически вместе с серверным каналом.

  • pipe_connected(pid:Integer):Integer - возвращает число подключений для данного канала. Каналы всех типов, кроме tcp server, имеют ровно 1 поток, совпадающий с самим каналом, поэтому для них pipe_connected(pid) может принимать значение 0 (отключен) или 1 (подключен). Каналы типа tcp server сами по себе потоками не являются, но могут содержать несколько потоков pipe_stream(pid,index), через которые и делается ввод-вывод. Каждый из потоков сервера обслуживает подключение своего отдельного клиента и также может иметь значение pipe_connected(pipe_stream(pid,index)) равное нулю или единице. А вот сам tcp server может иметь значение pipe_connected(pid) в диапазоне 0..pipe_count(pid), по числу активных клиентских подключений.
    Надо также заметить, что задача task считается подключенной, пока она работает, то есть pipe_connected(pid)=ord(task_wait(pid,0)).

Особо надо сказать о работе с каналами tcp server. Дело в том, что канал этого типа сам по себе не может служить для чтения-записи данных, то есть вызов типа pipe_send(pid,data) для него будет неверным. Вместо этого надо использовать вызовы pipe_send(pipe_stream(pid,i),data) с индексом i от 0 до pipe_count(pid)-1. Каждый поток соответствует клентскому подключению. Наличие нескольких потоков pipe_stream(pid,i) позволяет серверу обслуживать несколько клиентских подключений одновременно. При этом обработка может зависеть от адреса клиента, который можно узнать вызовом

         pipe_ctrl(pipe_stream(pid,i),'PeerIP')    адрес удаленного клиента номер i
         pipe_ctrl(pipe_stream(pid,i),'PeerName')  имя машины удаленного клиента номер i
        

В общем случае работа с каналом строится примерно так:

        TCP Server:
         if runcount=1 then begin
          srv:=pipe_init('tcp port 1000 server 2');
         end else
         if isinf(runcount) then begin
          b:=pipe_free(srv);
         end else
         begin
          if pipe_connected(srv)>0 then
          for i:=0 to pipe_count(srv)-1 do begin
           pid:=pipe_stream(srv,i);
           if pipe_connected(pid)>0 then begin
            if pipe_rxcount(pid)>0
            then writeln(' Receive ',pipe_recv(pid,255),
                         ' from remote host ',pipe_ctrl(pid,'PeerName'));
            if HasDataToSend then
            if pipe_send(pid,GetDataToSend)=0 then Error;
           end;
          end;
         end;

        TCP Client:
         if runcount=1 then begin
          pid:=pipe_init('tcp port 1000 client localhost');
         end else
         if isinf(runcount) then begin
          b:=pipe_free(pid);
         end else
         begin
          if pipe_connected(pid)>0 then begin
            if pipe_rxcount(pid)>0
            then writeln(' Receive ',pipe_recv(pid,255),
                         ' from remote host ',pipe_ctrl(pid,'PeerName'));
            if HasDataToSend then
            if pipe_send(pid,GetUserDataToSend)=0 then Error;
          end;
         end;
        
Заметьте, что клиенту не обязательно использовать циклы и вызовы pipe_count, pipe_stream, так как для него всегда pipe_count(pid)=1 и pipe_stream(pid,0)=pid.

Пример программирования: DEMO_PIPE.

Пример:

          1)Ввод/вывод данных из консольной программы...

            pid:=pipe_init('task ping /t crwbox');
            if pipe_run(pid) then {Ok} else {Error};
            ....
            if pipe_rxcount(pid)>0 then writeln(pipe_recv(pid,255));
            if HasDataToSend then
            if pipe_send(pid,GetData)=0 then {Error};
            ....
            b:=pipe_free(pid);

          2)Ввод/вывод данных из именованного канала...

            pid:=pipe_init('pipe crwbox\test');
            if pipe_run(tid) then {Ok} else {Error};
            ....
            b:=pipe_free(pid);

          3)Ввод/вывод данных из порта TCP/IP (server)...

            srv:=pipe_init('tcp port 1000 server 2');
            if pipe_run(srv) then {Ok} else {Error};
            ....
            if pipe_connected(srv)>0 then
            for i:=0 to pipe_count(srv)-1 do begin
             pid:=pipe_stream(srv,i);
             if pipe_connected(pid)>0 then begin
              if pipe_rxcount(pid)>0
              then writeln(' Receive ',pipe_recv(pid,255),
                           ' from remote host ',pipe_ctrl(pid,'PeerName'));
              if HasDataToSend then
              if pipe_send(pid,GetUserDataToSend)=0 then Error;
             end;
            end;
            ...
            b:=pipe_free(srv);

            

Daq Pascal Api -> easyipc_init easyipc_free easyipc_poll easyipc_send easyipc_recv easyipc_ctrl

Определение:

function easyipc_init(pipename,options:string):integer
function easyipc_free(ipc:Integer):boolean
function easyipc_poll(ipc:Integer):boolean
function easyipc_send(ipc:Integer; textlines:string):boolean
function easyipc_recv(ipc:Integer; count:integer):string
function easyipc_ctrl(ipc:Integer; request:String):string

Аргументы:

  • pipename - строка с именем канала связи.
  • options - строка со списком параметров канала связи.
  • ipc - идентификатор канала связи.
  • textlines - текст для передачи в канал связи.
  • count - число байт для чтения из канала.
  • request - идентификатор запроса для чтения параметров канала.

Результат:

Работа с простым двунаправленным текстовым каналом связи IPC (inter process communication, межпроцессное взаимодействие), реализованным через именованные каналы.

Описание:

Набор функций easyipc_xxx предназначен для организации работы с двунаправленным каналом межпроцессной связи (IPC), ориентированным на передачу текста в режиме последовательного доступа, с дисциплиной доступа FIFO. Первоначально функции easyipc_xxx были разработаны для связи с программами DieselPascal. Но они могут применяться для любых других целей, где нужен IPC.

Данный набор функций предназначен для работы в режиме опроса, без создания отдельного потока. (Это отличает семейство функций EasyIpc_xxx от pipe_xxx, где есть свой поток). При этом используется неблокирующий ввод-вывод, что предотвращает задержку вызывающего потока (при TimeOut=0). Предполагается, что передаются и принимаются байтовые строки текста, разделенные с помощью разделителя строк EOL.
Для структурирования передаваемых данных рекомендуется использовать один из двух способов:
         1) @cmd arg   - Команда cmd с аргументами arg. Анализируется, например, с помощью функции GotCommand(Data,cmd,arg).

         2) Name=Value - Выражение присвоения параметру Name значение Value. Анализируется, например, с помощью CookieScan(Data,Name,0).
         
Двоичные данные можно передавать через текстовый канал связи с помощью кодирования, например, в HEX.

easyipc_init(pipename,options) создает канал связи с именем pipename, списком параметров options и возвращает идентификатор канала ipc или ноль при ошибке. Для сервера имя канала pipename должно быть простым, без разделителя (\). Для клиента имя канала pipename должно иметь вид (host\pipename) с разделителем (\). В качестве имени сервера host обычно фигурирует точка (.), что означает текущий компьютер. Имя канала должно быть уникальным в рамках данной системы и должно совпадать для клиента и сервера. Список параметров options содержит разделенные с помощью EOL строки вида Name=Value, где Name принимает значения:
  • TimeOut - время ожидания операций подключения и ввода-вывода в мс, по умолчанию 0.
  • RxBuffSize - размер буфера приемника Rx, по умолчанию 65536 байт.
  • TxBuffSize - размер буфера передатчика Tx, по умолчанию 65536 байт.
  • LogsHistory - длина истории журнала событий, по умолчанию 0.


easyipc_free(ipc) освобождает канал связи, созданный вызовом easyipc_init. Вызывается для завершения работы канала.

easyipc_poll(ipc) опрашивает канал связи, созданный вызовом easyipc_init. Вызывается периодически, в цикле опроса для обеспечения нормальной работы канала.

easyipc_send(ipc,textlines) посылает в канал связи блок текста textlines. Вызывается по мере необходимости для посылки данных подключенному клиенту или серверу.

easyipc_recv(ipc,count) принимает из канала связи блок текста не более count байт. Вызывается по мере необходимости для приема данных от подключенного клиента или сервера. Для проверки наличия подключения к каналу можно использовать вызов easyipc_ctrl(ipc,'connected'). Для проверки наличия данных в буфере FIFO можно использовать easyipc_ctrl(ipc,'rxlength').

easyipc_ctrl(ipc,request) читает состояние канала с помощью запроса request. Вызывается по мере необходимости для проверки состояния канала. Запрос request может принимать значения:
  • Connected - 0/1 - состояние подключения.
  • IsServer - 0/1 - режим работы в качестве сервера.
  • IsClient - 0/1 - режим работы в качестве клиента.
  • FileName - UNC имя файла канала, типа \\.\pipe\demo.
  • PipeName - имя канала, например, demo для сервера или .\demo для клиента.
  • HostName - имя подключаемого сервера (для клиента) или пустая строка (для сервера).
  • BaseName - базовое имя канала, например, demo - одинаковое для сервера и для клиента.
  • Handle - дескриптор канала или -1 если канал не открыт.
  • TimeOut - время ожидания операций подключения и ввода-выода, мс, по умолчанию 0.
  • RxBuffSize - размер буфера приемника Rx, по умолчанию 65536 байт.
  • TxBuffSize - размер буфера передатчика Tx, по умолчанию 65536 байт.
  • RxLost - счетчик потерянных байт приемника Rx.
  • TxLost - счетчик потерянных байт передатчика Tx.
  • RxTotal - счетчик общего числа принятых байт приемника Rx.
  • TxTotal - счетчик общего числа переданных байт передатчика Tx.
  • RxLength - счетчик байт накопленных в буфере FIFO приемника Rx.
  • TxLength - счетчик байт накопленных в буфере FIFO передатчика Tx.
  • RxFifoLimit - предел роста FIFO приемника Rx, по умолчанию 64 МБ.
  • TxFifoLimit - предел роста FIFO передатчика Tx, по умолчанию 64 МБ.
  • LogsHistory - длина истории журнала событий, по умолчанию 0 строк.
  • LogsCount - текущая длина журнала, в строках.
  • LogsTextMove - перемещает (копирует и очищает) текст журнала.
  • LogsTextCopy - копирует текст журнала.
  • Clear xxx - очищает список xxx=RxLost,TxLost,RxFifo,TxFifo,RxTotal,TxTotal,Logs (ненужное убрать из списка).
  • * - возвращает все параметры в виде списка.

Пример:

          1)Сервер:
            ... Инициализация ...
            ipc:=easyipc_init('test',''); // Для клиента указывается только basename - в данном случае test.
            ... В цикле опроса ...
            b:=easyipc_poll(ipc); // обязательно надо вызывать процедуру опроса канала
            if val(easyipc_ctrl(ipc,'connected'))>0 then // если подключен и есть данные
            if val(easyipc_ctrl(ipc,'rxlength'))>0  then writeln(easyipc_recv(ipc,maxint));
            if HasDataToSend then if not easyipc_send(ipc,data) then {Error};
            ... Завершение ...
            b:=easyipc_free(ipc);

          1)Клиент:
            ... Инициализация ...
            ipc:=easyipc_init('.\test',''); // Для клиента указывается hostname\basename - в данном случае .\test
            ... В цикле опроса ...
            b:=easyipc_poll(ipc); // обязательно надо вызывать процедуру опроса канала
            if val(easyipc_ctrl(ipc,'connected'))>0 then // если подключен и есть данные
            if val(easyipc_ctrl(ipc,'rxlength'))>0  then writeln(easyipc_recv(ipc,maxint));
            if HasDataToSend then if not easyipc_send(ipc,data) then {Error};
            ... Завершение ...
            b:=easyipc_free(ipc);

            

Daq Pascal Api -> daqdllinit daqdllfree daqdllcall

Определение:

function daqdllinit(name:String):Integer
function daqdllfree(id:Integer):Boolean
function daqdllinit(id,cmd:Integer):Boolean

Аргументы:

  • name - имя файла DLL библиотеки.
  • id - ссылка для вызова библиотеки.
  • cmd - команда для выполнения.

Результат:

Группа функций работы с внешними DLL библиотеками.

Описание:

Группа функций работы с внешними DLL библиотеками. Внешние библиотеки, поскольку они пишутся на Delphi и дают полный доступ к библиотекам Win32 API, могут использоваться для решения задач, для которых у Daq Pascal не хватает средств или производительности.

daqdllinit(name) - загружает DLL библиотеку по имени *.DLL файла name. В случае успеха возвращает некую отличную от нуля ссылку id, которая может быть использована только для вызова функций daqdllfree и daqdllcall. Если файл не найден или не содержит функции CRW32_PLUGIN, загрузки DLL не происходит и возвращается 0. Сразу после загрузки DLL файла DAQ-программа должна вызвать daqdllcall(id,DAQ_CMD_INIT) для того, чтобы библиотека могла себя корректно инициализировать. Если вызов вернул false, инициализация не прошла и библиотеку надо завершать и выгружать.

daqdllfree(id) - выгружает DLL со ссылкой id из памяти. Перед выгрузкой DAQ-программа должна вызвать daqdllcall(id,DAQ_CMD_FREE) для того, чтобы библиотека могла себя корректно завершить.

daqdllcall(id,cmd) - вызывает DLL со ссылкой id на выполнение. Функция возвращает true в случае успешного выполнения или false в случае ошибки в коде DLL. Параметр - команда cmd указывает библиотеке, что надо делать. Код DLL программы получает эту команду вызовом CrwApi.DaqApi.DaqCommand. Все остальные данные для работы код DLL библиотеки получает из тегов и кривых при помощи вызова функций CrwApi.

Есть стандартные команды, как описано в файле _CRWAPI.PAS:

          // Команды DaqApi.DaqCommand
          Daq_Cmd_Init      = 1;       // Команда "Начало работы"
          Daq_Cmd_Free      = 2;       // Команда "Завершение работы"
          Daq_Cmd_Poll      = 3;       // Команда "Опрос устройства"
          Daq_Cmd_User      = $1000;   // Начало пользовательских команд
         
Стандартными являются команды:
  • DAQ_CMD_INIT - это всегда первый вызов после загрузки, по которому код библиотеки должен инициализировать все, что ему надо для работы.
  • DAQ_CMD_FREE - это всегда последний вызов перед выгрузкой, по которому библиотека должна освободить все занятые ресурсы.
  • DAQ_CMD_POLL - штатная команда опроса, обычно вызывается в каждом кванте времени потока DAQ - программы для выполнения содержательной работы в процессе измерений.
Другие команды являются частной договоренностью кода Daq Pascal программы и Delphi программы.

Библиотеки пишутся на Delphi, встроенном в CRW-DAQ, по специальным правилам. Для компиляции библиотек надо открыть *.DPR файл в меню Файл/Открыть, указав тип файла "Программа". В этом окне будет кнопка компиляции . Краткая сводка самых элементарных правил:

  • Библиотека DLL должна экспортировать единственную функцию
    function CRW32_PLUGIN(TheCrwApi:TCrwApi):Integer; StdCall;
    Функция возвращает 0 при успешном выполнении или -1 при ошибке.
  • Единственным аргументом функции является экземпляр CrwApi класса TCrwApi.
    Класс TCrwApi описан в файле _CrwApi.pas, он должен использоваться для написания DLL. Класс TCrwApi содержит подклассы SysApi, GuiApi, DaqApi и т.д. В совокупности эти классы содержит более 500 функций. В частности, DaqApi содержит функции для взаимодействия с DAQ-системой (доступ к кривым, тегам и т.д.).
  • Вызов CrwApi.DaqApi.DaqCommand используется для получения кода команды cmd. Функция CRW32_PLUGIN должна корректно обрабатывать вызовы DAQ_CMD_INIT, DAQ_CMD_FREE, DAQ_CMD_POLL, а также другие команды (если надо).

Обычно библиотеки подключаются через стандартную DAQ программу _DLLWRAP.PAS. Эту программу можно использовать как шаблон для создания других программ для работы с DLL.

См. также: описание технологии создания DLL в файле DEMO_RFA-1229.doc, а также примеры из каталога Demo.

Пример:

USRDRV.CFG - простейший пример DLL драйвера. В каталоге Demo есть еще несколько примеров.

Программа - оболочка на DAQ PASCAL для вызова DLL:

          {
           *********************************************************************
           Назначение: Интерфейс для вызова DLL из DAQ Pascal.
           Пример конфигурирования:
           [DeviceList]
           &DRIVER = device software program
           [&DRIVER]
           ProgramSource   = ..\DAQPAS\_DLLWRAP.PAS ; Program source file
           DLL_FILE_PATH   = ..\DAQPAS\DRIVER.DLL   ; DLL driver
           .....                                   ; Other information
           *********************************************************************
          }
          program DLLWRAP;
          var
           hDll:Integer; { Ссылка на DLL }
           {
           Процедура освобождения DLL
           }
           procedure DLL_FREE(var hDll:Integer);
           var b:Boolean;
           begin
            if hDll<>0 then begin
             b:=daqdllcall(hDll,Daq_Cmd_Free);
             b:=daqdllfree(hDll);
             hDll:=0;
            end;
           end;
           {
           Процедура пытается загрузить DLL с заданным именем файла.
           Возвращает ненулевую ссылку DLL или ноль при ошибке.
           }
           function DLL_INIT(DllPath:String):Integer;
           var b:Boolean; hDll:Integer;
           begin
            hDll:=daqdllinit(DllPath);
            if hDll<>0 then if not daqdllcall(hDll,Daq_Cmd_Init) then DLL_FREE(hDll);
            if hDll=0 then b:=fixerror(registererr('Fail load '+DllPath+' !'));
            DLL_INIT:=hDll;
           end;
           {
           Процедура опроса DLL.
           }
           procedure DLL_POLL(hDll:Integer);
           var b:Boolean;
           begin
            if hDll<>0 then b:=daqdllcall(hDll,Daq_Cmd_Poll);
           end;
          begin
           {
           При старте загружаем DLL
           }
           if runcount=1 then hDll:=DLL_INIT(readini('DLL_FILE_PATH')) else
           {
           При останове удаляем DLL
           }
           if isinf(runcount) then DLL_FREE(hDll) else
           {
           В цикле опроса вызываем DLL
           }
           if hDll<>0 then DLL_POLL(hDll);
          end.
         
Простейший код DLL может выглядеть так:
          LIBRARY DEMO_DRIVER;

          {$I _sysdef}

          uses ShareMem, SysUtils, Windows, Math, Classes, mmsystem, ShellApi, _CrwApi;

          type
           PDemoDriver = ^TDemoDriver;               // Pointer to user driver data
           TDemoDriver = object                  // General user driver object
           public
            CrwApi        : TCrwApi;               // Points to CrwApi interface
            dllFilePath   : ShortString;            // Executable DLL file path
            OwnDevice     : Integer;               // Device reference
           public
            procedure DAQ_CMD_INIT(TheCrwApi:TCrwApi);   // Driver initialization
            procedure DAQ_CMD_FREE;               // Driver finalization
            procedure DAQ_CMD_POLL;               // Driver polling loop
            procedure DAQ_CMD_FAIL;               // Driver failure handler
           end;

          procedure TDemoDriver.DAQ_CMD_INIT(TheCrwApi:TCrwApi);
          begin
           FillChar(Self,sizeof(Self),0);
           CrwApi:=TheCrwApi;
           with CrwApi,SysApi,GuiApi,DaqApi do begin
            RedirectStdIn(Input);
            RedirectStdOut(Output);
            dllFilePath:=readini('DLL_FILE_PATH');
            OwnDevice:=FindObjectRef('Device','');
            Echo(Format('%s: Initializing %s ...',[DeviceName(OwnDevice),dllFilePath]));
           end;
          end;

          procedure TDemoDriver.DAQ_CMD_FREE;
          begin
           if Assigned(CrwApi) then
           with CrwApi,SysApi,GuiApi,DaqApi do begin
            Echo(Format('%s: Finalizing %s ...',[DeviceName(OwnDevice),dllFilePath]));
            DaqDataSheet(0);
           end;
          end;

          procedure TDemoDriver.DAQ_CMD_POLL;
          begin
           if Assigned(CrwApi) then
           with CrwApi,SysApi,GuiApi,DaqApi do begin
            Echo('Dll is running!');
           end;
          end;

          procedure TDemoDriver.DAQ_CMD_FAIL;
          begin
           if Assigned(CrwApi) then
           with CrwApi,SysApi,GuiApi,DaqApi do
           RAISE EDaqApi.Create(Format('%s: wrong DaqCommand=%d!',[DeviceName(OwnDevice),DaqCommand]));
          end;

          function CRW32_PLUGIN(TheCrwApi:TCrwApi):Integer; StdCall;
          var DemoDriver:PDemoDriver;
          begin
           Result:=0;
           with TheCrwApi,SysApi,GuiApi,DaqApi do
           try
            if Target <> ForDataAcquisition then RAISE EDaqApi.Create('DemoDriver: Invalid Target!');
            DemoDriver:=DaqDataSheet(sizeof(DemoDriver^));
            if not Assigned(DemoDriver)
            then RAISE EDaqApi.Create('DemoDriver: Out of memory!');
            if Assigned(DemoDriver.CrwApi) and (DemoDriver.CrwApi<>TheCrwApi)
            then RAISE EDaqApi.Create('DemoDriver: Invalid CrwApi.');
            case DaqCommand of
             DAQ_CMD_INIT: DemoDriver.DAQ_CMD_INIT(TheCrwApi);   // Initialize DemoDriver driver
             DAQ_CMD_FREE: DemoDriver.DAQ_CMD_FREE;         // Finalize   DemoDriver driver
             DAQ_CMD_POLL: DemoDriver.DAQ_CMD_POLL;         // General    DemoDriver data acquisition loop
             else          DemoDriver.DAQ_CMD_FAIL;         // Detected   DemoDriver driver failure
            end;
           except
            on E:Exception do Result:=-1;
           end;
          end;

          exports CRW32_PLUGIN name CRW32_PLUGIN_ID;
          begin
          end.

         

Daq Pascal Api -> hashlist_init hashlist_free hashlist_count hashlist_getkey hashlist_indexof hashlist_delete hashlist_getdata hashlist_setdata hashlist_getlink hashlist_setlink hashlist_getpara hashlist_setpara

Определение:

function hashlist_init(mode:Integer):Integer
function hashlist_free(hid:Integer):Boolean
function hashlist_count(hid:Integer):Integer
function hashlist_getkey(hid:Integer; index:Integer):String
function hashlist_indexof(hid:Integer; key:String):Integer
function hashlist_delete(hid:Integer; key:String):Boolean
function hashlist_getdata(hid:Integer; key:String):Real
function hashlist_setdata(hid:Integer; key:String; data:Real):Boolean
function hashlist_getlink(hid:Integer; key:String):Integer
function hashlist_setlink(hid:Integer; key:String; link:Integer):Boolean
function hashlist_getpara(hid:Integer; key:String):String
function hashlist_setpara(hid:Integer; key:String; para:String):Boolean

Аргументы:

  • mode - режим работы хеш-таблицы (чувствительность к регистру символов ключа key и метод хеширования).
  • hid - hashlist id, идентификатор, т.е. ссылка хеш-таблицы, используемая для работы с ней.
  • key - ключ для индексации элементов данных, используемый для доступа к данным хеш-таблицы.
  • data - вещественные данные (например измеренное значение), связанные с ключом key.
  • link - целочисленные данные (например ссылка), связанные с ключом key.
  • para - строковые данные (например строковый параметр), связанные с ключом key.
  • index - целочисленный индекс элементов списка.

Результат:

Работа с динамическими хеш-таблицами (см HashList). Хеш-таблицы - это списки элементов данных, которые индексируются по строковым ключам key. С каждым ключом связано три элемента - вещественное число data (например для хранения значения измеренных данных), целое число link (например для хранения ссылки) и строковый параметр para (например для хранения комментария).

Описание:

Набор функций hashlist_xxx предназначен для работы с хеш-таблицами, т.е. динамическими массивами данных, индексируемых строковыми ключами. Обычно это поименованные объекты (кривые, устройства, команды, теги, файлы), которые идентифицируются по именам. Хеш-таблицы позволяют работать с такими массивами с высокой скоростью доступа, практически не зависящей от размера массива.

Согласно Википедии, Хэш-таблица или хеш-таблица — это структура данных, реализующая интерфейс ассоциативного массива, а именно, она позволяет хранить пары (ключ, значение) = (key,value) и выполнять три операции: операцию добавления новой пары, операцию поиска и операцию удаления пары по ключу.

В библиотеке DaqPascal реализованы хеш-таблицы, которые позволяют хранить в качестве значений записи из трех элементов - вещественного числа data, целого числа link и строкового параметра para, поэтому каждому ключу соответствует запись (key, data, link, para). Кроме того, реализованные хеш-таблицы позволяют независимо менять значения данных (data,link,para) по ключу key.

Библиотека включает 12 функций:

  • hashlist_init(mode:Integer):Integer - создает хеш-таблицу с заданным режимом работы mode:
    • Бит 0 = 0/1 = не/чувствительность к регистру символов имен.
      При значении 0 ключи не чувствительны к регистру символов.
      При значении 1 ключи чувствительны к регистру символов.
      Надо иметь в виду, что хеш-таблицы, чувствительные к регистру символов, работают немного быстрее регистронезависимых, т.к. не тратится время на преобразование регистра символов.
    • Биты 8..15 (второй байт данных) - задает метод хеширования. В пакете реализована большая библиотека некриптографических хеш-функций. Задавая метод хеширования, можно выбрать одну из 39 библиотечных хеш-функций. По умолчанию используется функция c номером 0 (Hash32_RS). По методам хеширования см. здесь и здесь.
    Возвращает идентификатор хеш-таблицы (hashlist identifier, далее hid). Возвращает ноль, если создание хеш-таблицы не удалось. Идентификатор хеш-таблицы hid далее используется во всех остальных функциях для работы с хеш-таблицей, его можно также использовать в качестве ссылки при вызове refinfo.
    Например:
                   // Создать хеш-таблицу, чувствительную к регистру
                   // символов и выбрать функцию хеширования номер 3
    
                   hid:=hashlist_init(1+256*3);
                  

  • hashlist_free(hid:Integer):Boolean - освобождает хеш-таблицу hid. Всегда следует завершать работу с хеш-таблицей вызовом hashlist_free(hid).

  • hashlist_count(hid:Integer):Integer - возвращает текущее число элементов в списке хеш-таблицы hid. Возврачает 0, если список пуст.

  • hashlist_getkey(hid:Integer; index:Integer):String - возвращает ключ элемента списка хеш-таблицы hid с заданным численным индексом index, лежащим в диапазоне от 0 до hashlist_count(hid)-1. Эта функция позволяет пройти по всем элементам списка, если это требуется. Следует иметь в виду, что численный индекс элементов не является постоянной величиной и может меняться с добавлением или удалением элементов. А вот ключи элементов данных постоянны и используются для доступа к данным.

  • hashlist_indexof(hid:Integer; key:String):Integer - возвращает целочисленный индекс элемента данных хеш-таблицы hid по ключу key или значение -1, если элемента с таким ключом нет в списке. Функция служит для проверки наличия элементов в списке.

  • hashlist_delete(hid:Integer; key:String):Boolean - удаляет из хеш-таблицы hid элемент данных по ключу key.

  • hashlist_getdata(hid:Integer; key:String):Real - читает из хеш-таблицы hid элемент вещественных данных по ключу key. Если элемента с таким ключом нет, возвращает значение по умолчанию = 0. Наличие элементов проверяется вызовом hashlist_indexof.

  • hashlist_setdata(hid:Integer; key:String; data:Real):Boolean - создает или записывает в хеш-таблицу hid элемент data вещественных данных по ключу key. Если элемент данных с ключом key отсутствует, то создается новый элемент данных и помещается в список. Если элемент данных с ключом key уже существует, то производится запись данных.

  • hashlist_getlink(hid:Integer; key:String):Integer - читает из хеш-таблицы hid элемент целочисленных данных по ключу key. Если элемента с таким ключом нет, возвращает значение по умолчанию = 0. Наличие элементов проверяется вызовом hashlist_indexof.

  • hashlist_setlink(hid:Integer; key:String; link:Integer):Boolean - создает или записывает в хеш-таблицу hid элемент link целочисленных данных по ключу key. Если элемент данных с ключом key отсутствует, то создается новый элемент данных и помещается в список. Если элемент данных с ключом key уже существует, то производится запись данных.

  • hashlist_getpara(hid:Integer; key:String):String - читает из хеш-таблицы hid элемент строковых данных по ключу key. Если элемента с таким ключом нет, возвращает значение по умолчанию = ''. Наличие элементов проверяется вызовом hashlist_indexof.

  • hashlist_setpara(hid:Integer; key:String; para:String):Boolean - создает или записывает в хеш-таблицу hid элемент para строковых данных по ключу key. Если элемент данных с ключом key отсутствует, то создается новый элемент данных и помещается в список. Если элемент данных с ключом key уже существует, то производится запись данных.

Пример:

         procedure TestHashList(MaxIter:Integer);
         var hid,i,numerr,link:Integer; key,para:String; data:Real; b:Boolean;
         begin
          numerr:=0;
          hid:=HashList_Init(0);
          for i:=1 to MaxIter do begin
           key:='Data_'+Str(i);
           b:=HashList_SetData(hid,key,+i);
           b:=HashList_SetLink(hid,key,-i);
           b:=HashList_SetPara(hid,key,Str(2*i));
          end;
          b:=HashList_SetPara(hid,'abc','def');
          b:=HashList_SetPara(hid,'ghi','jkl');
          b:=HashList_SetPara(hid,'mno','pqr');
          b:=HashList_SetPara(hid,'stu','vwx');
          b:=HashList_SetPara(hid,'yz0','123');
          b:=HashList_SetPara(hid,'456','789');
          if HashList_Count(hid)<>MaxIter+6 then numerr:=numerr+1;
          writeln('Count = '+Str(HashList_Count(hid)));
          for i:=0 to HashList_Count(hid)-1 do begin
           key:=HashList_GetKey(hid,i);
           writeln(Str(i)+' '+key+' = '+Str(HashList_GetData(hid,key))+', '
                  +Str(HashList_GetLink(hid,key))+', '+HashList_GetPara(hid,key));
          end;
          b:=HashList_Delete(hid,'abc');
          b:=HashList_Delete(hid,'stu');
          if HashList_Count(hid)<>MaxIter+4 then numerr:=numerr+1;
          writeln('Count = '+Str(HashList_Count(hid)));
          for i:=0 to HashList_Count(hid)-1 do begin
           key:=HashList_GetKey(hid,i);
           writeln(Str(i)+' '+key+' = '+Str(HashList_GetData(hid,key))+', '
                  +Str(HashList_GetLink(hid,key))+', '+HashList_GetPara(hid,key));
          end;
          writeln('IndexOf(data_0) = '+Str(HashList_IndexOf(hid,'data_0')));
          writeln('IndexOf(data_1) = '+Str(HashList_IndexOf(hid,'data_1')));
          for i:=1 to MaxIter do begin
           key:='Data_'+Str(i);
           data:=HashList_GetData(hid,key); if data<>+i       then numerr:=numerr+1;
           link:=HashList_GetLink(hid,key); if link<>-i       then numerr:=numerr+1;
           para:=HashList_GetPara(hid,key); if para<>Str(2*i) then numerr:=numerr+1;
          end;
          writeln(Str(numerr)+' error(s) found');
          b:=HashList_Free(hid);
         end;
         

Daq Pascal Api -> fsm_new fsm_free fsm_ref fsm_root fsm_type fsm_parent fsm_name fsm_path fsm_ctrl fsm_count fsm_items fsm_get_iparam fsm_set_iparam fsm_get_fparam fsm_set_fparam fsm_get_sparam fsm_set_sparam fsm_add fsm_find fsm_get_state fsm_set_state fsm_modified fsm_name_rule

Определение:

function fsm_new:Integer;
function fsm_free(ref:Integer):Boolean;
function fsm_ref(ref:Integer):TFsmEntity;
function fsm_root(ref:Integer):Integer;
function fsm_type(ref:Integer):Integer;
function fsm_parent(ref:Integer):Integer;
function fsm_name(ref:Integer):String;
function fsm_path(ref:Integer):String;
function fsm_ctrl(ref:Integer; arg:String):String;
function fsm_count(ref,typ:Integer):Integer;
function fsm_items(ref,typ,i:Integer):Integer;
function fsm_get_iparam(ref:Integer):Integer;
function fsm_set_iparam(ref:Integer; data:Integer):Boolean;
function fsm_get_fparam(ref:Integer):Real;
function fsm_set_fparam(ref:Integer; data:Real):Boolean;
function fsm_get_sparam(ref:Integer):String;
function fsm_set_sparam(ref:Integer; data:String):Boolean;
function fsm_add(ref:Integer; typ:integer; key:String):Integer;
function fsm_find(ref:Integer; typ:integer; key:String):Integer;
function fsm_get_state(ref:Integer):Integer;
function fsm_set_state(ref:Integer; state:String):Integer;
function fsm_set_state(ref:Integer; state:Integer):Integer;
function fsm_link(ref:Integer; arg:String):Integer;
function fsm_modified(ref:Integer; delta:Integer):Integer;
function fsm_name_rule(typ:Integer):Integer;

Аргументы:

  • ref - ссылка Менеджера Конечных Автоматов FSM (Finite State Machine) или его дочерних элементов.
  • typ - тип интересующего элемента (entity) Конечного Автомата FSM.
  • key - ключ (имя или путь) для идентификации элемента Конечного Автомата FSM.
  • data - целые,вещественные или строковые данные для записи параметров.
  • i - целочисленный индекс для элементов Конечного Автомата FSM.
  • arg - строка аргументов для чтения/записи полей Конечного Автомата функцией fsm_ctrl и fsm_link.
  • state - новое состояние (имя или ссылка) логического объекта Конечного Автомата FSM.
  • delta - код маркера модификации объекта Конечного Автомата FSM функцией fsm_modified.

Результат:

Функции FSM API для работы с Конечными Автоматами (см. FSM).
Функции FSM API дополняются библиотечным модулем FsmManager со средствами прикладной разработки (чтение и разбор SML файлов и прочее).

Описание:

Набор функций fsm_xxx предназначен для работы с Конечными Автоматами - FSM (Finite State Machine).
Реализация FSM в целом ориентируется на поддержку технологии SMI++, поэтому структура и терминология соответствует ей.
Однако библиотеку можно использовать и автономно для решения задач управления без использования SMI++.

Конечные Автоматы (FSM) - это (концептуально) логическая модель для описания (сложных) объектов управления.
Как всякая модель, концепция FSM предполагает некоторый уровень абстрагирования, т.е. отбрасывания несущественных (с точки зрения задач управления) подробностей. Как и всякая абстрактная модель, она может быть адекватной (соответсвующей реальности) или неадекватной (не соответствующей реальности). Задача формулирования адекватной модели FSM - сложная задача, которую надо четко отделять от задачи реализации уже сформулированной логической модели FSM. Здесь предполагается, что адекватная модель FSM уже сформулирована, и рассматриваются только средства для её реализации.

Конечные Автоматы FSM имеют сложную структуру и состоят из элементов или сущностей (entity).
Элементы делятся на
1) контейнерные элементы, которые могут содержать другие (дочерние) элементы, т.е. быть их родителями (parent), и
2) элементы - параметры (parameter), которые содержат скалярные данные (int,float,string), но не другие элементы.
Все контейнерные элементы могут содержать элементы - параметры для своих внутренних нужд.
Число дочерних элементов в контейнерах (в принципе) не ограничено.

Главным или корневым (root) контейнерным элементом FSM является Менеджер FSM (manager), который создается функцией fsm_new и имеет фиксированное начальное имя FsmManager (которое можно изменить вызовом fsm_ctrl(fsm,'name=...')). Менеджер FSM - это корневой контейнер, который содержит в себе список Доменов (domain), т.е. "рабочих групп", содержащих в себе логически связанные объекты. Например, в сложной системе домены могут означать автономные подсистемы, в которые входят объекты (приборы и устройства).

Домены FSM - это контейнеры, которые содержат в себе Объекты (object), Классы (class) и Наборы объектов (objectset). Домены можно представить себе как "рабочие группы", в которые объединяются тесно связанные друг с другом "рабочие" - объекты, активные элементы FSM. Например, в домен могут объединяться объекты, работающие на одном физическом сервере, или объекты, обслуживающие одну (автономную) подсистему.

Объекты FSM (object) - это контейнеры, содержащие в себе конечное число поименованных Состояний (state), в которых может находиться объект, а также Функции (function) для выполнения различных инструкций. Можно сказать, что объекты FSM - это логические модели реальных (физических) устройств, которым (в рамках модели FSM) приписываются состояния, в которых они могут находиться. Например, источник питания (ИП) может быть ВКЛЮЧЕН, ВЫКЛЮЧЕН и НЕИСПРАВЕН. Другие подробности (например, в чем именно состоит неисправность ИП), для задачи управления (верхнего уровня) несущественна. Классы FSM (class) - это прототипы для описания похожих объектов. Если в системе есть несколько однотипных объектов, удобно описать эти объекты в виде класса, а затем вместо описания каждого объекта указать на его принадлежность этому классу. Классы являются виртуальными объектами, т.е. они не работают явно, а только служат шаблоном для создания реальных объектов путем их копирования. Наборы объектов FSM (objectset) - это группы (списки) объектов для групповых операций с ними. Например, если объединить все источники питания в одну группу, то включать и выключать ИП можно путем операции с группой, вместо множества операций с каждым ИП по отдельности.

Состояния FSM (state) - это контейнеры, содержащие в себе конечное число поименованных Действий (action), то есть некоторых процедур, в результате которых состояние этого объекта (и других тоже) может измениться. Например, в состоянии ВЫКЛЮЧЕН источник питания может иметь действия ВКЛЮЧИТЬ (если он работает) или ЗАБРАКОВАТЬ (если обнаружена неисправность).

Действия FSM (action) - это контейнеры, которые могут содержать только параметры вызова, необходимые для выполнения этих действий. Функции FSM (function) - аналогичны действиям (action), только они принадлежат, не состояниям, а объектам. Например, если разные действия (action) содержат одинаковые инстукции, их можно оформить в виде функций и вызывать по мере необходимости при реализации этих действий.

Библиотека включает следующие функции:

  • fsm_new - создает Менеджер FSM (с фиксированным начальным именем FsmManager, которое можно изменить вызовом fsm_ctrl(fsm,'name=...')) и типом fsm_type_manager) и возвращает его ссылку.
    Менеджер FSM является корневым (root) элементом - контейнером, содержащим все остальные элементы FSM.
    Именно его надо хранить для работы с FSM и освобождать вызовом fsm_free в конце работы.

  • fsm_free(ref) - уничтожает экземпляр Менеджера FSM со ссылкой ref, созданной ранее вызовом fsm_new.
    Вызывается в конце работы для освобождения занимаемой памяти.

  • fsm_ref(ref) - проверяет ссылку элемента FSM.
    Если ссылка действительная (соответствует существующему элементу FSM), возвращается ненулевая ссылка (ref), иначе возвращается ноль.

  • fsm_root(ref) - возвращает корневой элемент (т.е. Менеджер FSM) для ссылки ref элемента FSM.
    Эта функция позволяет по любому элементу FSM быстро найти Менеждер, которому он принадлежит.

  • fsm_type(ref) - возвращает тип элемента FSM со ссылкой ref.
    Это одна из следующих констант:
    1. fsm_type_nil - NIL, т.е. несуществущий элемент.
    2. fsm_type_int - параметр типа (int) - целочисленный.
    3. fsm_type_float - параметр типа (float) - вещественный.
    4. fsm_type_string - параметр типа (string) - строковый.
    5. fsm_type_parameter - любой параметр (parameter) - абстрактный тип.
    6. fsm_type_manager - менеджер (manager) - корневой контейнерный элемент FSM, содержащий список доменов.
    7. fsm_type_domain - домен (domain) - принадлежащий Менеждеру контейнер для логических объектов FSM.
    8. fsm_type_class - класс (class) - прототип для однотипных логических объектов FSM в домене.
    9. fsm_type_object - логический объект (object) - основной рабочий элемент FSM в домене.
    10. fsm_type_state - состояние (state) логического объекта FSM.
    11. fsm_type_action - действие (action) для изменения состояния логического объекта FSM.
    12. fsm_type_objectset - набор (set) логических объектов FSM для групповых операций с ними.
    13. fsm_type_function - функция (function) - набор инструкций для работы с объектами FSM.
    14. fsm_type_parent - родительский (parent) элемент FSM - абстрактный тип.
    15. fsm_type_entity - любой элемент (entity) FSM - абстрактный тип.
      fsm_type_any - синоним fsm_type_entity.

    Абстрактные типы применяются для поиска элементов.
    Тип fsm_type_entity или fsm_type_any обозначает любой существующий элемент.
    Тип fsm_type_parameter обозначает любой элемент-параметр (int,float,string), содержащий склярные данные, но не другие элементы.
    Тип fsm_type_parent обозначает любой контейнерный тип (manager,domain,class,object,state,action,objectset,function), который может содержать другие (дочерние) элементы. Все контейнерные типы могут содержать свои (дочерние) параметры (parameter). Кроме того, менеджер (manager) содержит домены (domain); домены содержат классы, объекты и их наборы (class,object,objectset); классы и объекты содержат состояния и функции (state,function); состояния содержат действия (action).

  • fsm_parent(ref) - возвращает родительскую ссылку элемента FSM со ссылкой ref.
    Менеджер (manager) не имеет родителя (возвращает ноль).
    Родителем домена (domain) является менеджер.
    Родителем класса/объекта/набора (class,object,objectset) является домен.
    Родителем состояния и функции (state,function) является класс/объект.
    Родителем действия (action) является состояние.
        Дерево принадлежности элементов FSM:
    
        Manager                   Менеджер FSM
        |_Parameter               Параметры менеждера (int,float,string)
        |_Domain                  Домены менеджера
          |_Parameter             Параметры домена
          |_Class,Object          Классы или объекты, принадлежащие домену
          | |_Parameter           Параметры класса или объекта
          | |_State               Состояния объекта, принадлежащие классу/объекту
          | | |_Parameter         Параметры состояния
          | | |_Action            Действия, принадлежащие состоянию
          | |   |_Parameter       Параметры вызова действия
          | |_Function            Функции принадлежащие классу/объекту
          |   |_Parameter         Параметры вызова функции
          |_ObjectSet             Наборы (группы) объектов в домене
            |_Parameter           Параметры набора объектов
                  
    Независимо от глубины иерархии ссылки (ref), корневой элемент (менеджер) доступен вызовом fsm_root(ref).

  • fsm_name(ref) - возвращает имя (name) элемента FSM.
    Имена элементов соответствуют правилам имен данного типа IsLexeme(name,rule), где rule=fsm_name_rule(fsm_type(ref)) и идентифицируют элемент в своем родительском контейнере. Имена при поиске элементов не чувствительны к регистру, но хранятся они всегда в верхнем регистре. Имена элементов должны быть уникальны в своем контейнере. Например, нельзя создать в домене объект и класс с одним именем.

  • fsm_path(ref) - возвращает путь (path) элемента FSM.
    Пути элементов идентифицируют элемент глобально, т.е. в пространстве путей корневого Менеждера. Они имеют следующий вид:
        #PARAM                                  параметр Менеждера
        DOMAIN                                  имя      Домена
        DOMAIN#PARAM                            параметр Домена
        DOMAIN::OBJECT                          путь     Объекта/Класса/Набора
        DOMAIN::OBJECT#PARAM                    параметр Объекта
        DOMAIN::OBJECT/STATE                    путь     Состояния/Функции
        DOMAIN::OBJECT/STATE#PARAM              параметр Состояния
        DOMAIN::OBJECT/STATE/ACTION             путь     Действия
        DOMAIN::OBJECT/STATE/ACTION#PARAM       параметр Действия
    
        Например:
           DEMO::LOGGER/READY/START#RUN_NUM
        содержит путь параметра RUN_NUM действия START
        состояния READY объекта LOGGER в домене DEMO
                  
    Пути объектов используются при глобальном поиске (fsm_find) элементов в Менеджере (с любой глубиной иерархии).

  • fsm_ctrl(ref,arg) - функция управления (control) для чтения/записи внутренних данных (полей) элемента FSM со ссылкой ref.
    Аргумент arg имеет вид name для чтения или name=value для записи (без лишних пробелов).
        Имя параметра   - Комментарий                                           (применимо для ...   )
        *               - список всех свойств, разделенный CRLF                 (для всех            )
        *=              - список всех свойств, доступных для записи (writable)  (для всех            )
        state           - текущее состояние объекта, аналог fsm_get/set_state   (для class/object    )
        initial_state   - начальное состояние объекта                           (для class/object    )
        dead_state      - состояние мертвого объекта                            (для class/object    )
        name            - имя объекта,  аналог fsm_name                         (для всех            )
        path            - путь объекта, аналог fsm_path                         (для всех            )
        type            - тип объекта, в виде строки вида FsmDomain             (для всех            )
        classname       - имя класса объекта в виде строки вида TFsmDomain      (для всех            )
        catalog         - каталог Менеджера, т.е. список путей всех элементов   (для всех            )
        cookie          - поле для произвольных данных в виде списка name=value (для контейнеров     )
        body            - поле для хранения кода                                (для контейнеров     )
        color           - поле для хранения цвета состояния                     (для контейнеров     )
        defaultcolor    - поле для хранения цвета по умолчанию                  (для manager         )
        associated      - поле для флага /associated                            (для class/object    )
        declaration     - декларация элемента (object: LOGGER /associated)      (для всех            )
        is_of_class     - поле для хранения свойства is_of_class                (для object/objectset)
        unionlist       - поле для списка объектов, входящих в набор            (для objectset       )
        unionmode       - поле для флага union                                  (для objectset       )
        visible         - поле для свойства видимости (!visible:)               (для контейнеров     )
        Примечание: (серым/синим) показаны свойства, доступные для (чтения/записи)
    
        Например: s:=fsm_ctrl(obj,'state=READY'); // установить для объекта obj состояние READY
                  
    При использовании вызова вида fsm_ctrl(obj,'state=READY') следует иметь в виду, что это вполне допустимый метод установки состояния объекта (obj) по имени состояния, но он работает несколько медленнее, чем вызов fsm_set_state(obj,state) с установкой состояния объекта (obj) по ссылке состояния (state). Поэтому из соображений удобства можно использовать fsm_ctrl(obj,'state=...'), а из соображений производительности следует запоминать ссылки состояний в локальных переменных, чтобы затем использовать вызов fsm_set_state(obj,state).

  • fsm_count(ref,typ) - счетчик списка дочерних элементов FSM типа typ для элемента ref.
    Применяется для контейнерных элементов (которые могут содержать дочерние элементы).
    Параметр типа (typ) указывает, какой именно тип дочерних элементов интересует. Например:
        procedure FsmTest1;
        var fsm,i:Integer;
        begin
         fsm:=fsm_new;                                             // create FsmManager
         i:=fsm_add(fsm,fsm_type_domain,'DEMO');                   // add domain DEMO
         i:=fsm_add(fsm,fsm_type_domain,'TEST');                   // add domain TEST
         for i:=0 to fsm_count(fsm,fsm_type_domain)                // for all domains
         do writeln(fsm_name(fsm_items(fsm,fsm_type_domain,i)));   // print domain name
         bNul(fsm_free(fsm));                                      // free FsmManager
        end;
                  
    Доступные для перечисления типы зависят от элемента. Например, в домене можно перечислять объекты, в объекте - состояния, а в состоянии - действия.
    При указании абстрактных типов (например, fsm_type_any) перечисляются все типы дочерних элементов, принадлежащие этому абстрактному типу. Например, fsm_count(obj,fsm_type_parameter) вернет счетчик всех параметров объекта (obj).

  • fsm_items(ref,typ,i) - возвращает ссылку дочернего элемента FSM типа typ с индексом i для элемента ref.
    Применяется для контейнерных элементов (которые могут содержать дочерние элементы).
    Индекс (i) меняется от 0 до fsm_count(ref,typ)-1. См. пример выше.

  • fsm_get_iparam(ref), fsm_set_iparam(ref,data)
    fsm_get_fparam(ref), fsm_set_fparam(ref,data)
    fsm_get_sparam(ref), fsm_set_sparam(ref,data)
    - функции чтения/записи скалярных параметров элемента FSM со ссылкой ref для типов данных (int,float,string).
    Например:
        procedure FsmTest2;
        var fsm,dom,par,i:Integer;
        begin
         fsm:=fsm_new;                                             // create FsmManager
         dom:=fsm_add(fsm,fsm_type_domain,'DEMO');                 // add domain DEMO
         par:=fsm_add(dom,fsm_type_int,'NUMBER');                  // add domain parameter int NUMBER
         bNul(fsm_set_iparam(par,123));                            // set parameter value
         par:=fsm_add(dom,fsm_type_float,'VOLT');                  // add domain parameter float VOLT
         bNul(fsm_set_fparam(par,pi));                             // set parameter value
         par:=fsm_add(dom,fsm_type_string,'USER');                 // add domain parameter string USER
         bNul(fsm_set_sparam(par,paramstr('username')));           // set parameter value
         for i:=0 to fsm_count(dom,fsm_type_parameter)-1 do begin  // for all domain parameters
          par:=fsm_items(dom,fsm_type_parameter,i);                // reference of parameter[i]
          write(fsm_name(par));                                    // print parameter name
          if fsm_type(par)=fsm_type_int then writeln(' = ',fsm_get_iparam(par));    // int value
          if fsm_type(par)=fsm_type_float then writeln(' = ',fsm_get_fparam(par));  // float value
          if fsm_type(par)=fsm_type_string then writeln(' = ',fsm_get_sparam(par)); // string value
         end;
         bNul(fsm_free(fsm));                                      // free FsmManager
        end;
                  
    Скалярные параметры могут иметь любые контейнерные элементы, но сами параметры не содержат других элементов. Число параметров в контейнерах не ограничено.

  • fsm_add(ref,typ,key) - добавляет в контейнерный элемент FSM со ссылкой ref новый дочерний элемент типа typ с ключем key и возвращает ссылку на созданный элемент (или ноль при неудаче). В качестве ключа (key) используется имя создаваемого дочернего элемента или его путь (только при добавлении элементов в Менеджере). Тип создаваемого элемента (typ) должен быть согласован с типом контейнера (ref), иначе элемент не будет создан (функция вернет ноль). Например, в домене можно создать объект, но нельзя создать состояние или действие. Например:
        fsm:=fsm_new;                                   // Создать Менеджер
        dom:=fsm_add(fsm,fsm_type_domain,'EGP');        // Создать Домен EGP в менеджере
        obj:=fsm_add(dom,fsm_type_object,'UPS');        // Создать Объект UPS в домене EGP
        sta:=fsm_add(obj,fsm_type_state,'OFF');         // Создать Состояние OFF в объекте UPS
        act:=fsm_add(sta,fsm_type_action,'SWITCH_ON');  // Создать Действие SWITCH_ON в состоянии OFF
        par:=fsm_add(act,fsm_type_float,'VOLTAGE');     // Создать Параметр VOLTAGE в Действии SWITCH_ON
        bNul(fsm_set_fparam(par,12.5));                 // Задать  значение 12.5 для параметра VOLTAGE
        xxx:=fsm_add(dom,fsm_type_state,'ERROR');      // !!! Ошибка, нельзя создать состояние в Домене!!!
                  
    Элементы, созданные вызовом fsm_add, не требуется освобождать, т.к. они принадлежат Менеджеру FSM и освобождаются вместе с ним.

  • fsm_find(ref,typ,key) - ищет в контейнерном элементе FSM со ссылкой ref существующий дочерний элемент типа typ с ключем key и возвращает ссылку на найденный элемент (или ноль при неудаче). В качестве ключа (key) используется имя искомого дочернего элемента или его путь (только при поиске элементов в Менеджере). Следует учитывать, что локальный поиск в контейнере дочерних элементов по имени работает несколько быстрее, чем глобальный поиск элементов по пути в Менеджере. Поэтому по возможности лучше использовать локальный поиск, сохраняя при необходимости ссылки интересующих контейнеров.

  • fsm_get_state(ref), fsm_set_state(ref,state) - функции чтения/записи текущего состояния state логического объекта FSM со ссылкой ref.
    Функции применяются только к объектам (object) и их прототипам - классам (class). Обе функции возвращают ссылку (типа fsm_type_state) на текущее состояние объекта. Состояние state - это ссылка на элемент состояния объекта типа fsm_type_state или имя этого состояния. Ссылку можно получить по имени из вызова fsm_add(obj,fsm_type_state,name) или fsm_find(obj,fsm_type_state,name). Имя состояния по ссылке можно узнать вызовом типа fsm_name(ref).
    Например:
        obj:=fsm_find(fsm,fsm_type_object,'EGP::UPS');  // Найти объект UPS в домене EGP менеджера fsm
        ... в цикле опроса ...
        if (fsm_name(fsm_get_state(obj))='OFF')         // Если состояние OFF
        then sta:=fsm_set_state(obj,'ON');              // то задать состояние ON
    
        другой вариант, существенно более быстрый, но более сложный:
    
        staON:=fsm_find(obj,fsm_type_state,'ON');       // Найти и запомнить ссылку состояния ON
        staOFF:=fsm_find(obj,fsm_type_state,'OFF');     // Найти и запомнить ссылку состояния OFF
        ... в цикле опроса ...
        if (fsm_get_state(obj)=staOFF)                  // Если состояние OFF
        then sta:=fsm_set_state(obj,staON);             // то задать состояние ON
    
        сложность второго подхода в том, что он требует сохранять ссылки состояний.
                  
    Функция fsm_set_state(ref,state) является полиморфной, т.е. принимает аргументы разных типов. В качестве аргумента (state) можно указать как имя нового состояния, так и его ссылку. Работа со ссылкой идет ощутимо (в 3-4 раза) быстрее, но и при использовании имени производительность тоже достаточно высока и приемлема для подавляющего числа задач.

    Функция fsm_set_state(ref,state) защищена от грубых ошибок. Если в качестве состояния указать неверную ссылку (например, ноль или ссылку на другой тип элемента) или несуществующее имя состояния, то состояние объекта не изменится, а функция вернет ссылку на прежнее состояние. При желании сбросить состояние объекта в ноль можно сделать вызов fsm_ctrl(ref,'state=<NIL>'. Это единственный способ задать "нулевое" состояние (хотя непонятно зачем это надо - разве что для тестирования). При нормальной работе "нулевое" состояние никогда не должно возникать или использоваться.

    Состояние можно задавать также вызовом типа fsm_ctrl(ref,'state=OFF'), но вызов fsm_set_state(ref,state) со ссылкой будет быстрее (при наличии ссылки state). Для повышения скорости работы можно сохранять ссылки состояний в переменных (например state_ready:=fsm_find(obj,fsm_type_state,'READY')) и затем использовать эти ссылки (например fsm_set_state(obj,state_ready)).

    При обработке данных можно хранить минимальное число переменных - например, можно хранить только текущее состояние, так как другие ссылки легко найти:
        sta:=fsm_get_state(fsm_find(fsm,fsm_type_object,'EGP::UPS'));   // текущее состояние объекта UPS в домене EGP
        ... в цикле опроса ...
        if (fsm_name(sta)='OFF')                                        // Если текущее состояние OFF
        then sta:=fsm_set_state(fsm_parent(sta),'ON');                  // то задать новое состояние ON
    
        ссылку объекта можно не хранить, а использовать вместо этого вызов fsm_parent(sta)
                  
    При анализе состояний следует помнить, что все имена элементов FSM (кроме менеджера) хранятся в верхнем регистре, поэтому для сравнения имен можно использовать не только IsSameText(), но и прямое сравнение строк, при этом имена состояний надо задавать в верхнем регистре.

  • fsm_link(ref,arg) - возвращает для элемента FSM со ссылкой (ref) связанную ссылку (link) с идентификатором arg. Эта функция позволяет "привязывать" к элементу FSM дополнительные данные - теги, кривые, устройства и т.д. При этом число "привязанных" ссылок не ограничено, а доступ к ним осуществляется по имени.
    Аргумент arg имеет вид name для чтения или name=value для записи связанной ссылки. Если ссылка (ref) недействительна (не соответствует существующему элементу FSM), или если имя name не соответсвует правилу имен IsLexeme(name,fsm_name_rule(fsm_type_int))), или если значение связанной ссылки не было ранее задано вызовом fsm_link, то возвращается ноль.
    Например:
      Привязка:
        tag:=findtag('COUNTER');                    // Поиск тега COUNTER
        iNul(fsm_link(fsm,'tagCOUNTER='+Str(tag))); // Запись (привязка) тега COUNTER под именем 'tagCOUNTER'
      Использование:
        tag:=fsm_link(fsm,'tagCOUNTER');            // Чтение тега по имени 'tag'
        bNul(iSetTag(tag,123));                     // Использование тега
                  
    Функция fsm_link используется для хранения связанных ссылок в runtime, когда чтение ссылок производится часто (в цикле опроса), а запись - редко (при старте). Использование связанных ссылок позволяяет резко сократить число статических переменных, т.к. многие данные можно хранить в связанных ссылках элементов FSM, а также позволяет хранить динамические данные (например, загружаемые из файла) вместе с элементами FSM.

  • fsm_modified(ref,delta) - функция для чтения/записи счетчика (маркера) модификации элемента FSM со ссылкой ref с кодом delta. Это вспомогательная функция для организации обработки событий Конечного Автомата в случае изменения его состояний. В некоторых случаях обработка событий (т.е. изменений или модификаций элементов) может или должна быть отложена. Например, состояние объекта было изменено по сообщению, а обработку изменения удобнее делать в цикле опроса. В этом случае объект можно не обрабатывать сразу, а пометить как "модифицированный" для дальнейшей обработки. При этом считается, что при модификации элемента его родительский элемент тоже становится модификированным, т.е. тоже должен быть маркирован. Это правило облегчает организацию цикла обработки состояний.
    Работа функции зависит от значения delta.
    При delta<0 вызов fsm_modified(ref,delta) просто возвращает счетчик модификаций элемента со ссылкой ref. Нулевой счетчик маркирует отсутсвие изменений (и тогда обработка или обновление не требуется).
    При delta=0 вызов fsm_modified(ref,delta) возвращает счетчик модификаций элемента со ссылкой ref, а затем сбрасывает его в ноль, то есть маркирует его как "не модифицированный". Чтение со сбросом позволяет организовать совмещенную проверку счетчика модификаций и его очистку в процедуре обработки Конечного Автомата.
    При delta>0 вызов fsm_modified(ref,delta) инкрементирует счетчик модификаций элемента со ссылкой ref на величину delta, и затем возвращает его новое значение. Кроме того, вызывается рекурсивная процедура модификации родительского элемента, т.к. при модификации элемента его родитель тоже считается модифицированным. Маркировка элемента с помощью счетчика модификаций позволяет отложить обработку событий, когда вместо непосредственной обработки элементы маркируются как модифицированные, а потом (в другом месте) все модифицированные элементы централизованно обрабатываюся.
    Например:
      Модификация по событию:
        obj:=fsm_find(fsm,'DEMO::LOGGER');                      // Находим объект DEMO::LOGGER
        sta:=fsm_set_state(obj,'LOGGING');                      // Задаем ему новое состояние LOGGING
        iNul(fsm_modified(obj,1));                              // Маркируем объект как модифицированный
      Использование в цикле опроса:
        if fsm_modified(fsm,0)>0 then                           // Если Конечный Автомат модифицирован
        for i:=0 to fsm_count(fsm,fsm_type_domain)-1 do begin   // то в цикле по доменам:
         dom:=fsm_items(fsm,fsm_type_domain,i);                 // берем домен
         if fsm_modified(dom)>0 then                            // проверяем, был ли он модифицирован
         for j:=0 to fsm_count(dom,fsm_type_object)-1 do begin  // если был, то в цикле по объектам
          obj:=fsm_items(dom,fsm_type_object,j);                // берем объект
          if fsm_modified(obj,0)>0 then begin                   // проверяем, был ли он модифицирован
           writeln('Object '+fsm_name(obj)+' was modified.');   // и обрабатываем его (в данном случае - печатаем)
          end;
         end;
        end;
                  
    Функция fsm_modified используется для удобной и эффективной организации цикла обработки обработки событий, связанных с элементами FSM.

  • fsm_name_rule(typ) - функция возвращает правило имен (код) для элемента FSM с типом typ. Возвращаемое значение - это код для для функции лексического анализа IsLexeme, нужный для проверки имени данного типа. Это вспомогательная функция, используемая для проверки имен при лексическом анализе.
    Например:
        if IsLexeme(name,fsm_name_rule(fsm_type_domain))
        then dom:=fsm_add(fsm,fsm_type_domain,name)
        else writeln('Invalid domain name '+name);
                  
    Функция fsm_name_rule используется для синтаксического анализа имен, связанных с элементами FSM, с помощью функции IsLexeme.

    В настоящей версии: правило имен для (int,float,string,parameter,manager,domain,objectset,function) соответствует lex_Name; правило имен для (object,state,action) соответствует lex_FsmName.

Примечание: Тестирование производительности показало примерно такие результаты на CPU i7-4700MQ-2.4GHz:
    Вызов                         Время вызова   Комментарий
    fsm_get_state(obj)            0.125 mks      Чтение  состояния по ссылке
    fsm_set_state(obj,state)      0.250 mks      Задание состояния по ссылке
    fsm_set_state(obj,name)       0.950 mks      Задание состояния по имени
    fsm_ctrl(obj,'state')         1.250 mks      Чтение  состояния по имени через fsm_ctrl
    fsm_ctrl(obj,'state='+key)    2.500 mks      Задание состояния по имени через fsm_ctrl
    fsm_find(obj,name)            0.790 mks      Локальный  поиск элемента по имени
    fsm_find(fsm,path)            1.550 mks      Глобальный поиск элемента по пути
    fsm_link(fsm,name)            0.470 mks      Чтение привязанной ссылки по имени
    fsm_modified(fsm,0)           0.078 mks      Чтение/сброс счетчика модификаций
         
Это очень грубые оценки, полученные при размерностях порядка (dom[3]*obj[10]*state[10]*act[10],name[10]), но они отражают основные закономерности. Наиболее быстрая, "дешевая" по времени работа происходит с готовыми ссылками (но при этом надо позаботиться об их сохранении). Чтение/изменение состояния по имени удобно, но ощутимо "дороже", т.к. работа со строками вообще идет медленнее, к тому же состояние приходится искать по имени в списке дочерних элементов. Сравнительно "дорогим" является также поиск элементов, причем "локальный" поиск по имени идет быстрее, чем глобальный поиск по пути. Эти соображения следует учитывать при программировании, отдавая предпочтение тому или иному способу работы в зависимости от задачи. Важно, однако, отметить, что при работе со ссылками скорость FSM примерно соответствует максимально возможной для программ на языке DaqPascal (для сравнения, вызов msecnow занимает примерно 0.1 mks). Другими словами, скорость FSM вполне достаточна для решения задач, которые вообще можно решать средствами DaqPascal.

Пример:

Следующий пример содержит вызовы всех функций FSM, а также грубую оценку производительности.
 {
 Test 8.
 Check FSM functions.
 }
 procedure Test8;
 var n:Integer; ms:Real;
  procedure Timing(who,arg:String; nn:Integer);
  begin
   n:=nn;
   if (who='') then ms:=msecnow else begin
    ms:=msecnow-ms;
    writeln(who,' ',ms*1000/n:1:3,' mks,  ',ms*1e6/n/length(arg):1:3,' ns/char,  ',ms:1:0,' ms');
   end;
  end;
  procedure FsmTest1;
  var fsm,dom,i:Integer;
  begin
   Success(GetDateTime(msecnow)+' => FSM Test1.');
   fsm:=fsm_new;                                             // create FsmManager
   dom:=fsm_add(fsm,fsm_type_domain,'DEMO');                 // add domain DEMO
   dom:=fsm_add(fsm,fsm_type_domain,'TEST');                 // add domain TEST
   for i:=0 to fsm_count(fsm,fsm_type_domain)                // for all domains
   do writeln(fsm_name(fsm_items(fsm,fsm_type_domain,i)));   // print domain name
   bNul(fsm_free(fsm));                                      // free FsmManager
  end;
  procedure FsmTest2;
  var fsm,dom,par,i:Integer;
  begin
   Success(GetDateTime(msecnow)+' => FSM Test2.');
   fsm:=fsm_new;                                             // create FsmManager
   dom:=fsm_add(fsm,fsm_type_domain,'DEMO');                 // add domain DEMO
   par:=fsm_add(dom,fsm_type_int,'NUMBER');                  // add domain parameter int NUMBER
   bNul(fsm_set_iparam(par,123));                            // set parameter value
   par:=fsm_add(dom,fsm_type_float,'VOLT');                  // add domain parameter float VOLT
   bNul(fsm_set_fparam(par,pi));                             // set parameter value
   par:=fsm_add(dom,fsm_type_string,'USER');                 // add domain parameter string USER
   bNul(fsm_set_sparam(par,paramstr('username')));           // set parameter value
   for i:=0 to fsm_count(dom,fsm_type_parameter)-1 do begin  // for all domain parameters
    par:=fsm_items(dom,fsm_type_parameter,i);                // reference of parameter[i]
    write(fsm_name(par));                                    // print parameter name
    if fsm_type(par)=fsm_type_int then write(' = ',fsm_get_iparam(par));    // int value
    if fsm_type(par)=fsm_type_float then write(' = ',fsm_get_fparam(par));  // float value
    if fsm_type(par)=fsm_type_string then write(' = ',fsm_get_sparam(par)); // string value
    writeln;
   end;
   bNul(fsm_free(fsm));                                      // free FsmManager
  end;
  procedure FsmTest3(ndom,nobj,nsta,nact,npar:Integer);
  var fsm,dom,obj,sta,act,par,idom,iobj,ista,iact,ipar,ref,nerr,i:Integer; key:String;
  begin
   key:='';
   Success(GetDateTime(msecnow)+' => FSM Test3.');
   nerr:=0;
   fsm:=fsm_new;
   if not IsSameText(fsm_name(fsm),'FsmManager') then nerr:=nerr+1;
   for idom:=1 to ndom do begin
    dom:=fsm_add(fsm,fsm_type_domain,StrFmt('domain_%d',idom));
    if (fsm_type(dom)<>fsm_type_domain) then nerr:=nerr+1;
    if (fsm_type(fsm_root(dom))<>fsm_type_manager) then nerr:=nerr+1;
    if (fsm_type(fsm_parent(dom))<>fsm_type_manager) then nerr:=nerr+1;
    for iobj:=1 to nobj do begin
     obj:=fsm_add(dom,fsm_type_object,StrFmt('object_%d',iobj));
     if (fsm_type(obj)<>fsm_type_object) then nerr:=nerr+1;
     if (fsm_type(fsm_root(obj))<>fsm_type_manager) then nerr:=nerr+1;
     if (fsm_type(fsm_parent(obj))<>fsm_type_domain) then nerr:=nerr+1;
     for ista:=1 to nsta do begin
      sta:=fsm_add(obj,fsm_type_state,StrFmt('state_%d',ista));
      if (fsm_type(sta)<>fsm_type_state) then nerr:=nerr+1;
      if (fsm_type(fsm_root(sta))<>fsm_type_manager) then nerr:=nerr+1;
      if (fsm_type(fsm_parent(sta))<>fsm_type_object) then nerr:=nerr+1;
      for iact:=1 to nact do begin
       act:=fsm_add(sta,fsm_type_action,StrFmt('action_%d',iact));
       if (fsm_type(act)<>fsm_type_action) then nerr:=nerr+1;
       if (fsm_type(fsm_root(act))<>fsm_type_manager) then nerr:=nerr+1;
       if (fsm_type(fsm_parent(act))<>fsm_type_state) then nerr:=nerr+1;
      end;
     end;
    end;
   end;
   //
   key:=StrFmt('state_%d',nsta);
   if not IsSameText(fsm_name(fsm_get_state(obj)),'STATE_1') then nerr:=nerr+1;
   if not IsSameText(fsm_name(fsm_set_state(obj,key)),key) then nerr:=nerr+1;
   if not IsSameText(fsm_name(fsm_set_state(obj,'STATE_1')),'STATE_1') then nerr:=nerr+1;
   if not IsSameText(fsm_name(fsm_set_state(obj,StrFmt('state_%d',nsta))),key) then nerr:=nerr+1;
   if not IsSameText(fsm_name(fsm_set_state(obj,'STATE_1')),'STATE_1') then nerr:=nerr+1;
   //
   //writeln(trim(fsm_ctrl(fsm,'catalog')));
   writeln('last action: ',fsm_path(fsm_ref(act)));
   key:=StrFmt('domain_%d',ndom)+'::'+StrFmt('object_%d',nobj)+'/'+StrFmt('state_%d',nsta)+'/'+StrFmt('action_%d',nact);
   if not IsSameText(fsm_path(act),key) then nerr:=nerr+1;
   ref:=fsm_find(fsm,fsm_type_any,key); if (ref<>act) then nerr:=nerr+1;
   //
   Timing('',key,1000*20);
   for i:=1 to n do ref:=fsm_find(fsm,fsm_type_any,key);
   Timing('fsm_find path',key,n);
   //
   key:=StrFmt('action_%d',nact);
   ref:=fsm_find(sta,fsm_type_any,key); if (ref<>act) then nerr:=nerr+1;
   Timing('',key,1000*100);
   for i:=1 to n do ref:=fsm_find(sta,fsm_type_any,key);
   Timing('fsm_find name',key,n);
   //
   key:=StrFmt('state_%d',nsta);
   ref:=fsm_set_state(obj,sta); if (ref<>sta) then nerr:=nerr+1;
   ref:=fsm_get_state(obj); if (ref<>sta) then nerr:=nerr+1;
   Timing('',key,1000*1000);
   for i:=1 to n do ref:=fsm_get_state(obj);
   Timing('fsm_get_state fast',key,n);
   Timing('',key,1000*500);
   for i:=1 to n do ref:=fsm_set_state(obj,sta);
   Timing('fsm_set_state fast',key,n);
   //
   key:=StrFmt('state_%d',nsta);
   ref:=fsm_find(obj,fsm_type_state,fsm_ctrl(obj,'state='+key)); if (ref<>sta) then nerr:=nerr+1;
   ref:=fsm_find(obj,fsm_type_state,fsm_ctrl(obj,'state')); if (ref<>sta) then nerr:=nerr+1;
   Timing('',key,1000*50);
   for i:=1 to n do ref:=Ord(fsm_ctrl(obj,'state')<>'');
   Timing('fsm_get_state slow',key,n);
   Timing('',key,1000*50);
   for i:=1 to n do ref:=Ord(fsm_ctrl(obj,'state='+key)<>'');
   Timing('fsm_set_state slow',key,n);
   Timing('',key,1000*50);
   for i:=1 to n do ref:=fsm_set_state(obj,key);
   Timing('fsm_set_state strn',key,n);
   //
   key:='tag';
   Timing('',key,1000*100);
   writeln('Link ',fsm_link(obj,'tag=123'):1);
   for i:=1 to n do ref:=fsm_link(obj,key);
   Timing('fsm_link',key,n);
  //
   obj:=fsm_find(fsm,fsm_type_object,'domain_2::object_3');
   sta:=fsm_find(obj,fsm_type_state,'state_4');
   iNul(fsm_modified(sta,1));
   writeln(fsm_path(sta),' ',fsm_modified(sta,-1):1,' ',fsm_modified(obj,-1):1,' ',fsm_modified(fsm,-1):1);
   if fsm_modified(fsm,0)>0 then
   for idom:=0 to fsm_count(fsm,fsm_type_domain)-1 do begin
    dom:=fsm_items(fsm,fsm_type_domain,idom);
    if fsm_modified(dom,0)>0 then
    for iobj:=0 to fsm_count(dom,fsm_type_object)-1 do begin
     obj:=fsm_items(dom,fsm_type_object,iobj);
     if fsm_modified(obj,0)>0 then begin
      writeln('Object '+fsm_path(obj)+' was modified.');
     end;
    end;
   end;
   //
   key:='fsm_modified';
   Timing('',key,1000*1000);
   for i:=1 to n do ref:=fsm_modified(obj,0);
   Timing('fsm_modified',key,n);
   //
   bNul(fsm_free(fsm));
   writeln(nerr:1,' error(s) found in Test3.');
   key:='';
  end;
 begin
  FsmTest1;
  FsmTest2;
  FsmTest3(2,3,10,10,3);
 end;
         

Daq Pascal Api -> db_create db_connection db_recordset db_command db_free db_ref db_root db_type db_parent db_engineid db_active db_state db_close db_open db_ctrl db_bugscount db_bugsclear db_errors db_errorscount db_errorsclear db_execute db_cancel db_update db_cancelupdate db_begintrans db_committrans db_rollbacktrans db_bof db_eof db_movefirst db_movelast db_movenext db_moveprevious db_fieldscount db_fieldsnames db_fieldstypes db_fieldsasint db_fieldsasfloat db_fieldsasstring db_addnew db_delete db_requery db_resync db_supports db_save

Определение:

function db_create(arg:String):Boolean;
function db_connection(eid:Integer; arg:String):Integer;
function db_recordset(dbo:Integer; arg:String):Integer;
function db_command(dbo:Integer; arg:String):Integer;
function db_free(dbo:Integer):Boolean;
function db_ref(dbo:Integer):TDbEntity;
function db_root(dbo:Integer):TDbConnection;
function db_type(dbo:Integer):Integer;
function db_parent(dbo:Integer):Integer;
function db_engineid(dbo:Integer):Integer;
function db_active(dbo:Integer):Boolean;
function db_state(dbo:Integer):Integer;
function db_close(dbo:Integer):Boolean;
function db_open(dbo:Integer; opt:Integer):Boolean;
function db_ctrl(dbo:Integer; arg:String):String;
function db_bugscount(dbo:Integer):Integer;
function db_bugsclear(dbo:Integer):Integer;
function db_errors(dbo:Integer):String;
function db_errorscount(dbo:Integer):Integer;
function db_errorsclear(dbo:Integer):Integer;
function db_execute(dbo:Integer; arg:String; opt:Integer):TDbRecordset;
function db_cancel(dbo:Integer):Boolean;
function db_update(dbr:Integer):Boolean;
function db_cancelupdate(dbr:Integer):Boolean;
function db_begintrans(dbc:Integer):Integer;
function db_committrans(dbc:Integer):Boolean;
function db_rollbacktrans(dbc:Integer):Boolean;
function db_bof(dbr:Integer):Boolean;
function db_eof(dbr:Integer):Boolean;
function db_movefirst(dbr:Integer):Boolean;
function db_movelast(dbr:Integer):Boolean;
function db_movenext(dbr:Integer):Boolean;
function db_moveprevious(dbr:Integer):Boolean;
function db_fieldscount(dbr:Integer):Integer;
function db_fieldsnames(dbr:Integer; i:Integer):String;
function db_fieldstypes(dbr:Integer; id:String):Integer;
function db_fieldsasint(dbr:Integer; id:String; op:Char; arg:Integer):Integer;
function db_fieldsasfloat(dbr:Integer; id:String; op:Char; arg:Double):Double;
function db_fieldsasstring(dbr:Integer; id:String; op:Char; arg:String):String;
function db_addnew(dbr:Integer; arg:String):Boolean;
function db_delete(dbr:Integer; aff:Integer):Boolean;
function db_requery(dbr:Integer; opt:Integer):Boolean;
function db_resync(dbr:Integer; aff,res:Integer):Boolean;
function db_supports(dbr:Integer; opt:Integer):Boolean;
function db_save(dbr:Integer; dst:String; fmt:Integer):Boolean;

Аргументы:

  • eid - идентификатор движка (engine id) программного интерфейса к СУБД.
  • arg - аргумент, задающий данные или параметры (зависит от функции).
  • dbo - ссылка на любой объект доступа к базам данных (database object).
  • dbr - ссылка на объект "набор записей" (database recordset).
  • id - идентификатор (имя) поля (столбца данных в таблице).
  • op - направление операции для чтения ('R') или записи ('W').
  • aff - параметр аффекта (affect), т.е. к чему применять операцию.
  • opt - опции для задания режимов выполнения функций.
  • dst - целевой файл для записи (destination).
  • fmt - параметр описывает формат записи.

Результат:

Функции DbApi (database application program interface) для работы с Базами Данных.
Функции DbApi дополняются библиотечным модулем DbLibrary со средствами прикладной разработки (вспомогательные функции).

Описание:

Набор функций db_xxx предоставляет программный интерфейс, именуемый DbApi, который предназначен для работы с Системами Управления Базами Данных - СУБД.

Программный интерфейс DbApi базируется на следующих основных понятиях:
  • Engine - движок, мотор. Движок - это конкретная реализация программного интерфейса. Доступно несколько движков (сейчас - [1,2,3]=[ADO,SQLDB,ZEOS]), можно использовать любой из них. Рекомендуемый движок - SQLDB. Движки имеют свои особенности реализации, который надо учитывать при выборе движка. В конечном счете, движки - это программные библиотеки от разных производителей или авторов.
  • Provider - провайдер, поставщик, производитель. Провайдер - это программный модуль, с помощью которого работает движок. Обычно провайдеры предоставляются в виде клиентских динамических библиотек от поставщиков (производителей) драйверов СУБД, которые реализуют связку общего программного интерфейса движка с конкретным типом БД. Провайдеров иногда (в некоторых движках) также называют протоколами.
  • Driver - драйвер. Некоторые провайдеры обслуживают несколько типов БД, поэтому для них требуется дополнительно указывать (и устанавливать) драйверы для конкретной БД. Например, провайдер ODBC может подключаться практически к любому типу БД, если имеется драйвер протокола ODBC.

Таким образом, для описания подключения к БД необходимо указать, как минимум, движок, провайдер, и возможно, драйвер конкретного типа БД. Описание остальных параметров (имя сервера, имя БД, имя и пароль пользователя, и т.д.) зависит от движка и провайдера. Каждый провайдер описывает параметры подключения по-своему (хотя обычно и похоже на другие провайдеры). Например, имя пользователя у разных провайдеров может описываться параметрами User, User ID или UID. Поэтому описание подключения может быть довольно сложным.

В настоящее время доступны такие движки:

  1. Движок ADO - библиотека ActiveX Data Objects от Microsoft. Этот движок работает только под Windows. Технология ADO предоставляет единый программный интерфейс к СУБД разных типов. Обычно она уже есть в составе Windows поставляет по крайней мере провайдер с именем MSDASQL, достоинство которого в его постоянной доступности. Но есть и другие провайдеры для конкретных СУБД. Например, для СУБД Interbase/Firebird есть свободно распространяемый провайдер IBProvider, идентифицируемый именем LCPI.IBProvider.5.Free. Его надо устанавливать специально. После установки провайдера ADO позволяет работать с соответствующей БД.

  2. Движок SQLDB - кроссплатформенная библиотека от Lazarus. Библиотека SQLDB является штатным компонентом Lazarus и предоставляет единый программный интерфейс к СУБД для списка популярных типов БД: SQLite, Oracle, MS SQL, Sybase, MySQL, Interbase, Firebird, PostgreSQL, ODBC. Провайдер ODBC позволяет (при наличии драйверов ODBC) подключать любой другой тип БД.

  3. Движок ZEOS - кроссплатформенная библиотека для Lazarus. Библиотека ZEOS является дополнительным (требующим установки) компонентом Lazarus и предоставляет единый программный интерфейс к СУБД для списка популярных типов БД: SQLite, Oracle, MS SQL, Sybase, MySQL, Interbase, Firebird, PostgreSQL, ODBC. Провайдер ODBC позволяет (при наличии драйверов ODBC) подключать любой другой тип БД.

Для идентификации движков используются константы db_engine_ado=1; db_engine_sqldb=2; db_engine_zeos=3; Предпочтительнее использовать эти константы (они встроены в компилятор), чем численные значения.

Некоторые провайдеры (включая ADO/MSDASQL, ZEOS/ODBC и SQLDB/ODBC) поддерживают технологию ODBC. Технология ODBC позволяет работать практически с любым типом БД (при условии установки драйвера ODBC для интересующешго типа БД). Это дает широкие возможности для подключения баз данных разного типа.

То есть это работает примерно так:
         
         -- ПРИЛОЖЕНИЕ------DbApi------ДВИЖКИ-----ПРОВАЙДЕРЫ-----ДРАЙВЕРЫ------ БАЗЫ ДАННЫХ 
          
         --------------                       ___ MSDASQL    --- Driver 1 ------ Interbase/Firebird
         | DAQ-system  |                     |
         | Application | -- DbApi ------ ADO ---- MSDASQL    --- Driver 2 ------ MS SQL Server, и так далее
         |  DaqPascal  |          | |        |
         |_____________|          | |        |___ Provider N --- Driver N ------ MySQL, и так далее
                                  | |        
                                  | |
                                  | |         ___ IB          ------------------- Interbase/Firebird
                                  | |        |
                                  | \-- SQLDB ---- ODBC       -- драйверы ODBC -- много типов БД
                                  |          |
                                  |          |___ SQLite3     ------------------- SQLite
                                  |          |
                                  |           ---- PQ         ------------------- PostgreSQL, и так далее
                                  |
                                  |           ___ Firebird    ------------------- Interbase/Firebird
                                  |          |
                                  \-- ZEOS------- ODBC        -- драйверы ODBC -- много типов БД
                                             |
                                             |___ SQLite      ------------------- SQLite
                                             |
                                             ---- PostgreSQL  ------------------- PostgreSQL, и так далее
         
         


Интерфейс DbApi работает с объектами, представленными в виде целочисленных ссылок.
  • Объект Connection - подключение, соединение - является главным или корневым (root) объектом, который обеспечивает связь с сервером БД. Объекту подключения принадлежат дочерние (child) объекты - команды и наборы записей. Эти дочерние объекты выполняют свои функции через родительское (parent) соединение. Заметим, что некоторые движки (ADO) могут иметь много дочерних объектов на одно подключение, а другие движки (SQLDB,ZEOS) работают с одним дочерним объектом на одно подключение. При этом число объектов - подключений не ограничено.
    Подключения dbc создаются вызовом dbc:=db_connection(eid,arg); с указанием движка eid и строки подключение arg.
  • Объект Command - команда - используется для выполнения SQL запросов. Как правило, это запросы, не возвращающие данные.
    Команды создаются вызовом db_command(dbo,arg) с указанием родительского объекта подключения dbo и строки SQL запроса arg.
  • Объект Recordset - набор записей - используется для доступа к прочитанным из БД данным, которые находятся после чтения в буфере памяти. Чтение данных из БД обычно выполнянется с помощью SQL запросов типа SELECT с указанием интересующих таблиц и полей.
    Наборы записей создаются вызовом db_execute(dbo,arg) с указанием родительского объекта подключения dbo и строки SQL запроса arg. А также вызовом db_recordset(dbo,arg) - для создания наборов записей, доступных для редактирования (вставки, удаления, изменения) данных.
Все функции DbApi привязаны к этим объектам, причем некоторые функции работают с одним типом объектов, а другие полиморфны, т.е. применимы к разным объектам. Независимо от этого все вызовы безопасны, просто функции одного типа ничего не делают с объектами другого (неверного) типа, и возвращают нейтральное значение (обычно 0 или пустую строку).


Вкратце логика работы функций DbApi для чтения базы данных примерно такова.

  1. Создается подключение con:=db_connection(eid,arg).
    Здесь задается номер движка eid и описание параметров подключения arg.
    Номер движка после создания соединения можно узнать вызовом db_engineid(con).

  2. Открывается созданное соединение db_open(con,..).
    Большинство функций работают только с открытым соединением.
    Статус соединения можно узнать вызовом db_active(con) или db_state(con).

  3. Начинается транзакция db_begintrans(con,..).
    Большинство операций чтения/записи доступны только после открытия транзакции.

  4. Выполняется SQL запрос rst:=db_execute(con,'select … from …;') с указанием нужной таблицы.
    Результат возвращается в объекте - наборе записей recordset (rst).
    Большинство операций чтения/записи работают с этим набором записей.

  5. Пока не достигнут конец файла while not db_eof(rst) do …, идет циклический перебор записей.
    Записи соответствуют строкам таблицы. Для перехода к следующей записи вызывается db_movenext(rst).

  6. В цикле выполняется выборка и обработка массива из db_fieldsCount(rst) полей (столбцов таблицы)
    с именами id:=db_fieldsNames(rst,i), используемыми для доступа к этим полям.
    Для доступа к данным используются функции db_fieldsAsInt(rst,id,..), db_fieldsAsFloat(rst,id,..),
    db_fieldsAsString(rst,id,..) в зависимости от типа полей db_fieldsTypes(rst,id).
    Следует заметить, что интерпретация типов db_fieldsTypes зависит от движка.
    Поэтому при анализе данных надо учитывать номер движка db_engineid(con).
    Также могут пригодиться разные параметры из функции контроля db_ctrl(con).

  7. После успешной обработки записей транзакция подтверждается вызовом db_committrans(con,..).
    Если изменения нужно отменить, они "откатываются" вызовом db_rollbacktrans(con,..).

  8. В конце работы с таблицей набор записей закрывается db_close(rst).
    Потом он освобождается вызовом db_free(rst), хотя можно этого не делать.
    Все дочерние объекты (recordset,command) освобождаются вместе с их родительским соединением.

  9. В конце работы соединение закрывается db_close(con).
    При надобности его можно открыть снова.

  10. В конце работы ненужное более подключение освобождается db_free(con).
    Все дочерние объекты (recordset,command) уничтожаются автоматически.


Типичная схема вызова функций DbApi показана на приведенном ниже примере.

Библиотека включает следующие функции:


  • db_create(arg) - создает пустую базу данных, описанную в аргументах arg. Если база успешно создана, возвращается true, иначе возвращается false. Аргументы arg должны содержать описание движка, провайдера и базы данных в виде куки (списка параметров cookie вида name=value; с разделителем ";"). Движок задается параметром Engine, EngineId или EngineName, содержащим номер или имя движка ([1,2,3]=[ADO,SQLDB,ZEOS]), при этом интерпретация провайдера и параметров зависит от движка. Указание движка и провайдера обязательно.
    Например:
        arg:='Engine=ADO;Provider=LCPI.IBProvider.5.Free;User Id=SYSDBA;Password=masterkey;ctype=win1251;location=localhost:c:\demo\test.fdb;';
        if db_create(arg) then writeln('Создана БД c:\demo\test.fbd') else writeln('Ошибка создания БД.');
        
        arg:='Engine=SQLDB;Provider=IB;User=SYSDBA;Password=masterkey;Charset=utf8;Database=c:\demo\test.fdb;';
        if db_create(arg) then writeln('Создана БД c:\demo\test.fbd') else writeln('Ошибка создания БД.');
                  
    Этот пример создает БД в указанном месте. Если файл БД уже есть, функция вернет ошибку.



  • db_connection(eid,arg) - создает объект connection (подключение или соединение), используя движок (eid) и аргумент (arg), и возвращает его ссылку или 0 при ошибке. Любая работа с БД требует обязательного подключения, поэтому вызов db_connection будет присутствовать в любой программе для БД как первый вызов.

    Параметр (eid = EngineId) задает движок (engine). В текущей версии пакета доступны движки db_engine_ado=1, db_engine_sqldb=2, db_engine_zeos=3. Значение (eid=0) задает движок "по умолчанию" (в настоящий момент - это ADO=1). Однако в большинстве случаев рекомендуется указывать движок явно.

    Аргумент (arg) задает строку подключения (ConnectionString) в виде списка параметров формата Name=Value; с разделителем ; с описанием параметров подключения.
    Для движков SQLBD, ZEOS строка подключения также может иметь вид DB URI, т.е. вид имени ресурса (URI) в стиле Web адресов.

    Первым элементом списка параметров ConnectionString обязательно идет Provider=...

    • Для движка ADO под Windows можно использовать встроенный в Windows провайдер MSDASQL. Этот провайдер поставляется в составе Windows, так что он всегда под рукой. Этот провайдер поддерживает драйверы ODBC, который является универсальным интерфейсом к СУБД. Если установить драйвер ODBC для интересующей СУБД, то MSDASQL сможет с ней работать. Так, например, для Firebird есть драйвер ODBC и поэтому провайдер MSDASQL может работать с Firebird (при указании соответствующих параметров ConnectionString). Кроме того, возможна установка других провайдеров ADO от других фирм-производителей.


    • Для движка SQLDB под Windows/Linux доступны встроенные провайдеры MSSQL, Sybase, PQ, Oracle, ODBC, MySQL40, MySQL41, MySQL50, MySQL51, MySQL55, MySQL56, MySQL57, MySQL80, SQLite3, IB (здесь PQ=PostgreSQL, IB=Interbase/Firebird). Провайдер ODBC позволяет подключить любую другую базу данных при наличии драйверов ODBC. Прикладной код при этом не изменится.


    • Для движка ZEOS под Windows/Linux доступны встроенные провайдеры ado, ASA, asa_capi, firebird, interbase, mariadb, mssql, mysql, odbc_a, odbc_w, OleDb, oracle, pooled.*, posgresql, sqlite, sybase, WebServiceProxy Провайдер ODBC позволяет подключить любую другую базу данных при наличии драйверов ODBC. Прикладной код при этом не изменится.


    Обычно в ConnectionString присутствуют, как минимум, такие параметры:

    • Provider - имя провайдера, см. комментарии выше.
    • Driver - имя драйвера (если он нужен - например, для ODBC).
    • DataBase - имя базы данных. Имена параметров (один из перечисленных) и форматы имен баз данных зависят от провайдера. Это могут быть простые имена зарегистрированных БД, имена файлов (с путями).
      Движки SQLDB, ZEOS также распознают имена в виде HOSTNAME:DATABASE. При этом явное указание параметра HOSTNAME более приоритетно, т.е. ему отдается предпочтение, если он явно указан.
    • HostName - имя сервера базы данных.
    • UserName - имя пользователя.
    • Password - пароль пользователя.
      Для задания пароля есть несколько способов.
      • Параметр Password в строке подключения в явном виде.
        Например, UserName=SYSDBA;Password=masterkey;
        Это просто, но совсем не безопасно, т.к. пароль легко прочитать
        в коде прикладной программы и воспользоваться им.
      • Параметр Password в строке подключения в зашифрованном виде.
        Например, UserName=SYSDBA;Password=8KGjli65aKi2;PwdCryptMode=6;
        Это сложнее, но гораздо более безопасно, т.к. зашифрованным паролем
        нельзя просто так воспользоваться.
        Шифрованный пароль можно узнать в диалоге Database Broser или в программе SQLook.
      • Пароль по умолчанию из переменных окружения.
        Это способ более безопасный, чем открытый пароль, но тоже небезупречный,
        т.к. переменные окружения процесса в принципе можно прочитать.
    • PwdCryptMode - (необязательный) режим шифрования пароля.
      Если этот параметр задан значением 1/2/3/4/5/6/7/8, то работает алгоритм шифрования пароля BlowFish/GOST/RC2/RC4/RC5/RC6/B64/HEX. Пароль задается в шифрованом виде, а расшифровка проходит внутри функций DbApi. Передача шифрованного пароля повышает безопасность работы, т.к. пароль нельзя просто так узнать из строки подключения (в тексте программы) и воспользоваться им.
      Шифрованный пароль можно узнать в диалоге Database Broser или в программе SQLook.
    • PwdCryptKey - (необязательный) ключ для шифрования пароля.
      Обычно этот параметр явно не задается, а используется значение по умолчанию.
    • PwdCryptIv - (необязательный) начальный вектор для шифрования пароля.
      Обычно этот параметр явно не задается, а используется значение по умолчанию.
    • Charset - кодировка символов.


    Список остальных параметров зависит от провайдера и требует уточнения в документации конкретного провайдера, а также драйвера ODBC, если он используется. Увы, в этом вопросе трудно избежать изучения фирменной документации конкретной СУБД и "шаманства" с подбором параметров ConnectionString. Для правильного составления ConnectionString даже существует специальный сайт www.connectionstrings.com где собраны правила составления ConnectionString для провайдеров десятков СУБД. Несколько полезных страниц с этого сайта приведены здесь: ado/connectionstrings.
    Пример:
        // Для движка ADO, Firebird и провайдера MSDASQL с драйвером ODBC от IBPhoenix:
        con:=db_connection(db_engine_ado,'Provider=MSDASQL;DRIVER=Firebird/InterBase(r) driver;'
                       +'DBNAME=localhost:employee;UID=SYSDBA;PWD=masterkey;');
    
        // Для движка ADO, Firebird и провайдера IBProvider:
        con:=db_connection(db_engine_ado,'Provider=LCPI.IBProvider.5.Free;ctype=win1251;'
           +'Location=localhost:employee;User ID=SYSDBA;Password=masterkey;');
    
        // Для движка SQLDB, Firebird и провайдера IB:
        con:=db_connection(db_engine_sqldb,'Provider=IB;ctype=utf8;'
           +'Location=/opt/crwdaq/demo/demo_data/employee.fdb;User ID=SYSDBA;Password=masterkey;');
    
        // Для движка SQLDB, Firebird и провайдера IB:
        con:=db_connection(db_engine_sqldb,'Provider=IB;Charset=utf8;'
           +'DBNAME=SYSDBA:masterkey@localhost:/opt/crwdaq/demo/demo_data/employee.fdb;');
                  
    При задании строки подключения следует учитывать, что разные провайдеры могут использовать разные имена для одних и тех же параметров.
    • Для Имени-Базы-Данных используются имена DataBase;Location;Data Source;DBNAME;DSN;DataBaseName.
    • Для Имени-Сервера используются имена HostName;DataSource;Server;Host.
    • Для Имени-Пользователя используются имена UserName;User Name;User_Name;UserId;User ID;User_ID;User;UID.
    • Для Пароля используются имена Password;PWD.
    • Для Файла-Источника-Данных используются имена FILEDSN;FileDataSourceName.
    • Для Набора-Символов используются имена Charset;Codepage;ClientCodepage.
    Движки (особенно SQLDB и ZEOS) стараются распознавать все указанные варианты имен параметров, но могут быть нюансы для разных провайдеров. Поэтому описание строки подключения стоит уточнить на сайте поставщика СУБД.

    Для движков SQLDB, ZEOS строка подключения также может быть задана в виде URI в стиле RFC-3986.
    Назовем такие строки подключения DB URI.
    Строка DB URI имеет вид
    Формат DB URI:
    
        provider://[userspec@][hostspec][/dbname][?paramspec][#comment]
    
    где userspec это:
    
        user[:password]
    
    где hostspec это:
    
        [host][:port]
    
    где paramspec это:
    
        name=value[&...]
    
    Например:
    
        // Для движка SQLDB, Firebird и провайдера IB:
        uri:='IB://SYSDBA:masterkey@localhost/employee.fdb?Charset=utf8&PageSize=4096#Example';
        con:=db_connection(db_engine_sqldb,uri);
                  


    Строка DB URI соответствует (с точностью до названий компонентов) URI по стандарту RFC-3986:
    scheme://authority/path?query#fragment
    В терминах стандартного URI:
    • scheme соответсвует provider,
    • authority соответствует userspec@hostspec,
    • path соответствует dbname,
    • query соответствует paramspec,
    • fragment не используется.

    Исполнительная система автоматически преобразует строки подключения в стиле DB URI в список cookie параметров вида key=value;...
    Дальнейшая обработка параметров происходит обычным образом.


  • db_command(con,arg) - создает объект - команду (command) для подключения (con) с SQL запросом в аргументе (arg). Команды применяются для выполнения SQL запросов и требуют предварительного создания подключения. Обычно команды обслуживают запросы, не требующие чтения данных, а для запросов чтения данных (select) используются наборы записей. Команды лучше создавать после успешного открытия соединения db_open(conn), при этом команда автоматически привязывается к подключению (conn), которое им владеет. Команды, поскольку они принадлежат подключению, не обязательно освобождать (db_free), т.к. все дочерние объекты подключения освобождаются автоматически вместе с ним при его освобождении.

    Особенности движков:
    • Движок ADO: команд (для данного соединения) может быть много, они могут работать в асинхронном режиме.
    • Движок SQLDB и ZEOS: команда только одна на соединение, она работает только в синхронном режиме.


  • db_recordset(con,arg) - создает объект - набор записей (recordset) для подключения (con) и возвращает ссылку (rst) для работы с этим объектом. Аргумент (arg) задает источник данных (Source), т.е. SQL запрос для чтения записей. Наборы записей используются для чтения/записи данных в буфере памяти после выполнения SQL запросов. Наборы записей примерно соответствуют объектам - курсорам (cursor = current set of records) в других API. Явное создание наборов записей необязательно, вместо этого можно использовать вызов db_execute, который тоже создает набор записей с результатами SQL запроса. Особенность состоит в том, что после вызова db_execute созданная recordset доступна только для чтения, а с помощью db_recordset можно создать recordset для записи.

    Особенности движков:
    • Движок ADO - позволяет создавать (для данного подключения) любое число наборов записей. Наборы записей могут работать в синхронном или в асинхронном режиме.
    • Движок SQLDB - позволяет создавать (для данного подключения) только один набор записей. Наборы записей могут работать только в асинхронном режиме. Поскольку вызов db_execute тоже создает набор записей, используется либо вызов db_execute (для чтения), либо db_recordset (для чтения и записи).
    • Движок ZEOS - позволяет создавать (для данного подключения) только один набор записей. Наборы записей могут работать только в асинхронном режиме. Поскольку вызов db_execute тоже создает набор записей, используется либо вызов db_execute (для чтения), либо db_recordset (для чтения и записи).


    После создания набора записей его надо открыть вызовом db_open(rst). После работы с записями их надо закрыть вызовом db_close(rst) и освободить вызовом db_free(rst).


  • db_free(dbo) - освобождает объект DbApi со ссылкой (dbo). Надо иметь в виду, что подключение (connection) является контейнером, содержащим другие объекты. Поэтому команды (command) и наборы записей (recordset) можно не освобождать явно, т.к. они автоматически удаляются вместе с объектом connection. Однако и явное освобождение ненужныых более дочерних объектов допускается и даже рекомендуется, чтобы не накапливать в памяти мусор. Объект connection должен освобождаться, когда он более не нужен, и обязательно - в конце работы.


  • db_ref(dbo) - возвращает ссылку объекта (dbo) или 0, если ссылка объекта недействительна. Это способ проверить корректность ссылки.


  • db_root(dbo), db_parent(dbo)
    - возвращают для любого объекта DbApi корневой объект подключения (root) или родительский объект (parent). Это позволяет вызывать функции корневого или родительского объекта по ссылке на любой их дочерний объект.


  • db_type(dbo) - возвращает тип объекта (dbo) или 0 для некорректной ссылки. Значения типов:
    1. db_type_nil(0),
    2. db_type_connection(1),
    3. db_type_recordset(2),
    4. db_type_command(3).
    Функция db_type позволяет проверять тип объекта перед использованием.


  • db_engineid(dbo) - функция, возвращающая номер (id) движка (engine) для любого объекта (dbo). Если объект не существует (например, нулевая ссылка), возвращается 0.
    Возвращает одну из констант идентификации движков:
    1. Ноль - возвращается для несуществующего или негодного объекта.
    2. db_engine_ado - для работы объекта используется движок ADO.
    3. db_engine_sqldb - для работы объекта используется движок SQLDB.
    4. db_engine_zeos - для работы объекта используется движок ZEOS.
    Функция db_engineid используется, когда надо делать разные действия в зависимости от текущего движка. Номер движка определяет библиотеку, используемую для подключения к базам данных и, соответственно, особенности работы объектов DbApi.


  • db_active(dbo) - полиморфная функция, возвращающая статус активности для любого объекта (dbo).

    • Подключение connection активно, когда есть соединение после успешного вызова db_open(dbо).
    • Команда command активна, когда есть соединение после успешного вызова db_open(dbo).
    • Набор записей recordset активен, когда есть соединение после успешного вызова db_open(dbo),
      а объект recordset был открыт для чтения после успешного вызова db_open(dbr).
      То есть надо открыть соединение (db_open), начать транзакцию (db_begintrans),
      выполнить SQL запрос (db_execute), открыть набор записей (db_open).
      Только после этого recordset будет активен.

    Большинство функций исполнения/чтения/записи работают только с активными объектами DbApi.
    Поэтому важно проверять статус активности объектов.


  • db_state(dbo) - полиморфная функция, возвращающая состояние для любого объекта (dbo).

    Особенности движков:
    • В движке ADO - возвращает одну из констант состояния (соединения, команды или набора записей): adStateClosed(0)(закрыто), adStateOpen(1)(открыто), adStateConnecting(2)(соединяется), adStateExecuting(3)(выполняется), adStateFetching(4)(считывается).
    • В движке SQLDB и ZEOS - для объекта соединения или команды - возвращает состояния 1/0=открыто/закрыто. Для набора записей возвращает константы dsInactive (закрыто), dsBrowse (режим чтения), … dsRefreshFields.
    Состояние определяет логику работы объектов DbApi.


  • db_close(dbo) - полиморфная функция, закрывающая соединение данного объекта, если оно было открыто. Закрытие корневого объекта подключения закрывает все дочерние объекты, но не наоборот.


  • db_open(con,opt) - открывает соединение для работы данного подключения (con).

    Особенности движков:
    • В движке ADO: опция (opt) являются одним из значений:
      adAsyncConnect(16) - открывает подключение асинхронно.
      adConnectUnspecified(-1) - по умолчанию. Подключение открывается в синхронном режиме.
    • В движке SQLDB и ZEOS: опция (opt) не используется. Подключение открывается всегда в синхронном режиме.

    После успешного соединения можно создавать команды и наборы записей, а также вызывать выполнение SQL запросов (db_execute). Перед выполнением запросов надо обязательно открыть транзакцию (db_begintrans) и не забыть закрыть её (db_committrans или db_rollbacktrans) после выполнения работы с данными. Потом надо закрыть соединение (db_close) и освободить (db_free).

    Метод db_open(rst,opt) также используется для работы с наборами записей (recordset), созданными вызовом rst:=db_recordset(con,arg). При открытии recordset используется родительское подключение root (оно болжно быть открыто). Открытый набор записей можно использовать для чтения и записи данных (в движке ADO для записи надо перед открытием ещё задать значения CursorType и LockType).

  • db_ctrl(dbo,arg) - команда управления объектом (dbo).
    При задании (arg) в виде name считывает и возвращает параметр (name).
    При задании (arg) в виде name=value задает новое значение (value) параметра (name).
    Функция позволяет получить доступ к большому числу относительно редко используемых параметров.
    Список параметров получается вызовом db_ctrl(dbo,'*').
    Доступны такие параметры:

    • ConnectionStringInit - исходная строка с описанием подключения к БД, переданная в аргументе db_connection.
      Только для чтения.

    • ConnectionString - строка с описанием подключения к БД, см. db_connection (движок может её изменить).
      Только для чтения.

    • ConnectionTimeout - таймаут для подключения в секундах. По умолчанию 15 секунд.
      Используется только в движке ADO.

    • CommandTimeout - таймаут для команд и SQL запросов в секундах. По умолчанию 30 секунд.
      Используется только в движке ADO.

    • IsolationLevel - уровень изоляции (adXactUnspecified, adXactChaos, adXactBrowse, adXactReadUncommitted, adXactCursorStability, adXactReadCommitted, adXactRepeatableRead, adXactIsolated, adXactSerializable). По умолчанию adXactChaos.
      Используется только в движке ADO.

    • Provider - имя провайдера (задается при вызове db_connection в параметре Provider). Например, MSDASQL или LCPI.IBProvider.5.Free для Firebird.
      Только для чтения.

    • Driver - имя драйвера (задается при вызове db_connection в параметре Driver). Драйверы используются, например, провайдером ODBC и задаются в строке подключения в db_connection. Например, SQLite3 ODBC Driver.
      Только для чтения.

    • DefaultDatabase - расположение базы данных. Обычно задается через ConnectionString.
      Доступно для записи при закрытом соединении.

    • CursorLocation - расположение курсора (adUseNone, adUseServer, adUseClient). По умолчанию adUseServer.
      Используется только в движке ADO.

    • Attributes - атрибуты соединения (0, adXactAbortRetaining, adXactCommitRetaining) По умолчанию 0.
      Используется только в движке ADO.

    • AbsolutePage - номер (начиная c 1) страницы буфера (для recordset).
      Используется только в движке ADO.

    • AbsolutePosition - положение (начиная c 1) страницы (буфера для recordset).
      Используется только в движке ADO.

    • Bookmark - закладка (если провайдер поддерживает закладки).
      Используется только в движке ADO.

    • CacheSize - размер временного буфера (кэша) в единицах числа записей, по умолчанию 1.
      Используется только в движке ADO.

    • CursorType - тип курсора (adOpenUnspecified, adOpenForwardOnly, adOpenKeyset, adOpenDynamic, adOpenStatic). По умолчанию adOpenForwardOnly.
      Используется только в движке ADO.

    • Mode - режим доступа соединения на чтение/запись (adModeUnknown, adModeRead, adModeWrite, adModeReadWrite, adModeShareDenyRead, adModeShareDenyWrite, adModeShareExclusive, adModeShareDenyNone, adModeRecursive). По умолчанию Mode=adModeUnknown для Connection и Mode=adModeRead для Recordset.
      Используется только в движке ADO.

    • Filter - фильтр (строка) для работы с группами записей. По умолчанию не задан (пуст).
      Доступно для чтения и записи.

    • Index - задает имя индекса (используется для поиска Find и Seek). По умолчанию не задан (пуст).
      Доступно для чтения и записи.

    • LockType - задает тип блокировки записей (adLockUnspecified, adLockReadOnly, adLockPessimistic, adLockOptimistic, adLockBatchOptimistic). По умолчанию adLockReadOnly.
      Используется только в движке ADO.

    • PageCount - число страниц recordset. Для эффективности записи загружаются не все сразу, а постранично.
      Используется только в движке ADO.

    • PageSize - размер страницы recordset. По умолчанию 10.
      Используется только в движке ADO.

    • RecordCount - число записей в выборке recordset после выполнения SQL запроса.
      Используется только в движке ADO.

    • Sort - строка с именами полей для сортировки записей recordset. По умолчанию пустая.
      Используется только в движке ADO.

    • Source - источник данных recordset.
      Это SQL запрос (строка с командой) для чтения записей. Обычно задается при вызове db_execute.
      Доступно для записи для объекта - команды.

    • Status - статус текущей записи recordset. Используется для проверки состояния записей при редактировании.
      В движке ADO принимает значения (adRecOK, adRecNew, adRecModified, adRecDeleted, adRecUnmodified, adRecInvalid, adRecMultipleChanges, adRecPendingChanges, adRecCanceled, adRecCantRelease, adRecConcurrencyViolation, adRecIntegrityViolation, adRecMaxChangesExceeded, adRecObjectOpen, adRecOutOfMemory, adRecPermissionDenied, adRecSchemaViolation, adRecDBDeleted).
      Используется только в движке ADO.

    • StayInSync - Флаг, будет ли меняться ссылка на дочерние записи при изменении позиции родительской записи. По умолчанию true.
      Используется только в движке ADO.

    • GetString - возвращает текущую запись recordset в виде строки текста, где поля разделены символом табуляции.
      GetString=n,d,s,z возвращает (не более чем) n записей в виде строк текста с разделителем полей d, разделителем строк s и строкой замещения z для обозначения отсутствующих данных NULL. Для задания символов-разделителей используется кодирование с обратным слешем (backslash).
      Например, GetString=1000,\t,\n,(NULL) возвратит строку из (не более чем) 1000 записей с разделителем полей табуляцией, разделителем строк LF и заместителем (NULL) для пустых полей.
      GetString используется в основном для отладлочных целей.
      Этот параметр надо использовать с осторожностью, т.к. он может генерировать большой объем данных, что может привести к переполнению строк при чтении больших таблиц.

    • CommandType - тип команды (adCmdUnspecified, adCmdText, adCmdTable, adCmdStoredProc, adCmdFile, adCmdTableDirect). По умолчанию adCmdUnknown - тип неизвестен и определется автоматически. Для SQL запросов обычно используется тип adCmdText. Если тип команды известен, желательно его указывать, т.к. автоматическое распознавание типа команды замедляет работу.
      Используется только в движке ADO.

    • CommandText - текст команды, т.е. текст SQL запроса. Задается обычно при вызове db_command или db_execute.
      Доступен для чтения и записи.

    • Properties - возвращает список свойств, специфичных для соединения и провайдера в виде куки, т.е. текста, состоящего из списка строк вида Name=Value, разделенных EOL. При отсутствии параметров возвращает весь список свойств, что может быть дорогостоящей процедурой.
      Properties=p1;p2;p3 - возвращает массив свойств, указанных в списке p1;p2;p3. Список интересующих свойств разделяется символом ; или EOL.
      Properties=p1=v1;p2=v2;p3=v3 - не только возвращает массив свойств, указанных в списке p1;p2;p3, но также (сначала) задает свойствам указанные значения v1;v2;v3. Список интересующих свойств разделяется символом ; или EOL.

      Вызов s:=db_ctrl(dbo,'Properties=p1;p2;p3'); возвращает список, содержащий несколько свойств.
      Для извлечения значений требуемых свойств следует использовать CookieScan.
      Например:
      buff:=db_ctrl(dbo,'Properties=Connection.Database;Connection.UserName;Connection.Password');
      dbs:=CookieScan(buff,'Connection.Database',Ord(';')); writeln('Database: ',dbs);
      uid:=CookieScan(buff,'Connection.UserName',Ord(';')); writeln('UserName: ',uid);
      pwd:=CookieScan(buff,'Connection.Password',Ord(';')); writeln('Password: ',pwd);
                         
      Properties - это своего рода "портал", дающий доступ к большому числу внутренних свойств подключения. Использовать его надо осторожно.
      Набор свойств зависит от движка. Уточнить набор свойств можно в диалоге Database Browser, где есть вкладка Info\Properties.
      Типичный набор свойств для движка SQLDB:
      Connection.EngineId=2
      Connection.EngineName=SQLDB
      Connection.EngineVersion=SQLDB LCL 3.6.0.0 FPC 3.2.2
      Connection.Provider=IB
      Connection.Connected=1
      Connection.Database=c:\opt\crwdaq\demo\demo_data\employee.fdb
      Connection.UserName=SYSDBA
      Connection.Password=masterkey
      Connection.HostName=
      Connection.Catalog=
      Connection.CharSet=utf-8
      Connection.Role=
      Connection.ReadOnly=1
      Connection.Options=[]
      Connection.ConnOptions=[sqSupportParams,sqEscapeRepeat,sqSupportReturning,sqSequences]
      Connection.ConnectionInfo="Firebird","030000","WI-V6.3.10.33601 Firebird 3.0","fbclient.dll",""
      Connection.KeepConnection=0
      Connection.IsSQLBased=0
      Transaction.Active=1
      Query.Active=0
      Query.Options=
      Query.SQLText=select * from employee
      Query.TableName=employee
      Query.UpdateMode=upWhereKeyOnly
      Query.SchemaType=stNoSchema
      Query.RowsAffected=42
      Query.Filter=
      Query.Filtered=0
      Query.IndexName=
      Query.StatementType=stSelect
      DataSource.State=dsInactive
      Connection.SaveNullSubs=(NULL)
      Connection.TableNames=COUNTRY,CUSTOMER,DEPARTMENT,EMPLOYEE,EMPLOYEE_PROJECT,JOB,PHONE_LIST,PROJECT,PROJ_DEPT_BUDGET,SALARY_HISTORY,SALES
      Connection.Params.DIALECT=3
      Connection.Params.PAGESIZE=8192
                         

      Типичный набор свойств для движка ZEOS:
      Connection.EngineId=2
      Connection.EngineName=SQLDB
      Connection.EngineVersion=ZEOS ZEOS 8.0.0-release LCL 3.6.0.0 FPC 3.2.2
      Connection.Provider=interbase
      Connection.Connected=1
      Connection.Database=c:\opt\crwdaq\demo\demo_data\employee.fdb
      Connection.UserName=SYSDBA
      Connection.Password=masterkey
      Connection.HostName=
      Connection.Port=0
      Connection.Catalog=
      Connection.CharSet=utf8
      Connection.LibLocation=
      Connection.ClientVersion=3.0.10
      Connection.ServerVersion=3.0.10
      Connection.KnownCodePages=ASCII,BIG_5,CYRL,DOS437,DOS850,DOS852,DOS857,DOS860,DOS861,DOS863,DOS865,EUCJ_0208,GB_2312,ISO8859_1,KSC_5601,NEXT,NONE,OCTETS,SJIS_0208,UNICODE_FSS,WIN1250,WIN1251,WIN1252,WIN1253,WIN1254,DOS737,DOS775,DOS858,DOS862,DOS864,DOS866,DOS869,ISO8859_2,ISO8859_3,ISO8859_4,ISO8859_5,ISO8859_6,ISO8859_7,ISO8859_8,ISO8859_9,ISO8859_13,WIN1255,WIN1256,WIN1257,WIN1258,KOI8R,KOI8U,UTF8,CP943C,GBK,TIS620,GB18030
      Connection.AutoCommit=1
      Connection.ReadOnly=0
      Connection.Ping=1
      Connection.PingServer=1
      Transaction.Active=0
      Query.Active=0
      Query.SQLText=select * from employee
      Query.TableName=employee
      Query.RowsAffected=0
      Query.Filter=
      Query.Filtered=0
      Query.IndexName=
      Query.StatementType=stSelect
      DataSource.State=dsInactive
      Connection.SaveNullSubs=(NULL)
      Connection.TableNames=COUNTRY,CUSTOMER,DEPARTMENT,EMPLOYEE,EMPLOYEE_PROJECT,JOB,MON$ATTACHMENTS,MON$CALL_STACK,MON$CONTEXT_VARIABLES,MON$DATABASE,MON$IO_STATS,MON$MEMORY_USAGE,MON$RECORD_STATS,MON$STATEMENTS,MON$TABLE_STATS,MON$TRANSACTIONS,PHONE_LIST,PROJECT,PROJ_DEPT_BUDGET,RDB$AUTH_MAPPING,RDB$BACKUP_HISTORY,RDB$CHARACTER_SETS,RDB$CHECK_CONSTRAINTS,RDB$COLLATIONS,RDB$DATABASE,RDB$DB_CREATORS,RDB$DEPENDENCIES,RDB$EXCEPTIONS,RDB$FIELDS,RDB$FIELD_DIMENSIONS,RDB$FILES,RDB$FILTERS,RDB$FORMATS,RDB$FUNCTIONS,RDB$FUNCTION_ARGUMENTS,RDB$GENERATORS,RDB$INDEX_SEGMENTS,RDB$INDICES,RDB$LOG_FILES,RDB$PACKAGES,RDB$PAGES,RDB$PROCEDURES,RDB$PROCEDURE_PARAMETERS,RDB$REF_CONSTRAINTS,RDB$RELATIONS,RDB$RELATION_CONSTRAINTS,RDB$RELATION_FIELDS,RDB$ROLES,RDB$SECURITY_CLASSES,RDB$TRANSACTIONS,RDB$TRIGGERS,RDB$TRIGGER_MESSAGES,RDB$TYPES,RDB$USER_PRIVILEGES,RDB$VIEW_RELATIONS,SALARY_HISTORY,SALES,SEC$DB_CREATORS,SEC$GLOBAL_AUTH_MAPPING,SEC$USERS,SEC$USER_ATTRIBUTES
      Connection.Params.DIALECT=3
      Connection.Params.PAGESIZE=8192
      Connection.Params.codepage=utf8
      Connection.Params.RawStringEncoding=DB_CP
                         
      Большинство свойств понятны по названию. О них можно почитать в документации ADO, SQLDB, ZEOS. Однако некоторые свойства нуждаются в комментариях.
      • Connection.TableNames - содержит список доступных таблиц. Это полезно при анализе данных - например, для проверки наличия интересующей таблицы в базе данных. Это свойство доступно для активных (открытых) подключений.
      • Query.SQLText - содержит текст текущего SQL запроса. Обычно этот текст задается при вызове db_execute, db_command или db_recordset.
      • Query.StatementType - содержит тип SQL выражения из запроса Query.SQLText. Возвращается как строка, принимающая одно из значений stUnknown, stSelect, stInsert, stUpdate, stDelete, stDDL, stGetSegment, stPutSegment, stExecProcedure, stStartTrans, stCommit, stRollback, stSelectForUpd.
      • Query.TableName - содержит имя таблицы, извлеченное из текста запроса Query.SQLText. Обычно значения таблиц заданы для SQL выражений типа stSelect.
      • Connection.Params.XXXX - содержит параметры XXXX, которые движок передает напрямую драйверу провайдера. Другие параметры код движка интерпретирует сам и использует непосредственно, либо транслирует в нужный драйверу провайдера формат. Таким образом провайдеру передаются параметры, которые не были изначально предусмотрены кодом движка. Как правило, это параметры, специфические для данного движка, задающие детали его работы. Например, параметр Connection.Params.DIALECT задает диалект языка SQL, используемый провайдером Interbase/Firebird. Описание подобного рода специфических параметров надо узнавать в документации конкретного провайдера.
      • Connection.SaveNullSubs - задает строку замещения нуля (NULL substitution) при отсутствии данных, т.е. когда поле имеет значение NULL (нулевой указатель). Эта строка замещения используется при сохранении данных db_save().


    • Errors - текст со списком ошибок от провайдера.

    • ErrorsCount - счетчик ошибок от провайдера.

    • ErrorsClear - счетчик ошибок от провайдера со сбросом в ноль.

    • State - состояние объекта, см. db_state.

    • Version - версия движка.

    • ProviderNames - список имен провайдеров, зарегистрированных в системе. Зависит от движка, указанного при создании подключения.

    • RecordsAffected - число записей, затронутых последней операцией с recordset.

    • EditMode - возвращает режим редактирования recordset (adEditNone, adEditInProgress, adEditAdd, adEditDelete).
      Используется только в движке ADO.

    • BugsCount - счетчик сбоев DbApi для данного подключения. Включает как ошибки от провайдера, так и ошибки в работе самого DbApi.

    • BugsClear - счетчик сбоев подключения DbApi со сбросом в ноль.

    • TotalBugsCount - общий счетчик сбоев DbApi для всех подключений.

    • TotalBugsClear - общий счетчик сбоев DbApi со сбросом в ноль.

    • BugReportPrefix - задает способ обработки ошибок (исключений) данного подключения.
      Значение '@!ESoftException: ' - при исключении выводится подробное сообщение в Главной Консоли
      Значение '@!EEchoException: ' - при исключении выводится краткое сообщение в Главной Консоли
      Значение '@!EHideException: ' - вывод в Главной Консоли отменяется
      Значение '' - обычная обработка исключений с выводом окна уведомления и занесением в журнал.
      Обращаем внимание - пробел в значении (в конце) важен и его не надо удалять.

    • BugReportPrefixAll - работает аналогично BugReportPrefix, но распространяется на все ошибки DbApi.

    • TimeStampInit - метка времени (msecnow) вызова db_connection. Используется для определения времени работы подключения.

    • TimeStampOpen - метка времени (msecnow) вызова db_open. Сбрасывается в 0 при вызове db_close.

    • TimeStampUser1 - метка времени (msecnow) для пользовательского события №1. Используется на усмотрение пользователя.

    • TimeStampUser2 - метка времени (msecnow) для пользовательского события №2. Используется на усмотрение пользователя.

    • TimeStampUser3 - метка времени (msecnow) для пользовательского события №3. Используется на усмотрение пользователя.

    • UserState - целочисленная переменная состояния пользователя. Используется на усмотрение пользователя.

    • UserFlags - целочисленная переменная битовых флагов пользователя. Используется на усмотрение пользователя.

    • UserLink - целочисленная переменная для ссылки пользователя. Используется на усмотрение пользователя.

    • UserData - строка данных пользователя. Используется на усмотрение пользователя.

    • Cookies - строка данных пользователя (предположительно вида name1=value1;name2=value2;..). Используется на усмотрение пользователя.

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

    См. также документацию ADO, SQLDB, ZEOS.


  • db_bugscount(dbo) - счетчик сбоев
    db_bugsclear(dbo) - счетчик сбоев со сбросом в ноль
    db_errors(dbo) - текст с описанием ошибок провайдера
    db_errorscount(dbo) - счетчик ошибок провайдера
    db_errorsclear(dbo) - счетчик ошибок провайдера со сбросом в ноль
    - группа функций обработки сбоев (bugs) и ошибок провайдера (errors). Сбои - это все возможные ошибки, включая те, которые прямо не связаны с БД (например, ошибки формата данных при чтении). Ошибки провайдера - это список ошибок, обнаруженных драйвером от провайдера, и явно связанных с БД. Анализ счетчиков сбоев и ошибок позволяет узнать, корректно ли выполнены запросы и прочитаны данные.


  • db_execute(dbo,arg,opt) - полиморфный метод выполнения SQL запроса для объекта (dbo) с аргументом (arg) - SQL командой и опциями (opt). Применяется для подключений (connection) и команд (command) для выполнения SQL запросов.
    Возвращает как результат объект recordset для чтения результатов запроса.

    • В движке ADO опции (opt) являются суммой флагов:
      adCmdText(1) - текстовое определение команды или хранимой процедуры.
      adCmdTable(2) - создать SQL-запрос, который вернёт все строки указанной таблицы.
      adCmdStoredProc(4) - хранимая процедура.
      adCmdUnknown(8) - тип команды неизвестен (по умолчанию).
      adAsyncExecute(16) - асинхронное выполнение команды.
      adAsyncFetch(32) - асинхронное выполнение выборки данных.
      adExecuteNoRecords(128) - не возвращать строки.

    • В движке SQLDB и ZEOS опции игнорируются. Тип команды определяется автоматически.

    Перед выполнением запросов должна быть открыта транзакция (db_begintrans), а по завершении закрыта (db_committrans или db_rollbacktrans).

    После успешного запроса принятые данные (если они есть) находятся в буфере набора записей (recordset), которую возвращает db_execute.
    При асинхронном выполнении надо следить за состоянием db_state.


  • db_cancel(dbo) - отменяет ранее начатое (вызовом db_open,db_execute) асинхронное действие. Позволяет прервать долгое ожидание ответа.


  • db_update(dbo), db_cancelupdate(dbo) - начинает обновление (db_update) записей (recordset) или отменяет ранее начатое асинхронное действие (db_cancelupdate). Позволяет прервать долгое ожидание ответа.


  • db_begintrans(con) - начать транзакцию,
    db_committrans(con) - подтвердить транзакцию,
    db_rollbacktrans(con) - отменить транзакцию
    - функции работы с транзакциями подключения (con).

    Транзакции начинаются (db_begintrans) после открытия соединения (db_open), но до вызова любых SQL запросов (db_execute). Функция db_begintrans возвращает уровень вложенности транзакций либо 0 при ошибке. Подтверждение (db_committrans) приводит к сохранению сделанных изменений в базе данных, а отмена (db_rollbacktrans) - возвращает базу данных к исходному состоянию до начала транзакции. Любая транзакция должна завершаться либо подтверждением, либо отменой.

    В движке ADO транзакции могут быть вложенными, в движке SQLDB, ZEOS - нет.

    Операции чтения-записи могут выполняться только поле успешного начала транзакции.


  • db_bof(rst) - признак начала файла,
    db_eof(rst) - признак конца файла
    - проверка признаков начала файла (bof, begin of file) или конца файла (eof, end of file) для объекта - курсора или "набора записей" (rst). Под файлом здесь понимается массив записей recordset, а не файл на диске. Обычно чтение записей результата SQL запроса выполняется в цикле до конца файла:
                   if db_active(con) then begin
                    rst:=db_execute(con,'select * from DemoTable');
                    if db_active(rst) then
                    while not db_eof(rst) do begin
                     // Print current table row as string
                     writeln(Trim(db_ctrl(rst,'GetString')));
                     bNul(db_movenext(rst));
                    end;
                   end;
                  
    .


  • db_movefirst(rst) - переход к первой записи
    db_movelast(rst) - переход к последней записи
    db_movenext(rst) - переход к следующей записи
    db_moveprevious(rst) - переход к предыдущей записи
    - функции перемещения курсора по записям в наборе записей (rst). Записи, напоминаем, - это строки таблицы, полученные в результате SQL запроса. Наборы записей обычно буферизуются по страницам (Page) для экономии памяти, подгрузка страниц набора записей происходит автоматически при перемещении курсора. Это позволяет держать в памяти не всю выборку, а только часть (страницу), что особенно актуально для больших БД. Обычно функции перемещения курсора используются для организации цикла обработки записей. Текущая запись соответствует строке электронной таблицы, а поля записи - столбцам электронной таблицы. При этом все операции чтения-записи относятся к текущей записи, т.е. к текущей строке таблицы.


  • db_fieldscount(rst) - счетчик полей текущей записи из набора записей rst
    db_fieldsnames(rst,i) - имя поля текущей записи с индексом i (от нуля)
    db_fieldstypes(rst,id) - тип данных поля текущей записи с именем id
    db_fieldsasint(rst,id,op,data) - доступ к записи с именем id как к целому числу
    db_fieldsasfloat(rst,id,op,data) - доступ к записи с именем id как к вещественному числу
    db_fieldsasstring(rst,id,op,data) - доступ к записи с именем id как к строке
    - функции работы с полями записей (то есть столбцами таблицы).

    Вызов db_fieldscount(rst) возвращает число (счетчик) полей текущей записи в наборе записей rst.

    Вызов db_fieldsnames(rst,i) позволяет узнать имена полей по индексу. Доступ к полям осуществляется по имени поля (id), а не по индексу, т.к. индекс, в отличие от имени, является временной характеристикой поля. Поэтому с полями данных лучше работать по их именам.

    Вызов db_fieldstypes(rst,id) возвращает тип данных поля с именем id. Типов известно много, они перечислены в файлах констант:
    • Для движка ADO - в файле _con_StdAdoDb.inc. Это константы adEmpty..adArray.
    • Для движка SQLDB, ZEOS - в файле _con_StdSqlDb.inc. Это константы ftUnknown..ftWideMemo.
    Поскольку типы данных зависят от движка, при анализе данных нужно учитывать номер движка, т.к. он определяет интерпретацию данных.

    Вызовы db_fieldsAsInt(rst,id,op,data), db_fieldsAsFloat(rst,id,op,data), db_fieldsAsString(rst,id,op,data) позволяют работать с полями соответсвующего типа (int,float,string). При этом операция op принимает значение 'R' (read) для чтения или 'W' (write) для записи данных (data). При чтении параметр data используется как значение по умолчанию, если произошла ошибка чтения или преобразования данных.

    Успешность операций чтения/записи можно узнать по счетчику сбоев db_bugscount, который считает все ошибки DbApi. Если счетчик не изменился после операции, значит она прошла успешно. Ошибки могут возникать, например, при попытке записи в набор recordset, открытый только для чтения (readonly). Набор recordset, который возвращает db_execute(con), открыт только для чтения. Курсоры recordset, доступные для записи, можно получить только вызовом db_recordset.


  • db_addnew(rst,arg) - добавление записи в recordset (rst)
    db_delete(rst,aff) - удаление записей recordset (rst)
    - функции добавления и удаления записей в recordset (rst). Аргумент (arg) при добавлении может содержать список полей в виде текста Name=Value. Аргумент (aff) при удалении задает влияние (affect) удаления - adAffectCurrent(1) для удаления текущей записи, или adAffectGroup(2) для удаления группы записей, выбранной по фильтру (Filter). Подробнее см. документацию ADO.


  • db_requery(rst,opt) - повторяет SQL запрос, которым был получен набор записей rst. Опции (opt) задают режим запроса, аналогично db_execute.


  • db_resync(rst,aff,res) - обновляет записи recordset (rst), заново считывая их на сервере СУБД. Параметр влияния (aff) задает, на что влияет обновление - текщую запись, все записи и т.д. Параметр (res) задает режим обновления. Подробнее см. документацию ADO.


  • db_supports(rst,opt) - проверяет, поддерживает ли курсор (rst) опцию, заданную параметром (opt). Например, db_supports(rst,adBookmark) проверяет у провайдера доступность закладок. Подробнее см. документацию ADO.


  • db_save(dbr,dst,fmt) - записывает данные из набора записей (rst) в дисковый файл назначения (dst) в формате (fmt). Данные берутся из текущей таблицы, для которой была выполнена выборка select. Набор записей должен быть открыт (активен), иначе возникнет ошибка. Имя файла надо указывать полное, с путем и расширением. Каталог расположения файла должен существовать.
    • Для движка ADO доступен формат записи XML - надо указать fmt=adPersistXML(1). При указании fmt=adPersistADTG(0) сохранение идет в двоичном формате Microsoft Advanced Data TableGram (ADTG). Подробнее см. документацию ADO.
    • Для движка SQLDB доступен формат записи XML - надо указать fmt=dfXml(1). Также доступен двоичный формат fmt=dfBin(0). Также можно указать fmt=dfAny(3), в этом случае формат записи определяется расширением файла - .csv для формата CSV (текст с кавычками и разделителем полей - запятой) или .txt;.tab для формата TXT (текст без кавычек с разделителем полей - табуляцией), или .xml для формата XML (такой же как dfXml).
      Таким образом, рекомендуется использовать вызов db_save(dbr,dst,dfAny) с указанием файла с "правильным" расширением (.csv, .txt или .xml), по которому определяется формат записи.
    • Для движка ZEOS доступен формат записи XML - надо указать fmt=dfXml(1). Также можно указать fmt=pfAny(3), в этом случае формат записи определяется расширением файла - .csv для формата CSV (текст с кавычками и разделителем полей - запятой) или .txt;.tab для формата TXT (текст без кавычек с разделителем полей - табуляцией), или .xml для формата XML (такой же как dfXml).
      Таким образом, рекомендуется использовать вызов db_save(dbr,dst,dfAny) с указанием файла с "правильным" расширением (.csv, .txt или .xml), по которому определяется формат записи.

Пример:

Следующий пример иллюстрирует применение функций DbApi. Это фрагмент работающей тестовой конфигурации DEMO_TESTBENCH, которая открывает демонстрационную БД Firebird с адресом (localhost:employee), делает SQL запрос и показывает первые 10 элементов таблицы COUNTRY.
 {
 Test 12.
 Database routines.
 }
 procedure Test12;
 var con,rst,ern:Integer;
  function FormatRow(rst:Integer):String;
  var id,res:String; i,tp:Integer;
  begin
   id:=''; res:='';
   for i:=0 to db_fieldscount(rst)-1 do begin
    id:=db_fieldsnames(rst,i); tp:=db_fieldstypes(rst,id);
    res:=res+' '+id+'='+Str(tp)+','+db_fieldsasstring(rst,id,'r','')+';';
   end;
   FormatRow:=res; id:=''; res:='';
  end;
 begin
  ern:=0;
  Success('Test12: Check Database routines.');
  con:=db_connection(db_engine_ado,'Provider=MSDASQL;DRIVER=Firebird/InterBase(r) driver;'
                      +'DBNAME=localhost:employee;UID=SYSDBA;PWD=masterkey;');
  if (con=0) then ern:=ern+1 else begin
   Success('ADO version '+db_ctrl(con,'Version'));
   if db_open(con,adConnectUnspecified) then begin
    Success('Database opened');
    if db_begintrans(con)>0 then begin
     Success('Transaction started, execute...');
     rst:=db_execute(con,'select first 10 COUNTRY.* from COUNTRY',adCmdText);
     if (rst<>0) then begin
      Success('Got data records, read data:');
      if db_active(rst) then
      if db_fieldscount(rst)>0 then
      while not db_eof(rst) do begin
       writeln(FormatRow(rst));
       bNul(db_movenext(rst));
      end;
     end else ern:=ern+1;
     if db_committrans(con) then Success('Transaction done');
    end else ern:=ern+1;
    bNul(db_close(con));
   end else ern:=ern+1;
   bNul(db_free(con));
  end;
  if (ern=0)
  then Success('Success.')
  else Problem(StrFmt('%d error(s) found',ern));
  writeln;
 end;
         

Daq Pascal Api -> shm_init shm_ref shm_free shm_delink shm_iop shm_rop shm_fop shm_sop shm_ctrl shm_ioresult

Определение:

function shm_init(name:string; size,mode:integer):integer
function shm_ref(shm:integer):integer
function shm_free(shm:integer):boolean
function shm_delink(name:string):boolean
function shm_iop(shm:integer; offset:integer; op:char; data:integer):integer
function shm_rop(shm:integer; offset:integer; op:char; data:real):real
function shm_fop(shm:integer; offset:integer; op:char; data:real):real
function shm_sop(shm:integer; offset:integer; op:char; data:string):string
function shm_ctrl(shm:integer; arg:string):string
function shm_ioresult(code:integer):integer

Аргументы:

  • name - имя для идентификации блока общей памяти.
  • size - требуемый размер блока общей памяти.
  • mode - режим доступа (для Unix).
  • shm - ссылка на объект общей памяти.
  • offset - смещение адреса операции от начала блока.
  • op - код операции (r - чтение, w - запись и т.д.).
  • data - данные для операции чтения/записи из/в общую память.
  • arg - имена/значения параметров для управления (ctrl = control).
  • code - код ошибки.

Результат:

Группа функций для работы с Общей Памятью (shm = shared memory), служащая способом быстрого IPC (взаимодействия между процессами).

Описание:

Функции shm_xxx служат для чтения/записи данных из/в общую память (shm = shared memory) для организации быстрого взаимодействия между процессами - IPC = Inter Process Communication. Ограничением shm является то, что взаимодействовать через общую память могут только процессы, выполянемые на одном компьютере.

function shm_init(name:string; size,mode:integer):integer
Функция создает или подключается к уже созданному блоку общей памяти с именем name размером size и режимом доступа mode и возвращает ссылку (reference) на объект для последующей работы с ним. Параметр name имеет системную область видимости, т.е. являетя общим для всех процессов. Параметр mode зарезервирован для прав доступа в Unix системах и под Windows не используется - рекомендуется использовать значение 0, что значит " режим по умолчанию". Следует учитывать, что память выделяется блоками размера shm_ctrl(0,'SysPageSize') (обычно это 4096 байт), однако функции shm_xxx ограничивают доступ запрошенным размером size байт.
В случае ошибки результатом будет ноль.

function shm_ref(shm:integer):integer
Функция проверяет корректноссть ссылки объекта общей памяти (shm) и возвращает либо ссылку, либо 0, если ссылка некорректна.

function shm_free(shm:integer):boolean
Функция освобождает (удаляет) ранее созданный объект общей памяти (shm). Не забывайте освобождать общую память, чтобы избежать утечки ресурсов.

function shm_delink(name:string):boolean
Функция (под Unix) удаляет файл общей памяти, оставшийся "сиротой", если его забыли освободить (например, процесс упал). Под Windows функция не используется.

function shm_iop(shm:integer; offset:integer; op:char; data:integer):integer
Функция целочисленной операции (integer operation) с общей памятью (shm) с 32-битным значением (LongInt).
Параметр shm задает ссылку объекта общей памяти.
Параметр offset задает смещение (байтов) от начала блока.
Параметр op задает символ операции, которую надо сделать (регистр не учитывается):
  • R - чтение данных - возвращается значение, прочитанное из общей памяти.
  • W - запись данных data в общую память с возвратом значения до записи.
  • + - атомарно прибавляет data к значению в памяти и возвращает результат.
  • - - атомарно вычитает data из значения в памяти и возвращает результат.
  • E - атомарно заменяет (exchange) значение памяти на data и возвращает предыдущее значение.

Параметр data задает операнд, который надо записать/прибавить/отнять в зависимости от операции.
Операция чтения/записи будет неуспешной, если считываемые/записываемые данные "вылезают" за границы области памяти.
Например, если смещение отрицательно или превышает размер блока памяти.
Статус операции можно узнать вызовом shm_ioresult.

function shm_rop(shm:integer; offset:integer; op:char; data:real):real
Функция вещественной операции (real operation) с общей памятью (shm) с 64-битным значением (Double).
Параметр shm задает ссылку объекта общей памяти.
Параметр offset задает смещение (байтов) от начала блока.
Параметр op задает символ операции, которую надо сделать (регистр не учитывается):
  • R - чтение данных - возвращается значение, прочитанное из общей памяти.
  • W - запись данных data в общую память с возвратом значения до записи.

Параметр data задает операнд, который надо записать (при чтении не используется).
Операция чтения/записи будет неуспешной, если считываемые/записываемые данные "вылезают" за границы области памяти.
Например, если смещение отрицательно или превышает размер блока памяти.
Статус операции можно узнать вызовом shm_ioresult.

function shm_fop(shm:integer; offset:integer; op:char; data:real):real
Функция вещественной операции (float operation) с общей памятью (shm) с 32-битным значением (Single).
Параметр shm задает ссылку объекта общей памяти.
Параметр offset задает смещение (байтов) от начала блока.
Параметр op задает символ операции, которую надо сделать (регистр не учитывается):
  • R - чтение данных - возвращается значение, прочитанное из общей памяти.
  • W - запись данных data в общую память с возвратом значения до записи.

Параметр data задает операнд, который надо записать (при чтении не используется).
Операция чтения/записи будет неуспешной, если считываемые/записываемые данные "вылезают" за границы области памяти.
Например, если смещение отрицательно или превышает размер блока памяти.
Статус операции можно узнать вызовом shm_ioresult.

function shm_sop(shm:integer; offset:integer; op:char; data:string):string
Функция строковой операции (string operation) с общей памятью (shm).
Параметр shm задает ссылку объекта общей памяти.
Параметр offset задает смещение (байтов) от начала блока.
Параметр op задает символ операции, которую надо сделать (регистр не учитывается):
  • R - чтение данных - возвращается значение, прочитанное из общей памяти.
  • W - запись данных data в общую память с возвратом значения до записи.

Параметр data задает операнд, который надо записать (при чтении задает размер).
Работа с shm_sop имеет ряд особенностей. Во-первых, строка считывается или записывается как бинарный массив, без какого-либо анализа содержимого (например, допускаются символы нуля т.е. конца строки в C/C++). Интерпретация строки - полностью ваша забота. Например, можно удалить лишние нули/ пробелы вызовом Trim. Во-вторых, при чтении данных длина строки, которую надо прочитать, берется равной длине строки data, при этом содержимое data не используется (может быть любым).
Операция чтения/записи будет неуспешной, если считываемые/записываемые данные "вылезают" за границы области памяти.
Например, если смещение отрицательно или превышает размер блока памяти.
Статус операции можно узнать вызовом shm_ioresult.

function shm_ctrl(shm:integer; arg:string):string
Функция управляет обьектом общей памяти (shm) путем чтения/записи его параметров.
Аргумент (arg) имеет вид Name или Name=Value для чтения/записи.
Имена параметров могут быть:
  • Name - имя блока общей памяти (чтение).
  • Size - размер блока памяти в байтах (чтение).
  • Mode - режим доступа (чтения, Unix).
  • Capacity - размер памяти с учетом страниц.
  • SysPageSize - размер страницы памяти в данной системе (чтение), обычно 4096.
  • ioresult - статус последней операции (чтение со сбросом и запись).
  • shm_total_errors - общий счетчик ошибок операций с общей памятью.
  • * - список имен всех параметров.
  • *= - список имен всех параметров, доступных для записи.
  • ? - список всех (чтение и запись) параметров в виде Name=Value.
  • ? - список всех записываемых параметров в виде Name=Value.


function shm_ioresult(code:integer):integer
Функция возвращаает статус последней операции и присваивает статусу код code. Обычно используется нулевой код code=0 для сброса статуса. Нулевой статус означает успешную операцию, другие значения - ошибку.
  1. shm_err_ok - успешно.
  2. shm_err_op - неверный символ операции op.
  3. shm_err_ref - некорректная ссылка на объект.
  4. shm_err_arg - неверные аргументы - например, недопустимое смещение.
  5. shm_err_bug - исключение - например, ошибка доступа к памяти.


Важное примечание по применению
Для совместного доступа к общей памяти нужна та или иная дисциплина (арбитраж), чтобы избежать коллизий (collision) одновременного чтения и записи данных, при котором данные могут быть повреждены. Для арбитража можно использовать мютексы или семафоры, но это усложняет задачу и создает свои проблемы (связаанные с блокировкой потоков). В данном API для арбитража предлагается использовать:
1) атомарные операции shm_iop(shm,offset,'+',1) для счетчиков
2) "спекулятивное" чтение данных с проверкой атомарных счетчиков

При этом методе используется неблокирующий механизм синхронизации на основе атомарных счетчиков и "спекулятивного" чтения данных. Этот метод состоит в том, что процесс-писатель инкрементирует атомарный счетчик (в общей памяти) ДО и ПОСЛЕ записи данных. Процесс-читатель считывает атомарный счетчик до и после чтения блока данных. Если счетчик нечетный (писатель пишет) или за время чтения счетчик изменился (писатель успел записать свой блок), то чтение данных считается "грязным" (dirty) и отбрасывается. Чтение повторяется позже, пока не станет "чистым". Такой метод синхронизации не требует дополнительных объектов (мютексов, семафоров) и не блокирует потоки, однако требует высокой дисциплины от прикладного программиста.
Например:
Писатель:
 shm_iop(shm,COUNTER,'+',1);        // инкремент счетчика ДО записи
 shm_rop(shm,ANYDATA,'W',data);     // запись данных
 shm_iop(shm,COUNTER,'+',1);        // инкремент счетчика ПОСЛЕ записи

Читатель:
 c1:=shm_iop(shm,COUNTER,'+',0);    // атомарное чтение счетчика ДО чтения данных
 data:=shm_rop(shm,ANYDATA,'R',0);  // чтение данных
 c2:=shm_iop(shm,COUNTER,'+',0);    // атомарное чтение счетчика ПОСЛЕ
 dirty:=(c1<>c2) or odd(c1);        // флаг "грязных" данных (коллизия)
 if dirty                           // если данные грязные
 then writeln('попробуйте позже')   // ингорируем их
 else Handle(data);                 // хорошие данные обрабатываем
         
Достоинством функций shm_xxx является то, что они доступны и работают одинаково в DaqPascal,Delphi,FreePascal на платформах Windows/Unix. Это облегчает создание и взаимодействие серверов и клиентов, написанных на разных языках.

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

Пример:

DEMO_SHM - демо конфигурация
demo_shm_main_ctrl.pas - сервер на DaqPascal
demo_shm_client.dpr - сервер на ObjectPascal


Описание стандартной библиотеки функций StdLibrary:



Целью библиотеки является:
1) Унификация структуры прикладных программ - для снижения вероятности ошибок,
2) Отделение постоянно используемого системного кода от часто меняющегося прикладного кода,
3) Сокращение объема прикладного кода - для улучшения читабельности и облегчения разработки,
4) Предоставление прикладным программистам большого числа готовых программных библиотек,
5) Закрепления достижений прикладного программирования за последние 10 лет в сжатом виде.

Использовать стандартную библиотеку надо c шаблоном прикладной программы template.pas.


Модуль StdLibrary

является супермодулем, обеспечивающим поддержку шаблона прикладной программы template.pas на верхнем уровне, то есть он включает в себя все остальные модули, заменяя их удобным и кратким интерфейсом. В состав StdLibrary входят следующие модули: StdErrors, StdStrings, StdConsole, StdDevices, StdCurves, StdSounds, StdVector, StdTags, StdTimes, StdTexts, StdTools, StdTasks, StdPipes, StdEdits, StdDims, StdWebs, StdDLLs, StdUtils, StdDiesel, StdAddons.

Самый простой способ правильно использовать библиотеку StdLibrary - просто следовать шаблону template.pas и следующим рекомендациям.

  1. Вид каждой прикладной программы, если только нет каких-то специальных требований или ограничений, должен структурно соответствовать шаблону template.pas. Тело основной программы жестко задано (путем включения файла {$ _std_main}). Это нужно для того, чтобы вид основной программы был всегда гарантированно согласован с версией библиотеки. Это, конечно, накладывает на программиста ограничения, но не такие уж сильные. Зато будет меньше случайных ошибок.
    Если вид основной программы (Main) жестко задан, куда помещать свой код прикладному программисту? Задачей прикладного программиста становится заполнение своим кодом пяти шаблонных процедур с жестко фиксированными именами: ClearApplication, InitApplication, FreeApplication, PollApplication, StdIn_Processor. Эти процедуры обязаны присутсвовать, даже если они будут пустыми. Скопировать их можно из шаблона.

  2. Весь пользовательский код помещается в пяти процедурах с фиксированными именами:

    1. ClearApplication - очистка всех строк, определенных в прикладной программе. Выполняется на этапе стартовой инициализации (Starting). Ничего лишнего эта процедура делать не должна - все, кроме очистки строк, делает InitApplication. Системные строки тоже очищать не надо, об этом заботится ClearStdLibrary.

    2. InitApplication - инициализация всех пользовательских переменных, тегов, кривых, устройств прикладной программы и т.д. Выполняется на этапе стартовой инициализации (Starting). Системная библиотека инициализируется в теле основной программы шаблона template.pas, поэтому позаботиться надо только об инициализации тех ресурсов, которые должна выделять сама прикладная программа. При возникновении в процессе инициализации критических ошибок надо делать вызов Trouble или инкрементировать Errors. При этом генерируется ошибка, инициализация считается неудачной, а цикл опроса программы Polling блокируется. Для вывода сообщений о некритических ошибках вместо Trouble надо вызывать Problem.

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

    4. PollApplication - процедура обработки данных в цикле опроса программы (Polling). Здесь, собственно, происходит основная обработка данных, кроме обработки консольных команд. Раньше эта обработка помещалась в основном теле программы. Унификация программ требует отказа от этой практики - тело основной программы должно быть шаблонным. А вот процедуру опроса каждый пишет как ему надо. Единственная рекомендация тут - избегать длительных циклов ожидания, вместо них надо использовать флаги и переменные состояния, чтобы продолжать выполнение ожидаемых операций на следующем цикле опроса. Такова общая философия программирования в DaqPascal.

    5. StdIn_Processor - процедура обработки данных (в основном команд) из консоли StdIn. Эта процедура должна брать команды с помощью GotCommand (это обязательно), обрабатывать полученные команды, проверяя их с помощью IsSameText(cmd,'@CommandName'), а в конце вызвать StdIn_DefaultHandler для обработки стандартных команд. Команды разделяются символами разделителя строк (_CR,_LF), начинаются с символа "собаки" _AtSign (@) и идентифицируются первым словом, например, @Help, за которым идет знак равенства (=) или пробел ( ), после которого идут необязательные аргументы, смысл которых определяется именем команды. Программы могут посылать друг другу сообщения DevSend, которые попадают в консоль ввода StdIn. Следует помнить, что вызов DevSend(cmd1+EOL+cmd2+EOL+..+cmdN+EOL) гарантированно передает пачку команд cmd1,cmd2,cmdN как единый атомарный (неделимый) блок. Если, например, надо одновременно указать несколько параметров, которые задаются разными командами, либо выполнить последовательность команд в фиксированном порядке, то их надо послать одним вызовом DevSend. При этом, хотя команды будут выполняться порознь, гарантируется их одновременный приход и строгий порядок их поступления. Если же было несколько вызовов DevSend, то между ними может "вклиниться" посылка из другого потока, программа ведь работает параллельно с многими другими. Все это надо учитывать при создании прикладных программ на основе сообщений.

  3. При использовании других (пользовательских) включаемых модулей и супермодулей их процедуры Clear, Init, Free, Poll инкапсулируются (включаются) в перечисленные одноименные процедуры прикладной программы по принципу "матрёшки" - один в другой. Это гарантирует корректную работу программы при большом числе включаемых модулей. При этом каждый модуль может заботиться только о своих внутренних ресурсах, не затрагивая другие модули. По этому принципу строится вся библиотека.

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

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

  6. Замечание по текущей реализаии - старайтесь избегать сложных конструкций при формировании строк, включающих цепочку вызовов вложенных функций. Вместо этого лучше вычисляейте и подставляйте при вызове готовое число, Например,
          Вместо:                                                используйте:
          WEB_Reply('<b>'+GetDateTime(GetMidnight(ms))+'</b>);   mid:=GetMidnight(ms);
                                                                 WEB_Reply('<b>'+GetDateTime(mid)+'</b>);
         
    Замечено, что такие сложные вызовы создают риск утечки памяти (строк). Это одна из проблем, которые предстоит решить в будущем.

Теперь описание супермодуля StdLibrary.

_con_StdLibrary.inc - файл описания констант супермодуля StdLibrary.

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

_typ_StdLibrary.inc - файл описания типов супермодуля StdLibrary.
Его включение в программу (пока) необязательно, но рекомендуется (на будущее). В этом файле определены некоторые стандартные типы, часто используемые в прикладном ПО. В настоящее время в нем определен тип TTagRef, как наиболее часто используемый в прикладных программах.

Тип TTagRef - запись, содержащая ссылку на тег (tag), а также ряд ассоциированных (связанных) с тегом полей: номера кривых аналоговых/цифровых входов/выходов (nai,nao,ndi,ndo), целочисленные ссылки (ref, crv, tid, pid, cid, key, lnk,obj) на ассоциированные (связанные с тегом) объекты, а также пользовательские вещественные данные (dat,val,tim), которые могут использоваться для обработки или обновления тегов. Тип TTagRef часто используется в прикладных программах потому, что теги обычно существуют не сами по себе, а связаны с другими объектами (входами/выходами, кривыми, окнами и т.д.). В стандартную библиотеку внесен наиболее общий (полный) набор ассоциированных с тегом данных из встречавшихся на практике (это удобно). Минусом такого множества полей может быть повышенное использование памяти. При необходимости можно увеличить сегмент данных (Compiler.dtabmax) через опции компилятора.

В файле стандартных типов также определен тип TStatSum2D - запись, содержащая 2D статистические суммы, см. модуль StdStatSum.

_fun_StdStatSum.inc - файл функций для статистических расчетов (включается опционально, при необходимости). В модуле определены функции TStatSum2D_Reset, TStatSum2D_Init, TStatSum2D_Add, TStatSum2D_Calculate. См. описание _man_StdStatSum.htm.

В файле стандартных типов также определен тип TVector - тип для хранения динамических массивов. В настоящее время это строка String, но это может измениться в будущем. Лучше расматривать TVector как "непрозрачный" абстрактный тип.

_var_StdLibrary.inc - файл описания переменных супермодуля StdLibrary.

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

_fun_StdLibrary.inc - файл описания функций супермодуля StdLibrary.

Супермодуль инкапсулирует все функции модулей библиотеки. А также содержит собственные процедуры.

procedure ClearStdLibrary;
procedure InitStdLibrary;
procedure FreeStdLibrary;
procedure PollStdLibrary;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса супермодуля в рамках шаблона прикладной программы template.pas.

procedure StdIn_DefaultHandler(var Data,cmd,arg:String);
- обработчик консольных команд "по умолчанию". Вызывается в конце процедуры StdIn_Processor, после обработки всех пользовательских команд. Обеспечивает обработку некоторого набора стандартных консольных команд. В настоящее время в него входят следующие команды:

    @Help                       Показать справку. Справка прикладной программы находится в [@Help].

    @DebugFlags n               Установить режим отладочного вывода n=1+2+4+8, где флаги разрешают:
                                 1  = Success(информационные сообщения);
                                 2  = Trouble(выводится сообщение и генерируется ошибка), Problem(только сообщение);
                                 4  = ViewExp(сообщения о выводе=экспорте, например, в канал или порт);
                                 8  = ViewImp(сообщения о вводе=импорте, например, из канала или порта).
                                 16 = Details(сообщения отладочного характера)

    @Eval e                     Вычислить выражение e в интерпретаторе данного устройства, например, @Eval 2+2
                                Для вычисления используется экземпляр калькулятора текущей программы DaqPascal,
                                то есть доступны все переменные и функции, которые доступны для функции Eval().
                                Например, @Eval @echo Привет выведет Привет в системную консоль.
                                Это очень мощная функция, но пользоваться ей надо весьма осторожно.

    @SysEval e                  Вычислить выражение e в Главной Консоли. Эквивалентно @Eval @System @Async e.
                                Помещает выражение e в буфер ввода Главной Консоли для последующего асинхронного исполнения.
                                Команда @SilentEval влияет на то, будет ли выражение вычисляться "молча" (@silent)
                                или будет выводиться эхо. По умолчанию режим эхо включен.
                                Например: @SysEval @run cmd    (запускает cmd.exe)

    @SilentEval n               Задать режим 0/1=эхо/тихий режим для команды @SysEval.
                                В эхо   режиме @SysEval e выполняется как @Eval @System @Async e.
                                В тихом режиме @SysEval e выполняется как @Eval @System @Async @Silent e.
                                По умолчанию используется эхо режим.

    @Async cmd                  Выполнить команду cmd асинхронно, т.е. поместить в буфер ввода консоли данного устройства.
                                Команда будет выполнена позже, после обработки всех команд, уже находящихся в буфере.

    @Speak s                    Произносит фразу s с помощью речевого синтезатора через сервер &SpeakSrv.
                                Фактически посылает сообщение серверу &SpeakSrv. Например: @Speak Hello world.

    @Voice s                    Издает звук s. Эквивалент вызова Voice(s). Например: @Voice Click

    @DevMsg  dev msg            Посылает сообщение msg устройству dev. Например: @DevMsg  &CronSrv @Speak Hello
    @DevSend dev msg            Посылает сообщение msg устройству dev. Например: @DevSend &CronSrv @Speak Hello

    @WinHide win                Спрятать окно с именем win.
    @WinShow win                Показать окно с именем win.
    @WinDraw win|opt            Перерисовать окно win с (необязательными) опциями opt.
    @WinSelect win              Показать окно с именем win и выделить его, поместив вперед и дав фокус ввода.

    @LoadIni                    Загружает параметры из INI файла. см. описание функции custominirw.
    @SaveIni                    Сохраняет параметры в  INI файл.  см. описание функции custominirw.
                                Если команде передаются аргументы, они передаются функции custominirw.
                                Например: @SaveIni ..\Config\Custom.ini [CustomParameters] [CustomParameters.TagList] ..\Data\Custom

    @CpuProfiler                Показать Help по команде @CpuProfiler для вывода ститистики о производительности.
    @CpuProfiler Start p v m s  Стартовать профайлер с параметрами p v m s, по умолчанию 1 60 7 0
                                 p - период опроса профайлера, секунд,  по умолчанию 1
                                 v - период вывода отчетов, секунд,     по умолчанию 60
                                     после вывода отчета счетчики зануляются, стартует новый цикл набора
                                     нулевой период подавляет вывод и соответсвенно рестарт профайлера
                                 m - режим вывода отчетов профайлера,   по умолчанию 7=1+2+4
                                     1=заголовок, 2=статистика загрузки, 4=счетчики
                                     нулевой режим подавляет вывод отчетов и рестарт профайлера
                                 s - тестовый режим стресс-нагрузки, %, по умолчанию 0 %
                                     создает искусственную нагрузку потока программы s %
                                     используется только для стресс-тестирования программы
                                     при различных загрузках CPU
    @CpuProfiler View           Досрочный вывод отчета, не дожидаясь очередного периода отчетов.
                                Можно задать нулевой период отчетов, подавив его периодический вывод
                                с рестартом, а статистику смотреть командой View.
    @CpuProfiler Stop           Останавливает профайлер, чтобы он не отнимал время процессора.
                                По умолчанию профайлер остановлен, его можно стартовать только
                                консольной командой.

    @Cron cmd                   Посылает команду cmd в консоль сервера &CronSrv для последующего исполнения.
                                Например:
                                  @cron @run -hide dns.exe             Запуск процесса через Cron
                                  @cron @winshow DAQ-SYSTEM            Показать окно с именем DAQ-SYSTEM

    @StartupScript              Выполнение сценария StartupScript. Сценарий задается в секции,
                                на которую указывает переменная StartupScript в описании устройства.
                                Например
                                  [&Device]
                                  StartupScript = [&Device.StartupScript]
                                  ...
                                  [&DemoDevice.StartupScript]
                                  @Command1 arg1
                                  @Command2 arg2
                                  ...

    @FinallyScript              Выполнение сценария FinallyScript. Сценарий задается в секции,
                                на которую указывает переменная FinallyScript в описании устройства.
                                Например
                                  [&Device]
                                  FinallyScript = [&Device.FinallyScript]
                                  ...
                                  [&DemoDevice.FinallyScript]
                                  @Command1 arg1
                                  @Command2 arg2
                                  ...

    @ParamStr arg               Вызывает ParamStr(arg) и печатает результат в консоль устройства. Полезно для справки.

    @Remote arg                 Команда @Remote предназначена для создания распределенных систем на основе DIM сервера.
                                Она выполняет команду (arg) удаленно, через вызов Dim_GuiConsoleSend(DevName,arg).
                                Команда передается удаленному серверу через сеть DIM и извлекается на стороне сервера
                                вызовом Dim_GuiConsoleRecv(DevName,''), примерно как показано в примере:

                                 procedure GUI_POLL;
                                 var s:String;
                                 begin
                                  s:='';
                                  DIM_GuiClickBuff:='';
                                  if ClickWhat<>0 then
                                  repeat
                                   // Copy GUI click to DIM buffer.
                                   DIM_GuiClickBuff:=DIM_GuiClickCopy;
                                   // Handle MouseDown/KeyDown
                                   if (ClickWhat=cw_MouseDown) or (ClickWhat=cw_KeyDown) then begin
                                    // Handle Left mouse button click
                                    if (ClickButton=VK_LBUTTON) then begin
                                     // Handle local clicks
                                     if ClickIsLocal then begin
                                      ......
                                     end;
                                     //
                                     // Handle remote clicks comes from DIM via @DimGuiClick message.
                                     // @DimGuiClick default handler decode and write events to FIFO,
                                     // so we can find it as clicks and can handle it in usual way.
                                     //
                                     if ClickIsRemote then begin
                                      //
                                      // Handle remote console commands...
                                      //
                                      s:=Dim_GuiConsoleRecv(DevName,'');
                                      if LooksLikeCommand(s) then DevSendCmdLocal(s);
                                      ......
                                     end;
                                    end;
                                   end;
                                  until (ClickRead=0);
                                  ......
                                  s:='';
                                 end;

    @Local arg                  Работает аналогично @Async arg, т.е. выполняет команду (arg) асинхронно.
                                Эта команда сделана как парная к команде @Remote.

    @Tooltip arg                Инициирует всплывающее сообщение (tooltip), заданное аргументами (arg).
                                Например:
                                @tooltip text "Желаю вам успеха" preset stdNotify delay 15000

    @OpenConsole dev            Открывает окно консоли устройства с заданным именем (dev).
                                Если имя пустое, открывается консоль текущего устройства.
                                Например:
                                @OpenConsole
                                @OpenConsole &CronSrv

    @BrowseHelp f               Открывает HTM справку, заданную файлом с именем (f) и с расширением по умолчанию (.htm).
                                Путь файла может быть задан относительно основного конфигурационного файла.
                                Если файл не задан, его имя берется из параметра конфигурации [DAQ] HelpFile.
                                Например:
                                @BrowseHelp
                                @BrowseHelp ..\Help\index.htm

    @DimServiceUp s             Обработчик сообщения об установлении связи DIM сервера с именем сервиса s.
                                Действием по умолчанию является печать сообщения в консоль.

    @DimServiceDie s            Обработчик сообщения о разрыве связи DIM сервера с именем сервиса s.
                                Действием по умолчанию является печать сообщения в консоль.
                                
    @Reports t msg              Процедура "докладывает", т.е. выводит сообщение (msg) типа (t).
             t - тип сообщения: Trouble,Problem,Success,Succeed,ViewImp,ViewExp,Details,Disturb,Alerter,CritErr,Fatally
             msg - сообщение:   Текст, который передается в сообщении при вызове процедуры, на которую указывает тип.
   
Следует отметить, что для каждой стандартной команды @XXXX в библиотеке есть переменная cmd_Std_XXXX, которая инициализируется в начале с помощью RegisterStdInCmd и содержит код команды.


Модуль StdErrors

обеспечивает обработку ошибок, поддержку шаблона прикладной программы template.pas и выполняет другие системные функции.

_con_StdErrors.inc - файл описания констант модуля StdErrors.

Константы acc_Deny (доступ закрыт), acc_Guest (гостевой доступ), acc_User (пользовательский доступ), acc_Root (полный доступ), acc_List (список уровней доступа), acc_DenyList (список всех уровней доступа), acc_webList (список уровней доступа Web) - служат для идентификации уровня доступа и проверки прав доступа, используется в серверах DimSrv, WebSrv.

Константы MinInt (минимальное целое), MinReal (минимальное положительное вещественное), MaxReal (максимальное вещественное) - определяют системно-зависимые величины - диапазоны основных типов.

Константа VitalErrCode содержит код критических ошибок. Это ошибка исполнения программы DaqPascal, возникающая из-за программных исключений - деления на ноль, ошибки индексации массивов, переполнения стека и т.д.

Константа HangsErrCode содержит код ошибок сторожевого таймера (WATACHDOG DEADLINE). Это ошибка исполнения программы DaqPascal, возникающая из-за недопустимо длительных задержек - замкнутых циклов, длительного ожидания событий и т.д.

_var_StdErrors.inc - файл описания переменных модуля StdErrors.

Переменные Ok (статус стартовой инициализации), Errors (счетчик ошибок стартовой инициализации), ErrorCode (кодовый номер ошибки данного устройства) - используются для обработки ошибок. Счетчик Errors, в начале равный нулю, инкрементируется при выполнении стартовой секции, если возникли существенные ошибки (Trouble). Если по завершении стартовой инициализации (Starting) счетчик равен нулю, функция проверки (CheckStdErrors) устанавливает переменную Ok в True, разрешая опрос программы (Polling). Если на этапе инициализации возникла ошибка, опрос программы блокируется.

Переменные FixmSecNow (начальное время mSecNow), FixMaxAvail (начальное значение таблицы свободных строк), FixStackAvail (начальное значение стека свободной памяти) - фиксируют начальные значения системных счетчиков, чтобы при старте и завершении программы проверить, достаточно ли ресурсов и правильно ли они используются. Сравнение счетчиков в начале (Starting) и конце (Stopping) работы позволяет найти "утечки" ресурсов и сгенерировать ошибку, если найдены проблемы.

Переменная FatalMaxAvail (фатальное значение MaxAvail) задает критическое значение счетчика свободных строк (MaxAvail), при достижении которого прикладная программа генерирует ошибку и останавливается с выводом сообщения в главную консоль. Исчерпание таблицы строк - один из видов критических сбоев, после которых программу надо останавливать. При недостатке свободного места в таблице строк исполнение программы становится проблематичным. См. подробнее PostMortalWill.
При загрузке конфигурации переменная FatalMaxAvail считывается (до первого успеха) из секций: [&DeviceName], [DAQ], Crw32.ini [DaqSys]. Это позволяет задавать значения как индивидуально каждому устройству, так и всем устройствам сразу. Значение по умолчанию (в файле Crw32.ini [DaqSys]) равно 128.

Переменные FixVitalBugs (начальное значение счетчика критических ошибок), FatalVitalBugs (фатальное значение счетчика критических ошибок) используются для остановки программы при возникновении критических ошибок, типа программных исключений из-за деления на ноль, ошибок индексации массивов и т.д. Критические ошибки (дословно vital bugs, жизненно важные сбои) возникают при невозможности дальнейшего исполнения программы Daq Pascal по причине серьезной ошибки. При этом исполнение программы прерывается до следующего кванта времени (RunCount), фиксируется ошибка номер VitalErrCode. При накоплении критического числа таких ошибок дальнейшее исполнение программы прекращается, с выводом диагностики и исполнением "завещания". См. подробнее PostMortalWill.
При загрузке конфигурации переменные FatalVitalBugs, FatalHangsBugs считываются (до первого успеха) из секций: [&DeviceName], [DAQ], Crw32.ini [DaqSys]. Это позволяет задавать значения как индивидуально каждому устройству, так и всем устройствам сразу. Значения по умолчанию (в файле Crw32.ini [DaqSys]) равны 1, 1.

Переменные FixHangsBugs (начальное значение счетчика ошибок Watchdog Deadline), FatalHangsBugs (фатальное значение счетчика ошибок Watchdog Deadline) используются для остановки программы при подвисании, то есть возникновении длительных периодов времени, когда программа не подает признаков жизни. Под признаками жизни понимается либо завершение очередного цикла исполнения программы, либо сброс стророжевого таймера (wdt_reset). Типичная ситуация, когда это может возникнуть - вход в бесконечный цикл из-за программной ошибки. Либо слишком долгое ожидание каких-то событий. Система снабжена сторожевым таймером Watchdog, который выполняется в отдельном высокоприоритетном потоке Daq.Watchdog и проверяет, когда программы в последний раз сбросили Watchdog. Сброс Watchdog происходит автоматически, при завершении исполнения программы на очередном цикле, а также программно, при вызове wdt_reset. Если со времени сброса прошло более, чем WatchdogDeadline миллисекунд (переменная задается в секции описания устройства и по умолчанию равна 60000, то есть минуте), то фиксируется WATCHDOG DEADLINE и исполнение программы принудительно прерывается до следующего кванта времени (RunCount), фиксируется ошибка номер HangsErrCode. При накоплении критического числа таких ошибок дальнейшее исполнение программы прекращается, с выводом диагностики и исполнением "завещания". См. подробнее PostMortalWill.

Переменная PostMortalWill (дословно - посмерная воля или завещание) задает действия, выполняемые при остановке программы в случае критических сбоев, см. FatalMaxAvail, FatalVitalBugs и FatalHangsBugs. В этой длинной строке содержится текст "завещания", разделенный на строки с помощью EOL, в которых задаются посмертные сообщения (DevMsg) и команды Eval в формате

   &DeviceName arguments        - задает сообщение DevMsg(...)
   @CommandName argiments       - задает команду Eval(...)

   Например:

       procedure InitApplication;
       begin
        FatalMaxAvail:=128;  // Минимальный размер таблицы строк.   Это значение по умолчанию.
        FatalVitalBugs:=128; // Предельное число критических сбоев. Это значение по умолчению.
        PostMortalWill:='@system @async @run -hide msg * "I am dead."'+EOL
                       +'@system @async @compile device '+DevName+EOL
                       +'&CronSrv @Warning I am dead.';
       end;
       procedure PollApplication;
       begin
        writeln(1 div 0); // Фатальная ошибка - целочисленное деление на ноль!
       end;

   В приведенном примере в процедуре опроса Poll есть фатальная ошибка - целочисленное деление на ноль.
   После 128 попыток выполнить программу будет зафиксировано предельное число критических ошибок
   времени исполнения (vital bugs - дословно жизненно важные сбои). При этом программа будет остановлена,
   веведена диагностика, а также будет исполнено её "завещание" - в данном случае будет выполнена команда
       Eval('@system @async @run -hide msg * "I am dead."') - запуск команды msg для вывода сообщения
       Eval('@system @async @compile device '+DevName)      - перезапуск программы путем перекомпиляции
       DevMsg('&CronSrv @Warning I am dead.')               - посылка сообщения серверу &CronSrv
   В зависимости от задачи в "завещании" можно указать, например, перезапуск самой сбойной программы
   или всей DAQ системы, включение блокировок или отключение аппаратуры и т.д.
   

При загрузке конфигурации переменная PostMortalWill считывается (последовательно, до певого успеха):

  1. Из секции, на которую указывает переменная PostMortalWill в секции описания устройства.
  2. Из секции, на которую указывает переменная PostMortalWill в секции [DAQ].
  3. Из секции [DAQ.Devices.Default.PostMortalWill] текущего конфигурационного файла.
  4. Из секции [DAQ.Devices.Default.PostMortalWill] инициализационного файла CRW32.INI.
При этом делается замена строки %DeviceName% или $DeviceName$ на имя текущего устройства. А также замена строк %CRW_DAQ_SYS_SESSION_NB%, %CRW_DAQ_SYS_EXE_PID%, %CRW_DAQ_SYS_TITLE% на идентификатор сеанса, идентификатор процесса и заголовок главного окна сеанса crwdaq. А также замена строк %date%, %time% на дату и время.

Типичное описание PostMortalWill имеет вид:

    [DefaultPostMortalWill]
    @system @async @run -hide unix tooltip-notifier head "Attension:" text "%time%: CRW-DAQ device $DeviceName$ critical error." preset stdError delay 60000
    @system @async @compile device $DeviceName$
    []
   
Разумеется, конкретная программа может также задавать значение PostMortalWill в InitApplication.

Переменные StartingDevMsg, StoppingDevMsg задают сообщения, которые посылаются в консоль в блоке Starting (при старте программы) и в блоке Stopping (при завершении программы). По умолчанию обе переменные пустые, то есть сообщения не посылаются. Задание значений StartingDevMsg:='@StartupScript';, StoppingDevMsg:='@FinallyScript'; позволяет вызывать стартовый и завершающий сценарий, например:

    procedure InitApplication;
    begin
     StartingDevMsg:='@StartupScript';  Задать стартовое   сообщение в консоль
     StoppingDevMsg:='@FinallyScript';  Задать завершающее сообщение в консоль
     StdIn_ToStopping:=MaxInt;          Разрешить обработку завершающих консольных команд
    end;
    ...
    В результате (при условии что в StdIn_Processor вызывается StdIn_DefaultHandler)
    будет выполнен StartupScript на старте и FinallyScript при завершении.
   

_fun_StdErrors.inc - файл описания функций модуля StdErrors.

function Starting:Boolean;
function Stopping:Boolean;
function Polling:Boolean;
function CheckStdErrors:Boolean;
procedure PostStartingDevMsg;
procedure PostStoppingDevMsg;
- обеспечивают поддержку шаблона прикладной программы template.pas.
Starting указывает на то, что программа выполняет этап начальной инициализации при старте. Если в процессе инициализации не было фатальных ошибок (Trouble), то функция CheckStdErrors устанавливает флаг Ok для разрешения опроса программы Polling.
Stopping указывает на то, что программа выполняет этап финального освобождения ресурсов при остановке системы.
PostStartingDevMsg посылает в консоль сообщение из переменной StartingDevMsg.
PostStoppingDevMsg посылает в консоль сообщение из переменной StoppingDevMsg.
Подробнее см. _std_main.inc

function CompareGuards(g1,g2:String):Integer;
- сравнивает уровни доступа g1 (реальный) и g2 (эталонный), возвращается результат -1 (отказать), 0 (уровень достаточен), +1 (уровень выше эталона). Например,

if CompareGuards(g,'user')<0 then AccessDenied else WelcomeToUserLevel;
Уровень доступа может быть guest,user,root, а также deny при отказе в доступе.

procedure cNul(c:Char);
procedure rNul(r:Real);
procedure sNul(s:String);
procedure bNul(b:Boolean);
procedure iNul(i:Integer);
- эти функции реализуют "устройство Nul" для основных типов DaqPascal. Их удобство заключается в том, что с ними можно не заводить временные переменные для результатов, которые часто и не нужны. Сравните два текста:

    procedure Long;          procedure Short;
    var b:Boolean;           begin
    begin                     bNul(Echo('Привет'));
     b:=Echo('Привет');      end;
    end;
   
Вторая запись очевидно короче, и не надо заводить временные переменные для результатов, которые все равно не используются. Поскольку при использовании результатов обычно вызывается if () then ..., то и в этом случае временные переменные не нужны. Присвоение будет идти только тогда, когда результат в самом деле нужен. Отрицательным моментом тут является некоторое снижение производительности (затраты на вызов пустой процедуры). Поэтому процедуры лучше использовать там, где производительность не играет критической роли.

function IsRefTag(ref:Integer):Boolean;
function IsRefCurve(ref:Integer):Boolean;
function IsRefDevice(ref:Integer):Boolean;
function IsRefWindow(ref:Integer):Boolean;
function IsRefTimer(ref:Integer):Boolean;
function IsRefText(ref:Integer):Boolean;
function IsRefTask(ref:Integer):Boolean;
function IsRefPipe(ref:Integer):Boolean;
function IsRefTcp(ref:Integer):Boolean;
function IsRefCom(ref:Integer):Boolean;
- эти функции проверяют ссылку "ref" для основных типов объектов DaqPascal, см. RefInfo(..). Их рекомендуется вызывать перед использованием ссылок для содержательной работы. Например:

    if IsRefTag(dev) then Success(TagName(tag)) else Trouble('Invalid tag reference');
    if IsRefDevice(dev) then i:=DevSend(dev,msg) else Trouble('Invalid device reference');
   

procedure ClearStdErrors;
procedure InitStdErrors;
procedure FreeStdErrors;
procedure PollStdErrors;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdStrings

обеспечивает обработку символов и строк и носит общесистемный характер.

_con_StdStrings.inc - файл описания констант модуля StdStrings.

Константы _NUL, _SOH, _STX, _ETX, _EOT, _ENQ, _ACK, _BEL, _BS, _HT, _LF, _VT, _FF, _CR, _SO, _SI, _DLE, _DC1, _DC2, _DC3, _DC4, _NAK, _SYN, _ETB, _CAN, _EM, _SUB, _ESC, _FS, _GS, _RS, _US, _SP, _DEL - определяют коды управляющих (непечатных) символов стандартной таблицы ASCII и Unicode. Для преобразования кодов в символы и обратно используйте chr и ord. Управляющие коды очень часто используются для реализации протоколов связи. Поэтому желательно использовать их стандартные названия, а не цифры.

Константы _Space, _Exclamation, _DoubleQuote, _NumberSign, _DollarSign, _PercentSign, _Ampersand, _SingleQuote, _LeftParent, _RightParent, _Asterisk, _PlusSign, _Comma, _MinusSign, _Dot, _Slash, _Colon, _SemiColon, _LessSign, _EqualsSign, _GreaterSign, _Question, _AtSign, _LeftBracket, _BackSlash, _RightBracket, _Caret, _Underscore, _GraveAccent, _LeftCurly, _PipeSign, _RightCurly, _Tilde, _CurrencySign, _TripleDot, _Dagger, _DoubleDagger, _eurosign, _PerMilleSign, _Bullet, _Trademark, _SectionSign, _Copyright, _NotSign, _DegreeSign, _PlusMinus, _MicroSign, _PilcrowSign, _NumeroSign - определяют специальные печатные символы (пунктуации, математические и другие) таблицы ASCII и Unicode. Эти константы полезно использовать в программах, так как обилие кавычек делает программу трудно читаемой. Например, понять запись

s:=s+_SingleQuote+_DoubleQuote;    все-таки легче чем     s:=''''+'"'
А это далеко не самый запутанный случай, с которыми приходится иметь дело. А такие символы, как знак микро или троеточие с клавиатуры так просто и не введешь. Поэтому использование мнемонических констант для специальных знаков может сильно помочь и сделать программу более понятной.

Константы cw_Nothing, cw_MouseDown, cw_MouseUp, cw_MouseMove, cw_KeyDown, cw_KeyUp cw_MouseWheel, cw_MouseWheelDown, cw_MouseWheelUp, - определяют коды возврата функции ClickWhat и других функций, возвращающих тип пользовательских событий (от мыши, клавиатуры).

Константа cw_NameList - содержит список имен возможных значений ClickWhat в виде строки. Имя можно получить в виде Name:=ExtractWord(1+ClickWhat,cw_NameList); What:=WordIndex(Name,cw_NameList)-1;

Константы VK_LBUTTON, VK_RBUTTON, VK_CANCEL, VK_MBUTTON, VK_BACK, VK_TAB, VK_CLEAR, VK_RETURN, VK_SHIFT, VK_CONTROL, VK_MENU, VK_PAUSE, VK_CAPITAL, VK_ESCAPE, VK_SPACE, VK_PRIOR, VK_NEXT, VK_END, VK_HOME, VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN, VK_SELECT, VK_PRINT, VK_EXECUTE, VK_SNAPSHOT, VK_INSERT, VK_DELETE, VK_HELP, VK_F1, VK_F2, VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10, VK_F11, VK_F12, VK_NUMLOCK, VK_SCROLL, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, VK_RCONTROL, VK_LMENU, VK_RMENU - определяют виртуальные коды клавиатуры, они же коды возврата функции ClickButton. При нажатии регистровых клавиш (Ctrl,Alt,Shift,NumLock,CapsLock) эти клавиши модифицируются по известным правилам, см. str2shortcut.

Константы risModeUpper, risModeLower, risModeTrimL, risModeTrimR, risModeRemComm, risModeDefault, risModeDefCase, risAlterSecRef, risAlterEnvRef, risModeAlter - определяет режимы readinisection и readinialter.

_var_StdStrings.inc - файл описания переменных модуля StdStrings.

Переменные ProgramSourcePas (имя файла *.PAS программы), ProgramSourceDir (каталог *.PAS программы) - содержат полное имя и каталог файла с исходным кодом текущей исполняющейся программы DAQ Pascal. Хотя эти имена можно всегда прочитать из переменной ReadIni('ProgramSource'), полезно иметь их в готовом виде.

Переменная WHEEL_DELTA содержит шаг приращения колесика мыши и используется при обработке события MouseWheel. После старта переменная получает стандартное для Windows значение, равное 120.

_fun_StdStrings.inc - файл описания процедур и функций модуля StdStrings. Это строковые функции общего назначения.

function IsEmptyStr(s:String):Boolean;
проверяет, является ли строка пустой. Строка считается пустой, если она не содержит символы, отличные от пробелов.

function IsNonEmptyStr(s:String):Boolean;
проверяет, является ли строка непустой. Строка считается непустой, если она содержит символы, отличные от пробелов.

function TailStr(S:String; Pos:Integer):String;
возвращает часть строки S ("хвост"), начиная с позиции Pos до конца строки.

function RightStr(S:String; Count:Integer):String;
возвращает правую часть строки S длиной (не более чем) Count последних символов.

function LeftStr(S:String; Count:Integer):String;
возвращает левую часть строки S длиной (не более чем) Count первых символов.

function StrAheadOf(S:String; Delim:Char):String;
возвращает часть строки S перед (первым найденным) символом Delim, либо пустую строку, если нет такого символа в строке.

function StrAfterOf(S:String; Delim:Char):String;
возвращает часть строки S после (первого найденного) символа Delim, либо исходную строку, если нет такого символа в строке.

function ContainsStr(AText,ASubText:String):Boolean;
возвращает флаг, если в строку AText входит подстрока ASubText. Сравнение строк идет с учетом регистра.

function StartsStr(ASubText,AText:String):Boolean;
возвращает флаг, если строка AText начинается с подстроки ASubText. Сравнение строк идет с учетом регистра.

function EndsStr(ASubText,AText:String):Boolean;
возвращает флаг, если строка AText заканчивается подстрокой ASubText. Сравнение строк идет с учетом регистра.

function ContainsText(AText,ASubText:String):Boolean;
возвращает флаг, если в строку AText входит подстрока ASubText. Сравнение строк идет без учета регистра.

function StartsText(aSubText,aText:String):Boolean;
возвращает флаг, если строка AText начинается с подстроки ASubText. Сравнение строк идет без учета регистра.

function EndsText(aSubText,aText:String):Boolean;
возвращает флаг, если строка AText заканчивается подстрокой ASubText. Сравнение строк идет без учета регистра.

function TrimDef(s,def:String):String;
возвращает строку Trim(s) с удалением незначащих пробелов, если строка (s) является НЕпустой.
Возвращает значение по умолчанию Trim(def), если строка (s) является пустой.
Т.е. возвращает непустую строку (s), либо значение по умолчанию (def).

function LastPos(sub,str:String):Integer;
находит позицию последнего вхождения подстроки (sub) в строку (str).

function CountPos(sub,str:String):Integer;
находит число (счетчик) вхождений подстроки (sub) в строку (str).

function NthPos(sub,str:String; n:Integer):Integer;
находит позицию n-го вхождения подстроки (sub) в строку (str). Заметим, что функция рекомендуется для поиска конкретного вхождения подстроки, а не для организации циклов. Для цикла итераций по подстрокам лучше использовать posex, т.к. это будет быстрее.

function IsSectionName(str:String):Boolean;
проверяет строку (str), является ли она именем секции типа [SectionName]. Признаком секции является начало [ и завершение ] строки. Незначащие пробелы впереди и сзади не допускаются.

function IsEnvVarLink(str:String):Boolean;
проверяет строку (str), является ли она ссылкой на переменную окружения типа %VariableName%. Признаком ссылки является начало % и завершение % строки. Незначащие пробелы впереди и сзади не допускаются.

function DupeString(s:String; n:Integer):String;
- функция копирует строку s и повторяет её n раз, т.е. возвращает строку, состоящую из n копий строки s. Возвращает пустую строку, если (n<1) или (s=''). Заметим, что для повторения строки из одного символа лучше использовать функцию StringOfChar(c,n), т.к. она работает быстрее.

function CharStr(Leng:Integer; Ch:Char):String;
function LeftPad(S:String; Leng:Integer; Ch:Char):String;
function RightPad(S:String; Leng:Integer; Ch:Char):String;
function CenterPad(S:String; Leng:Integer; Ch:Char):String;
- функции заполнения строки заданным символом Ch до длины Leng. CharStr просто заполняет строку символом Ch до заданной длины. LeftPad заполняет строку S, добавляя символ слева. RightPad заполняет строку, добавляя символ справа. CenterPad центрирует строку, добавляя символ справа и слева.

function iValDef(Data:String; aDefault:Integer):Integer;
function rValDef(Data:String; aDefault:Real):Real;
function iEvalDef(Data:String; aDefault:Integer):Integer;
function rEvalDef(Data:String; aDefault:Real):Real;
- функции преобразования строки Data в число (integer или real) с присвоением значения по умолчанию aDefault в случае пустой строки или строки, не содержащей числового значения. iValDef,rValDef используют строгое преобразование (val,rval), при этом строка должна явно содержать число. iEvalDef,rEvalDef использует интерпретатор (eval), при этом строка может содержать сложное выражение. Функции xEvalDef мощнее, но и потенциально опаснее, т.к. интерпретатор может запустить нежелательные функции. Если приоритетом является безопасность ввода, используйте xValDef. Если приоритетом является функциональность и гибкость, используйте xEvalDef.
Эти функции удобны для анализа параметров консольных команд, когда требуется интерпретировать аргументы команды, либо присваивая им значения по умолчанию, либо сохраняя старое значение в случае пустой или нечисловой строки:

   Пример:
    i:=iValDef(arg,3);    Преобразовать arg в Integer, присвоить i=3, если arg - не число.
    r:=rValDef(arg,r);    Преобразовать arg в Real, сохранять значение r в случае неудачи.
    i:=iEvalDef(arg,i);   Вычислить выражение arg, сохранять значение i в случае неудачи.
    r:=rEvalDef(arg,0);   Вычислить выражение arg, присвоить r=0 если arg - недопустимое выражение.
   
function WordIndex(s,list:String):Integer;
function WordCountDelims(Data,Delims:String):Integer;
function ExtractWordDelims(Num:Integer;Data,Delims:String):String;
function SkipWordsDelims(n:Integer;s,Delims:String):String;
- функции работы со словами. Слова - это цепочки символов, разделенные символами - сепараторами, в число которых по умолчанию входят пробел (_SP), табуляция (_HT), возврат каретки (_CR),(_LF), запятая (_Comma), точка с запятой (_Semicolon), знак равно (_EqualsSign). Этот набор сепараторов можно изменить с помощью WordDelims. WordIndex проверяет, есть ли слово s в списке слов list. Если слово в списке есть, возвращает его номер (начиная с 1), а если нет - возвращает ноль. SkipWords пропускает n слов в начале строки s и возвращает остаток строки, после n-го слова и сепаратора (используется текущий набор сепараторов). Это удобно для выделения аргументов команд. WordCountDelims работает так же, как WordCount, но при этом можно явно задавать набор сепараторов Delims для разделения слов. ExtractWordDelims работает так же, как ExtractWord, но при этом можно явно задавать набор сепараторов Delims для разделения слов. SkipWordsDelims работает так же, как SkipWords, но при этом можно явно задавать набор сепараторов Delims для разделения слов.
   Пример:
    if WordIndex(name,'tom,bob,john')>0    // Проверить, есть ли name в списке имен.
    then writeln(name,' is in user list'); // Возвращает 0 или индекс name в списке.
    s:=SkipWords(1,'@echo Привет мир!');   // Возвращает s="Привет мир!", после @echo и пробела.
    s:=ExtractWordDelims(2,'a\b/c','\/');  // Возвратит s="b", используя \/ как сепараторы слов.
   

function StrReplace(s,a,b:String; Flags:Integer):String;
- заменяет в строке s подстроку a на строку b. Маска Flags задает режим замены: 1 (бит 0) включает замену всех вхождений a на b (а не только первого), 2 (бит 1) включает режим нечувствительности к регистру, при котором большие и малые буквы считаются идентичными. Строго это работает для английских букв, а для национальных символов используется кодировка windows-1251.

function StrAddQuotes(s:String; Quote:Char):String;
- добавляет в начало и конец строки s "кавычки", заданные символом Quote. Если кавычки уже есть, строка не меняется.

function StrRemoveQuotes(s:String; Quote:Char):String;
- убирает из начала и конца строки s "кавычки", заданные символом Quote. Если кавычек нет, строка не меняется.

function StrAddBrackets(s:String; LBracket,RBracket:Char):String;
- добавляет в начало и конец строки s [скобки], заданные символами LBracket,RBracket. Если скобки уже есть, строка не меняется.

function StrRemoveBrackets(s:String; LBracket,RBracket:Char):String;
- убирает из начала и конца строки s [скобки], заданные символами LBracket,RBracket. Если скобок нет, строка не меняется.

function ExtractNameValuePair(arg:String; var Name,Value:String; Sign:Char; Mode:Integer):Integer;
- функция выделяет из строки аргумента arg вида Name Sign Value имя и значение Name,Value, разделенные символом Sign, и возвращает позицию символа Sign в строке или ноль (если его нет). Биты (0,1) режима Mode (то есть флаги 1,2) задают режим обрезания (Trim) лишних пробелов Name,Value соответсвенно. Обычно функция ExtractNameValuePair используется в разборе выражений типа Name=Value.
Например:

    s:=' os = unix ';
    p:=ExtractNameValuePair(s,n,v,'=',3);  // p=5, n='os',    v='unix'
    s:='Error: not found';
    p:=ExtractNameValuePair(s,n,v,':',3);  // p=6, n='Error', v='not found'
   

function ReadIniVar(VarName:String;Mode:Integer):String;
function ReadIniStr(Mode:Integer; FName,SecName,VarName:String):String;
- функции чтения конфигурационных файлов. Отличаются от ReadIni тем, что есть возможность задавать режим чтения конфигурации - например, отключать подавление пробелов и строк комментария, чтобы было можно помещать в секцию произвольный текст, см. ReadIniSection. ReadIniVar читает из секции устройства переменную (VarName = Value), в заданном режиме чтения секции. При этом можно извлекать строковые переменные, не подвергая их искажению (удаление пробелов, перевод в верхний регистр). ReadIniStr читает переменную с полным указанием всех координат (файл, секция, имя переменной, режим чтения), что позволяет считывать данные из произвольных файлов, а не только из файлов текущей конфигурации.

function ReadIniAlter(arg:String;Mode:Integer):String;
- функция чтения конфигурационных файлов с альтернативой (возможностью выбора способа чтения), что значительно расширяет возможности readini. Параметр arg означает то же самое, что в функции ReadIni, т.е. задает файл, секцию и имя переменной.
Режим Mode задает флаги чтения секции. Часть флагов - такие же как в функции ReadIniSection.
Флаг risAlterSecRef управляет чтением по секционной ссылке (section reference), т.е. если в качестве значения параметра указано [alterSection] alterName, то чтение происходит из указанной альтернативной секции и переменной.
Флаг risAlterEnvRef управляет чтением по ссылке на окружение (environment reference), т.е. если в качестве значения параметра указано %alterName%, то чтение происходит из указанной альтернативной переменной окружения.
Флаг risModeAlter является суммой флагов risAlterSecRef и risAlterEnvRef.
Если чтение из указанных альтернативных источников дало пустую строку, в качестве результата принимается остаток строки.
Общий формат такой:

   s:=ReadIniAlter('[&Sect] Param',risModeDefault+risModeAlter);

   При чтении файла CfgFile

   [&Sect]
   Param = [alterSect] alterName %alterVar% FallbackValue

   сначала читается секция  [alterSect] alterName
   потом, если не удалось:  %alterVar%
   потом, если не удалось:  FallbackValue

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

Например:
   s:=ReadIniAlter('DnsNode',risModeDefault+risModeAlter);

   При чтении файла

   [&DeviceName]
   DnsNode = [&DimSrv] DIM_DNS_NODE %DIM_DNS_NODE% localhost

   сначала читается секция  [&DimSrv] DIM_DNS_NODE
   потом, если не удалось:  %DIM_DNS_NODE%
   потом, если не удалось:  localhost

   Таким образом, будет прочитано одно одно из значений альтернативных источников.
   
При использовании функции ReadIniAlter следует учесть, что она выполняется существенно медленнее, чем readini. Не следует злоупотреблять её необоснованным использованием.

function Win2Koi(s:String):String;
function Koi2Win(s:String):String;
- функции преобразования из кодировки windows-1251 (которую любим мы) в кодировку KOI (которую любит GnuPlot). Это просто короткая версия StrConv, удобная для программ, использующих &PlotSrv.

procedure Cryptographer(var Data,Key:String; Encrypt:Boolean; CryptKind:Integer);
- функция шифрования. Она применяется для защиты данных DIM, WEB. Шифрует (Encrypt=True) или дешифрует (Encrypt=False) строку Data с открытым ключом Key методом CryptKind (0=CURRENT, 1=Blowfish, 2=Gost, 3=RC2, 4=RC4, 5=RC5, 6=RC6, 7=BASE64, 8=HEX, 9=NONE). Метод 0 использует текущий метод шифрования, заданный crypt_ctrl. Методы 1..6 задают один из алгоритмов "сильного" шифрования (для скорости рекомендуется использовать метод 4). Методы 7 и 8 используют простое кодирование, без шифрования (на вид сразу не поймешь, но взломать легко). Метод <0 или >8 вообще не меняет Data, то есть шифрования или кодирования при этом не происходит. Методы без шифрования (7,8) применяются, когда "сильная" защита данных не нужна, при этом достигается высокая скорость при минимальной защите (неквалифицированный оператор не сможет прочитать пароль, программист легко взломает). Если нужна "сильная" защита, используются методы 3..6 (для скорости рекомендуется метод 4). Метод 9 передает данные открытым текстом и может применяться для тестирования и отладки.

function CharReverseStr(s:String):String;
- функция возвращает строку с обратным порядком символов. Например, если s='abcdef', то функция вернет 'fedcba'.

function TextVarScan(var Buff:String; Name:String):String;
- функция ищет в текстовом буфере Buff, состоящем из строк, разделенных EOL, строку типа Name=Value, и возвращает Value.

function TextLineCount(s,delim:String):Integer;
function ExtractTextLine(num:Integer; s,delim:String):String;
- функции для работы с текстом в буфере строки (s), состоящем из строк, разделенных разделителем (delim), которым обычно является CRLF или EOL.
Функция TextLineCount подсчитывает число строк текста (s) с разделителем (delim). В отличие от функции WordCount, число разделителей здесь играет существенную роль, например, WordCount('A'+EOL+EOL+'B')=2, но TextLineCount('A'+EOL+EOL+'B',EOL)=3.
Функция ExtractTextLine извлекает из текста (s) с разделителем (delim) строку номер (num). Нумерация строк начинается с нуля.
Например:

    buff:='Line1'+EOL+'Line2'+EOL;
    for i:=0 to TextLineCount(buff,EOL)-1 do begin
     writeln('Line ',i,' = ',ExtractTextLine(i,s,EOL));
    end;
   
Из соображений производительности подобный цикл по строкам с помощью функции ExtractTextLine рекомендуется использовать только для небольших текстов (до нескольких килобайт), так как при работе с большими текстами трудоемкость такого цикла растет нелинейно (как квадрат размера текста). Функцию ExtractTextLine можно также использовать для извлечения конкретных строк с известными номерами. При работе с большими текстами рекомендуется "перегонять" их в объект-текст (StringToText) и работать уже с объектом-текстом. Несмотря на затраты на перевод строки в объект-текст это будет более эффективно для больших текстов.

function ColorToString(code:Integer):String;
function StringToColor(name:String):Integer;
function StringToColorDef(name:String; def:Integer):Integer;
- функции преобразования цветов: кода цвета (code) в имя цвета (name) и наоборот. Функции работают на базе вызовов ParamStr('ColorCode/ColorName '..), но удобнее для использования.

  • ColorToString(code) преобразует код цвета code в имя цвета (если такое найдется), либо возвращает его hex-код (типа $0000FF). Следует иметь в виду, что у цвета может быть несколько имен (например, _3DFace=_BtnFace), при этом возвращается первое найденное в таблице цветов имя.

  • StringToColor(name) преобразует имя цвета name в код цвета, либо возвращает код clNone, если имя не найдено. В качестве имени цвета допустимо указывать его числовое значение (типа 255) или hex-код (типа $0000FF).

  • StringToColorDef(name,def) преобразует имя цвета name в код цвета, либо возвращает код по умолчанию def, если имя не найдено. В качестве имени цвета допустимо указывать его числовое значение (типа 255) или hex-код (типа $0000FF). Функция применяется, если в качестве цвета по умолчанию используется цвет, отличный от clNone.

Доступны также следующие предопределенные константы основных физических цветов:

  •  clNone     - "прозрачный" цвет _None (не рисуется).
  •  clBlack    - черный.
  •  clMaroon   - темно-красный.
  •  clGreen    - темно-зеленый.
  •  clOlive    - оливковый.
  •  clNavy     - темно-синий.
  •  clPurple   - темно-фиолетовый.
  •  clTeal     - темно-бирюзовый.
  •  clGray     - темно-серый.
  •  clSilver   - серебряный.
  •  clRed      - красный.
  •  clLime     - зеленый.
  •  clYellow   - желтый.
  •  clBlue     - синий.
  •  clFuchsia  - фиолетовый.
  •  clAqua     - аквамариновый.
  •  clWhite    - белый.
Остальные имена доступны по имени через вызов StringToColor(code).

Для имен цветов действуют такие правила эквивалентности:
  1. Имена (Blue) и (clBlue) эквивалентны (для совместимости с именами цветов Painter);
  2. Имена (_3DFace) и (3DFace) эквивалентны (со знака подчеркивания начинаются системные цвета).
Таким образом, _BtnFace,BtnFace,clBtnFace,cl_BtnFace обозначают один и тот же цвет.

function GuiLanguageSign:String;
function GuiLanguageCode:Integer;
function glc_English:Integer;
function glc_Russian:Integer;
function RusEngStr(rus,eng:String):String;
- функции языковой поддержки для графического интерфейса.
Функция GuiLanguageSign возвращает текстовый 3-значный код языка, rus или eng. Этот код вычисляется из переменной окружения CRW_DAQ_SYS_LANG.
Функция GuiLanguageCode возвращает целочисленный код языка, Dump2i('rus') или Dump2i('eng'). Этот код вычисляется из значения GuiLanguageSign.
Функция glc_English возвращает код английского языка, равный Dump2i('eng').
Функция glc_Russian возвращает код русского языка, равный Dump2i('rus').
Функция RusEngStr(rus,eng) возвращает строку на русском rus или английском eng, в зависимости от значения GuiLanguageCode.
Пример:

   s:=RusEngStr('Привет мир','Hello world');
   if (GuiLanguageCode=glc_Russian) then writeln('Русский язык интерфейса.')
   

function IfThenStr(Cond:Boolean; S1,S2:String):String;
Возвращает в зависимости от условия Cond значение S1 или S2.
Пример:

   s:=IfThenStr(IsWindows,'Must Die!','Long Live!');
   

function CookieScanAlter(buff,items:String; mode:Integer):String;
- сканирование текстового буфера buff для поиска значения "куки" с альтернативным набором имен параметра, заданным списком items. Список items содержит альтернативный список имен читаемого параметра, разделенных символами запятой (,) или точки с запятой (;). Для каждого имени (name) из списка items вызывается функция CookieScan(buff,name,mode), пока не будет найден непустой результат вызова. Если такой результат найден, он немедленно возвращается в качестве значения функции. Таким образом, CookieScanAlter(buff,items,mode) позволяет одним вызовом читать параметр, который может иметь несколько альтернативных имен.
Чтение параметров с альтернативой выбора используется, например, для анализа строк описания подключения к базам данных. Различные СУБД используют разные имена для описания параметров подключения. Например, имя пользователя может быть задано переменными с альтернативным набором имен: UserName, User ID или UID. Вызов типа UserName:=CookieScanAlter(Buff,'UserName;User ID;UID',Ord(';')); позволяет прочитать параметр с альтернативным набором имен одним вызовом, вместо целого блока кода с тремя вызовами CookieScan для каждого альтернативного варианта.
Пример:

   // Буфер для чтения
   Buff:='Provider=ADODB;UserName=Master;';
   // Чтение переменной с именем UserName
   UserName:=CookieScan(Buff,'UserName',Ord(';'));
   // Чтение переменной UserName, или User ID, или UID.
   UserName:=CookieScanAlter(Buff,'UserName;User ID;UID',Ord(';'));
   

procedure ClearStdStrings;
procedure InitStdStrings;
procedure FreeStdStrings;
procedure PollStdStrings;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdConsole

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

_con_StdConsole.inc - файл описания констант модуля StdConsole.

Константы dfTrouble, dfSuccess, dfViewExp, dfViewImp, dfDetails - используются для управления режимом вывода различных групп сообщений: Trouble или Problem при ошибках, Success или Succeed при успешном выполнении, ViewExp при выводе в канал связи, ViewImp при вводе из канала связи, Details при выводе отладочных информационных сообщений. Собственно, режимом вывода управляет переменная DebugFlags, а константы нужны для проверки битов, например,

if iAnd(DebugFlags,dfDetails)>0 then Details(info);

_var_StdConsole.inc - файл описания переменных модуля StdConsole.

Переменная DebugFlags
управляет режимом вывода:

    dfTrouble  бит 0 - разрешает сообщения об ошибках Trouble,Problem,Failure
    dfSuccess  бит 1 - разрешает сообщения об успешных операциях и информации Success
    dfViewExp  бит 2 - разрешает сообщения при выводе данных в канал или порт ViewExp
    dfViewImp  бит 3 - разрешает сообщения при вводе данных из канала и порта ViewImp
    dfDetails  бит 4 - разрешает сообщения при отладке, выводе разных деталей Details
   
Для задания начального значения флагов используется CFG-переменная DebugFlags = n, по умолчанию n=3. Для изменения режима вывода в процессе работы служит команда @DebugFlags n, доступная в рамках шаблона прикладной программы template.pas.

Переменная StdIn_Line - служит временным буфером при анализе консольного ввода. Носит служебный характер.

Переменные StdIn_LineCount, StdIn_CmndCount, StdIn_LineErrors - содержит счетчики соответственно входных строк стандартной консоли, поступающих консольных команд, а также ошибок чтения консоли стандартного ввода StdIn.

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

Переменная StdIn_HelpLines является внутренней, использовать ее запрещено.

Переменная StdIn_HelpEchoOn разрешает эхо-вывод справки @Help в консоль, по умолчанию включена.

Переменная StdIn_EnablePoll разрешает цикл опроса консольного процессора StdIn_Processor в основном теле программы. По умолчанию включена. Запретите этот цикл, если вам нужна специальная процедура обработки консольного ввода. Это бывает, например, нужно в драйверах, где консольный ввод должен быть синхронизован с циклом опроса устройства.

Переменные StdIn_ToStarting, StdIn_ToStopping, StdIn_ToPredPoll, StdIn_ToPostPoll задают предельное время (Timeout) и порядок вызова консольного процессора StdIn_Processor в цикле опроса шаблонной программы template.pas.
Реализацию цикла опроса см. в _std_main.inc.

Переменная StdIn_ToStarting задает ограничение по времени (Timeout, ms) при вызове цикла опроса консольного процессора StdIn_Processor ПОСЛЕ исполнения процедуры инициализации работы InitApplication в блоке Starting, то есть при первом вызове программы. Если Timeout=0, то консольный процессор не вызывается. Если Timeout=MaxInt, то на время обработки команд ограничений не накладывается. Остальные значения задают максимальное время на обработку в миллисекундах.
По умолчанию StdIn_ToStarting = 0.

Переменная StdIn_ToStopping задает ограничение по времени (Timeout, ms) при вызове цикла опроса консольного процессора StdIn_Processor ПЕРЕД исполненем процедуры завершения работы FreeApplication в блоке Stopping, то есть при последнем вызове программы. Если Timeout=0, то консольный процессор не вызывается. Если Timeout=MaxInt, то на время обработки команд ограничений не накладывается. Остальные значения задают максимальное время на обработку в миллисекундах.
По умолчанию StdIn_ToStopping = 0.

Переменная StdIn_ToPredPoll задает ограничение по времени (Timeout, ms) при вызове цикла опроса консольного процессора StdIn_Processor ПЕРЕД исполнением основной процедуры пользовательской обработки PollApplication в блоке Polling. Если Timeout=0, то консольный процессор не вызывается. Если Timeout=MaxInt, то на время обработки команд ограничений не накладывается. Остальные значения задают максимальное время на обработку в миллисекундах.
По умолчанию StdIn_ToPredPoll = 0.

Переменная StdIn_ToPostPoll задает ограничение по времени (Timeout, ms) при вызове цикла опроса консольного процессора StdIn_Processor ПОСЛЕ исполнения основной процедуры пользовательской обработки PollApplication в блоке Polling. Если Timeout=0, то консольный процессор не вызывается. Если Timeout=MaxInt, то на время обработки команд ограничений не накладывается. Остальные значения задают максимальное время на обработку в миллисекундах.
По умолчанию StdIn_ToPostPoll = MaxInt.

_fun_StdConsole.inc - файл описания процедур и функций модуля StdConsole. Это консольные функции общего назначения. Их правильное использование гарантируется в рамках шаблона прикладной программы template.pas.

function DebugFlagEnabled(flag:Integer):Boolean;
- проверяет переменную DebugFlags на наличие в ней заданного флага, чтобы узнать, включен ли заданный режим отладочного вывода. Например:

    if DebugFlagEnabled(dfViewImp) then ViewImp('Приняты данные: '+Data);
    if DebugFlagEnabled(dfViewExp) then ViewExp('Посланы данные: '+Data);
    if DebugFlagEnabled(dfDetails) then Details('Детали обработки: '+Comment);
   

Переменная StdIn_CmdHashTab содержит хеш-таблицу команд, зарегистрированных функцией RegisterStdInCmd и используемая функцией GotCommandId. Трогать эту переменную не надо.

procedure Trouble(msg:String);
procedure Problem(msg:String);
procedure Failure(ErrorCode:Integer; msg:String);
procedure Success(msg:String);
procedure Succeed(msg:String);
procedure ViewImp(msg:String);
procedure ViewExp(msg:String);
procedure Details(msg:String);
procedure Disturb(msg:String);
procedure Alerter(msg:String);
procedure CritErr(msg:String);
procedure Fatally(msg:String);
procedure Reports(msg:String);
procedure Assertion(cond:Boolean; msg:String);
- осуществляют вывод сообщений в консоль, с записью в системный журнал (в зависимости от уровня значимости severity). Режимом вывода в консоль управляет переменная отладочных флагов DebugFlags, биты которой разрешают вывод сообщений разных типов. Проверять отладочные флаги можно с помощью функции DebugFlagEnabled.

Trouble, Problem, Failure, Disturb, Alerter, CritErr, Fatally выводят сообщения об ошибках в различных режимах (консоль, журнал, генерация ошибки). При этом Trouble, CritErr, Fatally также генерирует ошибку (FixError(ErrorCode)), связанную с именем данного устройства, Failure генерирует ошибку, явно указанную при вызове, а Problem, Disturb, Alerter вообще ничего не генерирует, а только выводит сообщение. То есть если ошибка заслуживает красной мордочки , то используется Trouble, CritErr, Fatally (обычно) или Failure (в случае специфических ошибок). А если ошибка не очень важная и не заслуживает такого внимания, используется Problem, Disturb, Alerter.

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

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

   Details ≤ ViewImp ≈ ViewExp ≤ Success ≤ Disturb ≤ Succeed ≤ Problem ≤ Alerter ≤ Trouble ≈ Failure ≤ CritErr ≤ Fatally
   


Assertion(cond,msg) ("утверждение") проверяет условие (cond) и в случае успеха выводит сообщение (Success), а в противном случае генерирует и выводит ошибку (Trouble). Например:
   Assertion(FileExists(f),'FileName='+f);

   это эквивалентно вызову

   if FileExists(f)
   then Success('FileName='+f)
   else Trouble('FileName='+f);

   но компактнее и исключает дублирование кода
   


Процедура Reports служит оболочкой для вызова процедур Trouble, Problem, Failure, Success, Succeed, ViewImp, ViewExp, Details, Disturb, Alerter, CritErr, Fatally с выбором по первому слову, например:
   Reports('Success Выполнено успешно.');
   Reports('Trouble Возникла ошибка.');
   

   Следует отметитить, что процедуры Trouble, Problem, Failure, Success, Succeed, ViewImp, ViewExp, Details, Disturb, Alerter, CritErr, Fatally являются также "обертками" для журнала SysLog. Они (кроме печати в консоль устройства) заносят записи в журнал в соответствии с настройками уровней значимости Severity.
   Следует также пояснить, что процедуры Success и Succeed работают аналогично, но имеют разные уровни значимости Severity - значимость Succeed должна быть выше. Это позволяет разделять потоки событий при записи в журнал. Вызывайте Succeed для наиболее важных (успешных) событий - таких как старт программы. Для остальных (не очень важных) событий используйте Success.
   Наконец, отметим, что перечисленные процедуры имеют динамический уровень Severity. Точнее, каждая процедура имеет отдельные уровни для различных фаз выполнения программы Starting, Stopping и Polling, так как, например, ошибки на старте имеют гораздо более высокое значение - программа не будет работать при ошибке на старте. Поэтому необходимо иметь разные уровни Severity для разных фаз выполнения.

Поведение вышеописанных процедур вывода описывается такой таблицей:

Процедура Флаг вывода
в консоль
SeverityOfStarting
SeverityOfStopping
SeverityOfPolling
Генерация
ошибки
FixError
Комментарий
Details dfDetails
16
5=DEBUG/Details
5=DEBUG/Details
5=DEBUG/Details
Нет Отладочный вывод с высокой детализацией.
Обычно в журнал не пишется.
ViewExp dfViewExp
8
6=DEBUG/ViewExp
6=DEBUG/ViewExp
6=DEBUG/ViewExp
Нет Отладочный вывод "экспорта" - т.е. вывода данных.
Обычно в журнал не пишется.
ViewImp dfViewImp
4
7=DEBUG/ViewImp
7=DEBUG/ViewImp
7=DEBUG/ViewImp
Нет Отладочный вывод "импорта" - т.е. ввода данных.
Обычно в журнал не пишется.
Success dfSuccess
2
14=INFO/Print
14=INFO/Print
14=INFO/Print
Нет Вывод сообщений об успешных действиях.
Обычно в журнал не пишется.
Disturb dfTrouble
1
23=WARN/Disturb
23=WARN/Disturb
14=INFO/Print
Нет Вывод сообщений об ошибке/проблеме (низкого уровня значимости) без генерации ошибки.
При старте/стопе значимость выше, чем в цикле опроса.
Обычно в журнал не пишется.
Succeed dfSuccess
2
16=INFO/Success
16=INFO/Success
16=INFO/Success
Нет Вывод сообщений об успешном завершении (значимых) операций.
Обычно в журнал пишется.
Problem dfTrouble
1
33=ERROR/Fail
33=ERROR/Fail
14=INFO/Print
Нет Вывод сообщений об ошибке/проблеме (низкого уровня значимости) без генерации ошибки.
При старте/стопе значимость выше, чем в цикле опроса.
Обычно в журнал пишется при старт/стопе, не пишется в цикле опроса.
Alerter dfTrouble
1
33=ERROR/Fail
33=ERROR/Fail
29=WARN/Alert
Нет Вывод сообщений об ошибке/проблеме (среднего уровня значимости) без генерации ошибки.
При старте/стопе значимость выше, чем в цикле опроса.
Обычно в журнал пишется.
Trouble dfTrouble
1
41=FATAL/Failure
41=FATAL/Failure
35=ERROR/Trouble
Да
с кодом
устройства
Вывод сообщений об ошибке (высокого уровня значимости) с генерацией ошибки.
При старте/стопе значимость выше, чем в цикле опроса.
Обычно в журнал пишется.
Failure dfTrouble
1
41=FATAL/Failure
41=FATAL/Failure
35=ERROR/Trouble
Да
с заданным
кодом
Вывод сообщений об ошибке (высокого уровня значимости) с генерацией ошибки.
При старте/стопе значимость выше, чем в цикле опроса.
Обычно в журнал пишется.
CritErr dfTrouble
1
39=ERROR/Critical
39=ERROR/Critical
39=ERROR/Critical
Да
с кодом
устройства
Вывод сообщений об ошибке (критического уровня значимости) с генерацией ошибки.
Обычно в журнал пишется.
Fatally dfTrouble
1
45=FATAL/Emergency
45=FATAL/Emergency
45=FATAL/Emergency
Да
с кодом
устройства
Вывод сообщений об ошибке (фатального уровня значимости) с генерацией ошибки.
Обычно в журнал пишется.
Для справки: нормальный (обычный) порог уровня значимости Severity равен 15=INFO/Notify.
В системный журнал записываются сообщения не ниже этого уровня.

function IoError:Boolean;
проверяет статус консоли ввода-вывода и при необходимости генерирует ошибку, показывает сообщение и инкрементирует счетчик ошибок StdIn_LineErrors. Обычно явно функция не используется, она вызывается автоматически в цикле опроса.

function StdIn_Readln(var Data:String):Boolean;
считывает очередную строку из консоли ввода, а также инкрементирует счетчик StdIn_LineCount входных строк. Функция обычно используется в теле основной программы в рамках шаблона прикладной программы template.pas.

procedure StdIn_SetTimeouts(init,stop,pred,post:Integer);
задает значения предельных времен (Timeout) StdIn_ToStarting, StdIn_ToStopping, StdIn_ToPredPoll, StdIn_ToPostPoll цикла обработки консольного ввода при СТАРТЕ, ЗАВЕРШЕНИИ, ПЕРЕД и ПОСЛЕ процедуры обработки данных (PollApplication) в цикле опроса.
Значения по умолчанию соответствуют вызову StdIn_SetTimeouts(0,0,0,MaxInt).
Функция обычно используется в процедуре InitApplication в рамках шаблона прикладной программы template.pas.

procedure StdIn_SetScripts(init,stop:String);
задает сообщения (скрипты) StartingDevMsg, StoppingDevMsg которые помещаются в консоль после старта и перед завершением программы.
Значения по умолчанию соответствуют вызову StdIn_SetScripts('','').
Функция обычно используется в процедуре InitApplication в рамках шаблона прикладной программы template.pas.

Пример:

    procedure InitApplication;
    begin
     StdIn_SetScripts('@StartupScript','@FinallyScript');    Задать стартовое,завершающее сообщение в консоль
     StdIn_SetTimeouts(0,MaxInt,MaxInt,0);                   Разрешить обработку завершающих и циклических консольных команд
    end;
    ...
    В результате (при условии что в StdIn_Processor вызывается StdIn_DefaultHandler)
    будет выполнен StartupScript на старте и FinallyScript при завершении.
   

function LooksLikeCommand(line:String):Boolean;
- функция (переводится "выглядит как команда") проверяет строку (line) на предмет соответствия формату консольной команды, у которой первый символ должен быть признаком команды (@), а затем должен следовать хотя бы один символ, отличный от пробела. Функция может применяться для проверки формата предполагаемых команд перед их выполнением.

function GotCommand(var Data,cmd,arg:String):Boolean;
- используется для первичной выборки команд в цикле обработки консольных команд (StdIn_Prosessor) в рамках шаблона прикладной программы template1.pas. При поступлении команд также инкрементирует счетчик StdIn_CmndCount. Входными данными является полученная из консоли строка Data. Возвращается команда cmd с аргументами arg. Признаком команды служит символ @ в начале строки Data. Если этого символа нет, функция вернет false.

Функция GotCommand допускается и поддерживается, но считается устаревшей.
Вместо нее рекомендуется использовать GotCommandId.

function GotCommandId(var Data,cmd,arg:String; var cmdid:Integer):Boolean;
- используется для первичной выборки команд в цикле обработки консольных команд (StdIn_Prosessor) в рамках шаблона прикладной программы template.pas. При поступлении команд также инкрементирует счетчик StdIn_CmndCount. Входными данными является полученная из консоли строка Data. Возвращается команда cmd с аргументами arg и идентификатор команды cmdid, зарегистрированный функцией RegisterStdInCmd. Признаком команды служит символ @ в начале строки Data. Если этого символа нет, функция вернет false. Если команда cmd не совпадает с одной из зарегистрированных функцией RegisterStdInCmd команд, то идентификатор команды cmdid будет равен -1 для сигнализации незарегистрированной команды. Если команда cmd совпадает с одной из зарегистрированных функцией RegisterStdInCmd команд, то идентификатор команды cmdid будет положительным числом.

Функцию GotCommandId рекомендуется использовать вместо GotCommand по причине того, что это позволяет существенно ускорить обработку консольных команд.

function RegisterStdInCmd(cmd,help:String):Integer;
- регистрирует команду cmd с поясняющим комментарием help в списке консольных команд и возвращает её идентификатор (положительное целое число) или ноль (признак ошибки). Зарегистрированные команды используются в цикле обработки консольных команд (StdIn_Prosessor) в рамках шаблона прикладной программы template.pas. Признаком команды служит символ @ в начале строки - если его нет, возвращается признак ошибки 0. Зарегистрированные команды могут использоваться в цикле обработки консольных команд с помощью функции GotCommandId. Комментарий help отображается в консоли при выполнении команды @help, поэтому надо давать содержательный комментарий, поясняющий назначение и синтаксис команды. Комментарий может быть текстом, состоящим из строк, разделенных с помощью EOL. Это позволяет давать развернутые комментарии с подробным описанием команд.
Использование функций RegisterStdInCmd и GotCommandId схематично показано на примере:

    program Demo;
    ...
    var
    ...
     cmd_Demo          : Integer;    { Command @Demo                    }   // Заводим целочисленную переменную под команду
    ...
     procedure InitApplication;
     begin
      ...
      cmd_Demo:=RegisterStdInCmd('@Demo','@Demo    - Run Demo command');    // Регистрируем команду в списке консольных команд
      ...                                                                   // При регистрации задаем комментарий для @Help
     end;
     ...
     procedure StdIn_Processor(var Data:String);
     var cmd,arg:String; cmdid:Integer;
     begin
      ...
      if GotCommandId(Data,cmd,arg,cmdid) then begin                        // Получаем команду cmd c идентификатором cmdid
       {
       @Demo ...
       }
       if (cmdid = cmd_Demo) then begin                                     // Сравниваем команду с этим идентификатором
        ...                                                                 // Обрабатываем, если совпало
       end else
       ...
      end;
      ...
     end;
     ...
    end.
   
Для каждой команды заводится целочисленная переменная cmd_XXX. С помощью функции RegisterStdInCmd эта команда регистрируется при инициализации программы. В цикле опроса с помощью функции GotCommandId вычисляется идентификатор поступившей команды и сравнивается с зарегистрированной командой. При совпадении выполняется обработка. Программы обработки консольного ввода, построенные по такой схеме, выполняются существенно быстрее, чем программы, построенные на вызове функции GotCommand и последующем сравнении строк.

procedure OpenConsole(Mode:Integer);
открывает консоль в режиме 1 (видна на экране) или 2 (свернута). При режиме 0 консольное окно не создается, хотя его всегда можно открыть через панель управления DAQ-системой. Для задания режима консоли используется CFG-переменная OpenConsole = ... в секции описания устройства. При работе в рамках шаблона прикладной программы template.pas явный вызов процедуры не требуется, он делается автоматически при старте.

procedure ShowHelp(AllowEcho:Boolean);
- отображает справку @Help. При работе в рамках шаблона прикладной программы template.pas вызывается автоматически в цикле обработки консольных команд при вызове StdIn_DefaultHandler.

function GetDateTime(ms:Real):String;
- возвращает строку даты и времени в формате типа 2012.03.18-22:01:47, взяв время ms по часам msecnow. При форматировании используется последовательность год.месяц.день-час:мин:сек. Этот формат хорош тем, что при наличии многих меток времени их упорядочевание в алфавитном порядке и в календарном (по времени) совпадают. Поэтому этот формат служит основой для генерации имен файлов, чтобы файлы были автоматически упорядочены по дате-времени.

procedure ClearStdConsole;
procedure InitStdConsole;
procedure FreeStdConsole;
procedure PollStdConsole;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdDevices

обеспечивает работу с DAQ-устройствами и стандартными серверами, в рамках шаблона прикладной программы template.pas.

_con_StdDevices.inc - файл описания констант модуля StdDevices.

Константы DimSrv, WebSrv, DatSrv, FdbSrv, CronSrv, PlotSrv, VkbdSrv, SpeakSrv, EmlSrv, OpcCln, ModbusSrv - задают имена стандартных серверов. Эти имена уже устоялись, менять их нет смысла. Тем более, что многие сервера (как Web,Dim) не допускают запуска двух экземпляров в рамках одной конфигурации.

_var_StdDevices.inc - файл описания переменных модуля StdDevices.

Переменные devMySelf, devDimSrv, devWebSrv, devDatSrv, devFdbSrv, devCronSrv, devPlotSrv, devVkbdSrv, devSpeakSrv, devEmlSrv, devOpcCln, devModbusSrv - содержат ссылки на стандартные сервера и на само устройство (devMySelf). То есть в каждой программе, сделанной по шаблону, эти сервера будут доступными.

_fun_StdDevices.inc - файл описания процедур и функций модуля StdDevices.

procedure InitDevice(var ref:Integer; name:String; typ:Integer);
- инициализирует ссылку ref на устройство name. typ задает режим вывода и генерации ошибок. При typ=0 просто происходит инициализация, без всяких сообщений или анализа результата. При typ=1 выводится сообщение (Success) при успешной инициализации, но при неуспешной (Problem) ошибка не генерируется. При typ=2 выводится сообщение (Success) при успешной инициализации или генерируется ошибка (Trouble) при неуспешной. Если инициализируемое устройство критически важно, используйте режим 2 - чтобы при отсутствии устройства был сигнал ошибки. Если инициализируемое устройство опционально, используйте режим 1 или 0 (если не нужны сообщения об инициализации). В этом случае программа гарантированно стартует без ошибки, но ссылка может оказаться нулевой.

procedure DevSendCmd(devRef:Integer; Cmd:String);
procedure DevPostCmd(devRef:Integer; Cmd:String);
procedure DevSendCmdLocal(Cmd:String);
procedure DevPostCmdLocal(Cmd:String);
- посылает устройству devRef команду (сообщение) Cmd, добавляя к нему маркер конца строки EOL, используя синхронный метод (devsend) или асинхронный (devpost). Локальная версия (local) посылает сообщение локально - т.е. устройству devMySelf (самому себе). Если сообщение не может быть послано, генерируется ошибка (Trouble). Использовать процедуры удобно - не надо постоянно вспоминать о необходимости добавлять EOL в конец строки (а это важно).

procedure Cron(msg:String);
- посылает сообщение (с добавлением EOL) серверу &CronSrv. Этот сервер весьма активно используется в последнее время, а процедура облегчает обращение к нему.

procedure Speak(msg:String);
- посылает команду @Speak=msg (с добавлением EOL) серверу речевого синтезатора &SpeakSrv. Облегчает использование этого сервера для вывода речевых сообщений.

function PlotSend(msg:String):Integer;
function VkbdSend(msg:String):Integer;
- посылает сообщение msg (без добавления EOL) серверу &PlotSrv или &VkbdSrv и возвращает длину посланного сообщения. Эта форма вызова удобна для использования именно этих серверов, позволяя добиться выразительной и понятной нотации типа

if PlotSend(PlotJob)>0 then Success('Ok');

procedure ClearStdDevices;
procedure InitStdDevices;
procedure FreeStdDevices;
procedure PollStdDevices;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdCurves

обеспечивает работу с DAQ-кривыми, в рамках шаблона прикладной программы template.pas.

_con_StdCurves.inc - файл описания констант модуля StdCurves.

Пока файл пуст.

_var_StdCurves.inc - файл описания переменных модуля StdCurves.

Пока файл пуст.

_fun_StdCurves.inc - файл описания процедур и функций модуля StdCurves.

procedure UpdateAo(n:Integer; t,y:Real);
procedure UpdateDo(n:Integer; t,y:Real);
- записывают точку (t,y) в AnalogOutput или DigitalOutput номер n.

function crvGetLastY(ref:Integer):Real;
- извлекает из кривой значение Y-координаты последней точки. То есть это текущее значение величины, которая записывается в кривую.

function GetCrvRange(Crv:Integer; var x1,y1,x2,y2:Real):Integer;
- вычисляет границы диапазона данных по X и Y, внутри которого находятся все точки кривой Crv. Если точек на кривой нет, диапазон будут пуст (x2<x1,y2<y1).

procedure ClearCurveData(Crv:Integer);
- очищает массив данных кривой.

function FindNaiByName(name:String):Integer;
function FindNdiByName(name:String):Integer;
function FindNaoByName(name:String):Integer;
function FindNdoByName(name:String):Integer;
- находит номер аналогового/цифрового входа/выхода по имени кривой name или возвращает -1, если номер не найден.
Эти функции рекомендуется вызывать при инициализации программы, а не в цикле опроса, т.к. поиск может занимать время. Лучше найти номер, запомнить его и затем использовать в цикле опроса. При этом удобно использовать запись TTagRef. Например:

   var DEMO : TTagRef;                              // Тип TTagRef объявлен в {$ _typ_StdLibrary}
   ...
    DEMO.tag:=FindTag('DEMO');                      // Инициализация тега по имени DEMO
    DEMO.nai:=FindNaiByName(NameTag(DEMO.tag));     // Номер AnalogInput одноименной кривой
    ...
    bNul(rSetTag(DEMO.tag,GetAi(DEMO.nai)));        // В цикле опроса используем этот номер
   

procedure ClearStdCurves;
procedure InitStdCurves;
procedure FreeStdCurves;
procedure PollStdCurves;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdSounds

обеспечивает работу со звуками, в рамках шаблона прикладной программы template.pas.

_con_StdSounds.inc - файл описания констант модуля StdSounds.

Константы snd_Click, snd_Press, snd_Fails, snd_Error, snd_Break, snd_Fatal, snd_Cancel, snd_Wheel, snd_Deny, snd_Exit, snd_Restart, snd_Alert1, snd_Alert2, snd_Siren, snd_Inc, snd_Dec - содержат имена характерных звуков (достаточно очевидных из названия), которые рекомендуется использовать в программах при вызове voice, вместо не столь очевидных имен файлов.

_var_StdSounds.inc - файл описания переменных модуля StdSounds.

Пока файл пуст.

_fun_StdSounds.inc - файл описания процедур и функций модуля StdSounds.

procedure ClearStdSounds;
procedure InitStdSounds;
procedure FreeStdSounds;
procedure PollStdSounds;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdVector

предоставляет набор функций, облегчающих работу с векторами - т.е. динамическими массивами, созданными в рамках шаблона прикладной программы template.pas.
Векторы – это по сути буферы в памяти, которые реализуют массивы динамического размера.
Индексация элементов вектора всегда начинается с нуля.
В настоящее время реализованы векторы iVec=IntegerVector и rVec=RealVector.
Векторы требуют обязательной очистки (clear), инициализации (init) и освобождения (free).
var iv,rv:TVector;
...
procedure ClearApplication;
begin
 iv:=ivec_init(0); rv:=rvec_init(0);      // Начальная очистка делается так.
 ...
end;
...
procedure FreeApplication;
begin
 ivec_free(iv); rvec_free(rv);            // Освобождение памяти делается так.
 ...
end;
...
procedure InitApplication;
begin
 iv:=ivec_init(100); rv:=rvec_init(2000); // Инициализация делается так.
 ...
end;
   


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

_con_StdVector.inc - файл описания констант модуля StdVector.

Пока файл пуст.

_var_StdVector.inc - файл описания переменных модуля StdVector.

Переменная ShouldPollStdVector содержит флаг опроса модуля. Менять её нежелательно.

Переменные iVec_ErrorsCount rVec_ErrorsCount содержат счетчики ошибок (при попытке записи в массив с неверным индексом).

_fun_StdVector.inc - файл описания процедур и функций модуля StdVector.

function iVec_Init(n:Integer):TVector;
function rVec_Init(n:Integer):TVector;
- создает динамический массив целочисленных или вещественных элементов заданной длины n. Фактически, при вызове функции выделяется (строковый) буфер памяти для хранения двоичных данных размера n*SizeOfInteger для целочисленных и n*SizeOfReal для вещественных чисел. Инициализация векторов обязательна перед их использованием - например в InitApplication. А в ClearApplication рекомендуется сделать начальную очистку типа iv:=ivec_init(0);.

procedure iVec_Free(var vec:TVector);
procedure rVec_Free(var vec:TVector);
- освобождает буфер памяти, выделенный для хранения двоичных данных вектора. Вызывается в FreeApplication.

function iVec_Get(var vec:TVector; i:Integer):Integer;
function rVec_Get(var vec:TVector; i:Integer):Real;
- возвращает значение элемента i в массиве вектора. Индексация всегда начинается с нуля. Если индекс i выходит за диапазон вектора, функция вернет 0.

procedure iVec_Set(var vec:TVector; i,data:Integer);
procedure rVec_Set(var vec:TVector; i:Integer; data:Real);
- записывает значение элемента i в массиве вектора. Индексация всегда начинается с нуля. Если индекс i выходит за диапазон вектора, инкрементируется счетчик ошибок.

function iVec_Length(var vec:TVector):Integer;
function rVec_Length(var vec:TVector):Integer;
- возвращает длину (количество элементов) вектора. Элементы вектора индексируются в диапазоне [0..Length-1].

procedure iVec_SetLength(var vec:TVector; n:Integer);
procedure rVec_SetLength(var vec:TVector; n:Integer);
- задает новую длину n (количество элементов) вектора. Если элементов становится больше, массив дополняется нулями. Содержимое прежних элементов вектора не меняется.

procedure ClearStdVector;
procedure InitStdVector;
procedure FreeStdVector;
procedure PollStdVector;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdTags

обеспечивает работу с DAQ-тегами, в рамках шаблона прикладной программы template.pas.

_con_StdTags.inc - файл описания констант модуля StdTags.

Пока файл пуст.

_var_StdTags.inc - файл описания переменных модуля StdTags.

Пока файл пуст.

_fun_StdTags.inc - файл описания процедур и функций модуля StdTags.

procedure InitTag(var tag:Integer; name:String; typ:Integer);
- инициализирует ссылку tag на (существующий) тег, находя его по имени name в базе данных. При неудаче поиска или неверном типе тега ссылка tag зануляется, и генерируется ошибка Trouble. Тип тега typ может указываться со знаком минус, в этом случае генерация ошибки подавляется, то есть вместо Trouble используется Problem. Это удобно для инициализации опциональных тегов, которые могут отсутствовать. Обычно инициализация тегов выполняется один раз, в момент старта системы (Starting).

procedure UpdateTag(tag:Integer; newValue:String; min,max:Real);
- обновляет содержимое тега tag, если строка newValue содержит допустимое (десятичное) число, лежащее в заданном диапазоне (min,max). Если тег имеет строковый тип, данные могут быть любыми, а диапазон игнорируется. Процедура ориентирована на анализ результатов редактирования или приема данных из сети. Если диапазон не нужен, указывается min=_MinisInf, max=_PlusInf.

function TagAsDump(tag:Integer):String;
function TagAsText(tag:Integer):String;
- возвращает значение тега в виде десятичной строки (text) или двоичного образа содержимого (dump). Строковые теги возвращаются "как есть".

procedure ClearStdTags;
procedure InitStdTags;
procedure FreeStdTags;
procedure PollStdTags;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdTimes

обеспечивает функции, связанные со временем, календерным и обычным, в рамках шаблона прикладной программы template.pas.

_con_StdTimes.inc - файл описания констант модуля StdTimes.

Константы mSecsPerSec, mSecsPerMin, mSecsPerHour, mSecsPerDay, SecsPerMin, MinsPerHour, HoursPerDay задают параметры перевода единиц времени (сколько миллисекунд в минуте и т.д.), вполне понятные из названий. Использование этих констант предпочтительнее, чем использование сбивающих с толку цифр, особенно с большим числом нулей.

Константы JavaTimeBase, JavaTimeUnit, UnixTimeBase, UnixTimeUnit, FileTimeBase, FileTimeUnit, WinsTimeBase, WinsTimeUnit, OleTimeBase, OleTimeUnit служат для перевода времени по часам функции msecnow (миллисекунд от Рождества Христова), принятого пакете за основное, во время по другим часам. Для перевода используются такие формулы

    UnixTime = (mSecNow - UnixTimeBase)/UnixTimeUnit;  // Перевод времени к единицам Unix time()
    JavaTime = (mSecNow - JavaTimeBase)/JavaTimeUnit;  // Перевод времени к единицам JavaScript
    FileTime = (mSecNow - FileTimeBase)/FileTimeUnit;  // Перевод времени к единицам Win32 FileTime
    WinsTime = (mSecNow - WinsTimeBase)/WinsTimeUnit;  // Перевод времени к единицам Windows System
    OleTime  = (mSecNow - OleTimeBase)/OleTimeUnit;    // Перевод времени к единицам OLE,Delphi/Lazarus
   
Время UnixTime используется, например, во многих интернет технологиях. Время JavaTime бывает необходимо, например, для работы Web-серверов, использующих JavaScript, AJAX и другие Web-технологии. Время FileTime использует файловая система Win32, а также OPC UA. Время WinsTime полезно для системного времени Windows. Время OleTime использует OLE, OLEDB, ADO, OPC DA, а также Delphi/Lazarus (тип TDateTime и Variant). Так что константы полезные.

Константа SysTimer_MaxNum задает максимальное число таймеров коллективного пользования, см. SysTimer_Pulse.

Константы MSecRangeMin, MSecRangeMax задают диапазон допустимых времен (миллисекунд от начала эры), см. MSecNow. Это диапазон [0..315537897599999], соответствующий датам [0001.01.01-00:00:00.000..9999.12.31-23:59:59.999].

_var_StdTimes.inc - файл описания переменных модуля StdTimes.

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

_fun_StdTimes.inc - файл описания процедур и функций модуля StdTimes.

function MsToDaqTime(ms:Real):Real;
function DaqTimeToMs(tm:Real):Real;
function MsToUnixTime(ms:Real):Real;
function UnixTimeToMs(tm:Real):Real;
function MsToJavaTime(ms:Real):Real;
function JavaTimeToMs(tm:Real):Real;
function MsToFileTime(ms:Real):Real;
function FileTimeToMs(tm:Real):Real;
function MsToWinsTime(ms:Real):Real;
function WinsTimeToMs(tm:Real):Real;
function MsToOleTime(ms:Real):Real;
function OleTimeToMs(tm:Real):Real;
- функции преобразования единиц времени (tm) в миллисекунды (ms) и обратно.

   Расшифровка: Функции MsToXxxxTime(ms) и XxxxTimeToMs(tm)

   ms            Число миллисекунд (ms) от Рождества Христова (Xmas),  т.е. от 0001.01.01-00:00:00 (UTC).
                 Можно также назвать эти единицы как "миллисекунды от начала Новой Эры".
                 Используется как основное время в DaqPascal, см. функцию msecnow.

   tm            другие единицы времени:
   
   DaqTime       Число единиц времени (TimeUnit), прошедших от начала отсчета (TimeBase) часов DAQ.
                 Используется как основное время (функция time) в системе DAQ.
                 Шкала времени DaqTime задается в конфигурации DAQ системы.
                 В секции [DAQ] задается два параметра шкалы времени time:
                 1. TimeBase - дата и время начала отсчета времени time,
                 2. TimeUnit - единица времени time в секундах.

   UnixTime      Число секунд, прошедших от начала эпохи Unix (Epoch), т.е. от 1970.01.01-00:00:00 (UTC).
                 Используется как основное время - функция time() - в системе Unix, Linux и т.д.

   JavaTime      Число миллисекунд, прошедших от начала эпохи (Epoch), т.е. от 1970.01.01-00:00:00 (UTC).
                 Используется в JavaScript, AJAX и других Web технологиях.

   FileTime      Число 100 наносекундных интервалов от точки начала,   т.е. от 1601.01.01-00:00:00 (UTC).
                 Это единицы времени, в которых Windows хранит время файлов. Также используется в OPC UA.

   WinsTime      Число миллисекунд от точки начала,                    т.е. от 1601.01.01-00:00:00 (UTC).
                 Это единицы времени, в которых Windows хранит системное время.

   OleTime       Число дней от точки начала (идет от Lotus 1-2-3),     т.е. от 1899.12.30-00:00:00 (UTC).
                 Это единицы времени, которые использует OLE, OLEDB, ADO, OPC DA, Excel, Access, Word.
                 DbApi (работа с СУБД) использует OleTime, т.к. ADO использует варианты (Variant).
                 Также эти единицы использует Delphi/Lazarus (тип TDateTime и Variant).

    Например:    tm:=MsToOleTime(msecnow);       // Преобразовали текущее время в формат для записи в СУБД
                 ms:=OleTimeToMs(tm);            // Преобразовали время из СУБД в миллисекунды от начала Новой Эры
   
Все единицы времени можно конвертировать в основное время пакета (msecnow) или наоборот. Какие именно единицы времени и функции преобразования использовать, зависит от конкретной задачи. Например, для СУБД будет полезно использовать OleTime, а для файлов - FileTime.

function msElapsedSinceMarker(t:real):Real;
- возвращает время в миллисекундах, прошедшее с момента "маркера" t, либо ноль, если маркер нулевой. Функция предназначена для облегчения организации асинхронной обработки событий в цикле опроса. Работа функции совмещает в себе "флаг" (признак события) и "таймер" (отсчет времени) и основана на том, что время (mSecNow) всегда 1) монотонно, 2) имеет положительное значение. Признаком события является положительное (ненулевое) значение маркера времени. Эту функцию удобно использовать для организации "задержанных одиночных событий", наступающих через какое-то время после интересующих одиночных событий. При этом важно, чтобы задержка после события была ненулевой (т.к. ноль используется как признак остутствия события).
Например:

   var KickTime:Real;                                   // Маркер события

   procedure Init;                                      // Процедура при старте
   begin
    KickTime:=0;                                        // Инициализация маркера
   end;

   procedure Poll;                                      // Процедура в цикле опроса
   begin
    if Event then KickTime:=mSecNow;                    // По какому-то событию устанавливаем маркер
    if msElapsedSinceMarker(KickTime)>5000 then begin   // Проверяем, что после события прошло 5 сек
     Success('Do something after 5 sec...');            // Что-то делаем в связи с этим
     KickTime:=0;                                       // И сбрасываем маркер события
    end;
   end;

   Примечание:
     Вызов
       if msElapsedSinceMarker(t)>5000 then ...
     эквивалентен
       if (t>0) and ((mSecNow-t)>5000) then ...
     но более понятен ("самодокументирован")
   

function GetDateAsNumber(ms:real):Integer;
- возвращает для данного времени число, которое в десятичной нотации (Str) содержит год,месяц и день, например, 20050816. Может пригодиться для генерации имен файлов по времени.

function GetMidnight(ms:Real):Real;
- возвращает для данного времени ms время полуночи (начала суток) того же дня. То есть ms-GetMidnight(ms) - это время суток в миллисекундах, то есть число миллисекунд от полуночи текущих суток. Эта функция полезна, например, для генерации отчетов, где требуется знать время суток. Или для сравнения моментов времени - принадлежат ли они одним и тем же суткам (тогда их время полуночи должно совпадать).

function ms2DayOfWeek(ms:Real):Integer;
function DayOfWeekList(Lang:String):String;
function DayOfWeekStr(DayOfWeek:Integer; Lang:String):String;
function ms2DayOfWeekStr(ms:Real; Lang:String):String;
- функции работы с днями недели.
ms2DayOfWeek возвращает день недели в виде числа (1..7=Mon..Sun).
DayOfWeekList возвращает список дней недели в сответствии с указанным языком Lang:

    Lang     Result
    En       Mo,Tu,We,Th,Fr,Sa,Su
    Ru       Пн,Вт,Ср,Чт,Пт,Сб,Вс
    Eng      Mon,Tue,Wed,Thu,Fri,Sat,Sun
    Rus      Пнд,Втр,Срд,Чтв,Птн,Сбт,Вск
    English  Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
    Russian  Понедельник,Вторник,Среда,Четверг,Пятница,Суббота,Воскресенье
    Default  1,2,3,4,5,6,7
   
DayOfWeekStr возвращает день недели по номеру (1..7) как строку, в соответствии с Lang.
ms2DayOfWeekStr возвращает день недели по времени msecnow как строку, в соответствии с Lang.

function MonthList(Lang:String):String;
function MonthStr(Month:Integer; Lang:String):String;
function ms2MonthStr(ms:Real; Lang:String):String;
- функции работы с месяцами.
MonthList возвращает список месяцев в соответствии с указанным языком Lang:

    Lang     Result
    Eng      Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec
    Rus      Янв,Фев,Мар,Апр,Май,Июн,Июл,Авг,Сен,Окт,Ноя,Дек
    English  January,February,March,April,May,June,July,August,September,October,November,December
    Russian  Январь,Февраль,Март,Апрель,Май,Июнь,Июль,Август,Сентябрь,Октябрь,Ноябрь,Декабрь
    Default  01,02,03,04,05,06,07,08,09,10,11,12
   
MonthStr возвращает месяц по номеру (1..12) как строку, в соответствии с Lang.
ms2MonthStr возвращает месяц по времени msecnow как строку, в соответствии с Lang.

function ms2HttpTimeStr(ms:Real):String;
возвращает время в формате HTTP (RFC 1123), это выглядит как Sun, 06 Nov 1994 08:49:37 GMT. Эта функция полезна, например, при формировании HTTP заголовков на Web-серверах.

procedure SetClockRes(res:Integer);
устанавливает разрешение времени Windows, то есть фактически определяет квант времени операционной системы res в миллисекундах. Процедуру следует вызывать один раз при инициализации (Starting). При завершении программы библиотека автоматически восстановит прежнее разрешение.

function FibonacciNumber(arg:Integer):Integer;
вычисляет число Фибоначчи. Причем делает это по трудоемкому (рекурсивному) алгоритму. Так и было задумано. Потому что эта функция, интересная сама по себе, служит для организации стресс-тестирования компьютера.

procedure CpuProfilerStress(mks:Real);
procedure CpuProfilerStart(Poll,View,Mode,Stress:Integer;ms:Real);
procedure CpuProfilerView;
procedure CpuProfilerPoll;
procedure CpuProfilerProcess(arg:String);
- процедуры реализуют функциональность монитора производительности (профайлера), доступного через командную строку (см. описание ) в команде @CpuProfiler. Напрямую функции вызывать не следует, кроме CpuProfilerStress(mks), загружающего процессор замкнутым циклом на mks микросекунд. Может пригодиться для организации коротких задержек (но это не рекомендуется делать). Этот профайлер собирает и отображает кучу статистики, вот пример отчета:

    &WEBCGI : Profiler report at 2012.03.19-12:40:24:        9 sec.work
    &WEBCGI :  ---------+----------+----------+----------+--------------+----------+----------+----------+----------+
    &WEBCGI :  CPU Load | Kernel,% |   User,% | Thread,% | VDPM,kOp/sec | Runs/sec | Errs/sec | Line/sec | Cmnd/sec |
    &WEBCGI :  ---------+----------+----------+----------+--------------+----------+----------+----------+----------+
    &WEBCGI :   Average |    0.855 |   17.608 |   18.463 |    11664.796 |   53.063 |    0.000 |    1.094 |    1.094 |
    &WEBCGI :   LastSec |    1.539 |   20.012 |   21.552 |    13164.811 |   51.232 |    0.000 |    0.985 |    0.985 |
    &WEBCGI :   Peaking |    1.539 |   20.012 |   21.552 |    13532.635 |   66.010 |    0.000 |    1.970 |    1.970 |
    &WEBCGI :  ---------+----------+----------+----------+--------------+----------+----------+----------+----------+
    &WEBCGI :  Status & | # Errors | StrTable |    Stack | Program Runs | >> Lines | Commands | VDPM,MOp | Time,sec |
    &WEBCGI :  ---------+----------+----------+----------+--------------+----------+----------+----------+----------+
    &WEBCGI :  Counters |        0 |     3142 |    16226 |         4046 |       64 |       64 |      226 |       67 |
    &WEBCGI :  ---------+----------+----------+----------+--------------+----------+----------+----------+----------+
   
Профайлер показывает % загрузку процессора (CPU Load) (среднюю за период, пиковую за период и за последнюю секунду), с разбивкой на Kernel(режим ядра), User(режим пользователя), Thread(их сумма). Расчитывается также число операций виртуальной машины DaqPascal (VDPM,kOp/sec), число опросов в секунду (Runs/sec), число ошибок устройства в секунду (Errs/sec), число консольных строк и команд в секунду (Line/sec,Cmnd/sec). Он также показывает счетчики - ошибок (# Errors), свободных строк maxavail (StrTable), свободного стекового пространства stackavail (Stack), вызовов программы runcount(Runs), входных строк (Lines) и команд (Commands) консоли ввода StdIn, операций Virtual Daq Pascal Machine (VDPM,MOp), времени со старта программы (Time,sec). В целом получается мощный инструмент диагностики работы программы. Причем всегда доступный.

procedure SysTimer_Init;
procedure SysTimer_Poll;
function SysTimer_Pulse(ThePeriod:Integer):Real;
function SysTimer_Count:Integer;
- фунции реализуют еще один тип системного таймера для генерации периодических событий и организации циклов опроса. Этот таймер работает в режиме "коллективного пользования" и имеет ряд важных отличий от таймеров других типов, например tm_event.
   Процедуры инициализации SysTimer_Init и опроса SysTimer_Poll надо рассмативать как внутренние - напрямую вызываться не должны, их автоматически вызывает библиотека StdTimes. Просто используйте шаблон - и забудьте про эти процедуры.
   Функция SysTimer_Count возвращает число активных таймеров, от 0 до SysTimer_MaxNum. Эта функция нужна, чтобы проверить - есть ли еще возможность создать новый коллективный таймер и не переполнена ли таблица коллективных таймеров.
   Функция SysTimer_Pulse(ThePeriod) выполняет основную работу системного коллективного таймера, включая его создание, освобождение и генерацию событий. Для идентификации коллективных таймеров функция использует период ThePeriod. При первом вызове функции она, не найдя таймера с данным периодом, создает его вновь в своей внутренней таблице. При всех последующих вызовах функция находит таймер по периоду и работает с ним, беря данные из таблицы. Это удобно - для коллективных таймеров не надо создавать индивидуальные переменные, достаточно константы (периода опроса), которая одновременно служит ссылкой для доступа к коллективному таймеру. Период таймера, разумеется, должен быть положительным числом. Таким образом, SysTimer_Pulse реализует таймеры "коллективного пользования", которые ориентированы на определенный круг задач, связанных с периодическим опросом, но без индивидуальной работы с таймером. Если таймер предполагается останавливать, запускать, менять его период - используйте другие типы таймеров. Их у нас уже много.
   Вызов SysTimer_Pulse(-ThePeriod) освобождает таймер с периодом ThePeriod, так что одной и той же функцией можно создать и удалить таймер. Однако удалять таймеры коллективного пользования надо весьма осторожно - их могут использовать другие подпрограммы. А вообще-то предполагается, что в большинстве случаев коллективные таймеры создаются автоматически, по факту вызова функции опроса SysTimer_Pulse(ThePeriod) с постоянным периодом, а потом вовсе не удаляются. Если период является константой, в этом нет необходимости.
   При последующих вызовах функция SysTimer_Pulse(ThePeriod) генерирует события каждый цикл таймера с периодом, заданным аргументом ThePeriod ms. Если очередной цикл таймера еще не наступил, функция возвращает ноль. Если цикл наступил, функция возвращает число полных циклов с данным периодом, прошедших с момента первого вызова таймера с этим периодом. Однако точная привязка циклов ко времени не связана с моментом первого вызова таймера. Моменты смены циклов жестко привязаны к шкале астрономического времени, так что, например, смена цикла секундного таймера происходит каждую целую секунду, начиная с Рождества Христова. Если первый вызов коллективного таймера произошел посередине цикла, то началом цикла все равно будет считаться ближайшее "круглое" число, составляющее целое (без дробной части) число периодов. Эту особенность надо учитывать при использовании коллективных таймеров. При этом, надо заметить, никакого накопления ошибок привязки к реальному времени из-за задержки исполнения циклов не происходит - шкала времени в этом смысле совершенно жесткая. В то же время, если программа долго задержалась с исполнением кода, возможны пропуски просроченных циклов.
   В отличие от функции tm_event, которая сбрасывает событие при вызове, функцию SysTimer_Pulse(ThePeriod) можно вызывать многократно, причем она будет возвращать одно и то же значение в пределах данного вызова программы (RunCount). Это как раз и позволяет использовать один и тот же таймер коллективно, для нескольких независимых подпрограмм, которым, возможно, нужен один и тот же период опроса. Все подпрограммы с одинаковым периодом опроса будут использовать один таймер и вызываться одновременно, в том смысле, что они будут исполняться в одном и том же цикле опроса программы RunCount.
   Фактическое изменение цикла системного таймера SysTimer_Pulse происходит в процедуре SysTimer_Poll, вызываемой, среди прочих, в цикле опроса программы PollApplication, где-то в начале исполнения данного прогона программы (RunCount). После этого, в рамках данного прогона, значение цикла (или ноль если он не наступил) не меняется до следующего RunCount. Таким образом, функция фиксирует факт наступления очередного цикла и дает возможность всем клиентам, вызывающим SysTimer_Pulse, исполнить свои периодические задачи.
   Этот системный таймер коллективного пользования является хорошим дополнением к уже имеющимся таймерам других типов, включая индивидуальные таймеры, работающие на переменных и вызовах mSecNow, интервальные таймеры tm_event, а также календарные таймеры &CronSrv. Одним из главных достоинств коллективного таймера является его удобство - не надо заводить переменные, инициализировать и освобождать их - все делается автоматически, по факту вызова SysTimer_Pulse. Коллективные таймеры имеют жесткую привязку к шкале времени и не накапливают ошибок - это тоже хорошо. Однако в них есть и свои минусы - например, нельзя менять период таймера, останавливать и запускать его. Предполагается, что период таймера является константой, а для управления исполнением служат какие-то другие переменные или условия. Если это не подходит - используйте альтернативные типы таймеров, упомянутые выше. Создание коллективных таймеров никак не отменяет другие типы таймеров - используйте каждый тип, когда это удобно.
   Можно также сказать, что коллективные таймеры ориентированы на задачи опроса состояния внешних по отношению к программе объектов, когда привязка к моменту старта или к состоянию внутренних переменных программы не важна. Коллективные таймеры хорошо подходят для опроса состояния сети, файлов, каналов, внешних процессов и т.д.

Пример:

   if SysTimer_Pulse(1000)>0 then begin
    writeln('Этот вызов будет происходить каждые 1000 ms.');
    writeln('Текущий № вызова равен ',SysTimer_Pulse(1000));
   end;
   if SysTimer_Pulse(5000)>0 then begin
    writeln('Этот вызов будет происходить каждые 5000 ms.');
    writeln('Текущий № вызова равен ',SysTimer_Pulse(5000));
   end;
   if SysTimer_Pulse(1000)>0 then begin
    writeln('Этот вызов тоже происходит каждые 1000 ms.');
    writeln('Текущий № вызова равен ',SysTimer_Pulse(1000));
   end;
   

procedure ClearStdTimes;
procedure InitStdTimes;
procedure FreeStdTimes;
procedure PollStdTimes;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdTexts

обеспечивает работу с DAQ-текстами, в рамках шаблона прикладной программы template.pas.

_con_StdTexts.inc - файл описания констант модуля StdTexts.

Пока файл пуст.

_var_StdTexts.inc - файл описания переменных модуля StdTexts.

Пока файл пуст.

_fun_StdTexts.inc - файл описания процедур и функций модуля StdTexts.

procedure ClearText(aText:Integer);
- очищает текст, то есть удаляет все строки текста. Сам объект-текст остается.

function TextAppendText(t,s:Integer):Integer;
- добавляет содержимое текста источника s (source) в конец целевого текста t (target) и возвращает ссылку целевого текста t. Применяется для добавления к тексту содержимого другого текста.

function TextAssignText(t,s:Integer):Integer;
- назначает содержимое текста источника s (source) целевому тексту t (target) и возвращает ссылку целевого текста t. Применяется для присвоения тексту содержимого другого текста.

function TextAppendString(t:Integer; s:String):Integer;
- добавляет текст из (длинной) строки источника s (source) в конец целевого текста t (target) и возвращает ссылку целевого текста t. Применяется для добавления к тексту содержимого другого текста, заданного длинной строкой с разделителями строк EOL.

function TextAssignString(t:Integer; s:String):Integer;
- назначает содержимое текста источниука s (source) из (длинной) строки целевому тексту t (target) и возвращает ссылку целевого текста t. Применяется для присвоения тексту содержимого другого текста, заданного длинной строкой с разделителями строк EOL.

function StringToText(s:String):Integer;
- преобразует (длинную) строку во вновь созданный объект-текст, возвращает ссылку на этот объект. После использования не забывайте удалять объект текста вызовом text_free.

function TextToString(t:Integer):String;
- преобразует объект-текст в (длинную) строку c разделителями crlf.

function GetTextLength(t:Integer):Integer;
- возвращает полную длину текста, то есть сумму длин всех строк и разделителей строк.

function IsEmptyText(aText:Integer):Boolean;
- проверяет, пустой ли текст, то есть есть ли в нем строки и символы, отличные от пробелов.

function GetStringVar(Text:Integer; Name:String; var Value:String):Boolean;
- ищет переменную с именем (Name) и считывает значение (Value) из текста (Text), содержащего строки вида "Name=Value". Если строка "Name=Value" не найдена в тексте, возвращается пустое значение Value и результат false. Если в тексте несколько строк с одинаковыми именами, находится первое вхождение выражения "Name=Value". При анализе считается, что перед и после знака равно не должно быть пробелов, как принято в INI-файлах и списках TStringList. Это значит, например, что выражение "Name = Value" с пробелами не подходит. Функция может применяться, например, для анализа текстов при чтении файлов инициализации (INI) или в других случаях, когда данные хранятся в списке "Name=Value".

function Text_StrReplace(txt:Integer; a,b:String; Flags:Integer):Integer;
- делает замену строки a на b в каждой строке текста txt, с флагами замены Flags. Функция всегда возвращает txt. См. также функцию StrReplace.

function Text_IndexOf(txt:Integer; s:String):Integer;
- ищет в тексте (txt) строку (s) и возвращает её индекс (начиная с 0), либо возвращает -1, если строка не найдена. Поиск выполняется независимо от регистра символов, см. функцию IsSameText.

function Text_Remove(txt:Integer; s:String):Integer;
- ищет в тексте (txt) строку (s) и удаляет её из текста (если строка была найдена). Возвращает индекс строки (начиная с 0), либо возвращает -1, если строка не найдена.

procedure ClearStdTexts;
procedure InitStdTexts;
procedure FreeStdTexts;
procedure PollStdTexts;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdTools

педоставляет набор служебных функций, в рамках шаблона прикладной программы template.pas.

_con_StdTools.inc - файл описания констант модуля StdTools.

Пока файл пуст.

_var_StdTools.inc - файл описания переменных модуля StdTools.

FreeAndZero_Verbosity : Integer; - переменная режима печати для процедуры FreeAndZero.

_fun_StdTools.inc - файл описания процедур и функций модуля StdTools.

procedure FreeAndZero(var ref:Integer);
- универсальная процедура деструктора, т.е. освобождения (free) и обнуления (zero) ссылки объекта (ref). Объект может иметь любой поддерживаемый динамический объектный тип: (Timer, Text, Task, Pipe, Com, Tcp, RegExp, HashList, FSM, DB), который определяется динамически вызовом RefInfo(ref,'Type'). Кроме того, для успешного удаления объект должен принадлежать программе (объяснения см. ниже). В зависимости от определенного таким образом типа вызывается соответсвующий деструктор (tm_free, text_free, task_free, pipe_free, pipe_free, pipe_free, regexp_free, hashlist_free, fsm_free, db_free). Если ссылка объекта имеет другой (квази статический) тип или не указывает на динамический объект, принадлежащий программе, то деструктор не вызывается. После этого ссылка зануляется (т.е. ей присваивается значение 0). Это маркирует тот факт, что ссылка свободна и не указывает на объект. По результатам работы процедуры может выдаваться отладочная печать в консоль.
Переменная FreeAndZero_Verbosity задает тип отладочного вывода и задается набором битов:

  • 0 - нет отладочного вывода в консоль;
  • 1 = Bit[0] - при выводе сообщается ClassName и Name удаляемого объекта;
  • 2 = Bit[1] - печать Problem при попытке удаления объекта неизвестного типа;
  • 4 = Bit[2] - печать Problem при неудачной (false) попытке удаления объекта;
  • 8 = Bit[3] - печать Problem при попытке удаления объекта статического типа;
  • 16 = Bit[4] - печать Success при удачном (true) завершении деструктора объекта;
  • 31 - максимально подробная печать (включает все перечисленные биты);
  • 15 - вывод при наличии проблем (значение по умолчанию);

Здесь следует дать ряд комментариев. Объект удаления может не иметь типа (например, если ссылка недействительна или тип не поддерживается DAQ системой). Объект может иметь (квази) статический тип, если он создан за пределами данной прикладной программы DaqPascal (как, например, устройства или кривые, созданные при загрузке DAQ системы). Статичекие объекты не удаляяются процедурой (так как они не принадлежат программе). Объект может иметь динамический тип - один из тех, которые может в принципе создать программа. Динамические объекты можно удалять, но для успешного удаления объект должен еще принадлежать программе (то есть быть занесенным во внутренний список динамических объектов). Таким образом, объект может быть успешно удален, если он имеет динамический тип и принадлежит программе, т.е. был создан в этой программе одним из конструкторов (tm_new, text_new, task_init, pipe_init, regexp_init, hashlist_init, fsm_new, db_connection). Отладочный вывод может быть нужен чтобы разбираться с этими ситуациями. Для включения отладочного вывода надо при инициализации программы задать ненулевое значение FreeAndZero_Verbosity.

Процедуру FreeAndZero(ref) настоятельно рекомендуется использовать в прикладныых программах, чтобы упорядочить и сделать более безопасной работу со ссылками динамических объектов прикладных программ. Общий прицип состоит в том, что в каждый момент времени ссылка объекта должна быть либо действительной (т.е. указывающей на существующий объект), либо нулевой (что является признаком отсутсвия объекта). Ссылка никогда не должна быть "битой" (поврежденной) или "осиротевшей" (когда объект остается без ссылки). Если создать объект (например, ref:=pipe_init(..);), а затем (в другом месте программы) вызвать деструктор (например bNul(pipe_free(ref));), но забыть при этом очистить ссылку (ref:=0;), то возникает "битая ссылка" (broken link), которая ни на что указывает. Попытка воспользоваться такой "битой" ссылкой может вызвать непредсказуемые последствия. Другой крайностью является случай, когда ссылку занулили перед тем, как удалить объект. Тогда объект остается вовсе без ссылки - "сиротой" (orphan object). Хотя объект остается в памяти и занимает ресурсы, воспользоваться им нельзя из-за потерянной ссылки. Правильное использование FreeAndZero(ref) вместе с конструкторами объектов гарантирует, что ссылка объекта будет корректной в любой момент времени. Это особенно актуально для больших программ (серверов, драйверов), когда конструкторы и деструкторы объектов вызываются в разных частях программы в разное время.
Рекомендуется такая дисциплина работы со ссылками:
   program demo;                                // Демонстрационная программа
   ...
   var ref:Integer;                             // Ссылка объекта
   ...
   procedure ClearApplication;                  // Процедура начальной очистки
   begin
    ref:=0;                                     // Начальное обнуление ссылки
    ...
   end;
   ...
   procedure InitApplication;                   // Инициализация программы
   begin
    ref:=pipe_init('tcp port 123 ...');         // Вызов какого-то конструктора объекта
    ...
   end;
   ...
   procedure FreeApplication;                   // Завершение программы
   begin
    FreeAndZero(ref);                           // Удаление и обнуление объекта
   end;

   procedure PollApplication;                   // Процедура опроса
   begin
    if (ref<>0) then ... use(ref) ...           // Использование объекта, если он существует
    ...
   end;
   ...
   procedure ResetObject;                       // Если надо "пересоздать" объект
   begin
    FreeAndNil(ref);                            // Удаляем старый объект, еcли он был,
    ref:=pipe_init(...);                        // и только затем создаем новый объект.
   end;
   ...
   procedure DeleteObject;                      // Если надо удалить объект
   begin
    FreeAndZero(ref);                           // Удаляем объект, зануляем ссылку
   end;                                         // Объекта больше нет, ссылки тоже
   ...
   
В данном примере программы конструкторы и универсальный деструктор объектов FreeAndZero вызываются так, чтобы гарантировать корректность ссылки объекта в любой момент времени, незаввисимо от того, существует объект или нет (еще не создан или уже удален). Такая дисциплина работы со ссылками повышает надежность работы прикладных программ.

function iGetBitState(Data,BitNum:Integer):Boolean;
- функция чтения в данных Data отдельного бита (номер BitNum, начиная с 0). Возвращает логическое состояние соответствующего бита. В принципе, это улучшенный аналог вызова IsBit(Data,BitNum).

function iSetBitState(Data,BitNum:Integer; SetOn:Boolean):Integer;
- функция установки в данных Data отдельного бита (номер BitNum, начиная с 0) в соответствии со значением SetOn. Функция полезна при работе с целыми числами как с массивами статусных битов, для установки значений отдельных битов. Для проверки битов можно использовать iGetBitState(Data,BitNum). Например:

    iSetBitState(4,0,true)  = 5 (взяли 4 и установили 0-й бит в 1)
    iSetBitState(6,1,false) = 4 (взяли 6 и установили 1-й бит в 0)
   

function iGetTagBitState(tag,BitNum:Integer):Boolean;
- функция чтения в целочисленном теге данных tag отдельного бита (номер BitNum, начиная с 0). Возвращает логическое состояние соответствующего бита. В принципе, это улучшенный аналог вызова IsBit(iGetTag(tag),BitNum).

function iSetTagBitState(tag,BitNum:Integer; SetOn:Boolean):Boolean;
- функция установки в целочисленном теге данных tag отдельного бита (номер BitNum, начиная с 0) в соответствии со значением SetOn. Функция полезна при работе с целыми числами как с массивами статусных битов, для установки значений отдельных битов в целочисленных тегах. Для проверки битов можно использовать iGetTagBitState(tag,BitNum). Например:

    bNul(iSetTagBitState(tag,0,true));  // установили 0-й бит тега в 1
    bNul(iSetTagBitState(tag,1,false)); // сбросили 1-й бит тега в 0
   

function iGetBit(data,i:Integer):Integer;
- функция чтения в данных data отдельного бита (номер i, начиная с 0). Возвращает 0 или 1 в соответствии со значением бита. В принципе, это улучшенный аналог вызова Ord(IsBit(data,i)).

function iSetBit(data,i,v:Integer):Integer;
- функция установки в данных data отдельного бита (номер i, начиная с 0) в соответствии со значением v (которое должно быть 0 или 1). Функция полезна при работе с целыми числами как с массивами статусных битов, для установки значений отдельных битов. Для проверки битов можно использовать isbit(data,i). Например:

    iSetBit(4,0,1) = 5 (взяли 4 и установили 0-й бит в 1)
    iSetBit(6,1,0) = 4 (взяли 6 и установили 1-й бит в 0)
   

function iSetTagBit(tag,i,v:Integer):Boolean;
- функция установки в целочисленном теге данных tag отдельного бита (номер i, начиная с 0) в соответствии со значением v (которое должно быть 0 или 1). Функция полезна при работе с целыми числами как с массивами статусных битов, для установки значений отдельных битов в целочисленных тегах. Для проверки битов можно использовать isbit(data,i). Например:

    bNul(iSetTagBit(tag,0,1)); // установили 0-й бит тега в 1
    bNul(iSetTagBit(tag,1,0)); // установили 1-й бит тега в 0
   

function iSetTagXor(tag,XorMask:Integer):Boolean;
- функция операции XOR в целочисленном теге данных tag в соответствии со значением XorMask. Фактически в значении тега tag инвертируются те биты, которые установлены в XorMask, остальные биты не меняются. Функция полезна при работе с целыми числами как с массивами статусных битов, для инверсии значений битов в целочисленных тегах. Для проверки битов можно использовать isbit(data,i). Например:

    bNul(iSetTagXor(tag,1)); // инвертировали 0-й бит тега
    bNul(iSetTagXor(tag,4)); // инвертировали 2-й бит тега
   

function invCalibr(n:Integer; y,z,a,b:Real):Real;
- функция обратной калибровки по отношению к calibr(n,x,z). Параметры a,b задают диапазон для возможных значений x (a<=x<=b). При удачном расчете функция возвратит такое значение x, что для него calibr(n,x,z)=y. При неудачном расчете (неверные входные параметры) функция возвратит _NaN. Функция может оказаться ресурсоемкой (она связана с решением уравнения итерационным методом), поэтому вызавать ее очень часто не стоит.

procedure RunStartupScript;
procedure RunFinallyScript;
функции служат для выполнения стартового и завершающего консольного скрипта. Под консольным скриптом понимается набор консольных команд данного устройства, который считывается из заданной секции файла конфигурации и помещается в консоль StdIn для исполнения.
RunStartupScript следует вызывать в конце InitApplication.
RunFinallyScript следует вызывать в начале FreeApplication.
В конфигурации устройства должно быть примерно следующее:

     [DeviceList]
     &Demo = device software program
     [&Demo]
     ...
     StartupScript=[&Demo.StartupScript]
     FinallyScript=[&Demo.FinallyScript]
     ...
     []
     [&Demo.StartupScript]
     @StartupCommand 1
     @StartupCommand 2
     ...
     []
     [&Demo.FinallyScript]
     @FinallyCommand 1
     @FinallyCommand 2
     ...
     []
   

procedure ClearStdTools;
procedure InitStdTools;
procedure FreeStdTools;
procedure PollStdTools;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdTasks

педоставляет набор функций для работы с DAQ-задачами и процессами OS, в рамках шаблона прикладной программы template.pas.

_con_StdTasks.inc - файл описания констант модуля StdTasks.

Пока файл пуст.

_var_StdTasks.inc - файл описания переменных модуля StdTasks.

Переменная CurrentProcessId в начале работы инициализируется в номер (PID, process id) текущего процесса. Это чтобы он всегда был под рукой, и быстро.

_fun_StdTasks.inc - файл описания процедур и функций модуля StdTasks.

function Task_Readln(tid:Integer; var Line,Buff:String):Boolean;
- функция чтения строки (с разделителем EOL) из анонимного канала связи задачи tid. Возвращается True, если строка успешно прочитана в переменную Line. Если канал пуст или с задачей что-то не в порядке, возвращается False и пустая Line. Буфер Buff должен быть глобальной переменной, определенной в основной программе (это очень важно), поскольку она должна сохранять значение между асихронными вызовами. Кроме того, буфер должен обязательно очищаться при старте и завершении задачи tid, иначе в буфере в начальный момент будет "мусор". Надо также иметь в виду, что длина строк не должна превышать 16K (16384), иначе сгенерируется ошибка и буфер будут очищен. Функция ориентирована на взаимодействие с консольными задачами через каналы стандартного ввода-вывода, переназначенные анонимный канал (для этого надо при запуске указать ненулевой размер task_ctrl(tid,'StdInPipeSize='..) и task_ctrl(tid,'StdOutPipeSize='..)).

function ExecCmdWait(ExeFile,CmdLine,Priority,OutFile:String; Display,TimeOut:Integer):Boolean;
- запуск процесса с исполняемым файлом ExeFile, (необязательными) параметрами командной строки CmdLine, (необязательным) приоритетом процесса (Idle,Low,Normal,Higher,High,RealTime), с (необязательным) переназначением стандартного вывода в файл OutFile, с режимом отображения Display (0=Hide,1=Show), и с ожиданием в течение TimeOut ms. Если срок ожидания истек, процесс досрочно "убивается" и возвращается False. Возвращается, только если процесс был успешно запущен и успел закончить работу сам. Функция удобна для (нечастого) вызова коротких по времени (секунды) операций. При более длительной работе надо от использования функции отказаться и организовывать асинхронный опрос состояния запущенного процесса.

procedure CheckBorlndmmDll(ServerExe:String);
Проверяет наличие и копирует (при необходимости) библиотеку borlndmm.dll в каталог программы ServerExe. Используется для уверенности, что консольные задачи, созданные во встроенном компиляторе Delphi, найдут нужные им библиотеки, хранящиеся в папке ресурсов программы.

function PidCounter(exe:String):Integer;
- возвращает число запущенных процессов с именем exe или 0, если их нет. Служит для проверки того, запущен ли интересующий процесс, идентифицируемый коротким (без пути, но с расширением) именем исполняемого файла.
Например, if PidCounter('dns.exe')=0 then Problem('DIM DNS server is not running.')

function RunSysCommand(CmdLine,Params,InText:String; var OutText:String; Timeout:Integer):Integer;
function RunSysCommandAsText(CmdLine,Params,InText:String; var Code:Integer; Timeout:Integer):String;
- функции для вызова команд операционной системы с возвратом кода выхода команды в code и чтением потока вывода stdout команды в OutText. При вызове задается командная строка для выполнения CmdLine, параметры Params для task_ctrl в виде списка name=value с разделителем EOL, входной текст InText для передачи в поток ввода stdin, а также время ожидания ответа Timeout (в миллисекундах). При этом, если команда не завершилась в отведенное время, она завершается принудительно, чтобы не "подвесить" вызывающую программу.
Например:

 function GetOsName:String;
 var s:String; code:Integer;
 begin
  s:='';
  if IsWindows then s:=RunSysCommandAsText('unix detectwindows','','',code,3000);
  if IsUnix then s:=Trim(RunSysCommandAsText('uname -snorm','HomeDir=/tmp'+EOL+'Priority=Normal','',code,3000));
  if IsUnix then s:=s+' '+Trim(RunSysCommandAsText('lsb_release -sd','','',code,3000));
  GetOsName:=Trim(s);
  s:='';
 end;
 // Возвращает примерно такой вывод:
 // Windows:  Windows 10 Enterprise LTSC 2019 -- Version 10.0.17763.5458
 // Linux:    Linux y510p 5.15.0-33-generic x86_64 GNU/Linux Astra Linux CE 2.12.45 (Orel)
   
Функции RunSysCommand, RunSysCommandsText позволяют использовать команды операционной системы, а также программные утилиты или сценарии фактически как внешние функции. При этом, однако, следует учитывать, что вызов является относительно дорогим (минимум несколько миллисекунд) и синхронным (блокирующим), поэтому он может вызывать задержку выполнения программы вплоть до указанного времени ожидания Timeout. Поэтому следует ограничивать использование этих функций теми случаями, когда это действительно необходимо и минимизировать число таких вызовов. Например, допустимо использование функций при инициализации программы или при обработке событий пользователя (по нажатию кнопок), поскольку это происходит (относительно) редко. В то же время использовать эти функции в цикле опроса настоятельно НЕ рекомендуется. Если нужно постоянное взаимодействие с внешней программой, его надо организовывать как асинхронное, через задачи/каналы, с чтением из каналов в цикле опроса.

function ExecuteProcessSafe(cmd,arg,opt:String;timeout:Integer):Integer;
- аналогично функции RunSysCommand, функция ExecuteProcessSafe вызывает команду cmd операционной системы с аргументами arg и опциями (параметрами task_ctrl) opt, ожидая выполнения в течение timeout миллисекунд и возвращает код выхода команды (программы). При ошибке (команда не найдена, процесс не запустился или время ожидания превышено) возвращается код -1. Функция ExecuteProcessSafe помечена как безопасная (safe), потому что ограничение по времени предотвращает потенциально возможное "зависание" потока, если запускаемый процесс не уложился в заданное время. Функция ExecuteProcessSafe применяется в тех случаях, когда пользователя не интересует ввод-вывод запускаемой команды, а важен лишь код возврата (результат выполнения). Например:

   if (ExecuteProcessSafe('chmod','644 demo/test.txt','',3000)=0)
   then writeln('Установлены права доступа 644 файла demo/test.txt')
   else writeln('Не удалось установить права доступа файла.');
   

procedure ClearStdTasks;
procedure InitStdTasks;
procedure FreeStdTasks;
procedure PollStdTasks;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdPipes

педоставляет набор функций для работы с DAQ-каналами связи, в рамках шаблона прикладной программы template.pas.

_con_StdPipes.inc - файл описания констант модуля StdPipes.

Константы rs_NotAvail, rs_NoRequest, rs_WaitQueue, rs_WaitAnswer, rs_Answer, rs_TimeOut - знакомые до боли состояния цикла опроса COM- порта, часто используемые при написании драйверов устройств со связью по RS-232/485.

_var_StdPipes.inc - файл описания переменных модуля StdPipes.

Пока файл пуст.

_fun_StdPipes.inc - файл описания процедур и функций модуля StdPipes.

function Pipe_Readln(pip:Integer; var Line,Buff:String):Boolean;
- функция чтения строки (с разделителем EOL) из канала связи pip. Возвращается True, если строка успешно прочитана в переменную Line. Если канал пуст или с задачей что-то не в порядке, возвращается False и пустая Line. Буфер Buff должен быть глобальной переменной, определенной в основной программе (это очень важно), поскольку она должна сохранять значение между асихронными вызовами. Кроме того, буфер должен обязательно очищаться при создании и удалении канала pip, иначе в буфере в начальный момент будет "мусор". Надо также иметь в виду, что длина строк не должна превышать 16K (16384), иначе сгенерируется ошибка и буфер будут очищен. Функция ориентирована на взаимодействие с каналами связи разного типа (COM порты, сокеты, именованные каналы, анонимные каналы запущенных задач).

function Com_Readln(var Line,Buff:String):Boolean;
- работает аналогично предыдущей процедуре, только чтение производится из COM-порта, открытого с помощью comopen.

procedure Com_Writeln(Data:String);
- записывает данные в COM-порт, открытый вызовом comopen, с добавлением CRLF, отображением данных (ViewExp,Trouble) и анализом результата записи. Процедура ориентирована на драйверы RS-232 с ASCII-протоколом.

procedure PurgeComPort;
- полностью очищает буферы ввода-вывода COM-порта, открытого вызовом comopen. Процедура ориентирована на драйверы RS-232.

procedure ClearStdPipes;
procedure InitStdPipes;
procedure FreeStdPipes;
procedure PollStdPipes;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdEdits

предоставляет набор функций для редактирования тегов и параметров с помощью функции edit, в рамках шаблона прикладной программы template.pas.

_con_StdEdits.inc - файл описания констант модуля StdEdits.

Константы
ef_Ready (редактор готов к новому заданию на редактирование),
ef_Done (редактирование закончено, можно читать данные),
ef_Busy (идет редакторование, редактор занят),
ef_Preparing (задание на редактирование в процессе приготовления),
ef_ErrorFound (найдена ошибка редактирования)
- содержат статусные флаги функции editstate, полезные в работе.

Константы
mr_OK = 1 (нажата кнопка Ok),
mr_CANCEL = 2 (нажата кнопка Cancel),
mr_ABORT = 3 (нажата кнопка Abort),
mr_RETRY = 4 (нажата кнопка Retry),
mr_IGNORE = 5 (нажата кнопка Ignore),
mr_YES = 6 (нажата кнопка Yes),
mr_NO = 7 (нажата кнопка No),
mr_CLOSE = 8 (нажата кнопка Close),
mr_HELP = 9 (нажата кнопка Help)
- возможные значения результата edit(?ans 0'), позволяющие анализировать ввод пользователя.

_var_StdEdits.inc - файл описания переменных модуля StdEdits.

Переменная StdEditTagFont
- содержит описание шрифта, используемого для редактирования тегов (StartEditTag).
Считывается из секции [DAQ] EditTagFont.

_fun_StdEdits.inc - файл описания процедур и функций модуля StdEdits.

procedure EditReset;
- процедура сброса редактирования в исходное состояние (EditStateReady) через вызов edit(''). Применяется после завершения редактирования для перехода в режим готовности к новому редактированию.

function EditStateReady:Boolean;
function EditStateDone:Boolean;
function EditStateBusy:Boolean;
function EditStateError:Boolean;
- функции проверки состояния редактирования (EditState).
Состояние EditStateReady (когда editstate = 0 = ef_Ready) указывает на готовность к редактированию. Эта проверка применяется перед началом нового редактирования (например, по нажатию сенсора).
Состояние EditStateDone (когда editstate = 1 = ef_Done) указывает на завершение редактирования. Эта проверка применяется для ожидания завершения редактирования и анализа его результатов. После анализа результатов надо выполнить сброс EditReset в исходное состояние.
Состояние EditStateBusy (когда editstate = 4 or 8 = ef_Busy or ef_Preparing) указывает на незавершенный процесс редактирования. В этом состоянии программа должна ожидать завершения редактирования, т.е. перехода в состояние EditStateDone.
Состояние EditStateError (когда editstate = 8 = ef_ErrorFound) указывает на ошибку редактирования. После обнаружения и обработки ошибки надо выполнить сброс EditReset в исходное состояние.

function EditGetTypes:String;
- функция возвращает edit('?types') - разделенный запятыми список типов диалогов (Warning,YesNo,..,FileOpenDialog,SelectDirectoryDialog). Она используется для проверки корректности ввода задания на редактирование.

function EditGetRequest:String;
function EditGetRequestType:String;
function EditGetRequestName:String;
- функция (через вызов edit('?req')) возвращает запрос (request) редактирования, в который входит тип диалога (type), имя (name) для идентификации диалога, а также другие параметры, разделенные пробелом.

function EditGetLastInputLn:String;
- функция (через вызов edit('?inp ...')) возвращает последнюю введенную строку в буфере ввода (input). Она используется для того, чтобы избежать повторения ввода строк при задании строк подтверждений (confirm) для меню, см. пример.

function EditGetUID(id:String):String;
- функция возвращает "уникальный идентификатор" UID (unique identifier) для использования в качестве идентификаторов диалогов в функции edit. Строка UID создается с помощью идентификатора (id), который должен быть уникальным в рамках данной прикладной программы. К этому идентификатору добавляется префикс, содержащий имя устройства (DevName), чтобы сделать UID уникальным в рамках всей системы, см. пример.

function EditGetWellKnownDevices(list:String):String;
- функция возвращает разделенный запятыми список "хорошо известных устройств" (well known devices), т.е. стандартных серверов, таких как &CronSrv или &DatSrv, который добавляется к заданному списку (list). Функция применяется при создании меню для работы с устройствами, например, для меню перезапуска устройств.

function EditGetAnswer(n:Integer):String;
function EditGetCommand(n:Integer):String;
function EditGetConfirm(n:Integer):String;
function EditGetSetting(n:Integer):String;
function EditGetAnswerCount:Integer;
function EditGetCommandCount:Integer;
function EditGetConfirmCount:Integer;
function EditGetSettingCount:Integer;
function EditGetAnswerText:String;
function EditGetCommandText:String;
function EditGetConfirmText:String;
function EditGetSettingText:String;
- функции извлечения ответа (answer), команды (command), подтверждения (confirm), настроек (setting) из буфера редактирования edit по номеру строки n и соответствующих им счетчиков (count) или всего текста (text).
Например:

   if EditStateDone then begin
    for i:=0 to EditGetAnswerCount-1  do writeln('Answer  ',i,' = ',EditGetAnswer(i));
    for i:=0 to EditGetCommandCount-1 do writeln('Command ',i,' = ',EditGetCommand(i));
    for i:=0 to EditGetConfirmCount-1 do writeln('Confirm ',i,' = ',EditGetConfirm(i));
    for i:=0 to EditGetSettingCount-1 do writeln('Setting ',i,' = ',EditGetSetting(i));
    writeln('Answer:  ',EOL,EditGetAnswerText);
    writeln('Command: ',EOL,EditGetCommandText);
    writeln('Confirm: ',EOL,EditGetConfirmText);
    writeln('Setting: ',EOL,EditGetSettingText);
    writeln('Request: '+EditGetRequest);
    writeln('Type:    '+EditGetRequestType);
    writeln('Name:    '+EditGetRequestName);
   end;
   

function EditGetResultName:String;
function EditGetResultCode:Integer;
- функции проверяют статус завершения (EditStateDone) и результат (Edit('?ans 0')) редактирования и возвращают имя и код (name=code) диалога редактирования, или пустую строку и 0, если редактирование не завершено.

function EditTestResultName(name:String):Boolean;
function EditTestResultCode(code:Integer):Boolean;
- функции проверяют статус завершения (EditStateDone) и результат (Edit('?ans 0')) редактирования на совпадение имени и кода (name=code) диалога редактирования, или возвращают false, если редактирование не завершено.
Например:

     procedure PollApplication;
     begin
      ...
      {
      Edit handling...
      }
      if EditStateDone then begin
       {
       Warning dialog completion.
       }
       if EditTestResultName('Warning') then begin
        if EditTestResultCode(mr_OK) then writeln('Warning: OK button pressed.');
        if EditTestResultCode(mr_CANCEL) then writeln('Warning: Cancel pressed.');
        EditReset;
       end;
       {
       YesNo dialog completion.
       }
       if EditTestResultName('YesNo') then begin
        if EditTestResultCode(mr_YES) then writeln('YesNo: YES button pressed.');
        if EditTestResultCode(mr_NO) then writeln('YesNo: NO button pressed.');
        EditReset;
       end;
       ... etc ...
      end;
      if EditStateDone then begin
       Problem('Unhandled Edit detected!');
       EditReset;
      end else
      if EditStateError then begin
       Problem('Edit error detected!');
       EditReset;
      end;
      ...
     end;
   

function EditGetMenuListSelectedIndex:Integer;
function EditGetMenuListSelectedCommand:String;
function EditGetMenuListSelectedConfirm:String;
- функции используются для чтения номера (index) выбранного пункта меню, соответствующей ему команды (command) и строки подтверждения (confirm). Если пункт меню не выбран или не нажата кнопка
OK, возвращается индекс -1 и пустые строки команды и подтверждения. Предполагается, что диалог edit имеет тип списка меню (MenuList,SelectionList,CheckBoxList) и для каждого пункта меню при создании введена команда и строка подтверждения.

function EditAddLine(line:String):Intgeer;
function EditAddText(lines,prefix,filter:String):Integer;
- функции добавляют в задание редактирования (edit) строку (line) или многострочный текст (lines) с возможным добавлением префикса (prefix) в начале каждой строки текста и фильтра (filter) для пропуска недопустимых строк. Через фильтр (если он задан) проходят только те строки текста, первый символ которых входит в строку фильтра. Функции возвращают 0 при успехе или ненулевое число ошибок, если в тексте обнаружены ошибки.
Например:

    function DemoMenuItems:String;
    begin
     DemoMenuItems:='Казнить'+EOL+'Помиловать'+EOL+'Наградить';
    end;
    ...
    if (ClickSensor='DEMO') then
    if EditStateReady then begin
     if EditAddLine('(Список выбора...')
       +EditAddLine(' Выбрать операцию:')
       +EditAddText(DemoMenuItems,Dump(' '),'')
       +EditAddLine(')MenuList DEMO_MENU 1')>0
     then Problem('Edit Error found.');
    end;
   

function EditAddOpening(line:String):Intgeer;
function EditAddInputLn(line:String):Intgeer;
function EditAddCommand(line:String):Intgeer;
function EditAddConfirm(line:String):Intgeer;
function EditAddSetting(line:String):Intgeer;
function EditAddClosing(typ,id,par:String):Integer;
- функции добавляют в задание редактирования (edit) открывающую строку (opening), строку во входной буфер (inputln = input line), строку команды (command), строку подтверждения (confirm), строку настройки (setting), а также завершающую строку (closing) с указанием типа диалога (typ), идентификатора диалога (id) и других параметров (par). В отличие от других функций edit, при указании строк (line) не надо указывать первый символ ( '(', ' ', '>', '!', '@', ')' ) для указания типа ввода, т.к. эти символы добавляются автоматически в каждой функции.
Данная группа функций задумана в первую очередь для облегчения создания и обработки ввода через меню. При задании списков меню (MenuList, SelectionList, CheckBoxList) надо строго соблюдать равное количество и порядок вводимых элементов (inputln, command, confirm), чтобы после ввода было соблюдено верное соответствие по индексу между пунктами меню (inputln), выполняемыми командами (command) и запросами на подтверждение (confirm). Предполагается, что каждой строке ввода соответствует строка команды и строка запроса подтверждения. После выбора пункта меню и запроса подтверждения (если он не пустой) выполняется соответствующая команда.

Вот пример задания меню выхода (Close) и его обработки:

     {
     Menu CLOSE Starter to start editing.
     }
     procedure MenuCloseStarter;
     var n:Integer;
     begin
      if EditStateReady then begin
       //////////////////////////////////////////
       n:=0+EditAddOpening('Команда "Закрыть"... ');
       n:=n+EditAddInputLn('Что выбираете:');
       //////////////////////////////////////////
       n:=n+EditAddInputLn('Продолжить работу текущего сеанса АСУ');
       n:=n+EditAddConfirm('');
       n:=n+EditAddCommand('@syseval @silent @tooltip text "Желаю успешной работы" preset stdNotify delay 15000');
       //////////////////////////////////////////
       n:=n+EditAddInputLn('Завершить сеанс АСУ и закрыть программу');
       n:=n+EditAddConfirm(EditGetLastInputLn);
       n:=n+EditAddCommand('@Cron @Shutdown Crw Exit');
       //////////////////////////////////////////
       n:=n+EditAddInputLn('Завершить сеанс АСУ и продолжить работу');
       n:=n+EditAddConfirm(EditGetLastInputLn);
       n:=n+EditAddCommand('@Cron @Shutdown Daq Exit');
       //////////////////////////////////////////
       n:=n+EditAddInputLn('Перезагрузить сеанс АСУ и начать заново');
       n:=n+EditAddConfirm(EditGetLastInputLn);
       n:=n+EditAddCommand('@Cron @Shutdown Daq Restart');
       //////////////////////////////////////////
       n:=n+EditAddInputLn('Завершить сеанс Windows');
       n:=n+EditAddConfirm(EditGetLastInputLn);
       n:=n+EditAddCommand('@Cron @Shutdown Win Logout');
       //////////////////////////////////////////
       n:=n+EditAddInputLn('Перезагрузить компьютер');
       n:=n+EditAddConfirm(EditGetLastInputLn);
       n:=n+EditAddCommand('@Cron @Shutdown Win Restart');
       //////////////////////////////////////////
       n:=n+EditAddInputLn('Выключить компьютер');
       n:=n+EditAddConfirm(EditGetLastInputLn);
       n:=n+EditAddCommand('@Cron @Shutdown Win Exit');
       //////////////////////////////////////////
       n:=n+EditAddSetting('@set ListBox.Font Size:16\Style:[Bold]');
       n:=n+EditAddSetting('@set Form.Left 530 relative '+Copy(DevName,2)+' PaintBox');
       n:=n+EditAddSetting('@set Form.Top  0   relative '+Copy(DevName,2)+' PaintBox');
       //////////////////////////////////////////
       n:=n+EditAddClosing('MenuList',EditGetUID('MENU_CLOSE'),'');
       if (n>0) then Problem('Error initializing MenuList!');
      end else Problem('Cannot edit right now!');
     end;
     {
     Menu CLOSE Handler to handle editing.
     }
     procedure MenuCloseHandler;
     begin
      EditMenuDefaultHandler(EditGetUID('MENU_CLOSE'));
     end;
     ...
     procedure PollApplication;
     begin
      ...
      {
      Handle clicks...
      }
      if (ClickButton=VK_LBUTTON) then begin
       if IsSameText(ClickSensor,'MAIN.CMD.CLOSE') then begin
        bNul(Voice(snd_Click));
        MenuCloseStarter;
       end;
       ...
      end;
      ...
      {
      Edit tags...
      }
      if EditStateDone then begin
       {
       Menu CLOSE.
       }
       MenuCloseHandler;
       ...
      end;
      if EditStateDone then begin
       Problem('Unhandled edit detected!');
       EditReset;
      end;
      if EditStateError then begin
       Problem('Dialog error detected!');
       EditReset;
      end;
     end;
     ...
   

procedure EditStartConfirmCommand(Msg,Id,Cmd,Params:String);
- процедура инициирует диалог (YesNo) подтверждения (confirm) с текстом сообщения (Msg), идентификатором (Id), командой (Cmd) для последующего выполнения и параметрами настройки (Params). Текст сообщения может быть многострочным с разделителем EOL. Эта процедура применяется при обработке списков меню (MenuList,SelectionList,CheckBoxList) для того, чтобы (в случае необходимости подтверждения) подтвердить выполнение выбранной команды, как показано в примере.

procedure EditMenuDefaultHandler(id:String);
- процедура стандартной (default) обработки (handler) при редактировании (edit) меню (menu) с идентификатором (id).
Предполагается, что меню подготовлено по следующим правилам:

  1. Для каждой введенной строки списка меню (EditAddInputLn) должна быть задана строка подтверждения (EditAddConfirm) и строка команды (EditAddCommand).
  2. Непустая строка подтверждения вызывает диалог для подтверждения выполнения выбранной команды. Строка подтверждения может быть пустой, при этом выбранная команда выполняется сразу.
  3. Команды должны соответствовать шаблону консольных команд (@cmd ...). При выборе пункта меню и подтверждения (если оно не пустое) команды посылаются в консоль текущего устройства. Поэтому все используемые в меню команды должны корректно обрабатываться в консольном обработчике.
  4. Идентификатор меню (id) должен совпадать с тем, который использовался при создании меню.
Есть хороший пример использования этой функции.

procedure Warning(msg:String);
procedure InfoBox(msg:String);
- процедуры отображают окошко с сообщениями предупреждающего характера (Warning) или информационного характера (InfoBox).

function SetFormUnderSensorLeftBottom(aClickParams:String);
- функция вычисляет параметры для вызова StartEditTagEx, для того, чтобы поместить окно редактирования тега под сенсором.
Аргумент функции берется из вызова ClickParams(''), откуда извлекается имя окна и координаты нажатого сенсора.
Например:

   // Редактирование с указанием координат окна под сенсором и шрифта
   StartEditTagEx(tag,'Введите значение',SetFormUnderSensorLeftBottom(ClickParams(''))
                                      +'@set StringGrid.Font Name:PT_Mono\Size:14\Color:Black\Style:[Bold]');
   

procedure WinSelectUnderClickSensor(win,click,opt:String);
- функция рисует (WinDraw) и активизирует (WinSelect) окно win, располагая его под нажатым (click) сенсором, с опциями opt. Эта функция позволяет, например, вызывать окно мнемосхемы непосредственно под кнопкой вызова этой мнемосхемы. Параметры нажатого сенсора (click) берутся из ClickParams(''). Например:

   if (ClickSensor='DEMO') then WinSelectUnderClickSensor('DEMO.WINDOW',ClickParams(''),'-Left,-Top');
   
За счет опций можно задавать фиксированное ('-Left,-Top') или свободное ('+Left,+Top') положение вызываемого окна.

procedure StartEditTagStrEx(tag:Integer; Caption,Data,Params:String);
procedure StartEditTagStr(tag:Integer; Caption,Data:String);
procedure StartEditTagEx(tag:Integer; Caption,Params:String);
procedure StartEditTag(tag:Integer; Caption:String);
- процедуры начинают редактирование тега tag с заголовком Caption. Процедура StartEditTagStr берет в качестве начального значения для редактирования данные Data, а процедура StartEditTag использует текущее значение тега. Расширенные (Ex = extended) версии этих процедур используют параметры (Params) для задания дополнительных параметров редактирования для функции edit (например, для задания шрифта или координат окна). Например:

   // Обычное редактирование
   StartEditTag(tag,'Введите значение');

   // Редактирование с явным заданием шрифта через параметр
   StartEditTagEx(tag,'Введите значение','@set StringGrid.Font Name:PT_Mono\Size:14\Color:Black\Style:[Bold]');

   // Редактирование с явным заданием шрифта и координат через параметр
   StartEditTagEx(tag,'Введите значение',
    '@set StringGrid.Font Name:PT_Mono\Size:14\Color:Black\Style:[Bold]'+EOL+
    '@set Form.Left 200 relative DemoEdit PaintBox'+EOL+
    '@set Form.Top 10 relative DemoEdit PaintBox');

   // Редактирование с заданием шрифта через переменную и восстановлением стандартного значения
   StdEditTagFont:='Name:PT_Mono\Size:14\Color:Red\Style:[Bold]';
   StartEditTag(tag,'Введите значение');
   StdEditTagFont:=ReadIni('[DAQ] EditTagFont');
   
Смотрите также DEMO_EDIT.

function CheckEditTag(tag:Integer; var newValue:String):Boolean;
- функция проверяет статус редактирования тега. Если редактирование тега было начато и успешно закончено, функция вернет True и отредактированные данные в строке newvalue. Смотрите также DEMO_EDIT.

procedure CheckEditTagUpdate(tag:Integer; minValue,maxValue:Real);
- процедура вызывает CheckEditTag, и если редактирование успешно завершено, обновляет тег с помощью вызова UpdateTag. Если тег числовой, присвоение тегу нового значения происходит с проверкой диапазона. Смотрите также DEMO_EDIT.

function VkbdEditTag(tag,Lang,Shift:Integer; Lab:String):Integer;
- вызывает сервер &VkbdSrv для редактирования тега. Комментировать не буду - смотрите DEMO_VKBD.

procedure WarningEx(Msg,Id,Cmd,Params:String);
procedure InfoBoxEx(Msg,Id,Cmd,Params:String);
- процедуры отображают окошко с сообщениями (Msg) предупреждающего характера (Warning) или информационного характера (InfoBox), при этом можно указывать идентификатор редактирования (Id), команду по кнопке Ok или Yes (Cmd), а также параметры редактирования (Params). Процедуры основаны на вызове MessageBoxDialogEx, при этом обработка выполняется с помощью EditMessageBoxDialogExDefaultHandler(id).

procedure MessageBoxDialogEx(DialogType,Msg,Id,Cmd,Params:String);
procedure EditMessageBoxDialogExDefaultHandler(id:String);
- процедуры для организации диалогов типа MessageBox с заданным типом диалога (DialogType=YesNo,YesNoCancel,Information,Warning,Error). MessageBoxDialogEx отображает окошко с сообщением (Msg) заданного типа (DialogType), при этом можно указывать идентификатор редактирования (Id), команду по кнопке Ok или Yes (Cmd), а также параметры редактирования (Params). Обработка выполняется с помощью EditMessageBoxDialogExDefaultHandler(Id). Например:

  Вызов окна диалога:
    MessageBoxDialogEx('Information','Скажи Привет...','DemoInfoBox','@tooltip text "Привет, мир."',SetFormUnderSensorLeftBottom(ClickParams('')));
  Обработка результата:
    EditMessageBoxDialogExDefaultHandler('DemoInfoBox');

  В результате будет открыт диалог 'Information' с текстом "Скажи Привет...".
  Диалог открывается под нажатым сенсором (вызов SetFormUnderSensorLeftBottom).
  Если в диалоге нажать кнопку OK, будет выполнена команда @tooltip text "Привет, мир.",
  которая покажет окно со всплывающим сообщением "Привет, мир." в нижней части экрана.
  Строка 'DemoInfoBox' служит для внутренней идентификации диалога.
  

procedure ClearStdEdits;
procedure InitStdEdits;
procedure FreeStdEdits;
procedure PollStdEdits;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdDims

предоставляет набор функций, облегчающих работу с сервером &DimSrv, и позволяющих вести разработку распределенных систем управления на основе технологии DIM в рамках шаблона прикладной программы template.pas.

_con_StdDims.inc - файл описания констант модуля StdDims.

Константа DIM_GuiClickCmnd - содержит строку @DimGuiClick идентификатора команды графического события (GUI click).

_var_StdDims.inc - файл описания переменных модуля StdDims.

Переменная DIM_CryptKind - указание на используемый метод шифрования: 0..9=DEFAULT,BLOWFISH,GOST,RC2,RC4,RC5,RC6,BASE64,HEX,NONE.

Переменная DIM_GuiClickTag - содержит ссылку на тег (типа string), используемый для приемо-передачи событий графического интерфейса (GUI click). Этот тег должен быть явно инициализирован прикладной программой и связан с командным сервисом DIM с помощью процедуры DIM_GuiClickInit.

Переменная DIM_GuiClickFallBackModeTag - содержит ссылку (изначально нулевую) на тег (типа integer), используемый для включения резервного режима работы для приемо-передачи событий графического интерфейса (GUI click). Резервный режим может использоваться, например, при неполадках в сети. После вызова процедуры DIM_GuiClickInit тег инициализируется ссылкой на тег с именем &DimSrv.FallBackMode, если он найден в текущей конфигурации. Тег может также инициализироваться явно, если нужно индивидуальное управление резервным режимом для данного устройства.

Переменная DIM_GuiClickBuff - содержит строковый буфер для временного хранения данных графического события (GUI click).

Переменная DIM_ClientServerMode - содержит флаги декларируемого режима работы DIM: bit[0] = клиент, bit[1] = сервер, т.е. 1=client, 2=server.

_fun_StdDims.inc - файл описания процедур и функций модуля StdDims.

function ShouldRefresh(var upd:Real; val:Real):Integer;
- функция используется для проверки необходимости обновления DIM сервисов (или других объектов, зависящих от значений параметров). Для каждого параметра (обычно тега или кривой) заводится копия её величины upd, хранящая предыдущее значение величины val этого параметра. В качестве величины val для тегов рекомендуется использовать их "отпечаток" GetStampOfTag, а в случае кривых используется либо значение (y), либо время (x) последней точки кривой. ShouldRefresh при вызове проверяет, совпадает ли upd и val. Если совпадает, функция просто вернет 0 (сигнал, что значение не изменилось). Если они отличаются, upd присваивается значение val и возвращается 1. В результате получается компактная запись типа:

   if ShouldRefresh(upd1,rGetTag(tag1))+
      ShouldRefresh(upd2,iGetTag(tag2))>0
   then begin
    DIM_UpdateTag(tag1,''); // Обновить с текущим значением тегов
    Success('Один из тегов изменился, надо обновлять DIM сервис.');
    // Считаем что оба тега входят в сервис, поэтому один вызов DIM_Update
   end;
   
Смотрите также пример DEMO_ALIPHOSCOOL.

function GetStampOfTag(tag:Integer; def:Real):Real;
- функция вычисляет "отпечаток" (stamp) для данного тега tag или возвращает значение def, если тег недопустим. "Отпечаток" тега - это условное числовое значение, указывающее на то, изменился ли тег. Для численных тегов это просто значение, а для строкового тега - это хэш (контрольная сумма). Использование "отпечатка" позволяет унифицировать обновление тегов.
Для целочисленного или вещественного тега его отпечаток совпадает со значением тега (iGetTag/rGetTag). Для строкового тега отпечаток вычисляется с помощью функции HashIndexOf. Функция используется совместно с ShouldRefresh для проверки необходимости обновления DIM сервисов, или других объектов, зависящих от значений тегов. Например:
   if ShouldRefresh(upd1,GetStampOfTag(tag1,0))+
      ShouldRefresh(upd2,GetStampOfTag(tag2,0))>0
   then begin
    DIM_UpdateTag(tag1,''); // Обновить с текущим значением тегов
    Success('Один из тегов изменился, надо обновлять DIM сервис.');
   end;
   
Достоинством GetStampOfTag является то, что он одинаково работает с тегами всех типов, включая строковый, и не требует проверки типа тега (эта проверка делается внутри функции).

function ClickIsLocal:Boolean;
- возвращает true, если текущий "клик" (графическое событие) является локальным, т.е. сгенерировано на локальной системе клавиатурой или мышью. Удаленные события следует обрабатывать отдельно от локальных, т.к. они зачастую предполагают различную реакцию на событие. Например, локальное событие может быть послано от клиента к серверу. Сервер, приняв событие (как удаленное), его исполняет.

function ClickIsRemote:Boolean;
- возвращает true, если текущий "клик" (графическое событие) является удаленным, т.е. сгенерировано на удаленной системе, а затем доставлено по сети и помещено в очередь командой ClickWrite. Удаленные события следует обрабатывать отдельно от локальных, т.к. они зачастую предполагают различную реакцию на событие. Например, локальное событие может быть послано от клиента к серверу. Сервер, приняв событие (как удаленное), его исполняет.

function DIM_Avail:Boolean;
- проверяет доступность DIM (фактически - наличие сервера &DimSrv).

function DIM_GuiAvail:Boolean;
- проверяет доступность графического интерфейса DIM (фактически - наличие сервера &DimSrv и тега DIM_GuiClickTag).

function DIM_GuiClickAvail:Boolean;
- проверяет инициализацию графического интерфейса DIM (фактически - наличие тега DIM_GuiClickTag). см. функцию DIM_GuiClickInit.

function DIM_GuiClickFallBackMode:Integer;
- проверяет режим работы графического интерфейса DIM сервера.
Если функция возвращает 0, это обычный (сетевой) режим работы DIM сервера.
Если функция возвращает -1, DIM сервер отсутствует (тогда используется резервный режим работы).
Если функция возвращает ненулевое значение, DIM сервер использует резервный режим работы (по тегу DIM_GuiClickFallBackModeTag).
см. описание функции DIM_GuiClickSend.

function DIM_IsClientMode:Boolean;
function DIM_IsServerMode:Boolean;
- возвращает декларируемый режим работы устройства - клиент или сервер DIM. Фактически проверяет биты внутренней переменной режима DIM_ClientServerMode (но это может измениться в будущем). Декларируемый режим работы задается через параметры конфигурации DimClientMode=1 или DimServerMode=1. Декларировать режим работы нужно потому, что зачастую клиент и сервер используют один и тот же программный код (хотя он физически может находиться на разных машинах), поэтому нужен способ различать, в каком качестве программа намерена работать в данной конфигурации. Декларирование режима работы еще не означает доступности DIM-сервера, это лишь "декларация о намерениях" программы работать в выбранной роли клиента или сервера. Алгоритмы работы прикладных программ зачастую определяются именно декларируемой ролью, независимо от фактического состояния связи.

procedure DIM_Send(msg:String);
procedure DIM_Post(msg:String);
- посылает сообщение в консоль &DimSrv, без добавления EOL, используя синхронный метод (devsend) или асинхронный (devpost). Это важно - вы должны не забывать сами добавлять EOL при вызове, иначе будут сбои связи. Данные передаются "как есть", без каких-либо преобразований, с необходимыми проверками и фиксацией ошибок. Смотрите также пример DEMO_ALIPHOSCOOL.

procedure DIM_UpdateTag(tag:Integer; data:String);
- процедура служит для обновления DIM-сервиса, связанного с данным тегом. Обновлять сервис можно по любому тегу, входящему в DIM - сервис. Но обычно сервисы идентифицируются и обновляются по первому тегу сервиса, так быстрее идет поиск.

Если в data передается пустая строка, сервер DIM будет обновлять сервис, беря текущие значения тегов обновляемого сервиса (предполагается, что они уже установлены в нужные значения). Недостатком этого метода является то, что промежуточные значения тегов или кривых могут пропадать, ведь между посылкой сигнала обновления и самим обновлением есть зазор по времен, в течение которого тег может измениться, пока сервер занимается поиском и обновлением сервиса. Так что этот метод не годится для "исторических" данных или очередей событий, где важно каждое значение. Этот метод рекомендуется для "медленных" данных, где история не важна.

Если же передается непустая строка data, она рассматривается как двоичный дамп (dump) данных сервиса, преобразуется во внутренний формат (неважно какой) и отсылается серверу &DimSrv. Значение самого тега при этом не меняется, оно вообще не используется, т.к. в этом случае тег служит просто маркером для указания нужного сервиса. Этот метод позволяет отсылать клиентам произвольные данные, вообще не связанные с тегом, а сам тег может быть фиктивным, заведенным только для передачи данных. Конечно, программист, пишущий сервер, должен хорошо знать что делает. Однако при этом появляется возможность передачи данных без потерь - все обновления помещаются в очередь и отсылаются клиентам одно за другим. Этот метод рекомендуется для передачи "исторических" данных или для организации очередей. Таким образом, функция мощная, использовать ее надо с умом, выбрав нужный режим обновления исходя из задачи. Смотрите также пример DEMO_ALIPHOSCOOL. Смотрите также пример DEMO_RDMS.

function DIM_Encrypt(Data,Key:String):String;
function DIM_Decrypt(Data,Key:String):String;
function DIM_EncryptCommand(Data:String):String;
function DIM_DecryptCommand(Data:String):String;
function DIM_EncryptAccess(Data:String):String;
function DIM_DecryptAccess(Data:String):String;
- функции шифрования-дешифрации (encrypt-decrypt). Используются для повышения защищенности DIM-серверов. Для шифрования используется метод DIM_CryptKind: 0=использовать текущий метод (см.crypt_ctrl), 1=Blowfish, 2=Gost, 3=RC2, 4=RC4, 5=RC5, 6=RC6, 7=MIME, 8=HEX, 9=NONE. Три последних метода на самом деле шифрование не используют (зато работают быстро, см. Cryptographer). В случае MIME данные передаются в виде mime_encode, в случае HEX - в виде hex_encode, в случае NONE - открытым текстом, "как есть". Во многих случаях это вполне допустимый вариант. При необходимости защиты рекомендуется использовать RC2..RC6, так как они работают довольно быстро (чего не скажешь о Blowfish,Gost).
DIM_Encrypt,DIM_Decrypt - вспомогательные функции, явно они не используются.
DIM_EncryptCommand,DIM_DecryptCommand - используются для шифрования-дешифрации команд и нужны для скрытия конфиденциальных данных (имени пользователя, пароля и т.д.) при передаче команд DIM-серверу.
DIM_EncryptAccess,DIM_DecryptAccess - используются для шифрования и дешифрации таблицы прав доступа [TrustedUsers].

function GrantAccessDim(Guard,User,Host,IP,MAC:String):Boolean;
- служит для определения прав доступа удаленного клиента. Сама таблица прав доступа находится в секции [TrustedUsers]. При получении запроса клиента данные дешифруются и сравниваются с таблицей прав доступа. Возвращается True, если клиент (User,Host,IP,MAC) имеет право на запрошенный уровень доступа Guard. Смотрите также пример DEMO_ALIPHOSCOOL. Смотрите также пример DEMO_RDMS.

function DIM_GuardParams(Name:String):String;
- служит для чтения текущих параметров безопасности (Name):

   DIM_GuardParams('Guard') - текущий уровень доступа (Root,User,Guest,Deny),
   DIM_GuardParams('User')  - имя пользователя, для идентифиукации пользователей,
   DIM_GuardParams('Host')  - имя сервера, для логической идентификации компьютера,
   DIM_GuardParams('IP')    - IP адрес компьютера для его идентификации по сетевому адресу,
   DIM_GuardParams('MAC')   - MAC адрес сетевой карты для аппаратной идентификации компьютера,
   DIM_GuardParams('')      - список всех перечисленных параметров с именами и разделителями строк.
   


procedure DIM_GuiClickSendToServer(data:String);
- служит для посылки графического события (удаленному) серверу (путем сообщения серверу &DimSrv). Для посылки используется командный сервис, связанный со строковым тегом DIM_GuiClickTag, который должен быть инициализирован процедурой DIM_GuiClickInit и связан с командным сервисом. Данные data для пересылки обычно генерируются функцией DIM_GuiClickCopy, которая копирует текущее локальное событие в строковый буфер (обычно это DIM_GuiClickBuff). Для безопасности данные передаются в зашифрованном виде.

procedure DIM_GuiClickSendLoopBack(data:String);
- служит для посылки графического события локальному серверу в резервном режиме. Работает аналогично DIM_GuiClickSendToServer, только вместо посылки DIM-серверу используется посылка в локальную консоль. Это позволяет работать (локально) при отсутствии DIM-сервера или при недоступной сети.

procedure DIM_GuiClickSend(data:String);
- служит для посылки графического события либо удаленному сетевому серверу (через вызов DIM_GuiClickSendToServer), либо в консоль локального сервера (через вызов DIM_GuiClickSendLoopBack). Выбор способа посылки осуществляется по результатам вызова функций DIM_GuiClickFallBackMode и DIM_IsServerMode. Использование резервного режима работы (FallBackMode) позволяет работать в условиях, когда сервер &DimSrv не запущен или когда сеть недоступна.

function DIM_GuiClickCopy:String;
- служит для копирования (copy) графического события (GUI click) в строковый буфер (обычно DIM_GuiClickBuff). Все поля события считываются из очереди событий и упаковываются в длинную строку текста. Эта строка текста может быть передана серверу для обработки вызовом DIM_GuiClickSend:
    if ClickWhat<>0 then begin                                      // Если было событие
     DIM_GuiClickBuff:=DIM_GuiClickCopy;                            // Прочитать его в буфер
     DIM_GuiClickBuff:=DIM_GuiClickBuff+EOL+'NewValue=123'+EOL;     // При необходимости можно добавить туда что-то еще
     DIM_GuiClickSend(DIM_GuiClickBuff);                            // Переслать буфер серверу
    end;
   
Собственно, переменная DIM_GuiClickBuff введена для того, чтобы иметь свободу обрабатывать "клики", полученные в буфере функцией DIM_GuiClickCopy, перед посылкой серверу командой DIM_GuiClickSend.

function DIM_GuiClickScan(var ClickBuff:String; ParamName:String):String;
- служит для сканирования (scan) буфера (ClickBuff) графического события (GUI click) и извлечения параметра с именем (ParamName). Используется при анализе данных графического события после копирования в строковый буфер.

    btn:=DIM_GuiClickScan(DIM_GuiClickBuff,'Button');  // Прочитать код кнопки       с которой связано событие
    dev:=DIM_GuiClickScan(DIM_GuiClickBuff,'Device');  // Прочитать имя устройства   с которым связано событие
    win:=DIM_GuiClickScan(DIM_GuiClickBuff,'Window');  // Прочитать имя окна         с которым связано событие
    sen:=DIM_GuiClickScan(DIM_GuiClickBuff,'Sensor');  // Прочитать имя сенсора      с которым связано событие
    val:=DIM_GuiClickScan(DIM_GuiClickBuff,'Value');   // Прочитать значение сенсора с которым связано событие
    crv:=DIM_GuiClickScan(DIM_GuiClickBuff,'Curve');   // Прочитать имя кривой       с которой связано событие
    tag:=DIM_GuiClickScan(DIM_GuiClickBuff,'Tag');     // Прочитать имя тега         с которым связано событие
   

function DIM_GuiClickWhat(var ClickBuff:String):Integer;
- служит для проверки удаленного графического события (GUI click) по типу события при анализе данных на стороне сервера. Проверяет права доступа вызовом GrantAccessDim, а затем возвращает значение типа события (ClickWhat). При отказе в доступе или при ошибке возвращает ноль. Функция используется приемником при обработке сообщения @DimGuiClick.

function DIM_GuiClickPack(What,Button,X,Y:Integer; ms:Real; Device,Window,Sensor,Tag,Curve,Value:String):String;
- служит для упаковки данных при генерации искусственных графических событий (например, программно генерируемых нажатий сенсора). Возвращает длинную строку буфера события, в которой упакованы основные данные события (тип события What, кнопка Button, положение X,Y, время ms, устройство Device, окно Window, сенсор Sensor, тег Tag, кривая Curve, значение сенсора Value) и параметры безопасности (Guard,User,Host,IP,MAC).

procedure Dim_GuiConsoleSend(DevName,data:String);
function Dim_GuiConsoleRecv(DevName,ClickBuff:String):String;
- служит для посылки и приема строки данных или команды data в виртуальную консоль устройства с именем DevName.
Функция Dim_GuiConsoleSend используется на стороне передатчика. Строка передаваемой команды data передается в поле Value искусственно скомпонованного события с параметрами: What=MOUSEDOWN, Button=VK_LBUTTON, Device=DevName, Window=Console DevName, Sensor=Input. Передача команд через искусственно сформированное сообщение требуется в том случае, если надо передавать события, которые не имеют конкретной привязки к окнам и сенсорам - например, консольные команды. Вызов Dim_GuiConsoleSend дает защищенный способ передачи команд серверу через тот же механизм, что и события нажатия сенсоров на мнемосхеме.
Функция Dim_GuiConsoleRecv используется на стороне приемника. Кроме имени устройства DevName ей передается строковый буфер события ClickBuff. Если передается пустая строка ClickBuff='', то вместо буфера данные считываются из буфера текущего события (ClickParams). Предполагается, что данные были записаны туда автоматически при обработке сообщения @DimGuiClick после получения данных из сети. Функция Dim_GuiConsoleRecv проверяет поля события (what,button,device,window,sensor) и возвращает команду только если все поля заполнены правильно. Учитывая защиту данных при передаче, это хороший способ безопасно передавать команды и небольшие данные серверу. Однако, учитывая высокие накладные расходы, не следует передавать таким образом большой поток данных. Предполагается, что трафик команд от клиента к серверу небольшой, а основной поток данных генерирует сервер, передавая измеренные данные клиентам.

procedure DIM_GuiClickInit(GuiClickParams:String);
- выполняет инициализацию системы обработки удаленного доступа.
Аргумент GuiClickParams состоит из слов, которые задают список параметров:

  1. Параметр задает имя тега DIM_GuiClickTag (типа string) , который будет отвечать за связь. Его обязательно надо инициализировать, иначе обработка графических команд не будет работать.
  2. Параметр задает имя тега DIM_GuiClickFallBackModeTag (типа integer), который будет отвечать за резервный режим обработки графических событий DIM-сервера. Параметр не обязателен, по умолчанию используется тег &DimSrv.FallBackMode.
Далее, в переменной [Device] TrustedUsers задается секция с описанием прав доступа, по умолчанию [TrustedUsers]. В этой секции задается таблица прав доступа (имеет смысл для сервера), например:
    [TrustedUsers]
    ;guard      user        host            IP          MAC
    *           .           .               .           .
    *           *           *               *           *
    []
    guard = уровень доступа (locked,guest,user,root)
    user  = имя пользователя
    host  = имя компьютера
    IP    = IP адрес
    MAC   = MAC адрес
    .     = текущее значение параметра для сервера
    *     = любое   значение параметра
    Например
    * . * 192.168.0.10 * - разрешить доступ любого уровня для удаленного пользователя, имя которого совпадает
                           с именем пользователя сервера, с IP адресом 192.168.0.10 и любым MAC
   
Кроме того, в переменной [Device] EncryptMethod задается метод шифрования, используемый при пересылке команд из списка (DEFAULT,BLOWFISH,GOST,RC2,RC4,RC5,RC6,BASE64,HEX,NONE), по умолчанию используется BASE64, рекомендуемое значение RC6. Шифрование используется для предотвращения возможности несанкционированной посылки команд серверу.
Также при инициализации считываются переменные [Device] DimServerMode, DimClientMode, которые задают декларируемый режим работы программы в качестве клиента или сервера, их указание обязательно. см. DIM_IsClientMode.

procedure ClearStdDims;
procedure InitStdDims;
procedure FreeStdDims;
procedure PollStdDims;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Рекомендации по написанию DIM приложений


1) Для начала надо включить в конфигурацию сервер &DimSrv:
    [ConfigFileList]                                        ; Добавляем конфигурацию
    ConfigFile = ~~\Resource\DaqSite\Default\DimSrv.cfg     ; Стандартная конфигурация
    []
    [&DimSrv]                                               ; Надо также добавить:
    DIM_TASK = DEMO/SERVER                                  ; Имя задачи для сервера (уникальное в пределах сети)
    DIM_DNS_NODE = .                                        ; Имя DIM DNS - сервера имен DIM. Точка означает localhost.
    []
   
а также надо включить в конфигурацию таблицу прав доступа, например:
    [TrustedUsers]                               ; Секция прав доступа или "доверенных пользователей"
    ;guard  user  host      IP           MAC     ; Параметры: * значит "любой", . значит "текущий"
    *       .     .         .            .       ; Разрешить всё текущему пользователю на сервере
    *       *     *         *            *       ; Разрешить всё всем удаленным клиентам
    root    main  daq-host  192.168.0.1  *       ; Разрешить уровень root пользователю main компьютера daq-host с данным IP
    []
   


2) В каждом устройстве, которое будет выступать в качестве DIM сервера или клиента можно (но необязательно) задать секцию прав доступа (TrustedUsers) и метод шифрования (EncryptMethod=DEFAULT,BLOWFISH,GOST,RC2,RC4,RC5,RC6,BASE64,HEX,NONE). Если эти параметры не указаны, по умолчанию принимается [TrustedUsers] и BASE64. Кроме того, надо задать параметры режима работы DIM: сервер (DimServerMode=1) или клиент (DimClientMode=1). Это нужно для того, чтобы указать программе, в каком качестве он будет работать в данной конфигурации. Как правило, для сокращения объема и во избежание дублирования кода одна и та же программа работает как сервер или клиент, в зависимости от декларируемого режима.
    [DeviceList]
    [&DEMO.CTRL = device software program
    [&DEMO.CTRL]
    TrustedUsers   = [TrustedUsers]         ; Задает секцию прав доступа
    EncryptMethod  = RC6                    ; Задает метод шифрования
    DimServerMode  = 1                      ; Задает режим сервера DIM
    DimClientMode  = 0                      ; Задает режим клиента DIM
    ...
    []
   


3) Для получения возможности обработки графических событий надо завести строковый тег для обмена данными, а в стартовой процедуре вызвать DIM_GuiClickInit() с именем этого тега, например:
    [TagList]
    DEMO.DIMGUICLICK = string * ; The DIM GUI click sensor data
    []
    ...
    procedure InitApplication;
    begin
     DIM_GuiClickInit('DEMO.DIMGUICLICK');
     ...
    end;
   
Эта процедура инициализирует тег DIM_GuiClickTag, используемый в процедурах приемо-передачи событий.

4) Надо завести для графического тега командные сервисы DIM:
    ; Приемник (только на стороне сервера)
    [&DimSrv.ServiceList]                                  ; Имя секции описания сервисов (постоянное)
    DIS_CMND-DEMO.DIMGUICLICK = dis_cmnd DEMO/DIMGUICLICK  ; Декларация приемника, имя сервиса уникально в пределах сети
    [DIS_CMND-DEMO.DIMGUICLICK]                            ; Секция приемника, имя секции уникально в пределах конфигурации
    tag DEMO.DIMGUICLICK                                   ; Тег приемо-передачи графических событий
    devPostMsg &DEMO.CTRL @DIMGUICLICK=%**                 ; Присылать сообщение устройству для обработки
    []
    ; Передатчик (на обеих сторонах)
    [&DimSrv.ServiceList]                                  ; Имя секции описания сервисов (постоянное)
    DIC_CMND-DEMO.DIMGUICLICK = dic_cmnd DEMO/DIMGUICLICK  ; Декларация передатчика, имя сервиса как у приемника
    [DIC_CMND-DEMO.DIMGUICLICK]                            ; Секция передатчика, имя секции уникально в пределах конфигурации
    tag DEMO.DIMGUICLICK                                   ; Тег приемо-передачи графических событий
    []

    dis_cmnd = dim command server - сервис для приема   команд на стороне сервера
    dic_cmnd = dim command client - сервис для передачи команд от клиента к серверу
   
Заметим, что на стороне клиента используется только секция передатчика, а на стороне сервера - обе секции приемо-передачи, т.к. сервер посылает события в сеть DIM и сам же их получает. Это сделано для того, чтобы не дублировать локальный и удаленный GUI.

5) Надо завести другие теги и сервисы (информационные и командные) для приема или передачи измеряемых данных. Например, для передачи данных о времени сервера можно использовать такой сервис:
    ; Передатчик (только на стороне сервера)
    [&DimSrv.ServiceList]                                  ; Имя секции описания сервисов (постоянное)
    DIS_INFO-DEMO.TIME = dis_info DEMO/TIME                ; Декларация передатчика, имя сервиса уникально в пределах сети
    [DIS_INFO-DEMO.TIME]                                   ; Секция передатчика, имя секции уникально в пределах конфигурации
    tag DEMO.TIME                                          ; Тег приемо-передачи нужной информации
    []
    ; Приемник (только на стороне клиента)
    [&DimSrv.ServiceList]                                  ; Имя секции описания сервисов (постоянное)
    DIC_INFO-DEMO.TIME = dic_info DEMO/TIME                ; Декларация приемника, имя сервиса как у передатчика
    [DIC_INFO-DEMO.TIME]                                   ; Секция приемника, имя секции уникально в пределах конфигурации
    tag DEMO.TIME                                          ; Тег приемо-передачи нужной информации
    devPostMsg &DEMO.CTRL @DimTagUpdate=DEMO.TIME          ; Присылать сообщение устройству для обработки
    []

    dis_info = dim information server - сервис для передачи данных от сервера ко клиенту
    dic_info = dim information client - сервис для приема   данных на стороне клиента
   
Сервисы могут включать один или несколько тегов. Если в сервис входит строковый тег, он должен идти последним. Сообщения (devPostMsg или devSendMsg) необязательны, они используются для уведомления клиентских программ о поступлении данных, а также могут передавать сами данные (если нужна очередь событий).

6) В цикле опроса надо поместить примерно такой код (показан схематично):
     var updTest1,updTest2:Real; // Variables uses to update DIM services
     {
     GUI polling.
     }
     procedure GUI_POLL;
     var s:String;
     begin
      DIM_GuiClickBuff:=''; s:='';
      {
      Handle user mouse/keyboard clicks...
      ClickWhat=(cw_Nothing,cw_MouseDown,cw_MouseUp,cw_MouseMove,cw_KeyDown,cw_KeyUp,cw_MouseWheel,...)
      ClickButton=(VK_LBUTTON,VK_RBUTTON,VK_CANCEL,VK_MBUTTON,VK_BACK,VK_TAB,VK_CLEAR,VK_RETURN,...)
      }
      if ClickWhat<>0 then
      repeat
       {
       Copy GUI click to DIM buffer for remote execution.
       }
       DIM_GuiClickBuff:=DIM_GuiClickCopy;
       {
       Handle MouseDown/KeyDown
       }
       if (ClickWhat=cw_MouseDown) or (ClickWhat=cw_KeyDown) then begin
        {
        Handle Left mouse button click
        }
        if (ClickButton=VK_LBUTTON) then begin
         {
         Handle local clicks (works on client side)
         }
         if ClickIsLocal then begin
          {
          Local button click. Send it to DIM Server.
          }
          if ClickTag<>0 then begin
           if (ClickTag=Test1ButtonTag) then DIM_GuiClickSend(DIM_GuiClickBuff+'NewValue='+Str(iXor(iGetTag(ClickTag),1)));
           if (ClickTag=Test2ButtonTag) then DIM_GuiConsoleSend(DevName,'@Test2Button '+Str(iGetTag(ClickTag)));
          end;
          {...etc...}
         end;
         {
         Handle remote clicks (works on server side)
         Handle remote clicks comes from DIM via @DimGuiClick message.
         @DimGuiClick default handler decode and write events to FIFO,
         so we can find it as clicks and can handle it in usual way.
         }
         if ClickIsRemote then begin
          {
          Handle remote console commands...
          }
          s:=Dim_GuiConsoleRecv(DevName,'');
          if Pos('@',s)=1 then DevSendCmdLocal(s);                         // @Test2Button received, execute it ...
          {
          Handle remote sensor clicks...
          }
          if TypeTag(ClickTag)>0 then begin
           s:=ClickParams('NewValue');
           if Length(s)>0 then begin
            if (ClickTag=Test1ButtonTag) then UpdateTag(ClickTag,s,0,1);   // Test1ButtonTag click received with NewValue ...
           end;
          end;
          {...etc...}
         end;
        end;
       end;
      until (ClickRead=0);
      {
      Update services on server side...
      }
      if DIM_IsServerMode then begin
       if ShouldRefresh(updTest1,iGetTag(Test1ButtonTag))>0   // If Test1ButtonTag changed
       then DIM_UpdateTag(Test1ButtonTag,'');                 // then update DIM service
       if ShouldRefresh(updTest2,iGetTag(Test2ButtonTag))>0   // If Test1ButtonTag changed
       then DIM_UpdateTag(Test2ButtonTag,'');                 // then update DIM service
      end;
      { ...etc... }
      DIM_GuiClickBuff:=''; s:='';
     end;
     {...etc...}
     {
     Process data coming from standard input...
     }
     procedure StdIn_Processor(var Data:String);
     var cmd,arg:String; tag:Integer;
     begin
      {
      Handle "@cmd=arg" or "@cmd arg" commands:
      }
      cmd:=''; arg:='';
      if GotCommand(Data,cmd,arg) then begin
       {
       @DimGuiClick - will be handled by StdIn_DefaultHandler(Data,cmd,arg);
       }
       {
       @DimTagUpdate tag
       }
       if IsSameText('@DimTagUpdate',cmd) then begin
        if DIM_IsClientMode and not DIM_IsServerMode then begin
         tag:=FindTag(Trim(arg));
         if tag=Test1ButtonTag then bNul(iSetTag(tag));
         if tag=Test2ButtonTag then bNul(iSetTag(tag));
        end;
       end else
       {
       Handle other commands by default handler...
       }
       StdIn_DefaultHandler(Data,cmd,arg);
      end;
      Data:=''; cmd:=''; arg:='';
     end;
     {...etc...}
   
Здесь видно, что на стороне клиента (в секции ClickIsLocal) локальные нажатия на кнопки (для примера Test1Button, Test2Button) отсылаются серверу двумя разными методами - DIM_GuiClickSend и DIM_GuiConsoleSend. Сервер (в секции ClickIsRemote) принимает события и выполняет какие-то действия. Кроме того, сервер проводит измерения и публикует данные в секции DIM_IsServerMode. А клиент получает данные из сети в секции DIM_IsClientMode с помощью обновления тегов, а также получения сообщений (например, @DimTagUpdate). Несмотря на условность, приведенный схематический код позволяет понять структуру программ приемо-передачи DIM.

Специально следует оговорить некоторые потенциальные трудности при создании распределенных систем. Пожалуй, наиболее логически сложным вопросом является разделение кода клиента и сервера с помощью функций DIM_IsServerMode, DIM_IsClientMode, ClickIsLocal, ClickIsRemote. Дело в том, что клиент и сервер, которые могут находится на разных компьютерах, обязательно имеют общий программный код и общие конфигурационные файлы. Значительная часть кода является общей для клиента и сервера, что не удивительно, учитывая то обстоятельство, что клиент является удаленной копией ("аватаром") сервера и работает с теми же данными. Однако часть работы клиент и сервер выполняют по-разному. Например, сервер выполняет измерения и публикует данные в сети. А клиент берет даннные из сети, не проводя измерений, но должен обновлять свои теги при получении данных из сети. Таким образом, часть клиент-серверного кода надо различать. Для этого различения и служат функции DIM_IsServerMode, DIM_IsClientMode, ClickIsLocal, ClickIsRemote. Они помогают разделить те "ветки" кода, которые работают по-разному на стороне клиента или сервера. При этом прикладной программист всегда должен следить за тем, в какой "ветке" кода он находится - серверной, клиентской или общей. В процессе разработки всегда нужно держать в уме вопрос "в какой ветке кода я сейчас работаю?". Таким образом, разработка клиент-серверных программ требует повышенной дисциплины при кодировании.


Модуль StdWebs

предоставляет набор функций, облегчающих работу с сервером &WebSrv, и позволяющих вести разработку распределенных систем управления на основе Web - технологий в рамках шаблона прикладной программы template.pas.

_con_StdWebs.inc - файл описания констант модуля StdWebs.

Константа HTTP_REQUEST_ACCEPTED_CMD - определяет имя команды HTTP запроса - @HTTP_REQUEST_ACCEPTED. Оно обрабатывается консольном процессоре StdIn_Processor примерно так:

   {
   Example: @HTTP_REQUEST_ACCEPTED=&WEBSRV,63295844422438,1049397,1049374
   }
   if IsSameText(cmd,HTTP_REQUEST_ACCEPTED_CMD) then begin
    WEB_HandleRequest(Data);
    Data:='';
   end else
   ... другие сообщения ...
   
Константы HTTP_REQUEST_OK (HTTP запрос допустим к обработке), HTTP_REQUEST_NO (HTTP запрос не найден), HTTP_REQUEST_BAD (HTTP запрос недопустим) - определяют статус обработки сообщения @HTTP_REQUEST_ACCEPTED, который возвращает функция WEB_CheckRequest.

_var_StdWebs.inc - файл описания переменных модуля StdWebs.

Переменная WEB содержит запись (record), необходимую для разработки Web-серверов. Среди полей записи можно указать следующие.
Sender - ссылка устройства Web-сервера, приславшего HTTP запрос для обработки.
ReqTime - время прихода HTTP запроса, ms.
Request - ссылка на текст, содержащий HTTP запрос.
Reply - ссылка на текст, куда надо помещать ответ на HTTP запрос. Собственно, задачей Web-сервера является корректное заполение этого текста содержимым ответа, то есть текстом HTML-страницы для отсылки клиенту. По принятому соглашению, помещение Dump(0) в конце текста маркирует конец формирования Web-страницы. Это делается вызовом WEB_ReplyEnd. После этого сервер &WebSrv отсылает сформированную страницу клиенту.
TrustedUsers - ссылка на текст, с таблицей прав доступа к сайту.
CryptKind - указание на используемый метод шифрования: 0..9=Current,Blowfish,Gost,RC2,RC4,RC5,RC6,MIME,HEX,NONE
QueryCount - число параметров GET-запроса (Query), передаваемого через строку адреса (URL). Например http://akuryaki-xp/cgi-bin/websrv.cgi?Action=View&Mode=1. Запрос Query отделяется от адреса знаком вопроса, элементы списка запроса разделяются амперсандом & и имеют вид name=value.
QueryItems - ссылка на текст со списком параметров GET-запроса (Query).
CookieCount - число элементов в списке куков (Cookie). Список куков хранит информацию о сеансе связи с сервером (например, пароли или ключи доступа).
CookieItems - сссылка на текст со списком параметров Cookie.
ContentCount - число элементов в списке POST-запроса (Content).
ContentItems - ссылка на текст со списком параметров POST-запроса (Content). Этот список используется при передаче данных HTTP запроса методом POST.
GatewayInterface - версия CGI протокола.
RequestMethod - метод передачи данных (поддерживается GET или POST).
QueryString - строка URL-запроса (Query).
Content - строка POST-запроса (Content).
ContentLength - длина строки POST-запроса (Content).
ContentType - тип POST-запроса (например, application/x-www-form-urlencoded для форм).
ServerName - имя разработкика Web-сервера (например, RITLABS S.R.L.).
ServerPort - порт сервера.
ServerProtocol - тип протокола сервера (например, HTTP/1.1).
ServerSoftware название программы Web-сервера (например, TinyWeb/1.93).
RemoteHost - имя компьютера удаленного клиента, приславшего HTTP-запрос.
RemoteAddr - IP адрес удаленного клиента.
ScriptName - имя CGI-скипта с точки зрения клиента (например, /cgi-bin/websrv.cgi).
PathInfo - короткое имя CGI скрипта (например, /websrv.cgi).
PathTranslated - путь CGI скрипта с точки зрения сервера (например, C:\CRW32EXE\DEMO\DEMO_WEB4DAQ\websrv.cgi).
WebSrvName - имя устройства Web-сервера, приславшего HTTP запрос (обычно &WebSrv).
WebSrvSite - имя Web-сайта.
WebSrvRoot - локальный путь корневого каталога Web-сайта с точки зрения сервера. Сточки зрения клиента это всегда "/".
WebSrvPort - номер порта Web-сервера, обычно 80.
WebSrvIndex - локальный путь индексного файла, с которого начинается просмотр Web-сайта, с точки зрения сервера.
UserName - имя пользователя (для аутентификации).
Password - пароль пользователя (для доступа).
DateTime - строка с текущим временем сервера.
CustomStr - строка дополнительных данных (пока не используется).

Переменная JSON_Buffer служит временным буфером при формировании JSON - ответов на AJAX - запросы. Напрямую она не используется.

Переменная JSON_BufferMax содержит максимальную длину строки при форматировании JSON - ответов на AJAX - запросы.

_fun_StdWebs.inc - файл описания процедур и функций модуля StdWebs.

function WEB_Encrypt(Data,Key:String):String;
function WEB_Decrypt(Data,Key:String):String;
function WEB_EncryptCookie(Data:String):String;
function WEB_DecryptCookie(Data:String):String;
function WEB_EncryptAccess(Data:String):String;
function WEB_DecryptAccess(Data:String):String;
- функции шифрования-дешифрации (encrypt-decrypt). Используются для повышения защищенности создаваемых сайтов. Для шифрования используется метод WEB.CryptKind: 0=использовать текущий метод (см.crypt_ctrl), 1=Blowfish, 2=Gost, 3=RC2, 4=RC4, 5=RC5, 6=RC6, 7=MIME, 8=HEX, 9=NONE. Три последних метода на самом деле шифрование не используют (зато работают быстро, см. Cryptographer). В случае MIME данные передаются с в виде mime_encode, в случае HEX - в виде hex_encode, в случае NONE - открытым текстом, "как есть". Во многих случаях это вполне допустимый вариант. При необходимости защиты рекомендуется использовать RC2..RC6, так как они работают довольно быстро (чего не скажешь о Blowfish,Gost).
WEB_Encrypt,WEB_Decrypt - вспомогательные функции, явно они не используются.
WEB_EncryptCookie,WEB_DecryptCookie - используются для шифрования-дешифрации Cookie и нужны для скрытия конфиденциальных данных (имени пользователя, пароля и т.д.).
WEB_EncryptAccess,WEB_DecryptAccess - используются для шифрования и дешифрации таблицы прав доступа [TrustedUsers].
Смотрите также пример DEMO_WEB4DAQ.

function GrantAccessWeb(User,Host,IP,Password:String):Integer;
- внутренняя (вспомогательная) функция, проверяет права пользователя (User,Host,IP,Password) с помощью таблицы [TrustedUsers]. Возвращает предоставленный уровень доступа (deny,guest,user,root). Напрямую не используется - вместо нее используется WebAccesGranted (а она вызывает GrantAccessWeb внутри себя).

function WebAccessGranted(Mode:Integer):Integer;
- основная функция проверки прав доступа. При mode=0 использует текущие параметры (WEB.UserName, WEB.RemoteHost, WEB.RemoteAddr, WEB.Password) для вызова GrantAccessWeb. Этот случай используется один раз, на этапе прохождения процедуры аутентификации (Login), при получении формы с именем пользователя и паролем. При mode=1 параметры (UserName,Password) извлекаются из Cookie и дешифруются. Этот режим используется для всех страниц после прохождения процедуры аутентификации (Login). Возвращает предоставленный уровень доступа (deny,guest,user,root).
Смотрите также пример DEMO_WEB4DAQ.

procedure WEB_Clear;
- очистка записи web после обработанного HTTP запроса. Вызывается после обработки запроса для подготовки к следующему запросу.

procedure WEB_Init;
procedure WEB_Free;
- внутренние (вспомогательные) процедуры инициализации и завершения. Пользователю не надо их явно вызывать - это делается автоматически, в рамках шаблона прикладной программы template.pas.

procedure WEB_Reply(s:String);
- основная процедура формирования Web-страницы. Записывает строку s в текст WEB.Reply. Строка s должна содержать очередную строку текста формируемого HTML документа.

procedure WEB_ReplyText(t:Integer);
- добавляет к формируемой Web-странице текст t. Бывает полезно, если в формируемый документ надо вставить предварительно подготовленный текст.

procedure WEB_ReplyEnd;
- записывает маркер конца текста: WEB_Reply(Dump(0)); Это служит сигналом того, что документ сформирован и готов к отправке. Поэтому WEB_ReplyEnd должен стоять последним, завершающим вызовом при формировании Web - страницы.

function WEB_StrEscape(s:String);
- функция "маскировки" специальных символов для формирования Web-страницы из "простого текста" (plain text). Используется для вставки в документ фрагментов простого текста, который преобразуется так, что его можно вставлять с помощью WEB.Reply в текст формируемого HTML документа. Заменяет в строке s символы &,<,> на их HTML - представления (&amp;, &lt;, &gt;).

function WEB_StrUnescape(s:String);
- функция отмены "маскировки" специальных символов для формирования "простого текста" (plain text) из Web-страницы. Заменяет в строке s HTML - представления (&amp;, &lt;, &gt;) на символы &,<,>.

procedure WEB_ReplyEscape(s:String);
- записывает в текст WEB.Reply "замаскированную" строку s. Применяется для добавления фрагментов "простого текста" (plain text).

procedure WEB_ReplyTextEscape(t:Integer);
- добавляет к формируемой Web-странице текст t, "маскируя" каждую строку. Применяется для добавления фрагментов "простого текста" (plain text).

procedure WEB_InitHtmlReply;
procedure WEB_InitJsonReply;
- формирует HTTP - заголовки для HTML документов и JSON передач, соответственно. Эти процедуры являются внутренними и явно не используются.

function WEB_GetQueryItem(Name:String; var Item:String):Boolean;
function WEB_GetCookieItem(Name:String; var Item:String):Boolean;
function WEB_GetContentItem(Name:String; var Item:String):Boolean;
- функции извлечения по имени Name параметров Item строки URL-запроса (Query), куков (Cookie), и POST-данных (Content). HTTP использует два способа передачи параметров запроса - GET и POST. При методе GET параметры запроса (Query) передаются в строке адреса (URL). При методе POST параметры запроса передаются в теле запроса (Content). Куки (Cookie) передаются сервером клиенту (Web-браузеру), который потом включает эти куки в каждый запрос к этому сайту. Необходимость Cookie связана с тем, что в HTTP нет постоянного соединения клиент-сервер, каждый запрос независим от других. Так вот в Cookie хранится информация, нужная для поддержания сеанса связи клиент-сервер. Например, в Cookie может храниться имя и пароль пользователя, чтобы сервер мог определить, кто к нему обращается. Если поиск по имени прошел удачно, то в Item записывается значение переменной с запрошенным именем и возвращается True. Если имя не найдено, возвращается False. Пример:

   if WEB_GetQueryItem('Action',s) then writeln('В строке запроса есть пункт Action='+s);
   if WEB_GetCookieItem('UserName',s) then writeln('В куках есть имя пользователя UserName='+s);
   if WEB_GetContentItem('x1',s) then writeln('В теле POST-запроса есть пункт x1='+s);
   

Смотрите также пример DEMO_WEB4DAQ.

function WEB_IsQueryItem(Name,Value:String):Boolean;
function WEB_IsCookieItem(Name,Value:String):Boolean;
function WEB_IsContentItem(Name,Value:String):Boolean;
- функции проверяют, равен ли элемент с именем Name значению Value для строки запроса (Query), куков (Cookie) и тела запроса (Content), соответственно. Применяется, если ожидается какое-то определенное значение параметра при запросе. Например:

   if WEB_IsQueryItem('Action','Login') then WebPageLogin; // При запросе ?Action=Login открываем страницу Login.
   if WEB_IsQueryItem('Action','Plot')  then WebPagePlot;  // При запросе ?Action=Plot  открываем страницу Plot.
   

Смотрите также пример DEMO_WEB4DAQ.

function WEB_IsRequestMethod(InList:String):Boolean;
- проверяет, находится ли тип HTTP запроса в списке InList. Например:

   if WEB_IsRequestMethod('GET') then writeln('Этот HTTP-запрос имеет тип GET.');
   if not WEB_IsRequestMethod('GET,POST') then writeln('Поддерживаются только методы GET и POST.');
   

procedure WEB_CopyJsLibrary(Name:String);
procedure WEB_KillJsLibrary(Name:String);
- процедуры работы с библиотеками скриптов JavaScript. В системном каталоге ресурсов Resource\Tools\JavaScript есть несколько библиотек скриптов, в настоящее время основной библиотекой у нас будет flot. Для использования библиотеки скриптов в Web сервере эти библиотеки должны быть скопированы в каталог запущенной конфигурации, так как для Web сервера доступны только внутренние каталоги запущенной DAQ-системы.
Вызов WEB_CopyJsLibrary('flot') скопирует каталог Crw32exe\Resource\Tools\JavaScript\flot в локальный каталог ..\Utility\JavaScript\flot. После этого библиотека будет доступна на сайте по пути /Utility/JavaScript/flot - именно так этот каталог выглядит с точки зрения удаленного клиента. Этот вызов надо делать при старте системы, чтобы подготовить нужные библиотеки, взяв их копию из ресурсов пакета.
Вызов WEB_KillJsLibrary('flot') напротив удалит локальный каталог ..\Utility\JavaScript\flot, когда он уже не нужен. Этот вызов можно делать при завершении работы, чтобы ненужная больше локальная копия библиотеки не занимала место.
Использование этих процедур позволит не заботиться о наличии библиотек скриптов - они всегда доступны из системных ресурсов пакета. Прикладным системам также не придется заботиться об обновлении библиотек скриптов - они будут браться из текущей версии дистрибутива пакета.
Смотрите также пример DEMO_WEB4DAQ.

procedure WEB_CopyCssLibrary(Name:String);
procedure WEB_KillCssLibrary(Name:String);
- процедуры работы с библиотеками CSS - стилей StyleSheet. В системном каталоге ресурсов Resource\Tools\StyleSheet есть несколько библиотек стилей, в настоящее время основной библиотекой у нас будет alex. Для использования библиотеки стилей в Web сервере эти библиотеки должны быть скопированы в каталог запущенной конфигурации, так как для Web сервера доступны только внутренние каталоги запущенной DAQ-системы.
Вызов WEB_CopyCssLibrary('alex') скопирует каталог Crw32exe\Resource\Tools\StyleSheet\alex в локальный каталог ..\Utility\StyleSheet\alex. После этого библиотека будет доступна на сайте по пути /Utility/StyleSheet/alex - именно так этот каталог выглядит с точки зрения удаленного клиента. Этот вызов надо делать при старте системы, чтобы подготовить нужные библиотеки, взяв их копию из ресурсов.
Вызов WEB_KillCssLibrary('alex') напротив удалит локальный каталог ..\Utility\StyleSheet\alex, когда он уже не нужен. Этот вызов можно делать при завершении работы, чтобы ненужная больше локальная копия библиотеки не занимала место.
Использование этих процедур позволит не заботиться о наличии библиотек стилей - они всегда доступны из системных ресурсов пакета. Прикладным системам также не придется заботиться об обновлении библиотек стилей - они будут браться из текущей версии дистрибутива пакета.
Смотрите также пример DEMO_WEB4DAQ.

function WEB_FormDataPosted:Boolean;
- вернет True, если клиент заполнил форму (поля ввода, кнопки и т.д.) и послал ее серверу. Признаком этого служит метод запроса (POST) и тип содержимого (application/x-www-form-urlencoded). В этом случае Web-серверу надо анализировать Content и извлекать оттуда данные, которые ввел в форму удаленный клиент. Функция применяется при анализе запроса клиента.
Смотрите также пример DEMO_WEB4DAQ.

procedure WEB_MetaContentType(Data:String);
- устанавливает мета-теги типа содержимого (http-equiv="Content-Type") и кодировки символов (charset). Мета-теги должны входить в заголовок документа, поэтому процедуру надо вызывать в блоке <HEAD> ... </HEAD>. Параметр Data, устанавливает MIME-тип (обычно text/html;) содержимого и кодировку (обычно charset=utf-8). Если Data -пустая строка, принимается значение Data='text/html; charset=utf-8'. В результате вставляется мета-тег типа:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
Смотрите также пример DEMO_WEB4DAQ.

procedure WEB_MetaRefresh(Period:Integer; Link:String);
- устанавливает мета-теги кеширования и автоматического обновления (Refresh). Мета-теги должны входить в заголовок документа, поэтому процедуру надо вызывать в блоке <HEAD> ... </HEAD>. Мета-параметры кэширования (отключение кэша) делаются всегда, так как формируются динамические страницы и кэширование не нужно. Если Period>=0, устанавливает период автоматического обновления (мета-тег Refresh) страницы (в секундах). Если Period=-1, автоматическое обновление не устанавливается. Можно указать также Link, если надо через заданное время автоматически перейти на другую страницу.
Смотрите также пример DEMO_WEB4DAQ.

procedure WEB_MetaSetCookie(Name,Data:String);
- устанавливает мета-теги куков (Cookie). Мета-теги должны входить в заголовок документа, поэтому процедуру надо вызывать в блоке <HEAD> ... </HEAD>. Необходимость Cookie связана с тем, что в HTTP нет постоянного соединения клиент-сервер, каждый запрос независим от других. Так вот в Cookie хранится информация, нужная для поддержания сеанса связи клиент-сервер. Например, в Cookie может храниться имя и пароль пользователя, чтобы сервер мог определить, кто к нему обращается. Сервер устанавливает и посылает клиенту куки, чтобы клиент потом включал эти куки в каждый последующий запрос. По этим кукам сервер определяет, кто его вызывает. куки могут содержать дополнительную информацию, нужную серверу для обслуживания запросов клиентов.
Смотрите также пример DEMO_WEB4DAQ.

procedure WEB_LinkStyleSheet(NameCss:String);
- устанавливает связь (Link) с файлом стилей CSS. В настоящее время предлагается всегда использовать /Utility/StyleSheet/alex/default.css. Процедуру надо вызывать в блоке <HEAD> ... </HEAD>.
Смотрите также пример DEMO_WEB4DAQ.

procedure WEB_AddJavaScript(ScriptName:String);
- добавляет ссылку на JavaScript файл. Например, часто будет использоваться библиотека /Utility/JavaScript/flot/jquery.min.js. Обычто вызов процедуры делается в блоке <HEAD> ... </HEAD>.

procedure WEB_NoScriptWarning(msg:String);
- вставляет в документ красочное сообщение, если отключены скрипты JavaScript.

function WEB_CheckRequest(Msg:String):Integer;
- анализирует поступивший через консольное сообщение Msg HTTP запрос и заполняет запись WEB. Возвращает HTTP_REQUEST_OK, если запрос выглядит "правильным", а запись WEB подготовлена к дальнейшей обработке. Возвращает HTTP_REQUEST_NO, если сообщение Msg не содержит ожидаемой команды @HTTP_REQUEST_ACCEPTED. Возвращает HTTP_REQUEST_BAD, если запрос не может быть обработан (поврежден). Пример:

   {
   General procedure to process HTTP request (main web page selector).
   All HTTP request data is already located in WEB record when Processing called.
   User should add HTML page via WEB_Reply(...) and call WEB_ReplyEnd in the end.
   }
   procedure WEB_Processing;
   begin
    if not WEB_IsRequestMethod('Get,Post')
    then WEB_ErrorPage('Bad RequestMethod='+WEB.RequestMethod) else
    if WEB_IsQueryItem('Action','Login') then WEB_LoginPage else
    if WEB_IsQueryItem('Action','Home')  then WEB_HomePage  else
    if WEB_IsQueryItem('Action','View')  then WEB_ViewPage  else
    if WEB_IsQueryItem('Action','Edit')  then WEB_EditPage  else
    if WEB_IsQueryItem('Action','Plot')  then WEB_PlotPage  else
    if WEB_IsQueryItem('Action','Echo')  then WEB_EchoPage  else
    WEB_ErrorPage('Запрошенная функция не существует.');
   end;
   { 
   Handle @HTTP_REQUEST_ACCEPTED=ArgList message.
   }
   procedure WEB_HandleRequest(Msg:String);
   var Status:Integer;
   begin
    Status:=WEB_CheckRequest(Msg);
    if Status=HTTP_REQUEST_OK then begin
     if DebugEcho then WEB_EchoPage else WEB_Processing;
    end else
    if Status=HTTP_REQUEST_BAD then WEB_ErrorPage('Invalid HTTP request.');
    WEB_Clear;
   end;
   

Смотрите также пример DEMO_WEB4DAQ.

function WEB_ReadJsFromPas(txt:Integer;Section:String):Integer;
function WEB_ReadJsFromCfg(txt:Integer;Section:String):Integer;
- функции загрузки текста скрипта JavaScript из секции, расположенной соответственно в Pas - файле исходного текста программы DaqPascal или Cfg - файле конфигурации. Расположение текста JavaScript в секции, помещенной внутрь комментариев (* *) программы DaqPascal, или в секции конфигурационного файла позволяет держать код сервера (DaqPascal) и клиента (JavaScript) в одном месте, что бывает удобно.
Смотрите также пример DEMO_WEB4DAQ.

procedure JSON_Flush;
procedure JSON_Write(Data:String);
procedure JSON_BeginPage;
procedure JSON_EndPage;
procedure JSON_AddKey(Key:String);
procedure JSON_AddKeyStr(Key,Value:String;Separator:Char);
procedure JSON_AddKeyNum(Key:String;Value:Real;Separator:Char);
procedure JSON_AddStr(Value:String;Separator:Char);
procedure JSON_AddNum(Value:Real;Separator:Char);
procedure JSON_BeginArray;
procedure JSON_EndArray(Separator:Char);
procedure JSON_BeginObject;
procedure JSON_EndObject(Separator:Char);
- эти процедуры служат для формирования JSON - ответов на AJAX запросы. AJAX (Asyncrhronous Javascript And XML) - технология, при которой клиентский код на JavaScript сам периодически посылает запросы серверу, обрабатывает ответ и обновляет отдельные элементы документа без перезагрузки всей страницы. Это позволяет резко сократить трафик и избежать "моргания" страниц. Для передачи данных от сервера клиенту в AJAX обычно используется формат JSON (JavaScript Object Notation). Это гибкий формат, позволяющий описать весьма сложные структуры данных. В рамках этого формата существуют 1)значения, 2)пары "ключ": значение, 3){объекты}, 4)[массивы]. В качестве значения могут выступать 1)число, 2)"строка" 3){объект} 4)[массив]. Пара "ключ": значение определяет элемент объекта, доступ к которому осуществляется по ключу. Объекты помечаются фигурными скобками {}. Они содержат список элементов, разделенных запятыми. Элементами служат пары "ключ": значение, доступ к элементам объекта - по именам ключей. Массивы помещаются в квадратные скобки []. Они содержат список безымянных значений, разделенных запятыми. Доступ к элементам массива - по индексу (номеру). Каждый элемент объекта или массива также может содержать значение в виде объекта или массива, так что структура может иметь любой уровень сложности, за счет рекурсии. Наконец, на самом верхнем уровне JSON - страница - это объект, то есть все данные помещаются в фигурные скобки.

   Пример JSON - страницы:
   {
    "SinData": [ "On", "477.855", "-1.602", "1.133", 1, 2, 0.1 ],
    "HostTime": "2012.03.16-19:21:42", "HostDate": "2012.03.16-19:21:43",
    "HostInfo": "AKURYAKI-XP", "AccessId": "Guest"
   }
   Она создана вызовами:
   JSON_BeginPage;                                         //  {
   JSON_AddKey('SinData');                                 //  "SinData":
   JSON_BeginArray;                                        //  [
   JSON_AddStr('On'),_Comma);                              //  "On",
   JSON_AddStr('477.855',_Comma);                          //  "477.855",
   JSON_AddStr('-1.602',_Comma);                           //  "-1.602",
   JSON_AddStr('1.133',_Comma);                            //  "1.133",
   JSON_AddNum(1,_Comma);                                  //  1,
   JSON_AddNum(2,_Comma);                                  //  2,
   JSON_AddNum(0.1,_Space);                                //  0.1
   JSON_EndArray(_Comma);                                  //  ],
   JSON_Flush;                                             //  New line, for nice view
   JSON_AddKeyStr('HostTime','2012.03.16-19:21:42',_Comma);//  "HostTime": "2012.03.16-19:21:42",
   JSON_AddKeyStr('HostDate','2012.03.16-19:21:43',_Comma);//  "HostDate": "2012.03.16-19:21:43",
   JSON_AddKeyStr('HostInfo','AKURYAKI-XP',_Comma);        //  "HostInfo": "AKURYAKI-XP",
   JSON_AddKeyStr('AccessId','Guest',_Space);              //  "AccessId": "Guest"
   JSON_EndPage;                                           //  }

   
При формировании JSON - страницы следует придерживаться таких правил:
  1. Формирование любой JSON - страницы начинается с вызова JSON_BeginPage и завершается вызовом JSON_EndPage. Это обязательное правило.
  2. Для добавления пары ключ:значение с простыми (числовыми или строковыми) данными используются вызовы типа JSON_AddKeyNum('Pi',3.14,Separator) или JSON_AddKeyStr('Name','Alexey',Separator). В качестве Separator берется либо _Comma, если за этим элементом следуют другие элементы объекта или массива, либо _Space, если это последний элемент списка.
  3. Для добавления значений - элементов массива с простыми (числовыми или строковыми) данными используются вызовы типа JSON_AddNum(3.14,Separator) или JSON_AddStr('Alexey',Separator). В качестве Separator берется либо _Comma, если за этим элементом следуют другие элементы объекта или массива, либо _Space, если это последний элемент списка.
  4. Для формирования объектов используется цепочка вызовов: JSON_AddKey('Name'), JSON_BeginObject; ...список элементов объекта... JSON_EndObject(Separator). Если объект является элементом другого объекта, вызов JSON_AddKey обязателен. Если объект является элементом массива, вызов JSON_AddKey должен опускаться. В качестве списка элементов объекта выступают пары ключ:значение, где значением может быть простое число или строка, а также массив или другой объект. В качестве Separator берется либо _Comma, если за этим элементом следуют другие элементы объекта или массива, либо _Space, если это последний элемент списка.
  5. Для формирования массивов используется цепочка вызовов: JSON_AddKey('Name'), JSON_BeginArray; ...список элементов массива... JSON_EndArray(Separator). Если массив является элементом объекта, вызов JSON_AddKey обязателен. Если массив является элементом другого массива, вызов JSON_AddKey должен опускаться. В качестве списка элементов массива выступают значения, которые могут быть может быть простым числом или строкой, объектом или другим массивом. В качестве Separator берется либо _Comma, если за этим элементом следуют другие элементы объекта или массива, либо _Space, если это последний элемент списка.
  6. Вызов JSON_Flush (фактически, перевод строки) может производиться для улучшения "читабельности", но он необязателен.
  7. Вызывать JSON_Write не надо, это вспомогательная функция.
Смотрите также пример DEMO_WEB4DAQ.

procedure ClearStdWebs;
procedure InitStdWebs;
procedure FreeStdWebs;
procedure PollStdWebs;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdDlls

предоставляет набор функций, облегчающих работу с загружаемыми DLL программами (см. DaqDllInit), в рамках шаблона прикладной программы template.pas.

_con_StdDlls.inc - файл описания констант модуля StdDlls.

Пока файл пуст.

_var_StdDlls.inc - файл описания переменных модуля StdDlls.

STD_DLL_REF:Integer;
- переменная содержит ссылку на библиотеку DLL плагина, если она загружена вызовом STD_DLL_INIT, или содержит ноль. Эта переменная используется для стандартного (упрощенного и наиболее часто используемого) механизма загрузки и вызова DLL плагинов.

_fun_StdDlls.inc - файл описания процедур и функций модуля StdDlls.

function DLL_INIT(DllPath:String):Integer;
Загружает DLL-библиотеку, вызывает команду инициализации DAQ_DLL_INIT и возвращает ссылку на библиотеку. Если загрузка и инициализация прошли неуспешно, возвращает ноль. Процедура вызывается пользователем при старте системы.

procedure DLL_FREE(var hDll:Integer);
- вызывает команду завершения библиотеки DAQ_DLL_FREE, освобождает (выгружает) DLL-библиотеку, и зануляет ссылку hDll на библиотеку. То есть выполняет все, что надо для правильного завершения работы с загруженной DLL-библиотекой. Процедура вызывается пользователем при завершении работы системы.

procedure DLL_POLL(hDll:Integer);
- выполняет опрос библиотеки, то есть вызывает команду DAQ_DLL_POLL. Вызывается пользователем в цикле опроса программы.

procedure STD_DLL_INIT(arg:String);
- загружает библиотеку плагина DLL и сохраняет её ссылку в стандартную переменную STD_DLL_REF. Вызывается пользователем при инициализации программы, если требуется стандартная загрузка DLL плагина и стандартная его обработка (вызов плагина в цикле опроса, выгрузка плагина при завершении работы). Аргумент arg задает имя параметра INI файла, содержащее путь файла DLL плагина. Если аргумент пуст, используется параметр DLL_FILE_PATH.

Использование стандартного алгоритма обработку сильно упрощает обслуживание DLL плагина, т.к. в этом случае от пользователя требуется только инициализировать загрузку DLL библиотеки плагина вызовом STD_DLL_INIT(). После этого библиотечный код сам будет вызывать плагин в цикле опроса и освободит (выгрузит) его в конце работы. Главным ограничением стандартного алгоритма DLL плагина является фиксированный алгоритм работы - вызов плагина в каждом цикле опроса, завершение в конце работы. Если требуется другая дисциплина (например, вызов плагина только по событию), следует отказаться от стандартного механизма опроса и использовать явные вызовы DLL_INIT/FREE/POLL.

function STD_DLL_FILE_PATH(arg:String):String;
- вспомогательная функция, которая читает и адаптирует имя файла библиотеки плагина DLL для загрузки. Аргумент arg задает имя параметра INI файла, содержащее путь файла DLL плагина. Если аргумент пуст, используется параметр DLL_FILE_PATH. Функция адаптирует имя файла библиотеки плагина DLL по правилам текущей платформы, чтобы имя стало пригодным для загрузки библиотеки. Обычно эта функция вызывается автоматически при вызове STD_DLL_INIT, поэтому её редко требуется вызывать явно.

procedure STD_DLL_POLL;
- выполняет опрос стандартной библиотеки плагина, то есть вызывает для неё команду DAQ_DLL_POLL. Активизируется после инициализации стандартной библиотеки плагина, т.е. вызова STD_DLL_INIT. Вызывается автоматически в цикле опроса программы. Пользователю не надо вызывать её самостоятельно.

procedure STD_DLL_FREE;
- освобождает (выгружает) стандартную библиотеку плагина STD_DLL_REF и очищает ссылку. Обычно пользователю не надо вызывать её самостоятельно, т.к. она вызывается автоматически при завершении программы. Однако при необходимости досрочной выгрузки библиотеки можно её вызывать.

procedure STD_DLL_CLEAR;
- очищает ссылку стандартной библиотеки плагина STD_DLL_REF. Вызывается автоматически при старте программы. Пользователю не надо вызывать её самостоятельно.

procedure ClearStdDlls;
procedure InitStdDlls;
procedure FreeStdDlls;
procedure PollStdDlls;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdUtils

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

_con_StdUtils.inc - файл описания констант модуля StdUtils.

Пока файл пуст.

_var_StdUtils.inc - файл описания переменных модуля StdUtils.

Пока файл пуст.

_fun_StdUtils.inc - файл описания процедур и функций модуля StdUtils.

procedure WebBrowser(FileName:String);
- открывает Web - браузер для отображения файла (или сайта).

function RmtShareExe(Share,Local,Options:String; TimeOut:Integer):Boolean;
- вызывает утилиту rtmshare.exe, которая публикует локальный каталог (делает доступным в сети). Полный путь локального каталога задается в Local. Сетевой путь опубликованного каталога задается в Share. Опции Options задают права доступа, комментарии и т.д. TimeOut задает максимальное время ожидания результатов работы утилиты. Утилита полезна, если система носит распределенный характер и ей требуется публиковать каталог данных для удаленного доступа.
Подробности можно узнать, выполнив команду rtmshare.exe /?

   Пример:
    RtmShareExe('\\server\test','e:\test','/GRANT Все:READ'
                         +' /GRANT Администраторы:"FULL CONTROL"'
                         +' /GRANT "Опытные пользователи":CHANGE'
                         +' /REMARK:"Test share"');
    RtmShareExe('\\server\test','','/DELETE');
   

procedure UnixWBox(Title,Message:String; WW,FS,TM:Integer);
- открывает окно с заголовком Title, сообщением Message, шириной окна (window width) WW, размером фонта (font size) FS, задержкой на экране (timeout) не более TM секунд. Окно является неблокирующим (немодальным), что вместе с возможностью задания размеров и времени автоматического закрытия делает эту процедуру удобной альтернативой процедуры Warning(...). Основная цель процедуры - вывод ненавязчивых уведомляющих сообщений о наступлении тех или иных событий, без блокировки экрана и обязательного закрытия окна кнопкой. Если оператор ничего не предпринимает, окно закрывается само, по времени. А если оператору надо что-то быстро предпринять, неблокирующее окно удобнее, так как не мешает оператору в доступе к интерфейсу управления. Еще одним достоинством является то, что UnixWBox оставляет сообщение в консоли, то есть журналируется.
Пример:

   UnixWBox("Уведомление","Операция успешно завершена.",600,16,15);
   

procedure ShowTooltip(args:String);
procedure UnixTooltipNotifier(args:String);
- открывает всплывающее окно рядом с системной областью уведомлений с заданным в аргументах args сообщением. Окно является неблокирующим (немодальным), не сбивает текущий фокус ввода, что вместе с возможностью задания размеров и времени автоматического закрытия делает эту процедуру удобной альтернативой процедуры Warning(...). Основная цель процедуры - вывод ненавязчивых уведомляющих сообщений о наступлении тех или иных событий, без блокировки экрана и обязательного закрытия окна кнопкой. Если оператор ничего не предпринимает, окно закрывается само, по времени. А если оператору надо что-то быстро предпринять, неблокирующее окно удобнее, так как не мешает оператору в доступе к интерфейсу управления.
Пример:

   UnixTooltipNotifier('text "Операция успешно завершена." delay 15000 trans 255 bkColor green textColor 0x000000'
                      +' font "Arial Black" fontSize 16 ico notify.ico audio notify.wav');
    или

   ShowTooltip('text "Операция успешно завершена." delay 15000 preset stdSuccess');

   где:
    text "Операция успешно завершена."    - выводимый текст, обязательно в кавычках
    delay 15000                           - автоматическое закрытие через 15000 ms
    trans 255                             - прозрачность (transparency), 0..255
    bkColor green                         - цвет фона по имени (red,green,...) или HEX RGB
    textColor 0x000000                    - цвет текста, HEX RGB, 0xRRGGBB
    font "Arial Black" fontSize 16        - имя фонта и размер, pt
    ico notify.ico                        - имя файла картинки (exe,dll,ico)
    audio notify.wav                      - имя звукового файла (wav)
    preset stdSuccess                     - набор параметров stdSuccess (успешное выполнение)
   
Процедура UnixTooltipNotifier считается устаревшей и сохранена для совместимости. UnixTooltipNotifier отличается от ShowTooltip тем, что UnixTooltipNotifier вызывает команду @run -hide unix tooltip-notifier ... , а ShowTooltip вызывает более короткую и новую команду @tooltip ... . Подробнее см. вывод команды @tooltip.

procedure StdSensorHelpTooltip(msg:String; delay:Integer);
- открывает стандартное всплывающее справочное окно для отображения сообщения msg с задержкой (временем экспозиции) delay миллисекунд. Во всплывающем сообщении также помещается кнопка или ссылка на справку по текущей DAQ системе, взятой по ссылке [DAQ] Help. Эту процедуру рекомендуется применять для отображения справки (обычно по правой кнопке) на сенсорах (отсюда и название процедуры).

 {
 Procedure to show sensor help
 }
 procedure SensorHelp(s:String);
 begin
  StdSensorHelpTooltip(s,15000);
 end;
   

function CustomIniRW(RW:Char; arg:String; mode:Integer):Integer;
- функция считывает (при RW='R') или записывает (при RW='W') группу тегов из/в INI файл, с заданным режимом mode. Эта функция решает задачу загрузки и сохранения настроечных параметров измерительной системы, что бывает необходимо при создании автоматически загружаемых, автономно работающих конфигураций, которые при старте сами загружают параметры настройки из INI файла, а также могут сохранять состояние, по команде оператора или (если надо) автоматически. Обычно делается так. Оператор настраивает нужные ему параметры измерительной системы на данной установке и нажимает кнопку "Save INI", чтобы запомнить это состояние на диске. Это запомненное состояние затем загружается с диска, либо как начальное состояние при старте, либо по команде оператора "Read INI". Это позволяет облегчить конфигурирование сложных систем, содержащих большое число настроечных параметров.

Функция возвращает число прочитанных или записанных тегов, или 0 при какой-либо ошибке.

Параметр RW задает требуемую операцию - прочитать (RW='R') или записать (RW='W') группу тегов из/в INI файл, заданный аргументом arg. Таким образом, одна функция решает обе задачи (чтение и запись параметров).

Параметр arg содержит от 0 до 4 слов (IniFile, CustomSection, TagListSection, BackupDir), задающих, что и куда сохранять.

  1. IniFile - задает имя INI файла, где будут храниться параметры.
    Имя файла задается относительно основного конфигурационного файла, например, ..\Config\Custom.ini.
    Если имя файла не задано (пустое), то оно берется из ReadIni('CustomIniFileRef').
     
  2. CustomSection - задает имя Custom-секции, где будут храниться теги.
    Теги хранятся в формате "Имя=Значение". Строковые теги могут содержать пробелы, но не должны содержать управляющих символов (CR,LF и т.д.).
    При чтении тегов сначала считываются данные из Custom-секции конфигурационного файла, а затем из Custom-секции INI-файла. Это сделано для того, чтобы можно было задать значения тегов "по умолчанию" в конфигурационном файле. Таким образом, теги могут задаваться в трех местах. В секции [TagList] конфигурационного файла при объявлении тегов всегда задаются их начальные значения. Затем значения тегов считываются из Custom-секции конфигурационного файла, если эта секция есть. Затем значения тегов считываются из Custom-секции INI-файла, если он существует. Таким образом, просто удалив INI-файл, и затем вызвав функцию чтения, можно вернуться к значениям по умолчанию, которые заданы в конфигурационном файле.
    Если имя Custom-секции не задано (пустое), то оно берется из ReadIni('CustomIniSection').
     
  3. TagListSection - задает имя TagList-секции в конфигурационном файле, где задается список тегов, которые надо сохранять или загружать. Список содержит строки вида "TagList = Tag1, Tag2, ..." с идентификатором TagList и следующим за ним перечислением имен тегов. Все перечисленные теги помещаются в список читаемой/записываемой группы тегов.
    Кроме TagList-секции в группу сохраняемых/загружаемых тегов помещаются теги, которые записаны в Custom-секции конфигурационного файла, а также в Custom-секции INI-файла. То есть если тег поместить в Custom-секцию конфигурационного или INI-файла, то он тоже будет включен в группу, как и теги, описанные в TagList-секции. Это позволяет избежать дублирования тегов в списках TagList-секции и Custom-секции конфигурационного файла. Одновременное включение тега в эти списки тоже допустимо, оно не приведет к многократной записи иди чтению тега.
    Если имя TagList-секции не задано (пустое), то оно берется из ReadIni('CustomIniTagList').
     
  4. BackupDir - задает имя Backup-каталога, где будут храниться резервные копии предыдущих версий INI файлов. Это позволяет во-первых, сохранить предыдущее состояние системы, с возможностью воссстановления путем копирования INI-файла. А также автоматически обеспечивается журналирование сделанных оператором изменений контрольных параметров - для этого достаточно сохранить параметры в INI файл.
    Имя каталога задается относительно основного конфигурационного файла, например, ..\Config\Custom.ini.
    Если имя Backup-каталога не задано (пустое), то оно берется из ReadIni('CustomIniBackups').
     

Параметр mode содержит битовые флаги, задающие, режимы отображения и резервного копирования. Значение mode = 0 задает поведение по умолчанию:

  • Результат сохранения отображается в Главной Консоли (Echo),
  • По завершении операции отображается диалоговое окно (UnixWBox) с информационным сообщением на 15 секунд,
  • Перед записью в INI файл делается его резервная копия, как с тем же именем (например Custom.INI), так и с меткой времени (типа Custom-200121226-152345.INI). Сохранение поисходит в Backup-каталог.
  1. бит (1) - не отображать сообщение (Echo) в Главной Консоли.
  2. бит (2) - не отображать диалоговое окно (UnixWBox) по завершении операции.
  3. бит (4) - не сохранять копию предыдущего INI файла с меткой времени в имени в backup-каталоге. Метка времени файла Custom.INI имеет вид Custom-200121226-152345.INI и фиксирует время (с точностью до секунды), когда была сделана эта резервная копия.
  4. бит (8) - не сохранять копию предыдущего INI файла в backup-каталоге.

Примеры:

   1) iNul(CustomIniRW('R','',0));      чтение
      iNul(CustomIniRW('W','',0));      запись

      Этот пример читает/записывает группу тегов из/в INI файл.
      Устройство должно содержать в своей секции описание такого вида:

         [&DeviceSection]                               ; Секция описания устройства
         CustomIniFileRef = ..\Config\Custom.ini        ; Ссылка на INI файл для записи
         CustomIniSection = [CustomParameters]          ; Имя секции где храняться параметры
         CustomIniTagList = [CustomParameters.TagList]  ; Имя секции со списком сохраняемых тегов
         CustomIniBackups = ..\Data\Custom              ; Имя каталога для резервных копий INI файлов
         [CustomParameters.TagList]                     ; Секция со списком сохраняемых тегов
         TagList = tag1,tag2,tag3,tag4                  ; TagList = ИмяТега1, ИмяТега2, ...
         TagList = tag5,tag6,tag7,...                   ; Список может быть большим
         [CustomParameters]                             ; Секция со значениями тегов
         tag1=1235                                      ; в формате Имя=Значение
         tag2=5.67                                      ;
         tag3=abcd                                      ;
         []

   2) iNul(CustomIniRW('R','..\Config\Custom.ini [CustomParameters] [CustomParameters.TagList] ..\Data\Custom',2));
      iNul(CustomIniRW('W','..\Config\Custom.ini [CustomParameters] [CustomParameters.TagList] ..\Data\Custom',2));

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

function WinListByCurve(crv:Integer; comma:Char):String;
- функция возвращает список окон (графиков и таблиц), разделенный символами comma (обычно это запятая), в которых присутствует кривая, заданная ссылкой crv.

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

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

Пример:

    crv:=RefFind('Curve demo');                 // Находим ссылку по имени кривой
    lst:=WinListByCurve(crv,',');               // Получаем список окон, где она присутствует
    Writeln('Кривая demo входит в окна:'+lst);  // Печатаем этот список в консоли
   

function WinSelectByCurve(crv:Integer; selcrv:Integer):Integer;
- функция открывает (рисует и выделяет) окна (кривых и таблиц), в которые входит кривая, заданная ссылкой crv, а также выделяет в этих окнах кривую, заданную ссылкой selcrv.

Функция возвращает число найденных окон, в которых присутствует кривая crv, или ноль, если кривая не отображается в окнах.

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

Параметр selcrv задает ссылку кривой, которую надо выделить. При указании selcrv=0 выделения кривой не делается. При указании selcrv=-1 отменяется выделение кривой, то есть показываются все кривые одновременно.

Пример:

    if ClickButton=1 then begin                          // Если нажат сенсор
     ClickCurve:=RefFind('Curve '+ClickParams('Curve')); // Находим ассоциированную кривую
     if ClickCurve<>0 then begin                         // Если кривая найдена
      iNul(WinSelectByCurve(ClickCurve,ClickCurve));     // Открываем окна графиков, где есть эта кривая и выделяем её
      bNul(Voice(snd_Wheel));                            // Выдаем звук
     end;
    end;
   

function DeviceListEnum(n:Integer):Integer;
- функция возвращает ссылку устройства (Device), по номеру n в списке устройств [DeviceList], или ноль, если задан несуществующий номер устройства.

Параметр n задает номер устройства, начиная с 0. Возврат нулевой ссылки служит признаком завершения списка.

Пример:

   n:=0;
   repeat
    dev:=DeviceListEnum(n);
    if dev<>0 then Writeln('Device '+Str(i)+' is '+RefInfo(dev,'Name'));
    n:=n+1;
   until dev=0;
   

function CalibrOpenByCurve(crv:Integer):Boolean;
- функция находит и открывает диалог редактирования калибровки, найденной по кривой crv, к которой эта калибровка относится. Предполагается, что кривая подключена к аналоговому выходу AnalogOutput номер n программного устройства (device software program), который по номеру cовпадает с номером калибровки Calibration#n. Эта функция дает возможность пользователю редактировать калибровку по нажатию поля, ассоциированного с некоторой кривой.

Функция возвращает true, если найдена калибровка по кривой crv, или false, если калибровка не найдена.

Параметр crv задает кривую, по которой ищется калибровка.

Пример:

    if ClickButton=1 then begin                          // Если нажат сенсор
     ClickCurve:=RefFind('Curve '+ClickParams('Curve')); // Находим ассоциированную кривую
     if ClickCurve<>0 then begin                         // Если кривая найдена
      bNul(CalibrOpenByCurve(crv));                      // Открываем калибровку
      bNul(Voice(snd_Wheel));                            // Выдаем звук
     end;
    end;
   

procedure ClearStdUtils;
procedure InitStdUtils;
procedure FreeStdUtils;
procedure PollStdUtils;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdDiesel

содержит константы, переменные и функции для организации запуска, завершения и связи с программами DieselPascal в рамках шаблона прикладной программы template.pas.

_con_StdDiesel.inc - файл описания констант модуля StdDiesel.

Пока файл пуст.

_var_StdDiesel.inc - файл описания переменных модуля StdDiesel.

Переменная ShouldPollStdDiesel содержит флаг опроса модуля. Менять её нежелательно.

Переменная LM9_Flag_RedirectRxToStdIn содержит флаг перенаправления потока данных из каналов связи IPC программ LM9 в консоль StdIn. По умолчанию флаг включен, т.е. все данные из каналов связи по умолчанию передаются в консоль ввода для обработки. Предполагается, что по каналам связи передаются команды, обрабатываемые обычным способом. Если нужна другая обработка, то флаг отключается и обработка делается самостоятельно.

_fun_StdDiesel.inc - файл описания процедур и функций модуля StdDiesel.

function DieselPascalAssocLm9Exe:String;
- функция возвращает полный путь исполняемого файл для запуска файлов типа *.lm9. При инсталляции DieselPascal это будет программа CrossMachine.exe, cлужащая для запуска программ *.lm9.

function DieselPascalDManagerExe:String;
- функция возвращает полный путь программы DManager.exe, которая управляет проектами DieselPascal.

function DieselPascalCrossMachineExe:String;
- функция возвращает полный путь программы CrossMachine.exe, которая запускает программы DieselPascal.

function DieselPascalCrossDesignerExe:String;
- функция возвращает полный путь программы CrossDesigner.exe, которая служит для создания и редактированияя проектов DieselPascal.

function HasDieselPascal:Boolean;
- функция проверяет доступность DieselPascal и возможность запуска программ *.lm9.

Функции для организации канала межпроцессной связи EasyIpc с программами DieselPascal:
function EasyIpc_Valid(hIpc:Integer):Boolean; - проверка годности ссылки канала связи
function EasyIpc_Connected(hIpc:Integer):Boolean; - статус подключения
function EasyIpc_IsServer(hIpc:Integer):Boolean; - флаг работы в роли сервера
function EasyIpc_IsClient(hIpc:Integer):Boolean; - флаг работы в роли клиента
function EasyIpc_FileName(hIpc:Integer):String; - UNC имя файла канала связи (\\.\pipe\demo)
function EasyIpc_PipeName(hIpc:Integer):String; - имя канала связи (demo для сервера, .\demo для клиента)
function EasyIpc_HostName(hIpc:Integer):String; - имя сервера (только для клиента)
function EasyIpc_BaseName(hIpc:Integer):String; - базовое имя канала связи (demo)
function EasyIpc_Handle(hIpc:Integer):Integer; - файловая ссылка (handle) канала связи
function EasyIpc_TimeOut(hIpc:Integer):Integer; - время ожидания подключения
function EasyIpc_RxBuffSize(hIpc:Integer):Integer; - размер буфера Rx (приемника)
function EasyIpc_TxBuffSize(hIpc:Integer):Integer; - размер буфера Tx (передатчика)
function EasyIpc_RxLost(hIpc:Integer):Real; - счетчик потерянных байт Rx
function EasyIpc_TxLost(hIpc:Integer):Real; - счетчик потерянных байт Tx
function EasyIpc_RxTotal(hIpc:Integer):Real; - счетчик принятых байт Rx
function EasyIpc_TxTotal(hIpc:Integer):Real; - счетчик переданных байт Tx
function EasyIpc_RxLength(hIpc:Integer):Integer; - длина данных в FIFO буфере Rx, доступных для чтения
function EasyIpc_TxLength(hIpc:Integer):Integer; - длина данных в FIFO буфере Tx, ожидающих передачи
function EasyIpc_RxFifoLimit(hIpc:Integer):Integer; - предел объема данных в FIFO буфере Rx
function EasyIpc_TxFifoLimit(hIpc:Integer):Integer; - предел объема данных в FIFO буфере Tx
function EasyIpc_LogsHistory(hIpc:Integer):Integer; - предел буфера истории событий, строк
function EasyIpc_LogsCount(hIpc:Integer):Integer; - счетчик событий в буфере, строк
function EasyIpc_LogsTextMove(hIpc:Integer):String; - извлечение буфера событий и очистка
function EasyIpc_LogsTextCopy(hIpc:Integer):String; - извлечение буфера событий без очистки
function EasyIpc_Clear(hIpc:Integer; What:String):Boolean; - очистка * - всего

Функции для запуска программ DieselPascal, т.е. файлов *.LM9:
function LM9_TabCount:Integer - счетчик таблицы запущенных программ LM9
function LM9_TidByKey(key:String):Integer - идентификатор задачи (tid) по ключу (key) или 0
function LM9_KeyByTid(tid:Integer):String - ключ (key) по идентификатору задачи (tid) или пустая строка
function LM9_IndexOfKey(key:String):Integer - индекс ключа (key) в таблице или -1
function LM9_KeyByIndex(i:Integer):String - ключ (key) по индексу (i) в таблице или пустая строка
function LM9_IndexOfTid(tid:Integer):Integer - индекс задачи (tid) в таблице или 0
function LM9_TidByIndex(i:Integer):Integer - задача (tid) по индексу (i) или 0
function LM9_IpcByKey(key:String):Integer - канал связи (ipc) по ключу (key) или 0
function LM9_IpcByIndex(i:Integer):Integer - канал связи (ipc) по индексу (i) в таблице или 0
function LM9_IpcByTid(tid:Integer):Integer - канал связи (ipc) по идентификатору задачи (tid) или 0
function LM9_BufByKey(key:String):Integer - буфер текста (buf) по ключу (key) или 0
function LM9_BufByIndex(i:Integer):Integer - буфер текста (buf) по индексу (i) или 0
function LM9_BufByTid(tid:Integer):Integer - буфер текста (buf) по идентификатору задачи (tid) или 0
function LM9_ParamsByKey(key:String):String - параметры вызова (params) по ключу (key) или пустая строка
function LM9_ParamsByIndex(i:Integer):String - параметры вызова (params) по индексу или пустая строка
function LM9_ParamsByTid(tid:Integer):String - параметры вызова (params) по идентификатору задачи (tid) или пустая строка
function LM9_HasKey(key:String):Boolean - проверка наличия ключа (key) в таблице
function LM9_HasTid(tid:Integer):Boolean - проверка наличия идентификатора задачи (tid) в таблице
function LM9_Send(tid:Integer; data:String):Boolean - посылка программе LM9 с идентификатором задачи (tid) текста (data) в канал (ipc)
function LM9_Post(tid:Integer; data:String):Boolean - посылка программе LM9 с идентификатором задачи (tid) текста (data) в канал (ipc) с буферизацией
procedure LM9_KeyFree(key:String) - освободить программу LM9 по ключу (key)
procedure LM9_TidFree(tid:Integer) - освободить программу LM9 по идентификатору задачи (tid)
function LM9_Launch(lm9,arg,opt,inipar,ipcpar:String):Integer - запустить программу LM9 с заданным именем файла(lm9), аргументами (arg), опциями запуска (opt), начальными параметрами (inipar) и параметрами канала связи (ipcpar)

Эта группа функций LM9 для запуска программ DieselPascal довольно сложна и требует пояснений.
Программы DieselPascal хранятся в файлах *.LM9 и выполняются виртуальной машиной CrossMachine.exe. В DaqPascal эти программы естественным образом представляются в виде запускаемых задач (task). Однако, поскольку с этой задачей связаны дополнительные объекты (канал связи IPC, временный файл, параметры запуска, буфер текста), то эти дополнительные объекты хранятся в таблице, элементы которой идентифицируются по номеру задачи (tid, task id). Таблица позволяет быстро находить по номеру задачи все остальные связанные с ней объекты. Элемент таблицы, т.е. задачи вместе с связанными с ней объектами, мы и будем называть программами LM9.

Программы LM9 хранятся во внутренней таблице со счетчиком LM9_TabCount. Каждая программа LM9 после запуска функцией LM9_Launch помещается в таблицу и получает ключ (key), идентификатор задачи (tid = task id), канал связи (ipc), текстовый буфер (buf) и блок параметров (params), а также индекс (i) в таблице программ LM9.

Ключ (key) однозначно вычисляется по идентификатору задачи LM9_KeyByTid(tid), поэтому не требует отдельного хранения.

Основным параметром для идентификации программ является идентификатор задачи (tid), который возвращает функция запуска программы LM9_Launch. Это идентификатор задачи, созданный вызовом task_init, который можно использовать в любых функциях работы с задачами. Другие параметры извлекаются из таблицы по идентификатору задачи (tid), ключу (key) или индексу (i).

Индекс (i) используется только в циклах для перебора программ, он не является постоянным и может изменяться при запуске новых программ. Индекс меняется в диапазоне 0..LM9_TabCount-1. В цикле обработки по индексу вычисляются ссылки (tid,ipc,buf) и дальнейшая работа идет уже с ними.

Канал связи (ipc = inter process communication) прозволяет обмениваться текстовыми данными с программой LM9 в реальном времени. Для создания канала надо указать параметр ipcpar при вызове LM9_Launch. Предполагается, что данные представлены в виде текста со строками, разделенными EOL. Передача данных в программу LM9 осуществляется вызовом LM9_Send или LM9_Post. Вызов LM9_Send передает данные сразу и используется для передачи сообщений с минимальной задержкой. Однако при большом числе сообщений из соображений производительности лучше использовать буферизацию. Вызов LM9_Post передает данные не сразу, а использует внутренний буфер (buf) для накопления сообщений, чтобы передать их одной пачкой в цикле опроса. Буферизация используется для оптимизации производительности в случае, когда задержка передачи не важна. Прием данных происходит следующим образом. Если активен флаг LM9_Flag_RedirectRxToStdIn (по умолчанию он активен), то прием данных из канала связи идет автоматически и направляется в консоль ввода, чтобы его можно было обработать обычными средствами. Этот режим удобен для обработки потока команд, однако при этом теряется информация об источнике данных, т.е. о канале, из которого пришли данные. Если это представляет интерес, то надо снять флаг LM9_Flag_RedirectRxToStdIn и обрабатывать ввод самостоятельно. Для этого по идентификатору задачи (tid) надо получить ссылку на канал связи LM9_IpcByTid(ipc) и использовать функции EasyIpc.

Параметры вызова (params) - это текст для передачи и хранения дополнительных параметров вызова программы LM9. Этот текст формируется при вызове LM9_Launch из параметра (inipar) и других аргументов вызова. Параметры вызова передаются программе LM9 через временный файл, имя которого генерируется автоматически вызовом CreateTempFile и передается программе в аргументе командной строки (--params=filename.tmp). Предполагается, что текст содержит строки, разделенные EOL. В тексте, например, можно передавать параметры в виде выражений Name=Value, либо команды вида @cmd args.

Буфер (buf) используется для промежуточного накопления данных при вызове LM9_Post. Следует иметь в виду, что буфер использует короткие строки (255 символов), поэтому буферизацию LM9_Post следует использовать для передачи большого числа коротких сообщений, а для посылки длинных сообщений надо использовать LM9_Send.

Для запуска программы LM9 надо вызвать LM9_Launch(lm9,arg,opt,inipar,ipcpar). Здесь:
  • lm9 - обязательное имя запускаемого файла *.LM9 с программой DieselPascal. Если не указано расширение файла, устанавливается расширение .LM9. Если не указан путь файла, инициируется поиск файла в путях поиска PATH. Для указания положения файла можно использовать обычные правила файловых ссылок DAQ-системы.

  • arg - (необязательный) список аргументов командной строки. Заметим, что в командную строку автоматически добавляются опции (--params, --easyipc) помимо arg.

  • opt - (необязательный) список опций для настройки задачи task_ctrl. Опции разделяются на строки с помощью EOL. Например, 'Display=1'+EOL+'ProcessPriority=8'.

  • inipar - (необязательные) дополнительные параметры вызова в виде строк текста с разделителями EOL. Текст может содержать, например, выражения Name=Value для задания нужных параметров по имени. При вызове перед текстом inipar помещается секция текста [Caller.Params] с общими параметрами вызова, включая имя программы, имя канала связи, имя временного файла, дата и время и т.д. При вызове текст параметров помещается во временный файл *.TMP, имя которого генерируется автоматически вызовом CreateTempFile и передается в аргументах вызова (--params=filename.tmp). Вызванная программа считывает параметры из временного файла. При завершении LM9_TidFree(tid) временный файл удаляется.

  • ipcpar - (необязательные) параметры канала связи. Если задана пустая строка, то канал связи не создается. Если задане непустая строка, то она должна содержать текст из строк с разделителями EOL. Первой строкой идет префикс имени канала связи, например ipc. К этому префиксу прибавляется сгенерированная програмно уникальная строка, чтобы имена каналов не конфликтовали. Например, имя канала может выглядеть как .\ipc_xpo3wz4dr6irmx7hz8rmcq4p7c. Сгенерированное имя канала передается в аргументах вызова (--easyipc=.\ipc_xxxx). После строки префикса идут параметры канала связи для функции EasyIpc_Init.


Для завершении работы с программой LM9 надо вызвать LM9_TidFree(tid) для завершения задачи (tid), освобождения буфера (buf) и закрытия канала (ipc).

Например:
    Запуск программы:
     lm9:=LM9_Launch('demo.lm9','--demo','ProcessPriority=4','Volume=100','ipc'+EOL+'LogsHistory=32');

    В цикле опроса:
     if task_wait(lm9,0) then begin         // Если программа работает
      data:=StrFmt('RunCount=%g',RunCount); // Получить какие-то данные
      bNul(LM9_Post(lm9,data));             // Послать данные в канал связи
     end;

    Завершение программы:
     LM9_TidFree(lm9);
     lm9:=0;
   


procedure ClearStdDiesel;
procedure InitStdDiesel;
procedure FreeStdDiesel;
procedure PollStdDiesel;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль StdAddons

предоставляет собой виртуальный модуль - заглушку, специально для инкапсуляции пользовательских констант, переменных и функций в системную библиотеку, то есть во все программы, созданные в рамках шаблона прикладной программы template.pas.

_con_StdAddons.inc - файл описания констант модуля StdAddons.

Является специальной пустой заглушкой для инкапсуляции пользовательских констант. Пользователь может скопировать этот файл в каталог Daqpas, где обычно расположены прикладные программы, и заполнить его своим содержимым. Это содержимое станет доступным для всех программ в данном каталоге, использующих стандартную библиотеку. Менять сами программы при этом не надо.

_var_StdAddons.inc - файл описания переменных модуля StdAddons.

Является специальной пустой заглушкой для инкапсуляции пользовательских переменных. Пользователь может скопировать этот файл в каталог Daqpas, где обычно расположены прикладные программы, и заполнить его своим содержимым. Это содержимое станет доступным для всех программ в данном каталоге, использующих стандартную библиотеку. Менять сами программы при этом не надо.

_fun_StdAddons.inc - файл описания процедур и функций модуля StdAddons.

Является специальной пустой заглушкой для инкапсуляции пользовательских функций. Пользователь может скопировать этот файл в каталог Daqpas, где обычно расположены прикладные программы, и заполнить его своим содержимым. Это содержимое станет доступным для всех программ в данном каталоге, использующих стандартную библиотеку. Менять сами программы при этом не надо.

procedure ClearStdAddons;
procedure InitStdAddons;
procedure FreeStdAddons;
procedure PollStdAddons;
- обеспечивают очистку строк, инициализацию, завершение и цикл опроса модуля в рамках шаблона прикладной программы template.pas.


Модуль PhysLibrary

Модуль PhysLibrary содержит функции по общей физике и метрологии.
Содержит файлы _con_PhysLibrary, _fun_PhysLibrary, _var_PhysLibrary, _con_PhysUnits, _fun_PhysUnits, _var_PhysUnits, _con_PhysVapor, _fun_PhysVapor, _var_PhysVapor, _con_PhysRtdTc, _fun_PhysRtdTc, _var_PhysRtdTc,
Содержит константы thermocouple_R, thermocouple_S, thermocouple_B, thermocouple_J, thermocouple_T, thermocouple_E, thermocouple_K, thermocouple_N, thermocouple_A1, thermocouple_A2, thermocouple_A3, thermocouple_L, thermocouple_M, rtd_id_pt100_385, rtd_id_pt100_391, rtd_id_cu100_428, rtd_id_cu100_426, rtd_id_ni100_617,
Содержит переменные thermocouple_cnt, thermocouple_eps, thermocouple_tol, thermocouple_big, thermocouple_del, thermocouple_fac, thermocouple_met, thermocouple_mit, restemperdet_cnt, restemperdet_eps, restemperdet_tol, restemperdet_big, restemperdet_del, restemperdet_fac, restemperdet_met, restemperdet_mit,
Содержит функции ClearPhysLibrary, InitPhysLibrary, FreePhysLibrary, PollPhysLibrary, thermocouple_emf, thermocouple_lot, thermocouple_hit, thermocouple_idn, thermocouple_ids, thermocouple_ttc, thermocouple_tst, restemperdet_ohm, restemperdet_lot, restemperdet_hit, restemperdet_idn, restemperdet_ids, restemperdet_ttc, restemperdet_tst, atm_to_mbar, mbar_to_atm, mmhg_to_mbar, mbar_to_mmhg, psi_to_mbar, mbar_to_psi, cels_to_kelv, kelv_to_cels, cels_to_fahr, fahr_to_cels, vapor_efactor, vapor_pressure, dewpoint_to_rh, dewpoint_to_ppmv, vapor_nFormula, vapor_mFactor, TestHumidityCalc.


Модуль NetLibrary

Модуль NetLibrary содержит средства для поддержки сетевых протоколов, включая MODBUS.
Содержит файлы _con_NetLibrary, _fun_NetLibrary, _var_NetLibrary, _con_NetModbus, _fun_NetModbus, _var_NetModbus,
Содержит константы modbus_MinUnitId, modbus_MaxUnitId, modbus_MaxPduLen, modbus_MaxTcpLen, modbus_MaxRtuLen, modbus_MaxAscLen, modbus_MaxCSGet, modbus_MaxISGet, modbus_MaxIRGet, modbus_MaxHRGet, modbus_MaxCSPut, modbus_MaxHRPut, modbus_0xFF00, modbus_sc_off, modbus_sc_on, modbus_fn_ReadCS, modbus_fn_ReadIS, modbus_fn_ReadHR, modbus_fn_ReadIR, modbus_fn_WritSC, modbus_fn_WritSR, modbus_fn_ReadES, modbus_fn_Diagn, modbus_fn_EvCnt, modbus_fn_EvLog, modbus_fn_WritMC, modbus_fn_WritMR, modbus_fn_RepSID, modbus_fn_ReadFR, modbus_fn_WritFR, modbus_fn_MaskWR, modbus_fn_ReWrMR, modbus_fn_ReadFQ, modbus_fn_Escape, modbus_er_OK, modbus_er_ILLFUN, modbus_er_ILLADR, modbus_er_ILLVAL, modbus_er_EXCEPT, modbus_er_BADLEN, modbus_er_BADCRC, modbus_er_BADUID, modbus_er_BADCMD, modbus_er_BADREF, modbus_MaxWord, modbus_pr_IP, modbus_pr_RTU, modbus_pr_ASCII, modbus_pr_NAMES, modbus_pr_ALIAS, modbus_sw_native, modbus_sw_normal, ModbusProxy,
Содержит переменные modbus_errno, modbus_MaxCSAddr, modbus_MaxISAddr, modbus_MaxIRAddr, modbus_MaxHRAddr, devModbusProxy, hid_NetModbusLrc, hid_NetModbusСrc,
Содержит функции ClearNetLibrary, InitNetLibrary, FreeNetLibrary, PollNetLibrary, modbus_get_tag, modbus_set_tag, modbus_inc_tag, modbus_fixerror, modbus_swap_int16, modbus_swap2, modbus_swap4, modbus_swap8, modbus_dump_int16, modbus_dump_int32, modbus_dump_float, modbus_dump_double, modbus_take_int16, modbus_take_int32, modbus_take_float, modbus_take_double, modbus_addr_ok, modbus_prot_ok, modbus_prot_name, modbus_prot_alias, modbus_prot_byname, modbus_calc_lrc, modbus_calc_crc, modbus_join_lrc, modbus_join_crc, modbus_encode_ip, modbus_decode_ip, modbus_encode_rtu, modbus_decode_rtu, modbus_encode_ascii, modbus_decode_ascii, modbus_encode_adu, modbus_decode_adu, modbus_is_except, modbus_un_except, modbus_encode_pdu, modbus_decode_pdu, modbus_proxy_poll, modbus_proxy_nice, modbus_proxy_reply modbus_errmsg, ClearNetModbus, InitNetModbus, FreeNetModbus, PollNetModbus.


Модуль DbLibrary

Модуль DbLibrary содержит средства для поддержки СУБД (Баз Данных) и является расширением DbApi.

Содержит файлы _con_DbLibrary, _fun_DbLibrary, _var_DbLibrary, _con_StdAdoDb,

Содержит константы adOpenUnspecified adOpenForwardOnly adOpenKeyset adOpenDynamic adOpenStatic adHoldRecords adMovePrevious adAddNew adDelete adUpdate adBookmark adApproxPosition adUpdateBatch adResync adNotify adFind adSeek adIndex adLockUnspecified adLockReadOnly adLockPessimistic adLockOptimistic adLockBatchOptimistic adOptionUnspecified adAsyncExecute adAsyncFetch adAsyncFetchNonBlocking adExecuteNoRecords adConnectUnspecified adAsyncConnect adStateClosed adStateOpen adStateConnecting adStateExecuting adStateFetching adUseNone adUseServer adUseClient adUseClientBatch adEmpty adTinyInt adSmallInt adInteger adBigInt adUnsignedTinyInt adUnsignedSmallInt adUnsignedInt adUnsignedBigInt adSingle adDouble adCurrency adDecimal adNumeric adBoolean adError adUserDefined adVariant adIDispatch adIUnknown adGUID adDate adDBDate adDBTime adDBTimeStamp adBSTR adChar adVarChar adLongVarChar adWChar adVarWChar adLongVarWChar adBinary adVarBinary adLongVarBinary adChapter adFileTime adDBFileTime adPropVariant adVarNumeric adFldUnspecified adFldMayDefer adFldUpdatable adFldUnknownUpdatable adFldFixed adFldIsNullable adFldMayBeNull adFldLong adFldRowID adFldRowVersion adFldCacheDeferred adFldNegativeScale adFldKeyColumn adEditNone adEditInProgress adEditAdd adEditDelete adRecOK adRecNew adRecModified adRecDeleted adRecUnmodified adRecInvalid adRecMultipleChanges adRecPendingChanges adRecCanceled adRecCantRelease adRecConcurrencyViolation adRecIntegrityViolation adRecMaxChangesExceeded adRecObjectOpen adRecOutOfMemory adRecPermissionDenied adRecSchemaViolation adRecDBDeleted adGetRowsRest adPosUnknown adPosBOF adPosEOF adBookmarkCurrent adBookmarkFirst adBookmarkLast adMarshalAll adMarshalModifiedOnly adAffectCurrent adAffectGroup adAffectAll adAffectAllChapters adResyncUnderlyingValues adResyncAllValues adCompareLessThan adCompareEqual adCompareGreaterThan adCompareNotEqual adCompareNotComparable adFilterNone adFilterPendingRecords adFilterAffectedRecords adFilterFetchedRecords adFilterPredicate adFilterConflictingRecords adSearchForward adSearchBackward adPersistADTG adPersistXML adClipString adPromptAlways adPromptComplete adPromptCompleteRequired adPromptNever adModeUnknown adModeRead adModeWrite adModeReadWrite adModeShareDenyRead adModeShareDenyWrite adModeShareExclusive adModeShareDenyNone adXactUnspecified adXactChaos adXactReadUncommitted adXactBrowse adXactCursorStability adXactReadCommitted adXactRepeatableRead adXactSerializable adXactIsolated adXactCommitRetaining adXactAbortRetaining adXactAsyncPhaseOne adXactSyncPhaseOne adPropNotSupported adPropRequired adPropOptional adPropRead adPropWrite adErrInvalidArgument adErrNoCurrentRecord adErrIllegalOperation adErrInTransaction adErrFeatureNotAvailable adErrItemNotFound adErrObjectInCollection adErrObjectNotSet adErrDataConversion adErrObjectClosed adErrObjectOpen adErrProviderNotFound adErrBoundToCommand adErrInvalidParamInfo adErrInvalidConnection adErrNotReentrant adErrStillExecuting adErrOperationCancelled adErrStillConnecting adErrNotExecuting adErrUnsafeOperation adParamSigned adParamNullable adParamLong adParamUnknown adParamInput adParamOutput adParamInputOutput adParamReturnValue adCmdUnspecified adCmdUnknown adCmdText adCmdTable adCmdStoredProc adCmdFile adCmdTableDirect adStatusOK adStatusErrorsOccurred adStatusCantDeny adStatusCancel adStatusUnwantedEvent adRsnAddNew adRsnDelete adRsnUpdate adRsnUndoUpdate adRsnUndoAddNew adRsnUndoDelete adRsnRequery adRsnResynch adRsnClose adRsnMove adRsnFirstChange adRsnMoveFirst adRsnMoveNext adRsnMovePrevious adRsnMoveLast adSchemaProviderSpecific adSchemaAsserts adSchemaCatalogs adSchemaCharacterSets adSchemaCollations adSchemaColumns adSchemaCheckConstraints adSchemaConstraintColumnUsage adSchemaConstraintTableUsage adSchemaKeyColumnUsage adSchemaReferentialConstraints adSchemaTableConstraints adSchemaColumnsDomainUsage adSchemaIndexes adSchemaColumnPrivileges adSchemaTablePrivileges adSchemaUsagePrivileges adSchemaProcedures adSchemaSchemata adSchemaSQLLanguages adSchemaStatistics adSchemaTables adSchemaTranslations adSchemaProviderTypes adSchemaViews adSchemaViewColumnUsage adSchemaViewTableUsage adSchemaProcedureParameters adSchemaForeignKeys adSchemaPrimaryKeys adSchemaProcedureColumns adSchemaDBInfoKeywords adSchemaDBInfoLiterals adSchemaCubes adSchemaDimensions adSchemaHierarchies adSchemaLevels adSchemaMeasures adSchemaProperties adSchemaMembers adSchemaTrustees adSeekFirstEQ adSeekLastEQ adSeekAfterEQ adSeekAfter adSeekBeforeEQ adSeekBefore adCriteriaKey adCriteriaAllCols adCriteriaUpdCols adCriteriaTimeStamp adPriorityLowest adPriorityBelowNormal adPriorityNormal adPriorityAboveNormal adPriorityHighest adResyncNone adResyncAutoIncrement adResyncConflicts adResyncUpdates adResyncInserts adResyncAll adRecalcUpFront adRecalcAlways stNoSchema stTables stSysTables stProcedures stColumns stProcedureParams stIndexes stPackages stSchemata stSequences stUnknown stSelect stInsert stUpdate stDelete stDDL stGetSegment stPutSegment stExecProcedure stStartTrans stCommit stRollback stSelectForUpd detCustom detPrepare detExecute detFetch detCommit detRollBack detParamValue detActualSQL dsInactive dsBrowse dsEdit dsInsert dsSetKey dsCalcFields dsFilter dsNewValue dsOldValue dsCurValue dsBlockRead dsInternalCalc dsOpening dsRefreshFields sqSupportParams sqSupportEmptyDatabaseName sqEscapeSlash sqEscapeRepeat sqImplicitTransaction sqLastInsertID sqSupportReturning sqSequences ftUnknown ftString ftSmallint ftInteger ftWord ftBoolean ftFloat ftCurrency ftBCD ftDate ftTime ftDateTime ftBytes ftVarBytes ftAutoInc ftBlob ftMemo ftGraphic ftFmtMemo ftParadoxOle ftDBaseOle ftTypedBinary ftCursor ftFixedChar ftWideString ftLargeint ftADT ftArray ftReference ftDataSet ftOraBlob ftOraClob ftVariant ftInterface ftIDispatch ftGuid ftTimeStamp ftFMTBcd ftFixedWideChar ftWideMemo dfBinary dfXML dfXMLUTF8 dfAny dfDefault db_idns_uid, db_idns_pwd, db_idns_server, db_idns_dbname, db_brm_hide, db_brm_echo, db_brm_soft, db_brm_hard, db_sym_SQLite3, db_sym_Firebird, db_sym_PostgreSQL, db_sym_MySQL, db_sym_SYSDBA, db_sym_masterkey, db_sym_localhost, db_sym_IBProvider, db_sym_punctchars, db_sym_SysIniFile, db_sym_DbApiMasterKey, db_sym_ConnectionString, db_sym_ConnectionTimeout, db_sym_CommandTimeout, db_sym_IsolationLevel, db_sym_Provider, db_sym_Driver, db_sym_DefaultDatabase, db_sym_CursorLocation, db_sym_Attributes, db_sym_AbsolutePage, db_sym_AbsolutePosition, db_sym_TimeStampUser1, db_sym_TimeStampUser2, db_sym_TimeStampUser3, db_sym_UserState, db_sym_UserFlags, db_sym_UserLink, db_sym_UserData, db_sym_Cookies, db_sym_Bookmark, db_sym_CacheSize, db_sym_CursorType, db_sym_Mode, db_sym_Filter, db_sym_Index, db_sym_LockType, db_sym_MarshalOptions, db_sym_MaxRecords, db_sym_PageCount, db_sym_PageSize, db_sym_RecordCount, db_sym_Sort, db_sym_Source, db_sym_Status, db_sym_StayInSync, db_sym_GetString, db_sym_CommandType, db_sym_CommandText, db_sym_Properties, db_sym_Errors, db_sym_ErrorsCount, db_sym_ErrorsClear, db_sym_ConnectionStringInit, db_sym_TimeStampInit, db_sym_TimeStampOpen, db_sym_State, db_sym_Version, db_sym_EngineId, db_sym_EngineName, db_sym_ProviderNames, db_sym_RecordsAffected, db_sym_EditMode, db_sym_BugsCount, db_sym_BugsClear, db_sym_TotalBugsCount, db_sym_TotalBugsClear, db_sym_BugReportPrefix, db_sym_BugReportPrefixAll,

Содержит переменные ShouldPollDbLibrary, db_brm_uses,

Содержит функции db_engine_list, db_engine_uses, db_engine_uses_name, db_engine_uses_assign, db_AdoTypeToString, db_SqlDbTypeToString, db_EngineFieldTypeToString, db_FieldTypeToString, db_AdoTypeToTagType, db_SqlDbTypeToTagType, db_EngineTypeToTagType, db_FieldTypeToTagType, db_AdoTypeIsDateTime, db_SqlDbTypeIsDateTime, db_EngineTypeIsDateTime, db_FieldTypeIsDateTime, db_EngineTypeIsBlob, db_FieldTypeIsBlob, db_DetectBlobImageType, db_AdoDateTimeToMs, db_MsToAdoDateTime, db_SqlDbDateTimeToMs, db_MsToSqlDbDateTime, db_EngineDateTimeToMs, db_MsToEngineDateTime, db_FieldDateTimeToMs, db_MsToFieldDateTime, db_query_connectionstring, db_subst_connectionstring, db_sample_connectionstring, db_read_connectionstring_samples, db_query_dbapimasterkey, db_build_connectionstring, db_validate_adapt_dbname, db_validate_known_providers, db_build_selectalltables, db_supporteddbtypes, db_bugreport_mode, db_just_sql_query, db_easy_sql_query, db_default_uid, db_default_pwd, db_sample_database_file, db_detect_dbtype, db_query_selectalltables, db_query_showtables, db_read_connectionstring_samples, db_read_selectalltables_samples, db_read_database_file_samples, db_create_local_fdb, ClearDbLibrary, InitDbLibrary, FreeDbLibrary, PollDbLibrary,


Модуль StdMenuTools

Модуль StdMenuTools содержит средства для организации меню Инструменты.

Содержит файлы _man_stdmenutools, _fun_stdmenutools,
Содержит функции MenuConsolesStarter, MenuConsolesHandler, MenuEditProgramStarter, MenuEditProgramHandler, MenuDeviceRestartStarter, MenuDeviceRestartHandler, HasUserAccessLevelTip.

Техника программирования Cleanup

В практике прикладного программирования рекомендуется использовать технику Cleanup, описанную ниже.
Мотивация - дисциплинировать разработчиков в плане корректной работы с ресурсами (строками, объектами) в модулях (процедурах и функциях). Идея состоит в том, чтобы выделить очистку всех локальных ресурсов в отдельную локальную процедуру Cleanup. Это облегчит проверку корректности очистки и её самосогласованность (т.к. очистка делается в двух местах - при входе и выходе из процедуры/функции). В шаблонах редактора DaqPascal есть процедуры и функции, содержащие подпрограммы очистки Cleanup для корректной работы со строками и другими ресурсами. Например:
   {
   Return string with reverse chars order.
   }
   function ReverseStr(arg:String):String;
   var s:String; i:Integer;
    procedure Cleanup;
    begin
     s:=''; // Здесь делаем очистку всех локальных строк.
    end;
   begin
    Cleanup;
    for i:=Length(arg) downto 1 do s:=s+arg[i];
    ReverseStr:=s;
    Cleanup;
   end;
   
Использование процедуры Cleanup с очисткой строк и (возможно) других переменных гарантирует корректную работу менеджера строк, облегчает программирование (т.к. очистка переменных теперь делается в одном месте, а не в двух). Имя процедуры очистки (Cleanup) можно считать стандартным (рекомендуемым). Поскольку это локальная процедура, она может использоваться без ограничений в любом числе процедур и функций.

Для корректной работы со строками теперь достаточно двух простых правил:
  1. вызывать локульную очистку Cleanup строго в начале и строго в конце процедуры/функции, и
  2. в процедуре Cleanup инициализировать пустой строкой все строки, объявленные локально в процедуре/функции.

Процедуру очистки Cleanup рекомендуется размещать сразу после объявления локальных переменных, чтобы было легко увидеть, какие именно переменные очищаются. Очистку рекомендуется делать компактно, в порядке объявления переменных, чтобы легче было проверить полноту их списка. Размещение каждого оператора в отдельной строке в данном случае неуместно, т.к. лишь затрудняет чтение. Например:
    procedure Something(...);
    var a,b,c,d,e,f:String;
     procedure Cleanup;
     begin
      a:=''; b:=''; c:=''; d:=''; e:=''; f:='';
     end;
     ...
    begin
     Cleanup;
     ... Do Something ...
     Cleanup;
    end;
   
В более сложных случаях, например, при наличии локальных объектов (регулярных выражений, текстов, задач и т.д.), которые надо инициализировать в начале и освобождать в конце, можно исспользовать расширенную модификацию техники Cleanup, включающую параметр (how) для различения способа очистки - инициализация (how<>0) или завершение (how=0). Например:
   {
   Return string with reverse lines order.
   }
   function ReverseLines(arg:String):String;
   var s:String; lines,i:Integer;
    procedure Cleanup(how:Integer);
    begin
     s:='';                                                    // Здесь делаем очистку всех локальных строк.
     if (how<>0) then lines:=text_new else FreeAndZero(lines); // Инициализируем/освобождаем локальные объекты.
    end;
   begin
    Cleanup(1);
    iNul(StringToText(lines,arg));
    for i:=text_numln(arg)-1 downto 0 do s:=s+text_getln(lines,i)+EOL;
    ReverseLines:=s;
    Cleanup(0);
   end;
   
Другой рекомендуемой техникой является сочетание Cleanup с FreeAndZero, когда все ссылки используемых объектов обнуляются в Cleanup, а перед финальной очисткой вызываются деструкторы FreeAndZero. Этот метод гораздо более гибкий, чем использование Cleanup(how), т.к. он позволяет создавать объекты по мере необходимости (или не создавать их вовсе). Для корректной работы в Cleanup должны обнуляться ВСЕ используемые ссылки локальных объектов, чтобы FreeAndZero мог их потом корректрно обработать. Если объекты не были созданы, FreeAndZero (вызываемый в конце обработки) увидит нулевую ссылку и проигнорирует вызов деструктора. По описанным причинам сочетание Cleanup с FreeAndZero является предпочтительной техникой для организации сложных процедур обработки данных. Например:
   {
   Return string with reverse lines order.
   }
   function ReverseLines(arg:String):String;
   var s:String; lines,i:Integer;
    procedure Cleanup;
    begin
     s:='';             // Здесь делаем очистку всех локальных строк.
     lines:=0;          // Здесь очищаем все ссылки на локальные объекты.
    end;
   begin
    Cleanup;             // Делаем стандартную очистку
    if (arg<>'') then begin
     lines:=text_new;    // При необходимости создаем объекты
     iNul(StringToText(lines,arg));
     for i:=text_numln(arg)-1 downto 0 do s:=s+text_getln(lines,i)+EOL;
    end;
    ReverseLines:=s;
    FreeAndZero(lines);  // Освобождаем объекты, если они созданы
    Cleanup;             // Делаем стандартную очистку
   end;
   
Использование Cleanup является универсальной (применимой всегда) техникой программирования, которая дисциплинирует и делает программы надежнее, а потому рекомендуется к использованию во всех прикладных программах.

Желаю успешного использования DaqPascal!