/*
 * Modbus.c
 *
 *  Created on: 8. 3. 2020
 *      Author: Ondrej Vitoslavsky
 */

#include "Modbus.h"
#include "Modbus_RTU.h"
#include "Modbus_CRC.h"
#include "Modbus_functions.h"
#include "Modbus_registers.h"
#include "Modbus_periph.h"

uint16_t Modbus_Address;
volatile ModbusEvent Event;

extern volatile uint16_t	holding_register_Buffer[HOLDING_REGISTERS];
extern volatile uint8_t		Receive_Buffer[MODBUS_ADU_SIZE_MAX];
extern volatile uint16_t	Receive_Buffer_Pos;

extern uint8_t  Transmit_Buffer[MODBUS_ADU_SIZE_MAX];
extern volatile uint16_t Transmit_Buffer_Count;
extern volatile uint16_t Transmit_Buffer_Pos;

void Modbus_Init( uint8_t SlaveAddress)
{
	uint16_t        Timer_35_us=35; //1750us
	uint16_t        Timer_15_us=15;	//750us

	for(uint16_t i=0;i<HOLDING_REGISTERS;i++){
		holding_register_Buffer[i]=0;
	}
	holding_register_Buffer[SW_VERSION_OFFSET]=206;
	holding_register_Buffer[HW_VERSION_OFFSET]=206;
	holding_register_Buffer[BOM_OFFSET]=0xFEFF;

	__disable_irq();

	//save Slave address
	Modbus_Address = SlaveAddress;

    //Modbus RTU uses 8 Databits + stopbit => 9bits.
    Modbus_Serial_Init(115200, UART_WORDLENGTH_9B, UART_PARITY_EVEN);
	Modbus_Timers_Init(Timer_35_us, Timer_15_us);

	Event=EVENT_NONE;
    Modbus_RTU_Start();
    __enable_irq();

}



void Modbus_Poll( void )
{
	uint8_t   *Frame;
    uint8_t    Address;
    uint8_t    FunctionCode;
    uint16_t   Length;
    uint16_t        crc;
    ModbusException Exception;



    //Check for received event
    if(Event==EVENT_FRAME_RECEIVED)
    {
    	//check CRC
    	if(Modbus_CRC16((uint8_t*)Receive_Buffer, Receive_Buffer_Pos) == 0){

    		Address = Receive_Buffer[MODBUS_ADU_ADDR_OFF];
    		Frame = (uint8_t*)&Receive_Buffer[MODBUS_ADU_PDU_OFF];
    		FunctionCode = Frame[MODBUS_PDU_FUNC_OFF];
    		/*whole frame contains address, Modbus frame and CRC, therefore length
    		 of Modbus frame is length of whole frame minus CRC and address*/
    		Length = Receive_Buffer_Pos - MODBUS_ADU_PDU_OFF - MODBUS_ADU_SIZE_CRC;

			//execute function if this frame is addressed for this slave device
			if((Address==Modbus_Address)||(Address==MODBUS_ADDRESS_BROADCAST))
			{
					switch(FunctionCode){
					case MODBUS_FUNC_READ_HOLDING_REGISTER:
						Exception = Modbus_Function_Read_Holding_Register(Frame, &Length);
						break;
					case MODBUS_FUNC_WRITE_REGISTER:
						Exception = Modbus_Function_Write_Holding_Register(Frame, &Length);
						break;
					case MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS:
						Exception = Modbus_Function_Write_Multiple_Holding_Register(Frame, &Length);
						break;
					case MODBUS_FUNC_READWRITE_MULTIPLE_REGISTERS:
						Exception = Modbus_Function_Read_Write_Multiple_Holding_Register(Frame, &Length);
						break;
					default:
						Exception = EXCEPTION_ILLEGAL_FUNCTION;
						break;
					}

					//return a reply if the request was sent to this slave address.
					if(Address == Modbus_Address)
					{
						if(Exception != EXCEPTION_NONE)
						{
							//An exception occured. Build an error frame.
							//final ADU is address,function|128,exception,CRC
							Length = 1;
							Transmit_Buffer[Length++]=(uint8_t)(FunctionCode|MODBUS_FUNC_ERROR);
							Transmit_Buffer[Length++] = Exception;

						}

						crc = Modbus_CRC16(&Transmit_Buffer, Length);
						Transmit_Buffer[Length++] = (uint8_t)(crc & 0xFF);
						Transmit_Buffer[Length++] = (uint8_t)(crc >> 8);
						Transmit_Buffer_Pos=0;
						Transmit_Buffer_Count=Length;

						//Activate transmitter.
						Modbus_Serial_Enable(0, 1);
					}
			}
    	}
    	Event=EVENT_NONE;
    }
	if(Event==EVENT_FRAME_SENT){
		Event=EVENT_NONE;
	}
}


void Settings_Update(Settings_struct *Settings){
	//Scale and offset
	Settings->INP_SC=ModBus_Uint_to_Real(&holding_register_Buffer[INP_SC_OFFSET]);
	Settings->INP_OF=ModBus_Uint_to_Real(&holding_register_Buffer[INP_OF_OFFSET]);
	Settings->RDG_SC=ModBus_Uint_to_Real(&holding_register_Buffer[RDG_SC_OFFSET]);
	Settings->RDG_OF=ModBus_Uint_to_Real(&holding_register_Buffer[RDG_OF_OFFSET]);


	//DA settings
	Settings->AO0_Type=(holding_register_Buffer[AO_TYPE_OFFSET]>>8)&(0xFF);
	Settings->AO1_Type=holding_register_Buffer[AO_TYPE_OFFSET]&(0xFF);
	Settings->AO0_R=ModBus_Uint_to_Real(&holding_register_Buffer[AO0_RANGE_OFFSET]);
	Settings->AO0_Nom=ModBus_Uint_to_Real(&holding_register_Buffer[AO0_NOMINAL_OFFSET]);
	Settings->AO1_R=ModBus_Uint_to_Real(&holding_register_Buffer[AO1_RANGE_OFFSET]);
	Settings->AO1_Nom=ModBus_Uint_to_Real(&holding_register_Buffer[AO1_NOMINAL_OFFSET]);
	Settings->T_Limitation=ModBus_Uint_to_Real(&holding_register_Buffer[T_LIMITATION_OFFSET]);

	//AD Settings
	Settings->AD_Rate=holding_register_Buffer[AD_RATE_OFFSET];

	//Data in
	Settings->BOM=holding_register_Buffer[BOM_OFFSET];
	Settings->Command0=(holding_register_Buffer[COMMAND_OFFSET]>>8)&(0xFF);
	Settings->Command1=holding_register_Buffer[COMMAND_OFFSET]&(0xFF);
	Settings->Simulate_Nom=ModBus_Uint_to_Real(&holding_register_Buffer[SIMULATE_NOMINAL]);
	Settings->Simulate_T=ModBus_Uint_to_Real(&holding_register_Buffer[SIMULATE_THICKNESS]);



}


void Settings_Service(Settings_struct *Settings){
	Settings->INP_SC=1.0;
	Settings->INP_OF=0.0;
	Settings->RDG_SC=1.0;
	Settings->RDG_OF=0.0;

	//DA settings
	Settings->AO0_Type=Deviation_Bipolar_20mA;
	Settings->AO1_Type=Deviation_Bipolar_20mA;
	Settings->AO0_R=5.0;
	Settings->AO0_Nom=4.75;
	Settings->AO1_R=5.;
	Settings->AO1_Nom=4.75;
	Settings->T_Limitation=20.0;

	//AD Settings
	Settings->AD_Rate=200;

	//Data in
	Settings->Command0=(1<<ActivateMeasurementMode);
	Settings->Command1=0;
	Settings->Simulate_Nom=0;
	Settings->Simulate_T=0;
}


void ModBus_Real_to_Uint(float number,volatile uint16_t *store){
	uint16_t* pointer;
	pointer = (uint16_t*)&number;
	store[1] = *pointer;
	store[0] = *(pointer+1);
}


float ModBus_Uint_to_Real(volatile uint16_t *store){
	uint16_t pom=(store[0]<<16)|(store[1]);
	float *pointer=(float*)&pom;
	float number;
	number=*pointer;
	return number;
}

float Calculate_Thickness(float Voltage, Settings_struct *Settings){
	float T1,T;
	T1 = (Voltage + Settings->INP_OF)*(Settings->INP_SC);
	ModBus_Real_to_Uint(T1,&holding_register_Buffer[TI_OFFSET]);


	T = T1*(Settings->RDG_SC) + Settings->RDG_OF;
	if(T>Settings->T_Limitation)T=Settings->T_Limitation;
	ModBus_Real_to_Uint(T,&holding_register_Buffer[T_OFFSET]);

	return T;
}
