update: OTA

This commit is contained in:
JackSun-qc 2022-04-05 14:56:50 +08:00
parent 08dd3afb32
commit 9683b0a014
4 changed files with 244 additions and 150 deletions

View File

@ -12,8 +12,198 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import uos
import fota
import app_fota
import uzlib
import ql_fs
import uhashlib
import ubinascii
import app_fota_download
from queue import Queue
from usr.logging import getLogger
from usr.settings import SYSNAME
from usr.settings import PROJECT_NAME
log = getLogger(__name__)
def OTA(object): def OTA(object):
def __init__(self) def __init__(self, module, file_info):
pass self.module = module
self.file_info = file_info
self.fota_queue = Queue(maxsize=4)
def start(self):
if self.module == SYSNAME:
return self.start_fota()
elif self.module == PROJECT_NAME:
return self.start_sota()
else:
log.error('OTA Module %s Is Not Error!' % self.module)
return False
def start_fota(self):
fota_obj = fota()
url1 = self.file_info[0]['url']
url2 = self.file_info[1]['url'] if len(self.file_info) > 1 else ''
res = fota_obj.httpDownload(url1=url1, url2=url2, callback=self.fota_cb)
if res == 0:
fota_res = self.fota_queue.get()
return fota_res
else:
return False
def fota_cb(self, args):
down_status = args[0]
down_process = args[1]
if down_status != -1:
log.debug('DownStatus: %s [%s][%s%%]' % (down_status, '=' * down_process, down_process))
if down_status == 0 and down_process == 100:
self.fota_queue.put(True)
# TODO: Report To Cloud Upgrade Process.
else:
log.error('Down Failed. Error Code: %s' % down_process)
self.fota_queue.put(False)
def start_sota(self):
ota_module_obj = SotaDownloadUpgrade()
for file in self.file_info:
if ota_module_obj.app_fota_down(file['url']):
if ota_module_obj.file_update(file['md5']):
continue
else:
return False
else:
return False
ota_module_obj.sota_set_flag()
return True
class SotaDownloadUpgrade(object):
def __init__(self, parent_dir="/usr/.updater/usr/"):
self.fp_file = "/usr/sotaFile.tar.gz"
self.file_list = []
self.parent_dir = parent_dir
self.unzipFp = 0
self.hash_obj = uhashlib.md5()
def write_update_data(self, data):
with open(self.fp_file, "wb+") as fp:
fp.write(data)
self.hash_obj.update(data)
def app_fota_down(self, url):
app_fota_obj = app_fota.new()
fp = open(self.fp_file, "wb+")
fp.close()
res = app_fota_obj.download(url, self.fp_file)
if res == 0:
self.hash_obj = uhashlib.md5()
with open(self.fp_file, "rb+") as fp:
for fpi in fp.readlines():
self.hash_obj.update(fpi)
return True
else:
return False
def __get_file_size(self, data):
size = data.decode('ascii')
size = size.rstrip('\0')
if (len(size) == 0):
return 0
size = int(size, 8)
return size
def __get_file_name(self, name):
fileName = name.decode('ascii')
fileName = fileName.rstrip('\0')
return fileName
def file_update(self, md5_value):
md5Data = ubinascii.hexlify(self.hash_obj.digest())
md5Data = md5Data.decode('ascii')
md5Value = eval(md5_value)
log.debug("DMP Calc MD5 Value: %s, Device Calc MD5 Value: %s" % (md5Value, md5Data))
if (md5Value != md5Data):
log.error("MD5 Verification Failed")
return
log.debug("MD5 Verification Success.")
ota_file = open(self.fp_file, "wb+")
ota_file.seek(10)
self.unzipFp = uzlib.DecompIO(ota_file, -15)
log.debug('Unzip File Success.')
ql_fs.mkdirs(self.parent_dir)
try:
while True:
data = self.unzipFp.read(0x200)
if not data:
log.debug("Read File Size Zore.")
break
size = self.__get_file_size(data[124:135])
fileName = self.__get_file_name(data[:100])
log.debug("File Name: %s, File Size: %s" % (fileName, size))
if not size:
if len(fileName):
log.debug("Create File Dir: %s" % self.parent_dir + fileName)
ql_fs.mkdirs(self.parent_dir + fileName)
else:
log.debug("Have No File Unzip.")
break
else:
log.debug("File %s Write Size %s" % (self.parent_dir + fileName, size))
fp = open(self.parent_dir + fileName, "wb+")
fileSize = size
while fileSize:
data = self.unzipFp.read(0x200)
if (fileSize < 0x200):
fp.write(data[:fileSize])
fileSize = 0
fp.close()
self.file_list.append({"fileName": "/usr/" + fileName, "size": size})
break
else:
fileSize -= 0x200
fp.write(data)
for fileName in self.file_list:
app_fota_download.update_download_stat("/usr/.updater" + fileName["fileName"], fileName["fileName"], fileName["size"])
ota_file.close()
log.debug("Remove /usr/sotaFile.tar.gz")
uos.remove("/usr/sotaFile.tar.gz")
except Exception as e:
log.error("Unpack Error: %s" % e)
return False
return True
def sota_set_flag(self):
app_fota_download.set_update_flag()
class OTAFileClear(object):
def __init__(self):
self.usrList = uos.ilistdir("/usr/")
def __remove_updater_dir(self, path):
dirList = uos.ilistdir(path)
for fileInfo in dirList:
if fileInfo[1] == 0x4000:
self.__remove_updater_dir("%s/%s" % (path, fileInfo[0]))
else:
log.debug("remove file name: %s/%s" % (path, fileInfo[0]))
uos.remove("%s/%s" % (path, fileInfo[0]))
log.debug("remove dir name: %s" % path)
uos.remove(path)
def file_clear(self):
for fileInfo in self.usrList:
if fileInfo[0] == ".updater":
self.__remove_updater_dir("/usr/.updater")
elif fileInfo[0] == "sotaFile.tar.gz":
log.debug("remove update file sotaFile.tar.gz")
uos.remove("/usr/sotaFile.tar.gz")

View File

@ -12,15 +12,10 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import uos
import utime import utime
import osTimer import osTimer
import quecIot import quecIot
import uhashlib
import ubinascii
import uzlib
import ql_fs
import app_fota_download
from queue import Queue from queue import Queue
@ -28,6 +23,8 @@ from usr.logging import getLogger
from usr.settings import settings from usr.settings import settings
from usr.settings import PROJECT_NAME from usr.settings import PROJECT_NAME
from usr.settings import PROJECT_VERSION from usr.settings import PROJECT_VERSION
from usr.ota import SotaDownloadUpgrade
from usr.ota import OTAFileClear
log = getLogger(__name__) log = getLogger(__name__)
@ -309,8 +306,8 @@ class QuecThing(object):
"OTA File Info: componentNo: %s, sourceVersion: %s, targetVersion: %s, " "OTA File Info: componentNo: %s, sourceVersion: %s, targetVersion: %s, "
"batteryLimit: %s, minSignalIntensity: %s, useSpace: %s" % tuple(file_info) "batteryLimit: %s, minSignalIntensity: %s, useSpace: %s" % tuple(file_info)
) )
self.downlink_queue.put(('ota_plain', data))
self.downlink_queue.put(('object_model', [('ota_status', (data[0], 1, data[2]))])) self.downlink_queue.put(('object_model', [('ota_status', (data[0], 1, data[2]))]))
self.downlink_queue.put(('ota_plain', data))
elif errcode == 10701: elif errcode == 10701:
log.info('The module starts to download.') log.info('The module starts to download.')
if data != (7, 10701): if data != (7, 10701):
@ -351,7 +348,7 @@ class QuecThing(object):
self.file_size = size self.file_size = size
self.crc_value = crc self.crc_value = crc
self.download_size = 0 self.download_size = 0
self.update_mode = UpdateCtx() self.update_mode = SotaDownloadUpgrade()
self.md5_value = md5_value self.md5_value = md5_value
def sota_download_success(self, start, down_loaded_size): def sota_download_success(self, start, down_loaded_size):
@ -375,117 +372,10 @@ class QuecThing(object):
quecIot.otaAction(3) quecIot.otaAction(3)
file_update_res = self.update_mode.file_update(self.md5_value) file_update_res = self.update_mode.file_update(self.md5_value)
if file_update_res: if file_update_res:
self.update_mode.sota_set_flag()
log.debug("File Update Success, Power Restart.") log.debug("File Update Success, Power Restart.")
else: else:
log.debug("File Update Failed.") log.debug("File Update Failed.")
self.downlink_queue.put(('object_model', [('power_restart', 1)])) self.downlink_queue.put(('object_model', [('power_restart', 1)]))
else: else:
quecIot.otaAction(2) quecIot.otaAction(2)
class UpdateCtx(object):
def __init__(self, parent_dir="/usr/.updater/usr/"):
self.fp = open("/usr/sotaFile.tar.gz", "wb+")
self.file_list = []
self.parent_dir = parent_dir
self.unzipFp = 0
self.hash_obj = uhashlib.md5()
def write_update_data(self, data):
self.fp.write(data)
self.hash_obj.update(data)
def __get_file_size(self, data):
size = data.decode('ascii')
size = size.rstrip('\0')
if (len(size) == 0):
return 0
size = int(size, 8)
return size
def __get_file_name(self, name):
fileName = name.decode('ascii')
fileName = fileName.rstrip('\0')
return fileName
def file_update(self, md5_value):
md5Data = ubinascii.hexlify(self.hash_obj.digest())
md5Data = md5Data.decode('ascii')
md5Value = eval(md5_value)
log.debug("DMP Calc MD5 Value: %s, Device Calc MD5 Value: %s" % (md5Value, md5Data))
if (md5Value != md5Data):
log.error("MD5 Verification Failed")
return
log.debug("MD5 Verification Success.")
self.fp.seek(10)
self.unzipFp = uzlib.DecompIO(self.fp, -15)
log.debug('Unzip File Success.')
ql_fs.mkdirs(self.parent_dir)
try:
while True:
data = self.unzipFp.read(0x200)
if not data:
log.debug("Read File Size Zore.")
break
size = self.__get_file_size(data[124:135])
fileName = self.__get_file_name(data[:100])
log.debug("File Name: %s, File Size: %s" % (fileName, size))
if not size:
if len(fileName):
log.debug("Create File Dir: %s" % self.parent_dir + fileName)
ql_fs.mkdirs(self.parent_dir + fileName)
else:
log.debug("Have No File Unzip.")
break
else:
log.debug("File %s Write Size %s" % (self.parent_dir + fileName, size))
fp = open(self.parent_dir + fileName, "wb+")
fileSize = size
while fileSize:
data = self.unzipFp.read(0x200)
if (fileSize < 0x200):
fp.write(data[:fileSize])
fileSize = 0
fp.close()
self.file_list.append({"fileName": "/usr/" + fileName, "size": size})
break
else:
fileSize -= 0x200
fp.write(data)
for fileName in self.file_list:
app_fota_download.update_download_stat("/usr/.updater" + fileName["fileName"], fileName["fileName"], fileName["size"])
app_fota_download.set_update_flag()
self.fp.close()
log.debug("Remove /usr/sotaFile.tar.gz")
uos.remove("/usr/sotaFile.tar.gz")
except Exception as e:
log.error("Unpack Error: %s" % e)
return False
return True
class OTAFileClear(object):
def __init__(self):
self.usrList = uos.ilistdir("/usr/")
def __remove_updater_dir(self, path):
dirList = uos.ilistdir(path)
for fileInfo in dirList:
if fileInfo[1] == 0x4000:
self.__remove_updater_dir("%s/%s" % (path, fileInfo[0]))
else:
log.debug("remove file name: %s/%s" % (path, fileInfo[0]))
uos.remove("%s/%s" % (path, fileInfo[0]))
log.debug("remove dir name: %s" % path)
uos.remove(path)
def file_clear(self):
for fileInfo in self.usrList:
if fileInfo[0] == ".updater":
self.__remove_updater_dir("/usr/.updater")
elif fileInfo[0] == "sotaFile.tar.gz":
log.debug("remove update file sotaFile.tar.gz")
uos.remove("/usr/sotaFile.tar.gz")

View File

@ -22,11 +22,14 @@ from queue import Queue
import usr.settings as settings import usr.settings as settings
from usr.ota import OTA
from usr.common import Singleton from usr.common import Singleton
from usr.logging import getLogger from usr.logging import getLogger
from usr.settings import DATA_NON_LOCA from usr.settings import DATA_NON_LOCA
from usr.settings import DATA_LOCA_NON_GPS
from usr.settings import DATA_LOCA_GPS from usr.settings import DATA_LOCA_GPS
from usr.settings import DATA_LOCA_NON_GPS
from usr.settings import SYSNAME
from usr.settings import PROJECT_NAME
if settings.settings.get()['sys']['cloud'] == settings.default_values_sys._cloud.quecIot: if settings.settings.get()['sys']['cloud'] == settings.default_values_sys._cloud.quecIot:
from usr.quecthing import QuecThing from usr.quecthing import QuecThing
@ -87,17 +90,18 @@ class Controller(Singleton):
if upgrade_info: if upgrade_info:
current_settings = settings.settings.get() current_settings = settings.settings.get()
ota_status_info = current_settings['sys']['ota_status'] ota_status_info = current_settings['sys']['ota_status']
ota_info = {} if ota_status_info['sys_target_version'] == '--' and ota_status_info['app_target_version'] == '--':
if upgrade_info[0] == settings.SYSNAME: ota_info = {}
ota_info['upgrade_module'] = 1 if upgrade_info[0] == settings.SYSNAME:
ota_info['sys_target_version'] = upgrade_info[2] ota_info['upgrade_module'] = 1
elif upgrade_info[0] == settings.PROJECT_NAME: ota_info['sys_target_version'] = upgrade_info[2]
ota_info['upgrade_module'] = 2 elif upgrade_info[0] == settings.PROJECT_NAME:
ota_info['app_target_version'] = upgrade_info[2] ota_info['upgrade_module'] = 2
ota_info['upgrade_status'] = upgrade_info[1] ota_info['app_target_version'] = upgrade_info[2]
ota_status_info.update(ota_info) ota_info['upgrade_status'] = upgrade_info[1]
settings.settings.set('ota_status', ota_status_info) ota_status_info.update(ota_info)
settings.settings.save() settings.settings.set('ota_status', ota_status_info)
settings.settings.save()
def power_restart(self, perm, flag): def power_restart(self, perm, flag):
if perm == 'w': if perm == 'w':
@ -155,10 +159,11 @@ class DownLinkOption(object):
current_settings = settings.settings.get() current_settings = settings.settings.get()
if current_settings['app']['sw_ota'] and current_settings['app']['sw_ota_auto_upgrade']: if current_settings['app']['sw_ota'] and current_settings['app']['sw_ota_auto_upgrade']:
if current_settings['sys']['cloud'] == settings.default_values_sys._cloud.quecIot: if current_settings['sys']['cloud'] == settings.default_values_sys._cloud.quecIot:
self.tracker.remote.cloud_ota_action() self.tracker.remote.cloud_ota_action(val=1)
elif current_settings['sys']['cloud'] == settings.default_values_sys._cloud.AliYun: elif current_settings['sys']['cloud'] == settings.default_values_sys._cloud.AliYun:
log.debug('ota_plain args: %s' % str(args)) log.debug('ota_plain args: %s' % str(args))
log.debug('ota_plain kwargs: %s' % str(kwargs)) log.debug('ota_plain kwargs: %s' % str(kwargs))
self.tracker.remote.cloud_ota_action(val=1, kwargs=kwargs)
def ota_file_download(self, *args, **kwargs): def ota_file_download(self, *args, **kwargs):
log.debug('ota_file_download: %s' % str(args)) log.debug('ota_file_download: %s' % str(args))
@ -387,20 +392,24 @@ class Remote(Singleton):
else: else:
log.error('Current Cloud (0x%X) Not Supported!' % current_settings['sys']['cloud']) log.error('Current Cloud (0x%X) Not Supported!' % current_settings['sys']['cloud'])
def cloud_ota_action(self, val=1): def cloud_ota_action(self, val=1, kwargs=None):
current_settings = settings.settings.get() current_settings = settings.settings.get()
if current_settings['sys']['cloud'] == settings.default_values_sys._cloud.quecIot: if current_settings['sys']['cloud'] == settings.default_values_sys._cloud.quecIot:
self.cloud.ota_action(val) self.cloud.ota_action(val)
if val == 0:
self.tracker.ota_params_reset()
else:
if val == 0: if val == 0:
current_settings = settings.settings.get() current_settings = settings.settings.get()
ota_status_info = current_settings['sys']['ota_status'] ota_status_info = current_settings['sys']['ota_status']
ota_info = {} upgrade_module = SYSNAME if ota_status_info['upgrade_module'] == 1 else PROJECT_NAME
ota_info['sys_target_version'] = '--' self.cloud.ota_device_progress(step=-1, desc='User cancels upgrade.', module=upgrade_module)
ota_info['app_target_version'] = '--' self.tracker.ota_params_reset()
ota_info['upgrade_module'] = 0 else:
ota_info['upgrade_status'] = 0 upgrade_module = kwargs.get('module', '')
ota_status_info.update(ota_info) file_info = [{'size': i['fileSize'], 'url': i['fileUrl'], 'md5': i['fileMd5']} for i in kwargs.get('files', [])]
settings.settings.set('ota_status', ota_status_info) if not file_info:
settings.settings.save() file_info = [{'size': kwargs['size'], 'url': kwargs['url'], 'md5': kwargs['md5']}]
else: ota_obj = OTA(upgrade_module, file_info)
log.warn('Current Cloud (0x%X) Not Supported!' % current_settings['sys']['cloud']) ota_obj.start()
self.downlink_queue.put(('object_model', [('power_restart', 1)]))

View File

@ -222,14 +222,7 @@ class Tracker(Singleton):
current_settings = settings.settings.get() current_settings = settings.settings.get()
ota_status_info = current_settings['sys']['ota_status'] ota_status_info = current_settings['sys']['ota_status']
if ota_status_info['upgrade_status'] in (3, 4): if ota_status_info['upgrade_status'] in (3, 4):
ota_info = {} self.ota_params_reset()
ota_info['sys_target_version'] = '--'
ota_info['app_target_version'] = '--'
ota_info['upgrade_module'] = 0
ota_info['upgrade_status'] = 0
ota_status_info.update(ota_info)
settings.settings.set('ota_status', ota_status_info)
settings.settings.save()
def device_check(self): def device_check(self):
device_check_res = self.get_device_check() device_check_res = self.get_device_check()
@ -278,6 +271,18 @@ class Tracker(Singleton):
if net_check_res == (3, 1): if net_check_res == (3, 1):
pass pass
def ota_params_reset(self):
current_settings = settings.settings.get()
ota_status_info = current_settings['sys']['ota_status']
ota_info = {}
ota_info['sys_target_version'] = '--'
ota_info['app_target_version'] = '--'
ota_info['upgrade_module'] = 0
ota_info['upgrade_status'] = 0
ota_status_info.update(ota_info)
settings.settings.set('ota_status', ota_status_info)
settings.settings.save()
class SelfCheck(object): class SelfCheck(object):