demo.tracker-v2/code/quecthing.py

387 lines
15 KiB
Python
Raw Normal View History

2022-03-24 16:01:12 +08:00
# Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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.
2022-04-05 14:56:50 +08:00
import utime
import osTimer
2022-03-03 09:53:51 +08:00
import quecIot
2022-03-18 17:53:46 +08:00
from queue import Queue
2022-03-18 17:53:46 +08:00
2022-04-12 09:13:20 +08:00
from usr.ota import SOTA
2022-03-03 09:53:51 +08:00
from usr.logging import getLogger
2022-04-13 11:55:36 +08:00
from usr.common import CloudObservable, CloudObjectModel
2022-03-03 09:53:51 +08:00
log = getLogger(__name__)
2022-04-12 09:13:20 +08:00
EVENT_CODE = {
1: {
10200: "Device authentication succeeded.",
10420: "Bad request data (connection failed).",
10422: "Device authenticated (connection failed).",
10423: "No product information found (connection failed).",
10424: "PAYLOAD parsing failed (connection failed).",
10425: "Signature verification failed (connection failed).",
10426: "Bad authentication version (connection failed).",
10427: "Invalid hash information (connection failed).",
10430: "PK changed (connection failed).",
10431: "Invalid DK (connection failed).",
10432: "PK does not match authentication version (connection failed).",
10450: "Device internal error (connection failed).",
10466: "Boot server address not found (connection failed).",
10500: "Device authentication failed (an unknown exception occurred in the system).",
10300: "Other errors.",
},
2: {
10200: "Access is successful.",
10430: "Incorrect device key (connection failed).",
10431: "Device is disabled (connection failed).",
10450: "Device internal error (connection failed).",
10471: "Implementation version not supported (connection failed).",
10473: "Abnormal access heartbeat (connection timed out).",
10474: "Network exception (connection timed out).",
10475: "Server changes.",
10476: "Abnormal connection to AP.",
10500: "Access failed (an unknown exception occurred in the system).",
},
3: {
10200: "Subscription succeeded.",
10300: "Subscription failed.",
},
4: {
10200: "Transparent data sent successfully.",
10210: "Object model data sent successfully.",
10220: "Positioning data sent successfully.",
10300: "Failed to send transparent data.",
10310: "Failed to send object model data.",
10320: "Failed to send positioning data.",
},
5: {
10200: "Receive transparent data.",
10210: "Receive data from the object model.",
10211: "Received object model query command.",
10473: "Received data but the length exceeds the module buffer limit, receive failed.",
10428: "The device receives too much buffer and causes current limit.",
},
6: {
10200: "Logout succeeded (disconnection succeeded).",
},
7: {
10700: "New OTA plain.",
10701: "The module starts to download.",
10702: "Package download.",
10703: "Package download complete.",
10704: "Package update.",
10705: "Firmware update complete.",
10706: "Failed to update firmware.",
10707: "Received confirmation broadcast.",
},
8: {
10428: "High-frequency messages on the device cause current throttling.",
10429: "Exceeds the number of activations per device or daily requests current limit.",
}
}
2022-04-13 11:55:36 +08:00
class QuecObjectModel(CloudObjectModel):
def __init__(self):
2022-04-13 11:55:36 +08:00
super().__init__()
self.items_id = {}
2022-04-13 11:55:36 +08:00
def __init_items_id(self, om_key, om_key_id):
self.items_id[om_key_id] = om_key
return True
2022-04-13 11:55:36 +08:00
def __del_items_id(self, om_type, om_key):
if self.items.get(om_type) is not None:
if self.items[om_type].get(om_key):
2022-04-13 18:24:49 +08:00
om_key_id = self.items[om_type][om_key]["id"]
2022-04-13 11:55:36 +08:00
self.items_id.pop(om_key_id)
return True
def set_item(self, om_type, om_key, om_key_id, om_key_perm):
if super().set_item(om_type, om_key, om_key_id=om_key_id, om_key_perm=om_key_perm):
self.__init_items_id(om_key, om_key_id)
return True
return False
2022-04-13 11:55:36 +08:00
def del_item(self, om_type, om_key):
self.__del_items_id(om_type, om_key)
return super().del_item(om_type, om_key)
2022-04-12 09:13:20 +08:00
class QuecThing(CloudObservable):
def __init__(self, pk, ps, dk, ds, server, life_time=120, mcu_name="", mcu_version=""):
super().__init__()
self.__pk = pk
self.__ps = ps
self.__dk = dk
self.__ds = ds
self.__server = server
self.__life_time = life_time
self.__mcu_name = mcu_name
self.__mcu_version = mcu_version
2022-04-13 11:55:36 +08:00
self.__object_model = None
self.__file_size = 0
self.__md5_value = ""
self.__post_result_wait_queue = Queue(maxsize=16)
self.__quec_timer = osTimer()
2022-04-12 09:13:20 +08:00
def __rm_empty_data(self, data):
for k, v in data.items():
if not v:
del data[k]
def __quec_timer_cb(self, args):
self.__put_post_res(False)
def __get_post_res(self):
2022-04-13 11:55:36 +08:00
self.__quec_timer.start(1000 * 10, 0, self.__quec_timer_cb)
res = self.__post_result_wait_queue.get()
self.__quec_timer.stop()
2022-04-12 09:13:20 +08:00
return res
def __put_post_res(self, res):
if self.__post_result_wait_queue.size() >= 16:
self.__post_result_wait_queue.get()
self.__post_result_wait_queue.put(res)
2022-04-12 09:13:20 +08:00
def __sota_download_info(self, size, md5_value):
self.__file_size = size
self.__md5_value = md5_value
2022-04-12 09:13:20 +08:00
def __sota_upgrade_start(self, start_addr, need_download_size):
download_size = 0
sota_mode = SOTA()
while need_download_size != 0:
readsize = 4096
if (readsize > need_download_size):
readsize = need_download_size
updateFile = quecIot.mcuFWDataRead(start_addr, readsize)
sota_mode.write_update_data(updateFile)
log.debug("Download File Size: %s" % readsize)
need_download_size -= readsize
start_addr += readsize
download_size += readsize
if (download_size == self.__file_size):
2022-04-12 09:13:20 +08:00
log.debug("File Download Success, Update Start.")
self.ota_action(3)
if sota_mode.check_md5(self.__md5_value):
2022-04-12 09:13:20 +08:00
if sota_mode.file_update():
sota_mode.sota_set_flag()
log.debug("File Update Success, Power Restart.")
else:
log.debug("File Update Failed, Power Restart.")
break
else:
self.ota_action(2)
res_data = ("object_model", [("power_restart", 1)])
self.notifyObservers(self, *res_data)
2022-04-13 11:55:36 +08:00
def __data_format(self, k, v):
# log.debug("k: %s, v: %s" % (k, v))
k_id = None
struct_info = {}
2022-04-13 18:24:49 +08:00
if self.__object_model.items["event"].get(k):
k_id = self.__object_model.items["event"][k]["id"]
if isinstance(self.__object_model.items["event"][k]["struct_info"], dict):
struct_info = self.__object_model.items["event"][k]["struct_info"]
elif self.__object_model.items["property"].get(k):
k_id = self.__object_model.items["property"][k]["id"]
if isinstance(self.__object_model.items["property"][k]["struct_info"], dict):
struct_info = self.__object_model.items["property"][k]["struct_info"]
2022-04-13 11:55:36 +08:00
else:
return False
if isinstance(v, dict):
nv = {}
for ik, iv in v.items():
if struct_info.get(ik):
nv[struct_info[ik]["id"]] = iv
else:
nv[ik] = iv
v = nv
return {k_id: v}
2022-04-12 09:13:20 +08:00
def __event_cb(self, data):
res_data = ()
event = data[0]
errcode = data[1]
eventdata = b""
if len(data) > 2:
eventdata = data[2]
log.info("Event[%s] ErrCode[%s] Msg[%s] EventData[%s]" % (event, errcode, EVENT_CODE.get(event, {}).get(errcode, ""), eventdata))
if event == 3:
if errcode == 10200:
if eventdata:
file_info = eval(eventdata)
log.info("OTA File Info: componentNo: %s, sourceVersion: %s, targetVersion: %s, "
"batteryLimit: %s, minSignalIntensity: %s, minSignalIntensity: %s" % file_info)
elif event == 4:
if errcode == 10200:
self.__put_post_res(True)
elif errcode == 10210:
self.__put_post_res(True)
elif errcode == 10220:
self.__put_post_res(True)
elif errcode == 10300:
self.__put_post_res(False)
elif errcode == 10310:
self.__put_post_res(False)
elif errcode == 10320:
self.__put_post_res(False)
elif event == 5:
if errcode == 10200:
# TODO: Data Type Passthrough (Not Support Now).
res_data = ("raw_data", eventdata)
elif errcode == 10210:
dl_data = [(self.__object_model.items_id[k], v.decode() if isinstance(v, bytes) else v) for k, v in eventdata.items()]
2022-04-12 09:13:20 +08:00
res_data = ("object_model", dl_data)
elif errcode == 10211:
# eventdata[0] is pkgId.
object_model_ids = eventdata[1]
object_model_val = [self.__object_model.items_id[i] for i in object_model_ids if self.__object_model.items_id.get(i)]
2022-04-12 09:13:20 +08:00
res_data = ("query", object_model_val)
pass
elif event == 7:
if errcode == 10700:
if eventdata:
file_info = eval(eventdata)
log.info("OTA File Info: componentNo: %s, sourceVersion: %s, targetVersion: %s, "
"batteryLimit: %s, minSignalIntensity: %s, useSpace: %s" % file_info)
res_data = ("object_model", [("ota_status", (file_info[0], 1, file_info[2]))])
elif errcode == 10701:
res_data = ("object_model", [("ota_status", (None, 2, None))])
elif errcode == 10702:
res_data = ("object_model", [("ota_status", (None, 2, None))])
elif errcode == 10703:
res_data = ("object_model", [("ota_status", (None, 2, None))])
elif errcode == 10704:
res_data = ("object_model", [("ota_status", (None, 2, None))])
elif errcode == 10705:
res_data = ("object_model", [("ota_status", (None, 3, None))])
elif errcode == 10706:
res_data = ("object_model", [("ota_status", (None, 4, None))])
if res_data:
self.notifyObservers(self, *res_data)
if event == 7 and errcode == 10701 and eventdata:
file_info = eval(eventdata)
self.__sota_download_info(int(file_info[1]), file_info[2])
if event == 7 and errcode == 10703 and eventdata:
file_info = eval(eventdata)
log.info("OTA File Info: componentNo: %s, length: %s, md5: %s, crc: %s" % file_info)
self.__sota_upgrade_start(int(file_info[2]), int(file_info[3]))
2022-03-31 13:41:08 +08:00
def set_object_model(self, object_model):
2022-04-13 11:55:36 +08:00
if object_model and isinstance(object_model, QuecObjectModel):
self.__object_model = object_model
return True
return False
def init(self, enforce=False):
log.debug(
"[init start] enforce: %s QuecThing Work State: %s, quecIot.getConnmode(): %s"
% (enforce, quecIot.getWorkState(), quecIot.getConnmode())
)
2022-04-13 18:24:49 +08:00
log.debug("[init start] PK: %s, PS: %s, DK: %s, DS: %s, SERVER: %s" % (self.__pk, self.__ps, self.__dk, self.__ds, self.__server))
if enforce is False:
if quecIot.getWorkState() == 8 and quecIot.getConnmode() == 1:
return True
2022-03-16 11:38:50 +08:00
2022-03-17 13:25:59 +08:00
quecIot.init()
2022-04-12 09:13:20 +08:00
quecIot.setEventCB(self.__event_cb)
quecIot.setProductinfo(self.__pk, self.__ps)
if self.__dk or self.__ds:
quecIot.setDkDs(self.__dk, self.__ds)
quecIot.setServer(1, self.__server)
quecIot.setLifetime(self.__life_time)
quecIot.setMcuVersion(self.__mcu_name, self.__mcu_version)
2022-03-03 09:53:51 +08:00
quecIot.setConnmode(1)
2022-03-31 13:41:08 +08:00
count = 0
while quecIot.getWorkState() != 8 and count < 10:
utime.sleep_ms(200)
count += 1
2022-03-31 13:41:08 +08:00
if not self.__ds and self.__dk:
count = 0
while count < 3:
dkds = quecIot.getDkDs()
if dkds:
self.__dk, self.__ds = dkds
2022-04-12 09:13:20 +08:00
log.debug("dk: %s, ds: %s" % dkds)
break
count += 1
utime.sleep(count)
2022-03-03 09:53:51 +08:00
log.debug("[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 close(self):
2022-04-12 09:13:20 +08:00
return quecIot.setConnmode(0)
2022-03-23 14:21:45 +08:00
def post_data(self, data):
res = True
2022-04-12 09:13:20 +08:00
# log.debug("post_data: %s" % str(data))
2022-03-23 14:21:45 +08:00
for k, v in data.items():
2022-04-13 11:55:36 +08:00
om_data = self.__data_format(k, v)
if om_data is not False:
2022-03-28 20:29:01 +08:00
if v is not None:
phymodelReport_res = quecIot.phymodelReport(1, om_data)
2022-03-23 14:21:45 +08:00
if not phymodelReport_res:
res = False
break
else:
continue
2022-04-12 09:13:20 +08:00
elif k == "gps":
2022-03-23 14:21:45 +08:00
locReportOutside_res = quecIot.locReportOutside(v)
if not locReportOutside_res:
res = False
break
2022-04-12 09:13:20 +08:00
elif k == "non_gps":
2022-03-23 14:21:45 +08:00
locReportInside_res = quecIot.locReportInside(v)
if not locReportInside_res:
res = False
break
2022-03-04 17:09:04 +08:00
else:
2022-03-23 14:21:45 +08:00
v = {}
continue
2022-04-12 09:13:20 +08:00
res = self.__get_post_res()
2022-03-23 14:21:45 +08:00
if res:
v = {}
2022-03-04 17:09:04 +08:00
else:
2022-03-23 14:21:45 +08:00
res = False
break
2022-04-12 09:13:20 +08:00
self.__rm_empty_data(data)
2022-03-23 14:21:45 +08:00
return res
2022-03-03 09:53:51 +08:00
2022-04-12 09:13:20 +08:00
def ota_request(self, mp_mode=0):
return quecIot.otaRequest(mp_mode) if mp_mode in (0, 1) else False
2022-03-31 13:41:08 +08:00
2022-04-12 09:13:20 +08:00
def ota_action(self, action=1, module=None):
return quecIot.otaAction(action) if action in (0, 1, 2, 3) else False