/*
 * meas_tasks.c
 *
 *  Created on: Sep 5, 2024
 *      Author: jakubski
 */

#include <math.h>
#include <stdio.h>

#include "adc_buffers.h"
#include "meas_tasks.h"
#include "measurements.h"
#include "node-red-config.h"
#include "peripherial.h"

#include "cmsis_os.h"
#include "main.h"

#ifdef PV_BOARD
#define VOLTAGES_COUNT 1
#define CURRENTS_COUNT 1
#else
#define VOLTAGES_COUNT 3
#define CURRENTS_COUNT 3
#endif

#define CIRC_BUFF_LEN 10

osThreadId_t adc1MeasTaskHandle      = NULL;
osThreadId_t adc2MeasTaskHandle      = NULL;
osThreadId_t adc3MeasTaskHandle      = NULL;
osThreadId_t limiterSwitchTaskHandle = NULL;
osThreadId_t encoderXTaskHandle      = NULL;
osThreadId_t encoderYTaskHandle      = NULL;

osMessageQueueId_t adc1MeasDataQueue = NULL;
osMessageQueueId_t adc2MeasDataQueue = NULL;
osMessageQueueId_t adc3MeasDataQueue = NULL;

osMutexId_t vRefmVMutex;
osMutexId_t resMeasurementsMutex;
osMutexId_t sensorsInfoMutex;
osMutexId_t ILxRefMutex;

volatile uint32_t vRefmV = 3000;

RESMeasurements resMeasurements __attribute__ ((aligned (32))) = { 0 };
SesnorsInfo sensorsInfo __attribute__ ((aligned (32)))         = { 0 };

uint16_t ILxRef[CURRENTS_COUNT] __attribute__ ((aligned (32))) = { 0 };
EncoderTaskArg encoderXTaskArg __attribute__ ((aligned (32)))  = { 0 };
EncoderTaskArg encoderYTaskArg __attribute__ ((aligned (32)))  = { 0 };

extern TIM_HandleTypeDef htim3;
extern TIM_OC_InitTypeDef motorXYTimerConfigOC;
extern osTimerId_t motorXTimerHandle;
extern osTimerId_t motorYTimerHandle;


void MeasTasksInit (void) {
    vRefmVMutex          = osMutexNew (NULL);
    resMeasurementsMutex = osMutexNew (NULL);
    sensorsInfoMutex     = osMutexNew (NULL);
    ILxRefMutex          = osMutexNew (NULL);
    adc1MeasDataQueue    = osMessageQueueNew (8, sizeof (ADC1_Data), NULL);
    adc2MeasDataQueue    = osMessageQueueNew (8, sizeof (ADC2_Data), NULL);
    adc3MeasDataQueue    = osMessageQueueNew (8, sizeof (ADC3_Data), NULL);

    osThreadAttr_t osThreadAttradc1MeasTask = { 0 };
    osThreadAttr_t osThreadAttradc2MeasTask = { 0 };
    osThreadAttr_t osThreadAttradc3MeasTask = { 0 };

    osThreadAttradc1MeasTask.stack_size = configMINIMAL_STACK_SIZE * 2;
    osThreadAttradc1MeasTask.priority   = (osPriority_t)osPriorityRealtime;

    osThreadAttradc2MeasTask.stack_size = configMINIMAL_STACK_SIZE * 2;
    osThreadAttradc2MeasTask.priority   = (osPriority_t)osPriorityRealtime;

    osThreadAttradc3MeasTask.stack_size = configMINIMAL_STACK_SIZE * 2;
    osThreadAttradc3MeasTask.priority   = (osPriority_t)osPriorityNormal;

    adc1MeasTaskHandle = osThreadNew (ADC1MeasTask, NULL, &osThreadAttradc1MeasTask);
    adc2MeasTaskHandle = osThreadNew (ADC2MeasTask, NULL, &osThreadAttradc2MeasTask);
    adc3MeasTaskHandle = osThreadNew (ADC3MeasTask, NULL, &osThreadAttradc3MeasTask);

    osThreadAttr_t osThreadAttradc1LimiterSwitchTask = { 0 };
    osThreadAttradc1LimiterSwitchTask.stack_size     = configMINIMAL_STACK_SIZE * 2;
    osThreadAttradc1LimiterSwitchTask.priority       = (osPriority_t)osPriorityNormal;
    limiterSwitchTaskHandle                          = osThreadNew (LimiterSwitchTask, NULL, &osThreadAttradc1LimiterSwitchTask);

    encoderXTaskArg.dbgLed                   = DBG_LED2;
    encoderXTaskArg.pvEncoder                = &(sensorsInfo.pvEncoderX);
    encoderXTaskArg.currentPosition          = &(sensorsInfo.currentXPosition);
    osMessageQueueAttr_t encoderMsgQueueAttr = { 0 };
    encoderXTaskArg.dataQueue                = osMessageQueueNew (16, sizeof (uint32_t), &encoderMsgQueueAttr);
    encoderXTaskArg.initPinStates = ((HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_15) << 1) | HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_14)) & 0x3;

    encoderYTaskArg.dbgLed          = DBG_LED3;
    encoderYTaskArg.pvEncoder       = &(sensorsInfo.pvEncoderY);
    encoderYTaskArg.currentPosition = &(sensorsInfo.currentYPosition);
    encoderYTaskArg.dataQueue       = osMessageQueueNew (16, sizeof (uint32_t), &encoderMsgQueueAttr);
    encoderYTaskArg.initPinStates = ((HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_11) << 1) | HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10)) & 0x3;

    osThreadAttr_t osThreadAttrEncoderTask = { 0 };
    osThreadAttrEncoderTask.stack_size     = configMINIMAL_STACK_SIZE * 2;
    osThreadAttrEncoderTask.priority       = (osPriority_t)osPriorityRealtime;
    encoderXTaskHandle                     = osThreadNew (EncoderTask, &encoderXTaskArg, &osThreadAttrEncoderTask);
    encoderYTaskHandle                     = osThreadNew (EncoderTask, &encoderYTaskArg, &osThreadAttrEncoderTask);
}

void ADC1MeasTask (void* arg) {
    float circBuffer[VOLTAGES_COUNT][CIRC_BUFF_LEN] = { 0 };
    float rms[VOLTAGES_COUNT]                       = { 0 };
    ;
    ADC1_Data adcData    = { 0 };
    uint32_t circBuffPos = 0;
    float gainCorrection = 1.0;
    while (pdTRUE) {
        osMessageQueueGet (adc1MeasDataQueue, &adcData, 0, osWaitForever);
#ifdef GAIN_AUTO_CORRECTION
        if (osMutexAcquire (vRefmVMutex, osWaitForever) == osOK) {
            gainCorrection = (float)vRefmV;
            osMutexRelease (vRefmVMutex);
        }
        gainCorrection = gainCorrection / EXT_VREF_mV;
#endif
        for (uint8_t i = 0; i < VOLTAGES_COUNT; i++) {
            float val                  = adcData.adcDataBuffer[i] * deltaADC * U_CHANNEL_CONST * gainCorrection * U_MeasCorrectionData[i].gain + U_MeasCorrectionData[i].offset;
            circBuffer[i][circBuffPos] = val;
            rms[i]                     = 0.0;
            for (uint8_t c = 0; c < CIRC_BUFF_LEN; c++) {
                rms[i] += circBuffer[i][c];
            }
            rms[i] = rms[i] / CIRC_BUFF_LEN;
            if (osMutexAcquire (resMeasurementsMutex, osWaitForever) == osOK) {
                if (fabs (resMeasurements.voltagePeak[i]) < fabs (val)) {
                    resMeasurements.voltagePeak[i] = val;
                }
                resMeasurements.voltageRMS[i] = rms[i];
                resMeasurements.power[i]      = resMeasurements.voltageRMS[i] * resMeasurements.currentRMS[i];
                osMutexRelease (resMeasurementsMutex);
            }
        }
        ++circBuffPos;
        circBuffPos = circBuffPos % CIRC_BUFF_LEN;

        if (osMutexAcquire (ILxRefMutex, osWaitForever) == osOK) {
            uint8_t refIdx = 0;
            for (uint8_t i = (uint8_t)IL1Ref; i <= (uint8_t)IL3Ref; i++) {
                ILxRef[refIdx++] = adcData.adcDataBuffer[i];
            }
            osMutexRelease (ILxRefMutex);
        }

        float fanFBVoltage = adcData.adcDataBuffer[FanFB] * deltaADC * -4.35 + 12;
        if (osMutexAcquire (sensorsInfoMutex, osWaitForever) == osOK) {
            sensorsInfo.fanVoltage = fanFBVoltage;
            osMutexRelease (sensorsInfoMutex);
        }
    }
}

void ADC2MeasTask (void* arg) {
    float circBuffer[CURRENTS_COUNT][CIRC_BUFF_LEN] = { 0 };
    float rms[CURRENTS_COUNT]                       = { 0 };
    ADC2_Data adcData                               = { 0 };
    uint32_t circBuffPos                            = 0;
    float gainCorrection                            = 1.0;
    while (pdTRUE) {
        osMessageQueueGet (adc2MeasDataQueue, &adcData, 0, osWaitForever);
        if (osMutexAcquire (vRefmVMutex, osWaitForever) == osOK) {
            gainCorrection = (float)vRefmV;
            osMutexRelease (vRefmVMutex);
        }
        gainCorrection            = gainCorrection / EXT_VREF_mV;
        float ref[CURRENTS_COUNT] = { 0 };
        if (osMutexAcquire (ILxRefMutex, osWaitForever) == osOK) {
            for (uint8_t i = 0; i < CURRENTS_COUNT; i++) {
                ref[i] = (float)ILxRef[i];
            }
            osMutexRelease (ILxRefMutex);
        }
        for (uint8_t i = 0; i < CURRENTS_COUNT; i++) {
            float adcVal               = (float)adcData.adcDataBuffer[i];
            float val                  = (adcVal - ref[i]) * deltaADC * I_CHANNEL_CONST * gainCorrection * I_MeasCorrectionData[i].gain + I_MeasCorrectionData[i].offset;
            circBuffer[i][circBuffPos] = val;
            rms[i]                     = 0.0;
            for (uint8_t c = 0; c < CIRC_BUFF_LEN; c++) {
                rms[i] += circBuffer[i][c];
            }
            rms[i] = rms[i] / CIRC_BUFF_LEN;
            if (osMutexAcquire (resMeasurementsMutex, osWaitForever) == osOK) {
                if (resMeasurements.currentPeak[i] < val) {
                    resMeasurements.currentPeak[i] = val;
                }
                resMeasurements.currentRMS[i] = rms[i];
                osMutexRelease (resMeasurementsMutex);
            }
        }
        ++circBuffPos;
        circBuffPos = circBuffPos % CIRC_BUFF_LEN;
    }
}

void ADC3MeasTask (void* arg) {
    float motorXSensCircBuffer[CIRC_BUFF_LEN] = { 0 };
    float motorYSensCircBuffer[CIRC_BUFF_LEN] = { 0 };
    float pvT1CircBuffer[CIRC_BUFF_LEN]       = { 0 };
    float pvT2CircBuffer[CIRC_BUFF_LEN]       = { 0 };
    uint32_t circBuffPos                      = 0;
    ADC3_Data adcData                         = { 0 };
    while (pdTRUE) {
        osMessageQueueGet (adc3MeasDataQueue, &adcData, 0, osWaitForever);
        uint32_t vRef = __LL_ADC_CALC_VREFANALOG_VOLTAGE (adcData.adcDataBuffer[VrefInt], LL_ADC_RESOLUTION_16B);
        if (osMutexAcquire (vRefmVMutex, osWaitForever) == osOK) {
            vRefmV = vRef;
            osMutexRelease (vRefmVMutex);
        }
        float motorXCurrentSense          = adcData.adcDataBuffer[motorXSense] * deltaADC * 10 / 8.33333;
        float motorYCurrentSense          = adcData.adcDataBuffer[motorYSense] * deltaADC * 10 / 8.33333;
        motorXSensCircBuffer[circBuffPos] = motorXCurrentSense;
        motorYSensCircBuffer[circBuffPos] = motorYCurrentSense;
        pvT1CircBuffer[circBuffPos]       = adcData.adcDataBuffer[pvTemp1] * deltaADC * 45.33333333 - 63;
        pvT2CircBuffer[circBuffPos]       = adcData.adcDataBuffer[pvTemp2] * deltaADC * 45.33333333 - 63;
        float motorXAveCurrent            = 0;
        float motorYAveCurrent            = 0;
        float pvT1AveTemp                 = 0;
        float pvT2AveTemp                 = 0;
        for (uint8_t i = 0; i < CIRC_BUFF_LEN; i++) {
            motorXAveCurrent += motorXSensCircBuffer[i];
            motorYAveCurrent += motorYSensCircBuffer[i];
#ifdef PV_BOARD
            pvT1AveTemp += pvT1CircBuffer[i];
            pvT2AveTemp += pvT2CircBuffer[i];
#endif
        }
        motorXAveCurrent /= CIRC_BUFF_LEN;
        motorYAveCurrent /= CIRC_BUFF_LEN;
        pvT1AveTemp /= CIRC_BUFF_LEN;
        pvT2AveTemp /= CIRC_BUFF_LEN;
        if (osMutexAcquire (sensorsInfoMutex, osWaitForever) == osOK) {
            if (sensorsInfo.motorXStatus == 1) {
                sensorsInfo.motorXAveCurrent = motorXAveCurrent;
                if (sensorsInfo.motorXPeakCurrent < motorXCurrentSense) {
                    sensorsInfo.motorXPeakCurrent = motorXCurrentSense;
                }
            }
            if (sensorsInfo.motorYStatus == 1) {
                sensorsInfo.motorYAveCurrent = motorYAveCurrent;
                if (sensorsInfo.motorYPeakCurrent < motorYCurrentSense) {
                    sensorsInfo.motorYPeakCurrent = motorYCurrentSense;
                }
            }
            sensorsInfo.pvTemperature[0] = pvT1AveTemp;
            sensorsInfo.pvTemperature[1] = pvT2AveTemp;
            osMutexRelease (sensorsInfoMutex);
        }
        ++circBuffPos;
        circBuffPos = circBuffPos % CIRC_BUFF_LEN;
    }
}

void LimiterSwitchTask (void* arg) {
    uint8_t limitXSwitchDownPrevState   = 0;
    uint8_t limitXSwitchCenterPrevState = 0;
    uint8_t limitXSwitchUpPrevState     = 0;
    uint8_t limitYSwitchDownPrevState   = 0;
    uint8_t limitYSwitchCenterPrevState = 0;
    uint8_t limitYSwitchUpPrevState     = 0;
    uint8_t pinStates                   = 0;
    uint8_t limiterXTriggered           = 0;
    uint8_t limiterYTriggered           = 0;
    if (osMutexAcquire (sensorsInfoMutex, osWaitForever) == osOK) {
        sensorsInfo.positionXWeak = 1;
        sensorsInfo.positionYWeak = 1;
        osMutexRelease (sensorsInfoMutex);
    }
    while (pdTRUE) {
        osDelay (pdMS_TO_TICKS (100));
        if (osMutexAcquire (sensorsInfoMutex, osWaitForever) == osOK) {
            sensorsInfo.limitXSwitchDown = HAL_GPIO_ReadPin (GPIOD, GPIO_PIN_13);
            pinStates                    = (limitXSwitchDownPrevState << 1) | sensorsInfo.limitXSwitchDown;
            if ((pinStates & 0x3) == 0x1) {
                limiterXTriggered            = 1;
                sensorsInfo.currentXPosition = 0;
                sensorsInfo.positionXWeak    = 0;
            }
            limitXSwitchDownPrevState = sensorsInfo.limitXSwitchDown;

            sensorsInfo.limitXSwitchUp = HAL_GPIO_ReadPin (GPIOD, GPIO_PIN_12);
            pinStates                  = (limitXSwitchUpPrevState << 1) | sensorsInfo.limitXSwitchUp;
            if ((pinStates & 0x3) == 0x1) {
                limiterXTriggered            = 1;
                sensorsInfo.currentXPosition = 100;
                sensorsInfo.positionXWeak    = 0;
            }
            limitXSwitchUpPrevState = sensorsInfo.limitXSwitchUp;

            sensorsInfo.limitXSwitchCenter = HAL_GPIO_ReadPin (GPIOD, GPIO_PIN_10);
            pinStates                      = (limitXSwitchCenterPrevState << 1) | sensorsInfo.limitXSwitchCenter;
            if ((pinStates & 0x3) == 0x1) {
                sensorsInfo.currentXPosition = AXE_X_MIDDLE_VALUE;
                sensorsInfo.positionXWeak    = 0;
            }
            limitXSwitchCenterPrevState = sensorsInfo.limitXSwitchCenter;

            sensorsInfo.limitYSwitchDown = HAL_GPIO_ReadPin (GPIOD, GPIO_PIN_9);
            pinStates                    = (limitYSwitchDownPrevState << 1) | sensorsInfo.limitYSwitchDown;
            if ((pinStates & 0x3) == 0x1) {
                limiterYTriggered            = 1;
                sensorsInfo.currentYPosition = 0;
                sensorsInfo.positionYWeak    = 0;
            }
            limitYSwitchDownPrevState = sensorsInfo.limitYSwitchDown;

            sensorsInfo.limitYSwitchUp = HAL_GPIO_ReadPin (GPIOD, GPIO_PIN_11);
            pinStates                  = (limitYSwitchUpPrevState << 1) | sensorsInfo.limitYSwitchUp;
            if ((pinStates & 0x3) == 0x1) {
                limiterYTriggered            = 1;
                sensorsInfo.currentYPosition = 100;
                sensorsInfo.positionYWeak    = 0;
            }
            limitYSwitchUpPrevState = sensorsInfo.limitYSwitchUp;

            sensorsInfo.limitYSwitchCenter = HAL_GPIO_ReadPin (GPIOD, GPIO_PIN_8);
            pinStates                      = (limitYSwitchCenterPrevState << 1) | sensorsInfo.limitYSwitchCenter;
            if ((pinStates & 0x3) == 0x1) {
                sensorsInfo.currentYPosition = AXE_Y_MIDDLE_VALUE;
                sensorsInfo.positionYWeak    = 0;
            }
            limitYSwitchCenterPrevState = sensorsInfo.limitYSwitchCenter;
            if (((sensorsInfo.limitXSwitchDown == 1) || (sensorsInfo.limitXSwitchUp == 1)) && (limiterXTriggered == 1)) {
                sensorsInfo.motorXStatus = MotorControl (&htim3, &motorXYTimerConfigOC, TIM_CHANNEL_1, TIM_CHANNEL_2, motorXTimerHandle, 0, 0, sensorsInfo.limitXSwitchUp, sensorsInfo.limitXSwitchDown);
            }
            if (((sensorsInfo.limitYSwitchDown == 1) || (sensorsInfo.limitYSwitchUp == 1)) && (limiterYTriggered == 1)) {
                sensorsInfo.motorYStatus = MotorControl (&htim3, &motorXYTimerConfigOC, TIM_CHANNEL_3, TIM_CHANNEL_4, motorYTimerHandle, 0, 0, sensorsInfo.limitYSwitchUp, sensorsInfo.limitYSwitchDown);
            }
            limiterXTriggered = 0;
            limiterYTriggered = 0;
            osMutexRelease (sensorsInfoMutex);
        }
    }
}

void EncoderTask (void* arg) {
	// 01 11 10 00
    const uint32_t encoderStates[4] = { 0x00, 0x01, 0x03, 0x02 };
    uint8_t step                    = 0;
    EncoderTaskArg* encoderTaskArg  = (EncoderTaskArg*)arg;
    uint32_t pinStates              = encoderTaskArg->initPinStates;
    for (uint8_t i = 0; i < 4; i++) {
        if (pinStates == encoderStates[i]) {
            step = i;
            break;
        }
    }
    while (pdTRUE) {
        float encoderValue = *encoderTaskArg->pvEncoder;
        osMessageQueueGet (encoderTaskArg->dataQueue, &pinStates, 0, osWaitForever);
		if (osMutexAcquire (sensorsInfoMutex, osWaitForever) == osOK) {
			if (encoderStates[(step + 1) % 4] == pinStates) {
				step++;
				encoderValue += 360.0 / ENCODER_X_IMP_PER_TURN;
//				printf ("Forward\n");
			} else if (encoderStates[(step - 1) % 4] == pinStates) {
				encoderValue -= 360.0 / ENCODER_X_IMP_PER_TURN;
				if (encoderValue < 0) {
					encoderValue = 360.0 + encoderValue;
				}
//				printf ("Reverse\n");
				step--;
			} else {
				printf ("Forbidden\n");
			}
			step                             = step % 4;
			*encoderTaskArg->pvEncoder       = fmodf (encoderValue, 360.0);
			*encoderTaskArg->currentPosition = 100 * (*encoderTaskArg->pvEncoder) / MAX_X_AXE_ANGLE;
			osMutexRelease (sensorsInfoMutex);
		}
        DbgLEDToggle (encoderTaskArg->dbgLed);
    }
}
