using System;
using ltrModulesNet;


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


namespace ltr43_read_stream
{
    class ltr43_read_stream
    {
         /* Номер слота в крейте, где вставлен модуль */
        const int SLOT = 13;
        /* Скорость чтения данных с цифровых линий в Гц (до 400000) */
        const double READ_RATE = 100000;
        /* Количество слов в считываемом за раз блоке */
        const uint RECV_BLOCK_SIZE = 50000;
        /* Количество блоков, которые нужно принять и выйти */
        const uint RECV_BLOCK_CNT  = 20;
        /* Таймаут на ожидание данных при приеме (без учета времени преобразования) */
        const int RECV_TOUT = 4000;

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

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


                /* направление портов ввода-вывода */
                ltr43api.IOPortsCfg ioports = new ltr43api.IOPortsCfg();
                ioports.Port1 = ltr43api.PortDir.OUT;
                ioports.Port2 = ltr43api.PortDir.OUT;
                ioports.Port3 = ltr43api.PortDir.IN;
                ioports.Port4 = ltr43api.PortDir.IN;
                hltr43.IO_Ports = ioports;
                hltr43.StreamReadRate = READ_RATE;

                err = hltr43.Config();
                if (err != _LTRNative.LTRERROR.OK)
                {
                    Console.WriteLine("Не удалось установить настройки модуля. Ошибка {0}: {1}",
                            err, ltr43api.GetErrorString(err));                    
                }
                                
                if (err == _LTRNative.LTRERROR.OK)
                {   
                    err = hltr43.StartStreamRead();
                    if (err != _LTRNative.LTRERROR.OK)
                    {
                        Console.WriteLine("Не удалось запустить потоковое чтение данных. Ошибка {0}: {1}",
                                err, ltr43api.GetErrorString(err));
                    }
                    else
                    {
                        _LTRNative.LTRERROR stop_err;
                        /* Буферы на прием. Сырых слов в 2 раза больше, чем результирующих,
                         * т.к. каждое 32-битное состояние входов передается за два слова LTR */
                        uint rcv_wrds_cnt = 2 * RECV_BLOCK_SIZE;
                        uint[] rbuf = new uint[rcv_wrds_cnt];
                        uint[] data = new uint[RECV_BLOCK_SIZE];

                        for (int i = 0; (i < RECV_BLOCK_CNT) &&
                                (err == _LTRNative.LTRERROR.OK); i++)
                        {
                            int rcv_cnt;
                            double rcv_rate = READ_RATE;
                            /* в таймауте учитываем время выполнения самого преобразования*/
                            uint tout = RECV_TOUT + (uint)((double)(RECV_BLOCK_SIZE ) / (rcv_rate/1000) + 1);
                            /* прием необработанных слов. есть варинант с tmark и без него для удобства */
                            rcv_cnt = hltr43.Recv(rbuf, (uint)rbuf.Length, tout);

                            /* значение меньше 0 => код ошибки */
                            if (rcv_cnt < 0)
                            {
                                err = (_LTRNative.LTRERROR)rcv_cnt;
                                Console.WriteLine("Ошибка приема данных. Ошибка {0}: {1}",
                                                  err, ltr43api.GetErrorString(err));
                            }
                            else if (rcv_cnt != rbuf.Length)
                            {
                                err = _LTRNative.LTRERROR.ERROR_RECV_INSUFFICIENT_DATA;
                                Console.WriteLine("Приняли недостаточно данных: запрашивали {0}, приняли {1}",
                                                  rbuf.Length, rcv_cnt);
                            }
                            else
                            {    
                                /* перевод принятых слов в состояния входов модуля */
                                err = hltr43.ProcessData(rbuf, data, ref rcv_cnt);
                                if (err != _LTRNative.LTRERROR.OK)
                                {
                                    Console.WriteLine("Ошибка обработки данных. Ошибка {0}: {1}",
                                                       err, ltr43api.GetErrorString(err));
                                }
                                else
                                {
                                    /* обработка принятого блока из остчетов data[0]..data[RECV_BLOCK_SIZE-1]. 
                                     * Каждое слово состоит из 32 бит, каждый из которых соответствует 
                                     * состоянию 32 линий модуля в кокретный момент времени.
                                     * Значение битов состояний линий, настроенных на выход, всегда равно 0. */                                    

                                    /* Для примера выводим состояние старших 16 линий для первого слова */
                                    uint msk;
                                    uint wrd = data[0];
                                    Console.Write("Успешно считан блок данных. первое слово (биты 31-16): ");
                                    for (msk = (1U << 31); msk != (1 << 15); msk >>= 1)
                                        Console.Write("{0} ", (wrd & msk) != 0 ? 1 : 0);
                                    Console.WriteLine("");
                                }
                            }
                        }

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

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

            return (int)err;
        }
    }
}
