/*
* Copyright (c) 2020 AIIT XUOS Lab
* XiUOS is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*        http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/

/**
 * @file ps5308.c
 * @brief PS5308 PM1.0, PM2.5, PM10, driver base perception
 * @version 1.0
 * @author AIIT XUOS Lab
 * @date 2021.04.22
 */

#include <sensor.h>

static struct SensorDevice ps5308;
static int32_t active_task_id;
static int buff_lock;

static struct SensorProductInfo info =
{
    SENSOR_ABILITY_PM,
    "Pulsensor",
    "PS5308",
};

/**
 * @description: Read sensor task
 * @param sdev - sensor device pointer
 */
static void ReadTask(struct SensorDevice *sdev)
{
    while (1) {
        UserMutexObtain(buff_lock, WAITING_FOREVER);
        sdev->done->read(sdev, 32);
        UserMutexAbandon(buff_lock);
        UserTaskDelay(750);
    }
}

/**
 * @description: Open PS5308 sensor device
 * @param sdev - sensor device pointer
 * @return success: EOK , failure: other
 */
static int SensorDeviceOpen(struct SensorDevice *sdev)
{
    int result = EOK;

    buff_lock = UserMutexCreate();

    sdev->fd = open(SENSOR_DEVICE_PS5308_DEV, O_RDWR);
    
    struct SerialDataCfg cfg;
    cfg.serial_baud_rate    = BAUD_RATE_9600;
    cfg.serial_data_bits    = DATA_BITS_8;
    cfg.serial_stop_bits    = STOP_BITS_1;
    cfg.serial_buffer_size  = 128;
    cfg.serial_parity_mode  = PARITY_NONE;
    cfg.serial_bit_order    = 0;
    cfg.serial_invert_mode  = 0;
    cfg.ext_uart_no         = SENSOR_DEVICE_PS5308_DEV_EXT_PORT;
    cfg.port_configure      = PORT_CFG_INIT;

    result = ioctl(sdev->fd, OPE_INT, &cfg);

    UtaskType active_task;
    const char name[NAME_NUM_MAX] = "ps5308_task";

    strncpy(active_task.name, name, strlen(name));
    active_task.func_entry  = ReadTask;
    active_task.func_param  = sdev;
    active_task.prio        = KTASK_PRIORITY_MAX/2;
    active_task.stack_size  = 1024;

    active_task_id = UserTaskCreate(active_task);
    result = UserTaskStartup(active_task_id);

    return result;
}

/**
 * @description: Close PS5308 sensor device
 * @param sdev - sensor device pointer
 * @return EOK
 */
static int SensorDeviceClose(struct SensorDevice *sdev)
{
    UserTaskDelete(active_task_id);
    UserMutexDelete(buff_lock);
    return EOK;
}

/**
 * @description: Read sensor device
 * @param sdev - sensor device pointer
 * @param len - the length of the read data
 * @return get data length
 */
static x_size_t SensorDeviceRead(struct SensorDevice *sdev, size_t len)
{
    uint8 tmp = 0;
    int timeout = 0;
    while (1) {
        read(sdev->fd, &tmp, 1);
        if ((tmp == 0x44) || (timeout >= 1000))
            break;
        UserTaskDelay(10);
        ++timeout;
    }
    
    if (tmp != 0x44)
        return -ERROR;

    uint8 idx = 0;
    sdev->buffer[idx++] = tmp;

    while ((idx < len) && (timeout < 1000)) {
        if (read(sdev->fd, &tmp, 1) == 1) {
            timeout = 0;
            sdev->buffer[idx++] = tmp;
        }
        ++timeout;
    }

    return idx;
}

static struct SensorDone SensorDeviceDone =
{
    SensorDeviceOpen,
    SensorDeviceClose,
    SensorDeviceRead,
    NONE,
    NONE,
};

/**
 * @description: Init PS5308 sensor and register
 * @return void
 */
static void Ps5308Init(void)
{
    ps5308.name = SENSOR_DEVICE_PS5308;
    ps5308.info = &info;
    ps5308.done = &SensorDeviceDone;
    ps5308.status = SENSOR_DEVICE_ACTIVE;

    SensorDeviceRegister(&ps5308);
}


#ifdef SENSOR_QUANTITY_PS5308_PM1_0
static struct SensorQuantity ps5308_pm1_0;

/**
 * @description: Analysis PS5308 PM1.0 result
 * @param quant - sensor quantity pointer
 * @return quantity value
 */
static int32 ReadPm1_0(struct SensorQuantity *quant)
{
    if (!quant)
        return -ERROR;

    uint32 result;
    if (quant->sdev->done->read != NONE) {
        uint16 checksum = 0;
        UserMutexObtain(buff_lock, WAITING_FOREVER);

        for (uint8 i = 0; i < 30; i++)
            checksum += quant->sdev->buffer[i];          
        
        if (checksum == (((uint16)quant->sdev->buffer[30] << 8) + ((uint16)quant->sdev->buffer[31]))) {
            result = ((uint16)quant->sdev->buffer[4] << 8) + (uint16)quant->sdev->buffer[5];
            if (result > quant->value.max_value)
                quant->value.max_value = result;
            else if (result < quant->value.min_value)
                quant->value.min_value = result;
            quant->value.last_value = result;

            return result;
        }else{
            printf("This reading is wrong\n");
            result = SENSOR_QUANTITY_VALUE_ERROR;
            return result;
        }
    }else{
        printf("%s don't have read done.\n", quant->name);
    }
    
    return -ERROR;
}

/**
 * @description: Init PS5308 PM1.0 quantity and register
 * @return 0
 */
int Ps5308Pm1_0Init(void)
{
    Ps5308Init();
    
    ps5308_pm1_0.name = SENSOR_QUANTITY_PS5308_PM1_0;
    ps5308_pm1_0.type = SENSOR_QUANTITY_PM;
    ps5308_pm1_0.value.decimal_places = 0;
    ps5308_pm1_0.value.max_std = SENSOR_QUANTITY_VALUE_ERROR;
    ps5308_pm1_0.value.min_std = 0;
    ps5308_pm1_0.value.last_value = SENSOR_QUANTITY_VALUE_ERROR;
    ps5308_pm1_0.value.max_value = SENSOR_QUANTITY_VALUE_ERROR;
    ps5308_pm1_0.value.min_value = SENSOR_QUANTITY_VALUE_ERROR;
    ps5308_pm1_0.sdev = &ps5308;
    ps5308_pm1_0.ReadValue = ReadPm1_0;

    SensorQuantityRegister(&ps5308_pm1_0);

    return 0;
}
#endif

#ifdef SENSOR_QUANTITY_PS5308_PM2_5
static struct SensorQuantity ps5308_pm2_5;

/**
 * @description: Analysis PS5308 PM2.5 result
 * @param quant - sensor quantity pointer
 * @return quantity value
 */
static int32 ReadPm2_5(struct SensorQuantity *quant)
{
    if (!quant)
        return -ERROR;

    uint32 result;
    if (quant->sdev->done->read != NONE) {
        uint16 checksum = 0;
        UserMutexObtain(buff_lock, WAITING_FOREVER);

        for (uint i = 0; i < 30; i++)
            checksum += quant->sdev->buffer[i];
        
        if (checksum == (((uint16)quant->sdev->buffer[30] << 8) + ((uint16)quant->sdev->buffer[31]))) {
            result = ((uint16)quant->sdev->buffer[6] << 8) + (uint16)quant->sdev->buffer[7];
            if (result > quant->value.max_value)
                quant->value.max_value = result;
            else if (result < quant->value.min_value)
                quant->value.min_value = result;
            quant->value.last_value = result;

            return result;
        }else{
            printf("This reading is wrong\n");
            result = SENSOR_QUANTITY_VALUE_ERROR;
            return result;
        }
    }else{
        printf("%s don't have read done.\n", quant->name);
    }
    
    return -ERROR;
}

/**
 * @description: Init PS5308 PM2.5 quantity and register
 * @return 0
 */
int Ps5308Pm2_5Init(void)
{
    Ps5308Init();
    
    ps5308_pm2_5.name = SENSOR_QUANTITY_PS5308_PM2_5;
    ps5308_pm2_5.type = SENSOR_QUANTITY_PM;
    ps5308_pm2_5.value.decimal_places = 0;
    ps5308_pm2_5.value.max_std = 35;
    ps5308_pm2_5.value.min_std = 0;
    ps5308_pm2_5.value.last_value = SENSOR_QUANTITY_VALUE_ERROR;
    ps5308_pm2_5.value.max_value = SENSOR_QUANTITY_VALUE_ERROR;
    ps5308_pm2_5.value.min_value = SENSOR_QUANTITY_VALUE_ERROR;
    ps5308_pm2_5.sdev = &ps5308;
    ps5308_pm2_5.ReadValue = ReadPm2_5;

    SensorQuantityRegister(&ps5308_pm2_5);

    return 0;
}
#endif

#ifdef SENSOR_QUANTITY_PS5308_PM10
static struct SensorQuantity ps5308_pm10;

/**
 * @description: Analysis PS5308 PM10 result
 * @param quant - sensor quantity pointer
 * @return quantity value
 */
static int32 ReadPm10(struct SensorQuantity *quant)
{
    if (!quant)
        return -ERROR;

    uint32 result;
    if (quant->sdev->done->read != NONE) {
        uint16 checksum = 0;
        UserMutexObtain(buff_lock, WAITING_FOREVER);

        for (uint i = 0; i < 30; i++)
            checksum += quant->sdev->buffer[i];
        
        if (checksum == (((uint16)quant->sdev->buffer[30] << 8) + ((uint16)quant->sdev->buffer[31]))) {
            result = ((uint16)quant->sdev->buffer[8] << 8) + (uint16)quant->sdev->buffer[9];
            if (result > quant->value.max_value)
                quant->value.max_value = result;
            else if (result < quant->value.min_value)
                quant->value.min_value = result;
            quant->value.last_value = result;

            return result;
        }else{
            printf("This reading is wrong\n");
            result = SENSOR_QUANTITY_VALUE_ERROR;
            return result;
        }
    }else{
        printf("%s don't have read done.\n", quant->name);
    }
    
    return -ERROR;
}

/**
 * @description: Init PS5308 PM10 quantity and register
 * @return 0
 */
int Ps5308Pm10Init(void)
{
    Ps5308Init();
    
    ps5308_pm10.name = SENSOR_QUANTITY_PS5308_PM10;
    ps5308_pm10.type = SENSOR_QUANTITY_PM;
    ps5308_pm10.value.decimal_places = 0;
    ps5308_pm10.value.max_std = 75;
    ps5308_pm10.value.min_std = 0;
    ps5308_pm10.value.last_value = SENSOR_QUANTITY_VALUE_ERROR;
    ps5308_pm10.value.max_value = SENSOR_QUANTITY_VALUE_ERROR;
    ps5308_pm10.value.min_value = SENSOR_QUANTITY_VALUE_ERROR;
    ps5308_pm10.sdev = &ps5308;
    ps5308_pm10.ReadValue = ReadPm10;

    SensorQuantityRegister(&ps5308_pm10);

    return 0;
}
#endif
