#ifndef _LTRSRV_CRATES_H
#define _LTRSRV_CRATES_H

#include "config.h"
#include "ltr_crate_defs.h"
#include "ltimer.h"
#include "ltrsrv_cyclebuf.h"
#include "ltrsrv_params.h"

#include <time.h>


#define LTR_CRATE_DEVNAME_SIZE              32
#define LTR_CRATE_SERIAL_SIZE               16
#define LTR_CRATE_SOFTVER_SIZE              32
#define LTR_CRATE_REVISION_SIZE             16
#define LTR_CRATE_IMPLEMENTATION_SIZE       16
#define LTR_CRATE_BOOTVER_SIZE              16
#define LTR_CRATE_CPUTYPE_SIZE              16
#define LTR_CRATE_TYPE_NAME                 16
#define LTR_CRATE_SPECINFO_SIZE             48

#define LTR_CRATE_FPGA_NAME_SIZE            32
#define LTR_CRATE_FPGA_VERSION_SIZE         32
#define LTR_CRATE_FPGA_COMMENT_SIZE         256


#define LTR_CRATE_MODULE_NAME_SIZE          16


#define LTR_CRATE_MODULES_MAX_CNT           16
#define LTR_CRATE_MODULES_META_CNT          2  /* количество мета-модулей */
#define LTR_CRATE_MODULES_FULL_CNT         (LTR_CRATE_MODULES_MAX_CNT + LTR_CRATE_MODULES_META_CNT)

#define LTR_CRATE_MODULE_INDEX_RAW_DATA     17
#define LTR_CRATE_MODULE_INDEX_USER_DATA    18



#define LTR_CRATE_LOCATION_SIZE             128


#define LTRSRV_LOGSRC_CRATE_SIZE            128

#define LTR_CRATE_THERM_MAX_CNT             8




#define LTR_CRATE_SUPPORT_PROTOCOL_VER(crate)  (crate->descr.protocol_ver_major || crate->descr.protocol_ver_minor)
#define LTR_CRATE_SUPPORT_SEC_CTL_CON(crate)  (crate->descr.protocol_ver_major || crate->descr.protocol_ver_minor)
#define LTR_CRATE_SUPPORT_SLOTS_CONFIG(crate)  (crate->descr.slots_config_ver_major >=2)

//typedef struct st_ltr_crate t_ltr_crate;
struct st_ltr_crate;
struct st_ltr_client;
struct st_ltr_module;

/** информация о устройстве и прошивке  */
typedef struct {
    uint32_t snd_size;
    char devname[LTR_CRATE_DEVNAME_SIZE]; /**< название устройства */
    char serial[LTR_CRATE_SERIAL_SIZE];   /**< серийный номер */
    char soft_ver[LTR_CRATE_SOFTVER_SIZE]; /**< версия прошивки */
    char brd_revision[LTR_CRATE_REVISION_SIZE]; /**< ревизия платы */
    char brd_impl[LTR_CRATE_IMPLEMENTATION_SIZE]; /**< опции платы */
    char bootloader_ver[LTR_CRATE_BOOTVER_SIZE]; /**< версия загрузчика */
    char cpu_type[LTR_CRATE_CPUTYPE_SIZE]; /**< тип микроконтроллера */
    char fpga_name[LTR_CRATE_FPGA_NAME_SIZE];
    char fpga_version[LTR_CRATE_FPGA_VERSION_SIZE];
    char crate_type_name[LTR_CRATE_TYPE_NAME];
    char spec_info[LTR_CRATE_SPECINFO_SIZE]; /**< резерв */    
    uint8_t protocol_ver_major; /**< версия протокола между сервером и крейтом (мажорная) */
    uint8_t protocol_ver_minor; /**< версия протокола между сервером и крейтом (минорная) */
    uint8_t slots_config_ver_major; /**< версия протокола для сохранения настроек модуля (мажорная) */
    uint8_t slots_config_ver_minor; /**< версия протокола для сохранения настроек модуля (минорная) */
} t_ltr_crate_descr;


typedef enum {
    LTR_CRATE_CONSTATE_CLOSED         = 0,
    LTR_CRATE_CONSTATE_INITIALIZATION = 1,
    LTR_CRATE_CONSTATE_WORK           = 2,
    LTR_CRATE_CONSTATE_ERROR          = 3
} t_ltr_crate_con_state;

typedef enum {
    LTR_CRATE_FLAGS_RECV_RAW_DATA = 0x00001,
    LTR_CRATE_FLAGS_BUF_OVERFLOW  = 0x10000,
    LTR_CRATE_FLAGS_HW_ERROR      = 0x20000
} t_ltr_crate_flags;

typedef enum {
    LTR_CRATE_MODE_BOOTLOADER   = 1,
    LTR_CRATE_MODE_WORK         = 2,
    LTR_CRATE_MODE_CONTROL      = 3
} t_ltr_crate_mode;

typedef struct {
    t_ltr_crate_con_state state; /** Состояние подключения */
    t_ltr_crate_mode mode; /** Ражим работы (загрузчик/рабочий режим) */
    t_ltr_crate_interface intf; /** Интерфейс полключение */
    time_t register_time; /** время регистрации */
    char location[LTR_CRATE_LOCATION_SIZE]; /** Адрес/URL etc */
    int flags; /** флаги из t_ltr_crate_flags */
} t_ltr_crate_param;


typedef struct {
    uint64_t wrd_sent; /**< передано слов крейту */
    uint64_t wrd_recv; /**< слов принято от крейта и его модулей */
    double   bw_send; /**< скорость передачи слов */
    double   bw_recv; /**< скорость приема слов */
    uint64_t crate_wrd_recv; /**< количество управляющих слов непосредственно от крейта */
    uint64_t internal_rbuf_miss; /**< количество потерянных буферов из-за внутреннего переполнения */
    uint32_t internal_rbuf_ovfls; /**< количество переполнений внутреннго буфера крейта */
    uint32_t rbuf_ovfls; /**< количество переполнений буфера для модулей крейта */
    uint32_t total_start_marks; /**< кол-во меток старта как от крейта, так и от модулей */
    uint32_t total_sec_marks; /**< кол-во секундных меток как от крейта, так и от модулей */
    uint32_t crate_start_marks; /**< кол-во меток старта непосредственно от крейта */
    uint32_t crate_sec_marks; /**< кол-во секундных меток непосредственно от крейта */
    uint64_t crate_unixtime;  /**< значение последней метки unixtime */
    uint32_t therm_mask;      /**< маска действительных значений термометров */
    float therm_vals[LTR_CRATE_THERM_MAX_CNT]; /**< значение показаний термометров */
} t_ltr_crate_stat;


typedef enum {
    LTR_MODULE_STATE_OFF = 0,      /* модуль отсутствует в слоте (не было признака наличия модуля) */
    LTR_MODULE_STATE_NEED_RST = 1, /* для модуля нужно послать STOP-STOP-RESET-STOP и выполнить переинициализацию */
    LTR_MODULE_STATE_INIT = 2,     /* послана команда STOP-STOP-RESET-STOP, но еще не получен ответ */
    LTR_MODULE_STATE_CHECK_CFG = 3, /* для модуля необходимо выполнить проверку наличия сохраненной в крейте информации о типе модуля (kdr extenstion) */
    LTR_MODULE_STATE_WORK = 4, /* модуль в рабочем режиме */
    LTR_MODULE_STATE_NEED_GET_SUBID = 5, /* для завершения инициализации модуля необходимо дополнительно запросить SubID */
    LTR_MODULE_STATE_WAIT_SUBID = 6, /* послан запрос на получения subid, ожидмется ответ от модуля */
} t_ltr_module_state;

typedef enum {
    LTR_MODULE_FLAGS_HIGH_BAUD            = 0x001, /** признак, что модуль использует высокую скорость */
    LTR_MODULE_FLAGS_USE_SND_FIFO_STATUS  = 0x100, /** признак, что модуль использует статистику FIFO на передачу данных */
    LTR_MODULE_FLAGS_USE_SYNC_MARK        = 0x200 /** признак, что модуль поддерживает генерацию синхрометок */
} t_ltr_module_flags;

typedef enum {
    LTR_MODULE_STATE_FLAG_RCV_OV   = 0x01, /** признак, что было переполнение буфера */
    LTR_MODULE_STATE_FLAG_SND_SUSP = 0x02,  /** признак, что прием от клиентов был остановлен
                                            из-за нехватки места в буфера */
} t_ltr_module_state_flags;


/** код команды крейту */
typedef enum {
    LTR_CRATE_CMD_CODE_READ = 0, /**< чтение регистров крейта */
    LTR_CRATE_CMD_CODE_WRITE = 1, /**< запись регистров крейта */
    LTR_CRATE_CMD_CODE_IOCTL = 2 /**< специальный запрос ввода-вывода */
} t_crate_cmd_code;

/** смысл команды - причина ее вызова */
typedef enum {
    LTR_CRATE_CMD_REASON_GET_MODULES     = 0, /**< чтение состова модулей */
    LTR_CRATE_CMD_REASON_SYSFAULT        = 1, /**< чтение регистров при ошибке крейта */
    LTR_CRATE_CMD_REASON_BAUD_CH         = 2, /**< запись новой маски скорости */
    LTR_CRATE_CMD_REASON_CLIENT_REQ      = 3, /**< запрос от клиента */
    LTR_CRATE_CMD_REASON_SLOT_CFG        = 4, /**< проверка конфигурации слотов */
    LTR_CRATE_CMD_REASON_CRATE_POLL      = 5, /**< периодический опрос состояния крейта */
    LTR_CRATE_CMD_REASON_INTF_REQ        = 6  /**< запрос со стороны интерфейса */
} t_ltr_crate_cmd_reason;

typedef enum {
    LTR_CRATE_CMD_FLAGS_EXTERNAL_BUF = 1,
} t_ltr_crate_cmd_flags;

typedef enum {
    LTR_CRATE_DESCR_FLAGS_SUPPORT_BOARD_REV = 0x0001, /**< Поддержка запроса на получение ревизии */
    LTR_CRATE_DESCR_FLAGS_SUPPORT_POLL_CMD  = 0x0002, /**< Поддержка команды опроса состояния крейта */
    LTR_CRATE_DESCR_FLAGS_SUPPORT_GET_PRIMARY_IFACE = 0x0004, /**< Поддержка get-array для получения активного интерфейса для управления модулями */
    LTR_CRATE_DESCR_FLAGS_HAS_DATA_CLOSE_BUG = 0x0008  /**< Крейт требует выполнять повтор открытия соединения на данные из-за бага в прошивке */
} t_ltr_crate_descr_flags;

typedef struct {
    char serial[LTR_CRATE_SERIAL_SIZE];
    uint8_t type;
    uint8_t intf;
    uint8_t res[6];
} t_crate_info_entry;

#define CRATE_GET_MODULE_MID(crate, slot) (crate->modules[slot].state == LTR_MODULE_STATE_WORK ? \
      crate->modules[slot].mid : crate->modules[slot].state == LTR_MODULE_STATE_OFF ? \
      LTR_MID_EMPTY : LTR_MID_IDENTIFYING)



typedef enum {
    LTR_MODULE_INIT_FLAGS_RST_HBYTE_CMD_MSK = 0x000000FF, /* в данных битах сохраняется
                                                        старшее слово ответа на сброс,
                                                        если установлен MODULE_INIT_FLAG_ON_RESET */
    LTR_MODULE_INIT_FLAGS_ON_RESET          = 0x00010000, /* признак, что иницализация
                                                        идет по приему ответа на сброс */
    LTR_MODULE_INIT_FLAGS_ON_CFG_RESTORE    = 0x00020000, /* признак, что иницализация
                                                        идет по восстановлению
                                                        конфигурации из памяти крейта */
} t_ltr_module_init_flags;


enum en_LTR_GetCratesFlags {
    LTR_GETCRATES_FLAGS_WORKMODE_ONLY   = 0x1
};




/***************************************************************************//**
  Функция обработки модулем слова данных или команды.
    @param[in] module  модуль для которого нужно передать или принять данные
    @param[in] wrd     принятое слово или слово для передачи
    @return            0, если слово должно быть  передано дальше
                       1, если слово было обработано и дальше передавать не нужно
   *****************************************************************************/
typedef int (*t_ltr_module_proc_wrd_func)(struct st_ltr_crate *crate, struct st_ltr_module *module, uint32_t wrd);


/* функция для очистки памяти, для специфичной для модуля обработки */
typedef void (*t_ltr_module_spec_free)(struct st_ltr_crate *crate, struct st_ltr_module *module);

typedef void (*t_ltr_module_subid_req)(struct st_ltr_crate *crate, struct st_ltr_module *module);


/** callback-функция, вызываемая после завершения обработки команды интерфейсом.
    @param[in] hnd  Описатель крейта
    @param[in] err  Код ошибки выполнения команды
    @param[in] prv_data Указатель, переданный при начале выполнения команды
    @return         Если не 0 => критическая ошибка, требующая закрытия крейта */
typedef int (*t_ltr_crate_cmd_cb)(struct st_ltr_crate *crate, int err, void *prv_data);

typedef int (*t_ltr_crate_snd_rdy_cb)(struct st_ltr_crate *crate);

/*****************************************************************************//**
    Функция чтения регистров крейта по интерфейсу связи с ним
    @param[in] hnd    Описатель крейта
    @param[in] addr   Адрес регистра для чтения
    @param[in] size   Количество байт на чтение
    @param[in] buf    Указатель на буфер в который будут сохранены данные после
                      завершения выполнения команды
    @param[in] cb     Указатель на функцию, котороя будет вызвана по завершению
                      выполнения команды
    @param[in] data   Указатель на данные, которы будет передан как есть при вызове
                      функции cb
    @param            Код ошибки
    *****************************************************************************/
typedef int (*t_ltr_crate_regs_read)(struct st_ltr_crate *crate, uint32_t addr, int snd_size,
                                       uint8_t *buf, t_ltr_crate_cmd_cb cb, void *data);
typedef int (*t_ltr_crate_regs_write)(struct st_ltr_crate *crate, uint32_t addr, int snd_size,
                                      const uint8_t *buf, t_ltr_crate_cmd_cb cb, void *data);

typedef int (*t_ltr_crate_ioctl)(struct st_ltr_crate *crate, uint32_t req, uint32_t param,
                                 const uint8_t *snd_buf, uint32_t snd_size,
                                 uint8_t *rcv_buf, uint32_t *rcv_size,
                                 t_ltr_crate_cmd_cb cb, void *data);


/****************************************************************************//**
    Функция передачи данных крейту
    @param[in] hnd   Описатель крейста
    @param[in] buf   Буфер на передачу*
    @param[in] size  Размер данных на передачу
    @return          < 0   - код ошибки
                     >= 0  - сколько данных передано реально
  *****************************************************************************/
typedef int (*t_ltr_crate_send)(struct st_ltr_crate *crate, const uint8_t *buf,
                                int snd_size);
typedef int (*t_ltr_crate_wait_send_rdy)(struct st_ltr_crate *crate, t_ltr_crate_snd_rdy_cb cb);

/** Функция закрытия связи с крейтом */
typedef void (*t_ltr_crate_close)(struct st_ltr_crate *crate, int close_by_err);
typedef int (*t_ltr_crate_reopen)(struct st_ltr_crate *crate);


/** статистика по модулю */
typedef struct {
    uint64_t wrd_sent; /** кол-во переданных слов модулю */
    uint64_t wrd_recv; /** кол-во принятых слов от модуля */
    double bw_send; /** скорость передачи слов */
    double bw_recv; /** скорость приема слов */
    uint64_t wrd_sent_to_client; /** сколько слов передано клиенту */
    uint64_t wrd_recv_from_client; /** сколько слов принято от клиентов */
    uint64_t wrd_recv_drop; /** сколько выкинуто слов из за переполнения */
    uint32_t rbuf_ovfls; /** количество переполнений буфера на прием */
    uint32_t send_srvbuf_size; /** размер буфера для модуля на передачу */
    uint32_t recv_srvbuf_size; /** размер буфера для модуля на прием */
    uint32_t send_srvbuf_full; /** на сколько слов заполнен буфер на передачу */
    uint32_t recv_srvbuf_full; /** на сколько слов заполнен буфер на прием */
    uint32_t send_srvbuf_full_max; /** на сколько слов был максимально заполнен буфер на передачу */
    uint32_t recv_srvbuf_full_max; /** на сколько слов был максимально заполнен буфер на прием */
    uint32_t res2[17];    /** резерв */
    uint32_t start_mark; /** количество меток старт от модуля */
    uint32_t sec_mark; /** количество секундных меток от модуля */
    uint32_t hard_send_fifo_size; /** размер очереди (если есть флаг LTR_MODULE_FLAGS_USE_SND_FIFO_STATUS)*/
    uint32_t hard_send_fifo_full; /** заполненность очереди (если есть флаг LTR_MODULE_FLAGS_USE_SND_FIFO_STATUS)*/
    uint32_t hard_send_fifo_underrun; /** количество подтверждений с флагом, что было "голодание" очереди */
    uint32_t hard_send_fifo_overrun; /** количество подтверждений с флагом, что было переполнение очереди */
    uint32_t hard_send_fifo_internal; /** внутренний статус очереди (берется из слов
                                    подтверждений, если они есть) */
} t_ltr_module_stat;


/* специальные функции для нестандартной обработки модулем данных */
typedef struct {
    t_ltr_module_proc_wrd_func proc_snd; /* обработка команд и данных, передавамых модулю */
    t_ltr_module_proc_wrd_func proc_rcv_data; /* обработка принятых от модуля данных */
    t_ltr_module_proc_wrd_func proc_rcv_cmd; /* обработка  принятых команд */
    t_ltr_module_spec_free     free; /* освобождение памяти под специфичные для модуля данные */
} t_ltr_module_spec_func;






/** структура, описывающая состояние модуля */
typedef struct st_ltr_module {
    struct st_ltr_crate *crate;
    char name[LTR_CRATE_MODULE_NAME_SIZE]; /* название модуля */
    char *log_str; /* строка для вывода сообщений в журнал */
    t_ltr_module_state state; /** состояние модуля */
    int rst_retry_cnt; /*  количество повторных выполнений операции сброса модуля */
    int subid_retry_cnt; /* количество повторных запросов расширенного id модуля */
    int flags; /** флаги описывающие модуль из t_ltr_module_flags */
    int state_flags; /** флаги состояния модуля t_ltr_module_state_flags */
    t_ltr_module_spec_func func; /** функции, специфичные для модуля */
    void *spec_data; /* указатель для сохранения данных, специфичных для модуля */
    struct st_ltr_client *clients[LTRSRV_CLIENT_PER_MODULE_MAX]; /** список клиентов */
    //int clients_sent_pos[LTRSRV_CLIENT_PER_MODULE_MAX]; /** позиция в буфере модуля, откуда будем передавать клиенту */
    int client_ovfl[LTRSRV_CLIENT_PER_MODULE_MAX]; /** признак, что этот клиент не успел за остальными */
    uint16_t client_cnt; /** количество подключенных клиентов */
    uint16_t mid; /** id-модуля */
    uint16_t subid_code; /** subdi-модуля (ltr01) */
    t_ltr_module_stat stat; /** статистика модуля */

    uint64_t bw_last_wrd_sent; /** Количество переданных слов при последней проверке скорости передачи */
    uint64_t bw_last_wrd_recv; /** Количество принятых слов при последней проверке скорости передачи */


    t_cycle_buf sndbuf; /* параметры буфера с данными для передачи модулю */
    t_cycle_buf rcvbuf; /* параметры буфера для принятых данных от модуля */
} t_ltr_module;






/**  Функции, предоставляемые интерфейсом крейта, и параметры интерфейса */
typedef struct {
    t_ltr_crate_regs_read     rd_regs; /**< Чтение регистров крейта */
    t_ltr_crate_regs_write    wr_regs; /**< Запись регистров крейта */
    t_ltr_crate_ioctl         ioctl; /**< Передача указанного управляющего запроса */
    t_ltr_crate_send          send; /**< Передача данных */
    t_ltr_crate_wait_send_rdy wt_snd_rdy; /**< Ожидание, когда канал передачи в крейт
                                              будет готов передать еще данные */
    t_ltr_crate_close         close; /**< Закрытие соединения с крейтом */
    t_ltr_crate_reopen        reopen; /**< Закрытие крейта с последующим открытием */
    t_lclock_ticks poll_time; /**< Время периодического опроса крейта (0 - если не нужно) */
    t_ltimer poll_timer; /**< Таймер для выполнения опроса */
    uint32_t poll_ioctl; /**< Управляющий запрос для опроса статистки крейта */
} t_ltr_crate_intf_func;


typedef struct {
    float low_err;
    float low_warn;
    float high_warn;
    float high_err;
} t_ltr_crate_therm_limit;


/** описание состояния крейта */
typedef struct st_ltr_crate {
    t_ltr_crate_descr descr; /** информация о самом крейте */
    t_ltr_crate_param par; /** информация о состоянии крейта */
    t_ltr_crate_stat stat; /** статистика крейта */
    t_ltr_crate_intf_func intf_func;  /** функции конкретного интерфейса */
    t_ltr_crate_type type; /** тип крейта */
    uint32_t firm_ver; /** версия прошивки в виде числа */
    uint32_t descr_flags; /** доп. флаги для определения поддерживаемый данным крейтом возможностей */
    int close_req; /* запрос на закрытие крейта при ошибках */
    int close_req_err;
    int modules_cnt; /** максимальное количество модулей в крейте */
    t_ltr_module modules[LTR_CRATE_MODULES_FULL_CNT]; /** информация о модулях */
    const t_ltr_crate_therm_limit *therm_limits; /** Границы для значений температур */
    char *log_str;   /** строка, используемая в качестве источника при выводе сообщений
                        от крейта, формируется ltrsrv_log_make_crate_srcstr() */
    void *intf_data; /** данные, используемые интерфейсом */
    void *proc_data; /** данные, используемые для обработки данных крейта */
} t_ltr_crate;







int ltrsrv_crate_register(t_ltr_crate *crate);
int ltrsrv_crate_unregister(t_ltr_crate *crate);


void ltrsrv_crates_close(void);
int ltrsrv_crates_init(void);

int ltrsrv_crate_reopen(t_ltr_crate *crate);
void ltrsrv_crate_close_req(t_ltr_crate *crate, int err);
void ltrsrv_crate_check_close_req(t_ltr_crate *crate) ;


int ltrsrv_crate_rcv_done(t_ltr_crate *crate, uint32_t *buf, int snd_size);
int ltr_crate_wait_send_rdy(t_ltr_crate *crate);


int ltrsrv_crate_register_client(t_ltr_crate *crate, int slot, struct st_ltr_client *client);
int ltrsrv_crate_client_init(t_ltr_crate *crate, int slot, struct st_ltr_client *client);
int ltrsrv_crate_unregister_client(t_ltr_crate *crate, int slot, struct st_ltr_client *client);
int ltrsrv_notify_client_data_rdy(struct st_ltr_client *client, t_ltr_crate *crate, int slot);


t_ltr_crate *ltrsrv_crate_find(t_ltr_crate_interface intf, const char *serial);

int ltrsrv_get_crates(char serial_list[][LTR_CRATE_SERIAL_SIZE], int max_cnt, int *fnd_cnt);
int ltrsrv_get_crates_info(t_crate_info_entry *info_list, int max_cnt, int flags, int *fnd_cnt);
int ltrsrv_get_crate_modules(t_ltr_crate *crate, uint16_t *mids, int max_mids);
int ltrsrv_crate_start_client_cmd(t_ltr_crate *crate, t_crate_cmd_code cmd, uint32_t req, uint32_t param,
                                  uint8_t *buf, uint32_t snd_size, uint32_t *recvd_size, t_ltr_crate_cmd_cb cb, void *cb_data);
int ltrsrv_crates_exec_for_all(int (*exec_funt)(t_ltr_crate *crate, void *data), void *data);



int ltrsrv_crate_reset_module(t_ltr_crate *crate, int slot, int flags);


int ltrsrv_crate_init_spec(t_ltr_crate *crate);
int ltrsrv_crate_init_process_data(t_ltr_crate *crate, uint32_t addr, const uint8_t *data, uint32_t snd_size);
int ltrsrv_crate_init_parse_fpga_data(const char *data, t_ltr_crate_descr *descr);

int ltrsrv_crate_data_channel_reset(t_ltr_crate *crate);

int ltrsrv_crate_add_cmd_to_queue(t_ltr_crate *hnd, t_crate_cmd_code code, t_ltr_crate_cmd_reason reason,
                                 uint32_t req, uint32_t param,
                                 uint32_t snd_size, uint32_t rcv_size, uint32_t *recvd_size,
                                 uint8_t *data, int flags, t_ltr_crate_cmd_cb cb, void *cb_data);


#endif
