using System;
using ltrModulesNet;


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


namespace ltr216_console
{
    class ltr216_console
    {
         /* Номер слота в крейте, где вставлен модуль */
        const int SLOT = 2;

        const int LCH_CNT = 3;
        /* количество отсчетов на канал, принмаемых за раз */
        const int RECV_BLOCK_FRAMES_CNT = 100 / LCH_CNT;
        /* Таймаут на ожидание данных при приеме (без учета времени преобразования) */
        const int RECV_TOUT = 4000;
        /* Количество блоков, которые нужно принять и выйти */
        const int RECV_BLOCK_CNT = 10;



        /* вывод строки, соответствующий коду статуса фонового измерения */
        static void f_print_meas_status(ltr216api.MeasStatus status)
        {
            switch (status)
            {
                case ltr216api.MeasStatus.NOT_USED:  Console.Write("Не исп."); break;
                case ltr216api.MeasStatus.OK: Console.Write("Ок"); break;
                case ltr216api.MeasStatus.NOT_INIT: Console.Write("Иниц."); break;
                case ltr216api.MeasStatus.ADC_OVERRANGE: Console.Write("Зашкал"); break;
                case ltr216api.MeasStatus.SHORT: Console.Write("КЗ"); break;
                case ltr216api.MeasStatus.OPEN: Console.Write("Обр."); break;
                case ltr216api.MeasStatus.BAD_VALUE_RANGE: Console.Write("Вне диап."); break;
                case ltr216api.MeasStatus.CANT_CALC: Console.Write("Не выч."); break;
                default: Console.Write(status); break;
            }
        }


        /* вывод значения измерения, если оно действительно, или статуса в противном случае */
        static void print_meas_valstate(ltr216api.MEASSTATE  meas)
        {
            if (meas.ValueValid) {
                Console.Write(meas.Value.ToString("F3"));
            } else {
                f_print_meas_status(meas.Status);
            }
        }

        /* вывод значения и состояния Uref (при четырехпроводной схеме вместе со значением
        * UrefR) со статусами */
        static void print_uref(ltr216api.DATA_STATUS status)
        {
            Console.Write("Uref = ");
            print_meas_valstate(status.Uref);
            if (status.UrefR.Status != ltr216api.MeasStatus.NOT_USED)
            {
                Console.Write(", UrefR = ");
                print_meas_valstate(status.UrefR);
            }

        }

        static int Main(string[] args)
        {          
            /* LTR11_Init() вызывается уже в конструкторе */
            ltr216api hltr216 = new ltr216api();
            /* отрываем модуль. Используем упрощенный вариант функции с указанием только слота.
             * (есть вариант как с только со слотом, так и с серийным крейта и слотом 
             *  + полный) */
            _LTRNative.LTRERROR err = hltr216.Open(SLOT);
            if (err != _LTRNative.LTRERROR.OK)
            {
                Console.WriteLine("Не удалось открыть модуль. Ошибка {0}: {1}",
                    err, ltr216api.GetErrorString(err));
            }
            else
            {
               
                /* выводим информацию из hltr216.ModuleInfo */
                Console.WriteLine("Информация о модуле: ");
                Console.WriteLine("  Название модуля: {0}", hltr216.ModuleInfo.Name);
                Console.WriteLine("  Серийный номер : {0}", hltr216.ModuleInfo.Serial);
                Console.WriteLine("  Версия прошивки: {0}", hltr216.ModuleInfo.VerFPGA);

                /* --------------- задание параметров работы модуля ------------ */

                /* для заполнения полей конфируации используется отдельная структура.
                 * сначала получаем копию текущих настроек из поля Cfg описателя, 
                 * изменяем нужные поле, после чего записываем измененую конфигруацию
                 * обратно в поле Cfg описатиля и передаем настройки в модуль с помощью SetADC */
                ltr216api.CONFIG Cfg = hltr216.Cfg;

                /* Настройка модуля */
                /* многоканальный режим */
                Cfg.AdcSwMode = ltr216api.AdcSwModes.MULTICH_SYNC;
                /* частота АЦП - 10 КГц */
                Cfg.FillSyncFreqDiv(10000);
                /* Время на коммутация - 20 мкс (типичная длина 7-10 м) */
                Cfg.AdcMinSwTimeUs = 20;
                /* Заполняем параметры фильтра, исходя из частоты АЦП и времени на коммутацию */
                Cfg.FillFilterParams(0);
                /* Устанавливаем количество опрашиваемых каналов */
                Cfg.LChCnt = LCH_CNT;
                for (int ch_idx = 0; ch_idx < Cfg.LChCnt; ch_idx++)
                {
                    Cfg.SetLChannel(ch_idx, (byte)ch_idx, ltr216api.AdcRanges.Range_35);
                }

                /* выбор фоновых измерений - включаем все */
                Cfg.BgMeas = ltr216api.BgMeasTypes.MEASGROUP_ALL;
                /* Ток питания датчиков - 25 мА (используем калибровки для более точного подбора из ModuleInfo) */
                Cfg.FillISrcCode(hltr216.ModuleInfo.FabricCbr.ISrc, 25);

                /* 4-х проводная схема подключения опорного датчика */
                Cfg.Ch16ForUref = true;
                /* без тарировки */
                Cfg.TareEnabled = false;
                /* Сохраняем конфигурацию в описателе модуля */
                hltr216.Cfg = Cfg;

                /* передача установленных настроек в модуль */
                err = hltr216.SetADC();
                if (err != _LTRNative.LTRERROR.OK)
                {
                    Console.WriteLine("Не удалось установить настройки модуля. Ошибка {0}: {1}",
                         err, ltr216api.GetErrorString(err));
                }
                else
                {
                    /* после SetADC() обновляется поле AdcFreq и EnabledChCnt. Становится равной действительной
                     * установленной частоте */
                    Console.WriteLine("Модуль настроен успешно. Установленная частота АЦП {0}, частот на канал {1}, размер кадра: {2}",
                            hltr216.State.AdcFreq.ToString("F2"), hltr216.State.FrameFreq.ToString("F2"), hltr216.State.FrameWordsCount);
                }                
            }

            if (err == _LTRNative.LTRERROR.OK)
            {
                ltr216api.DATA_STATUS data_status;

                /* измерение собственного нуля перед сбором для учета в результатах измерения */
                err = hltr216.InitMeasParams(ltr216api.InitMeasTypes.ALL, ltr216api.BgMeasTypes.MEASGROUP_UREF_CHECK, out data_status);
                if (err != _LTRNative.LTRERROR.OK)
                {
                    Console.WriteLine("Ошибка иизмерение начальных параметров. Ошибка {0}: {1}",
                         err, ltr216api.GetErrorString(err));
                }
                else
                {
                    Console.WriteLine("Измерение начальных параметров выполнено успешно");
                }

                 print_uref(data_status);
                 Console.WriteLine();
            }

            if (err == _LTRNative.LTRERROR.OK)
            {
                /* количество отсчетов данных, которые хотим принимать за блок */
                int recv_data_cnt = RECV_BLOCK_FRAMES_CNT * (int)hltr216.Cfg.LChCnt;
                /* количество слов, соответствующее этому количеству данных,
                 * исходя из размера кадра */
                int recv_wrd_cnt = RECV_BLOCK_FRAMES_CNT * (int)hltr216.State.FrameWordsCount;
                 /* выделяем соответствующие массивы */
                uint[] rbuf = new uint[recv_wrd_cnt];
                double[] data = new double[recv_data_cnt];

                 /* запуск сбора данных */
                err = hltr216.Start();
                if (err != _LTRNative.LTRERROR.OK)
                {
                    Console.WriteLine("Не удалось запустить сбор данных. Ошибка {0}: {1}",
                         err, ltr12api.GetErrorString(err));
                }
                else
                {
                    _LTRNative.LTRERROR stop_err;
                    for (int i = 0; (i < RECV_BLOCK_CNT) &&
                            (err == _LTRNative.LTRERROR.OK); i++)
                    {
                        int rcv_cnt;
                        /* В таймауте учитываем время сбора запрашиваемого числа отсчетов */
                        uint tout = RECV_TOUT + (uint)((double)1000*RECV_BLOCK_FRAMES_CNT/
                                                         hltr216.State.FrameFreq + 1);
                        /* прием необработанных слов. есть варинант с tmark и без него для удобства */
                        rcv_cnt = hltr216.Recv(rbuf, (uint)rbuf.Length, tout);

                        /* значение меньше 0 => код ошибки */
                        if (rcv_cnt < 0)
                        {
                            err = (_LTRNative.LTRERROR)rcv_cnt;
                            Console.WriteLine("Ошибка приема данных. Ошибка {0}: {1}",
                                              err, ltr216api.GetErrorString(err));
                        }
                        else if (rcv_cnt != rbuf.Length)
                        {
                            err = _LTRNative.LTRERROR.ERROR_RECV_INSUFFICIENT_DATA;
                            Console.WriteLine("Приняли недостаточно данных: запрашивали {0}, приняли {1}",
                                              rbuf.Length, rcv_cnt);
                        }
                        else
                        {
                            ltr216api.DATA_STATUS data_status;
                            ltr216api.DATA_CH_STATUS[] ch_status_list;

                            err = hltr216.ProcessData(rbuf, data, ref rcv_cnt, ltr216api.ProcFlags.MEAS_UNBALANCE,
                                out data_status, out ch_status_list);
                            if (err != _LTRNative.LTRERROR.OK)
                            {
                                Console.WriteLine("Ошибка обработки данных. Ошибка {0}: {1}",
                                                   err, ltr216api.GetErrorString(err));

                            } else {
                                /* выводим параметры блока */
                                Console.Write("Блок {0} (размер = {1}: ", i + 1, rcv_cnt);
                                /* выводим состояние uref для справки */
                                print_uref(data_status);
                                Console.Write("): ");

                                /* выводим для примера по одному значению на
                                 * разрешенный канал */
                                for (uint ch=0; (ch < hltr216.Cfg.LChCnt) ; ch++) {
                                    /* Проверяем статус результирующего
                                     * измерения, чтобы не выводить явно не действительное
                                     * значение. При этом более подробный
                                     * анализ состояния всех флагов контроля по
                                     * всем фоновым измерениям в данном примере не
                                     * приводятся для краткости */
                                    if (ch_status_list[ch].Ures.ValueValid) {
                                        Console.Write("{0}", ((double)1000*data[ch]).ToString("F3"));
                                    } else {
                                        Console.Write("{0}", ch_status_list[ch].Ures.Status);
                                    }

                                    if (ch==(hltr216.Cfg.LChCnt-1)) {
                                        Console.Write("\n");
                                    } else {
                                        Console.Write(",  ");
                                    }
                                }
                            }
                        }
                    }

                    /* останавливаем сбор данных */
                    stop_err = hltr216.Stop();
                    if (stop_err != _LTRNative.LTRERROR.OK)
                    {
                        Console.WriteLine("Не удалось остановить сбор данных. Ошибка {0}: {1}",
                             stop_err, ltr216api.GetErrorString(stop_err));
                        if (err == _LTRNative.LTRERROR.OK)
                            err = stop_err;
                    }      
                }

            }
            

            /* закрываем соединение */
            if (hltr216.IsOpened() == _LTRNative.LTRERROR.OK)
                hltr216.Close();

            return (int)err;
        }
    }
}
