tibbo_uploader/tibbo_uploader.js

380 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var net_addr = "192.168.123.255";
//net_addr = "10.0.50.255";
const files_path = "./";
const dgram = require('dgram'),
clui = require('clui'),
// clc = require('cli-color'),
// Line = clui.Line,
Spinner = clui.Spinner,
Progress = clui.Progress,
inquirer = require('inquirer'),
fs = require('fs');
const shortSearch = 2, longSearch = 8;
const countdown = new Spinner('Поиск устройств. Найдено [0] Осталось 10 секунд... ', ['|', '/', '-', ' ']);// ['⣾','⣽','⣻','⢿','⡿','⣟','⣯','⣷']);
const m2 = [
"Загрузка прошивки" ,
{ name: "Загрузка firmware" },
"Перезагрузка",
new inquirer.Separator(),
"Выход",
];
// const confirmMenu = [
// "Да", "Нет"
// ]
const client = dgram.createSocket('udp4');
var number = shortSearch;
var mode = 1; // work mode - 1: search devices, 2 - uploading
var devices = []; // device list
var progressbar;
var step = 1; //upload step
var stopTimer;
var mac=""; // current device mac address
var fdata =[]; // uploading file content
var pageNumber = 0; // page of uploading file
var session = (Math.random() * 0xFFFFFFFF).toString(16); // work session
var pr;
var firmvare = 0;
// **
// Get work network interface
// used for create disable stoofing filter command
// **
function getInterface(){
var net = require('os').networkInterfaces();
for (var ifs in net) {
var res = net[ifs].find(ip => {
return ip.address.includes(net_addr.substring(0,net_addr.length-3))
});
if (res)
return ifs;
}
return "unknown";
}
// function confirm(message){
// var deferred = Q.defer();
// showMenu(confirmMenu, message, (choice)=>{
// console.log(choice)
// if (choice == "Да")
// deferred.resolve(true);
// else
// deferred.reject(false);
// })
// return deferred.promise;
// }
function toBytesInt16(num){
return [(num & 0xff00) >> 8,(num & 0x00ff)];
}
// **
// Send command to device
// **
function send(cmd,addr,msg,result){
var mess = "_"+addr+cmd+ (msg? "|"+msg :"");
client.send(mess,0,mess.length,65535,net_addr,result);
}
// **
// process error result
// **
function result(err,bytes) {
if (err) throw err;
};
function getFilesInDirectory (path,ext) {
let dir = fs.readdirSync( path );
return dir.filter( elm => elm.match(new RegExp(`.*\.(${ext})$`, 'ig')));
}
function getFile(path){
return fs.readFileSync(path);
}
console.log("")
console.log("Программа обновления прошивки для устройств Tibbo");
console.log("")
// attach events
client.on('listening', () =>{ // udp net binding
client.setBroadcast(1);
startSearch();
});
var tm;
client.on('message',(msg,rinfo) => { // udp message recived
var m = msg.toString(); // message string from Buffer
var addr = msg.toString().substring(0,25); // device mac address
switch (mode){
case 1:{ // поиск устройств
var d = devices.find(obj => { return obj.mac === addr });
if (!d){
devices.push({mac: addr, ip: rinfo.address});
// get device information
client.send("_"+addr+ "X",65535,net_addr);
}else
{
if (m.indexOf('/') >0){
if (m.indexOf('/')==m.lastIndexOf('/')){
d.platform = m.substr(26,m.length-27);
d.app_version = ""
}else{
d.platform = m.substr(27,m.indexOf('/')-28);
d.app_version = m.substr(m.lastIndexOf('/')+1);
}
}else
if ( !d.platform || d.app_version){ // requery if information not full
client.send("_"+addr+ "X",65535,net_addr);
}
}
countdown.message ("Поиск устройств. Найдено [" + devices.length + "] Осталось "+number+" секунд... ");
break;
}
case 2:{ // Программирование
switch (step){
case 1: // switch device to update mode
if (m.indexOf(session)<=0)
return;
if (addr == mac){
step=2; // send first data blocks
var cmdd="Q";
if (firmware==1)
cmdd="QF";
send(cmdd,mac,session);
tm = setInterval(function(){
var cmdd="Q";
if (firmware==1)
cmdd="QF";
send(cmdd,mac,session);
},500); // resend every 0.5 sec
}else{
console.log("ERROR - wrong reply",)
}
break;
case 2: // sending first block
if (pageNumber == 0 && addr == mac && m.indexOf(session>0))
if (tm){
clearInterval(tm);
}
progressbar = new Progress(20);
process.stdout.write("\u001b[1000D"+progressbar.update(pageNumber,fdata.length/128))
stopTimer = setTimeout(()=>{
// clearInterval(pr);
console.log();
console.log("ОШИБКА: Ответ от устройства не получен!");
console.log("Возможно включена защита от Spoofing'а");
console.log("Для отключения выполните комманду : ");
console.log("\tsudo sysctl net.ipv4.conf.all.rp_filter=0 && sudo sysctl net.ipv4.conf." +getInterface()+ ".rp_filter=0");
console.log();
process.exit();
},15000)
sendFilePage();
break;
case 3: // wait confirm block recieved switch to this mode in sendFilePage()
var b = toBytesInt16(pageNumber);
if (pageNumber >= 0 && addr == mac)
{
if (stopTimer) { // stop wrong net config check timer
clearTimeout(stopTimer);
stopTimer=null;
}
var b11 = Buffer.from([0x41,b[0],b[1]]) // number of confirmed block
if (msg.compare(b11,0,3,25)==0){
pageNumber++; // change current block number
process.stdout.write("\u001b[1000D"+progressbar.update(pageNumber,fdata.length/128))
if (((pageNumber)*128) >= fdata.length-1){ // if last block confirmed
step = 4; // switch to final step
if (firmware==1)
send("N",mac,session); // switch device to normal mode
else
send("T",mac,session); // switch device to normal mode
}
else
sendFilePage();
}
}
break;
case 4: // get last success message and switch to search mode
if (addr == mac && msg[25]==0x41){
clearTimeout(stopTimer);
send("E",mac,session); // send reboot command to device (if it id not auto reboot)
console.log("");
console.log("Загрузка завершена успешно");
console.log("");
mode = 1;
step = 1;
number = longSearch; // long time search for wait to device reboot and network ready
fdata = []; // clear file content buffer
startSearch();
}
}
}
}
});
client.on('close', function() {
console.log('Соединение неожиданно было закрыто. Проверьте сетевое подключение.');
console.log();
process.exit();
});
client.bind(44515); // bind socket
//**
// Get file names and show in menu
//**
function select_file(ext){
console.log("");
var filelist = getFilesInDirectory(files_path,ext);
filelist.push(new inquirer.Separator());
filelist.push("Отмена");
showMenu(filelist,'Выберите файл для загрузки в '+mac,item =>{
if (item.result == "Отмена"){
process.exit();
}
console.log("OK");
upload_application(mac,item.result);
});
}
function upload_application(mac, filename){
console.log("upload_application("+mac+","+filename+")" );
var d = devices.find(obj => { return obj.mac === mac });
var platform = d.platform.substring(1,d.platform.lastIndexOf('.'));
console.log("Начинается загрузка");
fdata = getFile(files_path+filename);
var fst = fdata.indexOf(String.fromCharCode(0)+"<FD>")+5;
var fen = fdata.indexOf(String.fromCharCode(0)+String.fromCharCode(0)+String.fromCharCode(0),fst);
var line = fdata.toString('ascii',fst+1,fen-1);
var finfo = line.split(String.fromCharCode(0));
if (firmware==0 && fdata.indexOf(platform)<0){
console.log("Выбрана неподходящая платформа :");
console.log("Устройство: "+platform+", файл:" +finfo[2]);
select_file(firmware==0?"tpc":"bin");
return;
}
mode = 2; //switch to uploading mode
step=1; // init device step
pageNumber = 0;
// init sending
send("X",mac,session); // switch device to program mode
}
function sendFilePage(){
step = 3;
clearTimeout(stopTimer); // clear resend timer
var bmac = Buffer.from("_"+mac);
var n = toBytesInt16(pageNumber);
var cmd = Buffer.from([0x44,n[0],n[1]]);
var f = Buffer.from(fdata.slice(128*pageNumber,128*(pageNumber+1)));
var mess = Buffer.concat([bmac,cmd,f]);
client.send(mess,0,mess.length,65535,net_addr,result);
if (fdata.length/128 >pageNumber)
stopTimer = setTimeout(sendFilePage,500); // resend every 0.5 sec
}
function startSearch(){
if (number<=0)
number = shortSearch;
devices = [];
session = (Math.random() * 0xFFFFFFFF).toString(16); // change session
countdown.start();
// get device list;
pr = setInterval(()=>{
send("","?","");
countdown.message( "Поиск устройств. Найдено [" + devices.length + "] Осталось "+number+" секунд... ");
if (number == 0){
clearInterval(pr);
countdown.stop();
proc();
}
number--;
},1000);
send("","?","");
}
function proc(){
if (devices.length == 0){
console.log("ОШИБКА: Устройства не найдены!");
console.log("Возможно включена защита от Spoofing'а");
console.log("Для отключения выполните комманды : ");
console.log("\tsudo sysctl net.ipv4.conf.all.rp_filter=0 && sudo sysctl net.ipv4.conf." +getInterface()+ ".rp_filter=0"); console.log();
process.exit();
}
var m = [];
var mlip = 0;
var mlpl = 0;
for (var i = 0; i < devices.length ; i++){
if (mlip < devices[i].ip.length)
mlip = devices[i].ip.length;
if (mlpl < devices[i].platform.length)
mlpl = devices[i].platform.length;
}
for (var i = 0; i < devices.length ; i++) {
m.push({ value: devices[i].mac,
name: devices[i].mac.replace("[",'').replace("]",'') + " | "
+ devices[i].ip.padStart(mlip," ") + " | "
+ devices[i].platform.padEnd(mlpl," ") + " | "
+ devices[i].app_version });
}
m.push(new inquirer.Separator());
m.push({ key:'0', value: "Выход" });
showMenu(m,"Выберите устройство",function(item) {
if (item){
if (item.result == "Выход")
process.exit();
else
mac = item.result;
console.log("");
showMenu(m2,"Выберите действие",(select)=>{
//console.log(select);
if (select.result=="Выход"){
process.exit();
}
if (select.result == 'Загрузка прошивки'){
//console.log("select file");
firmware=0;
select_file("tpc");
}
if (select.result == 'Загрузка firmware'){
firmware=1;
select_file("bin");
}
if (select.result == "Перезагрузка"){
send("E",mac,session);
number = longSearch;
startSearch();
}
})
}else
{
console.log("cancel")
}
//process.exit();
});
};
function showMenu(m,title,select){
var q = {
type: 'list',
name: 'result',
message: title,
choices: m
};
inquirer.prompt(q)
.then(select);
}