/*
FILENAME...     omsBaseAxis.cpp
USAGE...        Pro-Dex OMS asyn motor base axes support

Version:        $Revision$
Modified By:    $Author$
Last Modified:  $Date$
HeadURL:        $URL$
*/


/*
 *  Created on: 6/2012
 *      Author: eden
 *
 */

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

#include "omsBaseAxis.h"
#include "omsBaseController.h"

static const char *driverName = "omsBaseAxisDriver";

omsBaseAxis::omsBaseAxis(omsBaseController *pController, int axis, char axisChar)
    : asynMotorAxis(pController, axis), axisChar(axisChar)
{
    pC_ = pController;
    stepper = 1;
    invertLimit = 0;
    lastminvelo = 0;
}

asynStatus omsBaseAxis::move(double position, int relative, double min_velocity, double max_velocity, double acceleration)
{
//    omsBaseAxis *pAxis = this->getAxis(pasynUser);
    static const char *functionName = "moveAxis";

    asynStatus status = asynError;
    epicsInt32 minvelo, velo, acc, rela, pos;
    char *relabs[2] = {(char *) "MA", (char *) "MR"};
    char buff[100];

    if (relative)
        rela = 1;
    else
        rela = 0;

    if ( position < 0.0)
    	pos = (epicsInt32) (position - 0.5);
    else
    	pos = (epicsInt32) (position + 0.5);

    if (abs(pos) > 67000000){
        asynPrint(pasynUser_, ASYN_TRACE_ERROR,
              "%s:%s:%s axis %d position out of range %f\n",
              driverName, functionName, pC_->portName, axisNo_, position);
        return status;
    }

    velo = (epicsInt32) (max_velocity + 0.5);
    if (velo < 1) velo = 1;
    else if (velo > 4000000) velo = 4000000;

    minvelo = (epicsInt32) (min_velocity + 0.5);
    if (minvelo < 0) minvelo = 0;
    else if (minvelo >= velo) minvelo = velo - 1;

    acc = abs((epicsInt32) acceleration);
    if (acc > 8000000)
        acc = 8000000;
    else if (acc < 1)
        acc = 1;

    /* move to the specified position */
    if (velo < lastminvelo)
        sprintf(buff, "A%1c;AC%d;VB%d;VL%d;%s%d;GO;ID;", axisChar, acc, minvelo, velo, relabs[rela], pos);
    else
        sprintf(buff, "A%1c;AC%d;VL%d;VB%d;%s%d;GO;ID;", axisChar, acc, velo, minvelo, relabs[rela], pos);
    lastminvelo = minvelo;

    status = pC_->sendOnlyLock(buff);

    asynPrint(pasynUser_, ASYN_TRACE_FLOW,
        "%s:%s: Set driver %s, axis %d move to %f, min vel=%f, max_vel=%f, accel=%f",
        driverName, functionName, pC_->portName, axisNo_, position, min_velocity, max_velocity, acceleration );

    return status;
}

asynStatus omsBaseAxis::home(double min_velocity, double max_velocity, double acceleration, int forwards )
{
    static const char *functionName = "homeAxis";

    asynStatus status = asynError;
    char buff[60];
    char *direction[2] = {(char*) "HR", (char*) "HM"};
    epicsInt32 minvelo, velo, acc, fw = 0;

    if (forwards) fw = 1;

    velo = (epicsInt32) max_velocity;
    if (velo < 1) velo = 1;
    else if (velo > 1000000) velo = 1000000;

    minvelo = (epicsInt32) (min_velocity + 0.5);
    if (minvelo < 0) minvelo = 0;
    else if (minvelo >= velo) minvelo = velo - 1;

     acc = abs((epicsInt32) acceleration);
    if (acc > 8000000)
        acc = 8000000;
    else if (acc < 1)
        acc = 1;

    /* do a home run and move to the home position */
    if (velo < lastminvelo)
        sprintf(buff, "A%1c;AC%d;VB%d;VL%d;%s;MA0;GO;ID;", axisChar, acc, minvelo, velo, direction[forwards]);
    else
        sprintf(buff, "A%1c;AC%d;VL%d;VB%d;%s;MA0;GO;ID;", axisChar, acc, velo, minvelo, direction[forwards]);
    lastminvelo = minvelo;

    status = pC_->sendOnlyLock(buff);

    homing = 1;

    asynPrint(pasynUser_, ASYN_TRACE_FLOW,
        "%s:%s: Set driver %s, axis %d to home %s, min vel=%f, max_vel=%f, accel=%f",
        driverName, functionName, pC_->portName, axisNo_, (forwards?"FORWARDS":"REVERSE"), min_velocity, max_velocity, acceleration );

    return status;
}

asynStatus omsBaseAxis::doMoveToHome()
{
    static const char *functionName = "doMoveToHome";
    asynPrint(pasynUser_, ASYN_TRACE_ERROR,
        "%s:%s: This function is not yet implemented for axis %d\n",
        driverName, functionName, axisNo_);
    return asynError;
}

asynStatus omsBaseAxis::moveVelocity(double minVelocity, double maxVelocity, double acceleration )
{
    asynStatus status = asynError;
    static const char *functionName = "moveVelocityAxis";

    char buff[100];
    epicsInt32 velo, acc;

    acc = (epicsInt32) acceleration;
    if (acc < 1) acc = 1;
    else if (acc > 8000000) acc = 8000000;

    velo = (epicsInt32) maxVelocity;
    if (velo > 4000000) velo = 4000000;
    else if (velo < -4000000) velo = -4000000;

    sprintf(buff, "A%1c AC%d; JG%d;", axisChar, acc, velo);
    status = pC_->sendOnlyLock(buff);

    asynPrint(pasynUser_, ASYN_TRACE_FLOW,
        "%s:%s: Set port %s, axis %d move with velocity of %f, acceleration=%f",
        driverName, functionName, pC_->portName, axisNo_, maxVelocity, acceleration );

    /* Send a signal to the poller task which will make it do a poll, and switch to the moving poll rate */
    return status;
}

asynStatus omsBaseAxis::stop(double acceleration )
{
    asynStatus status = asynError;
    static const char *functionName = "stopAxis";
    int acc;
    char buff[50];

    acc = (int)(fabs(acceleration)+0.5);
    if (acc > 8000000) acc=8000000;
    if (acc < 1) acc = 200000;

    sprintf(buff, "A%1c AC%d; ST ID;", axisChar, acc);
    status = pC_->sendOnlyLock(buff);

    asynPrint(pasynUser_, ASYN_TRACE_FLOW,
        "%s:%s: port %s, set axis %d to stop with accel=%f\n",
        driverName, functionName, pC_->portName, axisNo_, acceleration );

    return status;
}

/** Set the current position of the motor.
 * \param[in] position The new absolute motor position that should be set in the hardware. Units=steps.
 */
asynStatus omsBaseAxis::setPosition(double position)
{
    static const char *functionName = "setPosition";
    asynStatus status = asynError;
    char buff[20];
    asynPrint(pasynUser_, ASYN_TRACE_FLOW,
              "%s:%s:%s axis %d set position to %f\n",
              driverName, functionName, pC_->portName, axisNo_, position);
    sprintf(buff,"A%1c LP%d;", axisChar, (int)(position));
    status = pC_->sendOnlyLock(buff);

  return status;
}

/** we need to implement this, because we need to use the motorUpdateStatus_ function
 * in asynMotorController, because we cannot access statusChanged_ (shouldn't be private)
 * ignore moving flag, since we have our own poller.
 */
asynStatus omsBaseAxis::poll(bool *moving)
{
    epicsEventSignal(pC_->pollEventId_);
    return asynSuccess;
}
