// MotorBase.h: interface for the CMotorBase class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_MOTORBASE_H__04B8BC9A_5FF9_4E00_BA08_FDEC856015F1__INCLUDED_)
#define AFX_MOTORBASE_H__04B8BC9A_5FF9_4E00_BA08_FDEC856015F1__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "Motor.h"
#include <map>

#ifdef  BUILD_IODLL
#define     IODLLCLASS  __declspec(dllexport)
#else
#define     IODLLCLASS  __declspec(dllimport)
#endif

class CMotionControllerBase;

class IODLLCLASS CMotionLogger
{
public:
    CMotionLogger(const char *formatString, ...);
    CMotionLogger(const char *loggerName);
    ~CMotionLogger();
    void Log(const char *formatString, ...);
    void Log(const char *filename, int linenr, const char *formatString, ...);
private:
    CHighResPerformanceCounter m_time;
    CString                    m_loggerName;
};

class IODLLCLASS CMotorBase : public CMtrPos, public CCard, public CMtrOnOff, public CMyWait
{
protected:
    enum HOME_SEQ
    {
        HOME_NORMAL,
        HOME_SENSOR_BLOCKED,
        HOME_LIMIT_BLOCKED,
    };

    enum HOME_DIR
    {
        HOME_FWD,
        HOME_REV,
    };

    enum MOTION_SPEED
    {
        SPEED_LOW,
        SPEED_HIGH,
    };

    enum LEAVE_HOME_CHECK_SENSOR
    {
        NO_CHECK,
        CHECK_HOME,
        CHECK_INDEX,
        CHECK_POS_LIMIT,
        CHECK_NEG_LIMIT,
    };

    enum PROFILE_CURVE
    {
        T_CURVE,
        S_CURVE,
    };

    // these profile parameters are in motor pulses/values instead of real world values
    struct motion_profile
    {
        enum PROFILE_TYPE
        {
            ACC,
            DEC,
            SACC,
            SDEC,
            START_SPEED,
            MAX_SPEED,
            CONSTANT_SPEED,
            HOME_SLOW_ACC,
            HOME_SLOW_DEC,
            HOME_SLOW_SACC,
            HOME_SLOW_SDEC,
            HOME_SLOW_START_SPEED,
            HOME_SLOW_MAX_SPEED,
            HOME_SLOW_CONSTANT_SPEED,
            POSITIVE_SOFT_LIMIT,
            NEGATIVE_SOFT_LIMIT,
            MAX_PROFILE_TPYE,
        };

        protected:
            std::map <PROFILE_TYPE, double> profile;

        public:
            motion_profile()
            {
                if (profile.empty())
                {
                    for (int i = 0; i < MAX_PROFILE_TPYE; i++)
                    {
                        profile[static_cast<PROFILE_TYPE>(i)] = 0;
                    }
                }
            }

            double Get(PROFILE_TYPE type)
            {
                return (profile.find(type) != profile.end()) ? profile[type] : 0;
            }

            void Set(PROFILE_TYPE type, double dValue)
            {
                profile[type] = dValue;
            }
    } m_profile;

    struct multiple_trigger
    {
        double dStartPosition;
        double dStopPosition;
        double dIntervalDistance;
        int nNoOfTrigger;
        int nCmpSource;

        multiple_trigger()
        {
            dStartPosition    = 0;
            dStopPosition     = 0;
            dIntervalDistance = 0;
            nNoOfTrigger      = 0;
            nCmpSource        = 0;
        }
    };

public:
    CMotorBase();
    CMotorBase(const char* cpMutexName, const CMtrProfile &mtrProfile, double *dpPos, CMotionControllerBase *pBaseController);
    virtual ~CMotorBase();

// **************************************************************
// Virtual Methods declaration
// **************************************************************

// method to close the axis
    virtual void CloseAxis();

// method to initialize and configure the axis
    virtual void ConstructAxis() = 0;

    void StartThreads();

    void KillThreads();

    HANDLE GetKillEventHandle()
    {
        return m_hKillThread;
    }

    virtual void MotorMoveIntThread();

    virtual void MotorHomeThread();

//  method to On/Off motor
    int          OnMtr(bool bOn);
    virtual bool OnMotor()  = 0;
    virtual bool OffMotor() = 0;

    virtual bool IsBenchDebug();

//  method to stop motor with decceleration
    int          DecStop();
    virtual bool DecelStop() = 0;

//  method to trigger Emergency stop the motor  virtual int EStop();
    int          EStop();
    virtual bool EmergStop() = 0;

// virtual method to get commanded position
    double       GetCommandPosition();
    virtual bool GetMotorCommandedPosition(int nMtrNo, double *pRaw) = 0;

// virtual method to get the motor axis IO status
    virtual int GetMotionIOStatus();

// virtual method to get the Motor Ready Status
    virtual BOOL IsMotorReady();

// virtual method to get the Motor Alarm Status
    virtual BOOL IsMotorAlarm();

// virtual method to get the Motor Negative/CCW Limit Status
    virtual BOOL IsNegLmtHit();

// virtual method to get the Motor Positive/CW Limit Status
    virtual BOOL IsPosLmtHit();

// virtual method to get the Motor Home Sensor Status
    virtual BOOL IsHomed();

// virtual method to check if the Motor is TURN ON
    virtual bool IsMtrOn();

// virtual method Check other axis home sensor
    virtual BOOL IsHomed(const CMtrProfile &mtrProfile);

// virtual method to get other motor axis IO status
    virtual int GetMotionIOStatus(const CMtrProfile &mtrProfile) = 0;

// virtual method to get Other axis motor ready status
    virtual BOOL IsMotorReady(const CMtrProfile &mtrProfile);

// virtual method to get Other axis Alarm status
    virtual BOOL IsMotorAlarm(const CMtrProfile &mtrProfile);

// virtual method to get Other axis negative/ccw limit status
    virtual BOOL IsNegLmtHit(const CMtrProfile &mtrProfile);

// virtual method to get Other axis positive/cw limit status
    virtual BOOL IsPosLmtHit(const CMtrProfile &mtrProfile);

// virtual method to get Other axis home sensor status
    virtual bool IsMtrOn(const CMtrProfile &mtrProfile);

// virtual method to get other motor axis current position
    double       GetOtherAxisPosition(const CMtrProfile &mtrProfile);
    virtual bool GetMotorPosition(const CMtrProfile &mtrProfile, double *pRawPosition) = 0;

// virtual method to get motor axis current position error
    double       GetPosErr();
    virtual bool GetMotorPositionError(double *pRawPositionError) = 0;

// virtual method to get the current motor position
    double GetPosition();

// virtual method to do homing by limit sensor
    virtual BOOL HomingByLimit();

// virtual method to do homing by sensor as well as index
    virtual BOOL HomeBySensorAndIndex();

// virtual method to do homing by index signal
    virtual BOOL HomeByIndex();

// virtual method to do homing by Home Sensor
    virtual BOOL HomeBySensor();

// virtual method to move absolute
    int          MoveAbs(double dPosition, int nPos = -1, int nMode = 0);
    virtual bool AbsoluteMove(double dPosition, int nMode, PROFILE_CURVE curve) = 0;

// virtual method to move continuously
    virtual int MoveContinuously(bool bCW = true);

// virtual method to move relative distance
    int          MoveRel(double dDistance, int nPos = -1);
    virtual bool RelativeMove(double dDistance, PROFILE_CURVE curve) = 0;

// virtual method to process interrupt to decode what happen to the motor
    virtual void ProcessInt(int nIntCode = 0) = 0;

// virtual method to reset single trigger
    virtual int ResetSingleTrigger();

// virtual method to set the commanded position register
    int          SetCommandPosition(double dPosition);
    virtual bool SetMotorCommandedPosition(double dPosition) = 0;

// virtual method to set the mechanical coversion factor
    int          SetConversionFactor(double dUPR, double dPPR, double dEPPR);
    virtual bool RefreshMotorConversionFactor() = 0;

// virtual method to set homing modes
    virtual int SetHomeMode(int iHomeMode) = 0;

// virtual method to set multiple trigger
    virtual int  SetMultipleTrigger(double dStartPosition, double dStopPosition, double dIntervalDistance, int nNoOfTrigger, int nCmpSource = 1);
    virtual bool SetMotorMultipleTrigger(const multiple_trigger &trigger);

// virtual method to force set the current position
    int          SetPosition(double dPosition);
    virtual bool SetMotorPosition(double dPosition) = 0;

// virtual method to arm controller axis for single trigger
    virtual int SetSingleTrigger(double dPosition,  int nCmpSource = 1, int nPulseStretch = STRETCH_NON);

// virtual method to arm multiaxis for simultaneous motion
    virtual int SimultaneousMove(int nNoOfAxis, CMtrProfile *mtrProfile,  double *dPosition, double *dStartVel, double *dMaxVel, double *dAcc, int nMode = 0);

// virtual method to move absolute with s-curve profile
    int SMoveAbs(double dPosition, int nPos = -1, int nMode = 0);

// virtual method to move motor in s-curve continuously
    virtual int SMoveContinuously(bool bCW = true);

// virtual method to move relative distance with s-curve profile
    int SMoveRel(double dDistance, int nPos = -1);

// return: success = MOTION_END; error = MOTION_ERROR; Force Stop = MOTION_FORCE_STOP
    virtual DWORD WaitHomeDone();

// wait for motion stop based on pulse send out or in-position signal
// call this function after calling a move operation
// return: success = MOTION_END; error = MOTION_ERROR; Force Stop = MOTION_FORCE_STOP
// positive limit = MOTION_LIMIT_POS_HIT; negative limit = MOTION_LIMIT_NEG_HIT
    virtual DWORD WaitMotionDone(DWORD dwTimeout = 1000);

// virtual method to get the encoder index signal
    virtual BOOL IsIndex();

// method to get other axis encoder index signal
    virtual BOOL IsIndex(const CMtrProfile &mtrProfile);

// **************************************************************
// virtual Methods declaration applicable to ACS ONLY - STARTS
// **************************************************************
    virtual int SegmentedMotion(int nAxis, const CMtrProfile *pMtrProfile, double *dPositions);
    virtual int SegmentedLine(double *dPositions);
    virtual int SegmentedEnd();
    virtual int SegmentedArc(double *dCenter, double *dFinalPoint, int rotation);
    virtual int MasterSlaveEnable(const CMtrProfile slaveMtrProfile, double offset, double leftBoundary, double rightBoundary);
    virtual int MasterSlaveDisable(const CMtrProfile slaveMtrProfile);
// **************************************************************
// virtual Methods declaration applicable to ACS ONLY - ENDS
// **************************************************************

// **************************************************************
// virtual Methods declaration applicable to GALIL ONLY - STARTS
// **************************************************************
// virtual method to get motion stop code
    virtual int GetStopCode();

// virtual method to get motion stop code
    virtual CString GetVariable(CString csVariableName);

// virtual method to send direct command string to controller
    virtual int SendCommand(CString csCommand);

// virtual method to stop any running script program and stop all motion
    virtual int StopProgramAndMotion();

// virtual method to update motion profile
    virtual int UpdateProfile();
// **************************************************************
// virtual Methods declaration applicable to GALIL ONLY - ENDS
// **************************************************************

// **************************************************************
// Methods declaration
// **************************************************************

// method to get the real world distance in pulse
    double ConvertToPulse(double dDistance);

// method to check if Axis will check for interrupt
    bool IsEnableIntCheck();

// method to check disable interrupt
    void DisableIntCheck();

// method to check enable interrupt
    void EnableIntCheck();

// method to enable/disable auto reset motor position during home operation
    void EnableResetPositionDuringHoming(bool bResetPositionDuringHoming)
    {
        m_bResetPositionDuringHoming = bResetPositionDuringHoming;
    }

// method to get the event for homimg complete
    HANDLE GetHomeMotionDoneTrigger();

// method to get the event for motion complete
    HANDLE GetMotionDoneTrigger();

// method to get the negative software limit in mm
    double GetNegativeSoftLimit();

// method to get the positive software limit in mm
    double GetPositiveSoftLimit();

// method to set positive software limit
    void SetPositiveSoftLimit(double dPosSoftLimit);

// method to set negative software limit
    void SetNegativeSoftLimit(double dPosSoftLimit);

//  method to home motor, normal or use limit sensor as home.
//  Whether to reset position tracking
    int Home(DWORD dwTimeout, bool bResetPositionTrack = true, bool bByLimit = false);

// method to do homing, by Sensor or by Index mode
    BOOL Homing();

// method to move motor based on position array
    int Move(int iPos, int nMode = 0);

// method to reset necessary motion events before any motion
    void ResetRequiredMotionEvents();

    virtual void SetMotorProfile(double dValue, motion_profile::PROFILE_TYPE type) = 0;

// method to set normal, slow acceleration
    int SetAcce(double dAcce, bool bHomeSlow = false);

// method to set normal, slow deceleration
    int SetDec(double dDec, bool bHomeSlow = false);

// method to set normal acceleration
    int SetSAcce(double dSAcce);

// method to set normal deceleration
    int SetSDec(double dSDec);

// method to set constant speed
    int SetConst(double dConst);

// method to set normal, slow start up speed
    int SetStartUp(double dStartUp, bool bHomeSlow = false);

// method to set normal, slow max speed
    int SetCruise(double dCruise, bool bHomeSlow = false);

// method to set virtual home position
    int SetVirtualHomePosition(double dVirtualHomePosition);

// method to get virtual home position
    double GetVirtualHomePosition();

//  method to move motor based on position array with s-curve profile
    int SMove(int iPos, int nMode = 0);

// method to update the current motor position number for tracking purpose
    void UpdatePosition(int nPos);

// method to update auto position number tracking after any motion done
// call to manually update the motor position tracking if never use wait motion done command
// normally by calling wait motion done, position tracking is automatically updated.
    void UpdatePosition();

// Gary 21Mar11, V1.28.01
// This method is used to reset alarm via MotionNet
    virtual int ResetAlarm(bool bReset);

// These methods only applies to ETEL Gantry Mode
// Gary 8Apr11, V1.29.00
    virtual int GantryEnable(int /*nNoOfAxis*/, CMtrProfile*  /*mtrProfile*/)
    {
        return MOTION_ERROR;
    }

    virtual int GantryDisable(int /*nNoOfAxis*/, CMtrProfile*  /*mtrProfile*/)
    {
        return MOTION_ERROR;
    }

// Method to force a compare output pulse
// Gary 2Jul12, V1.29.07
    virtual int TriggerCompareOutput()
    {
        return MOTION_ERROR;
    }

    BOOL TestIOStatus(const CMtrProfile &mtrProfile, unsigned short bitmask);

// **************************************************************
// variables declaration
// **************************************************************

// event to trigger axis interrupt
    HANDLE m_hAxisInt;

// event to start normal home operation
    HANDLE m_hStartHome;

// event to start home by limit operation
    HANDLE m_hStartHomeByLimit;

// event to inform motion is completed
    HANDLE m_hMotionDone;

// event to inform homing is completed
    HANDLE m_hHomeMotionDone;

// event to inform motion has stopped
    HANDLE m_hStop;

// event to inform motor trigger positive/cw limit
    HANDLE m_hPosLmtHit;

// event to inform motor trigger negative/cw limit
    HANDLE m_hNegLmtHit;

// Gary 24Feb11, V1.28.01, new positive limit handle for MotionNet Only
// event to inform motor trigger positive/cw limit
    HANDLE m_hMotionNetPosLmtHit;

// Gary 24Feb11, V1.28.01, new negative limit handle for MotionNet Only
// event to inform motor trigger negative/cw limit
    HANDLE m_hMotionNetNegLmtHit;

// mutex for interlocking
    CMutex m_mutexMotion;

// flag to indicate if axis should respond to any interrupts
    bool m_bEnableIntCheck;

// variable to keep track of original Home Sensor Logic
    int m_nOrgLogic;

// flag to indicate whether to reset position during homing
    bool m_bResetPositionDuringHoming;

// thread to monitor axis motion
    CWinThread *m_pThread;

// keep track of which motor no
    int m_iMtrNo;

// keep track of no of pos
    int m_iNoOfPos;

// pointer to position array
    double *m_dpPos;

// motor profile
    const CMtrProfile m_MtrProfile;

// variable to track what's the current position number of the axis
    int m_nCurrentPosNo;

    struct signal_mask
    {
        // home signal bit mask
        unsigned short m_usHomeBitMask;

        // index signal bit mask
        unsigned short m_usIndexBitMask;

        // CW Limit signal bit mask
        unsigned short m_usCWLimitBitMask;

        // CCW Limit signal bit mask
        unsigned short m_usCCWLimitBitMask;

        // Motor Ready signal bit mask
        unsigned short m_usReadyBitMask;

        // Motor On signal bit mask
        unsigned short m_usOnBitMask;

        // Alarm signal bit mask
        unsigned short m_usAlarmBitMask;

        // In Position signal bit mask
        unsigned short m_usInPositionBitMask;

        signal_mask()
        {
            m_usHomeBitMask       = 0;
            m_usIndexBitMask      = 0;
            m_usCWLimitBitMask    = 0;
            m_usCCWLimitBitMask   = 0;
            m_usReadyBitMask      = 0;
            m_usOnBitMask         = 0;
            m_usAlarmBitMask      = 0;
            m_usInPositionBitMask = 0;
        }
    } m_structSignalMask;

protected:
// methods to reset related motion events before any motion
    void ResetMotionEvents();

// methods to log trace data
    void Log(CString csData);

    void Log(const char *filename, int linenr, const char *formatString, ...);
    void StartHomingTimeoutTimer();

    void StopHomingTimeoutTimer();

    void SetRemainingHomingTimeout(DWORD dwTimeout);

    DWORD GetRemainingHomingTimeout();

    DWORD GetHomeTimeout();

    DWORD GetHomingStatus();

    void SetHomingStatus(DWORD dwStatus);

    DWORD WaitHomeComplete(DWORD dwTimeout);

    bool IsHomingComplete(DWORD dwStatus);

    bool IsHomingHitLimit(DWORD dwStatus);

    bool IsMotorAtHomeLimit();

    bool IsHomingDirectionCorrect(DWORD dwMotionStopStatus);

    bool DoLeaveHomeOperation(int nHowManyTimes, double dDistance, LEAVE_HOME_CHECK_SENSOR check);

    bool IsWithinSoftLimit(double dPosition);

    void StartMotionTimer();

    void StopMotionTimer();

    long GetMotionTime();

    double ConvertToMotorCoordinate(double dPosition);

    double ConvertToRealWorldCoordinate(double dPosition);

    double ConvertOutputPulseToPosition(double dPulse);

    double ConvertInputPulseToPosition(double dPulse);

    double ConvertInputPulseToPosition(const CMtrProfile &mtrProfile, double dPulse);

    double GetHomeLeaveSteps();

    bool IsMotorNumberValid(int nNumber);

    double Convert_M_To_mm(double dValue);

    double Convert_mm_To_M(double dValue);

    bool UseHomeSlowSpeed();

    CMotionControllerBase *m_pBaseController;

private:
// thread to monitor home operation
    CWinThread *m_pHomeThread;

// event to kill any running thread
    HANDLE m_hKillThread;

    CHighResPerformanceCounter m_homingTimer;

// High resolution timer object to track motion time
    CHighResPerformanceCounter m_timerMotion;

    DWORD m_dwHomingTimeout;

    DWORD m_dwHomingStatus;

// variable to store home motion timeout setting
    DWORD m_dwHomeTimeout;

// home leave steps with direction
    double m_dHomeLeaveSteps;

    // variable to store the no. of output pulse per mm
    double m_dPulsePerMM;

// variable to store the no. of encoder input pulse per mm
    double m_dEncoderPulsePerMM;

// virtual home position
    double m_dVirtualHomePosition;

    void SetHomeTimeout(DWORD dwTimeout);

    void SetHomeLeaveSteps(double dLeaveSteps);

    int BaseRelativeMove(double dDistance, int nPos, PROFILE_CURVE curve);

    int BaseAbsoluteMove(double dPosition, int nPos, int nMode, PROFILE_CURVE curve);
};

#define LOG_TRACE(format, ...) if (m_bTraceData) m_logger.Log(__FILE__, __LINE__, format, __VA_ARGS__)
#endif // !defined(AFX_MOTORBASE_H__04B8BC9A_5FF9_4E00_BA08_FDEC856015F1__INCLUDED_)
