#include "at32f403a_407.h" 
#include "settings_api.h"
#include "sys_api.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "common_config.h"
#include "common.h"
#include "at32_uid.h"
#include "hash.h"
#include <math.h> 
#include <string.h>
#include <stdlib.h>
#include <stdio.h>


SemaphoreHandle_t flash_mutex;


// Флаг подтверждения новых сетевых параметров пользователем
bool fConfirmWebParams = false;
   
// Системные настройки
sys_settings_t sys_settings;

// Общая структура настроек
settings_t settings;


//
void init_settings(void)
{
    flash_mutex = xSemaphoreCreateMutex();
}

// Загрузка структуры настроек из flesh
void settings_load(settings_t *settings)
{
    uint32_t loadCRC;  // CRC из flash
    uint32_t newCRC;   // CRC загруженной структуры настроек
    bool need_default = false;
    
    settings_read_from_flash((uint8_t*)settings, sizeof(settings_t));
    
    // Считываем CRC из флеш памяти
    loadCRC = (*(uint32_t*)CRC_ADDRESS);
    
    // Рассчитываем CRC для структуры настроек
    newCRC = settings_get_crc(settings);

    // Если CRC не совпадают нужно прошивать дефолтные настройки
    if (loadCRC != newCRC) {
        need_default = true;
    }
    // CRC совпала, проверяем контрольное слово если слово не совпадает
    // то это значит, что поплыла структура нстроек, прошиваем дефолт
    else if (settings->control_word != SETTINGS_CONTROL_WORD)  
    {
        need_default = true;
    }
    // CRC и контрольное слово совпали, проверяем номер версии настроек.
    // Если версия в настройках и прошивке не совпадают
    // (при обновлении изменили структуру настроек), прошиваем дефолт 
    else if (settings->settings_version != SETTINGS_VERSION)
    {
        need_default = true;
    }
    
    // Прошиваем дефолтные настройки если нужно
    if (need_default) 
    {
        settings_set_all_default();
        settings_save(settings);
    }
#if 1    
    //SETTINGS_Print();
#endif    
}

//
void settings_read_from_flash(uint8_t *data, uint32_t size)
{
    uint32_t baseAddress = SETTINGS_SECTOR;
        
    for (uint32_t i = 0; i < size; i++)
        *data++ = (*(uint32_t*)baseAddress++);;
}

//
uint32_t settings_get_crc(settings_t *settings)
{
    crc_data_reset();
    return crc_block_calculate((uint32_t*)settings, sizeof(settings_t)/4 - 1);
}

//
uint32_t settings_get_crit_sec_crc(settings_t *settings)
{
    crc_data_reset();
    uint32_t critsec_len = (uint32_t)((uint8_t *)(&settings->critical_section_crc) - (uint8_t *)settings) / 4;
    return crc_block_calculate((uint32_t *)settings, critsec_len);
}

// Сброс всех настроек в значения по умолчанию
void settings_set_all_default(void)
{
    settings_set_modbus_def(&settings.com_settings.mb_port);
    
    settings.settings_version = SETTINGS_VERSION;
    settings.critical_section_crc = settings_get_crit_sec_crc(&settings);
    settings.control_word = SETTINGS_CONTROL_WORD;
      
#if 0  
    SETTINGS_SetWebParamsDef();
    SETTINGS_SetTempWebParamsDef();
    SETTINGS_SetInfoDef();
    SETTINGS_SetGSMDef();
    SETTINGS_SetFlagsDef();
    
    SETTINGS_SetEthternetSwitchDef();
    SETTINGS_SetSntpDef();
    SETTINGS_SetServerParamsDef();
    SETTINGS_SetProxyParamsDef();
    SETTINGS_SetPortGwDef();
    SETTINGS_SetPSDDef();
    SETTINGS_SetServiceDef();
    
    sSettings.settVer = SETTINGS_VERSION;
    
    sSettings.CritSecCRC = settings_get_crit_sec_crc();
    
    sSettings.controlWorld = SETTINGS_CONTROL_WORD;
#endif    
}

// -------------------------------------------------------------------------- //
// Настройки по умолчанию

//
void settings_set_modbus_def(uint16_t *mb_port)
{
    modbus_t mb_settings;
    
    mb_settings.baud = BRD_115200;
	mb_settings.parity = NO_PAR;
	mb_settings.databits = DATABITS_8;
	mb_settings.stopbits = STOP_1;
    
    settings_conv_modbus_def(&mb_settings, mb_port);
}

//
void settings_conv_modbus_def(modbus_t *mb_settings, uint16_t *mb_port)
{
	uint16_t param = 0;

	// Количестро стоп бит (0-1 биты)
	if (mb_settings->stopbits == STOP_1)
		param = 0x00;
	else if (mb_settings->stopbits == STOP_2)
		param = 0x02; 		

	// Длина слова (2ой бит)
	param |= 0x00 << 2;

	// Контроль четности (3-4 биты)
	if (mb_settings->parity == NO_PAR)
		param |= 0x00 << 3;
	else if (mb_settings->parity == EVEN_PAR)
		param |= 0x02 << 3;
	else if (mb_settings->parity == ODD_PAR)
		param |= 0x03 << 3;

	// Скорость (5 - 7 биты)
	switch (mb_settings->baud)
	{
		case BRD_2400 :
			param |= 0x00 << 5;
		break;

		case BRD_4800 :
			param |= 0x01 << 5;
		break;

		case BRD_9600 :
			param |= 0x02 << 5;
		break;

		case BRD_19200 :
			param |= 0x03 << 5;
		break;

		case BRD_38400 :
			param |= 0x04 << 5;
		break;

		case BRD_57600 :
			param |= 0x05 << 5;
		break;

		case BRD_115200 :
			param |= 0x06 << 5;
		break;

		default : break;
	}

	*mb_port = param;
}

// -------------------------------------------------------------------------- //

// Запись структуры настроек во flash
void settings_save(settings_t *settings)
{
    xSemaphoreTake(flash_mutex, portMAX_DELAY);
    
    settings->critical_section_crc = settings_get_crit_sec_crc(settings);
    
    settings_write_to_flash((uint8_t*)settings, sizeof(settings_t));
    xSemaphoreGive(flash_mutex);
}

//
void settings_write_to_flash(uint8_t *data, uint32_t size)
{
    uint32_t baseAddress = SETTINGS_SECTOR;
    uint32_t checkCrc = 0;
    uint32_t crc = settings_get_crc((settings_t*)data);
    flash_status_type status;
    uint8_t *ptr = data;
    
    for (uint8_t i = 0; i < 3; i++)
    {	
        flash_unlock();
    
        settings_erase_flash_sector();

        for (uint32_t i = 0; i < size; i++)
            if ((status = flash_byte_program(baseAddress++, *data++)) != FLASH_OPERATE_DONE) {
                DBG printf("FLASH_ProgramByte error: status = %d\r\n", status);
                break;
            }
    
        if ((status = flash_word_program((uint32_t)CRC_ADDRESS, crc)) != FLASH_OPERATE_DONE) {
            DBG printf("FLASH_ProgramWord error: status = %d\r\n", status);
        }
    
        flash_lock();
    
        /* Считываем что записали */
        settings_read_from_flash(ptr, sizeof(settings_t));
    
        checkCrc = settings_get_crc((settings_t*)ptr);
    
        /* Проверяем  CRC того что было записано */
        if (checkCrc == crc)
            break;
    }
}

// Очистка сектора настроек
void settings_erase_flash_sector(void)
{
    flash_status_type status;

    if ((status = flash_sector_erase(SETTINGS_SECTOR)) != FLASH_OPERATE_DONE) {
        DBG printf("SETTINGS_EraseFlashSector error: status = %d/r/n", status);
    }
}


#if 0

// Установить параметры сетевого подключения по умолчанию
void SETTINGS_SetWebParamsDef(void)
{
    strcpy(sSettings.sWebParams.ip, "192.168.10.254");
    strcpy(sSettings.sWebParams.gate, "192.168.10.1");
    strcpy(sSettings.sWebParams.mask, "255.255.255.0");
    strcpy(sSettings.sWebParams.dns1, "8.8.8.8");
    strcpy(sSettings.sWebParams.dns2, "8.8.4.4");
    sSettings.sWebParams.dhcpEnable = 1;
}


// Установить временные параметры сетевого подключения по умолчанию
void SETTINGS_SetTempWebParamsDef(void)
{
    strcpy(sSettings.sWebTempParams.ip, "192.168.10.254");
    strcpy(sSettings.sWebTempParams.gate, "192.168.10.1");
    strcpy(sSettings.sWebTempParams.mask, "255.255.255.0");
    strcpy(sSettings.sWebTempParams.dns1, "8.8.8.8");
    strcpy(sSettings.sWebTempParams.dns2, "8.8.4.4");
    sSettings.sWebTempParams.dhcpEnable = 1;
}


// Установить Информацию об устройстве по умолчанию
void SETTINGS_SetInfoDef(void)
{
#if 0  
    sys_settings_t *sSys = NULL;
        
    sSys = pvPortMalloc(sizeof(*sSys));
    
    if (sSys) 
    {
        SYS_Load(sSys);
        strcpy(sSettings.sInfo.mac, sSys->mac);
        strcpy(sSettings.sInfo.serialNumber, sSys->serial);
        strcpy(sSettings.sInfo.productionData, sSys->proddate);
        strcpy(sSettings.sFlags.testState, sSys->testState);
        vPortFree(sSys);
    }
    else 
    {
        strcpy(sSettings.sInfo.productionData, "00.00.00 00:00");
        strcpy(sSettings.sInfo.mac, DEVICE_MAC);
        strcpy(sSettings.sInfo.serialNumber, DEVICE_SERIAL);
        memset(sSettings.sFlags.testState, 0, 16);
    }
    
    sSettings.sInfo.id = 0;
    strcpy(sSettings.sInfo.location, "");
    strcpy(sSettings.sInfo.owner, "");
    strcpy(sSettings.sInfo.comments, "");
    strcpy(sSettings.sInfo.incharge, "");
#endif    
}  

/**
  * @brief  Установить флаги по умолчанию
  */
void SETTINGS_SetFlagsDef(void)
{
    sSettings.sFlags.netsettingsChanged = false;
}

/**
  * @brief  Установить параметры сетевого подключения по умолчанию
  */
void SETTINGS_SetEthternetSwitchDef(void)
{
    sSettings.sEthernet.Enabled = true;
    sSettings.sEthernet.prior = 1;
    strcpy(sSettings.sEthernet.ip_test, "10.254.0.33");
}

/**
  * @brief  Установить параметры GSM подключения по умолчанию
  */
void SETTINGS_SetGSMDef(void)
{
    for(uint8_t i = 0; i < NUM_GSM; i++)
    {
        sSettings.sGSM[i].Enabled = true;
        sSettings.sGSM[i].prior = 0;
        sSettings.sGSM[i].type_profile = USER;
        strcpy(sSettings.sGSM[i].ip_test, "10.254.0.33");
        strcpy(sSettings.sGSM[i].APNaddr, "MDUDPTN");
        strcpy(sSettings.sGSM[i].APNlogin, "");
        strcpy(sSettings.sGSM[i].APNpass, "");
        strcpy(sSettings.sGSM[i].msisdn, "");
        sSettings.sGSM[i].timeout = 30;
        sSettings.sGSM[i].period_test = 100;
        sSettings.sGSM[i].num_bad_connect = 3;
        sSettings.sGSM[i].modem_mode = AUTOMATIC;
    }
    sSettings.sGSM[0].prior = 3;
    sSettings.sGSM[1].prior = 2;
}

/**
  * @brief  Установить параметры SNTP по умолчанию
  */
void SETTINGS_SetSntpDef(void)
{
    sSettings.sSNTP.sntpEnable = true;
    strcpy(sSettings.sSNTP.ip1, "10.254.0.50"); 
    strcpy(sSettings.sSNTP.ip2, "10.254.0.50"); 
    sSettings.sSNTP.timeZone = 3.0;  
    strcpy(sSettings.sSNTP.data, "none");
}

/**
  * @brief  Установить параметры подключения к серверу по умолчанию
  */
void SETTINGS_SetServerParamsDef(void)
{
    strcpy(sSettings.sServer.ip, "10.254.0.69");
    sSettings.sServer.port = 1884;
    sSettings.sServer.serverEnable = true;
    sSettings.sServer.controlConnect = true;
}

/**
  * @brief  Установить параметры PROXY по умолчанию
  */
void SETTINGS_SetProxyParamsDef(void)
{
    strcpy(sSettings.sProxy.ip, "79.135.245.84");
    sSettings.sProxy.port = 55679;
    sSettings.sProxy.local_port = 0;
}

/**
  * @brief  Установить значение настроек прозрачного порта по умолчанию
  */
void SETTINGS_SetPortGwDef(void)
{
    for(uint8_t i = 0; i < NUM_PORTGW; i ++)
    {
        sSettings.sPortGw[i].enableDispatch = false;    // Параллельная диспетчеризация (Вкл./Выкл.)
        sSettings.sPortGw[i].type_port = ACCOUNT_METER; // Тип порта (Модем / Прибор учета)
        sSettings.sPortGw[i].connect_port = 2;          // Связанный порт (#1-#3)
        strcpy(sSettings.sPortGw[i].description, "");   // Описание
        sSettings.sPortGw[i].enabled = true;            // Вкл/Откл 
        sSettings.sPortGw[i].transtype = GW_TCP;        // Тип транспорта (TCP/UDP)
        sSettings.sPortGw[i].mode = GW_SERVER;          // Режим: Клиент/Сервер 
        strcpy(sSettings.sPortGw[i].ip, "192.168.1.2"); // IP-адрес сервера (для клиента) 
        sSettings.sPortGw[i].port = 1001;               // Порт 
        sSettings.sPortGw[i].rs_mode = RS_485_1;        // Режим (RS-232/RS-485)
        sSettings.sPortGw[i].uart_set.baud = BRD_9600;  // Скорость порта
        sSettings.sPortGw[i].uart_set.parity = NO_PAR;  // Четность 
        sSettings.sPortGw[i].uart_set.databits = DATABITS_8;  // Число бит данных
        sSettings.sPortGw[i].uart_set.stopbits = STOP_1;      // Число стоп-бит
    }  

    sSettings.sPortGw[0].rs_mode = RS_232_1;
    sSettings.sPortGw[0].port = 1001;
    sSettings.sPortGw[0].connect_port = 1;
    
    sSettings.sPortGw[1].rs_mode = RS_485_1;
    sSettings.sPortGw[1].port = 1002;
    sSettings.sPortGw[1].connect_port = 2;
    sSettings.sPortGw[1].enabled = false;
    
    sSettings.sPortGw[2].rs_mode = RS_232_2;
    sSettings.sPortGw[2].port = 1003;
    sSettings.sPortGw[2].connect_port = 0;
    sSettings.sPortGw[2].enabled = false;
}

/**
  * @brief  Установить значение системы параллельной диспетчеризации по умолчанию
  */
void SETTINGS_SetPSDDef(void)
{
    sSettings.sPSD.enabled = false;
    sSettings.sPSD.mux_mode = PSD_MODE_7;
    sSettings.sPSD.rs_mode = RS_232_1;
    sSettings.sPSD.web_port = 0;
    sSettings.sPSD.assd_timeout = 120;
}

/**
  * @brief  Установить значение пароля по умолчанию
  */
void SETTINGS_SetServiceDef(void)
{
    sSettings.sAuth[0].level = ADMIN;
    strcpy(sSettings.sAuth[0].login, "user");
    //"b26b1868 705a3f0a a82cbd79 7cd58b4e" uchetmo
    *(uint32_t *)sSettings.sAuth[0].hash        = 0x68186bb2;
    *(uint32_t *)(sSettings.sAuth[0].hash + 4)  = 0x0a3f5a70;
    *(uint32_t *)(sSettings.sAuth[0].hash + 8)  = 0x79bd2ca8;
    *(uint32_t *)(sSettings.sAuth[0].hash + 12) = 0x4e8bd57c;
}

/**
  * @brief  Сброс всех настроек в значения по умолчанию кроме сетевых настроек
  */
void SETTINGS_SetPartDefault(void)
{
    SETTINGS_SetFlagsDef();
    
    SETTINGS_SetEthternetSwitchDef();
    SETTINGS_SetWebParamsDef();
    SETTINGS_SetTempWebParamsDef();
    SETTINGS_SetGSMDef();
    SETTINGS_SetSntpDef();
    SETTINGS_SetServerParamsDef();
    SETTINGS_SetProxyParamsDef();
    SETTINGS_SetPortGwDef();
    SETTINGS_SetPSDDef();
    
    sSettings.settVer = SETTINGS_VERSION;
    
    sSettings.CritSecCRC = settings_get_crit_sec_crc();
    
    sSettings.controlWorld = SETTINGS_CONTROL_WORD;
}
  

#endif



#if 0
/**
  * @brief  
  */






/**
  * @brief   Сброс флага boottry в регистре RTC
  */
void SETTINGS_ResetBootTry(void)
{
    uint8_t bootTry = (uint8_t)bpr_data_read(BPR_DATA2);
#if 0    
    if (bootTry > 0 )
    {
        bootTry = 0;
        bpr_data_write(BPR_DATA2, 0);
    
        // Check FW update flag
        if (bpr_data_read(BPR_DATA3)) {
            log_add_record(UPDATED_FW, 0);
            // Clear FW update flag
            bpr_data_write(BPR_DATA3, 0);
        }
    }
#endif    
}

/**
  * @brief  Преобразует mac адрес строкового формата в массив uint8_t
  * @param  mac - буфер для вывода mac адреса
  */
void SETTINGS_GetMac(uint8_t *mac)
{
    char dummy[2];
    char *macPtr = sSettings.sInfo.mac;	
  
    for (uint8_t i = 0; i < 6; i++)
    {
        strncpy(dummy, macPtr+i*3, 2);
        mac[i] = (uint8_t)strtol(dummy, NULL, 16);
    }
}

/**
  * @brief  Установить дату производства
  */
void SETTINGS_SetProDate(char *proDate, uint8_t len)
{
#if 0  
    sys_settings_t *sSys = NULL;
    
    sSys = pvPortMalloc(sizeof(*sys_settings_t));
    if (sSys) 
    {
        SYS_Load(sSys);
        strcpy(sSys->proddate, proDate);
        sys_save(sSys);
        memset(sSettings.sInfo.productionData, 0, 40);
        memcpy(sSettings.sInfo.productionData, proDate, len);
        vPortFree(sSys);
        //SETTINGS_Save();
    }
#endif
}

/**
  * @brief  Установить серийный номер
  */
void SETTINGS_SetSerialNumber(char *sn, uint8_t len)
{
#if 0  
    sys_settings_t *sSys = NULL;
    
    sSys = pvPortMalloc(sizeof(*sSys));
    if (sSys) 
    {
        SYS_Load(sSys);
        strcpy(sSys->serial, sn);
        sys_save(sSys);
        memset(sSettings.sInfo.serialNumber, 0, 16);
        memcpy(sSettings.sInfo.serialNumber, sn, len);
        vPortFree(sSys);
        //SETTINGS_Save();
    }
#endif
}

/**
  * @brief  Установить MAC
  */
void SysSETTINGS_SetMAC(char *mac, uint8_t len)
{
#if 0  
    sys_settings_t *sSys = NULL;
    
    sSys = pvPortMalloc(sizeof(*sSys));
    if (sSys) 
    {
        SYS_Load(sSys);
        strcpy(sSys->mac, mac);
        sys_save(sSys);
        memset(sSettings.sInfo.mac, 0, 18);
        memcpy(sSettings.sInfo.mac, mac, len);
        vPortFree(sSys);
        //SETTINGS_Save();
    }
#endif    
}

/**
  * @brief  Установить статус тестирования
  */
void SETTINGS_StatusTest(char *status, uint8_t len)
{
#if 0  
    SYS_t *sSys = NULL;
    
    sSys = pvPortMalloc(sizeof(*sSys));
    if (sSys) 
    {
        SYS_Load(sSys);
        strcpy(sSys->testState, status);
        sys_save(sSys);
        memset(sSettings.sFlags.testState, 0, 16);
        memcpy(sSettings.sFlags.testState, status, len);
        vPortFree(sSys);
        SETTINGS_Save();
    }
#endif
}

/**
  * @brief  Установить статус тестирования "T2OK"
  */
void SETTINGS_SetT2OK(void)
{
#if 0  
    SYS_t *sSys = NULL;
    
    sSys = pvPortMalloc(sizeof(*sSys));
    if (sSys) 
    {
        SYS_Load(sSys);
        
        //SETTINGS_SetEthternetSwitchDef(); // TODO временное решение
        
        memset(sSettings.sFlags.testState, 0, 16);
        memcpy(sSettings.sFlags.testState, "T2OK", 4);
        strcpy(sSys->testState, sSettings.sFlags.testState);
        sys_save(sSys);
        vPortFree(sSys);
        SETTINGS_Save();
    }
#endif    
}


/**
  * @brief  Сбросить статус тестирования "T2OK"
  */
void SETTINGS_ResetT2OK(void)
{
#if 0  
    SYS_t *sSys = NULL;
    
    sSys = pvPortMalloc(sizeof(*sSys));
    if (sSys) 
    {
        SYS_Load(sSys);
        
        SETTINGS_SetEthternetSwitchDef(); // TODO временное решение
        
        memset(sSettings.sFlags.testState, 0, 16);
        strcpy(sSys->testState, sSettings.sFlags.testState);
        sys_save(sSys);
        vPortFree(sSys);
        SETTINGS_Save();
    }
#endif    
}


/**
  * @brief  Устанавливает mac адрес исходя из unique id
  */
void COM_SetTestMAC(void)
{
    uint8_t octet1 = 0;
    uint8_t octet2 = 0;
    uint8_t octet3 = 0;
    uint8_t octet4 = 0;
    uint8_t octet5 = 0;

    char AT_ID_HEX[12];
    uint8_t hash[16];
    
    GetAT32IDInt(AT_ID_HEX);
    
    md5hash(hash, (unsigned char*)AT_ID_HEX, sizeof(AT_ID_HEX));
    
    octet1 = hash[0];
    octet2 = hash[1];
    octet3 = hash[2];
    octet4 = hash[3];
    octet5 = hash[4];
    /* Устанавливаем MAC, но не сохраняем */
    //snprintf(sSettings.sInfo.mac, MAC_LEN, "%02X-%02X-%02X-%02X-%02X-00", octet1 & 0xFE, octet2, octet3, octet4, octet5);
}

/**
  * @brief  Включить DHCP
  */
void SETTINGS_SetDHCPOn(void)
{
    sSettings.sWebTempParams.dhcpEnable = 1;
}

//
void SETTINGS_Print(void)
{
    printf("\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n");
    printf("   Общие настройки для bootloader и FW");
    printf("\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\n");
    
    printf("Сетевые настройки:\r\n");
    
    printf("\tIP: ");
    printf("%s\r\n", sSettings.sWebParams.ip);
    
    printf("\tGate: ");
    printf("%s\r\n", sSettings.sWebParams.gate);
    
    printf("\tMask: ");
    printf("%s\r\n", sSettings.sWebParams.mask);
    
    printf("\tDNS1: ");
    printf("%s\r\n", sSettings.sWebParams.dns1);
    
    printf("\tDNS2: ");
    printf("%s\r\n", sSettings.sWebParams.dns2);
    
    if (sSettings.sWebParams.dhcpEnable)
        printf("\tDHCP: On\r\n");
    else
        printf("\tDHCP: Off\r\n");
        
// -------------------------------------------------------------------------- //        
  
    printf("\r\nИнформация об устройстве:\r\n");
    
    printf("\tID: ");
    printf("%u\r\n", sSettings.sInfo.id);
    
    printf("\tДата производства: ");
    printf("%s\r\n", sSettings.sInfo.productionData);
    
    printf("\tMAC: ");
    printf("%s\r\n", sSettings.sInfo.mac);
    
    printf("\tСерийный номер: ");
    printf("%s\r\n", sSettings.sInfo.serialNumber);
    
    printf("\tВладелец: ");
    printf("%s\r\n", sSettings.sInfo.owner);
    
    printf("\tАдрес: ");
    printf("%s\r\n", sSettings.sInfo.location);
    
    printf("\tКомментарии: ");
    printf("%s\r\n", sSettings.sInfo.comments);
    
    printf("\tОтветственное лицо: ");
    printf("%s\r\n", sSettings.sInfo.incharge);
    
// -------------------------------------------------------------------------- //    
    
    for (uint8_t i = 0; i < NUM_GSM; i++)
    {
        printf("\r\nНастройки GSM модема:\r\n");
        printf("Профиль GSM %u:\r\n", i + 1);
        
        if (sSettings.sGSM[i].Enabled)
            printf("\tChannel: On\r\n");
        else
            printf("\tChannel: Off\r\n");
          
        printf("\tПриоритет: %u\r\n", sSettings.sGSM[i].prior);
       
        switch (sSettings.sGSM[i].type_profile)
        {
            case BEELINE :
                printf("\tТип профиля: BEELINE\r\n");
            break;
            case MTS :
                printf("\tТип профиля: MTS\r\n");
            break;
            case MEGAFON :
                printf("\tТип профиля: MEGAFON\r\n");
            break;
            case TELE2 :
                printf("\tТип профиля: TELE2\r\n");
            break;
            case USER :
                printf("\tТип профиля: USER\r\n");
            break;
        }
        
        printf("\tAPN: %s\r\n", sSettings.sGSM[i].APNaddr);
        printf("\tAPN логин: %s\r\n", sSettings.sGSM[i].APNlogin);
        printf("\tAPN пароль: %s\r\n", sSettings.sGSM[i].APNpass);
        printf("\tIP для проверки связи: %s\r\n", sSettings.sGSM[i].ip_test);
        printf("\tНомер SIM карты: %s\r\n", sSettings.sGSM[i].msisdn);
        printf("\tТаймаут ожидания ответа: %u, с\r\n", sSettings.sGSM[i].timeout);
        printf("\tВремя проверки каналов связи: %u\r\n", sSettings.sGSM[i].period_test);
        printf("\tКоличество неудачных попыток: %u\r\n", sSettings.sGSM[i].num_bad_connect);
        printf("\tРежим работы GSM модема: ");
        
        switch (sSettings.sGSM[i].modem_mode)
        {
            case AUTOMATIC :
                printf("Automatic\r\n");
            break;
            
            case GSM_3G :
                printf("GSM + 3G\r\n");
            break;
            
            case LTE :
                printf("LTE only\r\n");
            break;
            
            case WCDMA :
                printf("3G only\r\n");
            break;
            
            case GSM :
                printf("GSM only\r\n");
            break;
        }
        
    }
    
// -------------------------------------------------------------------------- //        
    
    printf("\r\nCRC критической секции: %X\r\n", sSettings.CritSecCRC);
    
// -------------------------------------------------------------------------- //    
    
    printf("\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n");
    printf("   Секция настрок для FW");
    printf("\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\n");
    
// -------------------------------------------------------------------------- //        
    
    printf("\r\nФлаги:\r\n");
    
    printf("\tИзменение сетевых настроек: %u\r\n", sSettings.sFlags.netsettingsChanged);
    printf("\tСтатус тестирования: %s\r\n", sSettings.sFlags.testState);
    
// -------------------------------------------------------------------------- //        
    
    printf("\r\nEthernet канал:\r\n");
    
    if (sSettings.sEthernet.Enabled)
        printf("\tКанал: вкл\r\n");
    else
        printf("\tКанал: выкл\r\n");

    printf("\tПриоритет: %u\r\n", sSettings.sEthernet.prior);
    printf("\tIP для проверки связи: %s\r\n", sSettings.sEthernet.ip_test);
    
// -------------------------------------------------------------------------- //
    
    printf("\r\nСервер:\r\n");
    
    if (sSettings.sServer.serverEnable)
        printf("\tСервер: вкл\r\n");
    else
        printf("\tСервер: выкл\r\n");
    
    printf("\tIP: %s\r\n", sSettings.sServer.ip);
    printf("\tPort: %u\r\n", sSettings.sServer.port);
    
    if (sSettings.sServer.controlConnect)
        printf("\tКонтроль соединения: вкл\r\n");
    else
        printf("\tКонтроль соединения: выкл\r\n");
    
// -------------------------------------------------------------------------- //    
    
    printf("\r\nProxy:\r\n");
    
    printf("\tIP: %s\r\n", sSettings.sProxy.ip);
    printf("\tPort: %u\r\n", sSettings.sProxy.port);
    printf("\tLocal port: %u\r\n", sSettings.sProxy.local_port);
    
// -------------------------------------------------------------------------- //        

    for (uint8_t i = 0; i < MAX_WEB_USERS; i++)
    {
        printf("\r\nНастройки аутентификации пользователя %u:\r\n", i + 1);

        switch (sSettings.sAuth[i].level)
        {
            case FACTORY :
                printf("\tУровень пользователя: FACTORY\r\n");
            break;
            case ADMIN :
                printf("\tУровень пользователя: ADMIN\r\n");
            break;
        }
    
        printf("\tlogin: %s\r\n", sSettings.sAuth[i].login);
        printf("\thash: %X\r\n", sSettings.sAuth[i].hash);
    }
    
// -------------------------------------------------------------------------- //        
    
    for (uint8_t i = 0; i < NUM_PORTGW; i++)
    {
        printf("\r\nНастройки прозрачного порта %u:\r\n", i + 1);
        
        if (sSettings.sPortGw[i].enableDispatch)
            printf("\tПараллельная диспетчеризация: вкл\r\n");
        else
            printf("\tПараллельная диспетчеризация: выкл\r\n");
        
        if (sSettings.sPortGw[i].enabled)
            printf("\tПрозрачный порт: вкл\r\n");
        else
            printf("\tПрозрачный порт: выкл\r\n");
        
        switch (sSettings.sPortGw[i].type_port)
        {
            case MODEM :
                printf("\tТип порта: Модем\r\n");
            break;
            case ACCOUNT_METER :
                printf("\tТип порта: Прибор учета\r\n");
            break;
        }
        
        printf("\tСвязанный порт %u:\r\n", sSettings.sPortGw[i].connect_port);
        printf("\tОписание: %s\r\n", sSettings.sPortGw[i].description);
        
        
        switch (sSettings.sPortGw[i].transtype)
        {
            case GW_UDP :
                printf("\tТип транспорта: UDP\r\n");
            break;
            case GW_TCP :
                printf("\tТип транспорта: TCP\r\n");
            break;
        }
        
        switch (sSettings.sPortGw[i].mode)
        {
            case GW_SERVER :
                printf("\tРежим: сервер\r\n");
            break;
            case GW_CLIENT :
              printf("\tРежим: клиент\r\n");
            break;
        }
        
        printf("\tIP сервера (режим клиента): %s\r\n", sSettings.sPortGw[i].ip);
        printf("\tПорт: %u\r\n", sSettings.sPortGw[i].port);
        
        switch (sSettings.sPortGw[i].rs_mode)
        {
            case RS_232_1 :
                printf("\tРежим (RS-232/RS-485): RS_232_1\r\n");
            break;
            case RS_232_2 :
                printf("\tРежим (RS-232/RS-485): RS_232_2\r\n");
            break;
            case RS_232_3 :
                printf("\tРежим (RS-232/RS-485): RS_232_3\r\n");
            break;
            case RS_485_1 :
                printf("\tРежим (RS-232/RS-485): RS_485_1\r\n");
            break;
            case RS_485_2 :
                printf("\tРежим (RS-232/RS-485): RS_485_2\r\n");
            break;
        }
                
        switch (sSettings.sPortGw[i].uart_set.baud)
        {
            case BRD_1200 :
                printf("\tBaudrate: 1200\r\n");
            break;
            case BRD_2400 :
                printf("\tBaudrate: 2400\r\n");
            break;
            case BRD_4800 :
                printf("\tBaudrate: 4800\r\n");
            break;
            case BRD_9600 :
                printf("\tBaudrate: 9600\r\n");
            break;
            case BRD_19200 :
                printf("\tBaudrate: 19200\r\n");
            break;
            case BRD_38400 :
                printf("\tBaudrate: 38400\r\n");
            break;
            case BRD_57600 :
                printf("\tBaudrate: 57600\r\n");
            break;
            case BRD_115200 :
                printf("\tBaudrate: 115200\r\n");
            break;
            case BRD_230400 :
                printf("\tBaudrate: 230400\r\n");
            break;
            case BRD_460800 :
                printf("\tBaudrate: 460800\r\n");
            break;
        }

        switch (sSettings.sPortGw[i].uart_set.databits)
        {
            case DATABITS_7 :
                printf("\tData bits: 7\r\n");
            break;
            case DATABITS_8 :
                printf("\tData bits: 8\r\n");
            break;
        }
        
        switch (sSettings.sPortGw[i].uart_set.stopbits)
        {
            case STOP_0_5 :
                printf("\tStop bits: 0_5\r\n");
            break;
            case STOP_1 :
                printf("\tStop bits: 1\r\n");
            break;
            case STOP_1_5 :
                printf("\tStop bits: 1_5\r\n");
            break;
            case STOP_2 :
                printf("\tStop bits: 2\r\n");
            break;
        }
    }
    
// -------------------------------------------------------------------------- //    
    
    printf("\r\nПротокол SNTP:\r\n");    
    
    if (sSettings.sSNTP.sntpEnable)
        printf("\tSNTP: вкл\r\n");
    else
        printf("\tSNTP: выкл\r\n");
        
    printf("\tСервер 1 IP: %s\r\n", sSettings.sSNTP.ip1);
    printf("\tСервер 2 IP: %s\r\n", sSettings.sSNTP.ip2);
    printf("\tДата последней синхронизации: %s\r\n", sSettings.sSNTP.data);
    printf("\tЧасовой пояс (utc): %f\r\n", sSettings.sSNTP.timeZone);
    
// -------------------------------------------------------------------------- //    
    
    printf("\r\nПараллельная диспетчеризация:\r\n");
    
    if (sSettings.sPSD.enabled)
        printf("\tДиспечеризация: вкл\r\n");
    else
        printf("\tДиспечеризация: выкл\r\n");

    switch (sSettings.sPSD.mux_mode)
    {
        case PSD_MODE_1 :
            printf("\tРежим (управление мультиплексором): MODE_1 - RS232#1 <-> RS232#2, USART6 <-> RS232#3\r\n");
        break;
        case PSD_MODE_2 :
            printf("\tРежим (управление мультиплексором): MODE_2 - RS485#1 <-> RS232#2, USART6 <-> RS232#3\r\n");
        break;
        case PSD_MODE_3 :
            printf("\tРежим (управление мультиплексором): MODE_3 - RS232#1 <-> RS485#2, USART6 <-> RS232#3\r\n");
        break;
        case PSD_MODE_4 :
            printf("\tРежим (управление мультиплексором): MODE_4 - RS485#1 <-> RS485#2, USART6 <-> RS232#3\r\n");
        break;
        case PSD_MODE_5 :
            printf("\tРежим (управление мультиплексором): MODE_5 - RS232#1 <-> RS232#3\r\n");
        break;
        case PSD_MODE_6 :
            printf("\tРежим (управление мультиплексором): MODE_6 - RS485#1 <-> RS232#3\r\n");
        break;
        case PSD_MODE_7 :
            printf("\tРежим (управление мультиплексором): MODE_7 - USART1 <-> RS485#1, \r\n\t\tUSART3 <-> RS485#2, USART4 <-> RS232#1, \r\n\t\tUSART5 <-> RS232#2, USART6 <-> RS232#3\r\n");
        break;
        
    }
    
    switch (sSettings.sPSD.rs_mode)
    {
        case RS_232_1 :
            printf("\tРежим (RS-232/RS-485): RS_232_1\r\n");
        break;
        case RS_232_2 :
            printf("\tРежим (RS-232/RS-485): RS_232_2\r\n");
        break;
        case RS_232_3 :
            printf("\tРежим (RS-232/RS-485): RS_232_3\r\n");
        break;
        case RS_485_1 :
            printf("\tРежим (RS-232/RS-485): RS_485_1\r\n");
        break;
        case RS_485_2 :
            printf("\tРежим (RS-232/RS-485): RS_485_2\r\n");
        break;
    }
    
    printf("\tНомер прозрачного порта: %u\r\n", sSettings.sPSD.web_port);
    printf("\tТаймаут: %u, сек\r\n", sSettings.sPSD.assd_timeout);
    
// -------------------------------------------------------------------------- //    
    
    printf("\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n");
    
    printf("\tКонтрольное слово: %u\r\n", sSettings.controlWorld);
    printf("\tCRC: %X\r\n", *(uint32_t*)CRC_ADDRESS);
}
#endif