update: 1. quec & ali cloud OTA; 2. GPS UART read; 3. module object;

This commit is contained in:
JackSun-qc 2022-04-05 12:09:23 +08:00
parent fbd42c42f9
commit 08dd3afb32
12 changed files with 844 additions and 202 deletions

View File

@ -488,7 +488,7 @@
},
{
"identifier": "user_ota_action",
"name": "是否OTA升级",
"name": "确认OTA升级",
"accessMode": "rw",
"desc": "用户操作,只写功能",
"required": false,
@ -506,14 +506,75 @@
"accessMode": "r",
"required": false,
"dataType": {
"type": "enum",
"specs": {
"0": "无升级",
"1": "待升级",
"2": "升级中",
"3": "升级成功",
"4": "升级失败"
}
"type": "struct",
"specs": [
{
"identifier": "sys_current_version",
"name": "系统当前版本",
"dataType": {
"type": "text",
"specs": {
"length": "1024"
}
}
},
{
"identifier": "sys_target_version",
"name": "系统目标版本",
"dataType": {
"type": "text",
"specs": {
"length": "1024"
}
}
},
{
"identifier": "app_current_version",
"name": "应用当前版本",
"dataType": {
"type": "text",
"specs": {
"length": "1024"
}
}
},
{
"identifier": "app_target_version",
"name": "应用目标版本",
"dataType": {
"type": "text",
"specs": {
"length": "1024"
}
}
},
{
"identifier": "upgrade_module",
"name": "当前升级模块",
"dataType": {
"type": "enum",
"specs": {
"0": "无",
"1": "系统",
"2": "应用"
}
}
},
{
"identifier": "upgrade_status",
"name": "当前升级状态",
"dataType": {
"type": "enum",
"specs": {
"0": "无",
"1": "待升级",
"2": "升级中",
"3": "升级成功",
"4": "升级失败"
}
}
}
]
}
},
{
@ -531,6 +592,22 @@
"step": "1"
}
}
},
{
"identifier": "current_speed",
"name": "当前时速",
"accessMode": "r",
"required": false,
"dataType": {
"type": "float",
"specs": {
"min": "0",
"max": "200.00",
"unit": "km/h",
"unitName": "千米每小时",
"step": "0.01"
}
}
}
],
"events": [
@ -975,7 +1052,7 @@
},
{
"identifier": "user_ota_action",
"name": "是否OTA升级",
"name": "确认OTA升级",
"dataType": {
"type": "enum",
"specs": {
@ -988,14 +1065,75 @@
"identifier": "ota_status",
"name": "OTA升级状态",
"dataType": {
"type": "enum",
"specs": {
"0": "无升级",
"1": "待升级",
"2": "升级中",
"3": "升级成功",
"4": "升级失败"
}
"type": "struct",
"specs": [
{
"identifier": "sys_current_version",
"name": "系统当前版本",
"dataType": {
"type": "text",
"specs": {
"length": "1024"
}
}
},
{
"identifier": "sys_target_version",
"name": "系统目标版本",
"dataType": {
"type": "text",
"specs": {
"length": "1024"
}
}
},
{
"identifier": "app_current_version",
"name": "应用当前版本",
"dataType": {
"type": "text",
"specs": {
"length": "1024"
}
}
},
{
"identifier": "app_target_version",
"name": "应用目标版本",
"dataType": {
"type": "text",
"specs": {
"length": "1024"
}
}
},
{
"identifier": "upgrade_module",
"name": "当前升级模块",
"dataType": {
"type": "enum",
"specs": {
"0": "无",
"1": "系统",
"2": "应用"
}
}
},
{
"identifier": "upgrade_status",
"name": "当前升级状态",
"dataType": {
"type": "enum",
"specs": {
"0": "无",
"1": "待升级",
"2": "升级中",
"3": "升级成功",
"4": "升级失败"
}
}
}
]
}
},
{
@ -1011,6 +1149,20 @@
"step": "1"
}
}
},
{
"identifier": "current_speed",
"name": "当前时速",
"dataType": {
"type": "float",
"specs": {
"min": "0",
"max": "200.00",
"unit": "km/h",
"unitName": "千米每小时",
"step": "0.01"
}
}
}
]
},
@ -1328,7 +1480,7 @@
},
{
"identifier": "user_ota_action",
"name": "是否OTA升级",
"name": "确认OTA升级",
"dataType": {
"type": "enum",
"specs": {
@ -1375,7 +1527,8 @@
"gps_mode",
"user_ota_action",
"ota_status",
"voltage"
"voltage",
"current_speed"
],
"outputData": [
{
@ -1811,7 +1964,7 @@
},
{
"identifier": "user_ota_action",
"name": "是否OTA升级",
"name": "确认OTA升级",
"dataType": {
"type": "enum",
"specs": {
@ -1824,14 +1977,75 @@
"identifier": "ota_status",
"name": "OTA升级状态",
"dataType": {
"type": "enum",
"specs": {
"0": "无升级",
"1": "待升级",
"2": "升级中",
"3": "升级成功",
"4": "升级失败"
}
"type": "struct",
"specs": [
{
"identifier": "sys_current_version",
"name": "系统当前版本",
"dataType": {
"type": "text",
"specs": {
"length": "1024"
}
}
},
{
"identifier": "sys_target_version",
"name": "系统目标版本",
"dataType": {
"type": "text",
"specs": {
"length": "1024"
}
}
},
{
"identifier": "app_current_version",
"name": "应用当前版本",
"dataType": {
"type": "text",
"specs": {
"length": "1024"
}
}
},
{
"identifier": "app_target_version",
"name": "应用目标版本",
"dataType": {
"type": "text",
"specs": {
"length": "1024"
}
}
},
{
"identifier": "upgrade_module",
"name": "当前升级模块",
"dataType": {
"type": "enum",
"specs": {
"0": "无",
"1": "系统",
"2": "应用"
}
}
},
{
"identifier": "upgrade_status",
"name": "当前升级状态",
"dataType": {
"type": "enum",
"specs": {
"0": "无",
"1": "待升级",
"2": "升级中",
"3": "升级成功",
"4": "升级失败"
}
}
}
]
}
},
{
@ -1847,6 +2061,20 @@
"step": "1"
}
}
},
{
"identifier": "current_speed",
"name": "当前时速",
"dataType": {
"type": "float",
"specs": {
"min": "0",
"max": "200.00",
"unit": "km/h",
"unitName": "千米每小时",
"step": "0.01"
}
}
}
]
}

View File

@ -22,6 +22,10 @@ from aLiYun import aLiYun
from usr.logging import getLogger
from usr.settings import settings
from usr.settings import default_values_sys
from usr.settings import PROJECT_VERSION
from usr.settings import PROJECT_NAME
from usr.settings import SYSNAME
from usr.settings import DEVICE_FIRMWARE_VERSION
from usr.common import numiter
log = getLogger(__name__)
@ -72,6 +76,15 @@ object_model = {
],
}
_gps_read_lock = _thread.allocate_lock()
def get_post_res_lock(func):
def wrapperd_fun(*args, **kwargs):
with _gps_read_lock:
return func(*args, **kwargs)
return wrapperd_fun
class AliYunIotError(Exception):
def __init__(self, value):
@ -83,11 +96,18 @@ class AliYunIotError(Exception):
class AliYunIot(object):
def __init__(self, pk, ps, dk, ds, downlink_queue):
def __init__(self, pk, ps, dk, ds, server, downlink_queue):
self.pk = pk
self.ps = ps
self.dk = dk
self.ds = ds
self.server = server
self.ali = None
self.downlink_queue = downlink_queue
self.post_res = {}
self.breack_flag = 0
self.ali_timer = osTimer()
self.downlink_queue = downlink_queue
self.id_iter = numiter()
self.id_lock = _thread.allocate_lock()
@ -99,20 +119,47 @@ class AliYunIot(object):
self.ica_topic_property_query = '/sys/%s/%s/thing/service/property/query' % (pk, dk)
self.ica_topic_event_post = '/sys/%s/%s/thing/event/{}/post' % (pk, dk)
self.ica_topic_event_post_reply = '/sys/%s/%s/thing/event/{}/post_reply' % (pk, dk)
self.ota_topic_device_inform = '/ota/device/inform/%s/%s' % (pk, dk)
self.ota_topic_device_upgrade = '/ota/device/upgrade/%s/%s' % (pk, dk)
self.ota_topic_device_progress = '/ota/device/progress/%s/%s' % (pk, dk)
self.ota_topic_firmware_get = '/sys/%s/%s/thing/ota/firmware/get' % (pk, dk)
self.ota_topic_firmware_get_reply = '/sys/%s/%s/thing/ota/firmware/get_reply' % (pk, dk)
# TODO: To Download OTA File For MQTT Association (Not Support Now.)
self.ota_topic_file_download = '/sys/%s/%s/thing/file/download' % (pk, dk)
self.ota_topic_file_download_reply = '/sys/%s/%s/thing/file/download_reply' % (pk, dk)
self.cloud_init()
def cloud_init(self, enforce=False):
if enforce is False and self.ali is not None:
if self.ali.getAliyunSta() == 0:
return True
else:
self.ali.disconnect()
current_settings = settings.get()
if current_settings['sys']['ali_burning_method'] == default_values_sys._ali_burning_method.one_type_one_density:
dk = None
self.dk = None
elif current_settings['sys']['ali_burning_method'] == default_values_sys._ali_burning_method.one_machine_one_density:
ps = None
self.ali = aLiYun(pk, ps, dk, ds)
self.clientID = dk
setMqttres = self.ali.setMqtt(self.clientID, clean_session=False, keepAlive=60, reconn=True)
if setMqttres == -1:
raise AliYunIotError('setMqtt Falied!')
self.ali.setCallback(self.ali_sub_cb)
self.ali_subcribe_topic()
self.ali.start()
self.ps = None
self.ali = aLiYun(self.pk, self.ps, self.dk, self.ds)
setMqttres = self.ali.setMqtt(self.dk, clean_session=False, keepAlive=current_settings['sys']['cloud_life_time'], reconn=True)
if setMqttres != -1:
self.ali.setCallback(self.ali_sub_cb)
self.ali_subcribe_topic()
self.ali.start()
else:
log.error('setMqtt Falied!')
return False
if self.ali.getAliyunSta() == 0:
self.device_report()
self.ota_request()
return True
else:
return False
def ali_subcribe_topic(self):
if self.ali.subscribe(self.ica_topic_property_post, qos=0) == -1:
@ -134,6 +181,15 @@ class AliYunIot(object):
if self.ali.subscribe(post_reply_topic, qos=0) == -1:
log.error('Topic [%s] Subscribe Falied.' % post_reply_topic)
if self.ali.subscribe(self.ota_topic_device_upgrade, qos=0) == -1:
log.error('Topic [%s] Subscribe Falied.' % self.ota_topic_device_upgrade)
if self.ali.subscribe(self.ota_topic_firmware_get_reply, qos=0) == -1:
log.error('Topic [%s] Subscribe Falied.' % self.ota_topic_firmware_get_reply)
# TODO: To Download OTA File For MQTT Association (Not Support Now.)
if self.ali.subscribe(self.ota_topic_file_download_reply, qos=0) == -1:
log.error('Topic [%s] Subscribe Falied.' % self.ota_topic_file_download_reply)
def get_id(self):
with self.id_lock:
try:
@ -147,14 +203,15 @@ class AliYunIot(object):
def put_post_res(self, msg_id, res):
self.post_res[msg_id] = res
@get_post_res_lock
def get_post_res(self, msg_id):
current_settings = settings.get()
self.ali_timer.start(current_settings['sys']['checknet_timeout'] * 1000, 2, self.ali_timer_cb)
self.ali_timer.start(current_settings['sys']['checknet_timeout'] * 1000, 0, self.ali_timer_cb)
while self.post_res.get(msg_id) is None:
utime.sleep_ms(200)
if self.breack_flag:
self.post_res[msg_id] = False
break
utime.sleep_ms(50)
self.ali_timer.stop()
self.breack_flag = 0
res = self.post_res.pop(msg_id)
@ -221,17 +278,104 @@ class AliYunIot(object):
return False
def device_report(self):
self.ota_device_inform(PROJECT_VERSION, module=PROJECT_NAME)
self.ota_device_inform(DEVICE_FIRMWARE_VERSION, module=SYSNAME)
def ota_request(self):
self.ota_firmware_get(PROJECT_NAME)
self.ota_firmware_get(SYSNAME)
def ota_device_inform(self, version, module='default'):
msg_id = self.get_id()
publish_data = {
'id': msg_id,
'params': {
'version': version,
'module': module,
},
}
publish_res = self.ali.publish(self.ota_topic_device_inform, ujson.dumps(publish_data), qos=0)
log.debug('version: %s, module: %s, publish_res: %s' % (version, module, publish_res))
return publish_res
def ota_device_progress(self, step, desc, module='default'):
msg_id = self.get_id()
publish_data = {
'id': msg_id,
'params': {
'step': step,
'desc': desc,
'module': module,
}
}
publish_res = self.ali.publish(self.ota_topic_device_progress, ujson.dumps(publish_data), qos=0)
if publish_res:
return self.get_post_res(msg_id)
else:
log.error('ota_device_progress publish_res: %s' % publish_res)
return False
def ota_firmware_get(self, module):
msg_id = self.get_id()
publish_data = {
'id': msg_id,
'version': '1.0',
'params': {
'module': module,
},
"method": "thing.ota.firmware.get"
}
publish_res = self.ali.publish(self.ota_topic_firmware_get, ujson.dumps(publish_data), qos=0)
log.debug('module: %s, publish_res: %s' % (module, publish_res))
if publish_res:
return self.get_post_res(msg_id)
else:
log.error('ota_firmware_get publish_res: %s' % publish_res)
return False
def ota_file_download(self, params):
msg_id = self.get_id()
publish_data = {
'id': msg_id,
'version': '1.0',
'params': params
}
publish_res = self.ali.publish(self.ota_topic_file_download, ujson.dumps(publish_data), qos=0)
if publish_res:
return self.get_post_res(msg_id)
else:
log.error('ota_file_download publish_res: %s' % publish_res)
return False
def ali_sub_cb(self, topic, data):
# log.info('topic: %s, data: %s' % (topic.decode(), data.decode()))
topic = topic.decode()
data = ujson.loads(data)
log.info('topic: %s, data: %s' % (topic, data))
if topic.endswith('/post_reply'):
log.info('topic: %s, data: %s' % (topic, data))
self.put_post_res(data['id'], True if data['code'] == 200 else False)
elif topic.endswith('/property/set'):
log.info('topic: %s, data: %s' % (topic, data))
if data['method'] == 'thing.service.property.set':
dl_data = list(zip(data.get("params", {}).keys(), data.get("params", {}).values()))
self.downlink_queue.put(('object_model', dl_data))
elif topic.startswith('/ota/device/upgrade/'):
self.put_post_res(data['id'], True if int(data['code']) == 1000 else False)
if int(data['code']) == 1000:
if data.get('data'):
self.downlink_queue.put(('object_model', [('ota_status', (data['data']['module'], 1, data['data']['version']))]))
self.downlink_queue.put(('ota_plain', data['data']))
elif topic.endswith('/thing/ota/firmware/get_reply'):
self.put_post_res(data['id'], True if int(data['code']) == 200 else False)
if data['code'] == 200:
if data.get('data'):
self.downlink_queue.put(('object_model', [('ota_status', (data['data']['module'], 1, data['data']['version']))]))
self.downlink_queue.put(('ota_plain', data['data']))
# TODO: To Download OTA File For MQTT Association (Not Support Now.)
elif topic.endswith('/thing/file/download_reply'):
self.put_post_res(data['id'], True if int(data['code']) == 200 else False)
if data['code'] == 200:
# self.downlink_queue.put(('ota_file_download', data['data']))
pass
else:
pass

View File

@ -85,18 +85,26 @@ class GPS(Singleton):
def uart_init(self):
global gps_data_retrieve_queue
gps_data_retrieve_queue = Queue(maxsize=8)
self.uart_open()
def uart_open(self):
self.uart_obj = UART(
self.gps_cfg['UARTn'], self.gps_cfg['buadrate'], self.gps_cfg['databits'],
self.gps_cfg['parity'], self.gps_cfg['stopbits'], self.gps_cfg['flowctl']
)
self.uart_obj.set_callback(gps_data_retrieve_cb)
gps_data_retrieve_queue = Queue(maxsize=8)
def uart_close(self):
self.uart_obj.close()
def first_gps_timer_callback(self, args):
global gps_data_retrieve_queue
self.__first_break = 1
log.debug('self.__first_break: %s' % self.__first_break)
if gps_data_retrieve_queue is not None:
gps_data_retrieve_queue.put(0)
log.debug('gps_data_retrieve_queue.put(0)')
def second_gps_timer_callback(self, args):
global gps_data_retrieve_queue
@ -105,13 +113,22 @@ class GPS(Singleton):
gps_data_retrieve_queue.put(0)
def uart_read(self):
self.uart_open()
log.debug('uart_read start')
global gps_data_retrieve_queue
while self.__first_break == 0:
self.__gps_timer.start(50, 0, self.first_gps_timer_callback)
log.debug('self.__first_break: %s' % self.__first_break)
timer_start_res = self.__gps_timer.start(50, 0, self.first_gps_timer_callback)
log.debug('timer_start_res: %s' % timer_start_res)
nread = gps_data_retrieve_queue.get()
# nread = 0
# self.__first_break = 1
log.debug('__first_break nread: %s' % nread)
data = self.uart_obj.read(nread).decode()
log.debug('__first_break data: %s' % data)
self.__gps_timer.stop()
log.debug('self.__first_break: %s' % self.__first_break)
self.__first_break = 0
data = ''
@ -119,8 +136,12 @@ class GPS(Singleton):
gga_data = ''
vtg_data = ''
while self.__second_break == 0:
log.debug('self.__second_break: %s' % self.__second_break)
self.__gps_timer.start(1500, 0, self.second_gps_timer_callback)
nread = gps_data_retrieve_queue.get()
# nread = 0
# self.__second_break = 1
log.debug('__second_break nread: %s' % nread)
if nread:
data += self.uart_obj.read(nread).decode()
if not rmc_data:
@ -132,8 +153,12 @@ class GPS(Singleton):
if rmc_data and gga_data and vtg_data:
self.__second_break = 1
self.__gps_timer.stop()
log.debug('self.__second_break: %s' % self.__second_break)
log.debug('__second_break data: %s' % data)
self.__second_break = 0
log.debug('uart_read data: %s' % data)
self.uart_close()
return data
def quecgnss_read(self):
@ -183,7 +208,6 @@ class GPS(Singleton):
self.gps_data = self.quecgnss_read()
else:
self.gps_data = ''
return self.gps_data
def read_location_GxRMC(self, gps_data):

View File

@ -13,29 +13,23 @@
# limitations under the License.
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.settings import SYSNAME
from usr.settings import DEVICE_FIRMWARE_VERSION
from usr.logging import getLogger
log = getLogger(__name__)
def main():
log.info('PROJECT_NAME: %s' % PROJECT_NAME)
log.info('PROJECT_VERSION: %s' % PROJECT_VERSION)
current_settings = settings.get()
log.info('PROJECT_NAME: %s, PROJECT_VERSION: %s' % (PROJECT_NAME, PROJECT_VERSION))
log.info('SYSNAME: %s, DEVICE_FIRMWARE_VERSION: %s' % (SYSNAME, DEVICE_FIRMWARE_VERSION))
tracker = Tracker()
# Start Device Check
tracker.device_check()
# Start OTA Check
if current_settings['sys']['cloud'] == default_values_sys._cloud.quecIot and \
current_settings['app']['sw_ota'] is True:
tracker.remote.check_ota()
# Start PowerManage
# Init Low Energy Work Mode
tracker.power_manage.low_energy_init()

View File

@ -121,4 +121,6 @@ class PowerManage(Singleton):
pm.wakelock_lock(self.lpm_fd)
over_speed_check_res = self.tracker.get_over_speed_check()
log.debug('over_speed_check_res: %s' % str(over_speed_check_res))
self.tracker.device_data_report(event_data=over_speed_check_res, msg=data)
self.tracker.remote.check_ota()

View File

@ -11,3 +11,9 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
def OTA(object):
def __init__(self)
pass

View File

@ -26,6 +26,8 @@ from queue import Queue
from usr.logging import getLogger
from usr.settings import settings
from usr.settings import PROJECT_NAME
from usr.settings import PROJECT_VERSION
log = getLogger(__name__)
@ -56,8 +58,9 @@ object_model = [
(36, ('device_module_status', 'r')),
(37, ('gps_mode', 'r')),
(38, ('user_ota_action', 'w')),
(39, ('ota_status', 'r')),
(41, ('voltage', 'r')),
(42, ('ota_status', 'r')),
(43, ('current_speed', 'r')),
# event
(6, ('sos_alert', 'r')),
@ -82,19 +85,27 @@ object_model_struct = {
'gps': 1,
'cell': 2,
'wifi': 3,
}
},
'ota_status': {
'sys_current_version': 1,
'sys_target_version': 2,
'app_current_version': 3,
'app_target_version': 4,
'upgrade_module': 5,
'upgrade_status': 6,
},
}
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):
def __init__(self, pk, ps, dk, ds, server, downlink_queue):
self.pk = pk
self.ps = ps
self.dk = dk
self.ds = ds
self.init_res = {}
self.server = server
self.fileSize = 0
self.needDownloadSize = 0
self.crcValue = 0
@ -104,48 +115,58 @@ class QuecThing(object):
self.downlink_queue = downlink_queue
self.post_result_wait_queue = Queue(maxsize=16)
self.quec_timer = osTimer()
self.queciot_init()
self.cloud_init()
fileClear = OTAFileClear()
fileClear.file_clear()
def queciot_init(self):
if quecIot.getWorkState() == 8 and quecIot.getConnmode() == 1:
return
def cloud_init(self, enforce=False):
current_settings = settings.get()
log.debug(
'[cloud_init start] enforce: %s QuecThing Work State: %s, quecIot.getConnmode(): %s'
% (enforce, quecIot.getWorkState(), quecIot.getConnmode())
)
if enforce is False:
if quecIot.getWorkState() == 8 and quecIot.getConnmode() == 1:
return True
quecIot.init()
quecIot.setEventCB(self.eventCB)
quecIot.setProductinfo(self.pk, self.ps)
quecIot.setDkDs(self.dk, self.ds)
quecIot.setServer(1, "iot-south.quectel.com:2883")
if self.dk or self.ds:
quecIot.setDkDs(self.dk, self.ds)
quecIot.setServer(1, self.server)
quecIot.setLifetime(current_settings['sys']['cloud_life_time'])
quecIot.setMcuVersion(PROJECT_NAME, PROJECT_VERSION)
quecIot.setConnmode(1)
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)
count += 1
if not self.ds and self.dk:
count = 0
while count < 3:
ndk, nds = quecIot.getDkDs()
if nds:
dkds = quecIot.getDkDs()
if dkds:
self.dk, self.ds = dkds
break
count += 1
utime.sleep(count)
current_settings = settings.get()
cloud_init_params = current_settings['sys']['cloud_init_params']
cloud_init_params['DS'] = nds
self.dk = ndk
self.ds = nds
cloud_init_params['DS'] = self.ds
settings.set('cloud_init_params', cloud_init_params)
settings.save()
log.debug('[cloud_init over] QuecThing Work State: %s, quecIot.getConnmode(): %s' % (quecIot.getWorkState(), quecIot.getConnmode()))
if quecIot.getWorkState() == 8 and quecIot.getConnmode() == 1:
return True
else:
return False
def get_post_res(self):
current_settings = settings.get()
self.quec_timer.start(current_settings['sys']['checknet_timeout'] * 1000, 1, self.quec_timer_cb)
self.quec_timer.start(5000, 0, self.quec_timer_cb)
res = self.post_result_wait_queue.get()
self.quec_timer.stop()
return res
@ -153,6 +174,7 @@ class QuecThing(object):
def quec_timer_cb(self, args):
# Power.powerRestart()
self.post_result_wait_queue.put(False)
self.quec_timer.stop()
@staticmethod
def rm_empty_data(data):
@ -161,7 +183,6 @@ 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():
@ -225,15 +246,12 @@ 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)
self.ota_request()
if data != (3, 10200):
ota_info = data.decode()
file_info = ota_info.split(',')
@ -243,7 +261,6 @@ class QuecThing(object):
)
if errcode == 10300:
log.info('Subscription failed.')
self.init_res['subscription'] = False
elif event == 4:
if errcode == 10200:
log.info('Data sending succeeded.')
@ -293,39 +310,39 @@ class QuecThing(object):
"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)]))
self.downlink_queue.put(('object_model', [('ota_status', (data[0], 1, data[2]))]))
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]))
self.downlink_queue.put(('object_model', [('ota_status', (None, 2, None))]))
elif errcode == 10702:
log.info('Package download.')
self.downlink_queue.put(('object_model', [('ota_status', 2)]))
self.downlink_queue.put(('object_model', [('ota_status', (None, 2, None))]))
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)]))
self.sota_download_success(int(file_info[2]), int(file_info[3]))
self.downlink_queue.put(('object_model', [('ota_status', (None, 2, None))]))
elif errcode == 10704:
log.info('Package updating.')
self.downlink_queue.put(('object_model', [('ota_status', 2)]))
self.downlink_queue.put(('object_model', [('ota_status', (None, 2, None))]))
elif errcode == 10705:
log.info('Firmware update complete.')
self.downlink_queue.put(('object_model', [('ota_status', 3)]))
self.downlink_queue.put(('object_model', [('ota_status', (None, 3, None))]))
elif errcode == 10706:
log.info('Failed to update firmware.')
self.downlink_queue.put(('object_model', [('ota_status', 4)]))
self.downlink_queue.put(('object_model', [('ota_status', (None, 4, None))]))
elif errcode == 10707:
log.info('Received confirmation broadcast.')
def dev_info_report(self):
quecIot.devInfoReport([i for i in range(1, 13)])
def ota_request(self):
quecIot.otaRequest(0)
def ota_action(self, val=1):
quecIot.otaAction(val)
@ -337,6 +354,11 @@ class QuecThing(object):
self.update_mode = UpdateCtx()
self.md5_value = md5_value
def sota_download_success(self, start, down_loaded_size):
self.need_download_size = down_loaded_size
self.start_addr = start
self.read_sota_file()
def read_sota_file(self):
while self.need_download_size != 0:
readsize = 4096
@ -354,19 +376,12 @@ class QuecThing(object):
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)]))
self.downlink_queue.put(('object_model', [('power_restart', 1)]))
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/"):

View File

@ -13,7 +13,6 @@
# limitations under the License.
import uos
import utime
import ql_fs
import ujson
import _thread
@ -81,18 +80,34 @@ class Controller(Singleton):
elif action == 1:
self.tracker.remote.cloud_ota_action(1)
def ota_status(self, perm, status=None):
def ota_status(self, perm, upgrade_info=None):
if perm == 'r':
self.tracker.device_data_report()
elif perm == 'w':
if status is not None:
settings.settings.set('ota_status', status)
if upgrade_info:
current_settings = settings.settings.get()
ota_status_info = current_settings['sys']['ota_status']
ota_info = {}
if upgrade_info[0] == settings.SYSNAME:
ota_info['upgrade_module'] = 1
ota_info['sys_target_version'] = upgrade_info[2]
elif upgrade_info[0] == settings.PROJECT_NAME:
ota_info['upgrade_module'] = 2
ota_info['app_target_version'] = upgrade_info[2]
ota_info['upgrade_status'] = upgrade_info[1]
ota_status_info.update(ota_info)
settings.settings.set('ota_status', ota_status_info)
settings.settings.save()
def power_restart(self, perm, flag):
if perm == 'w':
self.tracker.device_data_report(power_switch=False, msg='power_restart')
def work_cycle_period(self, perm, period):
if perm == 'w':
self.tracker.power_manage.rtc.enable_alarm(0)
self.tracker.power_manage.start_rtc()
class DownLinkOption(object):
def __init__(self, tracker):
@ -139,7 +154,14 @@ class DownLinkOption(object):
def ota_plain(self, *args, **kwargs):
current_settings = settings.settings.get()
if current_settings['app']['sw_ota'] and current_settings['app']['sw_ota_auto_upgrade']:
self.tracker.remote.cloud_ota_action()
if current_settings['sys']['cloud'] == settings.default_values_sys._cloud.quecIot:
self.tracker.remote.cloud_ota_action()
elif current_settings['sys']['cloud'] == settings.default_values_sys._cloud.AliYun:
log.debug('ota_plain args: %s' % str(args))
log.debug('ota_plain kwargs: %s' % str(kwargs))
def ota_file_download(self, *args, **kwargs):
log.debug('ota_file_download: %s' % str(args))
def downlink_process(argv):
@ -157,10 +179,11 @@ def downlink_process(argv):
DownLinkOptionObj = DownLinkOption(tracker=self.tracker)
option_attr = data[0]
args = data[1]
args = data[1] if not isinstance(data[1], dict) else ()
kwargs = data[1] if isinstance(data[1], dict) else {}
if hasattr(DownLinkOptionObj, option_attr):
option_fun = getattr(DownLinkOptionObj, option_attr)
option_fun(*args)
option_fun(*args, **kwargs)
if self.remote_read_cb:
self.remote_read_cb(*data)
else:
@ -185,22 +208,19 @@ def uplink_process(argv):
# Read history data that didn't send to server intime to hist-dictionary.
hist = self.read_history()
try:
if self.tracker.net_enable is False:
if self.cloud_connect() is False:
raise RemoteError('Net Is Disconnected.')
for key, value in hist.items():
# Check if non_loca data (sensor or device info data) or location gps data or location non-gps data (cell/wifi-locator data)
if key == 'hist_data':
for i, data in enumerate(value):
ntry = 0
# Try at most 3 times to post data to server.
while not self.cloud.post_data(data):
ntry += 1
if ntry >= 3: # Data post failed after 3 times, maybe network error?
if not self.cloud.post_data(data):
self.cloud.cloud_init(enforce=True)
if not self.cloud.post_data(data):
raise RemoteError('Data post failed.') # Stop posting more data, go to exception handler.
utime.sleep(1)
else:
value.pop(i) # Pop data from data-list after posting sueecss.
need_refresh = True # Data in hist-dictionary changed, need to refresh history file.
break
value.pop(i)
need_refresh = True # Data in hist-dictionary changed, need to refresh history file.
except Exception as e:
log.error('uplink_process Error: %s' % e)
while True: # Put all data in uplink_queue to hist-dictionary.
@ -224,10 +244,15 @@ def uplink_process(argv):
data = self.uplink_queue.get()
if data:
if data[1]:
if self.tracker.net_enable is True:
if self.cloud_connect() is True:
if self.cloud.post_data(data[1]):
sys_bus.publish(data[0], 'true')
continue
else:
self.cloud.cloud_init(enforce=True)
if self.cloud.post_data(data[1]):
sys_bus.publish(data[0], 'true')
continue
else:
log.warn('Net Is Disconnected.')
self.add_history(data[1])
@ -250,9 +275,23 @@ class Remote(Singleton):
current_settings = settings.settings.get()
cloud_init_params = current_settings['sys']['cloud_init_params']
if current_settings['sys']['cloud'] == settings.default_values_sys._cloud.quecIot:
self.cloud = QuecThing(cloud_init_params['PK'], cloud_init_params['PS'], cloud_init_params['DK'], cloud_init_params['DS'], self.downlink_queue)
self.cloud = QuecThing(
cloud_init_params['PK'],
cloud_init_params['PS'],
cloud_init_params['DK'],
cloud_init_params['DS'],
cloud_init_params['SERVER'],
self.downlink_queue
)
elif current_settings['sys']['cloud'] == settings.default_values_sys._cloud.AliYun:
self.cloud = AliYunIot(cloud_init_params['PK'], cloud_init_params['PS'], cloud_init_params['DK'], cloud_init_params['DS'], self.downlink_queue)
self.cloud = AliYunIot(
cloud_init_params['PK'],
cloud_init_params['PS'],
cloud_init_params['DK'],
cloud_init_params['DS'],
cloud_init_params['SERVER'],
self.downlink_queue
)
else:
raise settings.SettingsError('Current cloud (0x%X) not supported!' % current_settings['sys']['cloud'])
@ -316,6 +355,13 @@ class Remote(Singleton):
def clean_history(self):
uos.remove(self._history)
def cloud_connect(self):
net_check_res = self.tracker.check.net_check()
if net_check_res == (3, 1):
return self.cloud.cloud_init()
else:
return False
def post_data(self, topic, data):
'''
Data format to post:
@ -331,21 +377,30 @@ class Remote(Singleton):
def check_ota(self):
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 or \
current_settings['sys']['cloud'] == settings.default_values_sys._cloud.AliYun:
if current_settings['app']['sw_ota'] is True:
log.debug('OTA Check To Report Dev Info.')
self.cloud.dev_info_report()
self.cloud.ota_request()
else:
raise settings.SettingsError('OTA Upgrade Is Disabled!')
log.warn('OTA Upgrade Is Disabled!')
else:
raise settings.SettingsError('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):
current_settings = settings.settings.get()
if current_settings['sys']['cloud'] == settings.default_values_sys._cloud.quecIot:
self.cloud.ota_action(val)
if val == 0:
settings.settings.set('ota_status', 0)
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()
else:
raise settings.SettingsError('Current Cloud (0x%X) Not Supported!' % current_settings['sys']['cloud'])
log.warn('Current Cloud (0x%X) Not Supported!' % current_settings['sys']['cloud'])

View File

@ -16,14 +16,19 @@ import uos
import ure
import ql_fs
import ujson
import modem
import _thread
from machine import UART
from usr.common import Singleton
PROJECT_NAME = 'QuecPython_Tracker'
PROJECT_NAME = 'QuecPython-Tracker'
PROJECT_VERSION = '2.0.1'
SYSNAME = uos.uname()[0].split('=')[1]
DEVICE_FIRMWARE_VERSION = modem.getDevFwVersion()
DATA_NON_LOCA = 0x0
DATA_LOCA_NON_GPS = 0x1
DATA_LOCA_GPS = 0x2
@ -47,6 +52,15 @@ DEVICE_MODULE_STATUS = {
'mike': 6,
}
OTA_STATUS = {
'sys_current_version': 1,
'sys_target_version': 2,
'app_current_version': 3,
'app_target_version': 4,
'upgrade_module': 5,
'upgrade_status': 6,
}
DRIVE_BEHAVIOR_CODE = {
0: 'none',
1: 'quick_start',
@ -128,11 +142,11 @@ class default_values_app(object):
work_cycle_period = 30
low_power_alert_threshold = 30
low_power_alert_threshold = 20
low_power_shutdown_threshold = 5
over_speed_threshold = 60
over_speed_threshold = 50
sw_ota = True
@ -172,13 +186,18 @@ class default_values_sys(object):
internal = 0x1
external = 0x2
class _ota_status(object):
class _ota_upgrade_status(object):
none = 0
to_be_updated = 1
updating = 2
update_successed = 3
update_failed = 4
class _ota_upgrade_module(object):
none = 0
sys = 1
app = 2
class _ali_burning_method(object):
one_type_one_density = 0
one_machine_one_density = 1
@ -194,39 +213,63 @@ class default_values_sys(object):
gps_mode = _gps_mode.external
ota_status = _ota_status.none
ota_status = {}
drive_behavior_code = 0
cloud = _cloud.quecIot
cloud_life_time = 120
cloud_init_params = {}
work_mode_timeline = 3600
ali_burning_method = _ali_burning_method.one_machine_one_density
# trackdev0304
# trackdev0304 (PROENV)
_quecIot = {
'PK': 'p11275',
'PS': 'Q0ZQQndaN3pCUFd6',
'DK': 'trackdev0304',
'DS': '8eba9389af434974c3c846d1922d949f',
'SERVER': 'iot-south.quectel.com:2883',
}
# # trackerdemo0326
# # trackerdemo0326 (PROENV)
# _quecIot = {
# 'PK': 'p11275',
# 'PS': 'Q0ZQQndaN3pCUFd6',
# 'DK': 'trackerdemo0326',
# 'DS': '32d540996e32f95c58dd98f18d473d52',
# 'SERVER': 'iot-south.quectel.com:2883',
# }
# # IMEI (PROENV)
# _quecIot = {
# 'PK': 'p11275',
# 'PS': 'Q0ZQQndaN3pCUFd6',
# 'DK': '',
# 'DS': '',
# 'SERVER': 'iot-south.quectel.com:2883',
# }
# # TrackerDevEC600NCNLC (TESTENV)
# _quecIot = {
# 'PK': 'p119v2',
# 'PS': 'TXRPdVVhdkY3bU5s',
# 'DK': 'TrackerDevEC600NCNLC',
# 'DS': '',
# 'SERVER': 'mqtt://220.180.239.212:8382',
# }
# # IMEI (TESTENV)
# _quecIot = {
# 'PK': 'p119v2',
# 'PS': 'TXRPdVVhdkY3bU5s',
# 'DK': '',
# 'DS': '',
# 'SERVER': 'mqtt://220.180.239.212:8382',
# }
# tracker_dev_jack
@ -235,6 +278,7 @@ class default_values_sys(object):
'PS': 'HQraBqtV8WsfCEuy',
'DK': 'tracker_dev_jack',
'DS': 'bfdfcca5075715e8309eff8597663c4b',
"SERVER": '',
}
_JTT808 = {
@ -242,6 +286,7 @@ class default_values_sys(object):
'PS': '',
'DK': '',
'DS': '',
"SERVER": '',
}
locator_init_params = {}
@ -293,6 +338,19 @@ class default_values_sys(object):
return cloud_init_params
@staticmethod
def _ota_status_init_params():
ota_status = {
'sys_current_version': SYSNAME,
'sys_target_version': '--',
'app_current_version': PROJECT_VERSION,
'app_target_version': '--',
'upgrade_module': default_values_sys._ota_upgrade_module.none,
'upgrade_status': default_values_sys._ota_upgrade_status.none,
}
return ota_status
class Settings(Singleton):
@ -307,6 +365,7 @@ class Settings(Singleton):
try:
default_values_sys.locator_init_params = default_values_sys._get_locator_init_params(default_values_app.loc_method)
default_values_sys.cloud_init_params = default_values_sys._get_cloud_init_params(default_values_sys.cloud)
default_values_sys.ota_status = default_values_sys._ota_status_init_params()
default_settings_app = {k: v for k, v in default_values_app.__dict__.items() if not k.startswith('_')}
default_settings_sys = {k: v for k, v in default_values_sys.__dict__.items() if not k.startswith('_')}
@ -380,7 +439,13 @@ class Settings(Singleton):
return False
self.current_settings['app'][opt] = val
return True
elif opt == 'over_speed_threshold':
if not isinstance(val, int):
return False
if val < 1:
return False
self.current_settings['app'][opt] = val
return True
else:
return False
if opt in self.current_settings['sys']:
@ -390,7 +455,7 @@ class Settings(Singleton):
self.current_settings['sys'][opt] = val
return True
elif opt == 'ota_status':
if not isinstance(val, int):
if not isinstance(val, dict):
return False
self.current_settings['sys'][opt] = val
return True

View File

@ -13,6 +13,7 @@
# limitations under the License.
import pm
import sim
import ure
import utime
import _thread
@ -122,25 +123,18 @@ def test_tracker():
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('[.] 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('[.] 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('[.] sleep 3')
# utime.sleep(3)
# log.info('[.] test tracker.machine_check()')
# machine_check_res = tracker.machine_check()
# log.info('[.] machine_check_res:', machine_check_res)
# log.info('[.] test tracker.remote.check_ota()')
# check_ota_res = tracker.remote.check_ota()
# log.info('[.] check_ota_res:', check_ota_res)
log.info('[x] end test_tracker')
@ -274,6 +268,24 @@ def test_ostimer():
timer.start(1000, 2, timer_cb)
sim_queue = Queue(maxsize=8)
def sim_cb(args):
log.debug('sim_cb args: %s' % str(args))
sim_queue.put(args)
def test_sim():
sim_status = sim.getStatus()
log.debug('sim_status: %s' % sim_status)
sim.setCallback(sim_cb)
sim.setSimDet(1, 1)
while True:
data = sim_queue.get()
log.debug('sim_queue data: %s' % data)
def main():
# test_quecthing()
# test_settings()
@ -287,6 +299,7 @@ def main():
# test_rtc()
# test_gps_uart()
# test_ostimer()
# test_sim()
if __name__ == '__main__':

View File

@ -13,6 +13,7 @@
# limitations under the License.
import pm
import sim
import utime
import _thread
import sys_bus
@ -49,7 +50,7 @@ log = getLogger(__name__)
class Tracker(Singleton):
def __init__(self, *args, **kwargs):
self.net_enable = True
sim.setSimDet(1, 0)
self.num_iter = numiter()
self.num_lock = _thread.allocate_lock()
@ -140,7 +141,6 @@ class Tracker(Singleton):
loc_check_res = self.check.loc_check()
sensor_check_res = self.check.sensor_check()
self.net_enable = True if net_check_res == (3, 1) else False
device_module_status['net'] = 1 if net_check_res == (3, 1) else 0
device_module_status['location'] = 1 if loc_check_res else 0
@ -159,15 +159,20 @@ class Tracker(Singleton):
return alert_data
def get_over_speed_check(self):
alert_data = {}
alert_data = {
'current_speed': 0.00
}
current_settings = settings.settings.get()
if current_settings['app']['work_mode'] == settings.default_values_app._work_mode.intelligent:
if current_settings['app']['sw_over_speed_alert'] is True:
if self.locator.gps:
speed = self.locator.gps.read_location_GxVTG_speed()
gps_data = self.locator.gps.read()
speed = self.locator.gps.read_location_GxVTG_speed(gps_data)
if speed and float(speed) >= current_settings['app']['over_speed_threshold']:
alert_code = 30003
alert_info = {'local_time': self.get_local_time()}
alert_data = self.get_alert_data(alert_code, alert_info)
if speed:
alert_data['current_speed'] = float(speed)
return alert_data
@ -215,8 +220,15 @@ class Tracker(Singleton):
# OTA Status RST
current_settings = settings.settings.get()
if current_settings['sys']['ota_status'] in (3, 4):
settings.settings.set('ota_status', 0)
ota_status_info = current_settings['sys']['ota_status']
if ota_status_info['upgrade_status'] in (3, 4):
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()
def device_check(self):
@ -256,7 +268,6 @@ class Tracker(Singleton):
def nw_callback(self, args):
net_check_res = self.check.net_check()
if args[1] != 1:
self.net_enable = False
if net_check_res[0] == 1 and net_check_res[1] != 1:
log.warn('SIM abnormal!')
alert_code = 30004
@ -265,7 +276,7 @@ class Tracker(Singleton):
self.device_data_report(event_data=alert_data, msg='sim_abnormal')
else:
if net_check_res == (3, 1):
self.net_enable = True
pass
class SelfCheck(object):
@ -276,6 +287,7 @@ class SelfCheck(object):
checknet = checkNet.CheckNetwork(settings.PROJECT_NAME, settings.PROJECT_VERSION)
timeout = current_settings.get('sys', {}).get('checknet_timeout', 60)
check_res = checknet.wait_network_connected(timeout)
log.debug('net_check res: %s' % str(check_res))
return check_res
def loc_check(self):

View File

@ -2,7 +2,7 @@
"profile":{
"tslVersion":"1.1.0",
"productKey":"p11275",
"version":"20220329170419124"
"version":"20220402191608542"
},
"properties":[
{
@ -660,50 +660,13 @@
],
"code":"user_ota_action",
"dataType":"ENUM",
"name":"是否OTA升级",
"name":"确认OTA升级",
"subType":"RW",
"id":38,
"sort":31,
"type":"PROPERTY",
"desc":"当OTA自动升级关闭时, 用户通过查看设备当前是否有OTA升级来进行自主选择升级或不升级"
},
{
"specs":[
{
"dataType":"ENUM",
"name":"无升级",
"value":"0"
},
{
"dataType":"ENUM",
"name":"待升级",
"value":"1"
},
{
"dataType":"ENUM",
"name":"升级中",
"value":"2"
},
{
"dataType":"ENUM",
"name":"升级成功",
"value":"3"
},
{
"dataType":"ENUM",
"name":"升级失败",
"value":"4"
}
],
"code":"ota_status",
"dataType":"ENUM",
"name":"OTA升级状态",
"subType":"R",
"id":39,
"sort":32,
"type":"PROPERTY",
"desc":""
},
{
"specs":{
"unit":"mV",
@ -719,6 +682,127 @@
"sort":33,
"type":"PROPERTY",
"desc":""
},
{
"specs":[
{
"specs":{
"length":"1024"
},
"code":"sys_current_version",
"dataType":"TEXT",
"name":"系统当前版本",
"id":1
},
{
"specs":{
"length":"1024"
},
"code":"sys_tartget_version",
"dataType":"TEXT",
"name":"系统目标版本",
"id":2
},
{
"specs":{
"length":"1024"
},
"code":"app_current_version",
"dataType":"TEXT",
"name":"应用当前版本",
"id":3
},
{
"specs":{
"length":"1024"
},
"code":"app_target_version",
"dataType":"TEXT",
"name":"应用目标版本",
"id":4
},
{
"specs":[
{
"dataType":"ENUM",
"name":"无",
"value":"0"
},
{
"dataType":"ENUM",
"name":"系统",
"value":"1"
},
{
"name":"应用",
"value":"2"
}
],
"code":"upgrade_module",
"dataType":"ENUM",
"name":"当前升级模块",
"id":5,
"desc":""
},
{
"specs":[
{
"dataType":"ENUM",
"name":"无",
"value":"0"
},
{
"dataType":"ENUM",
"name":"待升级",
"value":"1"
},
{
"dataType":"ENUM",
"name":"升级中",
"value":"2"
},
{
"dataType":"ENUM",
"name":"升级成功",
"value":"3"
},
{
"dataType":"ENUM",
"name":"升级失败",
"value":"4"
}
],
"code":"upgrade_status",
"dataType":"ENUM",
"name":"当前升级状态",
"id":6,
"desc":""
}
],
"code":"ota_status",
"dataType":"STRUCT",
"name":"OTA升级",
"subType":"R",
"id":42,
"sort":34,
"type":"PROPERTY",
"desc":""
},
{
"specs":{
"unit":"km/h",
"min":"0",
"max":"200.00",
"step":"0.01"
},
"code":"current_speed",
"dataType":"FLOAT",
"name":"当前时速",
"subType":"R",
"id":43,
"sort":35,
"type":"PROPERTY",
"desc":""
}
],
"events":[