/***********************************************************************/
/*            Demo source for using esd CANopen slave Stack            */
/*                                                                     */
/*       Copyright by Shanghai ESD Electric Technology Co., Ltd.       */
/*---------------------------------------------------------------------*/
/*         Filename:      pv_slave_canopen.c                           */
/*         Date:          2023-11-15                                   */
/*         Language:      ANSI C                                       */
/*         Targetsystem:  Linux                                        */
/*         Purpose:       Demostrate how to control esd CAN-STEPCON-1h */
/*                        which is DS-402 Implementation for stepper   */
/*                        controller. The device will run under        */
/*                        profile velocity mode, drive start to rotate */
/*                        and continuously increases its velocity      */
/*                        until the drive will stop automatically.     */
/*         Author:        Bob Tu                                       */
/*---------------------------------------------------------------------*/ 
/* Revision history:                                                   */
/*---------------------------------------------------------------------*/
/* v1.0    Birth of module                                             */
/***********************************************************************/

#include <scanopen.h>

#define CONTROL_WORD            0x6040
#define MODES_OF_OPERATION      0x6060
#define TARGET_VELOCITY         0x60FF

#define STATUS_WORD             0x6041
#define VELOCITY_ACTUAL_VALUE   0x606C

#define TARGET_REACHED          0X0400

DictionaryData ddControlWord;
DictionaryData ddModesOfOperation;
DictionaryData ddTargetVelocity;

DictionaryData ddStatusWord;
DictionaryData ddVelocityActualValue;

HNODE hndNode;                    
HPDO  hndPdoModeOfOperation;	  
HPDO  hndPdoTargetVelocity;		  
HPDO  hndPdoVelocityActualValue;  

UINT16 pdoMapping30x[] = {0x6040, 0, 0x6060, 0, 0, 0};
UINT16 pdoMapping50x[] = {0x6040, 0, 0x60ff, 0, 0, 0};
UINT16 pdoMapping48x[] = {0x6041, 0, 0x606C, 0, 0, 0};

UINT8 pdoDataModeOfOperation[] = {0xf, 0, 0x3};
UINT8 pdoDataTargetVelocity[] = {0xf, 0, 0, 0x1, 0, 0};

UINT8 need_to_update_velocity = 0;
UINT8 need_to_stop = 0;
UINT8 in_op = 0;

/*
* Event handler callback of CANopen slave node to notify the 
* application about node state changes, communication problems, 
* etc.
*/ 
int NodeEventHandler(SLAVE_EVENT *pEvent)
{
    unsigned short net    = pEvent->usNetNo;
    unsigned short node   = pEvent->usModId;
    unsigned long  iEvent = pEvent->ulEvent;
    unsigned long  iArg1  = pEvent->ulArg1;
    printf("Event for net %#x node %#x: ", net, node);
    switch(iEvent)
    {
        case EV_STATE_CHANGE:
            printf("State changed to ");
            switch(iArg1)
            {
                case NodeInit:
                    printf("INITIALIZING\n");
                    if(in_op) need_to_stop = 1;
                case NodePrepared:
                    printf("STOPPED\n");
                    if(in_op) need_to_stop = 1;
                    break;
                case NodeOperational:
                    printf("OPERATIONAL\n");
                    break;
                case NodePreOperational:
                    printf("PRE-OPERATIONAL\n");
                    if(in_op) need_to_stop = 1;
                    break;
            }
        break;
    }
    return(0);
}

/*
* Data handler callback of CANopen slave node to notify the application
* about value changes of application defined object dictionary entries
* caused either by PDOs which are mapped into this object dictionary or
* caused by direct SDO access.
*/ 
int DataEventHandler(
    int NetNo,				/* Logical net number of CAN net        */
    int NodeNo,			    /* Logical node number of CANopen slave */
    int index,				/* Object dictionary entry index        */
    int subindex,			/* Object dictionary entry subindex     */
    void *data				/* Direct ptr to data                   */
    )
{
    // printf("New value for net %d node %d index %x subindex %d is %lx\n",
    //     NetNo, NodeNo, index, subindex, *(unsigned long *)data);
    if((index == STATUS_WORD) && (*(unsigned long *)data & TARGET_REACHED))
    {
        if(pdoDataTargetVelocity[3]==0xf)
            need_to_stop=1;
        else
            need_to_update_velocity=1;
    }
    return(0);
}

int main()
{

    int iNetNo = 3; 
    int iNodeNo = 0x11;
    SLAVE_NET_INFO slaveNetInfo;
    SLAVE_NODE_INFO slaveNodeInfo;

    memset(&slaveNetInfo, 0, sizeof(slaveNetInfo));	
    slaveNetInfo.usBaudrate = 1000;
    if (canOpenCreateNetworkEx(iNetNo,&slaveNetInfo))
    {
        printf("canOpenCreateNetworkEx() error!");
        (void)canOpenRemoveNetwork(iNetNo);
        exit(-1);
    }

    memset(&slaveNodeInfo, 0, sizeof(slaveNodeInfo));
    slaveNodeInfo.ulEventMask      = EV_STATE_CHANGE    | 
                                     EV_READ_DICTIONARY | 
                                     EV_SYNC;
    slaveNodeInfo.usRxPDO          = 4;
    slaveNodeInfo.usTxPDO          = 4;
    slaveNodeInfo.ucMaxIdentityObj = 1;	
    slaveNodeInfo.ulDeviceType     = DEV_TYPE(DEVTYPE_DS402, 4);
    slaveNodeInfo.ulVendorId       = VENDOR_ID_ESD;


    if(canOpenCreateNodeEx(iNetNo,iNodeNo,NodeEventHandler,&slaveNodeInfo,&hndNode))
    {
        printf("canOpenCreateNodeEx() error!");
        (void)canOpenRemoveNetwork(iNetNo);
        exit(-1);
    }

    /*
    * Definition of local Object Dictionary
    */
    BEGIN_DICTIONARY_TABLE(DicTableForNode1)
        DICTIONARY_ENTRY(CONTROL_WORD, 0, OBJ_VAR, MAP_UINT16, 
        MAPPABLE | READ_ACCESS | WRITE_ACCESS | MULTI_MAP,
        &ddControlWord, NULL, NULL)

        DICTIONARY_ENTRY(MODES_OF_OPERATION, 0, OBJ_VAR, MAP_INT8, 
        MAPPABLE | READ_ACCESS | WRITE_ACCESS,
        &ddModesOfOperation, NULL, NULL)

        DICTIONARY_ENTRY(TARGET_VELOCITY, 0, OBJ_VAR, MAP_INT32, 
        MAPPABLE | READ_ACCESS | WRITE_ACCESS,
        &ddTargetVelocity, NULL, NULL)

        DICTIONARY_ENTRY(STATUS_WORD, 0, OBJ_VAR, MAP_UINT16, 
        MAPPABLE | READ_ACCESS | WRITE_ACCESS,
        &ddStatusWord, DataEventHandler, NULL)

        DICTIONARY_ENTRY(VELOCITY_ACTUAL_VALUE, 0, OBJ_VAR, MAP_INT32, 
        MAPPABLE | READ_ACCESS | WRITE_ACCESS,
        &ddVelocityActualValue, NULL, NULL)
    END_DICTIONARY_TABLE()
    
    DECLARE_DICTIONARY(hndNode, DicTableForNode1);

    canOpenDefinePDO(
        hndNode,    /* handle of the node */
        NULL,       /* legacy parameter always set to NULL */
        0x301,      /* COB-ID of the PDO */
        TRANSMIT_PDO | AUTO_NOTIFY_PDO | 255,   /* transmission mode */
        0,              /* inhibit time disabled*/
        5000,           /* TxTout */
        5000,           /* RxTout*/
        0   ,           /* iEventTimer 0 msec*/
        &pdoMapping30x[0],   /* Mapping */
        &hndPdoModeOfOperation           /* handle of the pdo */
    );

    canOpenDefinePDO(
        hndNode,    /* handle of the node */
        NULL,       /* legacy parameter always set to NULL */
        0x501,      /* COB-ID of the PDO */
        TRANSMIT_PDO | AUTO_NOTIFY_PDO | 255,   /* transmission mode */
        0,              /* inhibit time */
        5000,           /* TxTout */
        5000,           /* RxTout*/
        0   ,           /* iEventTimer 0 msec*/
        &pdoMapping50x[0],          /* Mapping */
        &hndPdoTargetVelocity       /* handle of the pdo */
    );

    canOpenDefinePDO(
        hndNode,    /* handle of the node */
        NULL,       /* legacy parameter always set to NULL */
        0x481,      /* COB-ID of the PDO */
        RECEIVE_PDO | AUTO_NOTIFY_PDO | 255,   /* transmission mode */
        0,              /* inhibit time */
        5000,           /* TxTout */
        5000,           /* RxTout*/
        0   ,           /* iEventTimer 0 msec*/
        &pdoMapping48x[0],                  /* Mapping */
        &hndPdoVelocityActualValue          /* handle of the pdo */
    );

    canOpenActivateNode(hndNode);
    
    printf("Waiting for node state to OPERATIONAL\n");
    canOpenWaitForNodeState(hndNode, WFNS_OPERATIONAL);

    in_op=1;
    canOpenWritePDO(hndPdoModeOfOperation, &pdoDataModeOfOperation[0]);  
    canOpenWritePDO(hndPdoTargetVelocity, &pdoDataTargetVelocity[0]);

    while(1)
    {
        if(need_to_stop){
            PSYS_SLEEP_SEC(3);
            pdoDataModeOfOperation[0]=0x7;
            canOpenWritePDO(hndPdoModeOfOperation,&pdoDataModeOfOperation);
            break;
        }

        if(need_to_update_velocity){
            PSYS_SLEEP_SEC(3);
            pdoDataTargetVelocity[3]++;
            canOpenWritePDO(hndPdoTargetVelocity,&pdoDataTargetVelocity);
            need_to_update_velocity=0;
        }
    }
    
    printf("CANopen slave is going down\n");
    canOpenResetNode(hndNode);
    canOpenDeleteNode(hndNode);
    canOpenRemoveNetwork(iNetNo);

    return(0);
}
