package net.northking.cctp.element.desktop.keywords;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import net.northking.cctp.element.core.DeviceDriver;
import net.northking.cctp.element.core.IExecuteContext;
import net.northking.cctp.element.core.IStepTarget;
import net.northking.cctp.element.core.annotation.Argument;
import net.northking.cctp.element.core.annotation.Keyword;
import net.northking.cctp.element.core.annotation.Keywords;
import net.northking.cctp.element.core.annotation.Return;
import net.northking.cctp.element.core.exception.ParamMistakeException;
import net.northking.cctp.element.core.type.DataType;
import net.northking.cctp.element.core.type.InputType;
import net.northking.cctp.element.core.type.ParamScope;
import net.northking.cctp.element.desktop.constants.CmdConstant;
import net.northking.cctp.element.desktop.constants.DesktopConstant;
import net.northking.cctp.element.desktop.constants.ErrorMessageConstant;
import net.northking.cctp.element.desktop.constants.RobotParamConstant;
import net.northking.cctp.element.desktop.dto.EngineResult;
import net.northking.cctp.element.desktop.enums.*;
import net.northking.cctp.element.desktop.service.PublicService;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>Title: DesktopTools</p>
 * <p>Description: </p>
 *
 * @Author chenjianfeng
 * @Version V1.0
 * @Date 2022/11/07
 * <p>Copyright: NorthKing@RPA（C）2020 版权</p>
 */
@Keywords
public class DesktopTools {

    /**
     * 鼠标点击
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param mouseButton 鼠标键
     * @param clickType 点击动作类型
     * @param offsetX 点击坐标偏移X像素
     * @param offsetY 点击坐标偏移Y像素
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 执行后等待
     * @param inputMode 点击模式
     * @param waitForReady 就绪等待
     * @param combinationKey 组合功能键
     * @param deviceDriver 与PC机器连接的webSocket客户端
     */
    @Keyword(alias = "鼠标点击", attributes = "23", commonlyUse = true)
    public void click(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法",type = DataType.STRING,scope = ParamScope.STEP, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "mouseButton",comment = "鼠标键",type = DataType.INTEGER, required = false, enumValues = MouseButtonEnum.class, notNull = false, defaultValue = "1", inputType = InputType.select) Integer mouseButton,
            @Argument(name = "clickType",comment = "点击动作类型",type = DataType.INTEGER, enumValues = ClickTypeEnum.class, defaultValue = "1", inputType = InputType.select) Integer clickType,
            @Argument(name = "offsetX",comment = "点击坐标偏移X像素",type = DataType.INTEGER, required = false, notNull = false, defaultValue = "0", defaultDisplay = false) Integer offsetX,
            @Argument(name = "offsetY",comment = "点击坐标偏移Y像素",type = DataType.INTEGER, required = false, notNull = false, defaultValue = "0", defaultDisplay = false) Integer offsetY,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "inputMode",comment = "点击模式",type = DataType.INTEGER, required = false, enumValues = InputModeEnum.class, notNull = false, defaultValue = "1", inputType = InputType.select, defaultDisplay = false) Integer inputMode,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "combinationKey",comment = "组合功能键",type = DataType.STRING, required = false, enumValues = CombinationKeyEnum.class, notNull = false, inputType = InputType.selects, defaultDisplay = false) String combinationKey,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver,
            IExecuteContext executeContent){

        if (StrUtil.isBlank(id) || CollectionUtil.isEmpty(targets) || ObjectUtil.isNull(clickType)){
            throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
        }
        if (deviceDriver == null){
            throw new ParamMistakeException(ErrorMessageConstant.PC_DRIVER_IS_NULL);
        }
        
        Map<String, Object> baseMap = new HashMap<>(16);
        baseMap.put(RobotParamConstant.SEQ_ID, id);
        baseMap.put(RobotParamConstant.TITLE, title);
        baseMap.put(RobotParamConstant.MOUSE_BUTTON, mouseButton);
        baseMap.put(RobotParamConstant.CLICK_TYPE, clickType);
        baseMap.put(RobotParamConstant.OFFSET_X, offsetX);
        baseMap.put(RobotParamConstant.OFFSET_Y, offsetY);
        baseMap.put(RobotParamConstant.TIMEOUT, timeout);
        baseMap.put(RobotParamConstant.DELAY_BEFORE, delayBefore);
        baseMap.put(RobotParamConstant.DELAY_AFTER, delayAfter);
        baseMap.put(RobotParamConstant.INPUT_MODE, inputMode);
        baseMap.put(RobotParamConstant.WAIT_FOR_READY, waitForReady);
        baseMap.put(RobotParamConstant.COMBINATION_KEY, "");
        baseMap.put(RobotParamConstant.FORM_SELECTOR, formSelector);
        if (StrUtil.isNotEmpty(combinationKey)) {
            if (combinationKey.startsWith("[") && combinationKey.endsWith("]")) {
                baseMap.put(RobotParamConstant.COMBINATION_KEY, StrUtil.join(",", JSON.parseArray(combinationKey)));
            }
        }
        PublicService.parseParamAndSend(targets, deviceDriver, CmdConstant.CMD_SUFFIX_CLICK, baseMap, timeout,
                DesktopConstant.DEFAULT_TIMEUNIT, "", executeContent, false);
    }

    /**
     * 键盘输入
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param setValue 键盘输入的内容
     * @param emptyField 输入前是否清空控件的值
     * @param clickBeforeTyping 输入前点击控件
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 执行后等待
     * @param inputMode 点击模式
     * @param delayBetweenKeys 按键间隔
     * @param typingByDriver 是否键盘驱动输入
     * @param waitForReady 就绪等待
     * @param activeWindow 是否激活窗体
     * @param triggerEvent 是否触发回车事件
     * @param deviceDriver 与PC机器连接的webSocket客户端
     */
    @Keyword(alias = "键盘输入", attributes = "23", commonlyUse = true)
    public void input(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法",type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "setValue",comment = "键盘输入的内容",type = DataType.STRING, scope = ParamScope.VARIABLES, required = false, notNull = false) String setValue,
            @Argument(name = "emptyField", comment = "输入前是否清空控件的值", type = DataType.BOOLEAN, required = false, notNull = false, defaultValue = "true", defaultDisplay = false) Boolean emptyField,
            @Argument(name = "clickBeforeTyping", comment = "输入前点击控件", type = DataType.BOOLEAN, required = false, notNull = false, defaultValue = "true", defaultDisplay = false) Boolean clickBeforeTyping,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "inputMode",comment = "点击模式",type = DataType.INTEGER, required = false, enumValues = InputModeEnum.class, notNull = false, defaultValue = "1", inputType = InputType.select, defaultDisplay = false) Integer inputMode,
            @Argument(name = "delayBetweenKeys", comment = "按键间隔", type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.05", defaultDisplay = false) Float delayBetweenKeys,
            @Argument(name = "typingByDriver", comment = "是否键盘驱动输入", type = DataType.BOOLEAN, required = false, notNull = false, defaultValue = "false", defaultDisplay = false) Boolean typingByDriver,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "activeWindow", comment = "是否激活窗体", type = DataType.BOOLEAN, required = false, notNull = false, defaultValue = "true", defaultDisplay = false) Boolean activeWindow,
            @Argument(name = "triggerEvent", comment = "是否触发回车事件", type = DataType.BOOLEAN, required = false, notNull = false, defaultValue = "false", defaultDisplay = false) Boolean triggerEvent,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "simulateType", comment = "模拟输入类型", type = DataType.INTEGER, required = false, notNull = false, enumValues = SimulateTypeEnum.class, defaultValue = "1", inputType = InputType.select, defaultDisplay = false) Integer simulateType,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver
    ){
        if (StrUtil.isBlank(id)){
            throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
        }
        if (deviceDriver == null){
            throw new ParamMistakeException(ErrorMessageConstant.PC_DRIVER_IS_NULL);
        }
        
        Map<String, Object> baseMap = new HashMap<>(16);
        baseMap.put(RobotParamConstant.SEQ_ID, id);
        baseMap.put(RobotParamConstant.TITLE, title);
        baseMap.put(RobotParamConstant.SET_VALUE, setValue);
        baseMap.put(RobotParamConstant.EMPTY_FIELD, emptyField);
        baseMap.put(RobotParamConstant.CLICK_BEFORE_TYPING, clickBeforeTyping);
        baseMap.put(RobotParamConstant.TIMEOUT, timeout);
        baseMap.put(RobotParamConstant.DELAY_BEFORE, delayBefore);
        baseMap.put(RobotParamConstant.DELAY_AFTER, delayAfter);
        baseMap.put(RobotParamConstant.INPUT_MODE, inputMode);
        baseMap.put(RobotParamConstant.DELAY_BETWEEN_KEYS, delayBetweenKeys);
        baseMap.put(RobotParamConstant.TYPING_BY_DRIVER, typingByDriver);
        baseMap.put(RobotParamConstant.WAIT_FOR_READY, waitForReady);
        baseMap.put(RobotParamConstant.ACTIVE_WINDOW, activeWindow);
        baseMap.put(RobotParamConstant.TRIGGER_EVENT, triggerEvent);
        baseMap.put(RobotParamConstant.FORM_SELECTOR, formSelector);
        baseMap.put(RobotParamConstant.SIMULATE_TYPE, simulateType);

        PublicService.parseParamAndSend(targets, deviceDriver, CmdConstant.CMD_SUFFIX_INPUT,
                baseMap, timeout, DesktopConstant.DEFAULT_TIMEUNIT);
    }

    /**
     * 设置值
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param setValue 设置的值
     * @param waitForReady 就绪等待
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 执行后等待
     * @param deviceDriver 与PC机器连接的webSocket客户端
     */
    @Keyword(alias = "设置值", attributes = "23", commonlyUse = true)
    public void setText(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法",type = DataType.STRING,scope = ParamScope.STEP, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "setValue", comment = "设置的值", scope = ParamScope.VARIABLES,type = DataType.STRING, required = false, notNull = false) String setValue,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver
    ){
        if (StrUtil.isBlank(id) || CollectionUtil.isEmpty(targets)){
            throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
        }
        if (deviceDriver == null){
            throw new ParamMistakeException(ErrorMessageConstant.PC_DRIVER_IS_NULL);
        }
        
        Map<String, Object> baseMap = new HashMap<>(8);
        baseMap.put(RobotParamConstant.SEQ_ID, id);
        baseMap.put(RobotParamConstant.TITLE, title);
        baseMap.put(RobotParamConstant.SET_VALUE, setValue);
        baseMap.put(RobotParamConstant.WAIT_FOR_READY, waitForReady);
        baseMap.put(RobotParamConstant.TIMEOUT, timeout);
        baseMap.put(RobotParamConstant.DELAY_BEFORE, delayBefore);
        baseMap.put(RobotParamConstant.DELAY_AFTER, delayAfter);
        baseMap.put(RobotParamConstant.FORM_SELECTOR, formSelector);

        PublicService.parseParamAndSend(targets, deviceDriver, CmdConstant.CMD_SUFFIX_SET_TEXT,
                baseMap, timeout, DesktopConstant.DEFAULT_TIMEUNIT);
    }

    /**
     * 获取控件值
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param waitForReady 就绪等待
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 执行后等待
     * @param value 组件返回值
     * @param deviceDriver 与PC机器连接的webSocket客户端
     * @param executeContent 执行上下文
     * @return 执行结果
     */
    @Keyword(alias = "获取控件值", attributes = "23", commonlyUse = true)
    @Return(name = "result", comment = "返回值", type = DataType.STRING)
    public String getText(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法",type = DataType.STRING,scope = ParamScope.STEP, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "value", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String value,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver,
            IExecuteContext executeContent
    ){
        if (StrUtil.isBlank(id) || CollectionUtil.isEmpty(targets) || StrUtil.isBlank(value)){
            throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
        }
        if (deviceDriver == null){
            throw new ParamMistakeException(ErrorMessageConstant.PC_DRIVER_IS_NULL);
        }
        
        Map<String, Object> baseMap = new HashMap<>(8);
        baseMap.put(RobotParamConstant.SEQ_ID, id);
        baseMap.put(RobotParamConstant.TITLE, title);
        baseMap.put(RobotParamConstant.WAIT_FOR_READY, waitForReady);
        baseMap.put(RobotParamConstant.TIMEOUT, timeout);
        baseMap.put(RobotParamConstant.DELAY_BEFORE, delayBefore);
        baseMap.put(RobotParamConstant.DELAY_AFTER, delayAfter);
        baseMap.put(RobotParamConstant.FORM_SELECTOR, formSelector);

        EngineResult result = PublicService.parseParamAndSend(targets, deviceDriver, CmdConstant.CMD_SUFFIX_GET_TEXT,
                baseMap, timeout, DesktopConstant.DEFAULT_TIMEUNIT, value, executeContent, false);
        if (ObjectUtil.isNotNull(result) && StrUtil.isNotEmpty(result.getValue())){
            return result.getValue();
        }
        return "";
    }

    /**
     * 判断元素是否存在
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 执行后等待
     * @param waitForReady 就绪等待
     * @param value 组件返回值
     * @param deviceDriver 与PC机器连接的webSocket客户端
     * @param executeContent 执行上下文
     * @return 执行结果
     */
    @Keyword(alias = "判断元素是否存在", attributes = "23")
    @Return(name = "result", comment = "返回值", type = DataType.BOOLEAN)
    public Boolean containElement(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法",type = DataType.STRING,scope = ParamScope.STEP, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "value", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String value,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver,
            IExecuteContext executeContent
    ){
        if (StrUtil.isBlank(id) || CollectionUtil.isEmpty(targets)){
            throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
        }
        if (deviceDriver == null){
            throw new ParamMistakeException(ErrorMessageConstant.PC_DRIVER_IS_NULL);
        }
        
        Map<String, Object> baseMap = new HashMap<>(8);
        baseMap.put(RobotParamConstant.SEQ_ID, id);
        baseMap.put(RobotParamConstant.TITLE, title);
        baseMap.put(RobotParamConstant.WAIT_FOR_READY, waitForReady);
        baseMap.put(RobotParamConstant.TIMEOUT, timeout);
        baseMap.put(RobotParamConstant.DELAY_BEFORE, delayBefore);
        baseMap.put(RobotParamConstant.DELAY_AFTER, delayAfter);
        baseMap.put(RobotParamConstant.FORM_SELECTOR, formSelector);

        EngineResult result = PublicService.parseParamAndSend(targets, deviceDriver, CmdConstant.CMD_SUFFIX_EXISTS,
                baseMap, timeout, DesktopConstant.DEFAULT_TIMEUNIT, value, executeContent, true);
        if (ObjectUtil.isNotNull(result) && StrUtil.isNotEmpty(result.getValue())){
            return ObjectUtil.equal("true", result.getValue());
        }
        return false;
    }

    /**
     * 断言元素是否存在
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 执行后等待
     * @param waitForReady 就绪等待
     * @param value 组件返回值
     * @param deviceDriver 与PC机器连接的webSocket客户端
     * @param executeContent 执行上下文
     * @return 执行结果
     */
    @Keyword(alias = "断言元素是否存在", attributes = "23")
    @Return(name = "result", comment = "返回值", type = DataType.BOOLEAN)
    public Boolean assertElement(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法",type = DataType.STRING,scope = ParamScope.STEP, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "value", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String value,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver,
            IExecuteContext executeContent
    ){
        return containElement(id, title, targets, timeout, delayBefore, delayAfter,
                waitForReady, value, formSelector, deviceDriver, executeContent);
    }

    /**
     * 鼠标拖拽
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param mouseButton 鼠标键
     * @param offsetX 点击坐标偏移X像素
     * @param offsetY 点击坐标偏移Y像素
     * @param offsetRateX 点击坐标偏移X坐标百分比
     * @param offsetRateY 点击坐标偏移Y坐标百分比
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 执行后等待
     * @param waitForReady 就绪等待
     * @param combinationKey 组合功能键
     * @param deviceDriver 与PC机器连接的webSocket客户端
     */
    @Keyword(alias = "鼠标拖拽", attributes = "23", commonlyUse = true)
    public void mouseDrag(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法",type = DataType.STRING,scope = ParamScope.STEP, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "mouseButton",comment = "鼠标键",type = DataType.INTEGER, required = false, enumValues = MouseButtonEnum.class, notNull = false, defaultValue = "1", inputType = InputType.select) Integer mouseButton,
            @Argument(name = "offsetX",comment = "点击坐标偏移X像素",type = DataType.INTEGER, required = false, notNull = false, defaultValue = "0") Integer offsetX,
            @Argument(name = "offsetY",comment = "点击坐标偏移Y像素",type = DataType.INTEGER, required = false, notNull = false, defaultValue = "0") Integer offsetY,
            @Argument(name = "offsetRateX",comment = "点击坐标偏移X坐标百分比",type = DataType.INTEGER, required = false, notNull = false, defaultValue = "0", defaultDisplay = false) Integer offsetRateX,
            @Argument(name = "offsetRateY",comment = "点击坐标偏移Y坐标百分比",type = DataType.INTEGER, required = false, notNull = false, defaultValue = "0", defaultDisplay = false) Integer offsetRateY,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "combinationKey",comment = "组合功能键",type = DataType.LIST, required = false, enumValues = CombinationKeyEnum.class, notNull = false, inputType = InputType.selects, defaultDisplay = false) List<String> combinationKey,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver
    ){
        if (StrUtil.isBlank(id) || CollectionUtil.isEmpty(targets)){
            throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
        }
        if (deviceDriver == null){
            throw new ParamMistakeException(ErrorMessageConstant.PC_DRIVER_IS_NULL);
        }
        
        Map<String, Object> baseMap = new HashMap<>(16);
        baseMap.put(RobotParamConstant.SEQ_ID, id);
        baseMap.put(RobotParamConstant.TITLE, title);
        baseMap.put(RobotParamConstant.MOUSE_BUTTON, mouseButton);
        baseMap.put(RobotParamConstant.OFFSET_X, offsetX);
        baseMap.put(RobotParamConstant.OFFSET_Y, offsetY);
        baseMap.put(RobotParamConstant.OFFSET_RATE_X, offsetRateX);
        baseMap.put(RobotParamConstant.OFFSET_RATE_Y, offsetRateY);
        baseMap.put(RobotParamConstant.WAIT_FOR_READY, waitForReady);
        baseMap.put(RobotParamConstant.TIMEOUT, timeout);
        baseMap.put(RobotParamConstant.DELAY_BEFORE, delayBefore);
        baseMap.put(RobotParamConstant.DELAY_AFTER, delayAfter);
        baseMap.put(RobotParamConstant.COMBINATION_KEY, "");
        if (CollUtil.isNotEmpty(combinationKey)) {
            baseMap.put(RobotParamConstant.COMBINATION_KEY, StrUtil.join(",", combinationKey));
        }
        baseMap.put(RobotParamConstant.FORM_SELECTOR, formSelector);

        PublicService.parseParamAndSend(targets, deviceDriver, CmdConstant.CMD_SUFFIX_MOUSE_DRAG,
                baseMap, timeout, DesktopConstant.DEFAULT_TIMEUNIT);
    }

    /**
     * 鼠标滚动
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param direction 鼠标滚动方向
     * @param offsetValue 鼠标滚动值
     * @param scrollTime 滚动次数
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 执行后等待
     * @param waitForReady 就绪等待
     * @param deviceDriver 与PC机器连接的webSocket客户端
     */
    @Keyword(alias = "鼠标滚动", attributes = "23", commonlyUse = true)
    public void mouseScroll(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法",type = DataType.STRING,scope = ParamScope.STEP, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "direction",comment = "鼠标滚动方向", type = DataType.INTEGER, enumValues = DirectionEnum.class, defaultValue = "2", inputType = InputType.select) Integer direction,
            @Argument(name = "offsetValue",comment = "鼠标滚动值", type = DataType.FLOAT, enumValues = OffsetValueEnum.class, defaultValue = "0.25", inputType = InputType.select) Float offsetValue,
            @Argument(name = "scrollTime",comment = "滚动次数", type = DataType.INTEGER, enumValues = ScrollTimeEnum.class, defaultValue = "1", inputType = InputType.select) Integer scrollTime,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver
    ){
        if (StrUtil.isBlank(id) || CollectionUtil.isEmpty(targets)){
            throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
        }
        if (deviceDriver == null){
            throw new ParamMistakeException(ErrorMessageConstant.PC_DRIVER_IS_NULL);
        }
        
        Map<String, Object> baseMap = new HashMap<>(16);
        baseMap.put(RobotParamConstant.SEQ_ID, id);
        baseMap.put(RobotParamConstant.TITLE, title);
        baseMap.put(RobotParamConstant.OFFSET_VALUE, offsetValue);
        baseMap.put(RobotParamConstant.DIRECTION, direction);
        baseMap.put(RobotParamConstant.SCROLL_TIME, scrollTime);
        baseMap.put(RobotParamConstant.WAIT_FOR_READY, waitForReady);
        baseMap.put(RobotParamConstant.TIMEOUT, timeout);
        baseMap.put(RobotParamConstant.DELAY_BEFORE, delayBefore);
        baseMap.put(RobotParamConstant.DELAY_AFTER, delayAfter);
        baseMap.put(RobotParamConstant.FORM_SELECTOR, formSelector);

        PublicService.parseParamAndSend(targets, deviceDriver, CmdConstant.CMD_SUFFIX_MOUSE_SCROLL,
                baseMap, timeout, DesktopConstant.DEFAULT_TIMEUNIT);
    }

    /**
     * 发送热键
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param selectedKey 按下的键
     * @param clickBeforeTyping 输入前点击控件
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 就绪等待
     * @param waitForReady 就绪等待
     * @param combinationKey 组合功能键
     * @param deviceDriver 与PC机器连接的webSocket客户端
     */
    @Keyword(alias = "发送热键", attributes = "23")
    public void sendHotKey(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法",type = DataType.STRING,scope = ParamScope.STEP, required = false, notNull = false, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "selectedKey", comment = "按下的键", type = DataType.STRING, required = false, enumValues = SelectedKeyEnum.class, notNull = false, inputType = InputType.select) String selectedKey,
            @Argument(name = "clickBeforeTyping", comment = "输入前点击控件", type = DataType.BOOLEAN, required = false, notNull = false, defaultValue = "true", defaultDisplay = false) Boolean clickBeforeTyping,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "combinationKey",comment = "组合功能键",type = DataType.LIST, required = false, enumValues = CombinationKeyEnum.class, notNull = false, inputType = InputType.selects, defaultDisplay = false) List<String> combinationKey,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver
    ){
        if (StrUtil.isBlank(id)){
            throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
        }
        if (deviceDriver == null){
            throw new ParamMistakeException(ErrorMessageConstant.PC_DRIVER_IS_NULL);
        }
        
        Map<String, Object> baseMap = new HashMap<>(16);
        baseMap.put(RobotParamConstant.SEQ_ID, id);
        baseMap.put(RobotParamConstant.TITLE, title);
        baseMap.put(RobotParamConstant.SELECTED_KEY, selectedKey);
        baseMap.put(RobotParamConstant.CLICK_BEFORE_TYPING, clickBeforeTyping);
        baseMap.put(RobotParamConstant.WAIT_FOR_READY, waitForReady);
        baseMap.put(RobotParamConstant.TIMEOUT, timeout);
        baseMap.put(RobotParamConstant.DELAY_BEFORE, delayBefore);
        baseMap.put(RobotParamConstant.DELAY_AFTER, delayAfter);
        baseMap.put(RobotParamConstant.COMBINATION_KEY, "");
        if (CollUtil.isNotEmpty(combinationKey)) {
            baseMap.put(RobotParamConstant.COMBINATION_KEY, StrUtil.join(",", combinationKey));
        }
        baseMap.put(RobotParamConstant.FORM_SELECTOR, formSelector);

        PublicService.parseParamAndSend(targets, deviceDriver, CmdConstant.CMD_SUFFIX_SEND_HOT_KEY,
                baseMap, timeout, DesktopConstant.DEFAULT_TIMEUNIT);
    }

    /**
     * 鼠标悬停
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 执行后等待
     * @param waitForReady 就绪等待
     * @param deviceDriver 与PC机器连接的webSocket客户端
     */
    @Keyword(alias = "鼠标悬停", attributes = "23")
    public void mouseHover(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法",type = DataType.STRING,scope = ParamScope.STEP, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver
    ){
        if (StrUtil.isBlank(id) || CollectionUtil.isEmpty(targets)){
            throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
        }
        if (deviceDriver == null){
            throw new ParamMistakeException(ErrorMessageConstant.PC_DRIVER_IS_NULL);
        }
        
        Map<String, Object> baseMap = new HashMap<>(8);
        baseMap.put(RobotParamConstant.SEQ_ID, id);
        baseMap.put(RobotParamConstant.TITLE, title);
        baseMap.put(RobotParamConstant.WAIT_FOR_READY, waitForReady);
        baseMap.put(RobotParamConstant.TIMEOUT, timeout);
        baseMap.put(RobotParamConstant.DELAY_BEFORE, delayBefore);
        baseMap.put(RobotParamConstant.DELAY_AFTER, delayAfter);
        baseMap.put(RobotParamConstant.FORM_SELECTOR, formSelector);

        PublicService.parseParamAndSend(targets, deviceDriver, CmdConstant.CMD_SUFFIX_MOUSE_HOVER,
                baseMap, timeout, DesktopConstant.DEFAULT_TIMEUNIT);
    }

    /**
     * 勾选组件
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param action 勾选状态
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 执行后等待
     * @param waitForReady 就绪等待
     * @param deviceDriver 与PC机器连接的webSocket客户端
     */
    @Keyword(alias = "勾选组件", attributes = "23")
    public void check(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法",type = DataType.STRING,scope = ParamScope.STEP, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "action", comment = "勾选状态", type = DataType.INTEGER, enumValues = ActionEnum.class, defaultValue = "1", inputType = InputType.select) Integer action,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver
    ){
        if (StrUtil.isBlank(id) || CollectionUtil.isEmpty(targets) || ObjectUtil.isNull(action)){
            throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
        }
        if (deviceDriver == null){
            throw new ParamMistakeException(ErrorMessageConstant.PC_DRIVER_IS_NULL);
        }
        
        Map<String, Object> baseMap = new HashMap<>(16);
        baseMap.put(RobotParamConstant.SEQ_ID, id);
        baseMap.put(RobotParamConstant.TITLE, title);
        baseMap.put(RobotParamConstant.ACTION, action);
        baseMap.put(RobotParamConstant.WAIT_FOR_READY, waitForReady);
        baseMap.put(RobotParamConstant.TIMEOUT, timeout);
        baseMap.put(RobotParamConstant.DELAY_BEFORE, delayBefore);
        baseMap.put(RobotParamConstant.DELAY_AFTER, delayAfter);
        baseMap.put(RobotParamConstant.FORM_SELECTOR, formSelector);

        PublicService.parseParamAndSend(targets, deviceDriver, CmdConstant.CMD_SUFFIX_CHECK,
                baseMap, timeout, DesktopConstant.DEFAULT_TIMEUNIT);
    }

    /**
     * 下拉单选框
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param selection 下拉框选择的值
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 执行后等待
     * @param waitForReady 就绪等待
     * @param deviceDriver 与PC机器连接的webSocket客户端
     */
    @Keyword(alias = "下拉单选框", attributes = "23")
    public void selectItem(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法",type = DataType.STRING,scope = ParamScope.STEP, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "selection", comment = "下拉框选择的值", type = DataType.STRING, required = false, notNull = false, inputType = InputType.select) String selection,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver
    ){
        if (StrUtil.isBlank(id) || CollectionUtil.isEmpty(targets)){
            throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
        }
        if (deviceDriver == null){
            throw new ParamMistakeException(ErrorMessageConstant.PC_DRIVER_IS_NULL);
        }
        
        Map<String, Object> baseMap = new HashMap<>(16);
        baseMap.put(RobotParamConstant.SEQ_ID, id);
        baseMap.put(RobotParamConstant.TITLE, title);
        baseMap.put(RobotParamConstant.SELECTION, selection);
        baseMap.put(RobotParamConstant.WAIT_FOR_READY, waitForReady);
        baseMap.put(RobotParamConstant.TIMEOUT, timeout);
        baseMap.put(RobotParamConstant.DELAY_BEFORE, delayBefore);
        baseMap.put(RobotParamConstant.DELAY_AFTER, delayAfter);
        baseMap.put(RobotParamConstant.FORM_SELECTOR, formSelector);

        PublicService.parseParamAndSend(targets, deviceDriver, CmdConstant.CMD_SUFFIX_SELECT_ITEM,
                baseMap, timeout, DesktopConstant.DEFAULT_TIMEUNIT);
    }

    /**
     * 判断是否包含文本
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 执行后等待
     * @param waitForReady 就绪等待
     * @param value 组件返回值
     * @param deviceDriver 与PC机器连接的webSocket客户端
     * @param executeContent 执行上下文
     * @return 执行结果
     */
    @Keyword(alias = "判断是否包含文本", attributes = "23")
    @Return(name = "result", comment = "返回值", type = DataType.BOOLEAN)
    public Boolean containText(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法",type = DataType.STRING,scope = ParamScope.STEP, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "containText", comment = "判断的文本", type = DataType.STRING,required = false) String containText,
            @Argument(name = "value", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String value,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver,
            IExecuteContext executeContent
    ){
        if (StrUtil.isBlank(id) || CollectionUtil.isEmpty(targets) || StrUtil.isBlank(value)){
            throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
        }
        if (deviceDriver == null){
            throw new ParamMistakeException(ErrorMessageConstant.PC_DRIVER_IS_NULL);
        }
        if (executeContent == null){
            throw new ParamMistakeException(ErrorMessageConstant.EXECUTE_CONTENT_IS_NULL);
        }
        
        Map<String, Object> baseMap = new HashMap<>(8);
        baseMap.put(RobotParamConstant.SEQ_ID, id);
        baseMap.put(RobotParamConstant.TITLE, title);
        baseMap.put(RobotParamConstant.WAIT_FOR_READY, waitForReady);
        baseMap.put(RobotParamConstant.TIMEOUT, timeout);
        baseMap.put(RobotParamConstant.DELAY_BEFORE, delayBefore);
        baseMap.put(RobotParamConstant.DELAY_AFTER, delayAfter);
        baseMap.put(RobotParamConstant.CONTAIN_TEXT, containText);
        baseMap.put(RobotParamConstant.FORM_SELECTOR, formSelector);

        EngineResult result = PublicService.parseParamAndSend(targets, deviceDriver, CmdConstant.CMD_SUFFIX_CONTAIN_TEXT,
                baseMap, timeout, DesktopConstant.DEFAULT_TIMEUNIT, value, executeContent, true);
        if (ObjectUtil.isNotNull(result) && StrUtil.isNotEmpty(result.getValue())){
            return ObjectUtil.equal("true", result.getValue());
        }
        return false;
    }

    /**
     * 断言是否包含文本
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 执行后等待
     * @param waitForReady 就绪等待
     * @param value 组件返回值
     * @param deviceDriver 与PC机器连接的webSocket客户端
     * @param executeContent 执行上下文
     * @return 执行结果
     */
    @Keyword(alias = "断言是否包含文本", attributes = "23")
    @Return(name = "result", comment = "返回值", type = DataType.BOOLEAN)
    public Boolean assertText(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法",type = DataType.STRING,scope = ParamScope.STEP, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "containText", comment = "断言的文本", type = DataType.STRING,required = false) String containText,
            @Argument(name = "value", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String value,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver,
            IExecuteContext executeContent
    ){
        return containText(id, title, targets, timeout, delayBefore, delayAfter, waitForReady,
                containText, value, formSelector, deviceDriver, executeContent);
    }

    /**
     * 识别数字图片验证码
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 执行后等待
     * @param waitForReady 就绪等待
     * @param useClient 验证码使用客户
     * @param value 组件返回值
     * @param deviceDriver 与PC机器连接的webSocket客户端
     * @param executeContent 执行上下文
     * @return 执行结果
     */
    @Keyword(alias = "识别数字图片验证码", attributes = "23")
    @Return(name = "result", comment = "返回值", type = DataType.STRING)
    public String verification(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法",type = DataType.STRING,scope = ParamScope.STEP, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "useClient", comment = "验证码使用客户", type = DataType.STRING, required = false, notNull = false, defaultValue = "全部", defaultDisplay = false) String useClient,
            @Argument(name = "value", comment = "组件返回值", type = DataType.STRING, scope = ParamScope.OUTPUT, defaultValue = "") String value,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver,
            IExecuteContext executeContent
    ){
        if (StrUtil.isBlank(id) || CollectionUtil.isEmpty(targets) || StrUtil.isBlank(value)){
            throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
        }
        if (deviceDriver == null){
            throw new ParamMistakeException(ErrorMessageConstant.PC_DRIVER_IS_NULL);
        }
        if (executeContent == null){
            throw new ParamMistakeException(ErrorMessageConstant.EXECUTE_CONTENT_IS_NULL);
        }
        
        Map<String, Object> baseMap = new HashMap<>(8);
        baseMap.put(RobotParamConstant.SEQ_ID, id);
        baseMap.put(RobotParamConstant.TITLE, title);
        baseMap.put(RobotParamConstant.WAIT_FOR_READY, waitForReady);
        baseMap.put(RobotParamConstant.TIMEOUT, timeout);
        baseMap.put(RobotParamConstant.DELAY_BEFORE, delayBefore);
        baseMap.put(RobotParamConstant.DELAY_AFTER, delayAfter);
        baseMap.put(RobotParamConstant.FORM_SELECTOR, formSelector);
        baseMap.put(RobotParamConstant.USE_CLIENT, useClient);

        EngineResult result = PublicService.parseParamAndSend(targets, deviceDriver, CmdConstant.CMD_SUFFIX_VERIFICATION,
                baseMap, timeout, DesktopConstant.DEFAULT_TIMEUNIT, value, executeContent, false);
        if (ObjectUtil.isNotNull(result) && StrUtil.isNotEmpty(result.getValue())){
            return result.getValue();
        }
        return "";
    }

    /**
     * 屏幕截图
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 执行后等待
     * @param waitForReady 就绪等待
     * @param deviceDriver 与PC机器连接的webSocket客户端
     * @param executeContent 执行上下文
     * @return 执行结果
     */
    @Keyword(alias = "屏幕截图", attributes = "23")
    @Return(name = "result", comment = "返回值", type = DataType.STRING)
    public String takeScreen(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法",type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver,
            IExecuteContext executeContent
    ){
        if (StrUtil.isBlank(id)){
            throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
        }
        if (deviceDriver == null){
            throw new ParamMistakeException(ErrorMessageConstant.PC_DRIVER_IS_NULL);
        }
        if (executeContent == null){
            throw new ParamMistakeException(ErrorMessageConstant.EXECUTE_CONTENT_IS_NULL);
        }

        Map<String, Object> baseMap = new HashMap<>(8);
        baseMap.put(RobotParamConstant.SEQ_ID, id);
        baseMap.put(RobotParamConstant.TITLE, title);
        baseMap.put(RobotParamConstant.WORK_WAY, "1");
        if (CollUtil.isNotEmpty(targets)){
            baseMap.put(RobotParamConstant.WORK_WAY, "2");
        }
        baseMap.put(RobotParamConstant.WAIT_FOR_READY, waitForReady);
        baseMap.put(RobotParamConstant.TIMEOUT, timeout);
        baseMap.put(RobotParamConstant.DELAY_BEFORE, delayBefore);
        baseMap.put(RobotParamConstant.DELAY_AFTER, delayAfter);
        baseMap.put(RobotParamConstant.FORM_SELECTOR, formSelector);

        EngineResult result = PublicService.parseParamAndSend(targets, deviceDriver, CmdConstant.CMD_SUFFIX_TAKE_SCREEN,
                baseMap, timeout, DesktopConstant.DEFAULT_TIMEUNIT, "", executeContent, false);
        if (ObjectUtil.isNotNull(result) && StrUtil.isNotEmpty(result.getValue())){
            return result.getValue();
        }
        return "";
    }

    /**
     * 网银密码框输入
     * @param id 步骤id
     * @param title 步骤主标题
     * @param targets 元素识别方法
     * @param typeValue 键盘输入的内容
     * @param emptyField 输入前是否清空控件值
     * @param timeout 组件超时
     * @param delayBefore 执行前等待
     * @param delayAfter 执行后等待
     * @param waitForReady 就绪等待
     * @param comName 串口号
     * @param baudRate 波特率
     * @param stopBit 停止位
     * @param dataBit 数据位
     * @param checkBit 校验位
     * @param deviceDriver 与PC机器连接的webSocket客户端
     */
    /*@Keyword(alias = "网银密码框输入", attributes = "23")
    public void serialPortInput(
            @Argument(name = "id", comment = "步骤id", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) String id,
            @Argument(name = "title", comment = "步骤主标题", type = DataType.STRING, required = false, notNull = false, scope = ParamScope.STEP, defaultDisplay = false) String title,
            @Argument(name = "targets",comment = "元素识别方法", type = DataType.STRING, scope = ParamScope.STEP, defaultDisplay = false) List<IStepTarget> targets,
            @Argument(name = "typeValue", comment = "键盘输入的内容", type = DataType.STRING) String typeValue,
            @Argument(name = "emptyField", comment = "输入前是否清空控件值", type = DataType.BOOLEAN, required = false, notNull = false, defaultValue = "true", defaultDisplay = false) Boolean emptyField,
            @Argument(name = "timeout",comment = "组件超时", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "10") Integer timeout,
            @Argument(name = "delayBefore",comment = "执行前等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayBefore,
            @Argument(name = "delayAfter",comment = "执行后等待",type = DataType.FLOAT, required = false, notNull = false, defaultValue = "0.35", defaultDisplay = false) Float delayAfter,
            @Argument(name = "waitForReady",comment = "就绪等待",type = DataType.INTEGER, required = false, enumValues = WaitForReadyEnum.class, notNull = false, defaultValue ="1", inputType = InputType.select, defaultDisplay = false) Integer waitForReady,
            @Argument(name = "comName", comment = "串口号", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String comName,
            @Argument(name = "baudRate", comment = "波特率", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "57600", defaultDisplay = false) String baudRate,
            @Argument(name = "stopBit", comment = "停止位", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "1", defaultDisplay = false) String stopBit,
            @Argument(name = "dataBit", comment = "数据位", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "8", defaultDisplay = false) String dataBit,
            @Argument(name = "checkBit", comment = "校验位", type = DataType.INTEGER, required = false, notNull = false, defaultValue = "1", defaultDisplay = false) String checkBit,
            @Argument(name = "formSelector", comment = "窗体元素表达式", type = DataType.STRING, required = false, notNull = false, defaultDisplay = false) String formSelector,
            @Argument(name = "__deviceDriver", comment = "与PC机器连接的webSocket客户端", type = DataType.OBJECT, scope = ParamScope.CONTEXT, defaultDisplay = false) DeviceDriver deviceDriver
            ){
        if (StrUtil.isBlank(id) || CollUtil.isEmpty(targets) || StrUtil.isBlank(typeValue)){
            throw new ParamMistakeException(ErrorMessageConstant.REQUEST_PARAMS_IS_BLANK);
        }
        if (deviceDriver == null){
            throw new ParamMistakeException(ErrorMessageConstant.PC_DRIVER_IS_NULL);
        }
        Map<String, Object> baseMap = new HashMap<>(8);
        baseMap.put(RobotParamConstant.SEQ_ID, id);
        baseMap.put(RobotParamConstant.TITLE, title);
        baseMap.put(RobotParamConstant.TYPE_VALUE, typeValue);
        baseMap.put(RobotParamConstant.EMPTY_FIELD, emptyField);
        baseMap.put(RobotParamConstant.TIMEOUT, timeout);
        baseMap.put(RobotParamConstant.DELAY_BEFORE, delayBefore);
        baseMap.put(RobotParamConstant.DELAY_AFTER, delayAfter);
        baseMap.put(RobotParamConstant.WAIT_FOR_READY, waitForReady);
        baseMap.put(RobotParamConstant.COM_NAME, comName);
        baseMap.put(RobotParamConstant.BAUD_RATE, baudRate);
        baseMap.put(RobotParamConstant.STOP_BIT, stopBit);
        baseMap.put(RobotParamConstant.DATA_BIT, dataBit);
        baseMap.put(RobotParamConstant.CHECK_BIT, checkBit);
        baseMap.put(RobotParamConstant.FORM_SELECTOR, formSelector);

        PublicService.parseParamAndSend(targets, deviceDriver, CmdConstant.CMD_SUFFIX_SERIAL_PORT_INPUT,
                baseMap, timeout, DesktopConstant.DEFAULT_TIMEUNIT);
    }*/
}
