Общий подход к использованию интерфейсных функций{#sect_gen_descr}
==============================================================

Последовательность вызова функций{#sect_gen_call_seq}
=====================================================
При работе с крейтовой системой LTR через библиотеку ltrapi функции библиотеки не работают напрямую с крейтами или модулями. Вместо этого они используют службу ltrd, которая берет на себя взаимодействие с крейтами, устанавливая клиентское соединение со службой, которое уже может быть связано с определенным модулем или крейтом. 

Для описания клиентского соединения служит структура #TLTR, далее называемая описателем соединения, с использованием которой выполняются практически все функции данной библиотеки. Программа может иметь несколько соединений с ltrd и для каждого параллельного соединения необходимо создавать свой описатель соединения (экземпляр структуры TLTR).

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

Соответственно, типичная последовательность вызовов выглядит следующим образом:
1. Создать экземпляр структуры TLTR и проинициализировать его, вызвав функцию LTR_Init().
2. Установить соединения с ltrd одним из двух способов
    - вызвав функцию LTR_OpenSvcControl() или LTR_OpenCrate()  в зависимости от [типа соединения](@ref sect_con_types).
    - или заполнить необходимые поля структуры TLTR вручную и вызвать LTR_Open()
3.  Вызов нужных управляющих функций или обмен данными с модулем
4. По завершению работы закрыть соединение через LTR_Close()

Возможные действия после установления соединения зависят от типа соединения. Типы соединений описаны подробно в [следующем разделе](@ref sect_con_types).

Клиентское соединение между пользовательской программой и службой ltrd выполняется через сокеты. Это позволяет при необходимости подключаться из приложения к службе ltrd, запущенной на другой машине. В связи с этим в функцях открытия соединения участвуют параметры, указывающие адрес машины со службой ltrd и порт TCP для подключения (при использовании LTR_Open() эти данные задаются через поля [описателя соединения](@ref TLTR)  @fieldref{TLTR,saddr} и @fieldref{TLTR,sport}). В обычном режиме, когда служба и программа запущены на одной машине и используются TCP порт по умолчанию, в качестве адреса используется константа #LTRD_ADDR_DEFAULT, а в качестве порта --- #LTRD_PORT_DEFAULT.

Типы клиентских соединений{#sect_con_types}
 ==============================================================
 
 Можно выделить три типа клиентских соединений:
- управляющее соединение со службой ltrd
- управляющее соединение с крейтом
- соединение с конкретным модулем 


Управляющее соединение со службой ltrd{#sec_con_svcctl}
-------------------------------------------------------
Для открытия соединения данного типа служит функция LTR_OpenSvcControl(). Также для этой цели можно использовать функцию LTR_Open(), предварительно установив номер канала в поле @fieldref{TLTR,cc} равным #LTR_CC_CHNUM_CONTROL, а  поле @fieldref{TLTR,csn} заполнить специальной строкой #LTR_CSN_SERVER_CONTROL.

Соединение данного типа может быть выполнено даже если нет ни одного подключенного крейта. 

С использованием данного соединения можно выполнять управляющие команды, описанные в разделах:
- @ref func_geninfo
- @ref func_srvctl
- @ref func_ip

Управляющее соединение с крейтом{#sec_con_crate}
--------------------------------------------------------
Для открытия соединения данного типа служит функция LTR_OpenCrate(). Также для этой цели можно использовать функцию LTR_Open(), предварительно установив номер канала (поле @fieldref{TLTR,cc}) равным #LTR_CC_CHNUM_CONTROL и заполнить поле @fieldref{TLTR,csn} серийным номером крейта (или пустой строкой, если нужно установить соединение с первым крейтом).

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

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

Для определения, с каким крейтом будет связанно данное соединение, как правило достаточно указать серийный номера крейта (или пустую строку, если крейт всегда один). Однако один крейт может быть подключен одновременно по двум интерфейсам, например в случае, если крейт настроен на работу по Ethernet и подключен  по этому интерфейсу, но также и подключен по USB в режиме настройки. В связи с этим в общем случае подключение к крейту определяется двумя параметрами --- серийным номером крейта и интерфейсом подключения крейта. Без явного указания интерфейса соединение будет связано с крейтом с использованием интерфейса, который является рабочим для крейта, т.е. через который можно обмениваться данными с модулями крейта, что и необходимо для большинства программ. Однако при необходимости можно явно указать интерфейс подключения крейта через параметр функции LTR_OpenCrate() или через флаги #en_LTR_CC_Iface поля @fieldref{TLTR,cc} при использовании LTR_Open().

Соединение с конкретным модулем{#sec_con_module}
--------------------------------------------------------------
Соединение данного типа выполняется с помощью функции LTR_Open(), с предварительно заполненной в структуре #TLTR информацией о крейте, в котором находится нужный модуль, аналогично управляющему соединение с крейтом, но с указанном в поле @fieldref{TLTR,cc} номером слота крейта (вместо #LTR_CC_CHNUM_CONTROL), в который вставлен нужный модуль, с помощью значений от #LTR_CC_CHNUM_MODULE1 до #LTR_CC_CHNUM_MODULE16.

По данному соединению клиент обменивается данными с модулем по специализированному протоколу данного модуля с использованием функций из раздела @ref func_modules. 

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


 Множественное подключение {#sec_client_multiconnect}
==================================================================
  В случае управляющих соединений, клиент может параллельно открывать несколько соединений как со службой, так и с крейтами (как с одним крейтом, так и с несколькими). Каждое новое соединение не зависит от предыдущего и позволяет выполнять любой доступный для данного типа соединения набор функций.
  
  Иначе обстоит дело с подключениями для работы с конкретными модулями. Хотя служба ltrd позволяет устанавливать одновременно несколько соединений с одним модулем, но протокол работы с модулями таков, что для корректной работы с конкретным модулем должно быть одновременно установлено только одно соединение.
  
  При попытке открыть соединение с модулем, с которым уже есть установленное клиентское соединение (из этой или другой программы), функция открытия соединения возвращает ошибку #LTR_WARNING_MODULE_IN_USE.
  
  Обработка библиотеками данной ситуации несколько изменилось в последних версиях ltrapi по сравнению с версией 1.27.
  
 В версии 1.27 и ниже функции открытия соединения возвращали данную ошибку, однако выполи все действия, включая полный сброс и начальную инициализацию модуля. Это позволяло работать с  модулем также, как если бы не было первого подключение, но только при условии, если по первому подключению не будут передаваться команды, которые могут нарушить последовательность работы с модулем. В такой ситуации данное предупреждение можно было полностью игнорировать и работать с модулем, как если бы открытие завершилось успешно. Однако проблемой в таком варианте является то, что если первое соединение действительно используется и идет сбор данных, то попытка повторного установления соединения (возможно из совсем другой программы, которая ничего не знает о первом соединении), приведет к нарушению идущего сбора данных для первого соединения и более того, не гарантирует нормальной работы для второго, если первое не будет сразу закрыто. В частности при запуске программы, которая в первую очередь находит все модули и получает от них информацию, приводит к нарушению работающих в это время всех других программ.
 
 В версиях выше 1.27 при открытии соединения с модулем, если с этим модулем уже существует открытое соединение, сброс модуля, как и посылка любых других команд модулю не выполняется.  То есть это не нарушает работу первого соединения, однако модуль для второго соединения находится в неизвестном состоянии и, кроме того, оказываются невыполненными операции из функции открытия, специфичные для модуля (например, для модулей, в которых чтение информации из flash-памяти выполняется при открытии соединения эта информация прочитана не будет). Таким образом, в текущей версии ltrapi, если функция открытия соединения вернула ошибку #LTR_WARNING_MODULE_IN_USE, то такое соединение может использоваться лишь для ограниченного круга задач, а при штатной работе рекомендуется считать такое предупреждение равносильным ошибке и, как и при любой другой ошибке,  закрыть соединение. В этом случае запуск программы, описанной в предыдущем примере, приведет к тому, что она не сможет прочитать информацию о модулях, с которыми уже открыты работающие соединения, но работа ранее запущенных программ не будет нарушена.
 В случае же, если необходимо открыть новое соединение, сбросив предыдущее, то можно воспользоваться функцией LTR_ResetModule() через управляющее соединение со службой ltrd.
 
 Получение списка подключенных крейтов и модулей {#sec_modules_list}
 =================================================
 Функции ltrapi позволяют получить список крейтов, с которыми служба ltrd установила соединение, который называется списком активных крейтов службы ltrd. При этом служба сама добавляет в этот список все найденные и успешно проинициализированные крейты, подключенные по интерфейсу USB, в то время как подключение к крейтом по интерфейсу Ethernet выполняется посредством настроенных записей с IP-адресами крейтов, функции для работы с которыми описаны в разделе @ref func_ip.
 
 Список активных крейтов можно получить через управляющее соединение со службой ltrd, в то время как для получения списка модулей крейта необходимо устанавливать управляющее соединение с соответствующим крейтом.
 Таким образом, последовательность вызовов для получения полного списка активных крейтов и модулей может выглядеть следующим образом:
 
 1. Создать экземпляр структуры TLTR и проинициализировать его, вызвав функцию LTR_Init().
 2. Установить управляющее соединение со службой ltrd с помощью функции LTR_OpenSvcControl().
 3. Получить список серийных номеров всех активных крейтов с помощью функции LTR_GetCrates() или LTR_GetCratesEx().
 4. Для каждого действительного серийного номера крейта из полученного списка выполнить следующие действия:
      - Создать новый экземпляр структуры TLTR и проинициализировать его, вызвав функцию LTR_Init().
      - Установить управляющее соединение с крейтом, с текущим серийным номером из списка, вызвав LTR_OpenCrate()
      - Получить список модулей в крейте, вызвав LTR_GetCrateModules()
      - Закрыть управляющее соединение с крейтом с помощью LTR_Close()
 5. Закрыть управляющее соединение со службой ltrd, вызвав LTR_Close() для описателя соединения, созданного на шаге 1.

 
  Формат задания IP-адресов{#sect_ip_addr_format}
  ==============================================================
  IP-адреса  (здесь и далее под IP-адресом подразумевается адрес для протокола IP версии 4 , которая поддерживается крейтами и библиотекой) используются как для подключения крейтов по интерфейсу Ethernet (IP-адрес крейта), так и при установлении клиентского подключения к службе ltrd, запущенной на удаленной машине (IP-адрес машины со службой ltrd).
  
  IP адрес в текстовом виде представляет собой четыре цифры от 0 до 255, разделенные точками, например "192.168.1.12".
  Для задания IP-адресов в функциях и структурах данной библиотеки (как и в других библиотеках, связанных с ltrapi) как правило используется 32-битное число (типа DWORD). Каждый байт данного числа задает,
  соответственно, одну цифру из текстовой записи. При этом первой цифре соответствует старший байт числа, а последней --- младший.
  
  Таким образом, для адреса "a.b.c.d" 32-битное число будет определяться как \f$\verb!(a << 24) | (b << 16) | (c << 8) | d!\f$. Например для задания адреса 192.168.1.12 значение параметра в шестнадцатеричном виде будет равно  0xC0A8010C.
       
  Синхрометки{#sect_marks}
  ===================================================== 
  Синхрометки --- это общий механизм синхронизации крейтов LTR, который описан в главе "Принцип синхронизации сбора данных в системе LTR" [руководства пользователя](http://www.lcard.ru/download/ltr.pdf). Общий принцип заключается в том, что в общий поток данных от крейта могут вставляться специальные метки. Благодаря тому, что данные от всех модулей идут общим потоком в том порядке, в котором они попали в крейт, то на приемной строне по положению метки для каждого модуля можно определить, между какими отсчетами модуля возникло событие, соответствующее этой метки.
  
  Например, этот механизм позволяет привязать с точностью до одного периода дискретизации модуля следующие данные:
    - данные от разных модулей одного крейта относительно друг друга
    - данные от разных модулей разных крейтов, если крейты соединены через разъем синхронизации и используется генерация меток по принципу "ведущий-ведомые".
    - данные от разных модулей одного или нескольких крейтов к внешним событиям, если используется генерация метки от общего внешнего сигнала, подключенного к разъему синхронизации крейтов.
    
 В системе LTR используется два типа синхрометок --- метка "СТАРТ" и метка "СЕКУНДА". Названия меток отражают их изначальное назначение при внутренней генерации, однако при генерации от внешнего сигнала их назначение может быть произвольным и их можно рассматривать как привязку к двум разным типам синхрособытий.    

 Метки могут вставляться в поток либо специальными модулями (LTR41, LTR42, LTR43), либо самим крейтом, если он поддерживает данный механизм (поддерживается в крейтах LTR-EU, LTR-CEU, LTR-CU).  Также в урезанном виде синхрометки поддерживаются крейтом LTR-U-1-4, однако возможности генерации меток в этом крейте и точность привязки их к данным ограничены и при необходимости использования синхрометок в одноместном крейте рекомендуется применять крейты LTR-CEU-1 или LTR-CU-1.
 
 Функции для настройки меток, генерируемых модулями, описаны в руководствах программистов для этих модулей. Функции же для настройки меток, генерируемых крейтами, реализованы в ltrapi и описаны в данном документе. Исключением является крейт LTR-U-1-4, функции настроек меток которого реализованы в библиотеке ltr021api и в данном документе не рассматриваются. 
 
 Настройка генерации меток крейтов выполняется через [управляющее соединение с крейтом](@ref sec_con_crate) с помощью функций LTR_MakeStartMark(), LTR_StartSecondMark() и LTR_StopSecondMark().Для трансляции метки на выходы разъема синхронизации необходимо также предварительно настроить линии разъема синхронизации через LTR_Config().
 
 Прием и сопоставление синхрометок с данными{#sect_mark_recv}
 ------------------------------------------
 Служба ltrd принимает синхрометки в потоке данных от крейта и считает общее их количество, начиная с момента подключения крейта службой ltrd. При приеме данных от модуля с помощью функции LTR_Recv() принимается также и информация о моментах возникновения синхрометок с их счетчиком (в библиотеках конкретных модулей используется своя функция приема данных, однако ее принцип работы и параметры повторяют параметры функции LTR_Recv()). В результате функция LTR_Recv() на основе этой информации при необходимости формирует отдельный массив tmark из 32-битных слов. Каждое слово этого массива соответствует принятому слову от модуля с тем же номером элемента в массиве и содержит в себе информацию о счетчиках синхрометок на момент времени, соответствующему данному слову модуля. Каждое 32-битное слово tmark содержит в старшей половине счетчик меток "СТАРТ", а в младшей половине счетчик сеток "СЕКУНДА", как показано в таблице:
    
 |Номер бита    | Значение                                                                          |
 |--------------|-----------------------------------------------------------------------------------|
 |Биты 31-16    | Количество меток "СТАРТ", возникших до прихода соответствующего слова в крейт     |
 |Биты 15-0     | Количество меток "СЕКУНДА", возникших до прихода соответствующего слова в крейт   |
 
 Соответственно, по моменту изменения счетчика можно определить между какими словами от модуля возникло синхрособытие. Например, если принято 10 слов от модуля, а время возникновения синхрометки соответствует времени между 4 и 5 словом, а N -- количество синхрометок данного типа, пришедших до первого слова, то в первых 4-х элементах массива tmakr будет указан счетчик синхрометок данного типа равный N, а в оставшихся шести --- N + 1.
 
 Вставка синхрометки в поток данных, как и, соответственно, привязка синхрометки к данным модуля происходит на уровне крейта при приеме слов от модулей (до буферизации), т.е. если говорить более строго, изменение счетчика синхрометки между двумя словами модуля означает, что синхрособытие произошло после того, как в крейт пришло первое слово, но до того, как пришло второе. Для более точной привязки данных от разных типов модулей нужно также учесть задержки, вносимые самим модулем, например, если используется АЦП с фильтром, то следует учесть задержку этого фильтра. Эти задержки фиксированы для определенных настроек определенного модуля и могут быть получены, например, экспериментально для нужного случая.
 
 Следует также отметить, что так как счетчик синхрометок идет от начала установления соединения службы ltrd с крейтом и не связан с клиентскими соединениями с модулями, то на момент начала сбора значения счетчиков синхрометки могут быть отличны от нуля, если до работы с модулем от крейта приходили метки. Также следует учитывать, что само значение счетчиков для слов, соответствующих одному времени, одинаково для разных модулей одного крейта, но может отличаться для модулей из разных крейтов. Соответственно, нужно учитывать разные начальные значения счетчиков при старте и привязываться к их изменению, а не к абсолютному значению.
 
 Синхронизация нескольких крейтов{#sect_mark_multicrate}
 -----------------------------------------------
 Синхронизация данных модулей от нескольких крейтов с помощью синхрометок возможна для крейтов с разъемом SYNC (LTR-EU, LTR-CEU, LTR-CU), либо при наличие в крейте модулей, способных генерировать метки. В данном документе рассматривается только первый вариант. Принцип при использовании специальных модулей для генерации меток аналогичен, но для настройки использует функции модулей, которые не относятся к данному документу.
 
 При этом возможна как синхронизация нескольких крейтов между собой по принципу "Ведущий --- Ведомые", так и синхронизация от внешнего сигнала (в этом случае все крейты настраиваются как "ведомые").
 Для этого нужно соединить одну из линий DIGOUT разъема синхронизации ведущего крейта с одной из линий DIGIN разъема синхронизации каждого ведомого (при синхронизации от внешнего сигнала --- подключить этот сигнал к одной из линий DIGIN всех используемых крейтов).
 Если используются обе метки, то для каждой метки должен использоваться свой отдельный вход у ведущего крейта и свой вход у ведомых.
 Электрические вопросы такого подключения в данном руководстве не рассматриваются. Ниже рассматривается именно вопрос правильного использования функций данной библиотеки при выполнении такой синхронизации.

 Для примера настройки возьмем вариант синхронизации по принципу "Ведущий --- Ведомые". При этом будем считать, что выход DIGOUT1 ведущего крейта соединен с DIGIN1 всех ведомых и используется для метки "СТАРТ", а выход DIGOUT2 ведущего --- с DIGIN2 ведомых и используется для метки "СЕКУНДА".
 
 Общая последовательность настройки выглядит следующим образом:
1. В начальный момент генерация синхрометок во всех участвующих крейтах должна быть отключена. Если это может быть не так, то необходимо для каждого участвующего крейта отключить генерацию синхрометок, вызвав через управляющее соединение с каждым крейтом:
        - LTR_MakeStartMark() с указанием режима #LTR_MARK_OFF для отключения генерации метки "СТАРТ"
        - LTR_StopSecondMark() для отключения генерации метки "СЕКУНДА" 
2. Настройка выходов DIGOUT разъема синхронизации ведущего крейта. Для этого нужно вызвать функцию LTR_Config() для управляющего соединения с ведущим крейтом, передав структуру #TLTR_CONFIG, заполненную следующим образом:
        - в поле @fieldref{TLTR_CONFIG,digout_en} установлено значение 1
        - в поле @fieldref{TLTR_CONFIG,digout}[0] установлено значение #LTR_DIGOUT_START
        - в поле @fieldref{TLTR_CONFIG,digout}[1] установлено значение #LTR_DIGOUT_SECOND
3. Запуск сбора для всех модулей, данные от которых следует синхронизировать. Этот пункт можно выполнить и после настройки синхрометок ведомых (пункт 4), если есть гарантия, что синхрособытие не возникнет в момент запуска сбора, когда часть модулей запущена, а часть нет. В случае использования меток от ведущего крейта это гарантируется, т.к. метки будут генерироваться только после вызова соответствующих функций для ведущего крейта.
4. Настройка генерации синхрометок от сигналов на соответствующих входах  DIGIN  разъема синхронизации для всех ведомых крейтов. Для этого  для каждого ведомого крейта через управляющее соединения с соответствующим крейтом нужно:
       - вызвать функцию LTR_MakeStartMark() с указанием режима #LTR_MARK_EXT_DIGIN1_RISE для настройки генерации метки "СТАРТ" по фронту сигнала на входе DIGIN1
       - вызвать функцию LTR_StartSecondMark() с указанием режима #LTR_MARK_EXT_DIGIN2_RISE для настройки генерации метки "СЕКУНДА" по фронту сигнала на входе DIGIN2
5. Запуск секундных меток для ведущего крейта с помощью LTR_MakeStartMark() с указанием режима #LTR_MARK_INTERNAL через управляющее соединение ведущего крейта
6. Генерация метки "СТАРТ" ведущим крейтом с помощью LTR_MakeStartMark() с указанием режима #LTR_MARK_INTERNAL через управляющее соединение ведущего крейта

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



 
 
 
 
 
 
 
 
 
 
 
 
 
