rktplug.js

/**
 * @file rktplug.js
 * @author HouseMen
 * @version 2.0.5
 * @copyright Copyright (c) 2024
 */

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

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

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

    /**
     * 比较两个字符串版本号
     * @param {string} ver1 格式'xxx.xx.xx...'
     * @param {string} ver2 格式'xxx.xx.xx...'
     * @param {number} radix 默认十进制
     * @returns {number} 0=ver1=ver2 -1=ver1<ver2 1=ver1>ver2
     */
    compareVersion(ver1, ver2, radix = 10) {
        const arr1 = ver1.split('.');
        const arr2 = ver2.split('.');
        let v1, v2;
        for (let i = 0; ; ++i) {
            if (i >= arr1.length && i >= arr2.length) return 0;
            v1 = i < arr1.length ? parseInt(arr1[i], radix) : 0;
            v2 = i < arr2.length ? parseInt(arr2[i], radix) : 0;
            if (isNaN(v1)) v1 = 0;
            if (isNaN(v2)) v2 = 0;
            if (v1 < v2) return -1;
            else if (v1 > v2) return 1;
        }
    },

    /**
     * 依据字符数量切片字符串
     * @param {string} str 完整字符串
     * @param {number} count 字符数量
     * @returns {Array} 切片后的字符串数组 参数错误返回空数组
     */
    sliceCount(str, count) {
        const result = [];
        if (str.length % count != 0) return 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 = [];
        if (str.length % count != 0) return 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;
    },

    /**
     * 数值金额转字符串金额(单位分)
     * @param {number} money 以分为单位的正负数值
     * @returns {string}
     */
    moneyToStr(money) {
        let prefix = '';
        if (money < 0) {
            money = ~money + 1;
            prefix = '-';
        }
        return prefix + Number(money / 100).toFixed(2);
    },

    /**
     * 字符串金额转数值金额(单位分)
     * @param {string} str 以分为单位的字符串金额
     * @returns {number} 字符串格式异常返回null
     */
    strToMoney(str) {
        const tmp = Rokato.Utils.sliceCount(str, 1);
        const minus = tmp[0] == '-';
        if (minus) tmp.shift();

        let delim = 0;
        let idx = 0;
        for (let i = 0; i < tmp.length; ++i) {
            if (tmp[i] == '.' && delim == 0) {
                delim = i;
                if (delim < 1) return null;
                continue;
            }
            if (tmp[i] < '0' || tmp[i] > '9') return null;
            if (delim) {
                ++idx;
                if (idx > 2) return null;
            }
        }

        for (let i = idx; i < 2; ++i) tmp.push('0');
        if (delim) tmp.splice(delim, 1);

        let money = parseInt(tmp.join(''));
        if (minus) money = ~money + 1;
        return money;
    },

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

    /**
     * Base64解码转UTF8字符串
     * @param {string} str Base64编码字符串
     * @returns UTF8字符串 遇到异常返回参数原数据
     */
    base64ToUtf8(str) {
        try {
            return decodeURIComponent(atob(str).split('').map(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;
    },

    /**
     * 二进制位反转
     * @param {number} num 数值
     * @param {number} bitSize 有效位大小
     */
    reverseBits(num, bitSize) {
        let result = 0;
        for (let i = 0; i < bitSize; ++i) {
            result = ((result << 1) | (num & 1)) >>> 0;
            num >>= 1;
        }
        return result;
    },

    /**
     * 16位数值字节序反转
     */
    bswap16(num) {
        return ((num & 0xFF) << 8 | (num >> 8) & 0xFF) >>> 0;
    },

    /**
    * 32位数值字节序反转
    */
    bswap32(num) {
        return ((num & 0xFF) << 24 | (num & 0xFF00) << 8 | (num & 0xFF0000) >> 8 | (num & 0xFF000000) >>> 24) >>> 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;
    },
}

/**
 * CRC校验算法
 * @namespace
 */
Rokato.Crypto.CRC = {
    /**
     * 计算MODBUS校验码
     * @param {string} hexStr 十六进制字符串数据
     * @param {number} crc 运算初始值 默认为标准初始值
     * @returns {number} 十进制数值 参数错误返回-1
     */
    modbusBit16(hexStr, crc = 0xFFFF) {
        return Rokato.Crypto.CRC.calc(hexStr, 16, 0x8005, crc, true, 0x0000);
    },

    /**
     * 计算CCITT_false校验码
     * @param {string} hexStr 十六进制字符串数据
     * @param {number} crc 运算初始值 默认为标准初始值
     * @returns {number} 十进制数值 参数错误返回-1
     */
    ccittFalseBit16(hexStr, crc = 0xFFFF) {
        return Rokato.Crypto.CRC.calc(hexStr, 16, 0x1021, crc, false, 0x0000);
    },

    /**
     * 计算常规CRC32校验码
     * @param {string} hexStr 十六进制字符串数据
     * @param {number} crc 运算初始值 默认为标准初始值
     * @returns {number} 十进制数值 参数错误返回-1
     */
    bit32(hexStr, crc = 0xFFFFFFFF) {
        return Rokato.Crypto.CRC.calc(hexStr, 32, 0x04C11DB7, crc, true, 0xFFFFFFFF);
    },

    /**
     * 计算CRC_SEL校验码
     * @param {string} hexStr 十六进制字符串数据
     * @param {number} crc 运算初始值 默认为标准初始值
     * @returns {number} 十进制数值 参数错误返回-1
     */
    selBit8(hexStr, crc = 0x00) {
        return Rokato.Crypto.CRC.calc(hexStr, 8, 0x1D, crc, false, 0x00);
    },

    /**
     * CRC实时计算
     * @param {string} hexStr 十六进制字符串数据
     * @param {number} bitSize 二进制位大小
     * @param {number} poly 多项式
     * @param {number} crc 初始值
     * @param {boolean} refl 反转
     * @param {number} xorOut 结果异或值
     * @returns {number} 十进制数值 参数错误返回-1
     */
    calc(hexStr, bitSize, poly, crc, refl, xorOut) {
        const data = Rokato.Utils.sliceCountToInt(hexStr, 2, 16);
        if (data.length < 1) return -1;
        const mask = bitSize > 31 ? 0xFFFFFFFF : (1 << bitSize >>> 0) - 1;
        for (let byte of data) {
            crc ^= (refl ? Rokato.Utils.reverseBits(byte, 8) : byte) << (bitSize - 8);
            for (let i = 0; i < 8; ++i) {
                if (crc & (1 << (bitSize - 1))) {
                    crc = ((crc << 1) ^ poly) >>> 0;
                } else {
                    crc = (crc << 1) >>> 0;
                }
                crc = (crc & mask) >>> 0;
            }
        }
        if (refl) crc = Rokato.Utils.reverseBits(crc, bitSize);
        return (crc ^ xorOut) >>> 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_loopStop: 119,
        RfReader_loopContinue: 120,

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

        CpuCosTypeA_uidSelect: 300,
        CpuCosTypeA_deselect: 301,
        CpuCosTypeA_isSelect: 302,
        CpuCosTypeA_getUid: 303,
        CpuCosTypeA_findSelect: 304,
        CpuCosTypeA_cardNo: 305,
        CpuCosTypeA_cosCode: 306,
        CpuCosTypeA_cosVer: 307,
        CpuCosTypeA_atsBuf: 308,
        CpuCosTypeA_atsStr: 309,
        CpuCosTypeA_send: 310,
        CpuCosTypeA_apdu: 311,

        CosErr_getStr: 400,
        SdtApi_open: 401,
        SdtApi_close: 402,
        SdtApi_reset: 403,
        SdtApi_read: 404,
        CalcSectorPwd: 405,
        CalcCosKeys: 406,

        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,

        Cos7816_rand: 800,
        Cos7816_select: 801,
        Cos7816_auth: 802,
        Cos7816_verify: 803,
        Cos7816_calc: 804,
        Cos7816_writeBin: 805,
        Cos7816_writeLog: 806,
        Cos7816_addLog: 807,
        Cos7816_readBin: 808,
        Cos7816_readLog: 809,

        CosFm2_eraseDF: 900,
        CosFm2_createDF: 901,
        CosFm2_createEF: 902,
        CosFm2_createPboc: 903,
        CosFm2_writeKey: 904,

        CosPboc2_renPin: 1000,
        CosPboc2_resetPin: 1001,
        CosPboc2_unlockPin: 1002,
        CosPboc2_unlockApp: 1003,
        CosPboc2_lockApp: 1004,
        CosPboc2_lockCard: 1005,
        CosPboc2_getBalance: 1006,
        CosPboc2_getOrderTac: 1007,
        CosPboc2_orderReq: 1008,
        CosPboc2_orderCommit: 1009,
        CosPboc2_etcWriteLog: 1010,
    }

    /**
     * 构造核心接口服务通信对象
     * @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/?';
        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 (Object.prototype.hasOwnProperty.call(data, '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);


/**
 * RF读卡器设备操作类
 */
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,
     *        "uid": "FFFFFFFF",
     *        "application": "FFFFFFFF",
     *        "protocol": "FFFFFF"
     *    },
     *    {
     *        "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,
                pause: false,
                fixIdle: false
            }
        );
    }

    /**
     * 自动无限循环寻卡 注意重复调用将取消上次绑定的回调,同时之后调用其他某些读卡器方法也会自动停止
     * @param {callback} callback 事件回调evt参数对象包含retval(数组对象参考findId)
     * @param {number} ms 循环间隔毫秒
     * @param {boolean} pause 是否找到Id后自动暂停等待loopContinue继续 注意loop必须在暂停或停止状态才能执行其他读卡器操作
     * @param {boolean} fixIdle 是否启用对比Id更改变化修复芯片或设备不兼容Idle寻卡导致的重复寻到相同芯片现象
     * @returns {Promise} err/msg
     */
    async loopFindId(callback, ms = 100, pause = false, fixIdle = false) {
        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,
                pause: pause,
                fixIdle: fixIdle
            }
        );
    }

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

    /**
    * 继续loopFindId自动暂停中的循环处理任务
    * @returns {Promise} err/msg
    */
    async loopContinue() {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.RfReader_loopContinue, { 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";

    /**
     * 构造MifareClassic接口对象
     * @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 uidSelect(uid) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.MifareClassic_uidSelect, {
            rfIdx: this.__rfReader.__rfIdx,
            uid: uid
        });
    }

    /**
     * 查找选择 只有选择成功后才能进行操作
     * @returns {Promise} err/msg + retval/uid/atqa/sak
     */
    async findSelect() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.MifareClassic_findSelect, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 取消选择
     * @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
        });
    }

    /**
     * 获取前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} sector 扇区号
     * @returns {number} 块数量
     */
    static sectorToBlockCount(sector) {
        return sector < 32 ? 4 : 16;
    }

    /**
     * 获取读/写错误码字符串含义
     * @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系统SW错误码处理
 */
Rokato.CosErr = class {
    static SW_0000 = 0x0000; /** 异常 未知错误 非COS标准错误定义 */
    static SW_FFFF = 0xFFFF; /** 异常 通讯失败 非COS标准错误定义 */
    static SW_9000 = 0x0090; /** 正常 成功执行 */
    static SW_0001 = 0x0100; /** 错误 卡片已永久锁定 */
    static SW_6A88 = 0x886A; /** 错误 密钥未找到 */
    static SW_6A83 = 0x836A; /** 错误 记录未找到 */
    static SW_6A82 = 0x826A; /** 错误 文件未找到 */
    static SW_6A81 = 0x816A; /** 错误 应用目录临时锁定 */
    static SW_6988 = 0x8869; /** 错误 MAC校验失败,密钥错误 */
    static SW_6985 = 0x8569; /** 错误 使用条件不满足 */
    static SW_6983 = 0x8369; /** 错误 密钥已锁定 */
    static SW_6982 = 0x8269; /** 错误 权限安全状态不满足 */

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

    /**
     * 十进制小端序SW错误码转十六进制格式字符串
     * @param {number} sw 十进制小端序SW错误码
     * @returns 十六进制字符串
     */
    static hexSW(sw) {
        return Rokato.Utils.bswap16(sw).toString(16).toUpperCase().padStart(4, '0');
    }

    /**
     * 构造CosErr操作对象
     * @param {RfReader} plug 插件通讯对象
     */
    constructor(plug) {
        this.__plug = plug;
    }

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

    __plug = null;
}

Rokato.Utils.deepFreeze(Rokato.CosErr);


/**
 * 非标准通用接口对象-仅用于商务对接
 */
Rokato.NonStd = class {
    /**
     * 构造NonStd操作对象
     * @param {RfReader} plug 插件通讯对象
     */
    constructor(plug) {
        this.__plug = plug;
    }

    /**
     * 使用内置算法计算动态扇区密钥-仅用于商业应用对接
     * @param {string} uid 十六进制UID数据
     * @param {number} type 内置算法类型 频繁更新迭代-具体需商务沟通
     * @param {object} data 内置算法参数 频繁更新迭代-具体需商务沟通
     * @returns {Promise} err/msg + retval
     */
    async calcSectorPwd(uid, type, data) {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.CalcSectorPwd, {
            uid: uid,
            type: type,
            data: data
        });
    }

    /**
     * 使用内置算法计算COS应用密钥集-仅用于商业应用对接
     * @param {string} uid 十六进制UID数据
     * @param {number} type 内置算法类型 频繁更新迭代-具体需商务沟通
     * @param {object} data 内置算法参数 频繁更新迭代-具体需商务沟通
     * @returns {Promise} err/msg + retval
     */
    async calcCosKeys(uid, type, data) {
        return this.__plug.asyncSend(Rokato.RktPlug.GCODE.CalcCosKeys, {
            uid: uid,
            type: type,
            data: data
        });
    }

    __plug = null;
}

Rokato.Utils.deepFreeze(Rokato.NonStd);


/**
 * TypeA类CPU卡芯片COS操作
 */
Rokato.CpuCosTypeA = class {

    /**
     * 构造CpuCosTypeA操作对象
     * @param {RfReader} rfReader 读卡器设备对象
     */
    constructor(rfReader) {
        this.__rfReader = rfReader;
    }

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

    /**
     * 查找选择激活COS 只有激活成功后才能进行操作
     * @returns {Promise} err/msg + retval/uid/atqa/sak
     */
    async findSelect() {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CpuCosTypeA_findSelect, {
            rfIdx: this.__rfReader.__rfIdx
        });
    }

    /**
     * 取消选择
     * @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
        });
    }

    /**
     * 获取前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
        });
    }

    /**
     * 自定义APDU指令发送
     * @param {number} cla 类别码 参考芯片命令说明
     * @param {number} ins 指令码 参考芯片命令说明
     * @param {number} p1 参数P1 参考芯片命令说明
     * @param {number} p2 参数P2 参考芯片命令说明
     * @param {string} lcData Lc附加数据 默认忽略
     * @param {boolean} incLe 是否包含Le参数 默认false忽略Le参数
     * @param {number} le 期望响应字节 默认0x00 仅incLe为true时有效
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略
     * @returns {Promise} err/msg + retval/data
     */
    async apdu(cla, ins, p1, p2, lcData = '', incLe = false, le = 0x00, safe = null) {
        return this.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CpuCosTypeA_apdu, {
            rfIdx: this.__rfReader.__rfIdx,
            cla: cla,
            ins: ins,
            p1: p1,
            p2: p2,
            lcData: lcData,
            incLe: incLe,
            le: le,
            safe: safe
        });
    }

    __rfReader = null;
    __cpuId = 0;
}

Rokato.Utils.deepFreeze(Rokato.CpuCosTypeA);


/**
 * TypeB类CPU卡芯片COS操作
 */
Rokato.CpuCosTypeB = class {

    /**
     * 构造CpuCosTypeB操作对象
     * @param {RfReader} rfReader 读卡器设备对象
     */
    constructor(rfReader) {
        this.__rfReader = rfReader;
    }

    __rfReader = null;
    __cpuId = 1;
}

Rokato.Utils.deepFreeze(Rokato.CpuCosTypeB);


/**
 * 通用ISO-7816-4协议COS功能
 */
Rokato.Cos7816 = class {
    /**
     * 生成KeyData密钥数据对象参数
     * @param {string} param 十六进制字符串密钥数据或自定义提供者字符串附加参数
     * @param {KeyAlg} alg 默认DES
     * @param {string} provider 密钥算法运算提供程序回调 暂不支持自定义回调 默认空字符使用内置标准运算
     */
    static KeyData(param, alg = Rokato.Cos7816.KeyAlg.DES, provider = "") {
        if (param.length < 1) return null;
        return {
            param: param,
            alg: alg,
            provider: provider
        };
    }

    /**
     * 生成RfSafe线路安全对象参数
     * @param {KeyData} key 线路安全密钥 参考KeyData方法生成
     * @param {boolean} enc 安全选项-线路加密
     * @param {boolean} mac 安全选项-线路保护
     */
    static RfSafe(key, enc, mac) {
        return {
            key: key,
            enc: enc,
            mac: mac
        };
    }

    /**
     * 生成TlvData变长记录数据对象
     * @param {number} tag 记录标签 0x00~0xFF范围
     * @param {string} data 十六进制字符串数据 字节大小不可超过0xFA
     */
    static TlvData(tag, data) {
        if (tag > 0xFF) tag = 0xFF;
        data = Rokato.Utils.trimHex(data);
        if (data.length > 0xFA * 2) data = data.substring(0, 0xFA * 2);
        return {
            tag: tag,
            len: data.length / 2,
            data: data
        };
    }

    /**
     * 解析原始Tlv数据生成TlvData变长记录数据对象
     * @param {string} data 十六进制字符串数据
     */
    static parseTlvData(data) {
        data = Rokato.Utils.trimHex(data);
        const tag = parseInt(data.substring(0, 2), 16);
        const len = parseInt(data.substring(2, 4), 16);
        return Rokato.Cos7816.TlvData(tag, data.substring(4, 4 + len * 2));
    }

    /**
     * 获取TlvData变长记录数据对象原始十六进制数据
     * @param {TlvData} tlvData 变长记录数据对象
     */
    static dumpTlvData(tlvData) {
        let data = tlvData.tag.toString(16).padStart(2, '0');
        data += tlvData.len.toString(16).padStart(2, '0');
        data += tlvData.data;
        return data;
    }

    /**
     * 构造Cos7816操作对象
     * @param {CpuCosTypeA | CpuCosTypeB} cpuCosReader CPU-COS读卡器操作对象
     */
    constructor(cpuCosReader) {
        this.setCpuCosReader(cpuCosReader);
    }

    /**
     * 获取当前COS读卡器CPU协议ID
     * @returns {number}
     */
    getCpuId() { return this.__cpuCosReader.__cpuId; }

    /**
     * 重新设置CPU-COS读卡器操作对象
     * @param {CpuCosTypeA | CpuCosTypeB} cpuCosReader CPU-COS读卡器操作对象
     */
    setCpuCosReader(cpuCosReader) { this.__cpuCosReader = cpuCosReader; }

    /**
     * 获取随机数
     * @param {boolean} is32 默认false=8字节随机数 true=4字节随机数
     * @returns {Promise} err/msg + retval/data(十六进制字符串)
     */
    async rand(is32 = false) {
        return this.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Cos7816_rand, {
            rfIdx: this.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cpuCosReader.__cpuId,
            is32: is32
        });
    }

    /**
     * 选择专用标识
     * @param {string} aid 专用应用标识十六进制字符串
     * @returns {Promise} err/msg + retval/[fci={df/private/sfi/ver/tmp}(注意FCI每个字段都非必定存在)]
     */
    async selectAID(aid) {
        return this.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Cos7816_select, {
            rfIdx: this.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cpuCosReader.__cpuId,
            fid: 0,
            aid: aid
        });
    }

    /**
     * 选择字符串文件名
     * @param {string} name 字符串文件名称
     * @returns {Promise} err/msg + retval/[fci={df/private/sfi/ver/tmp}(注意FCI每个字段都非必定存在)]
     */
    async selectName(name) {
        return this.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Cos7816_select, {
            rfIdx: this.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cpuCosReader.__cpuId,
            fid: 0,
            name: name
        });
    }

    /**
     * 选择文件id
     * @param {number} fid 文件标识符ID
     * @returns {Promise} err/msg + retval/[fci={df/private/sfi/ver/tmp}(注意FCI每个字段都非必定存在)]
     */
    async selectFID(fid) {
        return this.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Cos7816_select, {
            rfIdx: this.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cpuCosReader.__cpuId,
            fid: fid
        });
    }

    /**
     * 外部认证授权
     * @param {number} id 外部认证密钥标识ID 通常为0x00
     * @param {KeyData} key 密钥数据对象 参考KeyData方法生成
     * @returns {Promise} err/msg + retval
     */
    async auth(id, key) {
        return this.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Cos7816_auth, {
            rfIdx: this.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cpuCosReader.__cpuId,
            id: id,
            key: key
        });
    }

    /**
     * 验证用户口令PIN
     * @param {number} id 口令密钥标识ID 通常为0x00
     * @param {string} pin 用户字符口令
     * @returns {Promise} err/msg + retval
     */
    async verify(id, pin) {
        return this.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Cos7816_verify, {
            rfIdx: this.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cpuCosReader.__cpuId,
            id: id,
            pin: pin
        });
    }

    /**
     * 内部认证运算
     * @param {number} id 内部认证密钥标识ID
     * @param {number} type 运算类型 通常为0x00=加密 0x01=解密 0x02=校验
     * @param {string} data 运算的十六进制字符串数据
     * @returns {Promise} err/msg + retval/data
     */
    async calc(id, type, data) {
        return this.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Cos7816_calc, {
            rfIdx: this.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cpuCosReader.__cpuId,
            id: id,
            type: type,
            data: data
        });
    }

    /**
     * 写普通文件数据
     * @param {number} sfid 文件短标识符(0x01~0x1F) 0x00=当前打开的文件
     * @param {number} pos 起始位置偏移量 如果sfid=0偏移最大0x7FFF否则最大0xFF
     * @param {string} data 写入的十六进制字符串数据
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略
     * @returns {Promise} err/msg + retval
     */
    async writeBin(sfid, pos, data, safe = null) {
        return this.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Cos7816_writeBin, {
            rfIdx: this.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cpuCosReader.__cpuId,
            sfid: sfid,
            pos: pos,
            data: data,
            safe: safe
        });
    }

    /**
     * 写记录文件数据
     * @param {number} sfid 文件短标识符(0x01~0x1F) 0x00=当前打开的文件
     * @param {number} id 写入位置的记录编号或标识 如果为0x00则代表当前记录
     * @param {LogIdMode} mode 记录ID模式 参考LogIdMode
     * @param {string} data 写入的十六进制字符串数据 注意不能超过创建时定长记录大小
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略
     * @returns {Promise} err/msg + retval
     */
    async writeLog(sfid, id, mode, data, safe = null) {
        return this.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Cos7816_writeLog, {
            rfIdx: this.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cpuCosReader.__cpuId,
            sfid: sfid,
            id: id,
            mode: mode,
            data: data,
            safe: safe
        });
    }

    /**
     * 写变长TLV记录文件 注意已添加的TLV记录不能改变长度
     * @param {number} sfid 文件短标识符(0x01~0x1F) 0x00=当前打开的文件
     * @param {number} id 写入位置的记录编号或标识 如果为0x00则代表当前记录
     * @param {LogIdMode} mode 记录ID模式 参考LogIdMode
     * @param {TlvData} tlvData 写入的变长记录TLV格式数据 参考TlvData方法生成
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略
     * @returns {Promise} err/msg + retval
     */
    async writeTlv(sfid, id, mode, tlvData, safe = null) {
        return this.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Cos7816_writeLog, {
            rfIdx: this.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cpuCosReader.__cpuId,
            sfid: sfid,
            id: id,
            mode: mode,
            data: Rokato.Cos7816.dumpTlvData(tlvData),
            safe: safe
        });
    }

    /**
     * 追加写入一条新的记录
     * @param {number} sfid 文件短标识符(0x01~0x1F) 0x00=当前打开的文件
     * @param {string} data 写入的十六进制字符串数据 注意不能超过创建时定长记录大小
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略
     * @returns {Promise} err/msg + retval
     */
    async addLog(sfid, data, safe = null) {
        return this.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Cos7816_addLog, {
            rfIdx: this.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cpuCosReader.__cpuId,
            sfid: sfid,
            data: data,
            safe: safe
        });
    }

    /**
     * 追加写入一条新的变长TLV记录
     * @param {number} sfid 文件短标识符(0x01~0x1F) 0x00=当前打开的文件
     * @param {TlvData} tlvData 写入的变长记录TLV格式数据 参考TlvData方法生成
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略
     * @returns {Promise} err/msg + retval
     */
    async addTlv(sfid, tlvData, safe = null) {
        return this.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Cos7816_addLog, {
            rfIdx: this.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cpuCosReader.__cpuId,
            sfid: sfid,
            data: Rokato.Cos7816.dumpTlvData(tlvData),
            safe: safe
        });
    }

    /**
     * 读普通文件数据
     * @param {number} sfid 文件短标识符(0x01~0x1F) 0x00=当前打开的文件
     * @param {number} pos 起始位置偏移量 如果sfid=0偏移最大0x7FFF否则最大0xFF
     * @param {number} size 指定读取的大小 默认0最大程度读取全部
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略
     * @returns {Promise} err/msg + retval/data
     */
    async readBin(sfid, pos, size = 0, safe = null) {
        return this.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Cos7816_readBin, {
            rfIdx: this.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cpuCosReader.__cpuId,
            sfid: sfid,
            pos: pos,
            size: size,
            safe: safe
        });
    }

    /**
     * 读记录文件数据
     * @param {number} sfid 文件短标识符(0x01~0x1F) 0x00=当前打开的文件
     * @param {number} id 读取的记录编号或标识 如果为0x00则代表当前记录
     * @param {LogIdMode} mode 记录ID模式 参考LogIdMode
     * @param {number} size 指定读取的大小 默认0最大程度读取全部
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略
     * @returns {Promise} err/msg + retval/data
     */
    async readLog(sfid, id, mode, size = 0, safe = null) {
        return this.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Cos7816_readLog, {
            rfIdx: this.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cpuCosReader.__cpuId,
            sfid: sfid,
            id: id,
            mode: mode,
            size: size,
            safe: safe
        });
    }

    /**
     * 读变长记录文件数据
     * @param {number} sfid 文件短标识符(0x01~0x1F) 0x00=当前打开的文件
     * @param {number} id 读取的记录编号或标识 如果为0x00则代表当前记录
     * @param {LogIdMode} mode 记录ID模式 参考LogIdMode
     * @param {number} size 指定读取的大小 默认0最大程度读取全部
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略
     * @returns {Promise} err/msg + retval/data(TlvData对象)
     */
    async readTlv(sfid, id, mode, size = 0, safe = null) {
        const result = await this.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.Cos7816_readLog, {
            rfIdx: this.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cpuCosReader.__cpuId,
            sfid: sfid,
            id: id,
            mode: mode,
            size: size,
            safe: safe
        });
        if (result.data) result.data = Rokato.Cos7816.parseTlvData(result.data);
        return result;
    }

    __cpuCosReader = null;
}

/**
 * 记录文件ID模式选项
 * @readonly
 * @enum {number}
 */
Rokato.Cos7816.LogIdMode = {
    /** 匹配记录标识第一条 */
    first: 0x00,
    /** 匹配记录标识最后一条 */
    last: 0x01,
    /** 匹配记录标识下一条 */
    down: 0x02,
    /** 匹配记录标识上一条 */
    up: 0x03,
    /** 匹配记录编号 */
    number: 0x04
}

/**
 * COS密钥算法
 * @readonly
 * @enum {number}
 */
Rokato.Cos7816.KeyAlg = {
    /** 无效未设置 */
    NONE: 0x00,
    /** 密码大小必须2~8字节之间 */
    PIN: 0x01,
    /** 密钥大小必须8或16字节 */
    DES: 0x02,
}

Rokato.Utils.deepFreeze(Rokato.Cos7816);


/**
 * 复旦FMCOS2.0协议COS功能
 */
Rokato.CosFm2 = class {

    /**
     * 生成KeyAttr密钥安全属性对象参数
     * @param {boolean} enc 是否启用线路加密
     * @param {boolean} mac 是否启用线路保护
     */
    static KeyAttr(enc, mac) {
        return {
            enc: enc,
            mac: mac
        };
    }

    /**
     * 生成FileAttr文件安全属性对象参数
     * @param {boolean} macr 是否启用线路保护读
     * @param {boolean} macw 是否启用线路保护写
     * @param {boolean} enc 是否启用线路加密写
     * @param {number} idr 读操作使用的线路安全密钥标识ID 默认为0
     * @param {number} idw 写操作使用的线路安全密钥标识ID 默认为0
     */
    static FileAttr(macr, macw, enc, idr = 0, idw = 0) {
        return {
            macr: macr,
            macw: macw,
            enc: enc,
            idr: idr,
            idw: idw,
        };
    }

    /**
     * 生成KeyErr密钥错误计数参数
     * @param {number} retry 剩余可重试次数 范围0~15 默认15
     * @param {number} max 最大可错误次数 范围0~15 默认15
     */
    static KeyErr(retry = 0xF, max = 0xF) {
        return (max << 4) | retry;
    }

    /**
     * 生成Power权限控制参数
     * @param {number} min 权值最小要求 范围0~15 注意如果最小要求>最大要求代表权限锁死
     * @param {number} max 权值最大要求 范围0~15 默认为0=忽略权值最大要求
     */
    static Power(min, max = 0) {
        return (max << 4) | min;
    }

    /**
     * 构造CosFm2操作对象
     * @param {Cos7816} cos7816 Cos7816操作对象
     */
    constructor(cos7816) { this.__cos7816 = cos7816; }

    /**
     * 擦除当前目录空间所有数据
     * @returns {Promise} err/msg + retval
     */
    async eraseDF() {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_eraseDF, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId
        });
    }

    /**
     * 创建DDF目录空间文件
     * @param {string} aid 十六进制字符串数据 注意如果为空字符则为匿名目录
     * @param {number} fid 文件标识符
     * @param {number} size 目录空间大小
     * @param {Power} create 创建文件所需权限 参考Power方法生成
     * @param {Power} erase 擦除目录所需权限 参考Power方法生成
     * @param {number} sfi 应用AID记录入口短文件SFI标识ID 默认0x01
     * @returns {Promise} err/msg + retval
     */
    async createDDF(aid, fid, size, create, erase, sfi = 0x01) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_createDF, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            aid: aid,
            fid: fid,
            size: size,
            create: create,
            erase: erase,
            sfi: sfi
        });
    }

    /**
     * 创建ADF应用目录文件
     * @param {string} aid 十六进制字符串数据 注意如果为空字符则为匿名目录
     * @param {number} fid 文件标识符
     * @param {number} size 目录空间大小
     * @param {Power} create 创建文件所需权限 参考Power方法生成
     * @param {Power} erase 擦除目录所需权限 参考Power方法生成
     * @param {number} sfi 设定FCI-TMP数据关联BIN短文件标识 默认0x15
     * @returns {Promise} err/msg + retval
     */
    async createADF(aid, fid, size, create, erase, sfi = 0x15) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_createDF, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            aid: aid,
            fid: fid,
            size: size,
            create: create,
            erase: erase,
            sfi: sfi == 0x00 ? 0x00 : sfi | 0x80
        });
    }

    /**
     * 创建安全密钥文件
     * @param {number} size 密钥空间大小 参考密钥数量*23
     * @param {Power} add 添加密钥所需权值 参考Power方法生成
     * @param {number} fid 文件标识符 默认密钥文件id必须是0
     * @returns {Promise} err/msg + retval
     */
    async createKey(size, add, fid = 0) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_createEF, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            type: Rokato.CosFm2.FileType.KEY,
            fid: fid,
            size: size,
            read: 0x00,
            write: add
        });
    }

    /**
     * 创建普通数据文件
     * @param {number} fid 文件标识符
     * @param {number} size 数据空间大小
     * @param {Power} read 读取所需权值 参考Power方法生成
     * @param {Power} write 写入所需权值 参考Power方法生成
     * @param {FileAttr} attr 文件安全属性 参考FileAttr方法生成
     * @returns {Promise} err/msg + retval
     */
    async createBin(fid, size, read, write, attr) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_createEF, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            type: Rokato.CosFm2.FileType.BIN,
            fid: fid,
            size: size,
            read: read,
            write: write,
            attr: attr
        });
    }

    /**
     * 创建定长记录文件
     * @param {number} fid 文件标识符
     * @param {number} logMax 记录最大数量
     * @param {number} logLen 记录固定长度
     * @param {Power} read 读取所需权值 参考Power方法生成
     * @param {Power} write 写入所需权值 参考Power方法生成
     * @param {FileAttr} attr 文件安全属性 参考FileAttr方法生成
     * @returns {Promise} err/msg + retval
     */
    async createFix(fid, logMax, logLen, read, write, attr) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_createEF, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            type: Rokato.CosFm2.FileType.FIX,
            fid: fid,
            size: Rokato.CosFm2.__LogFileSize(logMax, logLen),
            read: read,
            write: write,
            attr: attr
        });
    }

    /**
     * 创建循环记录文件
     * @param {number} fid 文件标识符
     * @param {number} logMax 记录最大数量
     * @param {number} logLen 记录固定长度
     * @param {Power} read 读取所需权值 参考Power方法生成
     * @param {Power} write 写入所需权值 参考Power方法生成
     * @param {FileAttr} attr 文件安全属性 参考FileAttr方法生成
     * @returns {Promise} err/msg + retval
     */
    async createLoop(fid, logMax, logLen, read, write, attr) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_createEF, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            type: Rokato.CosFm2.FileType.LOOP,
            fid: fid,
            size: Rokato.CosFm2.__LogFileSize(logMax, logLen),
            read: read,
            write: write,
            attr: attr
        });
    }

    /**
     * 创建变长记录文件
     * @param {number} fid 文件标识符
     * @param {number} size 数据空间大小
     * @param {Power} read 读取所需权值 参考Power方法生成
     * @param {Power} write 写入所需权值 参考Power方法生成
     * @param {FileAttr} attr 文件安全属性 参考FileAttr方法生成
     * @returns {Promise} err/msg + retval
     */
    async createTlv(fid, size, read, write, attr) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_createEF, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            type: Rokato.CosFm2.FileType.TLV,
            fid: fid,
            size: size,
            read: read,
            write: write,
            attr: attr
        });
    }

    /**
     * 创建PBOC钱包文件
     * @param {PurseType} type 钱包类型 参考Rokato.CosPboc2.PurseType
     * @param {Power} use 使用所需权值 参考Power方法生成
     * @param {number} sfi 交易循环记录短文件标识ID 默认0x18
     * @returns {Promise} err/msg + retval
     */
    async createPboc(type, use, sfi = 0x18) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_createPboc, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            type: type,
            use: use,
            sfi: sfi
        });
    }

    /**
     * 设置外部授权认证密钥
     * @param {KeyType} oldType 旧密钥类型 参考KeyType 注意不为NEW则为修改密钥
     * @param {number} id 密钥标识ID 注意0x00为主控密钥可用于后续密钥线路安全添加与修改
     * @param {KeyAttr} attr 密钥属性选项 参考KeyAttr方法生成
     * @param {KeyData} key 新添加或修改的密钥数据对象 参考KeyData方法生成
     * @param {Power} use 使用所需权值 参考Power方法生成
     * @param {Power} ren 更改所需权值 参考Power方法生成
     * @param {number} pow 提升的权值 范围0~15
     * @param {KeyErr} err 错误计数 参考KeyErr方法生成
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略 注意未添加主控密钥时无效
     * @returns {Promise} err/msg + retval
     */
    async setKeyAuth(oldType, id, attr, key, use, ren, pow, err, safe = null) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_writeKey, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            oldType: oldType,
            id: id,
            type: Rokato.CosFm2.KeyType.EAK,
            attr: attr,
            key: key,
            use: use,
            ren: ren,
            pow: pow,
            err: err,
            safe: safe
        });
    }

    /**
     * 设置内部加密运算密钥
     * @param {KeyType} oldType 旧密钥类型 参考KeyType 注意不为NEW则为修改密钥
     * @param {number} id 密钥标识ID
     * @param {KeyAttr} attr 密钥属性选项 参考KeyAttr方法生成
     * @param {KeyData} key 新添加或修改的密钥数据对象 参考KeyData方法生成
     * @param {Power} use 使用所需权值 参考Power方法生成
     * @param {Power} ren 更改所需权值 参考Power方法生成
     * @param {number} ver 定义密钥版本号 可空默认0x00
     * @param {number} flag 定义算法标识 可空默认0x00
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略 注意未添加主控密钥时无效
     * @returns {Promise} err/msg + retval
     */
    async setKeyEnc(oldType, id, attr, key, use, ren, ver = 0x00, flag = 0x00, safe = null) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_writeKey, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            oldType: oldType,
            id: id,
            type: Rokato.CosFm2.KeyType.IAK_ENC,
            attr: attr,
            key: key,
            use: use,
            ren: ren,
            pow: ver,
            err: flag,
            safe: safe
        });
    }

    /**
     * 设置内部解密运算密钥
     * @param {KeyType} oldType 旧密钥类型 参考KeyType 注意不为NEW则为修改密钥
     * @param {number} id 密钥标识ID
     * @param {KeyAttr} attr 密钥属性选项 参考KeyAttr方法生成
     * @param {KeyData} key 新添加或修改的密钥数据对象 参考KeyData方法生成
     * @param {Power} use 使用所需权值 参考Power方法生成
     * @param {Power} ren 更改所需权值 参考Power方法生成
     * @param {number} ver 定义密钥版本号 可空默认0x00
     * @param {number} flag 定义算法标识 可空默认0x00
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略 注意未添加主控密钥时无效
     * @returns {Promise} err/msg + retval
     */
    async setKeyDec(oldType, id, attr, key, use, ren, ver = 0x00, flag = 0x00, safe = null) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_writeKey, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            oldType: oldType,
            id: id,
            type: Rokato.CosFm2.KeyType.IAK_DEC,
            attr: attr,
            key: key,
            use: use,
            ren: ren,
            pow: ver,
            err: flag,
            safe: safe
        });
    }

    /**
     * 设置内部校验运算密钥
     * @param {KeyType} oldType 旧密钥类型 参考KeyType 注意不为NEW则为修改密钥
     * @param {number} id 密钥标识ID
     * @param {KeyAttr} attr 密钥属性选项 参考KeyAttr方法生成
     * @param {KeyData} key 新添加或修改的密钥数据对象 参考KeyData方法生成
     * @param {Power} use 使用所需权值 参考Power方法生成
     * @param {Power} ren 更改所需权值 参考Power方法生成
     * @param {number} ver 定义密钥版本号 可空默认0x00
     * @param {number} flag 定义算法标识 可空默认0x00
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略 注意未添加主控密钥时无效
     * @returns {Promise} err/msg + retval
     */
    async setKeyMac(oldType, id, attr, key, use, ren, ver = 0x00, flag = 0x00, safe = null) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_writeKey, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            oldType: oldType,
            id: id,
            type: Rokato.CosFm2.KeyType.IAK_MAC,
            attr: attr,
            key: key,
            use: use,
            ren: ren,
            pow: ver,
            err: flag,
            safe: safe
        });
    }

    /**
     * 设置PBOC交易认证密钥
     * @param {KeyType} oldType 旧密钥类型 参考KeyType 注意不为NEW则为修改密钥
     * @param {number} id 密钥标识ID
     * @param {KeyAttr} attr 密钥属性选项 参考KeyAttr方法生成
     * @param {KeyData} key 新添加或修改的密钥数据对象 参考KeyData方法生成
     * @param {Power} use 使用所需权值 参考Power方法生成
     * @param {Power} ren 更改所需权值 参考Power方法生成
     * @param {number} ver 定义密钥版本号 可空默认0x00
     * @param {number} flag 定义算法标识 可空默认0x00
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略 注意未添加主控密钥时无效
     * @returns {Promise} err/msg + retval
     */
    async setKeyTac(oldType, id, attr, key, use, ren, ver = 0x00, flag = 0x00, safe = null) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_writeKey, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            oldType: oldType,
            id: id,
            type: Rokato.CosFm2.KeyType.IAK_TAC,
            attr: attr,
            key: key,
            use: use,
            ren: ren,
            pow: ver,
            err: flag,
            safe: safe
        });
    }

    /**
     * 设置PBOC修改透支密钥
     * @param {KeyType} oldType 旧密钥类型 参考KeyType 注意不为NEW则为修改密钥
     * @param {number} id 密钥标识ID
     * @param {KeyAttr} attr 密钥属性选项 参考KeyAttr方法生成
     * @param {KeyData} key 新添加或修改的密钥数据对象 参考KeyData方法生成
     * @param {Power} use 使用所需权值 参考Power方法生成
     * @param {Power} ren 更改所需权值 参考Power方法生成
     * @param {number} ver 定义密钥版本号 可空默认0x00
     * @param {number} flag 定义算法标识 可空默认0x00
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略 注意未添加主控密钥时无效
     * @returns {Promise} err/msg + retval
     */
    async setKeyRen(oldType, id, attr, key, use, ren, ver = 0x00, flag = 0x00, safe = null) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_writeKey, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            oldType: oldType,
            id: id,
            type: Rokato.CosFm2.KeyType.UOK,
            attr: attr,
            key: key,
            use: use,
            ren: ren,
            pow: ver,
            err: flag,
            safe: safe
        });
    }

    /**
     * 设置PBOC圈提密钥
     * @param {KeyType} oldType 旧密钥类型 参考KeyType 注意不为NEW则为修改密钥
     * @param {number} id 密钥标识ID
     * @param {KeyAttr} attr 密钥属性选项 参考KeyAttr方法生成
     * @param {KeyData} key 新添加或修改的密钥数据对象 参考KeyData方法生成
     * @param {Power} use 使用所需权值 参考Power方法生成
     * @param {Power} ren 更改所需权值 参考Power方法生成
     * @param {number} ver 定义密钥版本号 可空默认0x00
     * @param {number} flag 定义算法标识 可空默认0x00
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略 注意未添加主控密钥时无效
     * @returns {Promise} err/msg + retval
     */
    async setKeySub(oldType, id, attr, key, use, ren, ver = 0x00, flag = 0x00, safe = null) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_writeKey, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            oldType: oldType,
            id: id,
            type: Rokato.CosFm2.KeyType.UK,
            attr: attr,
            key: key,
            use: use,
            ren: ren,
            pow: ver,
            err: flag,
            safe: safe
        });
    }

    /**
     * 设置PBOC消费密钥
     * @param {KeyType} oldType 旧密钥类型 参考KeyType 注意不为NEW则为修改密钥
     * @param {number} id 密钥标识ID
     * @param {KeyAttr} attr 密钥属性选项 参考KeyAttr方法生成
     * @param {KeyData} key 新添加或修改的密钥数据对象 参考KeyData方法生成
     * @param {Power} use 使用所需权值 参考Power方法生成
     * @param {Power} ren 更改所需权值 参考Power方法生成
     * @param {number} ver 定义密钥版本号 可空默认0x00
     * @param {number} flag 定义算法标识 可空默认0x00
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略 注意未添加主控密钥时无效
     * @returns {Promise} err/msg + retval
     */
    async setKeyPos(oldType, id, attr, key, use, ren, ver = 0x00, flag = 0x00, safe = null) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_writeKey, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            oldType: oldType,
            id: id,
            type: Rokato.CosFm2.KeyType.PK,
            attr: attr,
            key: key,
            use: use,
            ren: ren,
            pow: ver,
            err: flag,
            safe: safe
        });
    }

    /**
     * 设置PBOC圈存密钥
     * @param {KeyType} oldType 旧密钥类型 参考KeyType 注意不为NEW则为修改密钥
     * @param {number} id 密钥标识ID
     * @param {KeyAttr} attr 密钥属性选项 参考KeyAttr方法生成
     * @param {KeyData} key 新添加或修改的密钥数据对象 参考KeyData方法生成
     * @param {Power} use 使用所需权值 参考Power方法生成
     * @param {Power} ren 更改所需权值 参考Power方法生成
     * @param {number} ver 定义密钥版本号 可空默认0x00
     * @param {number} flag 定义算法标识 可空默认0x00
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略 注意未添加主控密钥时无效
     * @returns {Promise} err/msg + retval
     */
    async setKeyAdd(oldType, id, attr, key, use, ren, ver = 0x00, flag = 0x00, safe = null) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_writeKey, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            oldType: oldType,
            id: id,
            type: Rokato.CosFm2.KeyType.LK,
            attr: attr,
            key: key,
            use: use,
            ren: ren,
            pow: ver,
            err: flag,
            safe: safe
        });
    }

    /**
     * 设置解锁口令密钥
     * @param {KeyType} oldType 旧密钥类型 参考KeyType 注意不为NEW则为修改密钥
     * @param {number} id 密钥标识ID
     * @param {KeyAttr} attr 密钥属性选项 参考KeyAttr方法生成
     * @param {KeyData} key 新添加或修改的密钥数据对象 参考KeyData方法生成
     * @param {Power} use 使用所需权值 参考Power方法生成
     * @param {Power} ren 更改所需权值 参考Power方法生成
     * @param {KeyErr} err 错误计数 参考KeyErr方法生成
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略 注意未添加主控密钥时无效
     * @returns {Promise} err/msg + retval
     */
    async setKeyUnlock(oldType, id, attr, key, use, ren, err, safe = null) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_writeKey, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            oldType: oldType,
            id: id,
            type: Rokato.CosFm2.KeyType.PUK,
            attr: attr,
            key: key,
            use: use,
            ren: ren,
            pow: 0xFF,
            err: err,
            safe: safe
        });
    }

    /**
     * 设置文件线路安全密钥
     * @param {KeyType} oldType 旧密钥类型 参考KeyType 注意不为NEW则为修改密钥
     * @param {number} id 密钥标识ID
     * @param {KeyAttr} attr 密钥属性选项 参考KeyAttr方法生成
     * @param {KeyData} key 新添加或修改的密钥数据对象 参考KeyData方法生成
     * @param {Power} use 使用所需权值 参考Power方法生成
     * @param {Power} ren 更改所需权值 参考Power方法生成
     * @param {KeyErr} err 错误计数 参考KeyErr方法生成
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略 注意未添加主控密钥时无效
     * @returns {Promise} err/msg + retval
     */
    async setKeySafe(oldType, id, attr, key, use, ren, err, safe = null) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_writeKey, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            oldType: oldType,
            id: id,
            type: Rokato.CosFm2.KeyType.AMK,
            attr: attr,
            key: key,
            use: use,
            ren: ren,
            pow: 0xFF,
            err: err,
            safe: safe
        });
    }

    /**
     * 设置重置口令密钥
     * @param {KeyType} oldType 旧密钥类型 参考KeyType 注意不为NEW则为修改密钥
     * @param {number} id 密钥标识ID
     * @param {KeyAttr} attr 密钥属性选项 参考KeyAttr方法生成
     * @param {KeyData} key 新添加或修改的密钥数据对象 参考KeyData方法生成
     * @param {Power} use 使用所需权值 参考Power方法生成
     * @param {Power} ren 更改所需权值 参考Power方法生成
     * @param {KeyErr} err 错误计数 参考KeyErr方法生成
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略 注意未添加主控密钥时无效
     * @returns {Promise} err/msg + retval
     */
    async setKeyReset(oldType, id, attr, key, use, ren, err, safe = null) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_writeKey, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            oldType: oldType,
            id: id,
            type: Rokato.CosFm2.KeyType.RPK,
            attr: attr,
            key: key,
            use: use,
            ren: ren,
            pow: 0xFF,
            err: err,
            safe: safe
        });
    }

    /**
     * 添加用户PIN字符口令
     * @param {number} id 密钥标识ID 注意0x00主口令支持解锁重置错误计数
     * @param {string} pin 新添加的字符口令 不能小于3和超过12
     * @param {Power} use 使用所需权值 参考Power方法生成
     * @param {number} pow 提升的权值 范围0~15
     * @param {KeyErr} err 错误计数 参考KeyErr方法生成
     * @returns {Promise} err/msg + retval
     */
    async addPin(id, pin, use, pow, err) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosFm2_writeKey, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            oldType: Rokato.CosFm2.KeyType.NEW,
            id: id,
            type: Rokato.CosFm2.KeyType.PIN,
            attr: Rokato.CosFm2.KeyAttr(false, false),
            key: Rokato.Cos7816.KeyData(pin, Rokato.Cos7816.KeyAlg.PIN),
            use: use,
            ren: 0xEF,
            pow: pow,
            err: err,
            safe: null
        });
    }

    /**
     * FMCOS密钥类型获取字符串描述
     * @param {KeyType} type FMCOS密钥类型 参考Rokato.CosFm2.KeyType
     */
    static keyTypeStr(type) {
        switch (type) {
            case Rokato.CosFm2.KeyType.EAK:
                return "外部认证密钥";
            case Rokato.CosFm2.KeyType.AMK:
                return "应用维护密钥";
            case Rokato.CosFm2.KeyType.IAK_ENC:
                return "内部加密密钥";
            case Rokato.CosFm2.KeyType.IAK_DEC:
                return "内部解密密钥";
            case Rokato.CosFm2.KeyType.IAK_MAC:
                return "内部校验密钥";
            case Rokato.CosFm2.KeyType.IAK_TAC:
                return "交易认证密钥";
            case Rokato.CosFm2.KeyType.PUK:
                return "口令解锁密钥";
            case Rokato.CosFm2.KeyType.RPK:
                return "重置口令密钥";
            case Rokato.CosFm2.KeyType.PIN:
                return "口令认证密钥";
            case Rokato.CosFm2.KeyType.UOK:
                return "修改透支密钥";
            case Rokato.CosFm2.KeyType.UK:
                return "圈提减款密钥";
            case Rokato.CosFm2.KeyType.PK:
                return "消费取现密钥";
            case Rokato.CosFm2.KeyType.LK:
                return "圈存加款密钥";
            default:
                return "未知类型密钥";
        }
    }

    static __LogFileSize(logMax, logLen) {
        return (logMax << 8) | logLen;
    }

    __cos7816 = null;
}

/**
 * FMCOS文件类型
 * @readonly
 * @enum {number}
 */
Rokato.CosFm2.FileType = {
    /** 目录空间文件 */
    DF: 0x38,
    /** 安全密钥文件 */
    KEY: 0x3F,
    /** PBOC钱包文件 */
    PBOC: 0x2F,
    /** 普通数据文件 */
    BIN: 0x28,
    /** 定长记录文件 */
    FIX: 0x2A,
    /** 循环记录文件 */
    LOOP: 0x2E,
    /** 变长记录文件 */
    TLV: 0x2C,
}

/**
 * FMCOS密钥类型
 * @readonly
 * @enum {number}
 */
Rokato.CosFm2.KeyType = {
    /** 添加密钥专用 */
    NEW: 0x01,
    /** 卡片主控密钥 */
    CCK: 0x39,
    /** 应用主控密钥 */
    ACK: 0x39,
    /** 外部认证密钥 */
    EAK: 0x39,
    /** 应用维护密钥 */
    AMK: 0x36,
    /** 内部加密密钥 */
    IAK_ENC: 0x30,
    /** 内部解密密钥 */
    IAK_DEC: 0x31,
    /** 内部校验密钥 */
    IAK_MAC: 0x32,
    /** 交易认证密钥 */
    IAK_TAC: 0x34,
    /** 交易认证密钥 */
    TK: 0x34,
    /** 口令解锁密钥 */
    PUK: 0x37,
    /** 重置口令密钥 */
    RPK: 0x38,
    /** 口令认证密钥 */
    PIN: 0x3A,
    /** 修改透支密钥 */
    UOK: 0x3C,
    /** 圈提减款密钥 */
    UK: 0x3D,
    /** 消费取现密钥 */
    PK: 0x3E,
    /** 圈存加款密钥 */
    LK: 0x3F,
}

Rokato.Utils.deepFreeze(Rokato.CosFm2);


/**
 * 中国人民银行PBOC2.0协议COS功能
 */
Rokato.CosPboc2 = class {

    /**
     * 构造CosPboc2操作对象
     * @param {Cos7816} cos7816 Cos7816操作对象
     */
    constructor(cos7816) { this.__cos7816 = cos7816; }

    /**
     * 修改用户PIN字符口令
     * @param {number} id 口令标识ID
     * @param {string} oldPin 原字符口令 不能小于3和超过12
     * @param {string} newPin 新字符口令 不能小于3和超过12
     * @returns {Promise} err/msg + retval
     */
    async renPin(id, oldPin, newPin) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosPboc2_renPin, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            id: id,
            oldPin: oldPin,
            newPin: newPin
        });
    }

    /**
     * 重置用户PIN字符口令
     * @param {number} id 口令标识ID对应的重置口令密钥标识ID
     * @param {KeyData} key 重置口令密钥对象 参考KeyData方法生成
     * @param {string} pin 重置的新字符口令 不能小于3和超过12
     * @returns {Promise} err/msg + retval
     */
    async resetPin(id, key, pin) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosPboc2_resetPin, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            id: id,
            key: key,
            pin: pin
        });
    }

    /**
     * 解锁用户PIN字符口令
     * @param {number} id 口令标识ID对应的解锁口令密钥标识ID
     * @param {KeyData} key 解锁口令密钥对象 参考KeyData方法生成
     * @param {string} pin 正确的字符口令 不能小于3和超过12
     * @returns {Promise} err/msg + retval
     */
    async unlockPin(id, key, pin) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosPboc2_unlockPin, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            id: id,
            key: key,
            pin: pin
        });
    }

    /**
     * 解锁当前应用目录
     * @param {KeyData} key 主线路安全密钥对象 参考KeyData方法生成
     * @returns {Promise} err/msg + retval
     */
    async unlockApp(key) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosPboc2_unlockApp, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            key: key
        });
    }

    /**
     * 锁定当前应用目录
     * @param {KeyData} key 主线路安全密钥对象 参考KeyData方法生成
     * @param {boolean} die 是否锁死,如果为true则为永久锁定无法解锁
     * @returns {Promise} err/msg + retval
     */
    async lockApp(key, die) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosPboc2_lockApp, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            key: key,
            die: die
        });
    }

    /**
     * 永久锁定卡片所有应用目录
     * @param {KeyData} key 主线路安全密钥对象 参考KeyData方法生成
     * @returns {Promise} err/msg + retval
     */
    async lockCard(key) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosPboc2_lockCard, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            key: key
        });
    }

    /**
     * 读取余额
     * @param {PurseType} type 钱包类型 参考Rokato.CosPboc2.PurseType
     * @returns {Promise} err/msg + retval/money
     */
    async getBalance(type) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosPboc2_getBalance, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            type: type
        });
    }

    /**
     * 获取订单认证数据
     * @param {OrderType} type 订单类型 参考Rokato.CosPboc2.OrderType
     * @param {number} seq 订单序号
     * @returns {Promise} err/msg + retval/tac/mac
     */
    async getOrderTac(type, seq) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosPboc2_getOrderTac, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            type: type,
            seq: seq
        });
    }

    /**
     * 通用订单请求
     * @param {OrderType} type 订单类型 参考Rokato.CosPboc2.OrderType
     * @param {number} id 业务密钥标识ID
     * @param {KeyData} key 业务密钥数据对象 参考KeyData方法生成
     * @param {number} money 订单金额 单位分
     * @param {number} times 订单时间戳(秒级) 默认0自动获取
     * @param {string} number 订单终端编号-必须为12个十六进制字符串 默认6字节全0x00
     * @param {KeyData} keyTac 订单验证TAC密钥对象 默认空忽略验证
     * @returns {Promise} err/msg + retval/orderData(请求响应订单数据对象)
     */
    async orderReq(type, id, key, money, times = 0, number = '000000000000', keyTac = null) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosPboc2_orderReq, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            type: type,
            id: id,
            key: key,
            orderData: { number: number, money: money, times: times },
            keyTac: keyTac
        });
    }

    /**
     * 通用订单提交
     * @param {object} orderData 由订单请求响应的订单数据对象
     * @returns {Promise} err/msg + retval/orderData(提交响应订单数据对象)
     */
    async orderCommit(orderData) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosPboc2_orderCommit, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            orderData: orderData
        });
    }

    /**
     * 复合消费写记录文件数据
     * @param {number} sfid 文件短标识符(0x01~0x1F) 0x00=当前打开的文件
     * @param {number} id 写入位置的记录编号或标识 如果为0x00则代表当前记录
     * @param {LogIdMode} mode 记录ID模式 参考LogIdMode
     * @param {string} data 写入的十六进制字符串数据 注意不能超过创建时定长记录大小
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略
     * @returns {Promise} err/msg + retval
     */
    async etcWriteLog(sfid, id, mode, data, safe = null) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosPboc2_etcWriteLog, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            sfid: sfid,
            id: id,
            mode: mode,
            data: data,
            safe: safe
        });
    }

    /**
     * 复合消费写变长TLV记录文件数据 注意已添加的TLV记录不能改变长度
     * @param {number} sfid 文件短标识符(0x01~0x1F) 0x00=当前打开的文件
     * @param {number} id 写入位置的记录编号或标识 如果为0x00则代表当前记录
     * @param {LogIdMode} mode 记录ID模式 参考LogIdMode
     * @param {TlvData} tlvData 写入的变长记录TLV格式数据 参考TlvData方法生成
     * @param {RfSafe} safe 线路安全选项 参考RfSafe方法生成 默认忽略
     * @returns {Promise} err/msg + retval
     */
    async etcWriteTlv(sfid, id, mode, tlvData, safe = null) {
        return this.__cos7816.__cpuCosReader.__rfReader.__plug.asyncSend(Rokato.RktPlug.GCODE.CosPboc2_etcWriteLog, {
            rfIdx: this.__cos7816.__cpuCosReader.__rfReader.__rfIdx,
            cpuId: this.__cos7816.__cpuCosReader.__cpuId,
            sfid: sfid,
            id: id,
            mode: mode,
            data: Rokato.Cos7816.dumpTlvData(tlvData),
            safe: safe
        });
    }


    /**
     * 解析订单记录数据
     * @param {string} logHex 23字节订单记录
     * @returns {object} 数据异常解析失败返回null 否则返回解析后的对象
     */
    static parseOrderLog(logHex) {
        if (logHex.length != 23 * 2) return null;
        let result = {};
        result.seq = parseInt(logHex.substring(0, 4), 16);
        result.limit = parseInt(logHex.substring(4, 10), 16);
        result.money = parseInt(logHex.substring(10, 18), 16);
        result.type = parseInt(logHex.substring(18, 20), 16);
        result.number = logHex.substring(20, 32);
        result.bcdTime = logHex.substring(32);
        const year = parseInt(result.bcdTime.substring(0, 4), 10);
        const month = parseInt(result.bcdTime.substring(4, 6), 10) - 1;
        const day = parseInt(result.bcdTime.substring(6, 8), 10);
        const hour = parseInt(result.bcdTime.substring(8, 10), 10);
        const minute = parseInt(result.bcdTime.substring(10, 12), 10);
        const second = parseInt(result.bcdTime.substring(12, 14), 10);
        result.times = Math.round(new Date(year, month, day, hour, minute, second) / 1000);
        return result;
    }

    /**
     * 获取订单类型名称
     * @param {OrderType} type 订单类型 参考Rokato.CosPboc2.OrderType
     */
    static orderTypeStr(type) {
        switch (type) {
            case Rokato.CosPboc2.OrderType.ED_ADD:
                return "存折圈存加款";
            case Rokato.CosPboc2.OrderType.EP_ADD:
                return "钱包圈存加款";
            case Rokato.CosPboc2.OrderType.ED_SUB:
                return "存折圈提减款";
            case Rokato.CosPboc2.OrderType.ED_ATM:
                return "存折取现减款";
            case Rokato.CosPboc2.OrderType.ED_POS:
                return "存折消费减款";
            case Rokato.CosPboc2.OrderType.EP_POS:
                return "钱包消费减款";
            case Rokato.CosPboc2.OrderType.ED_REN:
                return "存折修改透支";
            case Rokato.CosPboc2.OrderType.EP_ETC:
                return "钱包复合消费";
            case Rokato.CosPboc2.OrderType.ED_ETC:
                return "存折复合消费";
            default:
                return "未知订单类型";
        }
    }

    __cos7816 = null;
}

/**
 * PBOC钱包类型
 * @readonly
 * @enum {number}
 */
Rokato.CosPboc2.PurseType = {
    /** 电子存折 占用文件标识0x0001 支持所有功能 需验证用户PIN口令 */
    ED: 0x01,
    /** 电子钱包 占用文件标识0x0002 无需验证PIN消费 不支持提现和透支 */
    EP: 0x02,
}

/**
 * PBOC订单类型
 * @readonly
 * @enum {number}
 */
Rokato.CosPboc2.OrderType = {
    /** 存折圈存加款 */
    ED_ADD: 0x01,
    /** 钱包圈存加款 */
    EP_ADD: 0x02,
    /** 存折圈提减款 */
    ED_SUB: 0x03,
    /** 存折取现减款 */
    ED_ATM: 0x04,
    /** 存折消费减款 */
    ED_POS: 0x05,
    /** 钱包消费减款 */
    EP_POS: 0x06,
    /** 存折修改透支 */
    ED_REN: 0x07,
    /** 钱包复合消费 */
    EP_ETC: 0x09,
    /** 存折复合消费 */
    ED_ETC: 0x0A,
}

Rokato.Utils.deepFreeze(Rokato.CosPboc2);


/**
 * 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;

    /**
     * 构造Iso15693操作对象
     * @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 {

    /**
     * 构造UdpComm操作对象
     * @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);


/**
 * 业务开发插件助手类 专注于封装常用且繁琐的通用需求
 */
Rokato.PlugAssist = class {
    /** 限定最低要求版本 */
    minVersion = null;
    /** 限定兼容主版本号 */
    majorVer = null;
    /** 错误提示回调 */
    errTip = null;
    /** 断开通知回调 */
    dieCb = null;
    /** 插件登录用户 */
    user = null;
    /** 插件登录密码 */
    pass = null;
    /** 插件通讯对象 */
    plug = null;
    /** RF读卡器对象 */
    rf = null;
    /** RF是否初始化 */
    hasInitRf = false;
    /** RF循环是否启动  */
    hasRfLoop = false;

    __reset() {
        this.hasInitRf = false;
        this.hasRfLoop = false;
    }

    /**
     * 构造业务插件助手操作对象
     * @param {string} minVersion 限定最低要求版本
     * @param {callback} errTipCb 错误提示回调 默认alert
     * @param {callback} dieCb 意外断开通知回调 默认忽略
     * @param {string} url 接口服务地址 默认 ws://127.0.0.1:3080
     * @param {string} user 登录用户默认为 "public"
     * @param {string} pass 登录密码默认为 "Rokato"
     */
    constructor(minVersion, errTipCb = msg => { alert(msg); }, dieCb = null, url = "ws://127.0.0.1:3080", user = "public", pass = "Rokato") {
        this.minVersion = minVersion;
        this.majorVer = minVersion.substring(0, minVersion.indexOf('.'));
        this.errTip = errTipCb;
        this.dieCb = dieCb;
        this.user = user;
        this.pass = pass;
        this.plug = new Rokato.RktPlug(async evt => {
            this.__reset();
            if (this.dieCb) this.dieCb(evt);
            else this.errTip('接口服务意外断开!');
        }, url);
        this.rf = new Rokato.RfReader(this.plug);
    }

    /**
     * 连接登录插件服务
     * @returns {boolean}
     */
    async login() {
        let data;
        for (let i = 0; i < 10; ++i) {
            this.__reset();
            data = await this.plug.login(this.user, this.pass);
            if (data.err == Rokato.RktPlug.GERR.ConnectFail.err ||
                data.err == Rokato.RktPlug.GERR.ReConnectClose.err) {
                if (i < 1) {
                    if (typeof OCSBrowser != 'undefined')
                        OCSBrowser.StartTool("rktplug.lnk", "");
                    else
                        location.href = Rokato.RktPlug.execUrl();
                    continue;
                } else if (i < 2) {
                    continue;
                } else if (i < 3) {
                    this.errTip("插件连接失败,即将重试(" + (3 - i) + ")次");
                    continue;
                }
            }
            if (data.err != 0) break;
            data = await this.plug.version();
            if (data.err != 0) break;
            if (Rokato.Utils.compareVersion(data.version.substring(data.version.indexOf('Ver:') + 4), this.minVersion) < 0) {
                this.errTip('插件版本过低请升级到v' + this.minVersion + '或以上版本');
                break;
            } else if (data.version.indexOf('Ver:' + this.majorVer + '.') < 1) {
                this.errTip('插件版本不兼容请更换v' + this.majorVer + '系列版本');
                break;
            }
            return true;
        }
        this.plug.close();
        if (data.err != 0) this.errTip(data.msg);
        return false;
    }

    /**
     * 主动结束通讯并关闭某些占用
     */
    async exit() {
        if (this.rf) await this.rf.close();
        if (this.plug) this.plug.close();
        this.__reset();
    }

    /**
     * 初始化RF读卡器
     * @param {DevType} devType 设备类型 默认自动识别
     * @param {boolean} iso14443A 是否启用Iso14443A协议支持 默认启用
     * @param {boolean} iso14443B 是否启用Iso14443B协议支持 默认启用
     * @param {boolean} iso15693 是否启用Iso15693协议支持 默认启用
     * @returns {boolean}
     */
    async initRf(devType = Rokato.RfReader.DevType.AUTO, iso14443A = true, iso14443B = true, iso15693 = true) {
        let data;
        if (!this.plug.isLogin()) {
            if (!await this.login()) return false;
        }
        while (true) {
            data = await this.rf.init(devType);
            if (data.err != 0) break;
            if (iso14443A) {
                data = await this.rf.setIso14443A(true);
                if (data.err != 0) break;
            }
            if (iso14443B) {
                data = await this.rf.setIso14443B(true);
                if (data.err != 0) break;
            }
            if (iso15693) {
                data = await this.rf.setIso15693(true);
                if (data.err != 0) break;
            }
            this.hasInitRf = true;
            return true;
        }
        this.errTip(data.msg);
        return false;
    }

    /**
     * RF打开设备连接
     * @returns {boolean}
     */
    async rfOpen() {
        let data;
        if (!this.hasInitRf) {
            if (!await this.initRf()) return false;
        }
        while (true) {
            data = await this.rf.ping();
            if (data.err != 0) break;
            if (data.retval == -1) {
                this.errTip("读卡器已被占用,请停止其他正在使用的程序!");
                return false;
            } else if (data.retval != 1) {
                this.errTip("连接读卡器失败,请检查设备是否正常!");
                return false;
            }
            return true;
        }
        this.errTip(data.msg);
        return false;
    }

    /**
     * 查找一张卡片ID
     * @param {RfType} rfType 过滤协议类型 默认忽略
     * @param {boolean} noCloseRf 是否不自动关闭RF设备 默认自动关闭
     * @returns {object} 失败返回null 成功返回对象属性示例如下
     *    {
     *        "rf_type": 1,
     *        "uid": "FFFFFFFFFFFFFFFFFFFF",
     *        "atqa": 65535,
     *        "sak": 255
     *    }
     */
    async findOneId(rfType = Rokato.RfReader.RfType.None, noCloseRf = false) {
        let id = null;
        if (!await this.rfOpen()) return id;
        const data = await this.rf.findId();
        while (true) {
            if (data.err != 0) {
                this.errTip(data.msg);
                break;
            }
            const ids = data.retval;
            await this.rf.beep();
            if (ids.length < 1) {
                this.errTip('未发现卡片,请靠近感应区后再试!');
                break;
            }
            if (ids.length > 1) {
                this.errTip('请勿将多张卡同时靠近感应区,防止操作混乱!');
                break;
            }
            if (rfType != Rokato.RfReader.RfType.None && ids[0].rf_type != rfType) {
                this.errTip('卡片芯片不匹配,请使用正确的卡片!');
                break;
            }
            id = ids[0];
            break;
        }
        noCloseRf ? await this.rf.rfReset() : await this.rf.close();
        return id;
    }

    /**
     * 等待查找到一张卡片ID
     * @param {RfType} rfType 过滤协议类型 默认忽略
     * @param {boolean} noCloseRf 是否不自动关闭RF设备 默认自动关闭
     * @returns {object} 返回参考findOneId方法
     */
    async waitFindOneId(rfType = Rokato.RfReader.RfType.None, noCloseRf = false) {
        let id = null;
        if (this.hasRfLoop) await this.rf.loopStop();
        if (!await this.rfOpen()) return id;
        this.hasRfLoop = true;
        const data = await this.rf.waitFindId();
        this.hasRfLoop = false;
        while (true) {
            if (data.err != 0) {
                this.errTip(data.msg);
                break;
            }
            const ids = data.retval;
            if (ids.length < 1) break;
            await this.rf.beep();
            if (ids.length > 1) {
                this.errTip('请勿将多张卡同时靠近感应区,防止操作混乱!');
                break;
            }
            if (rfType != Rokato.RfReader.RfType.None && ids[0].rf_type != rfType) {
                this.errTip('卡片芯片不匹配,请使用正确的卡片!');
                break;
            }
            id = ids[0];
            break;
        }
        noCloseRf ? await this.rf.rfReset() : await this.rf.close();
        return id;
    }

    /**
     * 循环查找到卡片ID
     * @param {callback} workCallback 卡片ID处理回调 回调返回false将停止循环 参数参考findOneId返回成功对象属性
     * @param {callback} errorCallback 错误停止回调 默认忽略
     * @param {boolean} noCloseRf 是否不自动关闭RF设备 默认自动关闭
     * @returns {boolean} 开启循环寻卡失败返回false
     */
    async loopFindId(workCallback, errorCallback = null, noCloseRf = false) {
        let data;
        if (this.hasRfLoop) await this.rf.loopStop();
        if (!await this.rfOpen()) return false;
        while (true) {
            data = await this.rf.loopFindId(async data => {
                const ids = data.retval;
                let isNext = true;
                for (let i = 0; isNext && i < ids.length; ++i) isNext = await workCallback(ids[i]);
                data = isNext ? await this.rf.loopContinue() : await this.rf.loopStop();
                if (data.err != 0) {
                    isNext = false;
                    if (errorCallback) errorCallback(data);
                    else this.errTip(data.msg);
                }
                if (!isNext) {
                    this.hasRfLoop = false;
                    if (!noCloseRf) await this.rf.close();
                }
            }, 100, true, true);
            if (data.err != 0) break;
            this.hasRfLoop = true;
            return true;
        }
        this.hasRfLoop = false;
        if (!noCloseRf) await this.rf.close();
        if (data.err != 0) {
            if (errorCallback) errorCallback(data);
            else this.errTip(data.msg);
        }
        return false;
    }
}

Rokato.Utils.deepFreeze(Rokato.PlugAssist);


Object.freeze(Rokato);