mirror of
https://gitee.com/qpy-solutions/tracker-v2.git
synced 2025-05-18 18:48:25 +08:00
update: Quecthing OTA
This commit is contained in:
parent
d54819d37d
commit
fbd42c42f9
@ -108,7 +108,7 @@ class GPS(Singleton):
|
||||
global gps_data_retrieve_queue
|
||||
|
||||
while self.__first_break == 0:
|
||||
self.__gps_timer.start(50, 1, self.first_gps_timer_callback)
|
||||
self.__gps_timer.start(50, 0, self.first_gps_timer_callback)
|
||||
nread = gps_data_retrieve_queue.get()
|
||||
data = self.uart_obj.read(nread).decode()
|
||||
self.__gps_timer.stop()
|
||||
@ -119,7 +119,7 @@ class GPS(Singleton):
|
||||
gga_data = ''
|
||||
vtg_data = ''
|
||||
while self.__second_break == 0:
|
||||
self.__gps_timer.start(1500, 1, self.second_gps_timer_callback)
|
||||
self.__gps_timer.start(1500, 0, self.second_gps_timer_callback)
|
||||
nread = gps_data_retrieve_queue.get()
|
||||
if nread:
|
||||
data += self.uart_obj.read(nread).decode()
|
||||
@ -141,7 +141,7 @@ class GPS(Singleton):
|
||||
quecgnss.gnssEnable(1)
|
||||
|
||||
while self.__first_break == 0:
|
||||
self.__gps_timer.start(50, 1, self.first_gps_timer_callback)
|
||||
self.__gps_timer.start(50, 0, self.first_gps_timer_callback)
|
||||
data = quecgnss.read(1024)
|
||||
self.__gps_timer.stop()
|
||||
self.__first_break = 0
|
||||
@ -152,7 +152,7 @@ class GPS(Singleton):
|
||||
vtg_data = ''
|
||||
count = 0
|
||||
while self.__second_break == 0:
|
||||
self.__gps_timer.start(1500, 1, self.second_gps_timer_callback)
|
||||
self.__gps_timer.start(1500, 0, self.second_gps_timer_callback)
|
||||
gnss_data = quecgnss.read(1024)
|
||||
if gnss_data and gnss_data[1]:
|
||||
data += gnss_data[1].decode() if len(gnss_data) > 1 and gnss_data[1] else ''
|
||||
|
@ -15,6 +15,8 @@
|
||||
import utime
|
||||
from usr.settings import settings
|
||||
|
||||
current_settings = settings.get()
|
||||
|
||||
|
||||
def asyncLog(name, level, *message, timeout=None, await_connection=True):
|
||||
'''
|
||||
@ -27,7 +29,6 @@ def asyncLog(name, level, *message, timeout=None, await_connection=True):
|
||||
|
||||
|
||||
def log(name, level, *message, local_only=False, return_only=False, timeout=None):
|
||||
current_settings = settings.get()
|
||||
if not current_settings.get('sys', {}).get('sw_log', True):
|
||||
return
|
||||
|
||||
|
@ -15,14 +15,12 @@
|
||||
from usr.tracker import Tracker
|
||||
from usr.settings import settings
|
||||
from usr.settings import default_values_sys
|
||||
from usr.settings import PROJECT_NAME
|
||||
from usr.settings import PROJECT_VERSION
|
||||
from usr.logging import getLogger
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
PROJECT_NAME = 'QuecPython_Tracker'
|
||||
|
||||
PROJECT_VERSION = '2.0.1'
|
||||
|
||||
|
||||
def main():
|
||||
log.info('PROJECT_NAME: %s' % PROJECT_NAME)
|
||||
|
@ -12,11 +12,16 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import uos
|
||||
import utime
|
||||
import osTimer
|
||||
import quecIot
|
||||
import uhashlib
|
||||
import ubinascii
|
||||
import uzlib
|
||||
import ql_fs
|
||||
import app_fota_download
|
||||
|
||||
# from misc import Power
|
||||
from queue import Queue
|
||||
|
||||
from usr.logging import getLogger
|
||||
@ -85,19 +90,44 @@ object_model_code = {i[1][0]: i[0] for i in object_model}
|
||||
|
||||
class QuecThing(object):
|
||||
def __init__(self, pk, ps, dk, ds, downlink_queue):
|
||||
self.pk = pk
|
||||
self.ps = ps
|
||||
self.dk = dk
|
||||
self.ds = ds
|
||||
self.init_res = {}
|
||||
self.fileSize = 0
|
||||
self.needDownloadSize = 0
|
||||
self.crcValue = 0
|
||||
self.downloadSize = 0
|
||||
self.fileFp = 0
|
||||
self.startAddr = 0
|
||||
self.downlink_queue = downlink_queue
|
||||
self.post_result_wait_queue = Queue(maxsize=16)
|
||||
self.quec_timer = osTimer()
|
||||
self.queciot_init(pk, ps, dk, ds)
|
||||
self.queciot_init()
|
||||
|
||||
fileClear = OTAFileClear()
|
||||
fileClear.file_clear()
|
||||
|
||||
def queciot_init(self):
|
||||
if quecIot.getWorkState() == 8 and quecIot.getConnmode() == 1:
|
||||
return
|
||||
|
||||
def queciot_init(self, pk, ps, dk, ds):
|
||||
quecIot.init()
|
||||
quecIot.setEventCB(self.eventCB)
|
||||
quecIot.setProductinfo(pk, ps)
|
||||
quecIot.setDkDs(dk, ds)
|
||||
quecIot.setProductinfo(self.pk, self.ps)
|
||||
quecIot.setDkDs(self.dk, self.ds)
|
||||
quecIot.setServer(1, "iot-south.quectel.com:2883")
|
||||
quecIot.setConnmode(1)
|
||||
if not ds and dk:
|
||||
|
||||
count = 0
|
||||
while quecIot.getWorkState() != 8 and count < 10:
|
||||
if self.init_res.get('subscription') is not None:
|
||||
self.init_res.pop('subscription')
|
||||
break
|
||||
utime.sleep_ms(200)
|
||||
|
||||
if not self.ds and self.dk:
|
||||
count = 0
|
||||
while count < 3:
|
||||
ndk, nds = quecIot.getDkDs()
|
||||
@ -108,6 +138,8 @@ class QuecThing(object):
|
||||
current_settings = settings.get()
|
||||
cloud_init_params = current_settings['sys']['cloud_init_params']
|
||||
cloud_init_params['DS'] = nds
|
||||
self.dk = ndk
|
||||
self.ds = nds
|
||||
settings.set('cloud_init_params', cloud_init_params)
|
||||
settings.save()
|
||||
|
||||
@ -129,6 +161,7 @@ class QuecThing(object):
|
||||
del data[k]
|
||||
|
||||
def post_data(self, data):
|
||||
self.queciot_init()
|
||||
res = True
|
||||
# log.debug('post_data: %s' % str(data))
|
||||
for k, v in data.items():
|
||||
@ -192,11 +225,25 @@ class QuecThing(object):
|
||||
elif event == 2:
|
||||
if errcode == 10200:
|
||||
log.info('Access succeeded.')
|
||||
self.init_res['access'] = True
|
||||
if errcode == 10450:
|
||||
log.error('Device internal error (connect failed).')
|
||||
self.init_res['access'] = False
|
||||
elif event == 3:
|
||||
if errcode == 10200:
|
||||
log.info('Subscription succeeded.')
|
||||
self.init_res['subscription'] = True
|
||||
quecIot.otaRequest(0)
|
||||
if data != (3, 10200):
|
||||
ota_info = data.decode()
|
||||
file_info = ota_info.split(',')
|
||||
log.info(
|
||||
"OTA File Info: componentNo: %s, sourceVersion: %s, targetVersion: %s, "
|
||||
"batteryLimit: %s, minSignalIntensity: %s, minSignalIntensity: %s" % tuple(file_info)
|
||||
)
|
||||
if errcode == 10300:
|
||||
log.info('Subscription failed.')
|
||||
self.init_res['subscription'] = False
|
||||
elif event == 4:
|
||||
if errcode == 10200:
|
||||
log.info('Data sending succeeded.')
|
||||
@ -239,16 +286,31 @@ class QuecThing(object):
|
||||
elif event == 7:
|
||||
if errcode == 10700:
|
||||
log.info('New OTA plain.')
|
||||
ota_info = data.decode()
|
||||
file_info = ota_info.split(',')
|
||||
log.info(
|
||||
"OTA File Info: componentNo: %s, sourceVersion: %s, targetVersion: %s, "
|
||||
"batteryLimit: %s, minSignalIntensity: %s, useSpace: %s" % tuple(file_info)
|
||||
)
|
||||
self.downlink_queue.put(('ota_plain', data))
|
||||
self.downlink_queue.put(('object_model', [('ota_status', 1)]))
|
||||
elif errcode == 10701:
|
||||
log.info('The module starts to download.')
|
||||
self.downlink_queue.put(('object_model', [('ota_status', 2)]))
|
||||
if data != (7, 10701):
|
||||
ota_info = data.decode()
|
||||
file_info = ota_info.split(',')
|
||||
self.sota_download_info(int(file_info[1]), file_info[2], int(file_info[3]))
|
||||
elif errcode == 10702:
|
||||
log.info('Package download.')
|
||||
self.downlink_queue.put(('object_model', [('ota_status', 2)]))
|
||||
elif errcode == 10703:
|
||||
log.info('Package download complete.')
|
||||
if data != (7, 10703):
|
||||
ota_info = data.decode()
|
||||
file_info = ota_info.split(',')
|
||||
log.info("OTA File Info: componentNo: %s, length: %s, md5: %s, crc: %s" % tuple(file_info))
|
||||
self.sota_download_success(file_info[2], file_info[3])
|
||||
self.downlink_queue.put(('object_model', [('ota_status', 2)]))
|
||||
elif errcode == 10704:
|
||||
log.info('Package updating.')
|
||||
@ -259,9 +321,156 @@ class QuecThing(object):
|
||||
elif errcode == 10706:
|
||||
log.info('Failed to update firmware.')
|
||||
self.downlink_queue.put(('object_model', [('ota_status', 4)]))
|
||||
elif errcode == 10707:
|
||||
log.info('Received confirmation broadcast.')
|
||||
|
||||
def dev_info_report(self):
|
||||
quecIot.devInfoReport([i for i in range(1, 13)])
|
||||
|
||||
def ota_action(self, val=1):
|
||||
quecIot.otaAction(val)
|
||||
|
||||
def sota_download_info(self, size, md5_value, crc):
|
||||
self.file_size = size
|
||||
self.crc_value = crc
|
||||
self.download_size = 0
|
||||
self.update_mode = UpdateCtx()
|
||||
self.md5_value = md5_value
|
||||
|
||||
def read_sota_file(self):
|
||||
while self.need_download_size != 0:
|
||||
readsize = 4096
|
||||
if (readsize > self.need_download_size):
|
||||
readsize = self.need_download_size
|
||||
updateFile = quecIot.mcuFWDataRead(self.start_addr, readsize)
|
||||
self.update_mode.write_update_data(updateFile)
|
||||
log.debug("Download File Size: %s" % readsize)
|
||||
self.need_download_size -= readsize
|
||||
self.start_addr += readsize
|
||||
self.download_size += readsize
|
||||
if (self.download_size == self.file_size):
|
||||
log.debug("File Download Success, Update Start.")
|
||||
quecIot.otaAction(3)
|
||||
file_update_res = self.update_mode.file_update(self.md5_value)
|
||||
if file_update_res:
|
||||
log.debug("File Update Success, Power Restart.")
|
||||
self.downlink_queue.put(('object_model', [('ota_status', 3)]))
|
||||
self.downlink_queue.put(('object_model', [('power_restart', 1)]))
|
||||
else:
|
||||
log.debug("File Update Failed.")
|
||||
self.downlink_queue.put(('object_model', [('ota_status', 4)]))
|
||||
else:
|
||||
quecIot.otaAction(2)
|
||||
|
||||
def sota_download_success(self, start, down_loaded_size):
|
||||
self.need_download_size = down_loaded_size
|
||||
self.start_addr = start
|
||||
self.read_sota_file()
|
||||
|
||||
|
||||
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")
|
||||
|
@ -163,8 +163,6 @@ def downlink_process(argv):
|
||||
option_fun(*args)
|
||||
if self.remote_read_cb:
|
||||
self.remote_read_cb(*data)
|
||||
else:
|
||||
log.warn('Remote read callback is not defined.')
|
||||
else:
|
||||
# TODO: Raise Error OR Conntinue
|
||||
raise RemoteError('DownLinkOption has no accribute %s.' % option_attr)
|
||||
@ -204,7 +202,7 @@ def uplink_process(argv):
|
||||
value.pop(i) # Pop data from data-list after posting sueecss.
|
||||
need_refresh = True # Data in hist-dictionary changed, need to refresh history file.
|
||||
except Exception as e:
|
||||
log.error('uplink_process error: %s' % e)
|
||||
log.error('uplink_process Error: %s' % e)
|
||||
while True: # Put all data in uplink_queue to hist-dictionary.
|
||||
if self.uplink_queue.size():
|
||||
data = self.uplink_queue.get()
|
||||
@ -335,11 +333,12 @@ class Remote(Singleton):
|
||||
current_settings = settings.settings.get()
|
||||
if current_settings['sys']['cloud'] == settings.default_values_sys._cloud.quecIot:
|
||||
if current_settings['app']['sw_ota'] is True:
|
||||
log.debug('OTA Check To Report Dev Info.')
|
||||
self.cloud.dev_info_report()
|
||||
else:
|
||||
raise settings.SettingsError('OTA upgrade is disabled!')
|
||||
raise settings.SettingsError('OTA Upgrade Is Disabled!')
|
||||
else:
|
||||
raise settings.SettingsError('Current cloud (0x%X) not supported!' % current_settings['sys']['cloud'])
|
||||
raise settings.SettingsError('Current Cloud (0x%X) Not Supported!' % current_settings['sys']['cloud'])
|
||||
|
||||
def cloud_ota_action(self, val=1):
|
||||
current_settings = settings.settings.get()
|
||||
@ -349,4 +348,4 @@ class Remote(Singleton):
|
||||
settings.settings.set('ota_status', 0)
|
||||
settings.settings.save()
|
||||
else:
|
||||
raise settings.SettingsError('Current cloud (0x%X) not supported!' % current_settings['sys']['cloud'])
|
||||
raise settings.SettingsError('Current Cloud (0x%X) Not Supported!' % current_settings['sys']['cloud'])
|
||||
|
@ -16,6 +16,7 @@ import pm
|
||||
import ure
|
||||
import utime
|
||||
import _thread
|
||||
import osTimer
|
||||
|
||||
from queue import Queue
|
||||
from machine import RTC
|
||||
@ -118,21 +119,21 @@ def test_tracker():
|
||||
device_check_res = tracker.device_check()
|
||||
log.info('[.] device_check_res:', device_check_res)
|
||||
|
||||
log.info('[.] sleep 3')
|
||||
utime.sleep(3)
|
||||
log.info('[.] sleep 10')
|
||||
utime.sleep(10)
|
||||
|
||||
log.info('[.] tracker.power_manage.low_energy_init()')
|
||||
tracker.power_manage.low_energy_init()
|
||||
log.info('[.] tracker.power_manage.start_rtc()')
|
||||
tracker.power_manage.start_rtc()
|
||||
log.info('[.] end tracker.power_manage.start_rtc()')
|
||||
# log.info('[.] tracker.power_manage.low_energy_init()')
|
||||
# tracker.power_manage.low_energy_init()
|
||||
# log.info('[.] tracker.power_manage.start_rtc()')
|
||||
# tracker.power_manage.start_rtc()
|
||||
# log.info('[.] end tracker.power_manage.start_rtc()')
|
||||
|
||||
# log.info('[.] test tracker.device_check()')
|
||||
# device_check_res = tracker.device_check()
|
||||
# log.info('[.] device_check_res:', device_check_res)
|
||||
|
||||
# log.info('[.] test tracker.remote.check_ota()')
|
||||
# tracker.remote.check_ota()
|
||||
log.info('[.] test tracker.remote.check_ota()')
|
||||
tracker.remote.check_ota()
|
||||
|
||||
# log.info('[.] sleep 3')
|
||||
# utime.sleep(3)
|
||||
@ -264,6 +265,15 @@ def test_gps_uart():
|
||||
log.debug('[test_gps_uart] gps_info size: %s' % len(gps_info))
|
||||
|
||||
|
||||
def timer_cb(args):
|
||||
print('[%s] timer callback' % utime.mktime(utime.localtime()))
|
||||
|
||||
|
||||
def test_ostimer():
|
||||
timer = osTimer()
|
||||
timer.start(1000, 2, timer_cb)
|
||||
|
||||
|
||||
def main():
|
||||
# test_quecthing()
|
||||
# test_settings()
|
||||
@ -276,6 +286,7 @@ def main():
|
||||
# test_pm()
|
||||
# test_rtc()
|
||||
# test_gps_uart()
|
||||
# test_ostimer()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -182,7 +182,7 @@ class Tracker(Singleton):
|
||||
return str(num)
|
||||
|
||||
def data_report_cb(self, topic, msg):
|
||||
log.debug('[x] recive res topic [%s]' % topic)
|
||||
log.debug('[x] recive res topic [%s] msg [%s]' % (topic, msg))
|
||||
sys_bus.unsubscribe(topic)
|
||||
|
||||
if topic.endswith('/wakelock_unlock'):
|
||||
|
Loading…
x
Reference in New Issue
Block a user