//build:20240724 // 负责固件的升级 import log from './dxLogger.js' import com from './dxCommon.js' import http from './dxHttp.js' import * as os from 'os'; const ota = {} //获取当前磁盘剩余大小(k)可能不同的操作系统指令不一样 ota.DF_CMD = `df -k / | awk 'NR==2 {print $4}'` ota.OTA_ROOT = '/ota' ota.OTA_RUN = ota.OTA_ROOT + '/run.sh' /** * HTTP升级:网络下载升级包升级 * @param {string} url 必填,下载升级包的http url地址 * @param {string} md5 必填,升级包的md5标识,下载完通过md5来判断是否完整。32长度的全小写16进制字符串 * @param {number} size 非必填,升级包的大概大小,单位是k,如果文件太大而剩余磁盘不够,会提前报错误,不会开始启动下载 * @param {number} timeout 非必填,尝试链接下载地址的超时时间(不是下载完成的时间),缺省是3秒 * @param {string} password 非必填,下载安装包的密码,可选,如果设置,下载时需要输入密码 */ ota.updateHttp = function (url, md5, timeout = 3, password, size) { if (!url || !md5) { throw new Error("The 'url' and 'md5' param should not be null") } if (size && (typeof size != 'number')) { throw new Error("The 'size' param should be a number") } //1. 查看磁盘还剩余的大小 let df = parseInt(com.systemWithRes(ota.DF_CMD, 1000)) if (size) { if (df < (3 * size)) {//大概本地必须有安装包3倍大小的空间 throw new Error('The upgrade package is too large, and not be enough space on the disk to download it') } } //2. 下载文件到临时目录 const firmware = '/upgrades.zip' const temp = '/upgrades.temp' com.systemBrief(`rm -rf ${firmware} && rm -rf ${temp} `) //先删除ota根目录 let downloadRet = http.download(url + (password ? "&password=" + password : '') , temp, null, timeout*1000) let fileExist = (os.stat(temp)[1] === 0) if (!fileExist) { log.error("download result" + downloadRet) com.systemBrief(`rm -rf ${firmware} && rm -rf ${temp} `) throw new Error('Download failed, please check the url:' + url) } //3. 计算并比较md5是否一样 let md5Hash = com.md5HashFile(temp) md5Hash = md5Hash.map(v => v.toString(16).padStart(2, 0)).join('') if (md5Hash != md5) { com.systemBrief(`rm -rf ${firmware} && rm -rf ${temp} `) throw new Error('Download failed with wrong md5 value') } //4. md5校验通过,将升级包放到升级目录下,等待重启升级 com.systemBrief(`mv ${temp} ${firmware} `) } /** * 文件升级:通过其他方式将升级包放到用户目录下,调用此方法升级 * @param {string} path 必填,下载升级包的http url地址 * @param {string} md5 必填,升级包的md5标识,下载完通过md5来判断是否完整。32长度的全小写16进制字符串 * @param {number} size 非必填,升级包的大概大小,单位是k,如果文件太大而剩余磁盘不够,会提前报错误,不会开始启动下载 */ ota.updateFile = function (path, md5, size) { if (!path || !md5) { throw new Error("The 'path' and 'md5' param should not be null") } if (size && (typeof size != 'number')) { throw new Error("The 'size' param should be a number") } //1. 查看磁盘还剩余的大小 let df = parseInt(com.systemWithRes(ota.DF_CMD, 1000)) if (size) { if (df < (3 * size)) {//大概本地必须有安装包3倍大小的空间 throw new Error('The upgrade package is too large, and not be enough space on the disk to download it') } } //2. 计算并比较md5是否一样 let md5Hash = com.md5HashFile(path) md5Hash = md5Hash.map(v => v.toString(16).padStart(2, 0)).join('') if (md5Hash != md5) { throw new Error('With wrong md5 value') } //3. md5校验通过,将升级包放到升级目录下,等待重启升级 const firmware = '/upgrades.zip' com.systemBrief(`mv ${path} ${firmware} `) } /** * 注意:此方法即将过期,用于兼容旧版本,新版本不推荐使用 * 升级分二大步骤,第一步是在应用端下载升级包(zip),解压升级包 * 第二步包括重启设备,利用脚本复制目录和文件或额外一些操作 * 如果在升级包根目录下放一个custom_update.sh,就会先执行这个shell文件,我们可以在这个文件里放一些自定义的升级动作 * @param {string} url 必填,下载升级包的http url地址 * @param {string} md5 必填,升级包的md5标识,下载完通过md5来判断是否完整。32长度的全小写16进制字符串 * @param {number} size 非必填,升级包的大概大小,单位是k,如果文件太大而剩余磁盘不够,会提前报错误,不会开始启动下载 * @param {string} shell 非必填,重启设备后的升级脚本内容,解压后的文件夹缺省是 /ota/temp,升级会缺省把/ota/temp下所有文件拷贝复制到/app/code/下 * @param {number} timeout 非必填,尝试链接下载地址的超时时间(不是下载完成的时间),缺省是3秒 */ ota.update = function (url, md5, size, shell, timeout = 3) { if (!url || !md5) { throw new Error("The 'url' and 'md5' param should not be null") } if (size && (typeof size != 'number')) { throw new Error("The 'size' param should be a number") } //1. 查看磁盘还剩余的大小 let df = parseInt(com.systemWithRes(ota.DF_CMD, 1000)) if (size) { if (df < (3 * size)) {//大概本地必须有安装包3倍大小的空间 throw new Error('The upgrade package is too large, and not be enough space on the disk to download it') } } //2. 下载文件到特定目录 const firmware = ota.OTA_ROOT + '/download.zip' const temp = ota.OTA_ROOT + '/temp' com.systemBrief(`rm -rf ${ota.OTA_ROOT} && mkdir ${ota.OTA_ROOT} `) //先删除ota根目录 let download = `wget --no-check-certificate --timeout=${timeout} -c "${url}" -O ${firmware} 2>&1` com.systemBrief(download, 1000) let fileExist = (os.stat(firmware)[1] === 0) let downloadRet if (!fileExist) { downloadRet = http.download(url, firmware, null, timeout*1000) } fileExist = (os.stat(firmware)[1] === 0) if (!fileExist) { log.error("download result" + downloadRet) throw new Error('Download failed, please check the url:' + url) } //3. 计算并比较md5是否一样 let md5Hash = com.md5HashFile(firmware) md5Hash = md5Hash.map(v => v.toString(16).padStart(2, 0)).join('') if (md5Hash != md5) { log.error("download result" + downloadRet) throw new Error('Download failed with wrong md5 value') } //4. 解压 com.systemBrief(`mkdir ${temp} && unzip -o ${firmware} -d ${temp}`) //5. 执行自定义的升级脚本 const custom_update = temp+'/custom_update.sh' if(os.stat(custom_update)[1] === 0){ com.systemBrief(`chmod +x ${custom_update}`) com.systemWithRes(`${custom_update}`) } //6. 构建脚本文件 if (!shell) { //缺省只是拷贝目录并删除ota根目录 shell = `cp -r ${temp}/* /app/code \n rm -rf ${ota.OTA_ROOT}` } com.systemBrief(`echo "${shell}" > ${ota.OTA_RUN} && chmod +x ${ota.OTA_RUN}`) fileExist = (os.stat(ota.OTA_RUN)[1] === 0) if (!fileExist) { throw new Error('Build shell file failed') } com.systemWithRes(`${ota.OTA_RUN}`) } /** * 注意:此方法即将过期,用于兼容旧版本,新版本不推荐使用 * 特殊:兼容旧的升级格式,必须是tar.xz格式,且只用来升级资源文件 * 升级分二大步骤,第一步是在应用端下载升级包(zip),解压升级包 * 第二步包括重启设备,利用脚本复制目录和文件或额外一些操作 * 如果在升级包根目录下放一个custom_update.sh,就会先执行这个shell文件,我们可以在这个文件里放一些自定义的升级动作 * @param {string} url 必填,下载升级包的http url地址 * @param {string} md5 必填,升级包的md5标识,下载完通过md5来判断是否完整。32长度的全小写16进制字符串 * @param {number} size 非必填,升级包的大概大小,单位是k,如果文件太大而剩余磁盘不够,会提前报错误,不会开始启动下载 * @param {string} shell 非必填,重启设备后的升级脚本内容,解压后的文件夹缺省是 /ota/temp,升级会缺省把/ota/temp下所有文件拷贝复制到/app/code/下 * @param {number} timeout 非必填,尝试链接下载地址的超时时间(不是下载完成的时间),缺省是3秒 */ ota.updateResource = function (url, md5, size, shell, timeout = 3) { if (!url || !md5) { throw new Error("The 'url' and 'md5' param should not be null") } if (size && (typeof size != 'number')) { throw new Error("The 'size' param should be a number") } //1. 查看磁盘还剩余的大小 let df = parseInt(com.systemWithRes(ota.DF_CMD, 1000)) if (size) { if (df < (3 * size)) {//大概本地必须有安装包3倍大小的空间 throw new Error('The upgrade package is too large, and not be enough space on the disk to download it') } } //2. 下载文件到特定目录 const firmware = ota.OTA_ROOT + '/download.tar.xz' const temp = ota.OTA_ROOT + '/temp' com.systemBrief(`rm -rf ${ota.OTA_ROOT} && mkdir ${ota.OTA_ROOT} `) //先删除ota根目录 let download = `wget --no-check-certificate --timeout=${timeout} -c "${url}" -O ${firmware} 2>&1` com.systemBrief(download, 1000) let fileExist = (os.stat(firmware)[1] === 0) if (!fileExist) { http.download(url, firmware, null, timeout*1000) } fileExist = (os.stat(firmware)[1] === 0) if (!fileExist) { throw new Error('Download failed, please check the url:' + url) } //3. 计算并比较md5是否一样 let md5Hash = com.md5HashFile(firmware) md5Hash = md5Hash.map(v => v.toString(16).padStart(2, 0)).join('') if (md5Hash != md5) { throw new Error('Download failed with wrong md5 value') } //4. 解压 //tar -xJvf test.tar.xz -C /path/ com.systemBrief(`mkdir ${temp} && tar -xJvf ${firmware} -C ${temp}`) //5. 构建脚本文件 if (!shell) { shell = ` source=${temp}/vgapp/res/image/bk.png target=/app/code/resource/image/bg.png if test -e "\\$source"; then cp "\\$source" "\\$target" fi source=${temp}/vgapp/res/image/bk_90.png target=/app/code/resource/image/bg_90.png if test -e "\\$source"; then cp "\\$source" "\\$target" fi source=${temp}/vgapp/res/font/AlibabaPuHuiTi-2-65-Medium.ttf target=/app/code/resource/font.ttf if test -e "\\$source"; then cp "\\$source" "\\$target" fi source=${temp}/vgapp/wav/*.wav target=/app/code/resource/wav/ cp "\\$source" "\\$target" rm -rf ${ota.OTA_ROOT} ` } com.systemBrief(`echo "${shell}" > ${ota.OTA_RUN} && chmod +x ${ota.OTA_RUN}`) fileExist = (os.stat(ota.OTA_RUN)[1] === 0) if (!fileExist) { throw new Error('Build shell file failed') } com.systemWithRes(`${ota.OTA_RUN}`) } /** * 由调用者来启动重启,一般是update函数没有错误,运行完成并向北向汇报结果后再调用重启 */ ota.reboot = function () { com.asyncReboot(2) } //-------------------------private------------------- export default ota