/*
 * megatec.c
 *
 *  Created on: 22.05.2017
 *      Author: balbekova
 */
#include "FreeRTOS.h"
#include "task.h"
#include "usart.h"
#include "megatec.h"
#include "log.h"
#include "snmp_api.h"
#include "settings_api.h"
#ifdef PRINTF_STDLIB
#include <stdio.h>
#endif
#ifdef PRINTF_CUSTOM
#include "tinystdio.h"
#endif

#include <string.h>
#include <math.h>

/**
  * @brief  Общая структура настроек
  */
extern SETTINGS_t sSettings;

#define UPS_PDU_MAX_LEN 50

float TimeParamFloat = 0;
uint16_t TimeParam = 0;
uint16_t TimeParam2 = 0;
bool megatec_send = true;
UPS_value_t UPS;

enum {
    NORMAL = 0x00,
    VER_ERROR = 0x01,
    CHKSUM_ERROR = 0x02,
    LCHKSUM_ERROR = 0x03,
    CID2_INVALID = 0x04,
    CMD_FMT_ERROR = 0x05,
    INVALID_DATA = 0x06,
};

static struct {
    uint8_t data[UPS_PDU_MAX_LEN];
    uint16_t len;
} ups_pdu;


const char *MegaTecCMD[] = {
    "Q1\r",
    "T\r",
    "TL\r",
    "T",
    "Q\r",
    "S",
    "R",
    "C\r",
    "CT\r",
    "I\r",
    "F\r",
    "Q2\r",
    "QGS\r",
    "QBV\r",
    "QMD\r",
    "QVFW\r",
    "QVFW2\r",
    "QID\r",
    "QPI\r",
    "QS\r",
    "M\r",
};

extern bool flUpdateLog;


void init_UPS_value(void)
{
    UPS.Freq_in = 0;
    UPS.VAC_in = 0;
    UPS.VAC_out = 0;
    UPS.Temp = 0;
    UPS.Load = 0;
    UPS.SOC = 0;
    UPS.work_time = 0;
    UPS.akb_work_time = 0;
    UPS.Status = 0;
    UPS.Alarm = 0;
    UPS.cnt_err_ups = 0;
    UPS.Flag_Present = false;
    UPS.Present = false;

    memset(UPS.model, 0,  sizeof(UPS.model));
    memset(UPS.vertion, 0, sizeof(UPS.vertion));
    memset(UPS.serial, 0, sizeof(UPS.serial));
}

void send_MegaTec_cmd(cmdMegaTecEnums_t command)
{
    DBG printf("MegaTecCMD: %s\r\n", MegaTecCMD[command]);
    if (command == ups_test_time) {
        uint8_t req[10];

        memset(req, 0, 10);
        if (TimeParam < 10) {
            sprintf(req, "%s0%d\r", MegaTecCMD[command], TimeParam);
        } else {
            sprintf(req, "%s%d\r", MegaTecCMD[command], TimeParam);
        }
        ups_send_block(req, strlen(req));
    } else if (command == ups_shutdown) {
        uint8_t req[10];

        memset(req, 0, 10);
        if (TimeParamFloat >= 1 && TimeParamFloat < 10) {
            sprintf(req, "%s0%d\r", MegaTecCMD[command], (uint16_t)TimeParamFloat);
        } else if (TimeParamFloat < 1) {
            sprintf(req, "%s.%d\r", MegaTecCMD[command], (uint16_t)(10 * TimeParamFloat));
        } else {
            sprintf(req, "%s%d\r", MegaTecCMD[command], (uint16_t)TimeParamFloat);
        }
        ups_send_block(req, strlen(req));
    } else if (command == ups_shutdown_restore) {
        uint8_t req[10];

        memset(req, 0, 10);
         if (TimeParamFloat >= 1 && TimeParamFloat < 10) {
            sprintf(req, "%s0%d%s000%d\r", MegaTecCMD[command - 1], (uint16_t)TimeParamFloat, MegaTecCMD[command], TimeParam2);
        } else if (TimeParamFloat < 1) {
            sprintf(req, "%s.%d%s000%d\r", MegaTecCMD[command - 1], (uint16_t)(10 * TimeParamFloat), MegaTecCMD[command], TimeParam2);
        } else {
            sprintf(req, "%s%d%s000%d\r", MegaTecCMD[command - 1], (uint16_t)TimeParamFloat, MegaTecCMD[command], TimeParam2);
        }
        ups_send_block(req, strlen(req));
    } else {
        // TODO ����������� ���������
        //ups_send_block(MegaTecCMD[command], strlen(MegaTecCMD[command]));
        ups_send_block((void *)MegaTecCMD[command], strlen(MegaTecCMD[command]));
    }
}

bool ups_megatec_rx_pdu(void)
{
    int c = 0;
    uint8_t cnt_answer = 0;

    ups_pdu.len = 0;

    while (c >= 0) {
        c = ups_getchar(200);//portMAX_DELAY200
        if (c >= 0) {
            ups_pdu.len = 0;
            memset(ups_pdu.data, 0, UPS_PDU_MAX_LEN);
            ups_pdu.data[ups_pdu.len++] = c;
            while ((ups_pdu.len < UPS_PDU_MAX_LEN) && (c != 0x0d)) {
                c = ups_getchar(100);//portMAX_DELAY200
                if (c < 0) {
                    ups_pdu.len = 0;
                    return false;
                }
                ups_pdu.data[ups_pdu.len++] = c;
            }
            DBG printf("UPS raw data: %s\r\n", ups_pdu.data);
            cnt_answer ++;
        } else {
            if (ups_pdu.len == 0) {
                return false;
            }
        }
    }

    if (cnt_answer > 1) {
        DBG printf("MegaTecCMD: false\r\n");
        return false;
    }

    DBG printf("UPS raw data: %s\r\n", ups_pdu.data);
    DBG printf("UPS raw data len: %d\r\n", ups_pdu.len);

    return true;
}

int8_t get_ups_param(char *buf, char *param, char *val)
{
    char *endValue;
    int8_t param_len;

    memset(val, 0, 20);
    endValue = strpbrk(buf, param);
    if (endValue != NULL) {
        param_len = endValue - buf;
        if (param_len < 20) {
            strncpy(val, buf, param_len);
        } else {
            param_len = 0;
        }
    } else {
        param_len = 0;
    }
    DBG printf("UPS parameter: %s\r\n", val);

    return param_len;
}

void ups_status_response(char *data)
{
    uint8_t i;
    char value[20];
    float tmp;
    uint8_t len = 0;
    DBG printf("ups_status_response: %s\r\n", data);
    if (data[0] != '(') {
        return;
    }
    DBG printf("ups_status_parser_start\r\n");
    UPS.Present = true;
    UPS.Flag_Present = true;
    UPS.cnt_err_ups = 0;

    if (flUpdateLog) {
        flUpdateLog = false;
        log_add(data);
    }

    data++;

    DBG printf("UPS ups_status_parser_startr: %s\r\n", data);

    len = get_ups_param(data, " ", value);
    data += (len + 1);
    if (len > 0) {
        tmp  = atof(value);
        if (tmp > 20) {
            UPS.VAC_in = tmp;
        } else {
            UPS.VAC_in = 0;
        }
    }

    //TODO
    len = get_ups_param(data, " ", value);
    data += (len + 1);

    len = get_ups_param(data, " ", value);
    data += (len + 1);
    if (len > 0) {
        UPS.VAC_out = atof(value);
    }

    len = get_ups_param(data, " ", value);
    data += (len + 1);
    if (len > 0) {
        UPS.Load = atoi(value);
    }

    len = get_ups_param(data, " ", value);
    data += (len + 1);
    if (len > 0) {
        UPS.Freq_in = atof(value);
    }

    //TODO
    len = get_ups_param(data, " ", value);
    data += (len + 1);
    if (len > 0) {
        UPS.Vakb_curr = atof(value);
    }
    if (sSettings.UPS_Setting.type_ups == ups_kestar || sSettings.UPS_Setting.type_ups == ups_offline) {
        if (UPS.Vakb_curr < 7) {
            UPS.SOC = round(100 * (UPS.Vakb_curr - sSettings.UPS_Setting.Ucellmin) / (sSettings.UPS_Setting.Ucellmax -
                        sSettings.UPS_Setting.Ucellmin));
        } else {
            UPS.SOC = round(100 * (UPS.Vakb_curr / AKB_NUM_CELL - sSettings.UPS_Setting.Ucellmin) /
                    (sSettings.UPS_Setting.Ucellmax - sSettings.UPS_Setting.Ucellmin));
        }
    }
    len = get_ups_param(data, " ", value);
    data += (len + 1);
    if (len > 0) {
        UPS.Temp = atof(value);
    }

    len = get_ups_param(data, "\r", value);
    data += (len + 1);
    if (len > 0) {
        uint8_t stat = 0;
        for (i = 0; i < len; i ++) {
            stat |= (value[i] - 0x30) << (7 - i);
        }
        UPS.Status = stat;
    }
}

void ups_general_status_response(char *data)
{
    uint8_t i;
    char value[20];
    float tmp;
    uint8_t len = 0;
    DBG printf("ups_status_response: %s\r\n", data);
    if (data[0] != '(') {
        return;
    }
    DBG printf("ups_status_parser_start\r\n");
    UPS.Present = true;
    UPS.Flag_Present = true;
    UPS.cnt_err_ups = 0;

    if (flUpdateLog) {
        flUpdateLog = false;
        log_add(data);
    }

    data++;

    DBG printf("UPS ups_status_parser_startr: %s\r\n", data);

    len = get_ups_param(data, " ", value);
    data += (len + 1);
    if (len > 0) {
        tmp  = atof(value);
        if (tmp > 20) {
            UPS.VAC_in = tmp;
        } else {
            UPS.VAC_in = 0;
        }
    }

    len = get_ups_param(data, " ", value);
    data += (len + 1);
    if (len > 0) {
        UPS.Freq_in = atof(value);
    }

    len = get_ups_param(data, " ", value);
    data += (len + 1);
    if (len > 0) {
        UPS.VAC_out = atof(value);
    }

    //TODO
    len = get_ups_param(data, " ", value);
    data += (len + 1);

    //TODO
    len = get_ups_param(data, " ", value);
    data += (len + 1);

    len = get_ups_param(data, " ", value);
    data += (len + 1);
    if (len > 0) {
        UPS.Load = atoi(value);
    }

    //TODO
    len = get_ups_param(data, " ", value);
    data += (len + 1);

    //TODO
    len = get_ups_param(data, " ", value);
    data += (len + 1);

    //TODO
    len = get_ups_param(data, " ", value);
    data += (len + 1);

    //TODO
    len = get_ups_param(data, " ", value);
    data += (len + 1);

    len = get_ups_param(data, " ", value);
    data += (len + 1);
    if (len > 0) {
        UPS.Temp = atof(value);
    }


    len = get_ups_param(data, "\r", value);
    data += (len + 1);
    if (len > 0) {
        uint8_t stat = 0;
        for (i = 2; i < (len - 2); i ++) {
            stat |= (value[i] - 0x30) << (7 - i);
        }
        UPS.Status = stat;
    }
}

void ups_info_response(char *data)
{
    uint8_t i = 0;
    DBG printf("ups_info_response: %s\r\n", data);
    if (data[0] != '#') {
        return;
    }

    DBG printf("ups_info_response_startr: %s\r\n", data);

    UPS.Present = true;
    UPS.Flag_Present = true;
    UPS.cnt_err_ups = 0;

    data++;

    data += 16;

    while (data[0] == ' ') {
        data ++;
        i ++;
    }
    if (i < 15) {
        /*endValue = strpbrk(data," ");
        len = endValue - data;*/
        if (UPS.model[0] == 0) {
            strncpy(UPS.model, data, 10);
            SNMP_SetObjDescr();
        } else {
            strncpy(UPS.model, data, 10);
        }

        data += 11;
    } else {
        if (UPS.model[0] == 0) {
            strcpy(UPS.model, "RTMP II");
            SNMP_SetObjDescr();
        } else {
            strcpy(UPS.model, "RTMP II");
        }
        data += 11;
    }

    strncpy(UPS.serial, data, 8);
    data += 8;

    strncpy(UPS.vertion, data, 2);
    SNMP_SetObjDescr();
}

void ups_remain_time_response(char *data)
{
    char value[20];
    if (data[0] != '(') {
        return;
    }

    DBG printf("ups_remain_time_response: %s\r\n", data);

    UPS.Present = true;
    UPS.Flag_Present = true;
    UPS.cnt_err_ups = 0;

    data++;

    if (strlen(data) > 5) {
        return;
    }

    memset(value, 0, 10);
    strcpy(value, data);
    //if((UPS.Status >> 7) & 0x01)
    UPS.work_time = atoi(value);
    //else
    //  UPS.work_time = 0;
}

void ups_akb_info_response(char *data)
{
    char value[20];
    uint8_t len = 0;
    DBG printf("ups_akb_info_response: %s\r\n", data);
    if (data[0] != '(') {
        return;
    }
    DBG printf("ups_akb_info_parser_start\r\n");
    UPS.Present = true;
    UPS.Flag_Present = true;
    UPS.cnt_err_ups = 0;

    data++;

    DBG printf("UPS ups_akb_info_parser_start: %s\r\n", data);

    //TODO
    len = get_ups_param(data, " ", value);
    data += (len + 1);

    //TODO
    len = get_ups_param(data, " ", value);
    data += (len + 1);

    //TODO
    len = get_ups_param(data, " ", value);
    data += (len + 1);

    len = get_ups_param(data, " ", value);
    data += (len + 1);
    if (len > 0) {
        UPS.SOC = atoi(value);
    }

    len = get_ups_param(data, "\r", value);
    data += (len + 1);
    if (len > 0) {
        UPS.work_time = atoi(value);
    }
}

void ups_model_response(char *data)
{
    char value[20];
    uint8_t len = 0;
    uint8_t j = 0;
    DBG printf("ups_akb_info_response: %s\r\n", data);
    if (data[0] != '(') {
        return;
    }
    DBG printf("ups_akb_info_parser_start\r\n");
    UPS.Present = true;
    UPS.Flag_Present = true;
    UPS.cnt_err_ups = 0;

    data++;

    DBG printf("UPS ups_akb_info_parser_start: %s\r\n", data);

    len = get_ups_param(data, " ", value);
    if (UPS.model[0] == 0) {
        for (uint8_t i = 0; i < len; i ++) {
            if (value[i] != '#') {
                UPS.model[j] = value[i];
                j ++;
            }
        }
        SNMP_SetObjDescr();
    } else {
        j = 0;
        for (uint8_t i = 0; i < len; i ++) {
            if (value[i] != '#') {
                UPS.model[j] = value[i];
                j ++;
            }
        }
    }
}

void ups_version_response(char *data)
{
    char value[20];
    uint8_t len = 0;
    DBG printf("ups_akb_info_response: %s\r\n", data);
    if (data[0] != '(') {
        return;
    }
    DBG printf("ups_akb_info_parser_start\r\n");
    UPS.Present = true;
    UPS.Flag_Present = true;
    UPS.cnt_err_ups = 0;

    data++;

    if (UPS.vertion[0] != 0) {
        return;
    }

    len = get_ups_param(data, ":", value);
    data += (len + 1);

    DBG printf("UPS ups_akb_info_parser_start: %s\r\n", data);

    len = get_ups_param(data, "\r", value);
    strncpy(UPS.vertion, value, 8);
}

void ups_version_part2_response(char *data)
{
    char value[20];
    uint8_t len = 0;
    DBG printf("ups_akb_info_response: %s\r\n", data);
    if (data[0] != '(') {
        return;
    }
    DBG printf("ups_akb_info_parser_start\r\n");
    UPS.Present = true;
    UPS.Flag_Present = true;
    UPS.cnt_err_ups = 0;

    if (strncmp(ups_pdu.data, "(NAK", 4) == 0) {
        return;
    }

    data++;

    if (UPS.vertion[8] != 0) {
        return;
    }

    DBG printf("UPS ups_akb_info_parser_start: %s\r\n", data);

    len = get_ups_param(data, ":", value);
    data += (len + 1);

    len = get_ups_param(data, "\r", value);
    strncat(UPS.vertion, "/", 1);
    strncpy(&UPS.vertion[9], value, 8);
}

void ups_serial_response(char *data)
{
    char value[20];
    uint8_t len = 0;
    DBG printf("ups_serialresponse: %s\r\n", data);
    if (data[0] != '(') {
        return;
    }
    DBG printf("ups_serial_parser_start\r\n");
    UPS.Present = true;
    UPS.Flag_Present = true;
    UPS.cnt_err_ups = 0;

    data++;

    DBG printf("UPS ups_serial_parser_start: %s\r\n", data);

    len = get_ups_param(data, "\r", value);
    strncpy(UPS.serial, &value[0], 14);
    UPS.serial[14] = 0;
}

void ups_protocol_id_response(char *data)
{

    DBG printf("ups_protocol_id_response: %s\r\n", data);

    UPS.Present = true;
    UPS.Flag_Present = true;
    UPS.cnt_err_ups = 0;

    if (data[0] != '(') {
        if (strncmp(data, "NAK", 3) == 0 || strncmp(data, " \r", 2) == 0) {
            sSettings.UPS_Setting.type_ups = ups_kestar;
        }
        else if(strncmp(data, "QPI", 3) == 0) {
            sSettings.UPS_Setting.type_ups = ups_offline;
        }
        return;
    }

    data++;
    sSettings.UPS_Setting.type_ups = ups_voltronic;

    DBG printf("UPS ups_protocol_id_parser_start: %s\r\n", data);

}

void ups_megatec_process_pdu(cmdMegaTecEnums_t command)
{
    switch (command) {
        case ups_status_req:
        case ups_offline_status_req:
            ups_status_response(ups_pdu.data);
            break;
        case ups_info:
            ups_info_response(ups_pdu.data);
            break;
        case ups_rating_info:
            break;
        case ups_remain_time_reg:
            ups_remain_time_response(ups_pdu.data);
            break;
        case ups_general_status_req:
            ups_general_status_response(ups_pdu.data);
            break;
        case ups_akb_info:
            ups_akb_info_response(ups_pdu.data);
            break;
        case ups_model_req:
            ups_model_response(ups_pdu.data);
            break;
        case ups_version_req:
            ups_version_response(ups_pdu.data);
            break;
        case ups_version2_req:
            ups_version_part2_response(ups_pdu.data);
            break;
        case ups_serial_req:
            ups_serial_response(ups_pdu.data);
            break;
        case ups_protocol_id_req:
            ups_protocol_id_response(ups_pdu.data);
            break;
        default:
            break;
    }
}

int ups_metac_service_pdu(cmdMegaTecEnums_t command)
{
    if (UPS.Present) {
        while (!megatec_send) {
            vTaskDelay(50);
        }
        memset(ups_pdu.data, 0, UPS_PDU_MAX_LEN);
        megatec_send = false;
        if (sSettings.UPS_Setting.type_ups == ups_offline && command == ups_shutdown) {
            TimeParam2 = 1;
            send_MegaTec_cmd(ups_shutdown_restore);
        } else {
            send_MegaTec_cmd(command);
        }
        if (ups_megatec_rx_pdu()) {
            megatec_send = true;
            if (strncmp(ups_pdu.data, "ACK", 3) == 0 || strncmp(ups_pdu.data, "(ACK", 4) == 0) {
                return 1;
            } else if (strncmp(ups_pdu.data, "NAK", 3) == 0 || strncmp(ups_pdu.data, "(NAK", 4) == 0) {
                return 0;
            }
        }
        megatec_send = true;
    }
    return -1;
}

// TODO ����������� ���������
//void request_task(void)
void request_task(void *params)
{
    uint8_t kestar_req[3] = { ups_status_req, ups_remain_time_reg, ups_info};
    uint8_t voltronic_req[6] = {ups_status_req, ups_akb_info, ups_model_req, ups_version_req, ups_version2_req, ups_serial_req};//
    uint8_t num_req = 0;
    uint8_t *req;

    //ups_megatec_rx_pdu();
    for (;;) {
        if (UPS.Present == true) {
            if (UPS.Flag_Present == false) {
                if (UPS.cnt_err_ups != 2) {
                    UPS.cnt_err_ups++;
                } else {
                    UPS.Freq_in = 0;
                    UPS.VAC_in = 0;
                    UPS.VAC_out = 0;
                    UPS.Temp = 0;
                    UPS.Load = 0;
                    UPS.SOC = 0;
                    UPS.work_time = 0;
                    UPS.Status = 0;
                    UPS.Alarm = 0;
                    UPS.Present = false;

                    //memset(UPS.model, 0, 11);
                    memset(UPS.vertion, 0, sizeof(UPS.vertion));
                    init_ups_rbuf();
                }
            }
        }

        if (megatec_send) {
            memset(ups_pdu.data, 0, UPS_PDU_MAX_LEN);
            megatec_send = false;
            UPS.Flag_Present = false;
            send_MegaTec_cmd(ups_protocol_id_req);
            if (ups_megatec_rx_pdu()) {
                ups_megatec_process_pdu(ups_protocol_id_req);
            }
            megatec_send = true;
        }

        switch (sSettings.UPS_Setting.type_ups) {
            case ups_kestar:
            case ups_offline:
                num_req = sizeof(kestar_req);
                req = kestar_req;
                break;
            case ups_voltronic:
                num_req = sizeof(voltronic_req);
                req = voltronic_req;
                break;
            default:
                num_req = sizeof(kestar_req);
                req = kestar_req;
                break;
        }
        vTaskDelay(1000);
        for (uint8_t i = 0; i < num_req; i++) {
            if (megatec_send) {
                memset(ups_pdu.data, 0, UPS_PDU_MAX_LEN);
                megatec_send = false;
                UPS.Flag_Present = false;
                send_MegaTec_cmd(req[i]);
                if (ups_megatec_rx_pdu()) {
                    ups_megatec_process_pdu(req[i]);
                }

                megatec_send = true;
            }
        }
    }
}

void akb_time_work_task(void *params)
{
    static uint32_t tick_prev = 0;
    for (;;) {
        if (UPS.Present && ((UPS.Status >> 7) & 0x01)) {
            if (tick_prev == 0) {
                tick_prev = xTaskGetTickCount();
            }
            UPS.akb_work_time += (xTaskGetTickCount() - tick_prev) / 1000;
            tick_prev = xTaskGetTickCount();

        } else {
            tick_prev = 0;
            UPS.akb_work_time = 0;
        }
        vTaskDelay(1000);
    }
}

void ups_megatec_init(void)
{
    init_UPS_value();
    UPS.Present = true;
    xTaskCreate(request_task, ( char * ) "request_task", configMINIMAL_STACK_SIZE * 2, NULL, tskIDLE_PRIORITY, NULL);
    xTaskCreate(akb_time_work_task, ( char * ) "akb_time_work_task", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY,
        NULL);
}
