rktplug.js

/**
 * @file rktplug.js
 * @version 2.0.2
 */

if (typeof WebSocket === 'undefined')
    alert('当前浏览器不支持WebSocket,请使用其他较新的专业浏览器!');

/**
 * 默认总命名空间
 * @namespace
 */
const Rokato = {};

/**
 * 工具辅助类方法
 * @namespace
 */
Rokato.Utils = {
    /**
     * 深度递归冻结对象
     */
    deepFreeze(obj) {
        let propNames = Object.getOwnPropertyNames(obj);
        propNames.forEach(function (name) {
            let prop = obj[name];
            if (typeof prop == 'object' && prop !== null)
                Rokato.Utils.deepFreeze(prop);
        });
        return Object.freeze(obj);
    },

    /**
     * 依据字符数量切片字符串
     * @param {string} str 完整字符串
     * @param {number} count 字符数量
     * @returns {Array} 切片后的字符串数组
     */
    sliceCount(str, count) {
        const result = [];
        for (let i = 0; i < str.length; i += count)
            result.push(str.substring(i, i + count));
        return result;
    },

    /**
     * 依据字符数量切片字符串并进制转换为数值
     * @param {string} str 完整字符串
     * @param {number} count 字符数量
     * @param {number} radix 字符串的进制
     * @returns {Array} 切片后的数值数组
     */
    sliceCountToInt(str, count, radix) {
        const result = [];
        for (let i = 0; i < str.length; i += count)
            result.push(parseInt(str.substring(i, i + count), radix));
        return result;
    },

    /**
     * 获取指定数量重复字符
     * @param {string} char 字符
     * @param {number} count 重复数量
     * @returns {string}
     */
    getChars(char, count) {
        let result = '';
        for (let i = 0; i < count; ++i)
            result += char;
        return result;
    },

    /**
     * 修剪清理影响HEX字符串的格式美化字符
     * @param {string} str
     * @returns {string}
     */
    trimHex(str) {
        str = str.trim();
        str = str.toUpperCase();
        str = str.replaceAll('\r', '');
        str = str.replaceAll('\n', '');
        str = str.replaceAll(' ', '');
        str = str.replaceAll('H', '');
        str = str.replaceAll('0X', '');
        str = str.replaceAll('{', '');
        str = str.replaceAll(',', '');
        str = str.replaceAll('}', '');
        return str;
    },

    /**
     * 获取COS应答数据末尾SW十进制错误码
     * @param {string} data COS应答HEX数据
     * @returns {number}
     */
    cosDataSW(data) {
        if (data.length < 4) return 0xFFFF;
        return parseInt(data.substring(data.length - 4), 16);
    },

    /**
     * UTF8字符串转Base64编码
     * @param {string} str UTF8字符串
     * @returns Base64编码字符串 遇到异常返回参数原数据
     */
    utf8ToBase64(str) {
        try {
            return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
                return String.fromCharCode('0x' + p1);
            }));
        } catch (error) {
            return str;
        }
    },

    /**
     * Base64解码转UTF8字符串
     * @param {string} str Base64编码字符串
     * @returns UTF8字符串 遇到异常返回参数原数据
     */
    base64ToUtf8(str) {
        try {
            return decodeURIComponent(atob(str).split('').map(function (c) {
                return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
            }).join(''));
        } catch (error) {
            return str;
        }
    },

    /**
     * UTF8字符串转小写十六进制字符串
     * @param {string} str UTF8字符串
     * @returns 小写十六进制字符串
     */
    utf8ToHex(str) {
        const utf8Array = new TextEncoder().encode(str);
        let result = '';
        for (let i = 0; i < utf8Array.byteLength; ++i) result += utf8Array[i].toString(16).padStart(2, '0');
        return result;
    },

    /**
     * 十六进制字符串转UTF8字符串
     * @param {string} hex 十六进制字符串
     * @returns UTF8字符串
     */
    hexToUtf8(hex) {
        let bytes = new Uint8Array(hex.match(/.{2}/g).map(byte => parseInt(byte, 16)));
        return new TextDecoder().decode(bytes);
    },

    /**
     * ASCII可视字符串转小写十六进制字符串
     * @param {string} str ASCII可视字符串
     * @returns 小写十六进制字符串
     */
    asciiToHex(str) {
        return Array.from(str, c => {
            let ascii = c.charCodeAt(0);
            return ascii < 32 ? '' : ascii.toString(16);
        }).join('');
    },

    /**
     * 十六进制字符串转ASCII可视字符串
     * @param {string} hex 十六进制字符串
     * @returns ASCII可视字符串
     */
    hexToAscii(hex) {
        let str = '', ascii = 0;
        for (let i = 0; i < hex.length; i += 2) {
            ascii = parseInt(hex.substring(i, i + 2), 16)
            if (ascii < 32) continue;
            str += String.fromCharCode(ascii);
        }
        return str;
    },

    /**
     * 32位无符号数乘法运算
     */
    uint32Mul(a, b) {
        const result = a * b;
        return result > 0xFFFFFFFF ? result << 0 >>> 0 : result;
    },

    /**
     * 32位无符号数加法运算
     */
    uint32Add(a, b) {
        const result = a + b;
        return result > 0xFFFFFFFF ? result << 0 >>> 0 : result;
    },

    /**
     * 32位无符号数减法运算
     */
    uint32Sub(a, b) {
        return (a - b) >>> 0;
    },
}

Rokato.Utils.deepFreeze(Rokato.Utils);

/**
 * 加解密算法类命名空间
 * @namespace
 */
Rokato.Crypto = {};

/**
 * TEA加解密算法
 * @namespace
 */
Rokato.Crypto.TEA = {
    /** TEA密钥大小 */
    KEY_SIZE: 16,
    /** TEA标准常数 */
    DELTA: 0x9E3779B9,
    /** TEA块大小 */
    BLOCK_SIZE: 8,
}

/**
 * BTEA(别称XXTEA)加解密算法
 * @namespace
 */
Rokato.Crypto.BTEA = {
    /** BTEA块大小 */
    BLOCK_SIZE: 4,

    /**
     * 加密
     * @param {string} data 十六进制数据 注意字节数必须为4的倍数且不小于8
     * @param {string} key 16字节十六进制密钥
     * @param {number} q 轮次强度 默认0=标准自动计算
     * @returns {string} 加密后的十六进制字符串 参数错误返回空字符串
     */
    enc(data, key, q = 0) {
        let result = ''
        let v = Rokato.Utils.sliceCountToInt(data, 8, 16);
        const n = v.length;
        if (n < 2) return result;
        const k = Rokato.Utils.sliceCountToInt(key, 8, 16);
        if (k.length != 4) return result;

        if (q < 1) q = 6 + 52 / n >>> 0;
        let z = v[n - 1];
        let y;
        let sum = 0;
        let e;
        let p;
        while (--q > -1) {
            sum = Rokato.Utils.uint32Add(sum, Rokato.Crypto.TEA.DELTA);
            e = (sum >>> 2) & 3;
            for (p = 0; p < n - 1; ++p) {
                y = v[p + 1];
                z = Rokato.Utils.uint32Add(v[p], Rokato.Crypto.BTEA.__bteaMX(z, y, sum, k, p, e));
                v[p] = z;
            }
            y = v[0];
            z = Rokato.Utils.uint32Add(v[n - 1], Rokato.Crypto.BTEA.__bteaMX(z, y, sum, k, p, e));
            v[n - 1] = z;
        }

        for (let i = 0; i < n; ++i) result += v[i].toString(16).padStart(8, '0').toUpperCase();
        return result;
    },

    /**
     * 解密
     * @param {string} data 十六进制数据 注意字节数必须为4的倍数且不小于8
     * @param {string} key 16字节十六进制密钥
     * @param {number} q 轮次强度 默认0=标准自动计算
     * @returns {string} 解密后的十六进制字符串 参数错误返回空字符串
     */
    dec(data, key, q = 0) {
        let result = ''
        let v = Rokato.Utils.sliceCountToInt(data, 8, 16);
        const n = v.length;
        if (n < 2) return result;
        const k = Rokato.Utils.sliceCountToInt(key, 8, 16);
        if (k.length != 4) return result;

        if (q < 1) q = 6 + 52 / n >>> 0;
        let z;
        let y = v[0];
        let sum = Rokato.Utils.uint32Mul(q, Rokato.Crypto.TEA.DELTA);
        let e;
        let p;
        while (--q > -1) {
            e = (sum >> 2) & 3;
            for (p = n - 1; p > 0; --p) {
                z = v[p - 1];
                y = Rokato.Utils.uint32Sub(v[p], Rokato.Crypto.BTEA.__bteaMX(z, y, sum, k, p, e));
                v[p] = y;
            }
            z = v[n - 1];
            y = Rokato.Utils.uint32Sub(v[0], Rokato.Crypto.BTEA.__bteaMX(z, y, sum, k, p, e));
            v[0] = y;
            sum = Rokato.Utils.uint32Sub(sum, Rokato.Crypto.TEA.DELTA);
        }

        for (let i = 0; i < n; ++i) result += v[i].toString(16).padStart(8, '0').toUpperCase();
        return result;
    },


    __bteaMX(z, y, sum, k, p, e) {
        let a = (z >>> 5 ^ y << 2) >>> 0;
        let b = (y >>> 3 ^ z << 4) >>> 0;
        const r1 = Rokato.Utils.uint32Add(a, b);
        a = (sum ^ y) >>> 0;
        b = (k[(p & 3) ^ e] ^ z) >>> 0;
        const r2 = Rokato.Utils.uint32Add(a, b);
        return (r1 ^ r2) >>> 0;
    },
}

Rokato.Utils.deepFreeze(Rokato.Crypto);

/**
 * 插件通信核心类
 */
Rokato.RktPlug = class {
    static GERR = {
        OK: { err: 0, msg: '完成' },
        ConnectUrlError: { err: -1, msg: '插件服务地址异常,请核对地址!' },
        ReConnectClose: { err: -2, msg: '尝试重新连接插件中' },
        ConnectClose: { err: -3, msg: '插件连接已关闭!' },
        ConnectFail: { err: -4, msg: '连接失败,请检查插件是否正常启动!' },
        NoConnect: { err: -5, msg: '请先连接登入插件服务后操作!' },
    };

    static GCODE = {
        Login: 0,
        Version: 1,

        RfReader_init: 100,
        RfReader_getDevType: 101,
        RfReader_setDevType: 102,
        RfReader_serialCfg: 103,
        RfReader_serialOnly: 104,
        RfReader_ping: 105,
        RfReader_close: 106,
        RfReader_led: 107,
        RfReader_getVer: 108,
        RfReader_getRfType: 109,
        RfReader_hasRfType: 110,
        RfReader_beep: 111,
        RfReader_rfReset: 112,
        RfReader_setIso14443A: 113,
        RfReader_setIso14443B: 114,
        RfReader_setIso15693: 115,
        RfReader_findId: 116,
        RfReader_loopFindId: 117,
        RfReader_loopEncrypt: 118,
        RfReader_stopLoop: 119,

        MifareClassic_protect: 200,
        MifareClassic_select: 201,
        MifareClassic_deselect: 202,
        MifareClassic_isSelect: 203,
        MifareClassic_getUid: 204,
        MifareClassic_getSak: 205,
        MifareClassic_cardNo: 206,
        MifareClassic_read: 207,
        MifareClassic_write: 208,
        MifareClassic_writeCtl: 209,
        MifareClassic_parsePowerStr: 210,
        MifareClassic_dumpPowerStr: 211,

        CpuCosTypeA_select: 300,
        CpuCosTypeA_deselect: 301,
        CpuCosTypeA_isSelect: 302,
        CpuCosTypeA_getUid: 303,
        CpuCosTypeA_getSak: 304,
        CpuCosTypeA_cardNo: 305,
        CpuCosTypeA_cosCode: 306,
        CpuCosTypeA_cosVer: 307,
        CpuCosTypeA_atsBuf: 308,
        CpuCosTypeA_atsStr: 309,
        CpuCosTypeA_send: 310,

        CosErr_getStr: 400,
        SdtApi_open: 401,
        SdtApi_close: 402,
        SdtApi_reset: 403,
        SdtApi_read: 404,

        Iso15693_setFlags: 500,
        Iso15693_stayQuiet: 501,
        Iso15693_readBlock: 502,
        Iso15693_writeBlock: 503,
        Iso15693_lockBlock: 504,
        Iso15693_readBlocks: 505,
        Iso15693_writeBlocks: 506,
        Iso15693_select: 507,
        Iso15693_resetToReady: 508,
        Iso15693_writeAFI: 509,
        Iso15693_lockAFI: 510,
        Iso15693_writeDSFID: 511,
        Iso15693_lockDSFID: 512,
        Iso15693_getSysInfo: 513,
        Iso15693_getBlockState: 514,

        IcodeSli_enableEAS: 600,
        IcodeSli_disableEAS: 601,
        IcodeSli_lockEAS: 602,
        IcodeSli_checkEAS: 603,
        IcodeSli_passwordProtect: 604,
        IcodeSli_verifyPassword: 605,
        IcodeSli_writePassword: 606,
        IcodeSli_lockPassword: 607,
        IcodeSli_kill: 608,
        IcodeSli_stayQuietPersistent: 609,
        IcodeSli_writeTwoBlock: 610,
        IcodeSli_readAuthStartAddr: 611,
        IcodeSli_writeAuthStartAddr: 612,

        UdpComm_start: 700,
        UdpComm_stop: 701,
        UdpComm_send: 702,
        UdpComm_devApiV1: 703,
    };

    /**
     * 核心接口服务通信对象
     * @param {callback} callback 意外断开通知回调
     * @param {string} url 接口服务地址 默认 ws://127.0.0.1:3080
     */
    constructor(callback, url = "ws://127.0.0.1:3080") {
        this.__url = url;
        this.__onlogout = callback;
    }

    /**
     * 获取启动插件服务自定义协议地址
     * @param {number} port 当端口号>0时则使用更改端口号方式重新启动
     */
    static execUrl(port = 0) {
        let url = 'rktplug://start/?n';
        if (port > 0) url += '&s=restart&p=' + port;
        return url;
    }

    /**
     * 登录接口服务
     * @param {string} user 如用户未修改则默认为 "public"
     * @param {string} pass 如用户未修改则默认为 "Rokato"
     * @returns {Promise}
     */
    async login(user = "public", pass = "Rokato") {
        return new Promise((res) => {
            this.close();
            this.__state = 1;
            this.__user = user;
            this.__pass = pass;
            this.__loginCallback = (data) => {
                res(data);
            };
            try {
                this.__ws = new WebSocket(this.__url);
                this.__ws.onopen = this.__onWsOpen.bind(this);
                this.__ws.onclose = this.__onWsClose.bind(this);
                this.__ws.onmessage = this.__onWsMessage.bind(this);
            } catch (error) {
                this.__onLogin(Rokato.RktPlug.GERR.ConnectUrlError);
            }
        });
    }


    /**
     * 获取当前已登录服务的版本信息
     * @returns {Promise} err/msg + ver/version/build
     */
    async version() {
        return this.asyncSend(Rokato.RktPlug.GCODE.Version, {});
    }


    /**
     * 接口协议发送
     * @param {number} code 接口命令码
     * @param {object} obj 接口参数对象
     * @returns {Promise} 
     */
    async asyncSend(code, obj) {
        return new Promise((res) => {
            this.__send(code, obj, (data) => {
                res(data);
            });
        });
    }

    /**
     * 绑定特殊指令事件回调
     * @param {number} code 特殊接口命令码
     * @param {callback} callback 事件回调
     */
    bindEvent(code, callback) {
        this.__evtCallback[code] = callback;
    }

    /**
     * 判断是否已登录
     * @returns {boolean}
     */
    isLogin() { return this.__state > 2; }

    /**
     * 关闭连接释放资源
     */
    close() {
        this.__onLogin(Rokato.RktPlug.GERR.ReConnectClose);
        if (this.__ws) {
            this.__ws.onopen = null;
            this.__ws.onmessage = null;
            this.__ws.onclose = null;
            this.__ws.close();
            this.__ws = null;
        }
        this.__clearCallback();
    }

    __onWsOpen() {
        this.__state = 2;
        this.__send(Rokato.RktPlug.GCODE.Login, { user: this.__user, pass: this.__pass }, this.__onLogin.bind(this));
    }

    __onWsClose(evt) {
        const isCallback = this.__state > 2 && this.__onlogout != null;
        this.__clearCallback();
        if (this.__state > 1)
            this.__onLogin(Rokato.RktPlug.GERR.ConnectClose);
        else
            this.__onLogin(Rokato.RktPlug.GERR.ConnectFail);
        if (isCallback) this.__onlogout(evt);
    }

    __onWsMessage(evt) {
        try {
            const data = JSON.parse(evt.data);
            if (data.hasOwnProperty('evt')) {
                this.__evtCallback[data.evt](data);
            } else {
                this.__ackCallback[data.ack](data);
                delete this.__ackCallback[data.ack];
            }
        } catch (error) {
            console.log(error);
        }
    }

    __onLogin(data) {
        this.__state = data.err == 0 ? 3 : 0;
        if (this.__loginCallback) {
            this.__loginCallback(data);
            this.__loginCallback = null;
        }
        this.__user = "";
        this.__pass = "";
    }

    __clearCallback() {
        for (const key in this.__ackCallback)
            this.__ackCallback[key](Rokato.RktPlug.GERR.ConnectClose);

        this.__ackCallback = {};
        this.__seq = 0;
    }

    __send(code, obj, callback) {
        while (this.__ws) {
            if (this.__ws.readyState != 1) break;
            ++this.__seq;
            if (this.__seq < 1) this.__seq = 1;
            this.__ackCallback[this.__seq + ''] = callback;
            obj['ack'] = this.__seq.toString();
            obj['code'] = code;
            this.__ws.send(JSON.stringify(obj));
            return;
        }
        callback(Rokato.RktPlug.GERR.NoConnect);
    }

    __onlogout = null;
    __url = '';
    __ws = null;
    __loginCallback = null;
    __user = "";
    __pass = "";
    __state = 0;
    __ackCallback = {};
    __evtCallback = {};
    __seq = 0;
}

Rokato.Utils.deepFreeze(Rokato.RktPlug);

/**
 * 读卡器设备操作类
 */
Rokato.RfReader = class {

    /**
     * Rf读卡器接口
     * @param {RktPlug} plug 插件通信对象
     * @param {number} rfIdx Rf读卡器对象索引
     */
    constructor(plug, rfIdx = 0) {
        this.__plug = plug;
        this.__rfIdx = parseInt(rfIdx);
    }

    /**
     * 获取当前Rf读卡器对象索引
     * @returns {number}
     */
    getRfIdx() { return this.__rfIdx; }

    /**
     * 设置切换Rf读卡器对象索引
     * @param {number} rfIdx Rf读卡器对象索引
     */
    setRfIdx(rfIdx) { this.__rfIdx = parseInt(rfIdx); }

    /**
     * 申请初始化读卡器对象
     * @param {DevType} devType 设备类型
     * @returns {Promise} err/msg
     */
    async init(devType = Rokato.RfReader.DevType.AUTO) {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_init, {
            rfIdx: this.__rfIdx,
            devType: devType,
        });
    }

    /**
    * 获取当前设备类型
    * @returns {Promise} err/msg + retval
    */
    async getDevType() {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_getDevType, { rfIdx: this.__rfIdx });
    }

    /**
    * 切换设备类型
    * @param {DevType} devType 设备类型
    * @returns {Promise} err/msg + retval
    */
    async setDevType(devType = Rokato.RfReader.DevType.AUTO) {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_setDevType, {
            rfIdx: this.__rfIdx,
            devType: devType
        });
    }

    /**
    * 串口设备配置 在使用串口类读卡器前必须最优先调用1次
    * @param {DevType} devType 设备类型参考RfReader.DevType 仅支持SERIAL类读卡器
    * @param {string} addr 端口地址 例如 "/dev/ttyS3"
    * @returns {Promise} err/msg + retval
    */
    async serialCfg(devType, addr = "") {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_serialCfg,
            {
                rfIdx: this.__rfIdx,
                devType: devType,
                addr: addr
            });
    }

    /**
     * 配置是否占用串口通信唯一连接 非串口设备无意义
     * @param {boolean} only 默认true=保持通信,false=仅在操作时占用串口通信
     * @returns {Promise} err/msg
     */
    async serialOnly(only = true) {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_serialOnly, {
            rfIdx: this.__rfIdx,
            only: only
        });
    }

    /**
     * 检查设备连接是否正常
     * @param {boolean} open 是否自动打开设备
     * @returns {Promise} err/msg + retval(-1=已被占用/1=已连接/0=连接失败)
     */
    async ping(open = true) {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_ping, {
            rfIdx: this.__rfIdx,
            open: open
        });
    }

    /**
    * 主动关闭设备连接占用
    * @returns {Promise} err/msg
    */
    async close() {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_close, { rfIdx: this.__rfIdx });
    }

    /**
    * 控制LED灯 仅支持带灯读卡器
    * @param {number} Y 黄灯 -1=忽略 0=熄灭 1=点亮
    * @param {number} G 绿灯 -1=忽略 0=熄灭 1=点亮
    * @returns {Promise} err/msg + retval
    */
    async led(Y = -1, G = -1) {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_led, { rfIdx: this.__rfIdx, Y: Y, G: G });
    }

    /**
    * 获取读卡器固件版本信息描述字符串
    * @returns {Promise} err/msg + retval/ver
    */
    async getVer() {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_getVer, { rfIdx: this.__rfIdx });
    }

    /**
    * 获取当前RF协议类型 参考RfReader.RfType 如果为0则代表设备暂未切换具体的协议模式
    * @returns {Promise} err/msg + retval
    */
    async getRfType() {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_getRfType, { rfIdx: this.__rfIdx });
    }

    /**
    * 设备当前是否具备指定的RF协议能力 需要当前读卡器已连接
    * @param {RfType} rfType 协议类型参考RfReader.RfType 如果设备不支持且无法切换协议模式返回false
    * @param {boolean} forceSwitch 默认false仅当前初始为0暂未切换时允许切换,true则为强制切换但会影响感应区RFID状态
    * @returns {Promise} err/msg + retval
    */
    async hasRfType(rfType, forceSwitch = false) {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_hasRfType, {
            rfIdx: this.__rfIdx,
            rfType: rfType,
            forceSwitch: forceSwitch
        });
    }

    /**
     * 蜂鸣器
     * @param {number} ms 持续毫秒
     * @returns {Promise} err/msg + retval
     */
    async beep(ms = 15) {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_beep, { rfIdx: this.__rfIdx, ms: ms });
    }

    /**
    * 射频感应天线复位重启 重置所有RFID状态
    * @returns {Promise} err/msg + retval
    */
    async rfReset() {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_rfReset, { rfIdx: this.__rfIdx });
    }

    /**
    * 设置Iso14443A协议寻卡
    * @param {boolean} enable 是否启用 默认为禁用状态
    * @param {number} limit 限制最大查找ID数量 默认0代表不限制
    * @returns {Promise} err/msg
    */
    async setIso14443A(enable, limit = 0) {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_setIso14443A, {
            rfIdx: this.__rfIdx,
            enable: enable,
            limit: limit
        });
    }

    /**
    * 设置Iso14443B协议寻卡
    * @param {boolean} enable 是否启用 默认为禁用状态
    * @param {number} limit 限制最大查找ID数量 默认0代表不限制
    * @param {number} afi 过滤AFI
    * @param {number} slotsNum 时隙槽数0~4分别代表Slots1/2/4/8/16
    * @returns {Promise} err/msg
    */
    async setIso14443B(enable, limit = 0, afi = 0x00, slotsNum = 0) {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_setIso14443B, {
            rfIdx: this.__rfIdx,
            enable: enable,
            limit: limit,
            afi: afi,
            slotsNum: slotsNum
        });
    }

    /**
    * 设置Iso15693协议寻卡
    * @param {boolean} enable 是否启用 默认为禁用状态
    * @param {number} limit 限制最大查找ID数量 默认0代表不限制
    * @param {number} afi 如果AFI>0xFF则忽略AFI过滤
    * @param {boolean} nb16Slots 时隙槽数false=1:slot,true=16:slots
    * @returns {Promise} err/msg
    */
    async setIso15693(enable, limit = 0, afi = 256, nb16Slots = false) {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_setIso15693, {
            rfIdx: this.__rfIdx,
            enable: enable,
            limit: limit,
            afi: afi,
            nb16Slots: nb16Slots
        });
    }

    /**
     * 查找初始状态的ID 如需重复查找非初始状态ID需rfReset重置状态
     * @returns {Promise} err/msg + retval(数组对象各协议最大示例值如下)
     * {
     *  "retval": [
     *    {
     *        "rf_type": 1,
     *        "uid": "FFFFFFFFFFFFFFFFFFFF",
     *        "atqa": 65535,
     *        "sak": 255
     *    },
     *    {
     *        "rf_type": 2,
     *        "pupi": "FFFFFFFF",
     *        "afi": 255,
     *        "number": 255,
     *        "uid": "FFFFFFFFFFFFFFFFFFFFFF"
     *    },
     *    {
     *        "rf_type": 4,
     *        "dsfid": 255,
     *        "uid": "FFFFFFFFFFFFFFFF"
     *    }
     *  ]
     * }
     */
    async findId() {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_findId, { rfIdx: this.__rfIdx });
    }

    /**
     * 自动等待循环寻卡 注意重复调用将自动停止上一次,同时之后调用其他某些读卡器方法也会自动停止
     * @param {number} ms 循环间隔毫秒
     * @returns {Promise} err/msg + retval(数组对象参考findId)
     */
    async waitFindId(ms = 100) {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_loopFindId,
            {
                rfIdx: this.__rfIdx,
                ms: ms,
                evt: false
            }
        );
    }

    /**
     * 自动无限循环寻卡 注意重复调用将取消上次绑定的回调,同时之后调用其他某些读卡器方法也会自动停止
     * @param {callback} callback 事件回调evt参数对象包含retval(数组对象参考findId)
     * @param {number} ms 循环间隔毫秒
     * @returns {Promise} err/msg
     */
    async loopFindId(callback, ms = 100) {
        this.__plug.bindEvent(Rokato.RktPlug.GCODE.RfReader_loopFindId, callback);
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_loopFindId,
            {
                rfIdx: this.__rfIdx,
                ms: ms,
                evt: true
            }
        );
    }

    /**
     * 自动无限循环加密 注意重复调用将取消上次绑定的回调,同时之后调用其他某些读卡器方法也会自动停止
     * @param {callback} callback 事件回调evt参数对象包含retval(加密结果)/cardNo(兼容卡号)/wait(等待结果)/tip(提示反馈)
     * @param {Array} oldPwd 数组40个扇区原密钥,如果对应扇区密钥为""则代表跳过此扇区加密
     * @param {number} ms 循环间隔毫秒
     * @returns {Promise} err/msg
     */
    async loopEncrypt(callback, oldPwd, ms = 100) {
        this.__plug.bindEvent(Rokato.RktPlug.GCODE.RfReader_loopEncrypt, callback);
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_loopEncrypt,
            {
                rfIdx: this.__rfIdx,
                oldPwd: oldPwd,
                ms: ms
            }
        );
    }

    /**
    * 主动停止Loop循环处理任务
    * @returns {Promise} err/msg
    */
    async stopLoop() {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_stopLoop, { rfIdx: this.__rfIdx });
    }

    /**
     * 猜测ATQA芯片类型字符串描述
     * @param {number} atqa ISO14443A响应的ATQA
     */
    static guessAtqaType(atqa) {
        switch (atqa) {
            case 0x0004:
                return "Mifare std. 1k(S50)";

            case 0x0002:
                return "Mifare std. 4k(S70)";

            case 0x0044:
                return "UltraLight";

            case 0x0010:
                return "MIFARE LIGHT";

            case 0x0008:
                return "MIFARE PRO";

            case 0x00B4:
                return "MIFARE PLUS";

            case 0x0344:
                return "DESFIRE";

            case 0x0005:
                return "FM005";

            case 0x3300:
                return "SHC1102";

            default:
                break;
        }
        return "Unknown";
    }

    /**
     * UID前4字节转换为十进制小端序10位的兼容卡号
     * @param {string} uid 不低于4字节的UID字符串
     */
    static uidToCardNo(uid) {
        if (uid.length < 8) return '';
        const arr = Rokato.Utils.sliceCount(uid.substring(0, 8), 2);
        if (arr.length != 4) return '';
        return parseInt(arr[3] + arr[2] + arr[1] + arr[0], 16).toString().padStart(10, '0');
    }

    __plug = null;
    __rfIdx = 0;
}

/**
 * 设备类型
 * @readonly
 * @enum {number}
 */
Rokato.RfReader.DevType = {
    /** 自动匹配 优先顺序HID>USB>SERIAL(YZ>HC>MU>CU) */
    AUTO: 0x00,
    HC_HID: 0x01,
    CU_SERIAL: 0x02,
    YZ_CH9326: 0x03,
    MU_HID: 0x04,
    YZ_SERIAL: 0x05,
    CU_CH340: 0x06,
    /** AE-API第三方读卡器接口 */
    AE_API: 0xFF,
}

/**
 * 射频协议类型
 * @readonly
 * @enum {number}
 */
Rokato.RfReader.RfType = {
    None: 0,
    Iso14443A: 1,
    Iso14443B: 2,
    Iso15693: 4,
}

Rokato.Utils.deepFreeze(Rokato.RfReader);

/**
 * Mifare-Classic操作类
 */
Rokato.MifareClassic = class {
    /** 块字节大小 */
    static BLOCK_SIZE = 16;

    /** 密匙字节大小 */
    static KEY_SIZE = 6;

    /** 权限位字节大小 */
    static POWER_SIZE = 4;

    /** 最大块数量 */
    static MAX_BLOCK_COUNT = 256;

    /** 最大扇区数量 */
    static MAX_SECTOR_COUNT = 40;

    /** 常见S50扇区数量 */
    static S50_SECTOR_COUNT = 16;

    /** 常见S50扇区块数量 */
    static S50_SECTOR_BLOCK_COUNT = 4;

    /** S70最大扇区块数量 */
    static S70_SECTOR_BLOCK_COUNT = 16;

    /** 16个扇区每扇区4块容量(常见S50) */
    static SIZE_1K = 1024;

    /** 32个扇区每扇区4块容量 */
    static SIZE_2K = 2048;

    /** 40个扇区,前32每扇区4块,后8每扇区16块容量(少见S70) */
    static SIZE_4K = 4096;

    /** 5个扇区每扇区4块容量 */
    static SIZE_MINI = 320;

    /** 出厂默认密钥 */
    static KEY_DEFAULT = "FFFFFFFFFFFF";

    /** 标准默认控制码 */
    static POWER_DEFAULT = "FF078069";

    /** 推荐默认权限控制码可后悔版,012块AB密匙可读可写 控制块AB密匙不可读 A认证不可写入 B认证可改密匙 B认证可改权限位 */
    static POWER_DEFAULT_PRO = "7F078869";

    /** 特殊权限控制码,写入数据后锁定不可更改 AB密匙认证可读取数据 B认证可修改控制块 */
    static POWER_WLOCK_PRO = "0F078F69";

    /**
     * Mifare Classic操作接口
     * @param {RfReader} rfReader 读卡器设备对象
     */
    constructor(rfReader) {
        this.__rfReader = rfReader;
    }

    /**
     * 更改控制块保护 默认为不允许写入控制块
     * @param {boolean} isWrite 是否允许写控制块数据
     * @param {boolean} isCheck 是否检查写入权限位正确性 强烈建议开启防止写坏扇区
     * @returns {Promise} err/msg
     */
    async protect(isWrite = false, isCheck = true) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.MifareClassic_protect, {
            rfIdx: this.__rfReader.__rfIdx,
            isWrite: isWrite,
            isCheck: isCheck
        });
    }

    /**
     * 选择UID 只有选择成功后才能进行操作
     * @param {string} uid 十六进制UID数据
     * @returns {Promise} err/msg + retval
     */
    async select(uid) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.MifareClassic_select, {
            rfIdx: this.__rfReader.__rfIdx,
            uid: uid
        });
    }

    /**
     * 取消选择
     * @returns {Promise} err/msg
     */
    async deselect() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.MifareClassic_deselect, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 是否已选择
     * @returns {Promise} err/msg + retval
     */
    async isSelect() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.MifareClassic_isSelect, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 获取当前的UID数据
     * @returns {Promise} err/msg + retval
     */
    async getUid() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.MifareClassic_getUid, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 获取当前的SAK码
     * @returns {Promise} err/msg + retval
     */
    async getSak() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.MifareClassic_getSak, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 获取前4字节兼容卡号
     * @returns {Promise} err/msg + retval
     */
    async cardNo() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.MifareClassic_cardNo, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 读取扇区数据
     * @param {boolean} reSelect 是否需要失败后支持继续读写业务操作
     * @param {number} sector 扇区索引
     * @param {string} pwd 扇区认证密匙
     * @param {number} pos 要读取扇区的起始块
     * @param {number} num 读取块数量 默认0自动计算最大可读数量
     * @param {boolean} keyB false=A密匙认证 true=B密匙认证
     * @param {boolean} fill 是否以当前认证密匙智能填充控制块密匙数据
     * @returns {Promise} err/msg + retval(成功读取数据大小或负数错误码)/data
     */
    async read(reSelect, sector, pwd, pos = 0, num = 0, keyB = false, fill = true) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.MifareClassic_read, {
            rfIdx: this.__rfReader.__rfIdx,
            reSelect: reSelect,
            sector: sector,
            pwd: pwd,
            pos: pos,
            num: num,
            keyB: keyB,
            fill: fill
        });
    }

    /**
     * 写入扇区数据
     * @param {boolean} reSelect 是否需要失败后支持继续读写业务操作
     * @param {number} sector 扇区索引
     * @param {string} pwd 扇区认证密匙
     * @param {string} data 写入数据 注意十六进制数据格式与长度必须正确
     * @param {number} pos 要写入扇区的起始块
     * @param {number} num 写入块数量 默认0自动计算最大可写数量
     * @param {boolean} keyB false=A密匙认证 true=B密匙认证
     * @returns {Promise} err/msg + retval(成功写入数据大小或负数错误码)
     */
    async write(reSelect, sector, pwd, data, pos = 0, num = 0, keyB = false) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.MifareClassic_write, {
            rfIdx: this.__rfReader.__rfIdx,
            reSelect: reSelect,
            sector: sector,
            pwd: pwd,
            data: data,
            pos: pos,
            num: num,
            keyB: keyB
        });
    }

    /**
     * 写入控制块数据-便利版
     * @param {number} sector 扇区索引
     * @param {string} keyA 扇区认证原A密匙
     * @param {string} keyB 扇区认证原B密匙
     * @param {string} ctl 写入的新控制块数据 注意十六进制数据格式与长度必须正确
     * @returns {Promise} err/msg + retval(错误<1与write含义一致,成功返回1=原密钥方式写完成/2=新密钥方式重写完成)
     */
    async writeCtl(sector, keyA, keyB, ctl) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.MifareClassic_writeCtl, {
            rfIdx: this.__rfReader.__rfIdx,
            sector: sector,
            keyA: keyA,
            keyB: keyB,
            ctl: ctl
        });
    }

    /**
     * 解析权限码数据为权限位描述字符串
     * @param {string} power 权限码数据 例如"FF078069"
     * @returns {Promise} err/msg + retval/bitStr
     */
    async parsePowerStr(power) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.MifareClassic_parsePowerStr, {
            rfIdx: this.__rfReader.__rfIdx,
            power: power
        });
    }

    /**
     * 获取权限位描述字符串的权限码数据
     * @param {string} bitStr 权限位描述字符串 例如"000|000|000|001"
     * @returns {Promise} err/msg + retval/power
     */
    async dumpPowerStr(bitStr) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.MifareClassic_dumpPowerStr, {
            rfIdx: this.__rfReader.__rfIdx,
            bitStr: bitStr
        });
    }

    /**
     * 依据SAK获取容量大小
     * @param {number} sak 选择应答的SAK码
     * @returns {number} 参考常量Rokato.MifareClassic.SIZE_
     */
    static getSize(sak) {
        switch (sak) {
            case 0x09:
                return Rokato.MifareClassic.SIZE_MINI;

            case 0x01:
            case 0x08:
            case 0x28:
            case 0x88:
                return Rokato.MifareClassic.SIZE_1K;

            case 0x10:
                return Rokato.MifareClassic.SIZE_2K;

            case 0x11:
            case 0x18:
            case 0x38:
            case 0x98:
            case 0xB8:
                return Rokato.MifareClassic.SIZE_4K;

            default:
                return 0;
        }
    }

    /**
     * 依据SAK判断是否为ISO14443-4协议芯片模拟Mifare Classic
     * @param {number} sak 选择应答的SAK码
     */
    static isEmulated(sak) {
        switch (sak) {
            case 0x28:
            case 0x38:
                return true;

            default:
                return false;
        }
    }

    /**
     * 获取读/写错误码字符串含义
     * @param {number} err 读/写方法retval错误码
     * @returns {string}
     */
    static errStr(err) {
        if (err > 2) return "字节:" + err;
        switch (err) {
            case 2: return "控制块复写完成";
            case 1: return "控制块写入完成";
            case 0: return "权限不足操作失败";
            case -1: return "参数错误";
            case -2: return "数据大小错误";
            case -3: return "未选择UID";
            case -4: return "已移开感应区";
            case -5: return "密匙认证失败";
            case -6: return "控制块写入保护";
            case -7: return "权限码异常保护";
            default:
                return "未知错误";
        }
    }

    __rfReader = null;
}

Rokato.Utils.deepFreeze(Rokato.MifareClassic);

/**
 * CPU-COS-TypeA操作类
 */
Rokato.CpuCosTypeA = class {

    /**
     * CPU-COS-TypeA操作接口
     * @param {RfReader} rfReader 读卡器设备对象
     */
    constructor(rfReader) {
        this.__rfReader = rfReader;
    }

    /**
     * 选择UID并复位激活COS 只有激活成功后才能进行操作
     * @param {string} uid 十六进制UID数据
     * @returns {Promise} err/msg + retval
     */
    async select(uid) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CpuCosTypeA_select, {
            rfIdx: this.__rfReader.__rfIdx,
            uid: uid
        });
    }

    /**
     * 取消选择
     * @returns {Promise} err/msg
     */
    async deselect() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CpuCosTypeA_deselect, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 是否已选择激活
     * @returns {Promise} err/msg + retval
     */
    async isSelect() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CpuCosTypeA_isSelect, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 获取当前的UID数据
     * @returns {Promise} err/msg + retval
     */
    async getUid() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CpuCosTypeA_getUid, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 获取当前的SAK码
     * @returns {Promise} err/msg + retval
     */
    async getSak() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CpuCosTypeA_getSak, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 获取前4字节兼容卡号
     * @returns {Promise} err/msg + retval
     */
    async cardNo() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CpuCosTypeA_cardNo, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 获取当前COS厂商码
     * @returns {Promise} err/msg + retval
     */
    async cosCode() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CpuCosTypeA_cosCode, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 获取当前COS版本
     * @returns {Promise} err/msg + retval
     */
    async cosVer() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CpuCosTypeA_cosVer, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 获取当前ATS响应数据
     * @returns {Promise} err/msg + retval
     */
    async atsBuf() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CpuCosTypeA_atsBuf, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 获取当前激活详细信息描述
     * @returns {Promise} err/msg + retval
     */
    async atsStr() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CpuCosTypeA_atsStr, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * COS指令发送
     * @param {string} apdu COS指令数据
     * @returns {Promise} err/msg + retval/data
     */
    async send(apdu) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CpuCosTypeA_send, {
            rfIdx: this.__rfReader.__rfIdx,
            apdu: apdu
        });
    }

    /**
     * 获取COS响应SW状态码含义描述字符串
     * @param {number} sw 十进制SW错误码
     * @returns {Promise} err/msg + retval
     */
    async errStr(sw) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosErr_getStr, {
            sw: sw
        });
    }

    __rfReader = null;
}

Rokato.Utils.deepFreeze(Rokato.CpuCosTypeA);


/**
 * ISO-15693操作类
 */
Rokato.Iso15693 = class {
    /** 无错误 */
    static OK = 0x00;
    static ERR_OK = 0x00;
    /** 通讯失败 */
    static ERR_Fail = 0xFF;
    /** 缓冲区不足 */
    static ERR_Buffer = 0xFE;
    /** 设备无效不支持 */
    static ERR_DevInvalid = 0xFD;
    /** 命令不支持 */
    static ERR_NotSupport = 0x01;
    /** 命令无法识别 */
    static ERR_Param = 0x02;
    /** 不支持此选项 */
    static ERR_Option = 0x03;
    /** 未知错误 */
    static ERR_Unknown = 0x0F;
    /** 块不可用 */
    static ERR_Block = 0x10;
    /** 块已锁定 */
    static ERR_Locked = 0x11;
    /** 块已锁定无法写入 */
    static ERR_LockWrite = 0x12;
    /** 块编程失败 */
    static ERR_Program = 0x13;
    /** 块锁定失败 */
    static ERR_LockFail = 0x14;

    /** 最大块数量 */
    static MAX_BLOCK_COUNT = 256;
    /** 最大块大小 */
    static MAX_BLOCK_SIZE = 32;

    /**
     * ISO-15693操作接口
     * @param {RfReader} rfReader 读卡器设备对象
     */
    constructor(rfReader) {
        this.__rfReader = rfReader;
    }

    /**
     * 设置指令执行前标记
     * @param {number} mode 参考Rokato.Iso15693.ExecMode
     * @param {string} uid 仅Addressed模式必须 其他模式忽略
     * @param {boolean} dataRate 是否使用高数据速率 默认true
     * @param {boolean} subCarrier 是否使用两个副载波 默认false
     * @returns {Promise} err/msg + retval
     */
    async setFlags(mode, uid = "0000000000000000", dataRate = true, subCarrier = false) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Iso15693_setFlags, {
            rfIdx: this.__rfReader.__rfIdx,
            mode: mode,
            uid: uid,
            dataRate: dataRate,
            subCarrier: subCarrier
        });
    }

    /**
     * 保持安静 进入安静状态后不在响应查找Id
     * @param {string} uid 十六进制UID字符串
     * @returns {Promise} err/msg + retval
     */
    async stayQuiet(uid) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Iso15693_stayQuiet, {
            rfIdx: this.__rfReader.__rfIdx,
            uid: uid
        });
    }

    /**
     * 读单块
     * @param {boolean} getState 是否包含块属性状态
     * @param {number} blockAddr 有效块地址
     * @returns {Promise} err/msg + retval/data
     */
    async readBlock(getState, blockAddr) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Iso15693_readBlock, {
            rfIdx: this.__rfReader.__rfIdx,
            getState: getState,
            blockAddr: blockAddr
        });
    }

    /**
     * 写单块
     * @param {number} blockAddr 有效块地址
     * @param {string} data 十六进制块数据
     * @returns {Promise} err/msg + retval
     */
    async writeBlock(blockAddr, data) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Iso15693_writeBlock, {
            rfIdx: this.__rfReader.__rfIdx,
            blockAddr: blockAddr,
            data: data
        });
    }

    /**
     * 锁块
     * @param {number} blockAddr 有效块地址
     * @returns {Promise} err/msg + retval
     */
    async lockBlock(blockAddr) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Iso15693_lockBlock, {
            rfIdx: this.__rfReader.__rfIdx,
            blockAddr: blockAddr
        });
    }

    /**
     * 读多块
     * @param {number} mode 操作模式0=芯片原生命令,1=单块命令循环,2=优先尝试原生命令,>=3优先尝试单块命令
     * @param {boolean} getState 是否包含块属性状态
     * @param {number} firstBlockAddr 起始块地址
     * @param {number} blockNum 块数量
     * @returns {Promise} err/msg + retval/data
     */
    async readBlocks(mode, getState, firstBlockAddr, blockNum) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Iso15693_readBlocks, {
            rfIdx: this.__rfReader.__rfIdx,
            mode: mode,
            getState: getState,
            firstBlockAddr: firstBlockAddr,
            blockNum: blockNum
        });
    }

    /**
     * 写多块 注意大部分芯片原生命令可能并不支持写多块因此不建议操作模式使用0
     * @param {number} mode 操作模式0=芯片原生命令,1=单块命令循环,2=优先尝试原生命令,>=3优先尝试单块命令
     * @param {number} firstBlockAddr 起始块地址
     * @param {number} blockNum 块数量
     * @param {string} data 十六进制块数据
     * @returns {Promise} err/msg + retval
     */
    async writeBlocks(mode, firstBlockAddr, blockNum, data) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Iso15693_writeBlocks, {
            rfIdx: this.__rfReader.__rfIdx,
            mode: mode,
            firstBlockAddr: firstBlockAddr,
            blockNum: blockNum,
            data: data
        });
    }

    /**
     * 选择 在感应区内选择UID 注意多次选择仅最后一次有效且离开感应区后选择自动失效
     * @param {string} uid 十六进制UID字符串
     * @returns {Promise} err/msg + retval
     */
    async select(uid) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Iso15693_select, {
            rfIdx: this.__rfReader.__rfIdx,
            uid: uid
        });
    }

    /**
     * 重置到准备就绪状态 可解除安静与选中状态
     * @returns {Promise} err/msg + retval
     */
    async resetToReady() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Iso15693_resetToReady, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 写AFI
     * @param {number} afi 有效范围0x00~0xFF
     * @returns {Promise} err/msg + retval
     */
    async writeAFI(afi) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Iso15693_writeAFI, {
            rfIdx: this.__rfReader.__rfIdx,
            afi: afi
        });
    }

    /**
     * 锁AFI
     * @returns {Promise} err/msg + retval
     */
    async lockAFI() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Iso15693_lockAFI, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 写DSFID
     * @param {number} dsfid 有效范围0x00~0xFF
     * @returns {Promise} err/msg + retval
     */
    async writeDSFID(dsfid) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Iso15693_writeDSFID, {
            rfIdx: this.__rfReader.__rfIdx,
            dsfid: dsfid
        });
    }

    /**
     * 锁DSFID
     * @returns {Promise} err/msg + retval
     */
    async lockDSFID() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Iso15693_lockDSFID, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 获取系统信息
     * @returns {Promise} err/msg + retval/flags/dsfid/uid/afi/blockNum/byteSize/icRef
     */
    async getSysInfo() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Iso15693_getSysInfo, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 获取多块的安全状态
     * @param {number} firstBlockAddr 起始块地址
     * @param {number} blockNum 块数量
     * @returns {Promise} err/msg + retval/data
     */
    async getBlockState(firstBlockAddr, blockNum) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Iso15693_getBlockState, {
            rfIdx: this.__rfReader.__rfIdx,
            firstBlockAddr: firstBlockAddr,
            blockNum: blockNum
        });
    }


    /**
     * 获取错误码字符串含义
     */
    static errStr(err) {
        switch (err) {
            case Rokato.Iso15693.OK:
                return "完成";
            case Rokato.Iso15693.ERR_Fail:
                return "通讯失败";
            case Rokato.Iso15693.ERR_Buffer:
                return "缓冲区不足";
            case Rokato.Iso15693.ERR_DevInvalid:
                return "设备无效不支持";
            case Rokato.Iso15693.ERR_NotSupport:
                return "命令不支持";
            case Rokato.Iso15693.ERR_Param:
                return "命令无法识别";
            case Rokato.Iso15693.ERR_Option:
                return "不支持此选项";
            case Rokato.Iso15693.ERR_Unknown:
                return "未知错误";
            case Rokato.Iso15693.ERR_Block:
                return "块不可用";
            case Rokato.Iso15693.ERR_Locked:
                return "块已锁定";
            case Rokato.Iso15693.ERR_LockWrite:
                return "块已锁定无法写入";
            case Rokato.Iso15693.ERR_Program:
                return "块编程失败";
            case Rokato.Iso15693.ERR_LockFail:
                return "块锁定失败";
            default:
                return "错误异常";
        }
    }

    __rfReader = null;
}

/**
 * 指令执行模式
 * @readonly
 * @enum {number}
 */
Rokato.Iso15693.ExecMode = {
    /** 无地址广播任意执行 仅少部分特殊指令支持 */
    NonAddressed: 0,
    /** 寻址匹配UID执行 支持所有指令 */
    Addressed: 1,
    /** 仅最后选择状态的执行 仅需额外传入uid参数的特殊指令不支持 */
    Select: 2,
}

Rokato.Utils.deepFreeze(Rokato.Iso15693);


/**
 * IcodeSli/SliX操作类
 */
Rokato.IcodeSli = class {

    /** 块大小 */
    static BLOCK_SIZE = 4;

    /**
     * IcodeSli/SliX操作接口
     * @param {Iso15693} iso15693 Iso15693操作对象
     */
    constructor(iso15693) {
        this.__iso15693 = iso15693;
    }

    /**
     * 启用EAS
     * @returns {Promise} err/msg + retval
     */
    async enableEAS() {
        return this.__iso15693.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.IcodeSli_enableEAS, {
            rfIdx: this.__iso15693.__rfReader.__rfIdx
        });
    }

    /**
     * 禁用EAS
     * @returns {Promise} err/msg + retval
     */
    async disableEAS() {
        return this.__iso15693.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.IcodeSli_disableEAS, {
            rfIdx: this.__iso15693.__rfReader.__rfIdx
        });
    }

    /**
     * 锁定EAS
     * @returns {Promise} err/msg + retval
     */
    async lockEAS() {
        return this.__iso15693.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.IcodeSli_lockEAS, {
            rfIdx: this.__iso15693.__rfReader.__rfIdx
        });
    }

    /**
     * 检查EAS
     * @returns {Promise} err/msg + retval+data
     */
    async checkEAS() {
        return this.__iso15693.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.IcodeSli_checkEAS, {
            rfIdx: this.__iso15693.__rfReader.__rfIdx
        });
    }

    /**
     * 密码保护 注意永久激活不可取消
     * @param {number} type 参考Rokato.IcodeSli.FlagType
     * @returns {Promise} err/msg + retval
     */
    async passwordProtect(type) {
        return this.__iso15693.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.IcodeSli_passwordProtect, {
            rfIdx: this.__iso15693.__rfReader.__rfIdx,
            type: type
        });
    }

    /**
     * 验证密码提权 注意验证失败将无法继续操作任何指令,除非重置RF射频感应
     * @param {string} pwd 十六进制有效密码
     * @param {number} id 参考Rokato.IcodeSli.PwdId
     * @returns {Promise} err/msg + retval
     */
    async verifyPassword(pwd, id) {
        return this.__iso15693.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.IcodeSli_verifyPassword, {
            rfIdx: this.__iso15693.__rfReader.__rfIdx,
            pwd: pwd,
            id: id
        });
    }

    /**
     * 写密码 成功后需重新验证密码提权
     * @param {string} pwd 十六进制有效密码
     * @param {number} id 参考Rokato.IcodeSli.PwdId
     * @returns {Promise} err/msg + retval
     */
    async writePassword(pwd, id) {
        return this.__iso15693.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.IcodeSli_writePassword, {
            rfIdx: this.__iso15693.__rfReader.__rfIdx,
            pwd: pwd,
            id: id
        });
    }

    /**
     * 锁定密码
     * @param {number} id 参考Rokato.IcodeSli.PwdId
     * @returns {Promise} err/msg + retval
     */
    async lockPassword(id) {
        return this.__iso15693.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.IcodeSli_lockPassword, {
            rfIdx: this.__iso15693.__rfReader.__rfIdx,
            id: id
        });
    }

    /**
     * 永久销毁 如果存在锁定数据则无法销毁
     * @param {string} pwd 十六进制有效密码
     * @returns {Promise} err/msg + retval
     */
    async kill(pwd) {
        return this.__iso15693.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.IcodeSli_kill, {
            rfIdx: this.__iso15693.__rfReader.__rfIdx,
            pwd: pwd
        });
    }

    /**
     * 永久持续保持安静 进入安静状态后不在响应查找Id 必须resetToReady复位解除
     * @param {string} uid 十六进制有效UID
     * @returns {Promise} err/msg + retval
     */
    async stayQuietPersistent(uid) {
        return this.__iso15693.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.IcodeSli_stayQuietPersistent, {
            rfIdx: this.__iso15693.__rfReader.__rfIdx,
            uid: uid
        });
    }

    /**
     * 写两个连续块
     * @param {number} blockAddr 有效块地址
     * @param {string} data 两块数据
     * @returns {Promise} err/msg + retval
     */
    async writeTwoBlock(blockAddr, data) {
        return this.__iso15693.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.IcodeSli_writeTwoBlock, {
            rfIdx: this.__iso15693.__rfReader.__rfIdx,
            blockAddr: blockAddr,
            data: data,
        });
    }

    /**
     * 读授权起始块地址 读取成功代表尚未锁定快速初始化模式且默认地址为A5
     * @returns {Promise} err/msg + retval/authStartAddr
     */
    async readAuthStartAddr() {
        return this.__iso15693.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.IcodeSli_readAuthStartAddr, {
            rfIdx: this.__iso15693.__rfReader.__rfIdx
        });
    }

    /**
     * 写授权起始块地址 永久锁定并退出快速初始化模式
     * @param {number} authStartAddr 授权起始块地址
     * @returns {Promise} err/msg + retval
     */
    async writeAuthStartAddr(authStartAddr) {
        return this.__iso15693.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.IcodeSli_writeAuthStartAddr, {
            rfIdx: this.__iso15693.__rfReader.__rfIdx,
            authStartAddr: authStartAddr
        });
    }

    __iso15693 = null;
}

/**
 * 密钥ID
 * @readonly
 * @enum {number}
 */
Rokato.IcodeSli.PwdId = {
    ReadWriteKill: 0x0F,
    EASAFI: 0x10,
}

/**
 * 特殊标志类型
 * @readonly
 * @enum {number}
 */
Rokato.IcodeSli.FlagType = {
    EAS: 0x00,
    AFI: 0x01,
}

Rokato.Utils.deepFreeze(Rokato.IcodeSli);


/**
 * 精伦身份证阅读器操作类(仅支持Windows系统)
 */
Rokato.SdtApi = class {

    /**
     * 精伦身份证阅读器操作接口
     * @param {RktPlug} plug 插件通信对象
     */
    constructor(plug) {
        this.__plug = plug;
    }

    /**
     * 连接读卡器设备
     * @returns {Promise} err/msg + retval
     */
    async open() {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.SdtApi_open, {});
    }

    /**
     * 关闭读卡器连接
     * @returns {Promise} err/msg + retval
     */
    async close() {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.SdtApi_close, {});
    }

    /**
     * 身份证寻卡复位激活
     * @param {boolean} idle 如果IDLE为true则当卡片已被读取后未离开阅读器时将返回false
     * @returns {Promise} err/msg + retval
     */
    async reset(idle = false) {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.SdtApi_reset, {
            idle: idle
        });
    }

    /**
     * 读取已复位激活的身份证信息
     * @returns {Promise} err/msg + retval/name/sex/nation/birthDay/address/IDNo/department/startDate/endDate/photo
     */
    async read() {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.SdtApi_read, {});
    }

    __plug = null;
}

Rokato.Utils.deepFreeze(Rokato.SdtApi);


/**
 * UDP通讯操作类
 */
Rokato.UdpComm = class {

    /**
     * UDP通讯接口
     * @param {RktPlug} plug 插件通信对象
     * @param {number} udpIdx UDP对象索引
     */
    constructor(plug, udpIdx = 0) {
        this.__plug = plug;
        this.__udpIdx = parseInt(udpIdx);
    }

    /**
     * 获取当前UDP对象索引
     * @returns {number}
     */
    getUdpIdx() { return this.__udpIdx; }

    /**
     * 设置切换UDP对象索引
     * @param {number} udpIdx UDP对象索引
     */
    setUdpIdx(udpIdx) { this.__udpIdx = parseInt(udpIdx); }

    /**
     * 启动通讯 注意重复调用仅callback更改有效,retval一定返回true
     * @param {callback} callback 事件回调evt参数对象包含host(来源地址信息)/data(十六进制数据包)
     * @param {boolean} broadcast 是否启用UDP广播支持
     * @param {string} host 默认绑定随机未占用端口(客户端场景) 否则为绑定指定地址与端口但端口被占用将会失败(服务端场景)
     * @returns {Promise} err/msg + retval
     */
    async start(callback, broadcast = false, host = '0.0.0.0:0') {
        this.__plug.bindEvent(Rokato.RktPlug.GCODE.UdpComm_start, callback);
        const pos = host.lastIndexOf(':');
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.UdpComm_start, {
            udpIdx: this.__udpIdx,
            broadcast: broadcast,
            addr: host.substring(0, pos),
            port: parseInt(host.substring(pos + 1))
        });
    }

    /**
    * 停止通讯
    * @returns {Promise} err/msg
    */
    async stop() {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.UdpComm_stop, { udpIdx: this.__udpIdx });
    }

    /**
    * 发送数据报
    * @param {string} host 发送目标地址
    * @param {string} data 十六进制数据包
    * @returns {Promise} err/msg + retval(实际发送字节数)
    */
    async send(host, data) {
        const pos = host.lastIndexOf(':');
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.UdpComm_send, {
            udpIdx: this.__udpIdx,
            addr: host.substring(0, pos),
            port: parseInt(host.substring(pos + 1)),
            data: data
        });
    }

    /**
    * 激活设备APIv1通讯模式
    * @param {string} keyStr 设备APIv1字符串密钥
    * @returns {Promise} err/msg + retval
    */
    async devApiV1(keyStr) {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.UdpComm_devApiV1, {
            udpIdx: this.__udpIdx,
            keyStr: keyStr
        });
    }

    __plug = null;
    __udpIdx = 0;
}

Rokato.Utils.deepFreeze(Rokato.UdpComm);

Object.freeze(Rokato);