
#ifndef ETHERCAT_ADVANCED_H
#define ETHERCAT_ADVANCED_H

#include "ethercat.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef enum {
    EC_DTYPE_BOOLEAN        = 0x0001,
    EC_DTYPE_INTEGER8       = 0x0002,
    EC_DTYPE_INTEGER16      = 0x0003,
    EC_DTYPE_INTEGER32      = 0x0004,
    EC_DTYPE_UNSIGNED8      = 0x0005,
    EC_DTYPE_UNSIGNED16     = 0x0006,
    EC_DTYPE_UNSIGNED32     = 0x0007,
    EC_DTYPE_REAL32         = 0x0008,
    EC_DTYPE_VISIBLE_STRING = 0x0009,
    EC_DTYPE_OCTET_STRING   = 0x000A,
    EC_DTYPE_UNICODE_STRING = 0x000B,

    EC_DTYPE_TIME_OF_DAY    = 0x000C,
    EC_DTYPE_TIME_DIFFERENCE = 0x000D,
    EC_DTYPE_DOMAIN         = 0x000F,
    EC_DTYPE_INTEGER24      = 0x0010,
    EC_DTYPE_REAL64         = 0x0011,

    EC_DTYPE_INTEGER40      = 0x0012,
    EC_DTYPE_INTEGER48      = 0x0013,
    EC_DTYPE_INTEGER56      = 0x0014,
    EC_DTYPE_INTEGER64      = 0x0015,
    EC_DTYPE_UNSIGNED24     = 0x0016,
    EC_DTYPE_UNSIGNED40     = 0x0018,
    EC_DTYPE_UNSIGNED48     = 0x0019,
    EC_DTYPE_UNSIGNED56     = 0x001A,
    EC_DTYPE_UNSIGNED64     = 0x001B,
    EC_DTYPE_PDO_MAPPING    = 0x0021
} ec_datatype_t;

typedef enum {
    EC_ACCESS_READ_PREOP   = 0x0001,
    EC_ACCESS_READ_SAFEOP  = 0x0002,
    EC_ACCESS_READ_OP      = 0x0004,
    EC_ACCESS_WRITE_PREOP  = 0x0008,
    EC_ACCESS_WRITE_SAFEOP = 0x0010,
    EC_ACCESS_WRITE_OP     = 0x0020,
    EC_ACCESS_RXPDO_MAP    = 0x0040,
    EC_ACCESS_TXPDO_MAP    = 0x0080
} ec_obj_access_t;

typedef struct {
    uint8_t  subindex;
    char     name[42];
    uint16_t datatype;
    uint16_t bit_length;
    uint16_t obj_access;
} od_entry_t;

typedef struct {
    uint16_t index;
    char     name[42];
    uint16_t datatype;
    uint8_t  object_code;
    uint8_t  max_sub;
    int      entry_count;
    od_entry_t* entries;
} od_object_t;

typedef struct {
    int count;
    od_object_t* objects;
} od_list_t;

od_list_t* od_load(dll_t* dll, uint16_t master, uint16_t slave, BOOL load_entries);

od_list_t* od_load_basic(dll_t* dll, uint16_t master, uint16_t slave);

void od_free(od_list_t* list);

od_object_t* od_find(od_list_t* list, uint16_t index);

od_entry_t* od_find_entry(od_object_t* obj, uint8_t subindex);

od_list_t* od_load_via_coe_h2(dll_t* dll, uint16_t master, uint16_t slave);

int coe_get_object_description(dll_t* dll, uint16_t master, uint16_t slave,
                                uint16_t index, od_object_t* out_obj);

int coe_get_entry_description(dll_t* dll, uint16_t master, uint16_t slave,
                               uint16_t index, uint8_t subindex,
                               od_entry_t* out_entry);

int sdo_read_u8(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, uint8_t* out);
int sdo_read_u16(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, uint16_t* out);
int sdo_read_u32(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, uint32_t* out);
int sdo_read_i8(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, int8_t* out);
int sdo_read_i16(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, int16_t* out);
int sdo_read_i32(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, int32_t* out);
int sdo_read_f32(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, float* out);
int sdo_read_f64(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, double* out);
int sdo_read_string(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, char* buf, int buf_size);

int sdo_write_u8(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, uint8_t val);
int sdo_write_u16(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, uint16_t val);
int sdo_write_u32(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, uint32_t val);
int sdo_write_i8(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, int8_t val);
int sdo_write_i16(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, int16_t val);
int sdo_write_i32(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, int32_t val);
int sdo_write_f32(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, float val);
int sdo_write_f64(dll_t* dll, uint16_t master, uint16_t slave, uint16_t index, uint8_t sub, double val);

int soe_read_i16(dll_t* dll, uint16_t master, uint16_t slave, uint8_t drive, uint16_t idn, int16_t* out);
int soe_read_i32(dll_t* dll, uint16_t master, uint16_t slave, uint8_t drive, uint16_t idn, int32_t* out);
int soe_read_u16(dll_t* dll, uint16_t master, uint16_t slave, uint8_t drive, uint16_t idn, uint16_t* out);
int soe_read_u32(dll_t* dll, uint16_t master, uint16_t slave, uint8_t drive, uint16_t idn, uint32_t* out);
int soe_read_i64(dll_t* dll, uint16_t master, uint16_t slave, uint8_t drive, uint16_t idn, int64_t* out);
int soe_read_u64(dll_t* dll, uint16_t master, uint16_t slave, uint8_t drive, uint16_t idn, uint64_t* out);
int soe_read_f32(dll_t* dll, uint16_t master, uint16_t slave, uint8_t drive, uint16_t idn, float* out);
int soe_read_f64(dll_t* dll, uint16_t master, uint16_t slave, uint8_t drive, uint16_t idn, double* out);
int soe_read_string(dll_t* dll, uint16_t master, uint16_t slave, uint8_t drive, uint16_t idn, char* buf, int buf_size);

int soe_write_i16(dll_t* dll, uint16_t master, uint16_t slave, uint8_t drive, uint16_t idn, int16_t val);
int soe_write_i32(dll_t* dll, uint16_t master, uint16_t slave, uint8_t drive, uint16_t idn, int32_t val);
int soe_write_u16(dll_t* dll, uint16_t master, uint16_t slave, uint8_t drive, uint16_t idn, uint16_t val);
int soe_write_u32(dll_t* dll, uint16_t master, uint16_t slave, uint8_t drive, uint16_t idn, uint32_t val);
int soe_write_f32(dll_t* dll, uint16_t master, uint16_t slave, uint8_t drive, uint16_t idn, float val);
int soe_write_f64(dll_t* dll, uint16_t master, uint16_t slave, uint8_t drive, uint16_t idn, double val);

typedef struct {
    int controlword;
    int target_position;
    int target_velocity;
    int target_torque;
    int modes_of_operation;
    int statusword;
    int position_actual;
    int velocity_actual;
    int torque_actual;
    int modes_display;
} cia402_pdo_map_t;

int cia402_scan_pdo(dll_t* dll, uint16_t master, uint16_t slave, cia402_pdo_map_t* map);

int cia402_read_statusword(dll_t* dll, uint16_t master, uint16_t slave, const cia402_pdo_map_t* map, uint16_t* out);
int cia402_write_controlword(dll_t* dll, uint16_t master, uint16_t slave, const cia402_pdo_map_t* map, uint16_t val);
int cia402_read_position(dll_t* dll, uint16_t master, uint16_t slave, const cia402_pdo_map_t* map, int32_t* out);
int cia402_write_target_position(dll_t* dll, uint16_t master, uint16_t slave, const cia402_pdo_map_t* map, int32_t val);
int cia402_read_velocity(dll_t* dll, uint16_t master, uint16_t slave, const cia402_pdo_map_t* map, int32_t* out);
int cia402_write_target_velocity(dll_t* dll, uint16_t master, uint16_t slave, const cia402_pdo_map_t* map, int32_t val);
int cia402_read_torque(dll_t* dll, uint16_t master, uint16_t slave, const cia402_pdo_map_t* map, int16_t* out);
int cia402_write_target_torque(dll_t* dll, uint16_t master, uint16_t slave, const cia402_pdo_map_t* map, int16_t val);

int cia402_read_object_observable(dll_t* dll, uint16_t master, uint16_t slave,
                                  uint16_t index, uint8_t subindex,
                                  cia402_read_result_t* result);

int cia402_read_error_code(dll_t* dll, uint16_t master, uint16_t slave, cia402_read_result_t* result);

int cia402_read_supported_drive_modes(dll_t* dll, uint16_t master, uint16_t slave, cia402_read_result_t* result);

int cia402_read_digital_inputs(dll_t* dll, uint16_t master, uint16_t slave, cia402_read_result_t* result);

int cia402_read_operation_mode_display(dll_t* dll, uint16_t master, uint16_t slave, cia402_read_result_t* result);

int cia402_read_vendor_drive_data(dll_t* dll, uint16_t master, uint16_t slave, uint8_t subindex, cia402_read_result_t* result);

uint16_t fsoe_crc16(const uint8_t* data, uint32_t length);

typedef struct {
    uint16_t connection_id;
    uint16_t safety_address;
    int      state;
    int      last_error;
    uint32_t error_count;
    uint32_t frames_sent;
    uint32_t frames_received;
    uint32_t crc_errors;
    uint32_t watchdog_errors;
    uint8_t  watchdog_expired;
    uint8_t  in_failsafe;
} fsoe_conn_info_t;

typedef struct fsoe_manager dx_fsoe_manager_t;

dx_fsoe_manager_t* fsoe_manager_create(dll_t* dll, uint16_t master, uint16_t slave);

void fsoe_manager_destroy(dx_fsoe_manager_t* mgr);

int fsoe_manager_add_connection(dx_fsoe_manager_t* mgr,
    uint16_t conn_id, uint16_t safety_addr, uint32_t watchdog_ms,
    uint16_t in_size, uint16_t out_size,
    uint32_t pdo_in_offset, uint32_t pdo_out_offset);

int fsoe_manager_close_all(dx_fsoe_manager_t* mgr);

int fsoe_manager_reset_all(dx_fsoe_manager_t* mgr);

int fsoe_manager_get_info(dx_fsoe_manager_t* mgr, int index, fsoe_conn_info_t* info);

int fsoe_manager_connection_count(dx_fsoe_manager_t* mgr);

BOOL fsoe_manager_all_in_data(dx_fsoe_manager_t* mgr);

BOOL fsoe_manager_any_in_failsafe(dx_fsoe_manager_t* mgr);

int fsoe_manager_enter_all_failsafe(dx_fsoe_manager_t* mgr);

typedef struct {
    uint16_t slot_index;
    uint16_t module_profile;
    uint16_t module_ident;
    uint16_t pdo_input_offset;
    uint16_t pdo_input_size;
    uint16_t pdo_output_offset;
    uint16_t pdo_output_size;
} mdp_module_t;

typedef struct {
    int count;
    mdp_module_t* modules;
} mdp_list_t;

mdp_list_t* mdp_discover(dll_t* dll, uint16_t master, uint16_t slave);

void mdp_free(mdp_list_t* list);

typedef struct {
    BOOL     success;
    uint32_t rtt_us;
    uint8_t  ttl;
} ping_result_t;

ping_result_t eoe_ping(dll_t* dll, uint16_t master, uint16_t slave,
                                    uint8_t port, uint32_t target_ip, int timeout_ms);

void eoe_arp_set(uint32_t ip, const uint8_t mac[6]);
void eoe_arp_clear(void);
BOOL eoe_arp_lookup(uint32_t ip, uint8_t mac[6]);

typedef struct aoe_sub_mgr aoe_sub_mgr_t;

typedef void (*aoe_data_callback_t)(uint32_t handle,
    uint32_t index_group, uint32_t index_offset,
    const uint8_t* data, uint32_t data_size,
    void* user_data);

aoe_sub_mgr_t* aoe_sub_create(dll_t* dll, uint16_t master, uint16_t slave);

void aoe_sub_destroy(aoe_sub_mgr_t* mgr);

int aoe_sub_add(aoe_sub_mgr_t* mgr,
    uint32_t index_group, uint32_t index_offset,
    uint32_t length, uint32_t trans_mode,
    uint32_t max_delay, uint32_t cycle_time,
    aoe_data_callback_t callback, void* user_data);

int aoe_sub_remove(aoe_sub_mgr_t* mgr, int subscription_id);

int aoe_sub_remove_all(aoe_sub_mgr_t* mgr);

int aoe_sub_count(aoe_sub_mgr_t* mgr);

typedef struct {
    uint16_t slave_index;
    uint8_t  drive_number;
    uint16_t idn;
    uint8_t* new_value;
    int      new_value_size;
    uint8_t* old_value;
    int      old_value_size;
} soe_notification_t;

typedef struct soe_notifier soe_notifier_t;

typedef void (*soe_notification_callback_t)(const soe_notification_t* event, void* user_data);

soe_notifier_t* soe_notifier_create(dll_t* dll, uint16_t master, uint16_t slave, uint8_t drive);

void soe_notifier_destroy(soe_notifier_t* notifier);

int soe_enable_notification(soe_notifier_t* notifier, uint16_t idn, int timeout_ms);

void soe_disable_notification(soe_notifier_t* notifier, uint16_t idn);

void soe_disable_all_notifications(soe_notifier_t* notifier);

int soe_poll_notifications(soe_notifier_t* notifier, int timeout_ms,
                                  soe_notification_callback_t callback, void* user_data);

void soe_notification_free(soe_notification_t* event);

int aoe_read_coe(dll_t* dll, uint16_t master, uint16_t slave,
                        uint16_t index, uint8_t subindex, uint32_t read_length,
                        uint8_t** read_data, uint32_t* bytes_read, int timeout_us);

int aoe_write_coe(dll_t* dll, uint16_t master, uint16_t slave,
                          uint16_t index, uint8_t subindex,
                          const uint8_t* data, uint32_t data_len, int timeout_us);

int aoe_read_soe(dll_t* dll, uint16_t master, uint16_t slave,
                        uint32_t idn, uint32_t read_length,
                        uint8_t** read_data, uint32_t* bytes_read, int timeout_us);

int aoe_write_soe(dll_t* dll, uint16_t master, uint16_t slave,
                          uint32_t idn, const uint8_t* data, uint32_t data_len, int timeout_us);

typedef struct pdo_monitor pdo_monitor_t;

typedef void (*pdo_change_callback_t)(uint16_t master, uint16_t slave,
    const uint8_t* old_data, const uint8_t* new_data,
    uint32_t size, void* user_data);

pdo_monitor_t* pdo_monitor_create(dll_t* dll, uint16_t master);

void pdo_monitor_destroy(pdo_monitor_t* mon);

int pdo_monitor_watch(pdo_monitor_t* mon, uint16_t slave,
                             pdo_change_callback_t callback, void* user_data);

void pdo_monitor_check(pdo_monitor_t* mon);

#ifdef __cplusplus
}
#endif

#endif
