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
JavaScript
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);
};