/****************************************************************************

 Module

   

   ServoService.c


 Revision

   1.0.1


 Description

   This is a template file for implementing a simple service under the

   Gen2 Events and Services Framework.


 Notes


 History

 When           Who     What/Why

 -------------- ---     --------

 01/16/12 09:58 jec      began conversion from TemplateFSM.c

****************************************************************************/

/*----------------------------- Include Files -----------------------------*/

/* include header files for this state machine as well as any machines at the

   next lower level in the hierarchy that are sub-machines to this machine

*/

//This Module

#include "Servo.h"


#include "ES_Configure.h"

#include "ES_Framework.h"

#include "PIC32_AD_Lib.h"

#include "dbprintf.h"

#include "ES_Port.h"

#include "terminal.h"

#include "PWM_PIC32.h"

#include "MasterService.h"

/*----------------------------- Module Defines ----------------------------*/

#define ONE_SEC 1000

#define SERVO_UPDATE_FREQUENCY 10

#define SERVO_UPDATE_SEC (ONE_SEC /SERVO_UPDATE_FREQUENCY)


#define MAX_PWM_VALUE 6350

#define MIN_PWM_VALUE 1350

#define NEUTRAL_PWM_VALUE ((MAX_PWM_VALUE + MIN_PWM_VALUE)/2)

#define SHIP_MOVING_RANGE (MAX_PWM_VALUE - MIN_PWM_VALUE)

#define MAX_SPEED 5000

#define ROWS_LEDMATRIX 32

#define UNIT_PWM_VALUE_STEP ((MAX_PWM_VALUE - MIN_PWM_VALUE) / (ROWS_LEDMATRIX -1))

#define Tolerance 0.2



#define INERTIA 23

/*---------------------------- Module Functions ---------------------------*/

/* prototypes for private functions for this service.They should be functions

   relevant to the behavior of this service

*/


/*---------------------------- Module Variables ---------------------------*/

// with the introduction of Gen2, we need a module level Priority variable


static uint8_t MyPriority;

static int32_t PWMValue = NEUTRAL_PWM_VALUE;

static int32_t ship_pos = SHIP_MOVING_RANGE/2;

static int16_t last_ship_pos = SHIP_MOVING_RANGE/2;

static int16_t last_vel = 0;

static int16_t crew_pos = 0;

static int16_t T_ship = 600;

static uint8_t num_crew = 8;

static int16_t alpha_ship = 0;

static int16_t direction_results = 0;


ServoState_t CurrentServoState = Init_Servo;

ServoState_t NextServoState = Init_Servo;


/*------------------------------ Module Code ------------------------------*/

 

/****************************************************************************

 Function

     InitTemplateService


 Parameters

     uint8_t : the priorty of this service


 Returns

     bool, false if error in initialization, true otherwise


 Description

     Saves away the priority, and does any

     other required initialization for this service

 Notes


 Author

     J. Edward Carryer, 01/16/12, 10:00

****************************************************************************/

bool InitServo(uint8_t Priority)

{

  ES_Event_t ThisEvent;


  MyPriority = Priority;

  /********************************************

   in here you write your initialization code

   *******************************************/

  // post the initial transition event

  ThisEvent.EventType = ES_INIT;

  ANSELAbits.ANSA0 = 1;

 

  TRISB |= 1<<12; // config pin as input

  PWMSetup_BasicConfig(1);

  PWMSetup_AssignChannelToTimer(1, _Timer2_);

  PWMSetup_SetPeriodOnTimer(25000, _Timer2_);

  PWMSetup_MapChannelToOutputPin(1, PWM_RPB15);


  if (ES_PostToService(MyPriority, ThisEvent) == true)

  {

    return true;

  }

  else

  {

    return false;

  }

}


/****************************************************************************

 Function

     PostServoService


 Parameters

     EF_Event_t ThisEvent ,the event to post to the queue


 Returns

     bool false if the Enqueue operation failed, true otherwise


 Description

     Posts an event to this state machine's queue

 Notes


 Author

     J. Edward Carryer, 10/23/11, 19:25

****************************************************************************/

bool PostServo(ES_Event_t ThisEvent)

{

  return ES_PostToService(MyPriority, ThisEvent);

}


/****************************************************************************

 Function

    RunServoService


 Parameters

   ES_Event_t : the event to process


 Returns

   ES_Event, ES_NO_EVENT if no error ES_ERROR otherwise


 Description

   add your description here

 Notes


 Author

   J. Edward Carryer, 01/15/12, 15:23

****************************************************************************/

ES_Event_t RunServo(ES_Event_t ThisEvent)

{

  ES_Event_t ReturnEvent;

  ReturnEvent.EventType = ES_NO_EVENT; // assume no errors

  ES_Event_t GameResultEvent;

  /********************************************

   in here you write your service code

   *******************************************/

  switch (CurrentServoState)

  {

    case(Init_Servo):

    {

      if(ThisEvent.EventType == GAME_START)

      {

        ship_pos = SHIP_MOVING_RANGE/2;

        last_ship_pos = SHIP_MOVING_RANGE/2;

        last_vel = 0;

        crew_pos = 0;

        T_ship = 600;

        num_crew = 8;

        alpha_ship = 0;

        // ES_Timer_InitTimer(SERVO_TIMER, SERVO_UPDATE_SEC);

        ES_Timer_InitTimer(SHIP_MOVE, SERVO_UPDATE_SEC);

        PWMValue = NEUTRAL_PWM_VALUE;

        PWMOperate_SetPulseWidthOnChannel(PWMValue, 1);

        NextServoState = Running_Servo;

      }

    }

    break;


    case(Running_Servo):

    {        

      if(ThisEvent.EventType == NEW_CREW_POSITION)

      {

        crew_pos = -(ThisEvent.EventParam)*UNIT_PWM_VALUE_STEP+16*UNIT_PWM_VALUE_STEP;

      }


      if(ThisEvent.EventType == ES_TIMEOUT)

      {

        // DB_printf("%d\n", PWMValue);

        // if(ThisEvent.EventParam == SERVO_TIMER){

        //   PWMOperate_SetPulseWidthOnChannel(PWMValue, 1);

        //   ES_Timer_InitTimer(SERVO_TIMER, SERVO_UPDATE_SEC);

        // }


        if(ThisEvent.EventParam == SHIP_MOVE)

        {        

          last_vel = SERVO_UPDATE_FREQUENCY * (ship_pos-last_ship_pos);

          last_ship_pos = ship_pos;

          alpha_ship = (num_crew*(T_ship-crew_pos))/ INERTIA;

          ship_pos = last_ship_pos + last_vel/ SERVO_UPDATE_FREQUENCY + (0.5*alpha_ship) /SERVO_UPDATE_FREQUENCY /SERVO_UPDATE_FREQUENCY ;

   

          if(ship_pos > SHIP_MOVING_RANGE)

          {

              ship_pos = SHIP_MOVING_RANGE;

          }

          if(ship_pos < 0)

          {

              ship_pos = 0;

          }


          PWMValue = ship_pos+MIN_PWM_VALUE;


          if(PWMValue < MAX_PWM_VALUE && PWMValue > MIN_PWM_VALUE)

          {

            PWMOperate_SetPulseWidthOnChannel(PWMValue, 1);

          }

          ES_Timer_InitTimer(SHIP_MOVE, SERVO_UPDATE_SEC);

        }

      }


      // Lose the game

      if(ship_pos > SHIP_MOVING_RANGE * (1-Tolerance) || ship_pos < SHIP_MOVING_RANGE * Tolerance)

      {

        GameResultEvent.EventType = LOSE;

        ES_PostAll(GameResultEvent);

        NextServoState = Completion_Lose;

      }


      if(ThisEvent.EventType == WIN)

      {

        NextServoState = Completion_Win;

        ES_Timer_InitTimer(SHIP_MOVE, SERVO_UPDATE_SEC);

      }


      if(ThisEvent.EventType == USER_LEFT)

      {

        NextServoState = Completion_User_Left;

        ES_Timer_InitTimer(SHIP_MOVE, SERVO_UPDATE_SEC);

      }  

    }

    break;


    case(Completion_Win):

    {

      if(ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == SHIP_MOVE)

      {

        if (ship_pos < SHIP_MOVING_RANGE*(1- 2*Tolerance) && direction_results == 1)

        {

          ship_pos += UNIT_PWM_VALUE_STEP;

        }

        else if ( ship_pos > SHIP_MOVING_RANGE*(1- 2*Tolerance))

        {

          direction_results = 0;

          ship_pos = SHIP_MOVING_RANGE*(1- 2*Tolerance);

        }

        else if (ship_pos > SHIP_MOVING_RANGE*2*Tolerance && direction_results == 0)

        {

          ship_pos -= UNIT_PWM_VALUE_STEP;

        }

        else if(ship_pos < SHIP_MOVING_RANGE*2*Tolerance)

        {

          direction_results = 1;

          ship_pos = SHIP_MOVING_RANGE*2*Tolerance;

        }

       

        PWMValue = ship_pos+MIN_PWM_VALUE;

        PWMOperate_SetPulseWidthOnChannel(PWMValue, 1);

        ES_Timer_InitTimer(SHIP_MOVE, SERVO_UPDATE_SEC);

        NextServoState = Completion_Win;

      }


      if(ThisEvent.EventType == GAMERESULT_OVER)

      {

        PWMValue = NEUTRAL_PWM_VALUE;

        PWMOperate_SetPulseWidthOnChannel(PWMValue, 1);

        NextServoState = Init_Servo;

      }

    }

    break;


    case(Completion_Lose):

    {

      if(ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == SHIP_MOVE)

      {

        if (ship_pos < SHIP_MOVING_RANGE && direction_results == 1)

        {

          ship_pos += UNIT_PWM_VALUE_STEP;

        }

        else if ( ship_pos > SHIP_MOVING_RANGE)

        {

          direction_results = 0;

          ship_pos = SHIP_MOVING_RANGE;

        }

        else if (ship_pos > 0 && direction_results == 0)

        {

          ship_pos -= UNIT_PWM_VALUE_STEP;

        }

        else if(ship_pos < 0)

        {

          direction_results = 1;

          ship_pos = 0;

        }

       

        PWMValue = ship_pos+MIN_PWM_VALUE;

        PWMOperate_SetPulseWidthOnChannel(PWMValue, 1);

        ES_Timer_InitTimer(SHIP_MOVE, SERVO_UPDATE_SEC/4);

        NextServoState = Completion_Lose;

      }


      if(ThisEvent.EventType == GAMERESULT_OVER)

      {

        PWMValue = NEUTRAL_PWM_VALUE;

        PWMOperate_SetPulseWidthOnChannel(PWMValue, 1);

        NextServoState = Init_Servo;

      }

    }

    break;


    case(Completion_User_Left):

    {

      if(ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == SHIP_MOVE)

      {

        PWMValue = NEUTRAL_PWM_VALUE;

        PWMOperate_SetPulseWidthOnChannel(PWMValue, 1);

        NextServoState = Init_Servo;

      }


      if(ThisEvent.EventType == GAMERESULT_OVER)

      {

        PWMValue = NEUTRAL_PWM_VALUE;

        PWMOperate_SetPulseWidthOnChannel(PWMValue, 1);

        NextServoState = Init_Servo;

      }

    }

    break;


    default:

    break;

  }

  CurrentServoState = NextServoState;


  return ReturnEvent;

}


/***************************************************************************

 private functions

 ***************************************************************************/


/*------------------------------- Footnotes -------------------------------*/

/*------------------------------ End of file ------------------------------*/