/**
 * @file welding.h
 * @brief 焊接应用相关接口
 * @copyright Copyright (C) 2024 ROKAE (Beijing) Technology Co., LTD. All Rights Reserved.
 * Information in this file is the intellectual property of Rokae Technology Co., Ltd,
 * And may contains trade secrets that must be stored and viewed confidentially.
 */

#ifndef XCORESDK_INCLUDE_ROKAE_WELDING_H_
#define XCORESDK_INCLUDE_ROKAE_WELDING_H_

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
#pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "base.h"
#include "data_types.h"
#include <vector>

namespace rokae {

 // forward declarations
 class XService;

 /**
  * @brief 激光跟踪参数
  */
 struct LaserTrackParams {
   double vel_max { 0.01 }; ///< 跟踪偏移值的最大速度, 单位: 米/秒, 范围(0, +∞), 建议值: 0.01m/s
   double acc_max { 0.2 }; ///< 跟踪偏移值的最大加速度, 单位: 米/秒^2, 范围(0, +∞), 建议值: 0.2m/s^2
   double jerk_max { 2.0 }; ///< 跟踪偏移值的最大加加速度, 单位: 米/秒^3, 范围(0, +∞), 建议值: 2.0m/s^3
   double filter_window_size { 5.0 }; ///< 激光器数据中值滤波窗口大小, 范围[0, 10], 建议值: 5.0 (过大产生滤波滞后)
   double filter_data { 0.2 }; ///< 激光器数据指数滑动滤波因子，范围[0, 1], 建议值: 0.2
   double filter_track { 0.2 }; ///< 跟踪偏移值指数滑动滤波因子，范围[0, 1], 建议值: 0.2
   double delay_time { 0.06 }; ///< 激光器数据延迟时间，用于与TCP数据对齐, 单位: 秒, 范围[0, +∞]。不同激光器延迟时间不同，明图建议值为0.06s，创想建议值为0.12s
   double cycle_time { 0.05 }; ///< 激光器数据刷新周期，与addLaserTrackOffset()调用周期保持一致，建议为激光器允许的最大刷新周期，范围[0, 0.5]s
   bool dir_y_enable { true }; ///< 焊道坐标系Y方向是否启用跟踪偏移
   double dir_y_max { 0.02 }; ///< 焊道坐标系Y方向最大跟踪偏移值，超过该值停止跟踪和运动，范围[0, +∞]m
   bool dir_z_enable { true }; ///< 焊道坐标系Z方向是否启用跟踪偏移
   double dir_z_max { 0.02 }; ///< 焊道坐标系Z方向最大跟踪偏移值，超过该值停止跟踪和运动，范围[0, +∞]m
   std::string coord_feature { "yz" }; ///< 激光器坐标系的方向特征，需要在跟踪前调用。目前支持：1) "xz" - 创想为XZ有效，激光器的写入有效数据为(X,0,Z)；
                              ///< 2) "yz" - 明图为YZ有效，激光器的写入有效数据为(0,Y,Z)
 };

 /**
  * @brief 摆动形状
  */
 enum WeavingType {
   ZIGZAG, ///< 三角摆动
   SINE,   ///< 正弦摆动
   CIRCULAR///< 圆弧摆动
 };

 /**
  * @brief 停留类型
  */
 enum DwellType {
   ROBOT_STAY,///< 机器人停留
   WEAVE_STOP ///< 摆动停留
 };

 /**
  * @brief 摆动参数
  */
 struct WeaveParams {
   double frequency{0.1};                          ///< 摆动频率，单位: Hz, 范围: [0.1, 5]
   double amplitude{0.0001};                       ///< 幅度，单位: 米, 范围: [0.0001, 0.05]
   WeavingType weaveing_type{ZIGZAG};              ///< 摆动形状
   DwellType dwell_type{ROBOT_STAY};               ///< 停留类型. 圆弧摆需要设为摆动停留
   std::array<double, 3> dwell_time{0.0, 0.0, 0.0};///< 停留时间，[左, 中, 右]，单位: 秒，范围: >=0
   double inclination_angle{0};                    ///< 倾斜角度，单位: 弧度
   std::array<double, 2> elevation_angle{0, 0};    ///< 仰角，[左，右]，单位: 弧度
   double radius{0.001};                           ///< 圆弧摆动半径，单位: 米
   bool phase_invert{false};                       ///< 波形反向.true - 向右或逆时针摆动,false - 向左或顺时针摆动
 };

 class XCORE_API BaseWelding: public Base<BaseWelding> {
  public:

   /// @cond DO_NOT_DOCUMENT
   explicit BaseWelding(std::shared_ptr<XService> rpc);
   virtual ~BaseWelding();
   /// @endcond

   /**
    * @brief 开始/停止摆动。开始摆动时可设置摆动参数
    * @param[in] enable true - 开始 | false - 关闭
    * @param[out] ec 错误码
    * @param[in] frequency 摆动频率, 范围[0.1, 5], 单位: Hz
    * @param[in] amplitude 单侧摆动幅值, 范围[0.0001, 0.05], 单位: 米
    * @param[in] dwell_time 停留时间 [左, 中, 右]，目前只支持左和右, 范围[0, 2], 单位: 秒
    */
   void setWeave(bool enable, error_code &ec, double frequency = 0, double amplitude = 0,
                 const std::array<double, 3> &dwell_time = {}) noexcept;

   /**
    * @brief 设置摆动加速度和加加速度
    * @param[in] acceleration 加速度
    * @param[in] jerk 加加速度
    * @param[out] ec 错误码
    */
   void setSwingMotion(double acceleration, double jerk, error_code &ec) noexcept;

   /**
    * @brief 设置摆动参数，支持在摆动过程中调整
    * @param[in] frequency 摆动频率, 范围[0.1, 5], 单位: Hz
    * @param[in] amplitude 单侧摆动幅值, 范围[0.0001, 0.05], 单位: 米
    * @param[out] ec 错误码
    * @param[in] dwell_time 停留时间 [左, 中, 右]，目前只支持左和右，范围[0, 2], 单位: 秒
    * @param[in] weavingType 摆动形状
    */
   void setWeaveParameters(double frequency, double amplitude, error_code &ec,
                         const std::array<double, 3> &dwell_time = {}, WeavingType weavingType = ZIGZAG) noexcept;
                  
   /**
    * @brief 设置摆动参数，支持在摆动过程中调整
    * @param[in] weaveParams 摆动参数
    * @param[out] ec 错误码
    */
   void setWeaveParameters(WeaveParams weaveParams, error_code &ec) noexcept;

   /**
    * @brief 多层多道偏移 - 计算直线轨迹的偏移。偏移量相对于焊道坐标系，焊道坐标系的Z轴方向为末端工具Z轴方向
    * @param[out] start 起始点, 返回偏移后的结果(工具相对于工件)。
    * @param[in] start_offs 起始点偏移量(工具相对于参考坐标系)
    * @param[out] target 目标点, 返回偏移后的结果(工具相对于工件)
    * @param[in] target_offs 目标点偏移量(工具相对于参考坐标系)
    * @param[out] ec 错误码
    */
   void calcWeldOffset(Frame &start, const Frame &start_offs,
                       Frame &target, const Frame &target_offs, error_code &ec) noexcept;

   /**
    * @brief 多层多道偏移 - 计算圆弧轨迹偏移。偏移量相对于焊道坐标系，焊道坐标系的Z轴方向为末端工具Z轴方向
    * @param[out] start 起始点, 返回偏移后的结果(工具相对于工件)。
    * @param[in] start_offs 起始点偏移量(工具相对于参考坐标系)
    * @param[out] target 目标点, 返回偏移后的结果(工具相对于工件)。
    * @param[in] target_offs 目标点偏移量(工具相对于参考坐标系)
    * @param[out] aux 圆弧辅助点, 返回偏移后的结果(工具相对于工件)。
    * @param[in] aux_offs 圆弧辅助点偏移量(工具相对于参考坐标系)
    * @param[out] ec 错误码
    */
   void calcWeldOffset(Frame &start, const Frame &start_offs,
                       Frame &target, const Frame &target_offs,
                       Frame &aux, const Frame &aux_offs, error_code &ec) noexcept;

   /**
    * @brief 多层多道偏移 - 计算直线轨迹偏移。偏移量的参考坐标系并非焊道坐标系，X,Y轴采用焊道坐标系，而Z轴由X,Y轴叉乘得到。
    * @param[out] start 起始点, 返回偏移后的结果(工具相对于工件)。
    * @param[in] start_offs 起始点偏移量(工具相对于参考坐标系)
    * @param[out] target 目标点, 返回偏移后的结果(工具相对于工件)。
    * @param[in] target_offs 目标点偏移量(工具相对于参考坐标系)
    * @param[out] ec 错误码
    */
   void calcWeldOffsetCalculatedZ(Frame &start, const Frame &start_offs,
                       Frame &target, const Frame &target_offs, error_code &ec) noexcept;

   /**
    * @brief 多层多道偏移 - 计算圆弧轨迹偏移。偏移量的参考坐标系并非焊道坐标系，X,Y轴采用焊道坐标系，而Z轴由X,Y轴叉乘得到。
    * @param[out] start 起始点, 返回偏移后的结果(工具相对于工件)。
    * @param[in] start_offs 起始点偏移量(工具相对于参考坐标系)
    * @param[out] target 目标点, 返回偏移后的结果(工具相对于工件)。
    * @param[in] target_offs 目标点偏移量(工具相对于参考坐标系)
    * @param[out] aux 圆弧辅助点, 返回偏移后的结果(工具相对于工件)。
    * @param[in] aux_offs 圆弧辅助点偏移量(工具相对于参考坐标系)
    * @param[out] ec 错误码
    */
   void calcWeldOffsetCalculatedZ(Frame &start, const Frame &start_offs,
                       Frame &target, const Frame &target_offs,
                       Frame &aux, const Frame &aux_offs, error_code &ec) noexcept;

   /**
    * @brief 多层多道辅助接口 - 根据指定的直线轨迹起点和终点，以及两个偏移方向，按指定的偏移距离进行坐标系转换，
    * 使偏移量相对的坐标系与calcWeldOffsetCalculatedZ()接口中的一致
    * @param[in] start 直线轨迹起点
    * @param[in] target 直线轨迹终点
    * @param[in] teach_point_1 示教点1
    * @param[in] offset_1 沿示教点1所在方向的偏移距离
    * @param[in] teach_point_2 示教点2
    * @param[in] offset_2 沿示教点2所在方向的偏移距离
    * @param[out] offset_start 直线轨迹起点偏移量，与calcWeldOffsetCalculatedZ()接口中偏移量的参考坐标系一致
    * @param[out] offset_target 直线轨迹终点偏移量，与calcWeldOffsetCalculatedZ()接口中偏移量的参考坐标系一致
    * @param[out] ec 计算失败的情况:
    *     1) 直线轨迹过短
    *     2) 示教点离焊道距离过近
    *     3) 示教点距离起点或终点过近
    */
   void transformOffsetByTeaching(const Frame &start, const Frame &target,
                                  const Frame &teach_point_1, double offset_1,
                                  const Frame &teach_point_2, double offset_2,
                                  std::array<double, 3> &offset_start, std::array<double, 3> &offset_target,
                                  error_code &ec) noexcept;

   /**
    * @brief 多层多道偏移 - 多条轨迹，适用于平角焊/平焊/横焊
    * @param[in] way_points 原始路径点。第一个为起始点，后续点为各个目标点
    * @param[in] teach_points 针对平角焊，每个目标点的左右平面示教点。向量长度应为way_points-1
    * @param[in] offsets 各层各道的偏移量，[X_start, X_end, Y, Z, A, B, C]
    * @param[out] error_index 计算失败时，出错的点位下标，从1开始（既返回1代表第一个目标点计算失败）
    * @param[out] ec 计算失败的情况:
    *     1) 259 - 原始路径点个数不足2个，或示教点个数和原始路径点个数不匹配;
    *     2) -50003 - 存在过短轨迹
    *     3) -50519 - 输入数据有误。详见日志，错误原因如下：
    *          a) traj_turn_back 轨迹存在折返；
    *          b) calc_plane_failed 计算焊接左右平面失败；
    *          c) not_in_xz_plane 焊枪不在上下平面内不符合平焊或横焊要求
    *     4) -50206 - 算法失败
    * @return 偏移后的路径点，[道索引][路径点索引]
    */
   std::vector<std::vector<Frame>> calcWeldOffset_Multi(const std::vector<Frame> &way_points,
                                                        const std::vector<Frame> &teach_points,
                                                        const std::vector<std::array<double, 7>> &offsets,
                                                        int &error_index, error_code &ec) noexcept;

   /**
    * @brief 电弧跟踪偏移 - 调整焊道偏移量
    * @param[in] offset [X, Y, Z] 单位: 米
    * @param[out] ec 错误码
    */
   void adjustTrackOffset(const std::array<double, 3> &offset, error_code &ec) noexcept;

   /**
    * @brief 设置摆动过程中DO输出规则。和设置系统IO类似，但不保存设置，重启后需要重新设置。
    * 开始DO输出的时机为通过接口绑定DO号后，非摆动轨迹默认DO全部置0，如果开启摆动，则按照设定规则输出。
    * @param[in] signal DO信号名称，如"DO0_1"
    * @param[in] action 设置对象："pos" - 位置; "vel" - 速度
    * @param[in] value
    *   1) 设置对象为位置：true - 位于左侧为0 | false - 位于右侧为0
    *   2) 设置对象为速度：true - 从左到右置0 | false - 从右到左置0
    * @param[out] ec 错误码: -14501 DO信号不存在; -14510 DO信号为系统IO
    */
   void bindWeaveDo(const std::string &signal, const std::string &action, bool value, error_code &ec) noexcept;

   /**
    * @brief 取消设置摆动过程中DO输出
    * @param[in] signal 要取消设置的DO信号名称。
    * @param[out] ec 错误码
    */
   void unbindWeaveDo(const std::string &signal, error_code &ec) noexcept;

   /**
    * @brief 指令-开始激光跟踪
    * @param[out] ec 错误码
    */
   void startTracking(error_code& ec) noexcept;

   /**
    * @brief 指令-结束激光跟踪
    * @param[out] ec 错误码
    */
   void stopTracking(error_code& ec) noexcept;

   /**
    * @brief 设置添加激光跟踪偏移
    * @param[in] x X方向偏移，单位: 米
    * @param[in] z Z方向偏移，单位: 米
    * @param[in] valid 有效性判断
    * @param[out] ec 错误码
    */
   void addLaserTrackOffset(double x, double z, bool valid, error_code& ec) noexcept;

   /**
    * @brief 设置添加激光跟踪偏移。和addLaserTrackOffset()接口类似，该接口支持Y方向偏移
    * @param[in] x X方向偏移，单位: 米
    * @param[in] y Y方向偏移，单位: 米
    * @param[in] z Z方向偏移，单位: 米
    * @param[in] valid 有效性判断
    * @param[out] ec 错误码
    */
   void addLaserTrackOffset(double x, double y, double z, bool valid, error_code& ec) noexcept;

   /**
    * @brief 设置激光器相对末端工具坐标
    * @param[in] sensor_frame 激光器相对tcp坐标 单位: 米
    * @param[out] ec 错误码
    */
   void setSensorFrame(const Frame& sensor_frame,error_code& ec) noexcept;

   /**
    * @brief [已废弃] 获取偏移目标点
    */
   [[deprecated("not support")]]
   void getLaserOffset(double x, double z, Frame& tar_tcp, error_code& ec)noexcept;

    /**
     * @brief 设置激光跟踪参数。需要在开始跟踪之前调用
     * @param[in] params 激光跟踪参数
     * @param[out] ec 错误码，包括：参数超出范围，机器人运动中无法设置等
     */
   void setLaserTrackParameters(const LaserTrackParams &params, error_code &ec) noexcept;

  XCORESDK_DECLARE_IMPL

 };
} // namespace rokae

#endif //XCORESDK_INCLUDE_ROKAE_WELDING_H_
