/*
 * @Author: zhanghongyang zhanghy@bmser.com
 * @Date: 2023-02-08 13:12:29
 * @LastEditors: zhanghongyang zhanghy@bmser.com
 * @LastEditTime: 2023-10-26 09:50:24
 * @FilePath: /lede-sdk-imx6_gcc-5.4.0_musl-1.1.16_eabi.Linux-x86_64/package/bmser_exec/src/src/bmser_exec.c
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "bmser_log.h"
#include "bmser_error_code.h"
#include "bmser_ems_db_control_db.h"

#define ENDIAN_TYPE_CONVERT_16(num)  ((((uint16_t)(num) & 0xff00U) >> 8) | \
                                      (((uint16_t)(num) & 0x00ffU) << 8))
#define ENDIAN_TYPE_CONVERT_32(num)  ((((uint32_t)(num) & 0xff000000U) >> 24) | \
                                      (((uint32_t)(num) & 0x00ff0000U) >> 8) | \
                                      (((uint32_t)(num) & 0x0000ff00U) << 8) | \
                                      (((uint32_t)(num) & 0x000000ffU) << 24))

#define BIG_TO_LITTLE_ENDIAN_16  ENDIAN_TYPE_CONVERT_16
#define BIG_TO_LITTLE_ENDIAN_32  ENDIAN_TYPE_CONVERT_32

#define LITTLE_TO_BIG_ENDIAN_16  BIG_TO_LITTLE_ENDIAN_16
#define LITTLE_TO_BIG_ENDIAN_32  BIG_TO_LITTLE_ENDIAN_32

#define SLAVE_MODE      1
#define PACKAGE_NAME    "bmser_db_dump"
#define DB_DUPM_VERSION "1.0.0"
#define MAX_REG_ADDR    0xFFFF

typedef enum {
    DB_READ,
    DB_UPDATE,
    DB_WRITE,
    INVAILD
} RW_TYPE_E;

const char *notice = 
"Usage:\n\
arg[1]: 0：BMS_DATA，1：EMS_DATA\n\
arg[2]: 0：读，1：写\n\
arg[3]: bankId，堆编号，下标从0开始\n\
arg[4]: DevId，设备id，0表示BAU，1以上表示BCU，约定空调、电表、IO等动环数据信息Devid为62\n\
arg[5]: startAddr，寄存器起始地址，范围为0~0xFFFF\n\
arg[6]: count，寄存器数量\n\
arg[7]: data，写寄存器数据传入0x1234,0x5678...，读寄存器时可不填\n\
Example:\n\
1、/app/bmser_db_dump 0 0 0 0 6 6\n\
读BMS_DATA,第一堆的BAU，起始地址为6，读6个寄存器数量的数据\n\
2、/app/bmser_db_dump 0 1 2 4 1024 2 0x0012,0x0034\n\
写BMS_DATA第三堆的第四个BCU，起始地址为1024，写2个寄存器数，数据为0x0012、0x0034\n";

static void print_usage()
{
	fprintf(stdout, PACKAGE_NAME " version " DB_DUPM_VERSION "\n");
    fprintf(stdout, "%s", notice);
    return;
}

static int32_t parse_reg_data(EMS_DEV_REG_DATA_S *reg, char *data, uint8_t type)
{
    if (!reg || !data) {
        return OPRT_PARA_NULL;
    }

    char tmp_data[16] = {0};
    uint16_t *p = (uint16_t *)reg->data;
    uint16_t dbvalue = 0;
    int32_t i = 0;

    char *token = strtok(data, ",");
    while (token != NULL) {
        memset(tmp_data, 0, sizeof(tmp_data));
        snprintf(tmp_data, sizeof(tmp_data), "%s", token);
        if (strstr(tmp_data, "0x") || strstr(tmp_data, "0X")) {
            dbvalue = strtol(tmp_data + 2, NULL, 16);
        } else {
            dbvalue = strtol(tmp_data, NULL, 16);
        }

        if (i >= reg->regCount) {
            printf("write cnt is not equal real data cnt, write cnt = %u, real data cnt = %u.\n.", reg->regCount, i + 1);
            return -1;
        }
        
        if (DB_UPDATE == type) {
            *(p + i) = LITTLE_TO_BIG_ENDIAN_16(dbvalue);
        } else {
            *(p + i) = dbvalue;
        }

        token = strtok(NULL, ",");
        i++;
    }
        
    if (0 == i) {
        if (reg->regCount != 1) {
            printf("data format is wrong.\n");
            return -1;
        } else {
            if (strstr(data, "0x") || strstr(data, "0X")) {
                dbvalue = strtol(data + 2, NULL, 16);
            } else {
                dbvalue = strtol(data, NULL, 16);  
            }  
            if (DB_UPDATE == type) {
                *(p + i) = LITTLE_TO_BIG_ENDIAN_16(dbvalue);
            } else {
                *(p + i) = dbvalue;
            }
        }
    }
    return OPRT_OK;
}

int main(int argc, char *argv[])
{
    OPRT_RET ret = 0;
    int i = 0, type = 0, dataType = 0;
    uint16_t bankId = 0, devId = 0, addr = 0, count = 0;
    char data[1024] = {0};

	argv++;
	argc--;

    if (argc < 6) {
        print_usage();
        return -1;
    }

    printf("bmser_db_dump argc = %d.\n", argc);
    for (i = 0; i < argc; i++) {
        printf("bmser_db_dump, argv[%d] = %s.\n", i + 1, argv[i]);
    }

    BMSER_EMS_DB_CONTROL_INIT_S initPara = {0};
    initPara.mode = SLAVE_MODE;
    ret = BmserProtocolExt_DBInit(&initPara);
    if (0 != ret) {
        printf("bmser_db_dump db init fail, ret = %d.\n", ret);
        return ret;
    }

    dataType = atoi(argv[0]);
    type = atoi(argv[1]);
    bankId = atoi(argv[2]);
    devId = atoi(argv[3]);
    addr = atoi(argv[4]);
    count = atoi(argv[5]);

    if ((type == DB_WRITE) || (type == DB_UPDATE)) {
        if (argc == 5) {
            print_usage();
            return -1;
        } else {
            snprintf(data, sizeof(data), "%s", argv[6]);
        }
    }

    if (count == 0) {
        printf("bmser_db_dump, reg count is zero.\n");
        return -1;   
    }

    if (addr + count >= MAX_REG_ADDR) {
        printf("bmser_db_dump, addr ranger out.\n");
        return -1; 
    }

    EMS_DEV_REG_DATA_S reg = {0};
    reg.dataType = (0 == dataType ? BMS_DATA : EMS_DATA); 
    reg.bankId = bankId;
    reg.rackId = devId;
    reg.startAddr = addr;
    reg.regCount = count;
    reg.len = reg.regCount * 2;

    reg.data = malloc(reg.len);
    if (NULL == reg.data) {
        printf("bmser_db_dump, No free mem.\n");
        return -1;
    }
    memset(reg.data, 0, reg.len);

    if (DB_READ == type) {
        ret = BmserProtocolExt_ReadRegisterDb(&reg, reg.data, reg.len);
        if (0 != ret) {
            printf("bmser_db_dump read reg db fail addr = 0x%x, ret = %d.\n", reg.startAddr, ret);
            free(reg.data);
            return -1;
        }

        printf("bmser_db_dump read reg data:\n");
        uint16_t *p = (uint16_t *)reg.data;
        for (i = 0; i < reg.len / 2; i++) {
            *(p + i) = ENDIAN_TYPE_CONVERT_16(*(p + i));
            if (i % 8 == 0) {
                printf("addr:0x%04x, data:", (reg.startAddr + i));
            }
            printf("0x%04x ", *(p + i));
            if ((i + 1) % 8 == 0) {
                printf("\n");
            }
        }
        printf("\n");
    } else if (DB_UPDATE == type) {
        ret = parse_reg_data(&reg, data, type);
        if (0 != ret) {
            free(reg.data);
            return ret;
        }
        ret = BmserProtocolExt_UpdateRegisterDb(&reg);
        printf("updata db %s, ret = %d.\n", 0 == ret ? "success" : "fail", ret);
    } else if (DB_WRITE == type) {
        ret = parse_reg_data(&reg, data, type);
        if (0 != ret) {
            free(reg.data);
            return ret;
        }
        sleep(2);
        ret = BmserProtocolExt_WriteRegisterDb(&reg);
        printf("write db %s, ret = %d.\n", 0 == ret ? "success" : "fail", ret);
    }

    free(reg.data);
    
    return 0;
}

// int main()
// {
//     char *argv[7] = {"0","1","0","1","2816","2","0x12,0x34"};

//     main_test(7,argv);
// }
