Российский производитель и разработчик сертифицированного измерительного оборудования с 1987 года

Форум

Вы не вошли.

 Поиск | Регистрация | Вход 

#2 Re: Техническая поддержка » Архитектура построения ПО для съема данных с двух АЦП LTR24 » 09.07.2019 13:53:19

Здравствуйте, Алексей!
Огромное Вам спасибо за подробные разъяснения, все стало понятно! Я действительно не учел что никакая многоядерность здесь не спасает. Алексей по поводу проверки установки события - это просто ожидание с некоторым таймаутом ? И дальше анализ, если таймаут значит не было установлено ? И вот еще такой вопрос. Мне по сути задачи надо получить буфер отсчетов с АЦП именно после того как датчик будет установлен в нужное уголовое положение и зафиксирован. То есть все операции от LTR24_Recv до копирования в общий буфер должны быть именно после установки датчика и фиксации. Можно ли воспользоваться Вашим алгоритмом исходя из этого условия ? Не получится ли так что я считаю данные до полной фиксации датчика ?

#3 Re: Техническая поддержка » Архитектура построения ПО для съема данных с двух АЦП LTR24 » 09.07.2019 11:51:35

Привожу участок лог-файла после вызова функции основного потока
4036 >> [ 11:38:16.591]  READ ADC Try Acquire csr
7024 >> [ 11:38:16.621] Thread csr Release
7024 >> [ 11:38:16.621]  Thread csr Acquire
7024 >> [ 11:38:16.691] Thread csr Release
7024 >> [ 11:38:16.691]  Thread csr Acquire
7024 >> [ 11:38:16.761] Thread csr Release
7024 >> [ 11:38:16.761]  Thread csr Acquire
7024 >> [ 11:38:16.831] Thread csr Release
7024 >> [ 11:38:16.831]  Thread csr Acquire
7024 >> [ 11:38:16.901] Thread csr Release
7024 >> [ 11:38:16.901]  Thread csr Acquire
7024 >> [ 11:38:16.971] Thread csr Release
7024 >> [ 11:38:16.971]  Thread csr Acquire
7024 >> [ 11:38:17.041] Thread csr Release
4036 >> [ 11:38:17.041] ReadADC Acquire csr
4036 >> [ 11:38:17.041] Reset Event OK
4036 >> [ 11:38:17.041] Set NeedCopy Flag To True
4036 >> [ 11:38:17.041] Release csr
4036 >> [ 11:38:17.041]  Wait Copy Event
7024 >> [ 11:38:17.041]  Thread csr Acquire
7024 >> [ 11:38:17.111] Copy Samples
7024 >> [ 11:38:17.111] PArms OK
7024 >> [ 11:38:17.111] Copy OK
7024 >> [ 11:38:17.111] Stay Inner Copy Buffer
7024 >> [ 11:38:17.111] Clear Copy Flag
7024 >> [ 11:38:17.111] Set Event

Хорошо видно что поток съема успевает несколько раз занять и осовободить критическую секцию csr прежде чем основной поток наконец в нее войдет. Как это понимать? И что с этим можно сделать ?

#4 Техническая поддержка » Архитектура построения ПО для съема данных с двух АЦП LTR24 » 09.07.2019 11:25:37

СергейC
Ответов: 5

Здравствуйте!
Хотел бы проконсультироваться по поводу выбора архитектуры построения ПО для доступа к АЦП LTR24 в крейте LTR-EU-2. Задача состоит в том чтобы установить определенный датчик, находящийся на опорно-поворотном устройстве в определенное положение, затем считать с помощью АЦП набор отсчетов с этого датчика и записать в файл. И так для всех заранее заданных позиций. То есть по сути у меня нет необходимости в непрерывном считывании с АЦП отсчетов. Вначале я хотел было делать просто. То есть стартовать АЦП в нужные моменты, считывать разово буфер отсчетов с помощью LTR24_Recv , преобразовывать данные с помощью LTR24_ProcessData ну и далее копировать с преобразованием в поканальный буфер (преобразование заключается в том чтобы последовательность каналов превратить в поканальные данные). После всего этого просто останавливать АЦП. Но выяснилось что операция останова АЦП может занимать заметное время. Поэтому пришел в голову другой вариант. Сделать поток в котором производить непрерывный съем данных с преобразованием, а копирование данных из этого потока в промежуточный буфер делать по требования основного потока в нужные моменты. Но тут начались странности в работе ПО.
Ниже привожу код потока. Прошу прощения за длинноты, стараюсь максимально подробно

 // Потокобезопасно получаем значение флага приостановки
  TInterlocked.Exchange(Pointer(bs), Pointer(bStop));
  if bs then // Если необходимо
  begin
    WriteLnProtocolIndirect(' Thread Suspended ');
    evResume.WaitFor(); // Приостанавливаем поток
  end;
  while not Terminated do
  begin
    csr.Acquire;// Входим в критическую секцию
    WriteLnProtocolIndirect(' Thread csr Acquire');
    if Assigned(adcR) then // Если инициализирован потоковый интерфейс с АЦП
    begin
//      WriteLnProtocolIndirect('Run');
      adcR.Run; // Вызываем потоковые функции АЦП (Вот здесь выполняются Recv и ProcessData)
    end;
    // Получаем значение флага необходимости копирования
    TInterlocked.Exchange(Pointer(bc), Pointer(bnc));
    if bc then // Если необходимо
    begin
      WriteLnProtocolIndirect('Copy Samples');
      // Здесь копирование в буфер если интерфейс с АЦП передан, указатель
      // на целевой буфер задан и количество данных в допустимых границах
      if Assigned(adc) And Assigned(pCopyData) And (nCopyCount > 0) And
        (nCopyCount <= nMaxCount) then
      begin
        // Копируем
        WriteLnProtocolIndirect('PArms OK'); // Эта функция сбрасывает сообщение в очередь потока - логгера, который записывает в  лог-файл
        adc.getData(pCopyData, nCopyCount);// Копирование в заданный буфер
        WriteLnProtocolIndirect('Copy OK');
        if Assigned(vBuffer) then // Если внутренний буфер инициализирован
        begin
          // Устанавливаем на него указатель копирования по умолчанию
          WriteLnProtocolIndirect('Stay Inner Copy Buffer');
          pCopyData := @vBuffer[0];
          nCopyCount := nMaxCount; // И длину данных сообразно размеру буфера
        end
        else // Иначе
        begin
          pCopyData := nil; // Устанавливаем указатель копирования в нуль
          nCopyCount := 0; // И количество копируемых данных в нуль
        end;


      end;
      // Сбрасываем флаг необходимости копирования (операция разовая)
      TInterlocked.Exchange(Pointer(bnc), Pointer(False));
      WriteLnProtocolIndirect('Clear Copy Flag');
      evCopyAll.SetEvent; // Сигнализируем об окончании копирования
      WriteLnProtocolIndirect('Set Event');
    end;
    csr.Release; // Выходим из критической секции
    WriteLnProtocolIndirect('Thread csr Release');
    // Потокобезопасно получаем значение флага приостановки
    TInterlocked.Exchange(Pointer(bs), Pointer(bStop));
    if bs then // Если необходимо
    begin
      WriteLnProtocolIndirect(' Thread Suspended ');
      evSuspend.SetEvent; // Уведомляем о приостановке потока
      evResume.WaitFor(); // Приостанавливаем поток
    end;
  end;
  WriteLnProtocolIndirect('Thread Terminated');

А вот код функции которая вызывается из главного потока:

  Result := 0; // По умолчанию ничего не считано
  // Проверка параметров
  // Если задан внешний буфер необходимо задать и размер копирования
  if Assigned(pData) then
  begin
    if nCount < 1 then
    begin
      Exit;
    end;

    if nCount > nMaxCount then
    begin
      Exit;
    end;
  end;


  csf.Acquire; // Входим в критическую секцию потокобезопасного вызова функции

  // Потокобезопасно получаем значение флага приостановки потока
  TInterlocked.Exchange(Pointer(bs), Pointer(bStop));
  if (not Suspended) And (not bs) then // Если поток выполнения запущен
  begin




      // Входим в критическую секцию атомарного выполнения съема плюс
      // копирование
      WriteLnProtocolIndirect(' READ ADC Try Acquire csr');
      csr.Acquire;
      WriteLnProtocolIndirect('ReadADC Acquire csr');
      // Устанавливаем параметры копирования
      if Assigned(pData) then
      begin
        TInterlocked.Exchange(Pointer(pCopyData), Pointer(pData));
        TInterlocked.Exchange(nCopyCount, nCount);
      end;

      // Сбрасываем предварительно событие окончания съема плюс копирование
      evCopyAll.ResetEvent;
      WriteLnProtocolIndirect('Reset Event OK');
      // Устанавливаем флаг необходимости копирования данных в следующем цикле
      // съема
      TInterlocked.Exchange(Pointer(bnc), Pointer(True));
      WriteLnProtocolIndirect('Set NeedCopy Flag To True');
      csr.Release; // Выходим из критической секции атомарного выполнения
      WriteLnProtocolIndirect('Release csr');
      WriteLnProtocolIndirect(' Wait Copy Event ');
      evCopyAll.WaitFor(); // Ожидаем окончания операции съем плюс копирования

  end;
  csf.Release; // Заканчиваем вызов функции выходом из критической секции

Возникает странная вещь. Критическая секция csr используется для того чтобы гарантировать непрерывность цикла съема плюс копирование относительно функции из главного потока. Но лог файл показывает что при вызове функции главного потока возникает задержка то есть поток съема успевает несколько раз занять/освободить csr прежде чем основной поток таки войдет в эту критическую секцию и установит все необходимые атрибуты для копирования. Я понимаю что у меня в потоке съема задержка составляет порядка 70 мс (накапливается 4096 отсчетов при частоте дискретизации LTR24_FREQ_58K). Может кто - нибудь сталкивался с похожей задачей и можно как то более элегантно выйти из положения чем городить то что я нагородил? Или может быть проблема в потоке логгирования? Каждый вызов WriteLnProtocolIndirect содержит внутри вход в критическую секцию и ее освобождение в конце функции. Прошу прощения за длинну кода, буду рад Любой Информации. Может быть есть какие - то примеры кода как правльно делать подобные вещи для данного АЦП ???

#5 Re: Техническая поддержка » Синхронизация начала съема с двух АЦП в крейте » 13.04.2019 23:03:14

Алексей, спасибо большое! В принципе вся схема обработки понятна. И еще один вопрос возник. Если я в функции LTR24_ProcessData  не ставлю флаг перевода в вольты, что за значения я получаю после преобразования? Это знаковые числа в рамках разрядной сетки АЦП? То есть если я ставлю формат 24 бита то это 23 бита со знаком ? Просто мне желательно после съема данных сохранять их в формате wav. И хотелось бы понять как лучше преобразовывать данные после обработки.

#6 Re: Техническая поддержка » Синхронизация начала съема с двух АЦП в крейте » 12.04.2019 11:43:59

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

if (i1 > -1) And (i0 > -1) then
begin
        sResult := LTR24_FindFrameStart(ltrModule1, rBuf[i0], N - i0, i00);
        sResult := LTR24_FindFrameStart(ltrModule2, rBuf2[i1], N2 - i1, i11);
        Inc(i00, i0);
        Inc(i11, i1);

        Move(rBuf[i00], rBuf[0], (N - i00) SHL 2);
        LTR24_Recv(ltrModule1, rBuf[N - i00], tmark, i00, 0);
        Move(rBuf2[i11], rBuf2[0], (N2 - i11) SHL 2);
        LTR24_Recv(ltrModule2, rBuf2[N2 - i11], tmark2, i11, 0);

       sX := N;
        sResult := LTR24_ProcessData(ltrModule1, rBuf, vBuf, sX,
          LTR24_PROC_FLAG_CALIBR Or //LTR24_PROC_FLAG_VOLT Or
            LTR24_PROC_FLAG_AFC_COR_EX);

        sX := N2;
        sResult := LTR24_ProcessData(ltrModule2, rBuf2, vBuf, sX,
          LTR24_PROC_FLAG_CALIBR Or //LTR24_PROC_FLAG_VOLT Or
            LTR24_PROC_FLAG_AFC_COR_EX);

        sResult := LTR24_Recv(ltrModule1, rBuf, tmark, N, 0);
        sResult := LTR24_Recv(ltrModule2, rBuf2, tmark2, N2, 0);

        sResult := LTR24_Recv(ltrModule1, rBuf, tmark, N, 0);
        sResult := LTR24_Recv(ltrModule2, rBuf2, tmark2, N2, 0);

      sX := N;
      sResult := LTR24_ProcessData(ltrModule1, rBuf, vBuf, sX,
        LTR24_PROC_FLAG_CALIBR Or //LTR24_PROC_FLAG_VOLT Or
          LTR24_PROC_FLAG_AFC_COR_EX);

      sX := N2;
      sResult := LTR24_ProcessData(ltrModule2, rBuf2, vBuf, sX,
        LTR24_PROC_FLAG_CALIBR Or //LTR24_PROC_FLAG_VOLT Or
          LTR24_PROC_FLAG_AFC_COR_EX);
end;

Первые два вызова LTR24rocessData проходят успешно. Ожидал получить ошибку при вызове функций LTR24_ProcessData после двух подряд вызовов LTR24_Recv. Вроде бы нарушается непрерывность потока данных для обработки. Но почему то функции вернули 0. Ошибки не возникло. Это нормальное поведение ?

#7 Re: Техническая поддержка » Синхронизация начала съема с двух АЦП в крейте » 12.04.2019 11:35:39

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

#8 Re: Техническая поддержка » Синхронизация начала съема с двух АЦП в крейте » 12.04.2019 10:58:49

Алексей, извините, назвал Вас Александром. Под вечер уже путаться начинаю smile

#9 Re: Техническая поддержка » Синхронизация начала съема с двух АЦП в крейте » 11.04.2019 23:40:05

Спасибо большое, Александр!
Теперь понятно как использовать синхрометки. Подскажите я правильно понимаю, что далее, после того как я получил позицию прихода синхрометки в каждом из буферов АЦП, я должен далее найти относительно этих позиций начало ближайшего следующего кадра с помощью функций LTR24_FindFrameStart, и обрабатывать данные уже начиная с этих новых позиций, обеспечивая синхронную обработку по времени? Вот примерный алгоритм:

     
if (i1 > -1) And (i0 > -1) then
begin
        sResult := LTR24_FindFrameStart(ltrModule1, rBuf[i0], N - i0, i00);
        sResult := LTR24_FindFrameStart(ltrModule2, rBuf2[i1], N2 - i1, i11);
        Inc(i00, i0);
        Inc(i11, i1);

        Move(rBuf[i00], rBuf[0], (N - i00) SHL 2);
        LTR24_Recv(ltrModule1, rBuf[N - i00], tmark, i00, 0);
        Move(rBuf2[i11], rBuf2[0], (N2 - i11) SHL 2);
        LTR24_Recv(ltrModule2, rBuf2[N2 - i11], tmark2, i11, 0);
end;

Далее уже можно считывать данные побуферно без сдвижек. И вот еще хотел узнать, можно ли вызывать далее функцию LTR24_ProcessData обычным образом, несмотря на то что для первых нескольких вызовов LTR24_Recv во время поиска синхрометки я ее не вызывал? Не будет ли ошибки при ее вызове? Понятно что в дальнейшей обработке я буду вызывать ее для каждого вызова LTR24_Recv.

#10 Re: Техническая поддержка » Синхронизация начала съема с двух АЦП в крейте » 10.04.2019 23:21:03

Алексей L Card пишет:

Добрый день.

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

Спасибо большое, Алексей! В принципе по сути текущей задачи съемы данных нужны в определенные моменты времени а не непрерывно. Поэтому изначально была мысль сделать считывание данных между LTR24_Start и LTR24_Stop. Но, помимо необходимости синхронизации потока данных от двух разных АЦП в одном крейте,  мы заметили что функция LTR24_Stop занимает довольно продолжительное время. Поэтому скорее склоняемся к мысли сделать непрерывное считывание данных в течение всего цикла работы, а использовать считанные данные в нужные моменты времени. А в процессе начального запуска программы дать команды на старт считывания и, после посылки синхрометки, в цикле ждать изменения счетчиков меток для обоих АЦП, чтобы синхронизировать считываемые потоки.
Алексей, но вот возникает такой вопрос, а не может ли сложиться такая ситуация, что для какого - нибудь из АЦП синхрометка прийдет либо раньше, либо вместе с первым же отсчетом первого канала ? То есть не может ли сложиться так, что ожидание изменения счетчика меток для этого АЦП будет бессмысленным ?

#11 Техническая поддержка » Синхронизация начала съема с двух АЦП в крейте » 09.04.2019 23:21:19

СергейC
Ответов: 10

Здравствуйте! Приобрели два АЦП LTR24 в крейте LTR-EU-2-5  и хотим сделать синхронный съем данных по шести каналам. Для этого в программе делаем подключения к крейту (для генерирования синхрометок) и к каждому модулю АЦП в крейте, задаем одинаковую частоту дискретизации(код LTR24_FREQ_58K) для каждого АЦП. В одном из них инициализируем и разрешаем все четыре канала в другом только два. Далее пытаемся в качестве теста запустить съем, послать синхрометку и выбрать стартовые отсчеты в каждом из двух массивов отсчетов, которые помечены синхрометкой. Вот ключевой участок кода (на Delphi):

	........
      N := 2048 * 4;
      N2 := 2048 * 2;
	........
      sResult := LTR24_Start(ltrModule1); // Запускаем съем с первого АЦП
      sResult := LTR24_Start(ltrModule2); // Запускаем съем со второго АЦП
      sResult := LTR_MakeStartMark(ltrCrate, LTR_MARK_INTERNAL); // Генерируем синхрометку СТАРТ
      sResult := LTR24_Recv(ltrModule1, rBuf, tmark, N, 0); // Принимаем данные от первого АЦП
      sResult := LTR24_Recv(ltrModule2, rBuf2, tmark2, N2, 0); // Принимаем данные от второго АЦП
      sResult := LTR24_Stop(ltrModule1); // Останавливаем съем с первого АЦП
      sResult := LTR24_Stop(ltrModule2); // Останавливаем съем со второго АЦП

Сразу оговорюсь код сугубо тестовый без всяких проверок. пытаюсь считать 2048 отсчетов. При этом возникают ситуации когда синхрометка вообще не успевает прийти в один из АЦП или в другом приходит практически после половины принятых отсчетов. Я отслеживаю приход синхрометки по изменению счетчика в массивах tmark и tmark2. С чем это может быть связано ? Действительно настолько долго происходит генерация синхрометки ? Или нужно как то по иному программировать синхронный съем данных ? В принципе в этой задаче я могу обойтись и без синхронного съема, но, в будущем, боюсь без этого будет трудно обойтись. Буду рад любой информации.

#12 Re: Техническая поддержка » L-502 Повторный запуск съема данных и генерации. » 15.12.2015 11:21:39

Здравствуйте, Алексей! Спасибо за ответ. Хотелось бы вот что еще узнать.. Если я правильно Вас понял, после подобной процедуры рестарта заполнение буфера драйвера начнется опять с начала ? И еще, я забыл упомянуть что сигнал выходной у меня циклический то есть я делал инициализацию функциями L502_OutCycleLoadStart, L502_PrepareData, L502_Send, L502_OutCycleSetup. Нужно ли повторять всю процедуру инициализации вывода с начала при рестарте ?

#13 Техническая поддержка » L-502 Повторный запуск съема данных и генерации. » 14.12.2015 18:27:17

СергейC
Ответов: 3

Здравствуйте! При использовании платы L-502 возник следущий вопрос. Какой минимальный набор действий необходим для остановки непрерывного съема данных и перезапуска? При этом плата настроена на сбор данных со всех 32 каналов, генерацию сигнала с цифровых выходов и прием этого же сигнала на цифровых входах. Можно ли просто остановить потоки данных функцией L502_StreamsStop. А потом снова запустить процесс сначала функцией L502_StreamsStart, при этом не делая никаких дополнительных перенастроек ?

#15 Техническая поддержка » L502 глубокое зависание в функции L502_Recv » 08.12.2015 14:26:35

СергейC
Ответов: 2

Здравствуйте !
Наша система содержит два АЦП, соединенных между собой кабелем синхронизации для синхронной работы. Помимо этого ведущий АЦП одновременно генерирует сигнал(меандр скважность 2) синхронизации с цифровых выходов на внешнее устройство который вводится еще и на цифровые входы того же АЦП. Идея в том чтобы разделять принятые отсчеты во всех 64 каналах АЦП относительно полупериода(нулевой или ненулевой)  сигнала с цифровых выходов. Программа для обработки данных написана на Delphi в среде Embarcadero RAD Studio XE2. Организован поток для сбора данных с АЦП в котором происходит чтение функцией L502_Recv, разделение на отсчеты АЦП и цифровые отсчеты (для ведущего АЦП) (функция реализована самостоятельно PreprocessData не вызывается) и так называемое транспонирование данных для того чтобы отсчеты каждого канала шли друг за другом в памяти вместо того чтобы идти группами поканально. Больше ничего в потоке не выполняется. В основном потоке программы реализованы сообщения таймера. Там производится копирование уже преобразованных данных из буфера потока в основной буфер, защищенное критической секцией, и дальнейшая обработка с выводом на графики. Очень редко, в случайные моменты времени, происходит зависание потока сбора данных на функции L502_Recv. Последней точкой стека вызовов является вызов функции CancelIoRequest уже внутри L502_Recv. Зависание очень глубокое. Попытка отменить выполнение программы приводит к зависанию среды программирования. Причем "выбить" программу из памяти невозможно ничем и даже перезагрузка системы не выполняется до конца приходится нажимать кнопку Reset. Работаю в системе Windows 7 x64. Возникает мысль что это зависание на уровне драйвера устройства. Неужели наша программа каким то непостижимым образом портит буфер для считывания данных и таким обрзом что зависание происходит в самом драйвере ???Может быть кто - нибудь сталкивался с такой проблемой до меня ?? Сразу скажу я работаю не с последней версией драйверов и библиотек для платы L-502. Мой инсталлятор lpcie_setup.exe датируется 20.01.2014.

#16 Re: Техническая поддержка » Многомодульный режим работы L-502 » 28.10.2015 15:26:05

Спасибо, Александр! Учтем ситуацию с гальваноразвязкой. Будем пробовать.

#17 Re: Техническая поддержка » Многомодульный режим работы L-502 » 28.10.2015 15:09:05

И вот только что еще вопросик возник. При соединении двух плат кабелем межмодульной синхронизации в принципе можно будет использовать их по отдельности как независимые устройства, сконфигурировав обе независимо ? Не помешает ли это соединение ?

#18 Re: Техническая поддержка » Многомодульный режим работы L-502 » 28.10.2015 15:04:47

Спасибо, Алексей! Будем пробовать. Если возникнут проблемы или вопросы обязательно напишем!

#19 Re: Техническая поддержка » Многомодульный режим работы L-502 » 28.10.2015 14:51:31

Спасибо, Александр! А насчет инсталлятора можете подсказать можно переставить поверх прежнего ?

#21 Re: Техническая поддержка » Многомодульный режим работы L-502 » 28.10.2015 14:18:45

Спасибо, ситуация с ведущим - ведомым в принципе понятна. Серийный номер - хороший способ привязки. Возникает еще несколько вопросов. Мы скачали с сайта обновленную версию инсталлятора ПО для платы. Ее можно поставить вместо прежней с гарантией что старые программы под эти платы будут работать ?
И еще вопрос - а нельзя ли засинхронизировать оба модуля от внешнего генератора с частотой 2 МГц.? Не решит ли это проблему синхронизации на данной частоте ?

#22 Техническая поддержка » Многомодульный режим работы L-502 » 28.10.2015 12:31:48

СергейC
Ответов: 10

Здравствуйте! Примерно год назад приобрели у Вас 2 платы АЦП L-502-P-G. До сего времени использовали их только в одномодульном режиме с максимальной частотой синхронизации 2 МГц. Теперь решили попробовать многомодульную конфигурацию. Кабель синхронизации приобрели заранее. В документации есть такой пункт что для ведомого модуля L-502 имеется ограничение частоты на вхоже CONV_IN - не более 1.5 МГц. Означает ли это что при наличии одного ведущего и одного ведомого модуля L-502 частота синхронизации такой многомодульной конфигурации не может быть выше чем 1.5 МГц.? Хотелось бы оставить частоту синхронизации 2 МГц.
И второй вопрос - существуют ли особенности программной инициализации и использования ведомого модуля L-502? И как в программе определить ведущий и ведомый модули ?

Контакты

Адрес: 117105, Москва, Варшавское шоссе, д. 5, корп. 4, стр. 2

Многоканальный телефон:
+7 (495) 785-95-25

Отдел продаж: sale@lcard.ru
Техническая поддержка: support@lcard.ru

Время работы: с 9-00 до 19-00 мск