UNPKG

ibctminer

Version:

```js const IntMiner = require('./src'); const Debug = require('./src/log')(); const fs = require('fs'); const COMP = '[SIPC]';

871 lines (776 loc) 30.9 kB
const Delimiter = require('@serialport/parser-delimiter'); const GenopApi = require('../algo/genopApi.js'); const Debug = require('../log')(); var SerialPort = require('serialport'); const EventEmitter = require('events'); var waitUntil = require('wait-until'); var crc32 = require('crc32'); const COMP = '[XMR]:'; const hwTarget = Buffer.from([0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); const PV = 0x10; const TypeSetBootMode = 0xA3; const TypeUpdateFW = 0xAA; const TypeQueryInfo = 0xA4; const TypeProductTest = 0xAB; const TypeSetLED = 0xA6; const TypeSendOpcode = 0xA7; const TypeSendWork = 0xA1; const TypeSetHWParams = 0xA2; const TypeReboot = 0xAC; const TypeRecvNonce = 0x51; const TypeRecvState = 0X52; const TypeRecvBootMode = 0x53; const TypeRecvInfo = 0x54; const TypeRecvOp = 0x57; const TypeRecvFWState = 0x5A; const TypeRecvTestResult = 0x5B; const pktHeader = Buffer.from([0xA5, 0x3C, 0x96]); const pktEnder = Buffer.from([0x69, 0xC3, 0x5A]); const typeOffset = 0; const xmrcfg = { model: "spark", algo: "scryptNight-v4", varity: 0x4, targetFreq: 950, //MHz targetVoltage: 750, //mv targetTemp: 65, warnTemp: 115, offTemp: 125 }; class xmr extends EventEmitter { constructor({devPath, algo, varity, crypto}) { super(); var _this = this; _this.devPath = devPath; _this.algo = algo; if (_this.algo.name === "scryptNight-v4") _this.varity = 4; else if (_this.algo.name === "scryptNight-v2") _this.varity = 2; else if (_this.algo.name === "spark") _this.varity = 13; _this.crypto = crypto; _this.MinerShouldStop = false; _this.inited = false; _this.txTimeoutCnt = 0; _this.work = { jobID: 0, target: 0, snonce: 0, height: 0 }; _this.info = { firmwareVer: 'V0.0.1', modelName: 'spark', sn:'unknown', hashRation: 0 }; _this.firmware = { retryCnt : 2, FWPageSize : 256, curState : 0, curID : 0 } _this.submitNonce = null; _this.jobsDone = 0; _this.status = { chips:0, temp:0, voltage:0, freq:0, varity:0x4, cores:0, goodcores:0, scanbits:0, scantime:0, tempwarn:0 }; _this.port = new SerialPort(_this.devPath, { baudRate: 115200, dataBits:8, stopBits:1, parity:'none', rtscts:false }, function (err) { if (err) { //_this.emit("error", "打开串口失败"); Debug.IbctLogErr(COMP, _this.devPath, '打开串口失败: ', err.message); return; } else Debug.IbctLogErr(COMP, _this.devPath, '打开串口成功'); }); const parser = _this.port.pipe(new Delimiter({ delimiter: pktEnder})); parser.on('data', function(data) { _this.xmrParseNotify(_this, data); }); _this.on("error", function(err) { Debug.IbctLogDbg(COMP, err); }) _this.on("warning", function(err) { Debug.IbctLogDbg(COMP, err); }) } xmrParseNotify(_this, data) { //Debug.IbctLogInfo(COMP, _this.devPath, 'Recv Pkt', data.toString('hex')); var location = data.indexOf(pktHeader); if(location === -1) { Debug.IbctLogErr(COMP, _this.devPath, 'Recv Invalid PKT', data); return; } var typeLocation = location + pktHeader.length + typeOffset; switch(data[typeLocation]) { case TypeRecvNonce: var jobID = data[9]; //var chipID = data[10]; //var coreID = data[11]; var lowNonce = data.readUInt32LE(12); //nonce var nonce = Buffer.alloc(4); nonce.writeUInt32LE(lowNonce, 0); if(jobID !== _this.work.jobID) { Debug.IbctLogDbg(COMP, _this.devPath, 'Find Stale ', jobID, _this.work.jobID, nonce.toString('hex')); } else { //var _nonce = Buffer.alloc(8); //_nonce.writeUInt32BE(_this.work.highNonce, 0); //_nonce.writeUInt32BE(lowNonce, 4); //var hash = Buffer.alloc(32); //var Hash = Buffer.alloc(32); //data.copy(hash, 0, 21, 53); //for (var i = 0; i < 32; i++) //Hash[i] = hash[31-i]; //Debug.IbctLogDbg(COMP, _this.devPath, 'Find Nonce ', nonce.toString('hex'), _nonce.toString('hex'), '---Hash', Hash.toString('hex')); if(_this.submitNonce) _this.submitNonce(null, nonce); } break; case TypeRecvInfo: _this.recvInfoPkt = true; //Debug.IbctLogDbg(COMP, _this.devPath, 'RecvInfoPkt::', data.toString('hex')); _this.info.modelName = data.toString('utf8', 10, 10 + data[9]); _this.info.firmwareVer = data.toString('utf8', 27, 27 + data[26]); _this.info.sn = data.toString('utf8', 36, 36 + data[35]); _this.info.hashRation = data.readUInt16LE(69); Debug.IbctLogDbg(_this.info); break; case TypeRecvFWState: _this.recvFWStatePkt = true; _this.firmware.curID = data.readUInt32LE(9); _this.firmware.curState = data[13]; break; case TypeRecvBootMode: _this.recvBootModePkt = true; break; case TypeRecvOp: _this.recvOpPkt = true; break; case TypeRecvState: _this.recvStatePkt = true; _this.recvQueryStatePkt = true; //Debug.IbctLogInfo(COMP, _this.devPath, 'RecvState pkt::', data.toString('hex')); _this.status.chips = data[9]; _this.status.cores = data[10]; _this.status.goodcores = data[11]; _this.status.scanbits = data[12]; _this.status.scantime = data.readUInt16LE(13) * 100; //ms _this.status.voltage = data.readUInt16LE(15); //mV _this.status.freq = data.readUInt16LE(17); //MHz _this.status.varity = data.readUInt32LE(19); _this.status.temp = data[23]; _this.status.hwreboot = data[24]; _this.status.tempwarn = data[25]; break; case TypeRecvTestResult: _this.recvPTInfoPkt = true; break; default: Debug.IbctLogErr(COMP, _this.devPath, 'Recv Unsupported PKT Type', data[location + typeOffset]) break; } return; } xmrSendPkt (pkt) { var _this = this; var offset = 0; var length = 0; Object.keys(pkt).forEach(function(key) { if(Buffer.isBuffer(pkt[key])) { length += pkt[key].length; } }); var msg = Buffer.alloc(length); Object.keys(pkt).forEach(function(key) { if(Buffer.isBuffer(pkt[key])) { pkt[key].copy(msg, offset); offset += pkt[key].length; } }); //Debug.IbctLogDbg(COMP, _this.devPath, 'Send pkt', msg.toString('hex')); _this.port.write(msg, function(err) { if (err) { Debug.IbctLogErr(COMP, _this.devPath, 'Error on write: ', err.message) } _this.port.drain(function(err) { if (err) { Debug.IbctLogErr(COMP, _this.devPath, 'Error on Drain: ', _this.devPath, err.message) } }) }) } xmrGetState(_this) { var pktQueryStatus = { header: Buffer.from(pktHeader), type: Buffer.from([TypeSetHWParams]), version: Buffer.from([PV]), pktlen: Buffer.from([0x7, 0x0 ,0x0 ,0x0]), flag:Buffer.from([0x52]), ender:Buffer.from(pktEnder) }; _this.recvQueryStatePkt = false; _this.xmrSendPkt(pktQueryStatus); /*TODO Wait response here*/ return true; } async xmrGetStaticInfo(modelName) { var _this = this; var pktQueryInfo = { header: Buffer.from(pktHeader), type: Buffer.from([TypeQueryInfo]), version: Buffer.from([PV]), pktlen: Buffer.from([0x6, 0x0 ,0x0 ,0x0]), ender:Buffer.from(pktEnder) }; _this.recvInfoPkt = false; _this.xmrSendPkt(pktQueryInfo); return new Promise(function (resolve, reject) { waitUntil() .interval(50) .times(10) .condition(function() { return _this.recvInfoPkt }) .done(function(result) { if(result === false) { Debug.IbctLogErr(COMP, _this.devPath, '获取矿机信息出错'); resolve(1); } else { if(modelName === _this.info.modelName) { resolve(0); } else { resolve(1); } } }) }); } xmrSetBootMode() { var _this = this; var pktSetBootMode = { header: Buffer.from(pktHeader), type: Buffer.from([TypeSetBootMode]), version: Buffer.from([PV]), pktlen: Buffer.from([0x7, 0x0 ,0x0 ,0x0]), ender:Buffer.from(pktEnder) }; _this.recvBootModePkt = false; _this.xmrSendPkt(pktSetBootMode); } xmrBurnFWInit() { var _this = this; var pktSetBootMode = { header: Buffer.from(pktHeader), type: Buffer.from([TypeSetBootMode]), version: Buffer.from([PV]), pktlen: Buffer.from([0x6, 0x0 ,0x0 ,0x0]), ender:Buffer.from(pktEnder) }; _this.recvBootModePkt = false; _this.xmrSendPkt(pktSetBootMode); return new Promise(function (resolve, reject) { waitUntil() .interval(20) .times(50) .condition(function() { return _this.recvBootModePkt }) .done(function(result) { if(result === false) { resolve(1); } else { resolve(0); } }) }) } xmrSetHWParams(varity, freq, voltage) { var _this = this; var pktSetParam = { header: Buffer.from(pktHeader), type: Buffer.from([TypeSetHWParams]), version: Buffer.from([PV]), pktlen: Buffer.from([0x10, 0x00, 0x00, 0x00]), flag:Buffer.from([0xA2]), voltage: Buffer.alloc(2), freq:Buffer.alloc(2), varity:Buffer.alloc(4), targettemp:Buffer.from([80]), ender:Buffer.from(pktEnder) }; pktSetParam.varity.writeUInt32LE(varity, 0); pktSetParam.freq.writeUInt16LE(freq, 0); pktSetParam.voltage.writeUInt16LE(voltage, 0); _this.recvStatePkt = false; _this.xmrSendPkt(pktSetParam); /*TODO Wait response here*/ return true; } xmrSetHWParamsAndWait(varity, freq, voltage) { var _this = this; var pktSetParam = { header: Buffer.from(pktHeader), type: Buffer.from([TypeSetHWParams]), version: Buffer.from([PV]), pktlen: Buffer.from([0x10, 0x00, 0x00, 0x00]), flag:Buffer.from([0xA2]), voltage: Buffer.alloc(2), freq:Buffer.alloc(2), varity:Buffer.alloc(4), targettemp:Buffer.from([80]), ender:Buffer.from(pktEnder) }; pktSetParam.varity.writeUInt32LE(varity, 0); pktSetParam.freq.writeUInt16LE(freq, 0); pktSetParam.voltage.writeUInt16LE(voltage, 0); _this.recvStatePkt = false; _this.xmrSendPkt(pktSetParam); return new Promise(function (resolve, reject) { waitUntil() .interval(20) .times(50) .condition(function() { return _this.recvStatePkt }) .done(function(result) { if(result === false) { Debug.IbctLogErr(COMP, _this.devPath, '设置矿机参数出错'); resolve(1); } else { resolve(0); } }) }) } async xmrSendOpcode(height, rr, dd, opcode) { var _this = this; var pktSendOpcode = { header: Buffer.from(pktHeader), type: Buffer.from([TypeSendOpcode]), version: Buffer.from([PV]), pktlen: Buffer.alloc(4), height: Buffer.alloc(4), rr: Buffer.alloc(1), dd: Buffer.alloc(1), opCode: Buffer.alloc(dd * 2), ender:Buffer.from(pktEnder) }; pktSendOpcode.pktlen.writeUInt32LE(dd * 2 + 12, 0); //pktlen pktSendOpcode.height.writeUInt32LE(height, 0); pktSendOpcode.rr.writeUInt8(rr, 0); pktSendOpcode.dd.writeUInt8(dd, 0); opcode.copy(pktSendOpcode.opCode, 0); _this.recvOpPkt = false; _this.xmrSendPkt(pktSendOpcode); return true; } async xmrWriteJob(jobID, target, data) { var _this = this; var pktSendJob = { header: Buffer.from(pktHeader), type: Buffer.from([TypeSendWork]), version: Buffer.from([PV]), pktlen: Buffer.alloc(4), target:Buffer.alloc(4), startNonce: Buffer.from([0,0,0,0,0,0,0,0]), //startNonce: Buffer.from([0x93,0,0,0,0,0,0,0]), endNonce:Buffer.from([0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff]), jobNum: Buffer.from([1]), jobID: Buffer.alloc(1), jobData:Buffer.alloc(76), ender:Buffer.from(pktEnder) }; pktSendJob.pktlen.writeUInt32LE(104, 0); //pktlen pktSendJob.target.writeUInt32LE(target, 0); //pktSendJob.startNonce.writeUInt32LE(0, 0); //pktSendJob.startNonce.writeUInt32LE(0, 4); //pktSendJob.endNonce.writeUInt32LE(0xffffffff, 0); //pktSendJob.endNonce.writeUInt32LE(0xffffffff, 4); pktSendJob.jobID[0] = jobID; data.copy(pktSendJob.jobData, 0); _this.xmrSendPkt(pktSendJob); return true; } xmrSendFWPktAndWait(cnt, FWPKT) { var _this = this; _this.recvFWStatePkt = false; _this.firmware.curState = 0; _this.firmware.curID = 0; _this.xmrSendPkt(FWPKT); return new Promise(function (resolve, reject) { waitUntil() .interval(20) .times(50) .condition(function() { return _this.recvFWStatePkt }) .done(function(result) { if(result === false) { reject(new Error(__('烧录连接超时'))); } else if(_this.firmware.curState === 0x02) { if(cnt < _this.firmware.retryCnt){ resolve(1); } else { reject(new Error(__('串口出错'))); } } else { resolve(0); } }) }) } async stopScanWork() { var _this = this; Debug.IbctLogInfo(COMP, _this.devPath, 'stopScanWork ...'); //_this.work.jobID = 0; _this.MinerShouldStop = true; } async scanWork(Job, callback) { var _this = this; Debug.IbctLogInfo(COMP, _this.devPath, 'scanWork Begin ...'); //var golden = Buffer.from([ //0x05, 0x05, 0xc0, 0xa7, 0xdb, 0xc7, 0x05, 0xb0, 0xad, 0xf8, 0x2c, 0x58, 0x1a, 0xae, 0xe4, 0x8b, //0x2e, 0x0a, 0xee, 0x2e, 0xa8, 0x97, 0x2d, 0xd7, 0x9d, 0xba, 0xf3, 0xca, 0x28, 0xac, 0xca, 0x5f, //0x73, 0xca, 0x2a, 0x90, 0x9c, 0x8c, 0x24, 0xf7, 0x09, 0x00, 0x80, 0xf9, 0x87, 0x13, 0xc6, 0x91, //0x9a, 0x42, 0x38, 0x9d, 0x53, 0xcb, 0xde, 0xd0, 0x4d, 0x02, 0x6c, 0x1d, 0xe4, 0x25, 0xf8, 0x77, //0xe8, 0x70, 0xb3, 0x8f, 0x91, 0x4c, 0xef, 0x40, 0xc6, 0x7f, 0xa4, 0x00 //]); if (!_this.inited) { callback(null, null); return; } //generate opcode if ((_this.varity === 4 || _this.varity === 13) && Job.height && _this.work.height !== Job.height) { //Debug.IbctLogErr(COMP, 'height', _this.work.height); _this.work.height = Job.height; const Genop = GenopApi({}); var opcode = Genop.genOpcode(_this.work.height); //Debug.IbctLogErr(COMP, 'opcode', opcode.toString('hex')); //Debug.IbctLogErr(COMP, 'l', opcode.length); let i = 0, len = opcode.length; var rr, dd; rr = 0; dd = len / 2; while (i < len) { if ((opcode[i] & 0x0f) === 0x01) rr++; if ((opcode[i] & 0xf0) === 0x10) i += 8; else i += 4; } //Debug.IbctLogErr(COMP, 'rr', rr.toString(16)); //Debug.IbctLogErr(COMP, 'dd', dd.toString(16)); _this.xmrSendOpcode(_this.work.height, rr, dd, opcode); } //send work if(_this.status.scantime === 0) { _this.status.scantime = 300000; //10s default } if(Buffer.compare(hwTarget, Job.target) > 0) { _this.work.target = (hwTarget[0] << 24) | (hwTarget[1] << 16) | (hwTarget[2] << 8) | hwTarget[3]; Job.hwTarget = hwTarget; } else { _this.work.target = (Job.target[0] << 24) | (Job.target[1] << 16) | (Job.target[2] << 8) | Job.target[3]; Job.hwTarget = Job.target; } _this.work.target = _this.work.target >>> 0; _this.work.highNonce = parseInt((Job.snonce) / 0x100000000) >>> 0; _this.work.payload = Buffer.alloc(76); _this.work.data = Buffer.from(Job.data); //Debug.IbctLogDbg(COMP, golden.toString('hex')); Job.data.writeUInt32LE(_this.work.highNonce, 39); Job.data.copy(_this.work.payload, 0); //golden.copy(_this.work.payload, 0); //Debug.IbctLogDbg(COMP, _this.work.payload.toString('hex')); _this.submitNonce = callback; _this.MinerShouldStop = false; _this.jobsDone++; _this.work.jobID++; if(_this.work.jobID === 16) _this.work.jobID = 1; /* Debug.IbctLogInfo(COMP, _this.devPath, 'Work Target:', _this.work.target.toString(16), 'Work highNonce:', _this.work.highNonce.toString(16), 'Work jobID:', _this.work.jobID); */ _this.xmrWriteJob(_this.work.jobID, _this.work.target, _this.work.payload); var interval = 50; //ms var times = (60000 - 500) / interval; waitUntil() .interval(interval) .times(times) .condition(function() { return _this.MinerShouldStop }) .done(function(result) { Debug.IbctLogInfo(COMP, _this.devPath, 'ScanWork Exit', result ? "(NewJob)...": "(ScanTime Out)..."); callback(null, null); }); } async setDevice(varity, freq, voltage) { var _this = this; _this.xmrSetHWParams(varity, freq, voltage); } getInfo() { var _this = this; return _this.info; } getState() { var _this = this; return _this.status; } async detect(modelName) { var _this = this; Debug.IbctLogErr(COMP, _this.devPath, 'xmr detect...'); return await _this.xmrGetStaticInfo(modelName); } async init(params) { var _this = this; Debug.IbctLogErr(COMP, _this.devPath, 'xmr init...'); if(_this.firmware.updating === true){ Debug.IbctLogErr(COMP, _this.devPath, 'Still Updating'); return 1; } if( _this.inited === true){ Debug.IbctLogErr(COMP, _this.devPath, 'Already Inited. return now'); return 0; } //_this.xmrSetHWParams(xmrcfg.varity, xmrcfg.targetFreq, xmrcfg.targetVoltage); var ret = await _this.xmrSetHWParamsAndWait(_this.varity, xmrcfg.targetFreq, xmrcfg.targetVoltage); if (ret) return ret; if (_this.intervalObj) { clearInterval(_this.intervalObj); _this.intervalObj = null; } _this.intervalObj = setInterval(function() { Debug.IbctLogErr(COMP, _this.devPath, 'Temp', _this.status.temp, 'Tempwarn', _this.status.tempwarn, 'Freq', _this.status.freq, 'Jobs', _this.jobsDone); _this.xmrGetState(_this); waitUntil() .interval(50) .times(10) .condition(function() { return _this.recvQueryStatePkt }) .done(function(result) { if(result === false) { _this.txTimeoutCnt++; Debug.IbctLogErr(COMP, _this.devPath, 'xmrGetState Timeout ', _this.txTimeoutCnt); if(_this.txTimeoutCnt > 10) { _this.emit("error", __('获得矿机状态超时')); _this.txTimeoutCnt = 0; } } else { _this.txTimeoutCnt = 0; if((_this.status.temp > xmrcfg.offTemp) || (_this.status.tempwarn)) { _this.emit("error", __('矿机高温关机')); } else if(_this.status.temp > xmrcfg.warnTemp && _this.status.temp < xmrcfg.offTemp) { _this.emit("warning", __('矿机高温警报')); } } }); }, 5000); _this.inited = true; return 0; } async stop(enable) { var _this = this; Debug.IbctLogErr(COMP, _this.devPath, 'xmr', enable ? 'remove...' : 'stop...'); if (!enable) { await _this.xmrSetHWParamsAndWait(0, 0, 0); } _this.inited = false; _this.txTimeoutCnt = 0; _this.freq = 0; _this.voltage = 0; _this.work.jobID = 0; _this.jobsDone = 0; _this.MinerShouldStop = true; if (_this.intervalObj) { clearInterval(_this.intervalObj); _this.intervalObj = null; } if (enable) { await _this.port.flush(function (err) { _this.port.close(function(err) { if(err) Debug.IbctLogDbg(COMP, err.message); }); }); } } async burnFirmware(firmware, callback) { var _this = this; if(_this.firmware.updating === true) { callback(__('升级中,请等待')); return; } if(_this.inited === true) { callback(__('挖矿中,请先暂停挖矿')); return; } var ret = await _this.xmrGetStaticInfo(_this.info.modelName); if (ret === 1) { callback(__('无法获取当前版本号')); return; } var init = await _this.xmrBurnFWInit(); if(init === 0) { _this.firmware.updating = true; } else { _this.firmware.updating = false; callback(__('烧入固件初始化失败')); return; } var fwLen = firmware.length; var totalPktNum = Math.ceil(firmware.length / _this.firmware.FWPageSize) var id = 0; Debug.IbctLogErr(COMP, _this.devPath, 'BurnFW, CurVersion:', _this.info.firmwareVer, 'FW lenth' , fwLen , "PKTnum", totalPktNum); try { while(fwLen > 0) { var currentLen = (fwLen > _this.firmware.FWPageSize) ? _this.firmware.FWPageSize : fwLen; var curStart = firmware.length - fwLen; var curEnd = curStart + currentLen; var pktUpdateFW = { header: Buffer.from(pktHeader), type: Buffer.from([TypeUpdateFW]), version: Buffer.from([PV]), pktLen: Buffer.alloc(4), pktID:Buffer.alloc(4), pageSize:Buffer.alloc(4), curLen:Buffer.alloc(4), flag:Buffer.alloc(1), crc32: Buffer.alloc(4), fwData:Buffer.alloc(currentLen), ender:Buffer.from(pktEnder) }; pktUpdateFW.curLen.writeUInt32LE(currentLen, 0); pktUpdateFW.pktID.writeUInt32LE(id, 0); pktUpdateFW.pageSize.writeUInt32LE(_this.firmware.FWPageSize, 0); pktUpdateFW.pktLen.writeUInt32LE(23 + currentLen); firmware.copy(pktUpdateFW.fwData, 0, curStart, curEnd); fwLen -= currentLen; if(fwLen) { pktUpdateFW.flag[0] = 0x00; } else { pktUpdateFW.flag[0] = 0x01;//last } var msg = Buffer.alloc(23 + currentLen); //console.log('3====>', curStart, curEnd, currentLen, fwLen); pktUpdateFW.type.copy(msg, 0); pktUpdateFW.version.copy(msg, 1); pktUpdateFW.pktLen.copy(msg, 2); pktUpdateFW.pktID.copy(msg, 6); pktUpdateFW.pageSize.copy(msg, 10); pktUpdateFW.curLen.copy(msg, 14); pktUpdateFW.flag.copy(msg, 18); pktUpdateFW.crc32.copy(msg, 19); pktUpdateFW.fwData.copy(msg, 23); var crcValue = parseInt(crc32(msg), 16); pktUpdateFW.crc32.writeUInt32LE(crcValue, 0); for(var i = 0; i < _this.firmware.retryCnt; i++) { var success = await _this.xmrSendFWPktAndWait(i + 1, pktUpdateFW); if(success === 0) break; } if(fwLen === 0) { _this.firmware.updating = false; } callback(null, ((id + 1) / totalPktNum).toFixed(3)); id++; } } catch(err) { Debug.IbctLogErr(COMP, _this.devPath, 'Updat firmware failed ' + err.message); _this.firmware.updating = false; callback(err.message); } } async rebootDev() { Debug.IbctLogErr(COMP, 'Set', this.devPath, 'reboot'); var _this = this; var pktReboot = { header: Buffer.from(pktHeader), type: Buffer.from([TypeReboot]), version: Buffer.from([PV]), pktlen: Buffer.from([0x6, 0x0 ,0x0 ,0x0]), ender:Buffer.from(pktEnder) }; await _this.stop(true) _this.xmrSendPkt(pktReboot); } setLed(Enable) { Debug.IbctLogErr(COMP, 'Set', this.devPath, 'Led to', Enable ? 'ON' : 'OFF'); var _this = this; var ledFlag = Enable === true ? 1 : 0; var pktSetLED = { header: Buffer.from(pktHeader), type: Buffer.from([TypeSetLED]), version: Buffer.from([PV]), pktlen: Buffer.from([0xb, 0x0 ,0x0 ,0x0]), Flag:Buffer.from([ledFlag]), led:Buffer.from([0xE8, 0x3, 0xC8, 0x0]), //ON 1s OFF:200ms ender:Buffer.from(pktEnder) }; _this.xmrSendPkt(pktSetLED); } async burnSNInfo(ptinfo) { var _this = this Debug.IbctLogErr(COMP, _this.devPath, 'Burn Sn Num..', ptinfo) var pktPTInfo = { header: Buffer.from(pktHeader), type: Buffer.from([TypeProductTest]), version: Buffer.from([PV]), pktlen: Buffer.from([70, 0x0, 0x0, 0x0]), SNInfo: Buffer.alloc(32), HashInfo: Buffer.alloc(32), ender: Buffer.from(pktEnder) } if (ptinfo.sn) { var sn = Buffer.from(ptinfo.sn) pktPTInfo.SNInfo[0] = sn.length sn.copy(pktPTInfo.SNInfo, 1) } else { pktPTInfo.SNInfo[0] = 0 } _this.recvPTInfoPkt = false; _this.xmrSendPkt(pktPTInfo) return new Promise(function (resolve, reject) { waitUntil() .interval(50) .times(10) .condition(function () { return _this.recvPTInfoPkt }) .done(function (result) { if (result === false) { Debug.IbctLogErr(COMP, _this.devPath, 'Burn SN num failed..') resolve(__('写入SN序列号失败')) } else { resolve(null) } }) }) } } module.exports = function Getxmr(options = {}) { return new xmr(options); };