/**
 * CANopen Emergency protocol.
 *
 * @file        CO_Emergency.h
 * @ingroup     CO_Emergency
 * @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_EMERGENCY_H
#define CO_EMERGENCY_H

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @defgroup CO_Emergency Emergency
 * @ingroup CO_CANopen
 * @{
 *
 * CANopen Emergency protocol.
 *
 * Error control and Emergency is used for control internal error state
 * and for sending a CANopen Emergency message.
 *
 * In case of error condition stack or application calls CO_errorReport()
 * function with indication of the error. Specific error condition is reported
 * (with CANopen Emergency message) only the first time after it occurs.
 * Internal state of the error condition is controlled with
 * @ref CO_EM_errorStatusBits. Specific error condition can also be reset by
 * CO_errorReset() function. If so, Emergency message is sent with
 * CO_EM_NO_ERROR indication.
 *
 * Some error conditions are informative and some are critical. Critical error
 * conditions sets the #CO_errorRegisterBitmask_t.
 *
 * Latest errors can be read from _Pre Defined Error Field_ (object dictionary,
 * index 0x1003). @ref CO_EM_errorStatusBits can also be read form CANopen
 * object dictionary.
 *
 * ###Emergency message contents:
 *
 *   Byte | Description
 *   -----|-----------------------------------------------------------
 *   0..1 | @ref CO_EM_errorCodes.
 *   2    | #CO_errorRegisterBitmask_t.
 *   3    | Index of error condition (see @ref CO_EM_errorStatusBits).
 *   4..7 | Additional argument informative to CO_errorReport() function.
 *
 * ####Contents of _Pre Defined Error Field_ (object dictionary, index 0x1003):
 * bytes 0..3 are equal to bytes 0..3 in the Emergency message.
 *
 * @see #CO_Default_CAN_ID_t
 */


/**
 * CANopen Error register.
 *
 * In object dictionary on index 0x1001.
 *
 * Error register is calculated from critical internal @ref CO_EM_errorStatusBits.
 * Generic and communication bits are calculated in CO_EM_process
 * function, device profile or manufacturer specific bits may be calculated
 * inside the application.
 *
 * Internal errors may prevent device to stay in NMT Operational state. Details
 * are described in _Error Behavior_ object in Object Dictionary at index 0x1029.
 */
typedef enum{
    CO_ERR_REG_GENERIC_ERR  = 0x01U, /**< bit 0, generic error */
    CO_ERR_REG_CURRENT      = 0x02U, /**< bit 1, current */
    CO_ERR_REG_VOLTAGE      = 0x04U, /**< bit 2, voltage */
    CO_ERR_REG_TEMPERATURE  = 0x08U, /**< bit 3, temperature */
    CO_ERR_REG_COMM_ERR     = 0x10U, /**< bit 4, communication error (overrun, error state) */
    CO_ERR_REG_DEV_PROFILE  = 0x20U, /**< bit 5, device profile specific */
    CO_ERR_REG_RESERVED     = 0x40U, /**< bit 6, reserved (always 0) */
    CO_ERR_REG_MANUFACTURER = 0x80U  /**< bit 7, manufacturer specific */
}CO_errorRegisterBitmask_t;


/**
 * @defgroup CO_EM_errorCodes CANopen Error codes
 * @{
 *
 * Standard error codes according to CiA DS-301 and DS-401.
 */
#define CO_EMC_NO_ERROR                 0x0000U /**< 0x00xx, error Reset or No Error */
#define CO_EMC_GENERIC                  0x1000U /**< 0x10xx, Generic Error */
#define CO_EMC_CURRENT                  0x2000U /**< 0x20xx, Current */
#define CO_EMC_CURRENT_INPUT            0x2100U /**< 0x21xx, Current, device input side */
#define CO_EMC_CURRENT_INSIDE           0x2200U /**< 0x22xx, Current inside the device */
#define CO_EMC_CURRENT_OUTPUT           0x2300U /**< 0x23xx, Current, device output side */
#define CO_EMC_VOLTAGE                  0x3000U /**< 0x30xx, Voltage */
#define CO_EMC_VOLTAGE_MAINS            0x3100U /**< 0x31xx, Mains Voltage */
#define CO_EMC_VOLTAGE_INSIDE           0x3200U /**< 0x32xx, Voltage inside the device */
#define CO_EMC_VOLTAGE_OUTPUT           0x3300U /**< 0x33xx, Output Voltage */
#define CO_EMC_TEMPERATURE              0x4000U /**< 0x40xx, Temperature */
#define CO_EMC_TEMP_AMBIENT             0x4100U /**< 0x41xx, Ambient Temperature */
#define CO_EMC_TEMP_DEVICE              0x4200U /**< 0x42xx, Device Temperature */
#define CO_EMC_HARDWARE                 0x5000U /**< 0x50xx, Device Hardware */
#define CO_EMC_SOFTWARE_DEVICE          0x6000U /**< 0x60xx, Device Software */
#define CO_EMC_SOFTWARE_INTERNAL        0x6100U /**< 0x61xx, Internal Software */
#define CO_EMC_SOFTWARE_USER            0x6200U /**< 0x62xx, User Software */
#define CO_EMC_DATA_SET                 0x6300U /**< 0x63xx, Data Set */
#define CO_EMC_ADDITIONAL_MODUL         0x7000U /**< 0x70xx, Additional Modules */
#define CO_EMC_MONITORING               0x8000U /**< 0x80xx, Monitoring */
#define CO_EMC_COMMUNICATION            0x8100U /**< 0x81xx, Communication */
#define CO_EMC_CAN_OVERRUN              0x8110U /**< 0x8110, CAN Overrun (Objects lost) */
#define CO_EMC_CAN_PASSIVE              0x8120U /**< 0x8120, CAN in Error Passive Mode */
#define CO_EMC_HEARTBEAT                0x8130U /**< 0x8130, Life Guard Error or Heartbeat Error */
#define CO_EMC_BUS_OFF_RECOVERED        0x8140U /**< 0x8140, recovered from bus off */
#define CO_EMC_CAN_ID_COLLISION         0x8150U /**< 0x8150, CAN-ID collision */
#define CO_EMC_PROTOCOL_ERROR           0x8200U /**< 0x82xx, Protocol Error */
#define CO_EMC_PDO_LENGTH               0x8210U /**< 0x8210, PDO not processed due to length error */
#define CO_EMC_PDO_LENGTH_EXC           0x8220U /**< 0x8220, PDO length exceeded */
#define CO_EMC_DAM_MPDO                 0x8230U /**< 0x8230, DAM MPDO not processed, destination object not available */
#define CO_EMC_SYNC_DATA_LENGTH         0x8240U /**< 0x8240, Unexpected SYNC data length */
#define CO_EMC_RPDO_TIMEOUT             0x8250U /**< 0x8250, RPDO timeout */
#define CO_EMC_TIME_DATA_LENGTH         0x8260U /**< 0x8260, Unexpected TIME data length */
#define CO_EMC_EXTERNAL_ERROR           0x9000U /**< 0x90xx, External Error */
#define CO_EMC_ADDITIONAL_FUNC          0xF000U /**< 0xF0xx, Additional Functions */
#define CO_EMC_DEVICE_SPECIFIC          0xFF00U /**< 0xFFxx, Device specific */

#define CO_EMC401_OUT_CUR_HI            0x2310U /**< 0x2310, DS401, Current at outputs too high (overload) */
#define CO_EMC401_OUT_SHORTED           0x2320U /**< 0x2320, DS401, Short circuit at outputs */
#define CO_EMC401_OUT_LOAD_DUMP         0x2330U /**< 0x2330, DS401, Load dump at outputs */
#define CO_EMC401_IN_VOLT_HI            0x3110U /**< 0x3110, DS401, Input voltage too high */
#define CO_EMC401_IN_VOLT_LOW           0x3120U /**< 0x3120, DS401, Input voltage too low */
#define CO_EMC401_INTERN_VOLT_HI        0x3210U /**< 0x3210, DS401, Internal voltage too high */
#define CO_EMC401_INTERN_VOLT_LO        0x3220U /**< 0x3220, DS401, Internal voltage too low */
#define CO_EMC401_OUT_VOLT_HIGH         0x3310U /**< 0x3310, DS401, Output voltage too high */
#define CO_EMC401_OUT_VOLT_LOW          0x3320U /**< 0x3320, DS401, Output voltage too low */
/** @} */


/**
 * @defgroup CO_EM_errorStatusBits Error status bits
 * @{
 *
 * Internal indication of the error condition.
 *
 * Each error condition is specified by unique index from 0x00 up to 0xFF.
 * Variable  (from manufacturer section in the Object
 * Dictionary) contains up to 0xFF bits (32bytes) for the identification of the
 * specific error condition. (Type of the variable is CANopen OCTET_STRING.)
 *
 * If specific error occurs in the stack or in the application, CO_errorReport()
 * sets specific bit in the _Error Status Bits_ variable. If bit was already
 * set, function returns without any action. Otherwise it prepares emergency
 * message.
 *
 * CO_errorReport(), CO_errorReset() or CO_isError() functions are called
 * with unique index as an argument. (However CO_errorReport(), for example, may
 * be used with the same index on multiple places in the code.)
 *
 * Macros defined below are combination of two constants: index and
 * @ref CO_EM_errorCodes. They represents specific error conditions. They are
 * used as double argument for CO_errorReport(), CO_errorReset() and
 * CO_isError() functions.
 *
 * Stack uses first 6 bytes of the _Error Status Bits_ variable. Device profile
 * or application may define own macros for Error status bits using
 * @ref CO_EM_MANUFACTURER_START and @ref CO_EM_MANUFACTURER_END values. Note that
 * _Error Status Bits_ must be large enough (up to 32 bytes).
 */
#define CO_EM_NO_ERROR                  0x00U /**< 0x00, Error Reset or No Error */
#define CO_EM_CAN_BUS_WARNING           0x01U /**< 0x01, communication, info, CAN bus warning limit reached */
#define CO_EM_RXMSG_WRONG_LENGTH        0x02U /**< 0x02, communication, info, Wrong data length of the received CAN message */
#define CO_EM_RXMSG_OVERFLOW            0x03U /**< 0x03, communication, info, Previous received CAN message wasn't processed yet */
#define CO_EM_RPDO_WRONG_LENGTH         0x04U /**< 0x04, communication, info, Wrong data length of received PDO */
#define CO_EM_RPDO_OVERFLOW             0x05U /**< 0x05, communication, info, Previous received PDO wasn't processed yet */
#define CO_EM_CAN_RX_BUS_PASSIVE        0x06U /**< 0x06, communication, info, CAN receive bus is passive */
#define CO_EM_CAN_TX_BUS_PASSIVE        0x07U /**< 0x07, communication, info, CAN transmit bus is passive */
#define CO_EM_NMT_WRONG_COMMAND         0x08U /**< 0x08, communication, info, Wrong NMT command received */
#define CO_EM_TIME_TIMEOUT              0x09U /**< 0x09, communication, info, TIME message timeout */
#define CO_EM_TIME_LENGTH               0x0AU /**< 0x0A, communication, info, Unexpected TIME data length */
#define CO_EM_0B_unused                 0x0BU /**< 0x0B, (unused) */
#define CO_EM_0C_unused                 0x0CU /**< 0x0C, (unused) */
#define CO_EM_0D_unused                 0x0DU /**< 0x0D, (unused) */
#define CO_EM_0E_unused                 0x0EU /**< 0x0E, (unused) */
#define CO_EM_0F_unused                 0x0FU /**< 0x0F, (unused) */

#define CO_EM_10_unused                 0x10U /**< 0x10, (unused) */
#define CO_EM_11_unused                 0x11U /**< 0x11, (unused) */
#define CO_EM_CAN_TX_BUS_OFF            0x12U /**< 0x12, communication, critical, CAN transmit bus is off */
#define CO_EM_CAN_RXB_OVERFLOW          0x13U /**< 0x13, communication, critical, CAN module receive buffer has overflowed */
#define CO_EM_CAN_TX_OVERFLOW           0x14U /**< 0x14, communication, critical, CAN transmit buffer has overflowed */
#define CO_EM_TPDO_OUTSIDE_WINDOW       0x15U /**< 0x15, communication, critical, TPDO is outside SYNC window */
#define CO_EM_16_unused                 0x16U /**< 0x16, (unused) */
#define CO_EM_17_unused                 0x17U /**< 0x17, (unused) */
#define CO_EM_SYNC_TIME_OUT             0x18U /**< 0x18, communication, critical, SYNC message timeout */
#define CO_EM_SYNC_LENGTH               0x19U /**< 0x19, communication, critical, Unexpected SYNC data length */
#define CO_EM_PDO_WRONG_MAPPING         0x1AU /**< 0x1A, communication, critical, Error with PDO mapping */
#define CO_EM_HEARTBEAT_CONSUMER        0x1BU /**< 0x1B, communication, critical, Heartbeat consumer timeout */
#define CO_EM_HB_CONSUMER_REMOTE_RESET  0x1CU /**< 0x1C, communication, critical, Heartbeat consumer detected remote node reset */
#define CO_EM_1D_unused                 0x1DU /**< 0x1D, (unused) */
#define CO_EM_1E_unused                 0x1EU /**< 0x1E, (unused) */
#define CO_EM_1F_unused                 0x1FU /**< 0x1F, (unused) */

#define CO_EM_EMERGENCY_BUFFER_FULL     0x20U /**< 0x20, generic, info, Emergency buffer is full, Emergency message wasn't sent */
#define CO_EM_21_unused                 0x21U /**< 0x21, (unused) */
#define CO_EM_MICROCONTROLLER_RESET     0x22U /**< 0x22, generic, info, Microcontroller has just started */
#define CO_EM_23_unused                 0x23U /**< 0x23, (unused) */
#define CO_EM_24_unused                 0x24U /**< 0x24, (unused) */
#define CO_EM_25_unused                 0x25U /**< 0x25, (unused) */
#define CO_EM_26_unused                 0x26U /**< 0x26, (unused) */
#define CO_EM_27_unused                 0x27U /**< 0x27, (unused) */

#define CO_EM_WRONG_ERROR_REPORT        0x28U /**< 0x28, generic, critical, Wrong parameters to CO_errorReport() function */
#define CO_EM_ISR_TIMER_OVERFLOW        0x29U /**< 0x29, generic, critical, Timer task has overflowed */
#define CO_EM_MEMORY_ALLOCATION_ERROR   0x2AU /**< 0x2A, generic, critical, Unable to allocate memory for objects */
#define CO_EM_GENERIC_ERROR             0x2BU /**< 0x2B, generic, critical, Generic error, test usage */
#define CO_EM_GENERIC_SOFTWARE_ERROR    0x2CU /**< 0x2C, generic, critical, Software error */
#define CO_EM_INCONSISTENT_OBJECT_DICT  0x2DU /**< 0x2D, generic, critical, Object dictionary does not match the software */
#define CO_EM_CALCULATION_OF_PARAMETERS 0x2EU /**< 0x2E, generic, critical, Error in calculation of device parameters */
#define CO_EM_NON_VOLATILE_MEMORY       0x2FU /**< 0x2F, generic, critical, Error with access to non volatile device memory */

#define CO_EM_MANUFACTURER_START        0x30U /**< 0x30, manufacturer, info, This can be used by macros to calculate error codes */
#define CO_EM_MANUFACTURER_END          0xFFU /**< 0xFF, manufacturer, info, This can be used by macros to check error codes */
/** @} */


/**
 * Size of internal buffer, whwre emergencies are stored after CO_errorReport().
 * Buffer is cleared by CO_EM_process().
 */
#define CO_EM_INTERNAL_BUFFER_SIZE      10


/**
 * Emergerncy object for CO_errorReport(). It contains error buffer, to which new emergency
 * messages are written, when CO_errorReport() is called. This object is included in
 * CO_EMpr_t object.
 */
typedef struct{
    uint8_t            *errorStatusBits;        /**< From CO_EM_init() */
    uint8_t             errorStatusBitsSize;    /**< From CO_EM_init() */

    /** Internal buffer for storing unsent emergency messages.*/
    uint8_t             buf[CO_EM_INTERNAL_BUFFER_SIZE * 8];
    uint8_t            *bufEnd;             /**< End+1 address of the above buffer */
    uint8_t            *bufWritePtr;        /**< Write pointer in the above buffer */
    uint8_t            *bufReadPtr;         /**< Read pointer in the above buffer */
    uint8_t             bufFull;            /**< True if above buffer is full */
    uint8_t             wrongErrorReport;   /**< Error in arguments to CO_errorReport() */

    /** From CO_EM_initCallback() or NULL */
    void              (*pFunctSignal)(void);
    /** From CO_EM_initCallbackRx() or NULL */
    void              (*pFunctSignalRx)(const uint16_t ident,
                                        const uint16_t errorCode,
                                        const uint8_t errorRegister,
                                        const uint8_t errorBit,
                                        const uint32_t infoCode);
}CO_EM_t;


/**
 * Report error condition.
 *
 * Function is called on any error condition inside CANopen stack and may also
 * be called by application on custom error condition. Emergency message is sent
 * after the first occurance of specific error. In case of critical error, device
 * will not be able to stay in NMT_OPERATIONAL state.
 *
 * Function is short and may be used form any task or interrupt.
 *
 * @param em Emergency object.
 * @param errorBit from @ref CO_EM_errorStatusBits.
 * @param errorCode from @ref CO_EM_errorCodes.
 * @param infoCode 32 bit value is passed to bytes 4...7 of the Emergency message.
 * It contains optional additional information inside emergency message.
 */
void CO_errorReport(CO_EM_t *em, const uint8_t errorBit, const uint16_t errorCode, const uint32_t infoCode);


/**
 * Reset error condition.
 *
 * Function is called if any error condition is solved. Emergency message is sent
 * with @ref CO_EM_errorCodes 0x0000.
 *
 * Function is short and may be used form any task or interrupt.
 *
 * @param em Emergency object.
 * @param errorBit from @ref CO_EM_errorStatusBits.
 * @param infoCode 32 bit value is passed to bytes 4...7 of the Emergency message.
 */
void CO_errorReset(CO_EM_t *em, const uint8_t errorBit, const uint32_t infoCode);


/**
 * Check specific error condition.
 *
 * Function returns 1, if specific internal error is present. Otherwise it returns 0.
 *
 * @param em Emergency object.
 * @param errorBit from @ref CO_EM_errorStatusBits.
 *
 * @return false: Error is not present.
 * @return true: Error is present.
 */
bool_t CO_isError(CO_EM_t *em, const uint8_t errorBit);


#ifdef CO_DOXYGEN
/** Skip section, if CO_SDO.h is not included */
    #define CO_SDO_H
#endif
#ifdef CO_SDO_H


/**
 * Error control and Emergency object. It controls internal error state and
 * sends emergency message, if error condition was reported. Object is initialized
 * by CO_EM_init(). It contains CO_EM_t object.
 */
typedef struct{
    uint8_t            *errorRegister;  /**< From CO_EM_init() */
    uint32_t           *preDefErr;      /**< From CO_EM_init() */
    uint8_t             preDefErrSize;  /**< From CO_EM_init() */
    uint8_t             preDefErrNoOfErrors;/**< Number of active errors in preDefErr */
    uint16_t            inhibitEmTimer; /**< Internal timer for emergency message */
    CO_EM_t            *em;             /**< CO_EM_t sub object is included here */
    CO_CANmodule_t     *CANdev;         /**< From CO_EM_init() */
    CO_CANtx_t         *CANtxBuff;      /**< CAN transmit buffer */
}CO_EMpr_t;


/**
 * Initialize Error control and Emergency object.
 *
 * Function must be called in the communication reset section.
 *
 * @param emPr This object will be initialized.
 * @param em Emergency object defined separately. Will be included in emPr and
 * initialized too.
 * @param SDO SDO server object.
 * @param errorStatusBits Pointer to _Error Status Bits_ array from Object Dictionary
 * (manufacturer specific section). See @ref CO_EM_errorStatusBits.
 * @param errorStatusBitsSize Total size of the above array. Must be >= 6.
 * @param errorRegister Pointer to _Error Register_ (Object dictionary, index 0x1001).
 * @param preDefErr Pointer to _Pre defined error field_ array from Object
 * dictionary, index 0x1003.
 * @param preDefErrSize Size of the above array.
 * @param CANdevRx CAN device for Emergency reception.
 * @param CANdevRxIdx Index of receive buffer in the above CAN device.
 * @param CANdevTx CAN device for Emergency transmission.
 * @param CANdevTxIdx Index of transmit buffer in the above CAN device.
 * @param CANidTxEM CAN identifier for Emergency message.
 *
 * @return #CO_ReturnError_t CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
 */
CO_ReturnError_t CO_EM_init(
        CO_EM_t                *em,
        CO_EMpr_t              *emPr,
        CO_SDO_t               *SDO,
        uint8_t                *errorStatusBits,
        uint8_t                 errorStatusBitsSize,
        uint8_t                *errorRegister,
        uint32_t               *preDefErr,
        uint8_t                 preDefErrSize,
        CO_CANmodule_t         *CANdevRx,
        uint16_t                CANdevRxIdx,
        CO_CANmodule_t         *CANdevTx,
        uint16_t                CANdevTxIdx,
        uint16_t                CANidTxEM);


/**
 * Initialize Emergency callback function.
 *
 * Function initializes optional callback function, which executes after
 * error condition is changed. Function may wake up external task,
 * which processes mainline CANopen functions.
 *
 * @param em This object.
 * @param pFunctSignal Pointer to the callback function. Not called if NULL.
 */
void CO_EM_initCallback(
        CO_EM_t               *em,
        void                  (*pFunctSignal)(void));


/**
 * Initialize Emergency received callback function.
 *
 * Function initializes optional callback function, which executes after
 * error condition is received. Function may wake up external task,
 * which processes mainline CANopen functions.
 *
 * @remark Depending on the CAN driver implementation, this function is called
 * inside an ISR
 *
 * @param em This object.
 * @param pFunctSignal Pointer to the callback function. Not called if NULL.
 */
void CO_EM_initCallbackRx(
        CO_EM_t                *em,
        void                  (*pFunctSignalRx)(const uint16_t ident,
                                                const uint16_t errorCode,
                                                const uint8_t errorRegister,
                                                const uint8_t errorBit,
                                                const uint32_t infoCode));


/**
 * Process Error control and Emergency object.
 *
 * Function must be called cyclically. It verifies some communication errors,
 * calculates bit 0 and bit 4 from _Error register_ and sends emergency message
 * if necessary.
 *
 * @param emPr This object.
 * @param NMTisPreOrOperational True if this node is NMT_PRE_OPERATIONAL or NMT_OPERATIONAL.
 * @param timeDifference_100us Time difference from previous function call in [100 * microseconds].
 * @param emInhTime _Inhibit time EMCY_ (object dictionary, index 0x1015).
 * @param timerNext_ms Return value - info to OS - see CO_process().
 */
void CO_EM_process(
        CO_EMpr_t              *emPr,
        bool_t                  NMTisPreOrOperational,
        uint16_t                timeDifference_100us,
        uint16_t                emInhTime,
        uint16_t               *timerNext_ms);


#endif

#ifdef __cplusplus
}
#endif /*__cplusplus*/

/** @} */
#endif
