/**
 * CANopen Process Data Object protocol.
 *
 * @file        CO_PDO.h
 * @ingroup     CO_PDO
 * @author      Janez Paternoster
 * @copyright   2004 - 2020 Janez Paternoster
 *
 * This file is part of CANopenNode, an opensource CANopen Stack.
 * Project home page is <https://github.com/CANopenNode/CANopenNode>.
 * For more information on CANopen see <http://www.can-cia.org/>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#ifndef CO_PDO_H
#define CO_PDO_H

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @defgroup CO_PDO PDO
 * @ingroup CO_CANopen
 * @{
 *
 * CANopen Process Data Object protocol.
 *
 * Process data objects are used for real-time data transfer with no protocol
 * overhead.
 *
 * TPDO with specific identifier is transmitted by one device and recieved by
 * zero or more devices as RPDO. PDO communication parameters(COB-ID,
 * transmission type, etc.) are in Object Dictionary at index 0x1400+ and
 * 0x1800+. PDO mapping parameters (size and contents of the PDO) are in Object
 * Dictionary at index 0x1600+ and 0x1A00+.
 *
 * Features of the PDO as implemented here, in CANopenNode:
 *  - Dynamic PDO mapping.
 *  - Map granularity of one byte.
 *  - After RPDO is received from CAN bus, its data are copied to buffer.
 *    Function CO_RPDO_process() (called by application) copies data to
 *    mapped objects in Object Dictionary. Synchronous RPDOs are processed AFTER
 *    reception of the next SYNC message.
 *  - Function CO_TPDO_process() (called by application) sends TPDO if
 *    necessary. There are possible different transmission types, including
 *    automatic detection of Change of State of specific variable.
 */


/**
 * RPDO communication parameter. The same as record from Object dictionary (index 0x1400+).
 */
typedef struct{
    uint8_t             maxSubIndex;    /**< Equal to 2 */
    /** Communication object identifier for message received. Meaning of the specific bits:
        - Bit  0-10: COB-ID for PDO, to change it bit 31 must be set.
        - Bit 11-29: set to 0 for 11 bit COB-ID.
        - Bit 30:    If true, rtr are NOT allowed for PDO.
        - Bit 31:    If true, node does NOT use the PDO. */
    uint32_t            COB_IDUsedByRPDO;
    /** Transmission type. Values:
        - 0-240:   Reciving is synchronous, process after next reception of the SYNC object.
        - 241-253: Not used.
        - 254:     Manufacturer specific.
        - 255:     Asynchronous. */
    uint8_t             transmissionType;
}CO_RPDOCommPar_t;


/**
 * RPDO mapping parameter. The same as record from Object dictionary (index 0x1600+).
 */
typedef struct{
    /** Actual number of mapped objects from 0 to 8. To change mapped object,
    this value must be 0. */
    uint8_t             numberOfMappedObjects;
    /** Location and size of the mapped object. Bit meanings `0xIIIISSLL`:
        - Bit  0-7:  Data Length in bits.
        - Bit 8-15:  Subindex from object distionary.
        - Bit 16-31: Index from object distionary. */
    uint32_t            mappedObject1;
    uint32_t            mappedObject2;  /**< Same */
    uint32_t            mappedObject3;  /**< Same */
    uint32_t            mappedObject4;  /**< Same */
    uint32_t            mappedObject5;  /**< Same */
    uint32_t            mappedObject6;  /**< Same */
    uint32_t            mappedObject7;  /**< Same */
    uint32_t            mappedObject8;  /**< Same */
}CO_RPDOMapPar_t;


/**
 * TPDO communication parameter. The same as record from Object dictionary (index 0x1800+).
 */
typedef struct{
    uint8_t             maxSubIndex;    /**< Equal to 6 */
    /** Communication object identifier for transmitting message. Meaning of the specific bits:
        - Bit  0-10: COB-ID for PDO, to change it bit 31 must be set.
        - Bit 11-29: set to 0 for 11 bit COB-ID.
        - Bit 30:    If true, rtr are NOT allowed for PDO.
        - Bit 31:    If true, node does NOT use the PDO. */
    uint32_t            COB_IDUsedByTPDO;
    /** Transmission type. Values:
        - 0:       Transmiting is synchronous, specification in device profile.
        - 1-240:   Transmiting is synchronous after every N-th SYNC object.
        - 241-251: Not used.
        - 252-253: Transmited only on reception of Remote Transmission Request.
        - 254:     Manufacturer specific.
        - 255:     Asinchronous, specification in device profile. */
    uint8_t             transmissionType;
    /** Minimum time between transmissions of the PDO in 100micro seconds.
    Zero disables functionality. */
    uint16_t            inhibitTime;
    /** Not used */
    uint8_t             compatibilityEntry;
    /** Time between periodic transmissions of the PDO in milliseconds.
    Zero disables functionality. */
    uint16_t            eventTimer;
    /** Used with numbered SYNC messages. Values:
        - 0:       Counter of the SYNC message shall not be processed.
        - 1-240:   The SYNC message with the counter value equal to this value
                   shall be regarded as the first received SYNC message. */
    uint8_t             SYNCStartValue;
}CO_TPDOCommPar_t;


/**
 * TPDO mapping parameter. The same as record from Object dictionary (index 0x1A00+).
 */
typedef struct{
    /** Actual number of mapped objects from 0 to 8. To change mapped object,
    this value must be 0. */
    uint8_t             numberOfMappedObjects;
    /** Location and size of the mapped object. Bit meanings `0xIIIISSLL`:
        - Bit  0-7:  Data Length in bits.
        - Bit 8-15:  Subindex from object distionary.
        - Bit 16-31: Index from object distionary. */
    uint32_t            mappedObject1;
    uint32_t            mappedObject2;  /**< Same */
    uint32_t            mappedObject3;  /**< Same */
    uint32_t            mappedObject4;  /**< Same */
    uint32_t            mappedObject5;  /**< Same */
    uint32_t            mappedObject6;  /**< Same */
    uint32_t            mappedObject7;  /**< Same */
    uint32_t            mappedObject8;  /**< Same */
}CO_TPDOMapPar_t;


/**
 * RPDO object.
 */
typedef struct{
    CO_EM_t            *em;             /**< From CO_RPDO_init() */
    CO_SDO_t           *SDO;            /**< From CO_RPDO_init() */
    CO_SYNC_t          *SYNC;           /**< From CO_RPDO_init() */
    const CO_RPDOCommPar_t *RPDOCommPar;/**< From CO_RPDO_init() */
    const CO_RPDOMapPar_t  *RPDOMapPar; /**< From CO_RPDO_init() */
    uint8_t            *operatingState; /**< From CO_RPDO_init() */
    uint8_t             nodeId;         /**< From CO_RPDO_init() */
    uint16_t            defaultCOB_ID;  /**< From CO_RPDO_init() */
    uint8_t             restrictionFlags;/**< From CO_RPDO_init() */
    /** True, if PDO is enabled and valid */
    bool_t              valid;
    /** True, if PDO synchronous (transmissionType <= 240) */
    bool_t              synchronous;
    /** Data length of the received PDO message. Calculated from mapping */
    uint8_t             dataLength;
    /** Pointers to 8 data objects, where PDO will be copied */
    uint8_t            *mapPointer[8];
    /** Variable indicates, if new PDO message received from CAN bus. */
    volatile void      *CANrxNew[2];
    /** 8 data bytes of the received message. */
    uint8_t             CANrxData[2][8];
    CO_CANmodule_t     *CANdevRx;       /**< From CO_RPDO_init() */
    uint16_t            CANdevRxIdx;    /**< From CO_RPDO_init() */
}CO_RPDO_t;


/**
 * TPDO object.
 */
typedef struct{
    CO_EM_t            *em;             /**< From CO_TPDO_init() */
    CO_SDO_t           *SDO;            /**< From CO_TPDO_init() */
    CO_SYNC_t          *SYNC;           /**< From CO_TPDO_init() */
    const CO_TPDOCommPar_t *TPDOCommPar;/**< From CO_TPDO_init() */
    const CO_TPDOMapPar_t  *TPDOMapPar; /**< From CO_TPDO_init() */
    uint8_t            *operatingState; /**< From CO_TPDO_init() */
    uint8_t             nodeId;         /**< From CO_TPDO_init() */
    uint16_t            defaultCOB_ID;  /**< From CO_TPDO_init() */
    uint8_t             restrictionFlags;/**< From CO_TPDO_init() */
    bool_t              valid;          /**< True, if PDO is enabled and valid */
    /** Data length of the transmitting PDO message. Calculated from mapping */
    uint8_t             dataLength;
    /** If application set this flag, PDO will be later sent by
    function CO_TPDO_process(). Depends on transmission type. */
    uint8_t             sendRequest;
    /** Pointers to 8 data objects, where PDO will be copied */
    uint8_t            *mapPointer[8];
    /** Each flag bit is connected with one mapPointer. If flag bit
    is true, CO_TPDO_process() functiuon will send PDO if
    Change of State is detected on value pointed by that mapPointer */
    uint8_t             sendIfCOSFlags;
    /** SYNC counter used for PDO sending */
    uint8_t             syncCounter;
    /** Inhibit timer used for inhibit PDO sending translated to microseconds */
    uint32_t            inhibitTimer;
    /** Event timer used for PDO sending translated to microseconds */
    uint32_t            eventTimer;
    CO_CANmodule_t     *CANdevTx;       /**< From CO_TPDO_init() */
    CO_CANtx_t         *CANtxBuff;      /**< CAN transmit buffer inside CANdev */
    uint16_t            CANdevTxIdx;    /**< From CO_TPDO_init() */
}CO_TPDO_t;


/**
 * Initialize RPDO object.
 *
 * Function must be called in the communication reset section.
 *
 * @param RPDO This object will be initialized.
 * @param em Emergency object.
 * @param SDO SDO server object.
 * @param operatingState Pointer to variable indicating CANopen device NMT internal state.
 * @param nodeId CANopen Node ID of this device. If default COB_ID is used, value will be added.
 * @param defaultCOB_ID Default COB ID for this PDO (without NodeId).
 * See #CO_Default_CAN_ID_t
 * @param restrictionFlags Flag bits indicates, how PDO communication
 * and mapping parameters are handled:
 *  - Bit1: If true, communication parameters are writeable only in pre-operational NMT state.
 *  - Bit2: If true, mapping parameters are writeable only in pre-operational NMT state.
 *  - Bit3: If true, communication parameters are read-only.
 *  - Bit4: If true, mapping parameters are read-only.
 * @param RPDOCommPar Pointer to _RPDO communication parameter_ record from Object
 * dictionary (index 0x1400+).
 * @param RPDOMapPar Pointer to _RPDO mapping parameter_ record from Object
 * dictionary (index 0x1600+).
 * @param idx_RPDOCommPar Index in Object Dictionary.
 * @param idx_RPDOMapPar Index in Object Dictionary.
 * @param CANdevRx CAN device for PDO reception.
 * @param CANdevRxIdx Index of receive buffer in the above CAN device.
 *
 * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
 */
CO_ReturnError_t CO_RPDO_init(
        CO_RPDO_t              *RPDO,
        CO_EM_t                *em,
        CO_SDO_t               *SDO,
        CO_SYNC_t              *SYNC,
        uint8_t                *operatingState,
        uint8_t                 nodeId,
        uint16_t                defaultCOB_ID,
        uint8_t                 restrictionFlags,
        const CO_RPDOCommPar_t *RPDOCommPar,
        const CO_RPDOMapPar_t  *RPDOMapPar,
        uint16_t                idx_RPDOCommPar,
        uint16_t                idx_RPDOMapPar,
        CO_CANmodule_t         *CANdevRx,
        uint16_t                CANdevRxIdx);


/**
 * Initialize TPDO object.
 *
 * Function must be called in the communication reset section.
 *
 * @param TPDO This object will be initialized.
 * @param em Emergency object.
 * @param SDO SDO object.
 * @param operatingState Pointer to variable indicating CANopen device NMT internal state.
 * @param nodeId CANopen Node ID of this device. If default COB_ID is used, value will be added.
 * @param defaultCOB_ID Default COB ID for this PDO (without NodeId).
 * See #CO_Default_CAN_ID_t
 * @param restrictionFlags Flag bits indicates, how PDO communication
 * and mapping parameters are handled:
 *  - Bit1: If true, communication parameters are writeable only in pre-operational NMT state.
 *  - Bit2: If true, mapping parameters are writeable only in pre-operational NMT state.
 *  - Bit3: If true, communication parameters are read-only.
 *  - Bit4: If true, mapping parameters are read-only.
 * @param TPDOCommPar Pointer to _TPDO communication parameter_ record from Object
 * dictionary (index 0x1400+).
 * @param TPDOMapPar Pointer to _TPDO mapping parameter_ record from Object
 * dictionary (index 0x1600+).
 * @param idx_TPDOCommPar Index in Object Dictionary.
 * @param idx_TPDOMapPar Index in Object Dictionary.
 * @param CANdevTx CAN device used for PDO transmission.
 * @param CANdevTxIdx Index of transmit buffer in the above CAN device.
 *
 * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
 */
CO_ReturnError_t CO_TPDO_init(
        CO_TPDO_t              *TPDO,
        CO_EM_t                *em,
        CO_SDO_t               *SDO,
        CO_SYNC_t              *SYNC,
        uint8_t                *operatingState,
        uint8_t                 nodeId,
        uint16_t                defaultCOB_ID,
        uint8_t                 restrictionFlags,
        const CO_TPDOCommPar_t *TPDOCommPar,
        const CO_TPDOMapPar_t  *TPDOMapPar,
        uint16_t                idx_TPDOCommPar,
        uint16_t                idx_TPDOMapPar,
        CO_CANmodule_t         *CANdevTx,
        uint16_t                CANdevTxIdx);


/**
 * Verify Change of State of the PDO.
 *
 * Function verifies if variable mapped to TPDO has changed its value. Verified
 * are only variables, which has set attribute _CO_ODA_TPDO_DETECT_COS_ in
 * #CO_SDO_OD_attributes_t.
 *
 * Function may be called by application just before CO_TPDO_process() function,
 * for example: `TPDOx->sendRequest = CO_TPDOisCOS(TPDOx); CO_TPDO_process(TPDOx, ....`
 *
 * @param TPDO TPDO object.
 *
 * @return True if COS was detected.
 */
uint8_t CO_TPDOisCOS(CO_TPDO_t *TPDO);


/**
 * Send TPDO message.
 *
 * Function prepares TPDO data from Object Dictionary variables. It should not
 * be called by application, it is called from CO_TPDO_process().
 *
 *
 * @param TPDO TPDO object.
 *
 * @return Same as CO_CANsend().
 */
int16_t CO_TPDOsend(CO_TPDO_t *TPDO);


/**
 * Process received PDO messages.
 *
 * Function must be called cyclically in any NMT state. It copies data from RPDO
 * to Object Dictionary variables if: new PDO receives and PDO is valid and NMT
 * operating state is operational. It does not verify _transmission type_.
 *
 * @param RPDO This object.
 * @param syncWas True, if CANopen SYNC message was just received or transmitted.
 */
void CO_RPDO_process(CO_RPDO_t *RPDO, bool_t syncWas);


/**
 * Process transmitting PDO messages.
 *
 * Function must be called cyclically in any NMT state. It prepares and sends
 * TPDO if necessary. If Change of State needs to be detected, function
 * CO_TPDOisCOS() must be called before.
 *
 * @param TPDO This object.
 * @param SYNC SYNC object. Ignored if NULL.
 * @param syncWas True, if CANopen SYNC message was just received or transmitted.
 * @param timeDifference_us Time difference from previous function call in [microseconds].
 */
void CO_TPDO_process(
        CO_TPDO_t              *TPDO,
        bool_t                  syncWas,
        uint32_t                timeDifference_us);

#ifdef __cplusplus
}
#endif /*__cplusplus*/

/** @} */
#endif
