/* Данный пример демонстрирует использование некотрых дополнительных
   спецчифчных для модуля E20-10 режимов:
     - передача тестовой последовательности вместо данных АЦП для проверки
       непрерывности и корректности передаваемого потока данных
     - проверка информации о переполнении буфера модуля во время сбора
     - вывод информации о прошивке и загрузчике AVR, темп. диапазон модуля
   Пример также подсчитывает и выводит дополнительную статистику по завершению
   сбора данных.
     */



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <pthread.h>
#include <stdint.h>

#include <iomanip>
#include <iostream>
#include <fstream>

#include <math.h>

using namespace std;

#define INITGUID

#include "lcomp/e2010cmd.h"
#include "lcomp/ifc_ldev.h"
#include <errno.h>

#include <signal.h>
#include <unistd.h>
#include <getopt.h>

typedef IDaqLDevice* (*CREATEFUNCPTR)(ULONG Slot);

#define DEV_BUF_SIZE    106496
#define DEV_IRQ_STEP    4096
#define DEV_ACQ_FREQ    10000000


unsigned short *p;
volatile unsigned int *pp;

/* признак необходимости завершить сбор данных */
static int f_out = 0;
#ifndef _WIN32
/* Обработчик сигнала завершения для Linux */
static void f_abort_handler(int sig) {
    (void)sig;
    f_out = 1;
}
#endif

//Att. for board slot numbers!!!!

int main(int argc, char **argv) {
    int err = L_SUCCESS;
#ifndef _WIN32
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    /* В ОС Linux устанавливаем свой обработчик на сигнал закрытия,
       чтобы завершить сбор корректно */
    sa.sa_handler = f_abort_handler;
    sigaction(SIGTERM, &sa, NULL);
    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGABRT, &sa, NULL);
#endif

    int devslot = 0;
    const char *fwname = NULL;
    if (argc < 3) {
        cerr << "insufficiend arguments count";
    } else {
        devslot = atoi(argv[1]);
        fwname = argv[2];
    }
    void *handle = NULL;
    CREATEFUNCPTR CreateInstance = NULL;
    IDaqLDevice* pI = NULL;
    PLATA_DESCR_U2 pd;
    SLOT_PAR sl;
    ADC_PAR adcPar;
    ULONG size;


    unsigned mcu_ver_h = 0, mcu_ver_l = 0;

    if (err == L_SUCCESS) {
        handle = dlopen("liblcomp.so", RTLD_LAZY);
        if(!handle) {
            cout << "error open dll!! " << dlerror() << endl;
            err = -2;
        }
    }

    if (err == L_SUCCESS) {
        CreateInstance = (CREATEFUNCPTR) dlsym(handle,"CreateInstance");
        if (!CreateInstance) {
            cout << "load symbol CreateInstance error " << dlerror() << endl;
            err = -3;
        }
    }


    if (err == L_SUCCESS) {
        LUnknown* pIUnknown = CreateInstance(devslot);
        if(pIUnknown == NULL) {
            cout << "Call CreateInstance failed " << errno << endl;
            err = -4;
        } else {
            cout << "Get IDaqLDevice interface" << endl;

            HRESULT hr = pIUnknown->QueryInterface(IID_ILDEV,(void**)&pI);
            if (hr != S_OK) {
                cout << "Get IDaqLDevice failed" << endl;
                err = -5;
            } else {
                printf("IDaqLDevice get success \n");
            }
        }

        if (pIUnknown) {
            pIUnknown->Release();
            cout << "Free IUnknown" << endl;
        }
    }

    if (err == L_SUCCESS) {
        if (pI->OpenLDevice() == INVALID_HANDLE_VALUE) {
            cout << "open device error" << endl;
            err = -6;
        } else {
            err = pI->GetSlotParam(&sl);
            if (err != L_SUCCESS) {
                cout << "get slot param error: " << err << endl;
            }
        }
    }


    if (err == L_SUCCESS) {
        cout << endl << "Read FLASH" << endl;

        err = pI->ReadPlataDescr(&pd);
        if (err != L_SUCCESS) {
            cout << "Read plata descr failed: " << err << endl;
        } else {
            if (sl.BoardType == E2010B) {
                cout << "Module description:" << endl;
                cout << "  SerNum       " << pd.t6.SerNum << endl;
                cout << "  BrdName      " << pd.t6.BrdName << endl;
                cout << "  Rev          " << pd.t6.Rev << endl;
                cout << "  DspType      " << pd.t6.DspType << endl;
                cout << "  IsDacPresent " << (pd.t6.IsDacPresent ? "Yes" : "No") << endl;
                cout << "  Quartz       " << dec << pd.t6.Quartz << endl;
                cout << "  Modification " << hex << (unsigned)(pd.t6.Modification & 0x3) << dec << endl;
            } else {
                cout << "unsupported board type: " << sl.BoardType << endl;
                err = -7;
            }
        }
    }

    if (err == L_SUCCESS) {
        err = pI->LoadBios(fwname);
        if (err != L_SUCCESS) {
            cout << "Load bios failed: " << err << endl;
        }
    }

    if (err == L_SUCCESS) {
        err =  pI->PlataTest();
        if (err != L_SUCCESS) {
            cout << "Plate test failed: " << err << endl;
        }
    }


    if (err == L_SUCCESS) {
        size = DEV_BUF_SIZE;
        err = pI->RequestBufferStream(&size);
        if (err != L_SUCCESS) {
            cout << "request buffer stream error: " << err << endl;
        }
    }

    if (err == L_SUCCESS) {
        if (sl.BoardType == E2010B) {
            adcPar.t2.s_Type = L_ADC_PARAM;
            adcPar.t2.AutoInit = 0;
            adcPar.t2.dRate = DEV_ACQ_FREQ/1000;
            adcPar.t2.dKadr = 0.000;

            adcPar.t2.SynchroType = INT_START_TRANS;
            adcPar.t2.SynchroSrc = INT_CLK_TRANS;
            adcPar.t2.AdcIMask = SIG_0 | V30_0 | GND_1 | GND_2 | GND_3;
            adcPar.t2.DigRate = 0;

            adcPar.t2.NCh = 1;
            adcPar.t2.Chn[0] = 0;


            /* размер FIFO в E20-10 не настраивается и соответствующее поле
             * настроек никак не используется, поэтому можем его не заполнять */
            adcPar.t2.IrqStep = DEV_IRQ_STEP;
            adcPar.t2.Pages = size/DEV_IRQ_STEP;
            adcPar.t2.IrqEna = 1;
            adcPar.t2.AdcEna = 1;

            // extra sync mode
            adcPar.t2.StartCnt = 0;
            adcPar.t2.StopCnt = 0;
            adcPar.t2.DM_Ena = 0;
            adcPar.t2.SynchroMode = 0;
            adcPar.t2.AdPorog = 0;


            err = pI->FillDAQparameters(&adcPar.t2);
            if (err != L_SUCCESS) {
                cout << "fill daq parameters error: " << err << endl;
            }

            if (err == L_SUCCESS) {
                err = pI->SetParametersStream(&adcPar.t2, &size, (void **)&p, (void **)&pp,L_STREAM_ADC);
                if (err != L_SUCCESS) {
                    cout << "set daq parameters error: " << err << endl;
                }
            }

            if (err == L_SUCCESS) {
                cout << "Buffer size(word): " << size << endl;
                cout << "Pages:             " << adcPar.t2.Pages << endl;
                cout << "IrqStep:           " << adcPar.t2.IrqStep << endl;
                cout << "Rate:              " << adcPar.t2.dRate << " KHz" << endl;
                cout << "Kadr:              " << adcPar.t2.dKadr << " ms" << endl;
            }
        }

        if (err == L_SUCCESS) {
            err = pI->EnableCorrection();
            if (err != L_SUCCESS)
                cout << "Enabled correction error: " << err << endl;
        }

        for (int i = 0; i < 3; ++i) {
            cout << "iteration " << i << endl;
            if (err == L_SUCCESS) {
                err = pI->InitStartLDevice();
                if (err != L_SUCCESS)
                    cout << "init start error: " << err << endl;
            }

            if (err == L_SUCCESS) {
                err = pI->StartLDevice();
                if (err != L_SUCCESS) {
                    cout << "start device error: " << err << endl;
                }

                if (err == L_SUCCESS) {
                    cout << "Data acquisition started. Press CTRL+C for stop..." << endl;
                    for (int i = 0; (i < 80) && !f_out; ++i) {
                        usleep(1000);
                        cout << "adc_buffer_offset  " << *pp << "  data  " << *p << endl;
                    }
                } else {
                    f_out = 1;
                }

                cout << "Start stop device..." << endl;
                pI->StopLDevice();
                cout << "Done stop device..." << endl;
            }
        }
    }

    if (pI) {
        pI->CloseLDevice();
        pI->Release();
    }

    if(handle) {
        dlclose(handle);
    }

    return err;
}
