structor optimization

This commit is contained in:
JackSun-qc 2022-04-18 13:55:36 +08:00
parent ab755f42d4
commit 4199e87391
24 changed files with 865 additions and 4531 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "code/modules"]
path = code/modules
url = https://gitee.com/qpy-solutions/modules.git

View File

@ -1,697 +0,0 @@
# 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.
import ujson
import utime
import _thread
import osTimer
from aLiYun import aLiYun
from usr.logging import getLogger
from usr.common import numiter, option_lock, CloudObservable, CloudObjectModel
log = getLogger(__name__)
_gps_read_lock = _thread.allocate_lock()
class AliObjectModel(CloudObjectModel):
"""This class is aliyun object model
This class extend CloudObjectModel.
Attribute:
items:
- object model dictionary
- data format:
{
"events": {
"name": "events",
"id": "",
"perm": "",
"struct_info": {
"name": "struct",
"id": "",
"struct_info": {
"key": {
"name": "key"
}
},
},
},
"properties": {
"name": "event",
"id": "",
"perm": "",
"struct_info": {}
}
}
"""
def __init__(self, om_file="/usr/aliyun_object_model.json"):
super().__init__(om_file)
self.init()
def init(self):
with open(self.om_file, "rb") as f:
cloud_object_model = ujson.load(f)
for om_type in cloud_object_model.keys():
if om_type not in ("events", "properties"):
continue
for om_item in cloud_object_model[om_type]:
om_key = om_item["identifier"]
om_key_id = ""
om_key_perm = ""
self.set_item(om_type, om_key, om_key_id, om_key_perm)
struct_info_list = []
if om_type == "properties":
if om_item["dataType"]["type"] == "struct":
struct_info_list = om_item["dataType"]["specs"]
elif om_type == "events":
if om_item.get("outputData"):
struct_info_list = om_item["outputData"]
for struct_info in struct_info_list:
struct_key = struct_info["identifier"]
struct_key_id = ""
struct_key_struct = {}
if struct_info["dataType"]["type"] == "struct":
for struct_key_struct_key in struct_info["dataType"]["specs"]:
struct_key_struct[struct_key_struct_key["identifier"]] = {
"name": struct_key_struct_key["identifier"]
}
self.set_item_struct(
om_type, om_key, struct_key,
struct_key_id=struct_key_id,
struct_key_struct=struct_key_struct
)
class AliYunIot(CloudObservable):
"""This is a class for aliyun iot.
This class extend CloudObservable.
This class has the following functions:
1. Cloud connect and disconnect
2. Publish data to cloud
2.1 Publish object module
2.2 Publish ota device info, ota upgrade process, ota plain info request
2.3 Publish rrpc response
3. Subscribe data from cloud
3.1 Subscribe publish object model result
3.2 Subscribe cloud message
3.3 Subscribe ota plain
3.4 Subscribe rrpc request
Attribute:
ica_topic_property_post: topic for publish object model property
ica_topic_property_post_reply: topic for subscribe publish object model property result
ica_topic_property_set: topic for subscribe cloud object model property set
ica_topic_event_post: topic for publish object model event
ica_topic_event_post_reply: topic for subscribe publish object model event result
ota_topic_device_inform: topic for publish device information
ota_topic_device_upgrade: topic for subscribe ota plain
ota_topic_device_progress: topic for publish ota upgrade process
ota_topic_firmware_get: topic for publish ota plain request
ota_topic_firmware_get_reply: topic for subscribe ota plain request response
ota_topic_file_download: topic for publish ota mqtt file download request
ota_topic_file_download_reply: topic for publish ota mqtt file download request response
rrpc_topic_request: topic for subscribe rrpc message
rrpc_topic_response: topic for publish rrpc response
Run step:
1. cloud = AliYunIot(pk, ps, dk, ds, server, client_id)
2. cloud.addObserver(RemoteSubscribe)
3. cloud.set_object_model(AliObjectModel)
4. cloud.init()
5. cloud.post_data(data)
6. cloud.close()
"""
def __init__(self, pk, ps, dk, ds, server, client_id, burning_method=0, life_time=120,
mcu_name="", mcu_version="", firmware_name="", firmware_version=""):
"""
1. Init parent class CloudObservable
2. Init cloud connect params and topic
"""
super().__init__()
self.__pk = pk
self.__ps = ps
self.__dk = dk
self.__ds = ds
self.__server = server
self.__burning_method = burning_method
self.__life_time = life_time
self.__mcu_name = mcu_name
self.__mcu_version = mcu_version
self.__firmware_name = firmware_name
self.__firmware_version = firmware_version
self.__object_model = None
self.__client_id = client_id
self.__ali = None
self.__post_res = {}
self.__breack_flag = 0
self.__ali_timer = osTimer()
self.__id_iter = numiter()
self.__id_lock = _thread.allocate_lock()
self.ica_topic_property_post = "/sys/%s/%s/thing/event/property/post" % (self.__pk, self.__dk)
self.ica_topic_property_post_reply = "/sys/%s/%s/thing/event/property/post_reply" % (self.__pk, self.__dk)
self.ica_topic_property_set = "/sys/%s/%s/thing/service/property/set" % (self.__pk, self.__dk)
self.ica_topic_event_post = "/sys/%s/%s/thing/event/{}/post" % (self.__pk, self.__dk)
self.ica_topic_event_post_reply = "/sys/%s/%s/thing/event/{}/post_reply" % (self.__pk, self.__dk)
self.ota_topic_device_inform = "/ota/device/inform/%s/%s" % (self.__pk, self.__dk)
self.ota_topic_device_upgrade = "/ota/device/upgrade/%s/%s" % (self.__pk, self.__dk)
self.ota_topic_device_progress = "/ota/device/progress/%s/%s" % (self.__pk, self.__dk)
self.ota_topic_firmware_get = "/sys/%s/%s/thing/ota/firmware/get" % (self.__pk, self.__dk)
self.ota_topic_firmware_get_reply = "/sys/%s/%s/thing/ota/firmware/get_reply" % (self.__pk, self.__dk)
# TODO: To Download OTA File For MQTT Association (Not Support Now.)
self.ota_topic_file_download = "/sys/%s/%s/thing/file/download" % (self.__pk, self.__dk)
self.ota_topic_file_download_reply = "/sys/%s/%s/thing/file/download_reply" % (self.__pk, self.__dk)
self.rrpc_topic_request = "/sys/%s/%s/rrpc/request/+" % (self.__pk, self.__dk)
self.rrpc_topic_response = "/sys/%s/%s/rrpc/response/{}" % (self.__pk, self.__dk)
def __get_id(self):
"""Get message id for publishing data"""
with self.__id_lock:
try:
msg_id = next(self.__id_iter)
except StopIteration:
self.__id_iter = numiter()
msg_id = next(self.__id_iter)
return str(msg_id)
def __put_post_res(self, msg_id, res):
"""Save publish result by message id
Parameter:
msg_id: publish message id
res: publish result, True or False
"""
self.__post_res[msg_id] = res
def __ali_timer_cb(self, args):
"""osTimer callback to break cycling of get publish result"""
self.__breack_flag = 1
@option_lock(_gps_read_lock)
def __get_post_res(self, msg_id):
"""Get publish result by message id
Parameter:
msg_id: publish message id
Return:
True: publish success
False: publish failed
"""
self.__ali_timer.start(1000 * 10, 0, self.__ali_timer_cb)
while self.__post_res.get(msg_id) is None:
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)
return res
def __ali_subscribe_topic(self):
"""Subscribe aliyun topic"""
if self.__ali.subscribe(self.ica_topic_property_post, qos=0) == -1:
log.error("Topic [%s] Subscribe Falied." % self.ica_topic_property_post)
if self.__ali.subscribe(self.ica_topic_property_post_reply, qos=0) == -1:
log.error("Topic [%s] Subscribe Falied." % self.ica_topic_property_post_reply)
if self.__ali.subscribe(self.ica_topic_property_set, qos=0) == -1:
log.error("Topic [%s] Subscribe Falied." % self.ica_topic_property_set)
for tsl_event_identifier in self.__object_model.items["events"].keys():
post_topic = self.ica_topic_event_post.format(tsl_event_identifier)
if self.__ali.subscribe(post_topic, qos=0) == -1:
log.error("Topic [%s] Subscribe Falied." % post_topic)
post_reply_topic = self.ica_topic_event_post_reply.format(tsl_event_identifier)
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)
if self.__ali.subscribe(self.rrpc_topic_request, qos=0) == -1:
log.error("Topic [%s] Subscribe Falied." % self.rrpc_topic_request)
def __ali_sub_cb(self, topic, data):
"""Aliyun subscribe topic callback
Parameter:
topic: topic info
data: response dictionary info
"""
topic = topic.decode()
data = ujson.loads(data)
log.info("topic: %s, data: %s" % (topic, data))
if topic.endswith("/post_reply"):
self.__put_post_res(data["id"], True if data["code"] == 200 else False)
elif topic.endswith("/property/set"):
if data["method"] == "thing.service.property.set":
dl_data = list(zip(data.get("params", {}).keys(), data.get("params", {}).values()))
self.notifyObservers(self, *("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.notifyObservers(self, *("object_model", [("ota_status", (data["data"]["module"], 1, data["data"]["version"]))]))
self.notifyObservers(self, *("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.notifyObservers(self, *("object_model", [("ota_status", (data["data"]["module"], 1, data["data"]["version"]))]))
self.notifyObservers(self, *("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.notifyObservers(self, *("ota_file_download", data["data"]))
elif topic.find("/rrpc/request/") != -1:
self.notifyObservers(self, *("rrpc_request", topic, data))
else:
pass
def __data_format(self, data):
"""Publish data format by AliObjectModel
Parameter:
data format:
{
"phone_num": "123456789",
"energy": 100,
"GeoLocation": {
"Longtitude": 100.26,
"Latitude": 26.86,
"Altitude": 0.0,
"CoordinateSystem": 1
},
}
Return:
{
"event": [
{
"id": 1,
"version": "1.0",
"sys": {
"ack": 1
},
"params": {
"sos_alert": {
"value": {},
"time": 1649991780000
},
},
"method": "thing.event.sos_alert.post"
}
],
"property": [
{
"id": 2,
"version": "1.0",
"sys": {
"ack": 1
},
"params": {
"phone_num": {
"value": "123456789",
"time": 1649991780000
},
"energy": {
"value": 100,
"time": 1649991780000
},
},
"method": "thing.event.property.post"
}
],
"msg_ids": [1, 2],
"event_topic": {
1: "/sys/{product_key}/{device_key}/thing/event/{event}/post",
2: "/sys/{product_key}/{device_key}/thing/event/property/post",
}
}
"""
res = {"event": [], "property": [], "msg_ids": [], "event_topic": {}}
property_params = {}
event_params = {}
# Format Publish Params.
for k, v in data.items():
if k in self.__object_model.items["properties"].keys():
property_params[k] = {
"value": v,
"time": utime.mktime(utime.localtime()) * 1000
}
elif k in self.__object_model.items["events"].keys():
event_params[k] = {
"value": {},
"time": utime.mktime(utime.localtime()) * 1000
}
else:
log.error("Publish Key [%s] is not in property and event" % k)
if property_params:
msg_id = self.__get_id()
publish_data = {
"id": msg_id,
"version": "1.0",
"sys": {
"ack": 1
},
"params": property_params,
"method": "thing.event.property.post"
}
res["property"].append(publish_data)
res["msg_ids"].append(msg_id)
if event_params:
for event in event_params.keys():
topic = self.ica_topic_event_post.format(event)
msg_id = self.__get_id()
publish_data = {
"id": msg_id,
"version": "1.0",
"sys": {
"ack": 1
},
"params": event_params[event],
"method": "thing.event.%s.post" % event
}
res["event"].append(publish_data)
res["event_topic"][msg_id] = topic
res["msg_ids"].append(msg_id)
return res
def set_object_model(self, object_model):
"""Register AliObjectModel to this class"""
if object_model and isinstance(object_model, AliObjectModel):
self.__object_model = object_model
return True
return False
def init(self, enforce=False):
"""Aliyun connect and subscribe topic
Parameter:
enforce:
True: enfore cloud connect and subscribe topic
False: check connect status, return True if cloud connected
Return:
Ture: Success
False: Failed
"""
log.debug("[init start] enforce: %s" % enforce)
if enforce is False and self.__ali is not None:
log.debug("self.__ali.getAliyunSta(): %s" % self.__ali.getAliyunSta())
if self.__ali.getAliyunSta() == 0:
return True
if self.__burning_method == 0:
self.__dk = None
elif self.__burning_method == 1:
self.__ps = None
log.debug("aLiYun init. self.__pk: %s, self.__ps: %s, self.__dk: %s, self.__ds: %s, self.__server: %s" % (self.__pk, self.__ps, self.__dk, self.__ds, self.__server))
self.__ali = aLiYun(self.__pk, self.__ps, self.__dk, self.__ds, self.__server)
log.debug("aLiYun setMqtt.")
setMqttres = self.__ali.setMqtt(self.__client_id, clean_session=False, keepAlive=self.__life_time, reconn=True)
log.debug("aLiYun setMqttres: %s" % setMqttres)
if setMqttres != -1:
self.__ali.setCallback(self.__ali_sub_cb)
self.__ali_subscribe_topic()
self.__ali.start()
else:
log.error("setMqtt Falied!")
return False
log.debug("self.__ali.getAliyunSta(): %s" % self.__ali.getAliyunSta())
if self.__ali.getAliyunSta() == 0:
return True
else:
return False
def close(self):
"""Aliyun disconnect"""
self.__ali.disconnect()
return True
def post_data(self, data):
"""Publish object model property, event
Parameter:
data format:
{
"phone_num": "123456789",
"energy": 100,
"GeoLocation": {
"Longtitude": 100.26,
"Latitude": 26.86,
"Altitude": 0.0,
"CoordinateSystem": 1
},
}
Return:
Ture: Success
False: Failed
"""
if self.__ali.getAliyunSta() == 0:
try:
publish_data = self.__data_format(data)
# Publish Property Data.
for item in publish_data["property"]:
self.__ali.publish(self.ica_topic_property_post, ujson.dumps(item), qos=0)
# Publish Event Data.
for item in publish_data["event"]:
self.__ali.publish(publish_data["event_topic"][item["id"]], ujson.dumps(item), qos=0)
pub_res = [self.__get_post_res(msg_id) for msg_id in publish_data["msg_ids"]]
return True if False not in pub_res else False
except Exception:
log.error("AliYun publish topic %s failed. data: %s" % (data.get("topic"), data.get("data")))
return False
def rrpc_response(self, message_id, data):
"""Publish rrpc response
Parameter:
message_id: rrpc request messasge id
data: response message
Return:
Ture: Success
False: Failed
"""
topic = self.rrpc_topic_response.format(message_id)
pub_data = ujson.dumps(data) if isinstance(data, dict) else data
self.__ali.publish(topic, pub_data, qos=0)
return True
def device_report(self):
"""Publish mcu and firmware name, version
Return:
Ture: Success
False: Failed
"""
muc_res = self.ota_device_inform(self.__mcu_version, module=self.__mcu_name)
fw_res = self.ota_device_inform(self.__firmware_version, module=self.__firmware_name)
return True if muc_res and fw_res else False
def ota_request(self):
"""Publish mcu and firmware ota plain request
Return:
Ture: Success
False: Failed
"""
sota_res = self.ota_firmware_get(self.__mcu_name)
fota_res = self.ota_firmware_get(self.__firmware_name)
return True if sota_res and fota_res else False
def ota_action(self, action, module=None):
"""Publish ota upgrade start or cancel ota upgrade
Parameter:
action: confirm or cancel upgrade
- 0: cancel upgrade
- 1: confirm upgrade
module: mcu or firmare model name
- e.g.: `QuecPython-Tracker`, `EC600N-CNLC`
Return:
Ture: Success
False: Failed
"""
if not module:
log.error("Params[module] Is Empty.")
return False
if action not in (0, 1):
log.error("Params[action] Should Be 0 Or 1, Not %s." % action)
return False
if action == 1:
return self.ota_device_progress(step=1, module=module)
else:
return self.ota_device_progress(step=-1, desc="User cancels upgrade.", module=module)
def ota_device_inform(self, version, module="default"):
"""Publish device information
Parameter:
version: module version
- e.g.: `2.1.0`
module: mcu or firmare model name
- e.g.: `QuecPython-Tracker`
Return:
Ture: Success
False: Failed
"""
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"):
"""Publish ota upgrade process
Parameter:
step: upgrade process
- 1 ~ 100: Upgrade progress percentage
- -1: Upgrade failed
- -2: Download failed
- -3: Verification failed
- -4: Programming failed
desc: Description of the current step, no more than 128 characters long. If an exception occurs, this field can carry error information.
module: mcu or firmare model name
- e.g.: `QuecPython-Tracker`
Return:
Ture: Success
False: Failed
"""
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):
"""Publish ota plain info request
Parameter:
module: mcu or firmare model name
- e.g.: `QuecPython-Tracker`
Return:
Ture: Success
False: Failed
"""
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):
"""Publish mqtt ota plain file info request
Parameter:
params: file download info
params format:
{
"fileToken": "1bb8***",
"fileInfo": {
"streamId": 1234565,
"fileId": 1
},
"fileBlock": {
"size": 256,
"offset": 2
}
}
Return:
Ture: Success
False: Failed
"""
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

View File

@ -1,94 +0,0 @@
# 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.
from misc import Power
from usr.logging import getLogger
log = getLogger(__name__)
BATTERY_OCV_TABLE = {
"nix_coy_mnzo2": {
55: {
4152: 100, 4083: 95, 4023: 90, 3967: 85, 3915: 80, 3864: 75, 3816: 70, 3773: 65, 3737: 60, 3685: 55,
3656: 50, 3638: 45, 3625: 40, 3612: 35, 3596: 30, 3564: 25, 3534: 20, 3492: 15, 3457: 10, 3410: 5, 3380: 0,
},
20: {
4143: 100, 4079: 95, 4023: 90, 3972: 85, 3923: 80, 3876: 75, 3831: 70, 3790: 65, 3754: 60, 3720: 55,
3680: 50, 3652: 45, 3634: 40, 3621: 35, 3608: 30, 3595: 25, 3579: 20, 3548: 15, 3511: 10, 3468: 5, 3430: 0,
},
0: {
4147: 100, 4089: 95, 4038: 90, 3990: 85, 3944: 80, 3899: 75, 3853: 70, 3811: 65, 3774: 60, 3741: 55,
3708: 50, 3675: 45, 3651: 40, 3633: 35, 3620: 30, 3608: 25, 3597: 20, 3585: 15, 3571: 10, 3550: 5, 3500: 0,
},
},
}
class Battery(object):
"""This class is for battery info.
This class can get battery voltage and energy.
"""
def __init__(self):
self.__energy = 100
self.__temp = 20
def __get_soc_from_dict(self, key, volt_arg):
"""Get battery energy from map"""
if key in BATTERY_OCV_TABLE["nix_coy_mnzo2"]:
volts = sorted(BATTERY_OCV_TABLE["nix_coy_mnzo2"][key].keys(), reverse=True)
pre_volt = 0
volt_not_under = 0 # Determine whether the voltage is lower than the minimum voltage value of soc.
for volt in volts:
if volt_arg > volt:
volt_not_under = 1
soc1 = BATTERY_OCV_TABLE["nix_coy_mnzo2"][key].get(volt, 0)
soc2 = BATTERY_OCV_TABLE["nix_coy_mnzo2"][key].get(pre_volt, 0)
break
else:
pre_volt = volt
if pre_volt == 0: # Input Voltarg > Highest Voltarg
return soc1
elif volt_not_under == 0:
return 0
else:
return soc2 - (soc2 - soc1) * (pre_volt - volt_arg) // (pre_volt - volt)
def __get_soc(self, temp, volt_arg, bat_type="nix_coy_mnzo2"):
"""Get battery energy by temperature and voltage"""
if bat_type == "nix_coy_mnzo2":
if temp > 30:
return self.__get_soc_from_dict(55, volt_arg)
elif temp < 10:
return self.__get_soc_from_dict(0, volt_arg)
else:
return self.__get_soc_from_dict(20, volt_arg)
def set_temp(self, temp):
"""Set now temperature."""
if isinstance(temp, int) or isinstance(temp, float):
self.__temp = temp
return True
return False
def get_voltage(self):
"""Get battery voltage"""
# Get voltage from vbat
# TODO: Get voltage by ADC
return int(sum([Power.getVbatt() for i in range(100)]) / 100)
def get_energy(self):
"""Get battery energy"""
self.__energy = self.__get_soc(self.__temp, self.get_voltage())
return self.__energy

View File

@ -1,276 +0,0 @@
# 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.
import _thread
LOWENERGYMAP = {
"EC200U": [
"POWERDOWN",
"PM",
],
"EC200U": [
"POWERDOWN",
"PM",
],
"EC600N": [
"PM",
],
"EC800G": [
"PM"
],
}
def numiter(num=99999):
"""Number generation iterator"""
for i in range(num):
yield i
def option_lock(thread_lock):
"""Function thread lock decorator"""
def function_lock(func):
def wrapperd_fun(*args, **kwargs):
with thread_lock:
return func(*args, **kwargs)
return wrapperd_fun
return function_lock
class BaseError(Exception):
"""Exception base class"""
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
class Singleton(object):
"""Singleton base class"""
_instance_lock = _thread.allocate_lock()
def __init__(self, *args, **kwargs):
pass
def __new__(cls, *args, **kwargs):
if not hasattr(cls, "instance_dict"):
Singleton.instance_dict = {}
if str(cls) not in Singleton.instance_dict.keys():
with Singleton._instance_lock:
_instance = super().__new__(cls)
Singleton.instance_dict[str(cls)] = _instance
return Singleton.instance_dict[str(cls)]
class Observer(object):
"""Observer base class"""
def update(self, observable, *args, **kwargs):
pass
class Observable(Singleton):
"""Observable base class"""
def __init__(self):
self.__observers = []
def addObserver(self, observer):
"""Add observer"""
try:
self.__observers.append(observer)
return True
except:
return False
def delObserver(self, observer):
"""Delete observer"""
try:
self.__observers.remove(observer)
return True
except:
return False
def notifyObservers(self, *args, **kwargs):
"""Notify observer"""
for o in self.__observers:
o.update(self, *args, **kwargs)
class CloudObserver(object):
"""Cloud observer base class"""
def execute(self, observable, *args, **kwargs):
pass
class CloudObservable(Singleton):
"""Cloud observable base class"""
def __init__(self):
self.__observers = []
def addObserver(self, observer):
"""Add observer"""
self.__observers.append(observer)
def delObserver(self, observer):
"""Delete observer"""
self.__observers.remove(observer)
def notifyObservers(self, *args, **kwargs):
"""Notify observer"""
for o in self.__observers:
o.execute(self, *args, **kwargs)
def init(self, enforce=False):
"""Cloud init"""
pass
def close(self):
"""Cloud disconnect"""
pass
def post_data(self, data):
"""Cloud publish data"""
pass
def ota_request(self, *args, **kwargs):
"""Cloud publish ota plain request"""
pass
def ota_action(self, action, module=None):
"""Cloud publish ota upgrade or not request"""
pass
class CloudObjectModel(Singleton):
"""This is a cloud object model base class
Attribute:
items: object model dictionary, default two keys
events: object model events
property: object model property
items data format:
{
"events": {
"name": "events",
"id": "",
"perm": "rw",
"struct_info": {
"name": "struct",
"id": "",
"struct_info": {
"key": {
"name": "key"
}
},
},
},
"property": {
"name": "event",
"id": "",
"perm": "rw",
"struct_info": {}
}
}
"""
def __init__(self, om_file):
self.items = {
"events": {},
"properties": {},
}
self.om_file = om_file
def init(self):
pass
def set_item(self, om_type, om_key, om_key_id=None, om_key_perm=None):
""" Set object model item
Parameter:
om_type: object model type
- e.g.: `events`, `properties`
om_key: object model code
- e.g.: `local_time`, `speed`, `GeoLocation`
om_key_id: object model id, not necessary, necessary for quecthing.
om_key_perm: object model permission, not necessary
- e.g.: `rw`, `w`, `r`
Return:
True: Success
False: Failed
"""
om_data = {
"name": om_key,
"id": om_key_id,
"perm": om_key_perm,
"struct_info": {}
}
if self.items.get(om_type) is not None:
self.items[om_type][om_key] = om_data
return True
return False
def del_item(self, om_type, om_key):
"""Delete object model item
Parameter:
om_type: object model type
om_key: object model code
Return:
True: Success
False: Failed
"""
if self.items.get(om_type) is not None:
if self.items[om_type].get(om_key) is not None:
self.items[om_type].pop(om_key)
return True
return False
def set_item_struct(self, om_type, om_key, struct_key, struct_key_id=None, struct_key_struct=None):
"""Set object model item struct
Parameter:
om_type: object model type
om_key: object model code
struct_key: object model item struct key name
struct_key_id: object model item struct key id, not necessary
struct_key_struct: object model item struct key struct, not necessary
Return:
True: Success
False: Failed
"""
if self.items.get(om_type) is not None:
if self.items[om_type].get(om_key) is not None:
if self.items[om_type][om_key].get("struct_info") is None:
self.items[om_type][om_key]["struct_info"] = {}
self.items[om_type][om_key]["struct_info"][struct_key] = {
"name": struct_key,
"id": struct_key_id,
"struct_info": struct_key_struct,
}
return True
return False

View File

@ -1,146 +0,0 @@
# 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.
import uos
import ql_fs
import ujson
import _thread
from usr.logging import getLogger
from usr.common import Singleton, option_lock
log = getLogger(__name__)
_history_lock = _thread.allocate_lock()
class History(Singleton):
"""This class is for manage history file."""
def __init__(self, history_file="/usr/tracker_data.hist", max_hist_num=100):
"""
Parameter:
history_file: filename include full path
max_hist_num: history data list max size
"""
self.__history = history_file
self.__max_hist_num = max_hist_num
def __read_hist(self):
"""Read history file info
Return:
data format:
{
"data": [
{
"xxx": "wwww"
}
]
}
"""
res = {"data": []}
if ql_fs.path_exists(self.__history):
with open(self.__history, "r") as f:
try:
hist_data = ujson.load(f)
if isinstance(hist_data, dict):
res["data"] = hist_data.get("data", [])
except Exception:
pass
return res
def __write_hist(self, data):
"""Write data to history file
Parameter:
data format:
{
"data": [
{
"xxx": "wwww"
}
]
}
Return:
True: Success
False: Falied
"""
try:
with open(self.__history, "w") as f:
ujson.dump(data, f)
return True
except:
return False
@option_lock(_history_lock)
def read(self):
"""Read history info
Return:
data format:
{
"data": [
{
"switch": True,
"energy": 100,
"gps": ["$GPRMCx,x,x,x", "$GPGGAx,x,x,x"],
},
{
"switch": True,
"energy": 100,
"non_gps": ["LBS"],
},
],
}
"""
res = self.__read_hist()
self.__write_hist({"data": []})
return res
@option_lock(_history_lock)
def write(self, data):
"""
Data Format For Write History:
[
{
"switch": True,
"energy": 100,
"gps": ["$GPRMCx,x,x,x", "$GPGGAx,x,x,x"],
},
{
"switch": True,
"energy": 100,
"non_gps": ["LBS"],
},
]
"""
res = self.__read_hist()
res["data"].extend(data)
if len(res["data"]) > self.__max_hist_num:
res["data"] = res["data"][self.__max_hist_num * -1:]
return self.__write_hist(res)
@option_lock(_history_lock)
def clean(self):
try:
uos.remove(self.__history)
return True
except:
return False
def update(self, observable, *args, **kwargs):
return self.write(list(args[1:]))

View File

@ -1,72 +0,0 @@
# 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.
import _thread
import osTimer
from machine import Pin
from usr.logging import getLogger
log = getLogger(__name__)
class LED(object):
def __init__(self, GPIOn, direction=Pin.OUT, pullMode=Pin.PULL_DISABLE, level=0):
self.__gpio = Pin(GPIOn, direction, pullMode, level)
self.__period = 0
self.__led_timer = osTimer()
self.__led_lock = _thread.allocate_lock()
def __led_timer_cb(self, args):
self.switch()
def get_period(self):
return self.__period
def set_period(self, period):
if isinstance(period, int) and period >= 0:
self.__period = period
return True
return False
def led_timer_start(self):
# __period is 0, not start led timer and stop led timer.
if self.__period > 0:
self.led_timer_stop()
if self.led_timer.start(self.__period, 1, self.__led_timer_cb) == 0:
return True
return False
def led_timer_stop(self):
return True if self.__led_timer.stop() == 0 else False
def get_led_status(self):
# TODO: Get LED Status From Pin
# Return:
# 1 LED ON (high level).
# 0 LED OFF (low level).
with self.__led_lock:
return self.__gpio.read()
def set_led_status(self, onoff):
# TODO: Set LED Status
with self.__led_lock:
return True if self.__gpio.write(onoff) == 0 else False
def switch(self):
# Auto Check LED Status ON To OFF or OFF To ON.
if self.get_led_status() == 1:
self.set_led_status(0)
else:
self.set_led_status(1)

View File

@ -1,618 +0,0 @@
# 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.
import ure
import osTimer
import _thread
import cellLocator
from queue import Queue
from machine import UART
from wifilocator import wifilocator
from usr.logging import getLogger
from usr.common import Singleton
from usr.common import option_lock
try:
import quecgnss
except ImportError:
quecgnss = None
log = getLogger(__name__)
_gps_read_lock = _thread.allocate_lock()
class _loc_method(object):
gps = 0x1
cell = 0x2
wifi = 0x4
class _gps_mode(object):
none = 0x0
internal = 0x1
external = 0x2
class GPSMatch(object):
"""This class is match gps NEMA 0183"""
def GxRMC(self, gps_data):
"""Match Recommended Minimum Specific GPS/TRANSIT DataRMC"""
if gps_data:
rmc_re = ure.search(
r"\$G[NP]RMC,\d+\.\d+,[AV],\d+\.\d+,[NS],\d+\.\d+,[EW],\d+\.\d+,\d+\.\d+,\d+,\d*\.*\d*,[EW]*,[ADEN]*,[SCUV]*\**(\d|\w)*",
gps_data)
if rmc_re:
return rmc_re.group(0)
return ""
def GxGGA(self, gps_data):
"""Match Global Positioning System Fix DataGGA"""
if gps_data:
gga_re = ure.search(
r"\$G[BLPN]GGA,\d+\.\d+,\d+\.\d+,[NS],\d+\.\d+,[EW],[0126],\d+,\d+\.\d+,-*\d+\.\d+,M,-*\d+\.\d+,M,\d*,\**(\d|\w)*",
gps_data)
if gga_re:
return gga_re.group(0)
return ""
def GxVTG(self, gps_data):
"""Match Track Made Good and Ground SpeedVTG"""
if gps_data:
vtg_re = ure.search(r"\$G[NP]VTG,\d+\.\d+,T,\d*\.*\d*,M,\d+\.\d+,N,\d+\.\d+,K,[ADEN]*\*(\d|\w)*", gps_data)
if vtg_re:
return vtg_re.group(0)
return ""
def GxGSV(self, gps_data):
"""Mactch GPS Satellites in ViewGSV"""
if gps_data:
gsv_re = ure.search(r"\$G[NP]GSV,\d+,\d+,\d+,\d*,\d*,\d*,\d*,\d*,\d*,\d*,\d*,\d*,\d*,\d*,\d*,\d*,\d*,\d*,\d*,\d*\**(\d|\w)*", gps_data)
if gsv_re:
return gsv_re.group(0)
return ""
class GPSParse(object):
"""Parse details from gps data"""
def GxGGA_satellite_num(self, gga_data):
"""Parse satellite num from GGA"""
if gga_data:
satellite_num_re = ure.search(r",[EW],[0126],\d+,", gga_data)
if satellite_num_re:
return satellite_num_re.group(0).split(",")[-2]
return ""
def GxVTG_speed(self, vtg_data):
"""Parse speed from VTG"""
if vtg_data:
speed_re = ure.search(r",N,\d+\.\d+,K,", vtg_data)
if speed_re:
return speed_re.group(0)[3:-3]
return ""
def GxGSV_satellite_num(self, gsv_data):
"""Parse satellite num from GSV"""
if gsv_data:
satellite_num_re = ure.search(r"\$G[NP]GSV,\d+,\d+,\d+,", gsv_data)
if satellite_num_re:
return satellite_num_re.group(0).split(",")[-2]
return ""
def GxGGA_latitude(self, gga_data):
"""Parse latitude from GGA"""
if gga_data:
latitude_re = ure.search(r",[0-9]+\.[0-9]+,[NS],", gga_data)
if latitude_re:
return latitude_re.group(0)[1:-3]
return ""
def GxGGA_longtitude(self, gga_data):
"""Parse longtitude from GGA"""
if gga_data:
longtitude_re = ure.search(r",[0-9]+\.[0-9]+,[EW],", gga_data)
if longtitude_re:
return longtitude_re.group(0)[1:-3]
return ""
def GxGGA_altitude(self, gga_data):
"""Parse altitude from GGA"""
if gga_data:
altitude_re = ure.search(r",-*[0-9]+\.[0-9]+,M,", gga_data)
if altitude_re:
return altitude_re.group(0)[1:-3]
return ""
class GPS(Singleton):
"""This class if for reading gps data.
Now support external gps and internal gps.
"""
def __init__(self, gps_cfg, gps_mode):
""" Init gps params
Parameter:
gps_cfg: this is uart init params for external gps
gps_mode: `internal` or `external`
"""
self.__gps_cfg = gps_cfg
self.__gps_mode = gps_mode
self.__external_obj = None
self.__internal_obj = quecgnss
self.__gps_match = GPSMatch()
self.__gps_parse = GPSParse()
self.__external_retrieve_queue = None
self.__first_break = 0
self.__break = 0
self.__gps_data = ""
self.__rmc_data = ""
self.__gga_data = ""
self.__vtg_data = ""
self.__gsv_data = ""
self.__gps_timer = osTimer()
self.__gps_clean_timer = osTimer()
if self.__gps_mode & _gps_mode.external:
self.__external_init()
elif self.__gps_mode & _gps_mode.internal:
self.__internal_init()
def __gps_timer_callback(self, args):
"""GPS read timer callback
When over time to get uart data, break queue wait
"""
self.__break = 1
if self.__external_retrieve_queue is not None:
self.__external_retrieve_queue.put(0)
def __gps_clean_callback(self, args):
"""GPS read old data clean timer callback
When GPS read over time, clean old gps data, wait to read new gps data.
"""
if self.__break == 0:
self.__gps_data = ""
self.__rmc_data = ""
self.__gga_data = ""
self.__vtg_data = ""
self.__gsv_data = ""
def __external_init(self):
"""External GPS init"""
self.__external_retrieve_queue = Queue(maxsize=8)
self.__external_open()
def __external_open(self):
"""External GPS start, UART init"""
self.__external_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.__external_obj.set_callback(self.__external_retrieve_cb)
def __external_close(self):
"""External GPS close, UART close, NOT GPS stop"""
self.__external_obj.close()
def __external_retrieve_cb(self, args):
"""
GPS data retrieve callback from UART
When data comes, send a message to queue of data length
"""
toRead = args[2]
log.debug("GPS __external_retrieve_cb args: %s" % str(args))
if toRead:
if self.__external_retrieve_queue.size() >= 8:
self.__external_retrieve_queue.get()
self.__external_retrieve_queue.put(toRead)
def __internal_init(self):
"""Internal GPS init"""
if self.__internal_obj:
if self.__internal_obj.init() != 0:
self.__insternal_open()
log.error("GNSS INIT Failed.")
else:
log.debug("GNSS INIT Success.")
else:
log.error("Module quecgnss Import Error.")
def __insternal_open(self):
"""Internal GPS enable"""
if self.__internal_obj.get_state() == 0:
self.__internal_obj.gnssEnable(1)
def __internal_close(self):
"""Internal GPS close"""
self.__internal_obj.gnssEnable(0)
@option_lock(_gps_read_lock)
def __external_read(self):
"""Read external GPS data
Return:
$GPTXT,01,01,02,ANTSTATUS=OPEN*2B
$GNRMC,073144.000,A,3149.330773,N,11706.946971,E,0.00,337.47,150422,,,D,V*07
$GNVTG,337.47,T,,M,0.00,N,0.00,K,D*22
$GNGGA,073144.000,3149.330773,N,11706.946971,E,2,19,0.66,85.161,M,-0.335,M,,*56
$GNGSA,A,3,01,195,06,03,21,194,19,17,30,14,,,0.94,0.66,0.66,1*02
$GNGSA,A,3,13,26,07,10,24,25,08,03,22,,,,0.94,0.66,0.66,4*03
$GPGSV,3,1,12,14,84,210,31,195,67,057,46,17,52,328,28,50,51,161,33,1*54
$GPGSV,3,2,12,194,49,157,33,03,48,090,37,19,36,305,32,06,34,242,32,1*58
$GPGSV,3,3,12,01,32,041,35,30,17,204,22,21,07,051,13,07,03,183,,1*6B
$BDGSV,5,1,18,07,86,063,30,10,75,322,30,08,60,211,34,03,52,192,33,1*71
$BDGSV,5,2,18,24,44,276,33,13,43,215,33,01,43,135,30,26,40,208,37,1*71
$BDGSV,5,3,18,02,38,230,,04,32,119,,22,26,135,30,19,25,076,,1*70
$BDGSV,5,4,18,05,17,251,,25,06,322,27,09,02,211,22,21,02,179,,1*78
$BDGSV,5,5,18,29,02,075,,20,01,035,,1*72
$GNGLL,3149.330773,N,11706.946971,E,073144.000,A,D*4E
"""
self.__external_open()
log.debug("__external_read start")
while self.__break == 0:
self.__gps_timer.start(50, 0, self.__gps_timer_callback)
nread = self.__external_retrieve_queue.get()
log.debug("[first] nread: %s" % nread)
self.__gps_data = self.__external_obj.read(nread).decode()
self.__break = 0
self.__gps_data = ""
self.__rmc_data = ""
self.__gga_data = ""
self.__vtg_data = ""
self.__gsv_data = ""
self.__gps_clean_timer.start(1050, 1, self.__gps_clean_callback)
while self.__break == 0:
self.__gps_timer.start(1500, 0, self.__gps_timer_callback)
nread = self.__external_retrieve_queue.get()
log.debug("[second] nread: %s" % nread)
if nread:
self.__gps_data += self.__external_obj.read(nread).decode()
if not self.__rmc_data:
self.__rmc_data = self.__gps_match.GxRMC(self.__gps_data)
if not self.__gga_data:
self.__gga_data = self.__gps_match.GxGGA(self.__gps_data)
if not self.__vtg_data:
self.__vtg_data = self.__gps_match.GxVTG(self.__gps_data)
if not self.__gsv_data:
self.__gsv_data = self.__gps_match.GxGSV(self.__gps_data)
if self.__rmc_data and self.__gga_data and self.__vtg_data and self.__gsv_data:
self.__break = 1
self.__gps_timer.stop()
self.__gps_clean_timer.stop()
self.__break = 0
self.__external_close()
log.debug("__external_read data: %s" % self.__gps_data)
return self.__gps_data
@option_lock(_gps_read_lock)
def __internal_read(self):
"""Read internal GPS data
Return:
$GPTXT,01,01,02,ANTSTATUS=OPEN*2B
$GNRMC,073144.000,A,3149.330773,N,11706.946971,E,0.00,337.47,150422,,,D,V*07
$GNVTG,337.47,T,,M,0.00,N,0.00,K,D*22
$GNGGA,073144.000,3149.330773,N,11706.946971,E,2,19,0.66,85.161,M,-0.335,M,,*56
$GNGSA,A,3,01,195,06,03,21,194,19,17,30,14,,,0.94,0.66,0.66,1*02
$GNGSA,A,3,13,26,07,10,24,25,08,03,22,,,,0.94,0.66,0.66,4*03
$GPGSV,3,1,12,14,84,210,31,195,67,057,46,17,52,328,28,50,51,161,33,1*54
$GPGSV,3,2,12,194,49,157,33,03,48,090,37,19,36,305,32,06,34,242,32,1*58
$GPGSV,3,3,12,01,32,041,35,30,17,204,22,21,07,051,13,07,03,183,,1*6B
$BDGSV,5,1,18,07,86,063,30,10,75,322,30,08,60,211,34,03,52,192,33,1*71
$BDGSV,5,2,18,24,44,276,33,13,43,215,33,01,43,135,30,26,40,208,37,1*71
$BDGSV,5,3,18,02,38,230,,04,32,119,,22,26,135,30,19,25,076,,1*70
$BDGSV,5,4,18,05,17,251,,25,06,322,27,09,02,211,22,21,02,179,,1*78
$BDGSV,5,5,18,29,02,075,,20,01,035,,1*72
$GNGLL,3149.330773,N,11706.946971,E,073144.000,A,D*4E
"""
self.__external_open()
while self.__break == 0:
self.__gps_timer.start(50, 0, self.__gps_timer_callback)
self.__gps_data = quecgnss.read(1024)
self.__gps_timer.stop()
self.__break = 0
self.__gps_data = ""
self.__rmc_data = ""
self.__gga_data = ""
self.__vtg_data = ""
self.__gsv_data = ""
self.__gps_clean_timer.start(1050, 1, self.__gps_clean_callback)
while self.__break == 0:
self.__gps_timer.start(1500, 0, self.__gps_timer_callback)
gnss_data = quecgnss.read(1024)
if gnss_data and gnss_data[1]:
self.__gps_data += gnss_data[1].decode() if len(gnss_data) > 1 and gnss_data[1] else ""
if not self.__rmc_data:
self.__rmc_data = self.__gps_match.GxRMC(self.__gps_data)
if not self.__gga_data:
self.__gga_data = self.__gps_match.GxGGA(self.__gps_data)
if not self.__vtg_data:
self.__vtg_data = self.__gps_match.GxVTG(self.__gps_data)
if not self.__gsv_data:
self.__gsv_data = self.__gps_match.GxGSV(self.__gps_data)
if self.__rmc_data and self.__gga_data and self.__vtg_data and self.__gsv_data:
self.__break = 1
self.__gps_timer.stop()
self.__gps_clean_timer.stop()
self.__break = 0
self.__internal_close()
return self.__gps_data
def read(self):
"""For user to read gps data
Return: (res_code, gps_data)
res_code:
- 0: Success
- -1: Failed
gps_data:
$GPTXT,01,01,02,ANTSTATUS=OPEN*2B
$GNRMC,073144.000,A,3149.330773,N,11706.946971,E,0.00,337.47,150422,,,D,V*07
$GNVTG,337.47,T,,M,0.00,N,0.00,K,D*22
$GNGGA,073144.000,3149.330773,N,11706.946971,E,2,19,0.66,85.161,M,-0.335,M,,*56
$GNGSA,A,3,01,195,06,03,21,194,19,17,30,14,,,0.94,0.66,0.66,1*02
$GNGSA,A,3,13,26,07,10,24,25,08,03,22,,,,0.94,0.66,0.66,4*03
$GPGSV,3,1,12,14,84,210,31,195,67,057,46,17,52,328,28,50,51,161,33,1*54
$GPGSV,3,2,12,194,49,157,33,03,48,090,37,19,36,305,32,06,34,242,32,1*58
$GPGSV,3,3,12,01,32,041,35,30,17,204,22,21,07,051,13,07,03,183,,1*6B
$BDGSV,5,1,18,07,86,063,30,10,75,322,30,08,60,211,34,03,52,192,33,1*71
$BDGSV,5,2,18,24,44,276,33,13,43,215,33,01,43,135,30,26,40,208,37,1*71
$BDGSV,5,3,18,02,38,230,,04,32,119,,22,26,135,30,19,25,076,,1*70
$BDGSV,5,4,18,05,17,251,,25,06,322,27,09,02,211,22,21,02,179,,1*78
$BDGSV,5,5,18,29,02,075,,20,01,035,,1*72
$GNGLL,3149.330773,N,11706.946971,E,073144.000,A,D*4E
"""
gps_data = ""
if self.__gps_mode & _gps_mode.external:
gps_data = self.__external_read()
elif self.__gps_mode & _gps_mode.internal:
gps_data = self.__internal_read()
res = 0 if gps_data else -1
return (res, gps_data)
def read_latitude(self, gps_data):
"""Read latitude from gps data"""
return self.__gps_parse.GxGGA_latitude(self.__gps_match.GxGGA(gps_data))
def read_longtitude(self, gps_data):
"""Read longtitude from gps data"""
return self.__gps_parse.GxGGA_longtitude(self.__gps_match.GxGGA(gps_data))
def read_altitude(self, gps_data):
"""Read altitude from gps data"""
return self.__gps_parse.GxGGA_altitude(self.__gps_match.GxGGA(gps_data))
def on(self):
"""GPS Module switch on"""
# TODO: Set GPS ON
return True
def off(self):
"""GPS Module switch off"""
# TODO: Set GPS OFF
return True
class CellLocator(object):
"""This class is for reading cell location data"""
def __init__(self, cell_cfg):
self.cell_cfg = cell_cfg
def read(self):
"""Read cell location data.
Return: (res_code, loc_data)
res_code:
- 0: Success
- -1: Initialization failed
- -2: The server address is too long (more than 255 bytes)
- -3: Wrong key length, must be 16 bytes
- -4: The timeout period is out of range, the supported range is (1 ~ 300) s
- -5: The specified PDP network is not connected, please confirm whether the PDP is correct
- -6: Error getting coordinates
loc_data:
(117.1138, 31.82279, 550)
"""
res = -1
loc_data = cellLocator.getLocation(
self.cell_cfg["serverAddr"],
self.cell_cfg["port"],
self.cell_cfg["token"],
self.cell_cfg["timeout"],
self.cell_cfg["profileIdx"]
)
if isinstance(loc_data, tuple) and len(loc_data) == 3:
res = 0
else:
res = loc_data
loc_data = ()
return (res, loc_data)
class WiFiLocator(object):
"""This class is for reading wifi location data"""
def __init__(self, wifi_cfg):
self.wifilocator_obj = wifilocator(wifi_cfg["token"])
def read(self):
"""Read wifi location data.
Return: (res_code, loc_data)
res_code:
- 0: Success
- -1: The current network is abnormal, please confirm whether the dial-up is normal
- -2: Wrong key length, must be 16 bytes
- -3: Error getting coordinates
loc_data:
(117.1138, 31.82279, 550)
"""
res = -1
loc_data = self.wifilocator_obj.getwifilocator()
if isinstance(loc_data, tuple) and len(loc_data) == 3:
res = 0
else:
res = loc_data
loc_data = ()
return (res, loc_data)
class Location(Singleton):
"""This class is for reading location data from gps, cell, wifi"""
gps = None
cellLoc = None
wifiLoc = None
def __init__(self, gps_mode, locator_init_params):
self.__gps_mode = gps_mode
self.__locator_init_params = locator_init_params
def __locater_init(self, loc_method):
"""Init gps, cell, wifi by loc_method
Parameter:
loc_method:
- 1: gps
- 2: cell
- 3: cell & gps
- 4: wifi
- 5: wifi & gps
- 6: wifi & cell
- 7: wifi & cell & gps
"""
if loc_method & _loc_method.gps:
if self.gps is None:
if self.__locator_init_params.get("gps_cfg"):
self.gps = GPS(self.__locator_init_params["gps_cfg"], self.__gps_mode)
else:
raise ValueError("Invalid gps init parameters.")
else:
self.gps = None
if loc_method & _loc_method.cell:
if self.cellLoc is None:
if self.__locator_init_params.get("cell_cfg"):
self.cellLoc = CellLocator(self.__locator_init_params["cell_cfg"])
else:
raise ValueError("Invalid cell-locator init parameters.")
else:
self.cellLoc = None
if loc_method & _loc_method.wifi:
if self.wifiLoc is None:
if self.__locator_init_params.get("wifi_cfg"):
self.wifiLoc = WiFiLocator(self.__locator_init_params["wifi_cfg"])
else:
raise ValueError("Invalid wifi-locator init parameters.")
else:
self.wifiLoc = None
def __read_gps(self):
"""Read loction data from gps module
Return:
$GPTXT,01,01,02,ANTSTATUS=OPEN*2B
$GNRMC,073144.000,A,3149.330773,N,11706.946971,E,0.00,337.47,150422,,,D,V*07
$GNVTG,337.47,T,,M,0.00,N,0.00,K,D*22
$GNGGA,073144.000,3149.330773,N,11706.946971,E,2,19,0.66,85.161,M,-0.335,M,,*56
$GNGSA,A,3,01,195,06,03,21,194,19,17,30,14,,,0.94,0.66,0.66,1*02
$GNGSA,A,3,13,26,07,10,24,25,08,03,22,,,,0.94,0.66,0.66,4*03
$GPGSV,3,1,12,14,84,210,31,195,67,057,46,17,52,328,28,50,51,161,33,1*54
$GPGSV,3,2,12,194,49,157,33,03,48,090,37,19,36,305,32,06,34,242,32,1*58
$GPGSV,3,3,12,01,32,041,35,30,17,204,22,21,07,051,13,07,03,183,,1*6B
$BDGSV,5,1,18,07,86,063,30,10,75,322,30,08,60,211,34,03,52,192,33,1*71
$BDGSV,5,2,18,24,44,276,33,13,43,215,33,01,43,135,30,26,40,208,37,1*71
$BDGSV,5,3,18,02,38,230,,04,32,119,,22,26,135,30,19,25,076,,1*70
$BDGSV,5,4,18,05,17,251,,25,06,322,27,09,02,211,22,21,02,179,,1*78
$BDGSV,5,5,18,29,02,075,,20,01,035,,1*72
$GNGLL,3149.330773,N,11706.946971,E,073144.000,A,D*4E
"""
if self.gps:
return self.gps.read()[1]
return ""
def __read_cell(self):
"""Read loction data from cell module
Return:
(117.1138, 31.82279, 550) or ()
"""
if self.cellLoc:
return self.cellLoc.read()[1]
return ()
def __read_wifi(self):
"""Read loction data from wifi module
Return:
(117.1138, 31.82279, 550) or ()
"""
if self.wifiLoc:
return self.wifiLoc.read()[1]
return ()
def read(self, loc_method):
"""Read location data by loc_method
1. If loc_method include gps then get gps data;
2. If loc_method inculde cell then get cell data;
3. If loc_method Include wifi then get wifi data;
Parameter:
loc_method:
- 1: gps
- 2: cell
- 3: cell & gps
- 4: wifi
- 5: wifi & gps
- 6: wifi & cell
- 7: wifi & cell & gps
Return Data Format:
{
1: "$GPGGA,XXX",
2: (0.00, 0.00, 0.00),
4: (0.00, 0.00, 0.00),
}
"""
loc_data = {}
self.__locater_init(loc_method)
if loc_method & _loc_method.gps:
loc_data[_loc_method.gps] = self.__read_gps()
if loc_method & _loc_method.cell:
loc_data[_loc_method.cell] = self.__read_cell()
if loc_method & _loc_method.wifi:
loc_data[_loc_method.wifi] = self.__read_wifi()
return loc_data

View File

@ -1,87 +0,0 @@
# 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.
import utime
class Logger:
def __init__(self, name):
self.name = name
self.__debug = True
self.__level = "debug"
self.__level_code = {
"debug": 0,
"info": 1,
"warn": 2,
"error": 3,
"critical": 4,
}
def get_debug(self):
return self.__debug
def set_debug(self, debug):
if isinstance(debug, bool):
self.__debug = debug
return True
return False
def get_level(self):
return self.__level
def set_level(self, level):
if self.__level_code.get(level) is not None:
self.__level = level
return True
return False
def log(self, name, level, *message):
if self.__debug is False:
if self.__level_code.get(level) < self.__level_code.get(self.__level):
return
if hasattr(utime, "strftime"):
print(
"[{}]".format(utime.strftime("%Y-%m-%d %H:%M:%S")),
"[{}]".format(name),
"[{}]".format(level),
*message
)
else:
t = utime.localtime()
print(
"[{}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}]".format(*t),
"[{}]".format(name),
"[{}]".format(level),
*message
)
def critical(self, *message):
self.log(self.name, "critical", *message)
def error(self, *message):
self.log(self.name, "error", *message)
def warn(self, *message):
self.log(self.name, "warn", *message)
def info(self, *message):
self.log(self.name, "info", *message)
def debug(self, *message):
self.log(self.name, "debug", *message)
def getLogger(name):
return Logger(name)

View File

@ -12,7 +12,7 @@
# 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.
from usr.logging import getLogger from usr.modules.logging import getLogger
from usr.tracker import tracker from usr.tracker import tracker
from usr.settings import PROJECT_NAME, PROJECT_VERSION, DEVICE_FIRMWARE_NAME, DEVICE_FIRMWARE_VERSION from usr.settings import PROJECT_NAME, PROJECT_VERSION, DEVICE_FIRMWARE_NAME, DEVICE_FIRMWARE_VERSION

1
code/modules Submodule

@ -0,0 +1 @@
Subproject commit 561ad0c23a8f81e8f5f2e7a0cecb4b1151aeab40

View File

@ -1,159 +0,0 @@
# 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.
import pm
import utime
import _thread
import osTimer
from queue import Queue
from usr.common import Observable
from usr.logging import getLogger
try:
from machine import RTC
except ImportError:
RTC = None
log = getLogger(__name__)
LOW_ENERGY_METHOD = ("NULL", "PM", "PSM", "POWERDOWN")
class LowEnergyManage(Observable):
"""This class is managing low energy wake up"""
def __init__(self):
super().__init__()
self.__timer = None
self.__period = 60
self.__low_energy_method = "PM"
self.__thread_id = None
self.__lpm_fd = None
self.__pm_lock_name = "low_energy_pm_lock"
self.__low_energy_queue = Queue(maxsize=8)
def __timer_callback(self, args):
self.__low_energy_queue.put(self.__low_energy_method)
def __low_energy_work(self, lowenergy_tag):
while True:
data = self.__low_energy_queue.get()
log.debug("__low_energy_work data: %s, lowenergy_tag: %s" % (data, lowenergy_tag))
if data:
if lowenergy_tag:
if self.__lpm_fd is None:
self.__lpm_fd = pm.create_wakelock(self.__pm_lock_name, len(self.__pm_lock_name))
pm.autosleep(1)
wlk_res = pm.wakelock_lock(self.__lpm_fd)
log.debug("pm.wakelock_lock %s." % ("Success" if wlk_res == 0 else "Falied"))
self.notifyObservers(self, *(data,))
if lowenergy_tag:
wulk_res = pm.wakelock_unlock(self.__lpm_fd)
log.debug("pm.wakelock_unlock %s." % ("Success" if wulk_res == 0 else "Falied"))
def __timer_init(self):
if RTC is not None:
self.__timer = RTC()
else:
if self.__low_energy_method in ("PSM", "POWERDOWN"):
raise TypeError("osTimer not support %s!" % self.__low_energy_method)
self.__timer = osTimer()
def __rtc_enable(self, enable):
enable_alarm_res = self.__timer.enable_alarm(enable)
return True if enable_alarm_res == 0 else False
def __rtc_start(self):
self.__rtc_enable(0)
atime = utime.localtime(utime.mktime(utime.localtime()) + self.__period)
alarm_time = [atime[0], atime[1], atime[2], atime[6], atime[3], atime[4], atime[5], 0]
self.__timer.register_callback(self.__timer_callback)
if self.__timer.set_alarm(alarm_time) == 0:
return self.__rtc_enable(1)
return False
def __rtc_stop(self):
return self.__rtc_enable(0)
def __timer_start(self):
res = self.__timer.start(self.__period * 1000, 0, self.__timer_callback)
return True if res == 0 else False
def __timer_stop(self):
res = self.__timer.stop()
log.debug("__timer_stop res: %s" % res)
return True if res == 0 else False
def get_period(self):
return self.__period
def set_period(self, seconds=0):
if isinstance(seconds, int) and seconds > 0:
self.__period = seconds
return True
return False
def get_low_energy_method(self):
return self.__low_energy_method
def set_low_energy_method(self, method):
if method in LOW_ENERGY_METHOD:
if RTC is None and method in ("PSM", "POWERDOWN"):
return False
self.__low_energy_method = method
return True
return False
def get_lpm_fd(self):
return self.__lpm_fd
def low_energy_init(self):
try:
if self.__thread_id is not None:
_thread.stop_thread(self.__thread_id)
if self.__lpm_fd is not None:
pm.delete_wakelock(self.__lpm_fd)
self.__lpm_fd = None
if self.__low_energy_method == "PM":
self.__thread_id = _thread.start_new_thread(self.__low_energy_work, (True,))
self.__lpm_fd = pm.create_wakelock(self.__pm_lock_name, len(self.__pm_lock_name))
pm.autosleep(1)
elif self.__low_energy_method == "NULL":
self.__thread_id = _thread.start_new_thread(self.__low_energy_work, (False,))
elif self.__low_energy_method in ("PSM", "POWERDOWN"):
pass
self.__timer_init()
return True
except:
return False
def start(self):
if RTC is not None:
return self.__rtc_start()
else:
return self.__timer_start()
def stop(self):
if RTC is not None:
return self.__rtc_stop()
else:
return self.__timer_stop()

View File

@ -1,238 +0,0 @@
# 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.
import uos
import fota
import uzlib
import ql_fs
import app_fota
import uhashlib
import ubinascii
import app_fota_download
from queue import Queue
from usr.logging import getLogger
log = getLogger(__name__)
FOTA_ERROR_CODE = {
1001: "FOTA_DOMAIN_NOT_EXIST",
1002: "FOTA_DOMAIN_TIMEOUT",
1003: "FOTA_DOMAIN_UNKNOWN",
1004: "FOTA_SERVER_CONN_FAIL",
1005: "FOTA_AUTH_FAILED",
1006: "FOTA_FILE_NOT_EXIST",
1007: "FOTA_FILE_SIZE_INVALID",
1008: "FOTA_FILE_GET_ERR",
1009: "FOTA_FILE_CHECK_ERR",
1010: "FOTA_INTERNAL_ERR",
1011: "FOTA_NOT_INPROGRESS",
1012: "FOTA_NO_MEMORY",
1013: "FOTA_FILE_SIZE_TOO_LARGE",
1014: "FOTA_PARAM_SIZE_INVALID",
}
class OTA(object):
def __init__(self, file_info, ota_type="FOTA", ota_cb=None):
self.file_info = file_info
self.ota_type = ota_type
self.ota_cb = ota_cb
self.fota_queue = Queue(maxsize=4)
def start(self):
if self.ota_type == "FOTA":
return self.start_fota()
elif self.ota_type == "SOTA":
return self.start_sota()
else:
log.error("OTA Type %s Is FOTA Or SOTA Error!" % self.ota_type)
return False
def __fota_callback(self, args):
down_status = args[0]
down_process = args[1]
if down_status in (0, 1):
# TODO: Report To Cloud Upgrade Process.
log.debug("DownStatus: %s [%s][%s%%]" % (down_status, "=" * down_process, down_process))
elif down_status == 2:
# Download Over & Check Over, To Power Restart Update.
self.fota_queue.put(True)
else:
log.error("Down Failed. Error Code [%s] %s" % (down_process, FOTA_ERROR_CODE.get(down_process, down_process)))
self.fota_queue.put(False)
if self.ota_cb:
self.ota_cb(args)
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_callback)
if res == 0:
fota_res = self.fota_queue.get()
return fota_res
else:
return False
def start_sota(self):
ota_module_obj = SOTA()
for file in self.file_info:
if ota_module_obj.app_fota_down(file["url"]):
if ota_module_obj.check_md5(file["md5"]):
if ota_module_obj.file_update():
continue
else:
return False
else:
return False
else:
return False
ota_module_obj.sota_set_flag()
return True
class SOTA(object):
def __init__(self, parent_dir="/usr/.updater/usr/"):
self.fp_file = "/usr/sotaFile.tar.gz"
self.parent_dir = parent_dir
self.hash_obj = None
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 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()
res = app_fota_obj.download(url, self.fp_file)
if res == 0:
uos.rename("/usr/.updater" + self.fp_file, self.fp_file)
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 check_md5(self, cloud_md5):
file_md5 = ubinascii.hexlify(self.hash_obj.digest())
file_md5 = file_md5.decode("ascii")
log.debug("DMP Calc MD5 Value: %s, Device Calc MD5 Value: %s" % (cloud_md5, file_md5))
if (cloud_md5 != file_md5):
log.error("MD5 Verification Failed")
return False
log.debug("MD5 Verification Success.")
return True
def file_update(self):
ota_file = open(self.fp_file, "rb+")
ota_file.seek(10)
unzipFp = uzlib.DecompIO(ota_file, -15)
log.debug("Unzip File Success.")
ql_fs.mkdirs(self.parent_dir)
file_list = []
try:
while True:
data = 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: %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 = unzipFp.read(0x200)
if (fileSize < 0x200):
fp.write(data[:fileSize])
fileSize = 0
fp.close()
file_list.append({"fileName": "/usr/" + fileName, "size": size})
break
else:
fileSize -= 0x200
fp.write(data)
for fileName in file_list:
app_fota_download.update_download_stat("/usr/.updater" + fileName["fileName"], fileName["fileName"], fileName["size"])
log.debug("Remove %s" % self.fp_file)
uos.remove(self.fp_file)
except Exception as e:
log.error("Unpack Error: %s" % e)
return False
finally:
ota_file.close()
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

@ -905,4 +905,4 @@
"desc":"" "desc":""
} }
] ]
} }

View File

@ -1,618 +0,0 @@
# 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.
import ujson
import utime
import osTimer
import quecIot
from queue import Queue
from usr.ota import SOTA
from usr.logging import getLogger
from usr.common import CloudObservable, CloudObjectModel
log = getLogger(__name__)
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.",
}
}
class QuecObjectModel(CloudObjectModel):
"""This class is queccloud object model
This class extend CloudObjectModel
Attribute:
items:
- object model dictionary
- data format:
{
"event": {
"name": "event",
"id": "",
"perm": "",
"struct_info": {
"name": "struct",
"id": "",
"struct_info": {
"key": {
"name": "key"
}
},
},
},
"property": {
"name": "event",
"id": "",
"perm": "",
"struct_info": {}
}
}
items_id:
- queccloud object model id and name map
- data format
{
4: "energy",
9: "power_switch",
23: "phone_num",
}
"""
def __init__(self, om_file="/usr/quec_object_model.json"):
super().__init__(om_file)
self.items_id = {}
self.init()
def init(self):
with open(self.om_file, "rb") as f:
cloud_object_model = ujson.load(f)
for om_type in cloud_object_model.keys():
if om_type not in ("events", "properties"):
continue
for om_item in cloud_object_model[om_type]:
om_key = om_item["code"]
om_key_id = om_item["id"]
om_key_perm = om_item["subType"].lower()
self.set_item(om_type, om_key, om_key_id, om_key_perm)
struct_info_list = []
event_out_put = []
if om_type == "properties":
if om_item["dataType"] == "STRUCT":
struct_info_list = om_item["specs"]
elif om_type == "events":
if om_item.get("outputData"):
event_out_put = [int(struct_item.get("$ref", "").split("/")[-1]) for struct_item in om_item["outputData"]]
for struct_info in struct_info_list:
struct_key = struct_info["code"]
struct_key_id = struct_info["id"]
struct_key_struct = {}
if struct_info["dataType"] == "STRUCT":
for struct_key_struct_key in struct_info["dataType"]["specs"]:
struct_key_struct[struct_key_struct_key["identifier"]] = {
"name": struct_key_struct_key["identifier"]
}
self.set_item_struct(
om_type, om_key, struct_key,
struct_key_id=struct_key_id,
struct_key_struct=struct_key_struct
)
for property_id in event_out_put:
struct_key = self.items_id.get(property_id, "")
struct_key_id = property_id
struct_key_struct = self.items.get(struct_key, {}).get("struct_info", {})
self.set_item_struct(
om_type, om_key, struct_key,
struct_key_id=struct_key_id,
struct_key_struct=struct_key_struct
)
def __set_items_id(self, om_key, om_key_id):
"""Set object model id, name to items_id
Parameter:
om_key: object model name
om_key_id: object model id
Return:
True: Success
False: Falied
"""
self.items_id[om_key_id] = om_key
return True
def __del_items_id(self, om_type, om_key):
"""Delete object model id, name from items_id
Parameter:
om_type: object model type, `event` or `property`
om_key: object model name
Return:
True: Success
False: Falied
"""
if self.items.get(om_type) is not None:
if self.items[om_type].get(om_key):
om_key_id = self.items[om_type][om_key]["id"]
self.items_id.pop(om_key_id)
return True
def set_item(self, om_type, om_key, om_key_id, om_key_perm):
"""Set object model item to items
This function extend CloudObjectModel.set_item and add __set_items_id function
Return:
True: Success
False: Falied
"""
if super().set_item(om_type, om_key, om_key_id=om_key_id, om_key_perm=om_key_perm):
self.__set_items_id(om_key, om_key_id)
return True
return False
def del_item(self, om_type, om_key):
"""Delete object model item from items
This function extend CloudObjectModel.del_item and add __del_items_id function
Return:
True: Success
False: Falied
"""
if super().del_item(om_type, om_key):
self.__del_items_id(om_type, om_key)
return True
return False
class QuecThing(CloudObservable):
"""This is a class for queccloud iot.
This class extend CloudObservable.
This class has the following functions:
1. Cloud connect and disconnect
2. Publish data to cloud
3. Monitor data from cloud by event callback
Run step:
1. cloud = QuecThing(pk, ps, dk, ds, server)
2. cloud.addObserver(RemoteSubscribe)
3. cloud.set_object_model(QuecObjectModel)
4. cloud.init()
5. cloud.post_data(data)
6. cloud.close()
"""
def __init__(self, pk, ps, dk, ds, server, life_time=120, mcu_name="", mcu_version=""):
"""
1. Init parent class CloudObservable
2. Init cloud connect params
"""
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
self.__object_model = None
self.__file_size = 0
self.__md5_value = ""
self.__post_result_wait_queue = Queue(maxsize=16)
self.__quec_timer = osTimer()
def __rm_empty_data(self, data):
"""Remove post success data item from data"""
for k, v in data.items():
if not v:
del data[k]
def __quec_timer_cb(self, args):
"""osTimer callback to break waiting of get publish result"""
self.__put_post_res(False)
def __get_post_res(self):
"""Get publish result"""
self.__quec_timer.start(1000 * 10, 0, self.__quec_timer_cb)
res = self.__post_result_wait_queue.get()
self.__quec_timer.stop()
return res
def __put_post_res(self, res):
"""Save publish result to queue"""
if self.__post_result_wait_queue.size() >= 16:
self.__post_result_wait_queue.get()
self.__post_result_wait_queue.put(res)
def __sota_download_info(self, size, md5_value):
self.__file_size = size
self.__md5_value = md5_value
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):
log.debug("File Download Success, Update Start.")
self.ota_action(3)
if sota_mode.check_md5(self.__md5_value):
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)
def __data_format(self, k, v):
"""Publish data format by AliObjectModel
Parameter:
k: object model name
v: object model value
return:
{
"object_model_id": object_model_value
}
e.g.:
k:
"sos_alert"
v:
{"local_time": 1649995898000}
return data:
{
6: {
19: 1649995898000
}
}
"""
# log.debug("k: %s, v: %s" % (k, v))
k_id = None
struct_info = {}
if self.__object_model.items["events"].get(k):
k_id = self.__object_model.items["events"][k]["id"]
if isinstance(self.__object_model.items["events"][k]["struct_info"], dict):
struct_info = self.__object_model.items["events"][k]["struct_info"]
elif self.__object_model.items["properties"].get(k):
k_id = self.__object_model.items["properties"][k]["id"]
if isinstance(self.__object_model.items["properties"][k]["struct_info"], dict):
struct_info = self.__object_model.items["properties"][k]["struct_info"]
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}
def __event_cb(self, data):
"""Queccloud downlink message callback
Parameter:
data: response dictionary info, all event info see `EVENT_CODE`
data format: (`event_code`, `errcode`, `event_data`)
- `event_code`: event code
- `errcode`: detail code
- `event_data`: event data info, data type: bytes or dict
"""
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()]
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)]
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]))
def set_object_model(self, object_model):
"""Register QuecObjectModel to this class"""
if object_model and isinstance(object_model, QuecObjectModel):
self.__object_model = object_model
return True
return False
def init(self, enforce=False):
"""queccloud connect
Parameter:
enforce:
True: enfore cloud connect
False: check connect status, return True if cloud connected
Return:
Ture: Success
False: Failed
"""
log.debug(
"[init start] enforce: %s QuecThing Work State: %s, quecIot.getConnmode(): %s"
% (enforce, quecIot.getWorkState(), quecIot.getConnmode())
)
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
quecIot.init()
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)
quecIot.setConnmode(1)
count = 0
while quecIot.getWorkState() != 8 and count < 10:
utime.sleep_ms(200)
count += 1
if not self.__ds and self.__dk:
count = 0
while count < 3:
dkds = quecIot.getDkDs()
if dkds:
self.__dk, self.__ds = dkds
log.debug("dk: %s, ds: %s" % dkds)
break
count += 1
utime.sleep(count)
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):
"""queccloud disconnect"""
return quecIot.setConnmode(0)
def post_data(self, data):
"""Publish object model property, event
Parameter:
data format:
{
"phone_num": "123456789",
"energy": 100,
"gps": [
"$GNGGA,XXX"
"$GNVTG,XXX"
"$GNRMC,XXX"
],
}
Return:
Ture: Success
False: Failed
"""
res = True
# log.debug("post_data: %s" % str(data))
for k, v in data.items():
om_data = self.__data_format(k, v)
if om_data is not False:
if v is not None:
phymodelReport_res = quecIot.phymodelReport(1, om_data)
if not phymodelReport_res:
res = False
break
else:
continue
elif k == "gps":
locReportOutside_res = quecIot.locReportOutside(v)
if not locReportOutside_res:
res = False
break
elif k == "non_gps":
locReportInside_res = quecIot.locReportInside(v)
if not locReportInside_res:
res = False
break
else:
v = {}
continue
res = self.__get_post_res()
if res:
v = {}
else:
res = False
break
self.__rm_empty_data(data)
return res
def ota_request(self, mp_mode=0):
"""Publish mcu and firmware ota plain request
Return:
Ture: Success
False: Failed
"""
return quecIot.otaRequest(mp_mode) if mp_mode in (0, 1) else False
def ota_action(self, action=1, module=None):
"""Publish ota upgrade start or cancel ota upgrade
Parameter:
action: confirm or cancel upgrade
- 0: cancel upgrade
- 1: confirm upgrade
module: useless
Return:
Ture: Success
False: Failed
"""
return quecIot.otaAction(action) if action in (0, 1, 2, 3) else False

View File

@ -1,135 +0,0 @@
# 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.
from usr.logging import getLogger
from usr.common import Observable, CloudObserver
log = getLogger(__name__)
class RemoteSubscribe(CloudObserver):
def __init__(self):
self.__executor = None
def get_executor(self):
return self.__executor
def add_executor(self, executor):
if executor:
self.__executor = executor
return True
return False
def raw_data(self, *args, **kwargs):
return self.__executor.event_option(*args, **kwargs) if self.__executor else False
def object_model(self, *args, **kwargs):
return self.__executor.event_done(*args, **kwargs) if self.__executor else False
def query(self, *args, **kwargs):
return self.__executor.event_query(*args, **kwargs) if self.__executor else False
def ota_plain(self, *args, **kwargs):
return self.__executor.event_ota_plain(*args, **kwargs) if self.__executor else False
def ota_file_download(self, *args, **kwargs):
# TODO: To Download OTA File For MQTT Association (Not Support Now.)
log.debug("ota_file_download: %s" % str(args))
if self.__executor and hasattr(self.__executor, "ota_file_download"):
return self.__executor.event_ota_file_download(*args, **kwargs)
else:
return False
def execute(self, observable, *args, **kwargs):
"""
1. observable: Cloud Iot Object.
2. args[1]: Cloud DownLink Data Type.
2.1 object_model: Set Cloud Object Model.
2.2 query: Query Cloud Object Model.
2.3 ota_plain: OTA Plain Info.
2.4 raw_data: Passthrough Data (Not Support Now).
2.5 ota_file_download: Download OTA File For MQTT Association (Not Support Now).
3. args[2]: Cloud DownLink Data(List Or Dict).
"""
opt_attr = args[1]
opt_args = args[2] if not isinstance(args[2], dict) else ()
opt_kwargs = args[2] if isinstance(args[2], dict) else {}
if hasattr(self, opt_attr):
option_fun = getattr(self, opt_attr)
return option_fun(*opt_args, **opt_kwargs)
else:
log.error("RemoteSubscribe Has No Attribute [%s]." % opt_attr)
return False
class RemotePublish(Observable):
def __init__(self):
"""
cloud:
CloudIot Object
"""
super().__init__()
self.__cloud = None
def __cloud_conn(self, enforce=False):
return self.__cloud.init(enforce=enforce) if self.__cloud else False
def __cloud_post(self, data):
return self.__cloud.post_data(data) if self.__cloud else False
def add_cloud(self, cloud):
if hasattr(cloud, "init") and \
hasattr(cloud, "post_data") and \
hasattr(cloud, "ota_request") and \
hasattr(cloud, "ota_action"):
self.__cloud = cloud
return True
return False
def cloud_ota_check(self):
return self.__cloud.ota_request() if self.__cloud else False
def cloud_ota_action(self, action=1, module=None):
return self.__cloud.ota_action(action, module) if self.__cloud else False
def post_data(self, data):
"""
Data format to post:
{
"switch": True,
"energy": 100,
"non_gps": [],
"gps": []
}
"""
res = True
if self.__cloud_conn():
if not self.__cloud_post(data):
if self.__cloud_conn(enforce=True):
if not self.__cloud_post(data):
res = False
else:
log.error("Cloud Connect Failed.")
res = False
else:
log.error("Cloud Connect Failed.")
res = False
if res is False:
# This Observer Is History
self.notifyObservers(self, *[data])
return res

View File

@ -1,61 +0,0 @@
# 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.
import _thread
from queue import Queue
from usr.common import Singleton
from usr.logging import getLogger
log = getLogger(__name__)
def sensor_process(args):
self = args
while True:
sensor_read = self.sensor_queue.get()
if sensor_read:
sensor_data = {}
sensor_data["temperature"] = self.temperature()
sensor_data["light"] = self.light()
sensor_data["driving_behavior_code"] = self.driving_behavior()
if self.sensor_read_cb:
self.sensor_read_cb(**sensor_data)
else:
log.warn("Sensor read callback is not defined.")
class Sensor(Singleton):
def __init__(self, sensor_read_cb=None):
self.sensor_read_cb = sensor_read_cb
self.sensor_queue = Queue(maxsize=64)
_thread.start_new_thread(sensor_process, (self,))
def temperature(self):
# TODO: Get temperature value
temperature_values = None
return temperature_values
def light(self):
# TODO: Get temperature value
light_values = None
return light_values
def driving_behavior(self):
# TODO: Get driving behavior code
driving_behavior_code = None
return driving_behavior_code
def post_data(self, sensor_type, sensor_value):
self.sensor_queue.put(True)

View File

@ -19,8 +19,8 @@ import ujson
import modem import modem
import _thread import _thread
from usr.common import Singleton from usr.moudles.common import Singleton
from usr.common import option_lock from usr.moudles.common import option_lock
from usr.settings_sys import SYSConfig from usr.settings_sys import SYSConfig
from usr.settings_loc import LocConfig from usr.settings_loc import LocConfig
from usr.settings_alicloud import AliCloudConfig from usr.settings_alicloud import AliCloudConfig

View File

@ -12,63 +12,8 @@
# 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 modem
import utime
import osTimer
from usr.logging import Logger
from usr.tracker import tracker from usr.tracker import tracker
from usr.battery import Battery from usr.settings import Settings
from usr.history import History
from usr.location import Location, _loc_method, GPSMatch, GPSParse
from usr.quecthing import QuecThing, QuecObjectModel
from usr.aliyunIot import AliYunIot, AliObjectModel
from usr.mpower import LowEnergyManage
from usr.common import Observable, Observer
from usr.remote import RemoteSubscribe, RemotePublish
from usr.settings import Settings, PROJECT_NAME, PROJECT_VERSION, \
DEVICE_FIRMWARE_NAME, DEVICE_FIRMWARE_VERSION, LocConfig, \
AliCloudConfig, QuecCloudConfig
log = Logger(__name__)
def test_led():
pass
def test_logger():
res = {"all": 0, "success": 0, "failed": 0}
log = Logger("test_logger")
log.debug("debug Level Log.")
log.info("info Level Log.")
log.warn("warn Level Log.")
log.error("error Level Log.")
log.critical("critical Level Log.")
assert log.get_debug() is True, "[test_logger] FAILED: log.get_debug() is not True."
print("[test_logger] SUCCESS: log.get_debug() is True.")
res["success"] += 1
assert log.set_debug(True) is True, "[test_logger] FAILED: log.set_debug(True)."
print("[test_logger] SUCCESS: log.set_debug(True).")
res["success"] += 1
assert log.set_debug(False) is True, "[test_logger] FAILED: log.set_debug(False)."
print("[test_logger] SUCCESS: log.set_debug(False).")
res["success"] += 1
assert log.get_level() == "debug", "[test_logger] FAILED: log.get_level() is not debug."
print("[test_logger] SUCCESS: log.get_level() is debug.")
res["success"] += 1
for level in ("debug", "info", "warn", "error", "critical"):
assert log.set_level(level) is True and log.get_level() == level, "[test_logger] FAILED: log.set_level(%s)." % level
print("[test_logger] SUCCESS: log.set_level(%s)." % level)
res["success"] += 1
res["all"] = res["success"] + res["failed"]
print("[test_logger] ALL: %s SUCCESS: %s, FAILED: %s." % (res["all"], res["success"], res["failed"]))
def test_settings(): def test_settings():
@ -111,451 +56,12 @@ def test_settings():
print("[test_settings] ALL: %s SUCCESS: %s, FAILED: %s." % (res["all"], res["success"], res["failed"])) print("[test_settings] ALL: %s SUCCESS: %s, FAILED: %s." % (res["all"], res["success"], res["failed"]))
run_time = 0
def get_voltage_cb(args):
global run_time
run_time += 5
def test_battery():
res = {"all": 0, "success": 0, "failed": 0}
battery = Battery()
temp = 30
msg = "[test_battery] %s: battery.set_temp(30)."
assert battery.set_temp(temp) and battery.__temp == temp, msg % "FAILED"
print(msg % "SUCCESS")
res["success"] += 1
timer = osTimer()
timer.start(5, 1, get_voltage_cb)
voltage = battery.get_voltage()
timer.stop()
global run_time
print("[test_battery] battery.get_voltage() run_time: %sms" % run_time)
msg = "[test_battery] %s: battery.get_voltage() %s."
assert isinstance(voltage, int) and voltage > 0, msg % ("FAILED", voltage)
print(msg % ("SUCCESS", voltage))
res["success"] += 1
energy = battery.get_energy()
assert isinstance(energy, int) and energy >= 0, "[test_battery] FAILED: battery.get_energy() %s." % energy
print("[test_battery] SUCCESS: battery.get_energy() is %s." % energy)
res["success"] += 1
res["all"] = res["success"] + res["failed"]
print("[test_battery] ALL: %s SUCCESS: %s, FAILED: %s." % (res["all"], res["success"], res["failed"]))
class TestHistObservable(Observable):
def produce_hist_data(self, local_time):
hist_data = [{"local_time": local_time}]
self.notifyObservers(self, *hist_data)
def test_history():
res = {"all": 0, "success": 0, "failed": 0}
history = History()
test_hist_obs = TestHistObservable()
test_hist_obs.addObserver(history)
hist_data = [{"test": "test"}]
assert history.write(hist_data), "[test_history] FAILED: history.write()."
print("[test_history] SUCCESS: history.write(%s)." % str(hist_data))
res["success"] += 1
hist = history.read()
assert hist.get("data") is not None and isinstance(hist["data"], list), "[test_history] FAILED: history.read() %s." % hist
print("[test_history] SUCCESS: history.read() is %s." % hist)
res["success"] += 1
local_time = utime.mktime(utime.localtime())
test_hist_obs.produce_hist_data(local_time)
hist = history.read()
obs_res = False
for i in hist.get("data", []):
if i.get("local_time") == local_time:
obs_res = True
break
assert obs_res, "[test_history] FAILED: history.update() %s." % str(hist)
print("[test_history] SUCCESS: history.update() %s." % str(hist))
res["success"] += 1
assert history.clean(), "[test_history] FAILED: history.clean()."
print("[test_history] SUCCESS: history.clean().")
res["success"] += 1
res["all"] = res["success"] + res["failed"]
print("[test_history] ALL: %s SUCCESS: %s, FAILED: %s." % (res["all"], res["success"], res["failed"]))
def test_location():
res = {"all": 0, "success": 0, "failed": 0}
settings = Settings()
current_settings = settings.get()
gps_mode = 0x2
locator_init_params = current_settings["LocConfig"]["locator_init_params"]
locator = Location(gps_mode, locator_init_params)
for loc_method in range(1, 8):
loc_data = locator.read(loc_method)
if loc_method & 0x1:
assert loc_data.get(0x1) not in ("", (), None), "[test_location] FAILED: locator.read(%s) loc_data: %s." % (loc_method, loc_data)
if loc_method & 0x2:
assert loc_data.get(0x2) not in ("", (), None), "[test_location] FAILED: locator.read(%s) loc_data: %s." % (loc_method, loc_data)
if loc_method & 0x4:
assert loc_data.get(0x4) not in ("", (), None), "[test_location] FAILED: locator.read(%s) loc_data: %s." % (loc_method, loc_data)
print("[test_location] SUCCESS: locator.read(%s) loc_data: %s." % (loc_method, loc_data))
res["success"] += 1
res["all"] = res["success"] + res["failed"]
print("[test_location] ALL: %s SUCCESS: %s, FAILED: %s." % (res["all"], res["success"], res["failed"]))
def get_quec_loc_data(loc_method, loc_data):
__gps_match = GPSMatch()
if loc_method == 0x1:
res = {"gps": []}
r = __gps_match.GxRMC(loc_data)
if r:
res["gps"].append(r)
r = __gps_match.GxGGA(loc_data)
if r:
res["gps"].append(r)
r = __gps_match.GxVTG(loc_data)
if r:
res["gps"].append(r)
return res
elif loc_method == 0x2:
return {"non_gps": ["LBS"]}
elif loc_method == 0x4:
return {"non_gps": []}
def test_quecthing():
res = {"all": 0, "success": 0, "failed": 0}
settings = Settings()
current_settings = settings.get()
cloud_init_params = QuecCloudConfig.__dict__
cloud = QuecThing(
cloud_init_params["PK"],
cloud_init_params["PS"],
cloud_init_params["DK"],
cloud_init_params["DS"],
cloud_init_params["SERVER"],
mcu_name=PROJECT_NAME,
mcu_version=PROJECT_VERSION
)
remote_sub = RemoteSubscribe()
cloud.addObserver(remote_sub)
quec_om = QuecObjectModel()
msg = "[test_quecthing] %s: cloud.set_object_model(%s)."
assert cloud.set_object_model(quec_om), msg % ("FAILED", quec_om)
print(msg % ("SUCCESS", quec_om))
res["success"] += 1
msg = "[test_quecthing] %s: cloud.init()."
assert cloud.init(), msg % "FAILED"
print(msg % "SUCCESS")
res["success"] += 1
msg = "[test_quecthing] %s: get_quec_loc_data(%s, %s) %s."
loc_method = _loc_method.gps
gps_mode = LocConfig._gps_mode.external
locator_init_params = current_settings["LocConfig"]["locator_init_params"]
locator = Location(gps_mode, locator_init_params)
loc_data = locator.read(loc_method)
quec_loc_data = get_quec_loc_data(loc_method, loc_data.get(loc_method))
assert quec_loc_data != "", msg % ("FAILED", loc_method, loc_data, quec_loc_data)
print(msg % ("SUCCESS", loc_method, loc_data, quec_loc_data))
res["success"] += 1
msg = "[test_quecthing] %s: cloud.post_data(%s)."
assert cloud.post_data(quec_loc_data), msg % ("FAILED", str(quec_loc_data))
print(msg % ("SUCCESS", str(quec_loc_data)))
res["success"] += 1
msg = "[test_quecthing] %s: cloud.ota_request()."
assert cloud.ota_request(), msg % ("FAILED",)
print(msg % ("SUCCESS",))
res["success"] += 1
# # PASS: No OTA Plain, ota_action Return False
# msg = "[test_quecthing] %s: cloud.ota_action()."
# assert cloud.ota_action() is True, msg % ("FAILED",)
# print(msg % ("SUCCESS",))
msg = "[test_quecthing] %s: cloud.close()."
assert cloud.close(), msg % "FAILED"
print(msg % "SUCCESS")
res["success"] += 1
res["all"] = res["success"] + res["failed"]
print("[test_quecthing] ALL: %s SUCCESS: %s, FAILED: %s." % (res["all"], res["success"], res["failed"]))
def get_ali_loc_data(loc_method, loc_data):
res = {"GeoLocation": {}}
__gps_match = GPSMatch()
__gps_parse = GPSParse()
if loc_method == 0x1:
gga_data = __gps_match.GxGGA(loc_data)
data = {}
if gga_data:
Latitude = __gps_parse.GxGGA_latitude(gga_data)
if Latitude:
data["Latitude"] = float("%.2f" % float(Latitude))
Longtitude = __gps_parse.GxGGA_longtitude(gga_data)
if Longtitude:
data["Longtitude"] = float("%.2f" % float(Longtitude))
Altitude = __gps_parse.GxGGA_altitude(gga_data)
if Altitude:
data["Altitude"] = float("%.2f" % float(Altitude))
if data:
data["CoordinateSystem"] = 1
res = {"GeoLocation": data}
elif loc_method in (0x2, 0x4):
if loc_data:
res["GeoLocation"] = {
"Longtitude": round(loc_data[0], 2),
"Latitude": round(loc_data[1], 2),
# "Altitude": 0.0,
"CoordinateSystem": 1
}
return res
def test_aliyuniot():
res = {"all": 0, "success": 0, "failed": 0}
settings = Settings()
current_settings = settings.get()
cloud_init_params = AliCloudConfig.__dict__
client_id = cloud_init_params["client_id"] if cloud_init_params.get("client_id") else modem.getDevImei()
cloud = AliYunIot(
cloud_init_params["PK"],
cloud_init_params["PS"],
cloud_init_params["DK"],
cloud_init_params["DS"],
cloud_init_params["SERVER"],
client_id,
burning_method=cloud_init_params["burning_method"],
mcu_name=PROJECT_NAME,
mcu_version=PROJECT_VERSION,
firmware_name=DEVICE_FIRMWARE_NAME,
firmware_version=DEVICE_FIRMWARE_VERSION
)
remote_sub = RemoteSubscribe()
cloud.addObserver(remote_sub)
ali_om = AliObjectModel()
msg = "[test_aliyuniot] %s: cloud.set_object_model(%s)."
assert cloud.set_object_model(ali_om), msg % ("FAILED", ali_om)
print(msg % ("SUCCESS", ali_om))
res["success"] += 1
msg = "[test_aliyuniot] %s: cloud.init()."
assert cloud.init(), msg % "FAILED"
print(msg % "SUCCESS")
res["success"] += 1
msg = "[test_aliyuniot] %s: get_ali_loc_data(%s, %s) %s."
loc_method = _loc_method.gps
gps_mode = LocConfig._gps_mode.external
locator_init_params = current_settings["LocConfig"]["locator_init_params"]
locator = Location(gps_mode, locator_init_params)
loc_data = locator.read(loc_method)
ali_loc_data = get_ali_loc_data(loc_method, loc_data.get(loc_method))
assert ali_loc_data["GeoLocation"] != {}, msg % ("FAILED", loc_method, loc_data, ali_loc_data)
print(msg % ("SUCCESS", loc_method, loc_data, ali_loc_data))
res["success"] += 1
msg = "[test_aliyuniot] %s: cloud.post_data(%s)."
assert cloud.post_data(ali_loc_data), msg % ("FAILED", str(ali_loc_data))
print(msg % ("SUCCESS", str(ali_loc_data)))
res["success"] += 1
msg = "[test_aliyuniot] %s: cloud.ota_request()."
assert cloud.ota_request(), msg % ("FAILED",)
print(msg % ("SUCCESS",))
res["success"] += 1
msg = "[test_aliyuniot] %s: cloud.device_report()."
assert cloud.device_report(), msg % ("FAILED",)
print(msg % ("SUCCESS",))
res["success"] += 1
# # PASS: No OTA Plain, ota_action Return False
# msg = "[test_aliyuniot] %s: cloud.ota_action()."
# assert cloud.ota_action() is True, msg % ("FAILED",)
# print(msg % ("SUCCESS",))
msg = "[test_aliyuniot] %s: cloud.close()."
assert cloud.close() and cloud.__ali.getAliyunSta() != 0, msg % "FAILED"
print(msg % "SUCCESS")
res["success"] += 1
res["all"] = res["success"] + res["failed"]
print("[test_aliyuniot] ALL: %s SUCCESS: %s, FAILED: %s." % (res["all"], res["success"], res["failed"]))
def test_remote():
res = {"all": 0, "success": 0, "failed": 0}
settings = Settings()
current_settings = settings.get()
cloud_init_params = current_settings["cloud"]
cloud = QuecThing(
cloud_init_params["PK"],
cloud_init_params["PS"],
cloud_init_params["DK"],
cloud_init_params["DS"],
cloud_init_params["SERVER"],
mcu_name=PROJECT_NAME,
mcu_version=PROJECT_VERSION
)
remote_sub = RemoteSubscribe()
cloud.addObserver(remote_sub)
remote_pub = RemotePublish()
msg = "[test_remote] %s: cloud.cloud_init()."
assert cloud.cloud_init(), msg % "FAILED"
print(msg % "SUCCESS")
msg = "[test_remote] %s: remote_pub.set_cloud(cloud)."
assert remote_pub.set_cloud(cloud), msg % "FAILED"
print(msg % "SUCCESS")
res["success"] += 1
msg = "[test_remote] %s: remote_pub.get_cloud()."
assert isinstance(remote_pub.get_cloud(), QuecThing), msg % "FAILED"
print(msg % "SUCCESS")
res["success"] += 1
msg = "[test_remote] %s: remote_pub.cloud_ota_check()."
assert remote_pub.cloud_ota_check(), msg % "FAILED"
print(msg % "SUCCESS")
res["success"] += 1
# # PASS: No OTA Plain, ota_action Return False
# msg = "[test_remote] %s: remote_pub.ota_request()."
# assert remote_pub.ota_request(), msg % "FAILED"
# print(msg % "SUCCESS")
gps_mode = 0x2
locator_init_params = current_settings["user_cfg"]["locator_init_params"]
locator = Location(gps_mode, locator_init_params)
loc_method = 0x1
loc_data = locator.read(loc_method)
quec_loc_data = cloud.get_loc_data(loc_method, loc_data.get(loc_method))
msg = "[test_remote] %s: remote_pub.post_data(%s)."
assert remote_pub.post_data(quec_loc_data), msg % ("FAILED", str(quec_loc_data))
print(msg % ("SUCCESS", str(quec_loc_data)))
res["success"] += 1
res["all"] = res["success"] + res["failed"]
print("[test_remote] ALL: %s SUCCESS: %s, FAILED: %s." % (res["all"], res["success"], res["failed"]))
class TestLEMObserver(Observer):
def update(self, observable, *args, **kwargs):
log.debug("observable: %s" % observable)
log.debug("args: %s" % str(args))
log.debug("kwargs: %s" % str(kwargs))
observable.start()
return True
def test_low_energy_manage():
res = {"all": 0, "success": 0, "failed": 0}
low_energy_manage = LowEnergyManage()
test_lem_obs = TestLEMObserver()
low_energy_manage.addObserver(test_lem_obs)
period = 5
msg = "[test_low_energy_manage] %s: low_energy_manage.set_period(%s)."
assert low_energy_manage.set_period(period), msg % ("FAILED", period)
print(msg % ("SUCCESS", period))
res["success"] += 1
msg = "[test_low_energy_manage] %s: low_energy_manage.get_period()."
assert low_energy_manage.get_period() == period, msg % "FAILED"
print(msg % "SUCCESS")
res["success"] += 1
low_energy_method = "PM"
msg = "[test_low_energy_manage] %s: low_energy_manage.set_low_energy_method(%s)."
assert low_energy_manage.set_low_energy_method(low_energy_method), msg % ("FAILED", low_energy_method)
print(msg % ("SUCCESS", low_energy_method))
res["success"] += 1
msg = "[test_low_energy_manage] %s: low_energy_manage.get_low_energy_method()."
assert low_energy_manage.get_low_energy_method() == low_energy_method, msg % "FAILED"
print(msg % "SUCCESS")
res["success"] += 1
msg = "[test_low_energy_manage] %s: low_energy_manage.low_energy_init()."
assert low_energy_manage.low_energy_init(), msg % "FAILED"
print(msg % "SUCCESS")
res["success"] += 1
msg = "[test_low_energy_manage] %s: low_energy_manage.get_lpm_fd()."
assert low_energy_manage.get_lpm_fd() is not None, msg % "FAILED"
print(msg % "SUCCESS")
res["success"] += 1
msg = "[test_low_energy_manage] %s: low_energy_manage.start()."
assert low_energy_manage.start(), msg % "FAILED"
print(msg % "SUCCESS")
res["success"] += 1
utime.sleep(period * 3 + 1)
msg = "[test_low_energy_manage] %s: low_energy_manage.stop()."
assert low_energy_manage.stop(), msg % "FAILED"
print(msg % "SUCCESS")
res["success"] += 1
res["all"] = res["success"] + res["failed"]
print("[test_low_energy_manage] ALL: %s SUCCESS: %s, FAILED: %s." % (res["all"], res["success"], res["failed"]))
def test_tracker(): def test_tracker():
tracker() tracker()
def main(): def main():
# test_logger()
# test_settings() # test_settings()
# test_battery()
# test_history()
# test_location()
# test_quecthing()
# test_aliyuniot()
# test_remote()
# test_low_energy_manage()
test_tracker() test_tracker()

View File

@ -13,28 +13,24 @@
# limitations under the License. # limitations under the License.
import sim import sim
import utime
import modem import modem
import checkNet
import dataCall import dataCall
from misc import Power from usr.modules.sensor import Sensor
from usr.modules.battery import Battery
from usr.led import LED from usr.modules.ota import OTAFileClear
from usr.sensor import Sensor from usr.modules.history import History
from usr.battery import Battery from usr.modules.logging import getLogger
from usr.ota import OTAFileClear from usr.modules.mpower import LowEnergyManage
from usr.history import History from usr.modules.remote import RemotePublish, RemoteSubscribe
from usr.logging import getLogger from usr.modules.aliyunIot import AliYunIot, AliObjectModel
from usr.mpower import LowEnergyManage from usr.modules.quecthing import QuecThing, QuecObjectModel
from usr.remote import RemotePublish, RemoteSubscribe from usr.modules.location import Location
from usr.aliyunIot import AliYunIot, AliObjectModel
from usr.quecthing import QuecThing, QuecObjectModel
from usr.common import Singleton, LOWENERGYMAP
from usr.location import Location, GPSMatch, GPSParse, _loc_method
from usr.settings import PROJECT_NAME, PROJECT_VERSION, \ from usr.settings import PROJECT_NAME, PROJECT_VERSION, \
DEVICE_FIRMWARE_NAME, DEVICE_FIRMWARE_VERSION, settings, UserConfig, \ DEVICE_FIRMWARE_NAME, DEVICE_FIRMWARE_VERSION, settings, SYSConfig
SYSConfig, Settings from usr.tracker_collector import Collector
from usr.tracker_controller import Controller
from usr.tracker_devicecheck import DeviceCheck
try: try:
from misc import USB from misc import USB
@ -50,16 +46,6 @@ log = getLogger(__name__)
sim.setSimDet(1, 0) sim.setSimDet(1, 0)
ALERTCODE = {
20000: "fault_alert",
30002: "low_power_alert",
30003: "over_speed_alert",
30004: "sim_abnormal_alert",
30005: "disassemble_alert",
40000: "drive_behavior_alert",
50001: "sos_alert",
}
def pwk_callback(status): def pwk_callback(status):
if status == 0: if status == 0:
@ -93,809 +79,6 @@ def nw_callback(args):
pass pass
class Collector(Singleton):
def __init__(self):
self.__controller = None
self.__devicecheck = None
self.__battery = None
self.__sensor = None
self.__locator = None
self.__history = None
self.__gps_match = GPSMatch()
self.__gps_parse = GPSParse()
def __format_loc_method(self, data):
loc_method = "%04d" % int(bin(data)[2:])
gps = bool(int(loc_method[-1]))
cell = bool(int(loc_method[-2]))
wifi = bool(int(loc_method[-3]))
loc_method = {
"gps": gps,
"cell": cell,
"wifi": wifi,
}
return loc_method
def __device_speed_check(self):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
if not self.__locator:
raise TypeError("self.__locator is not registered")
current_settings = self.__controller.settings_get()
alert_data = {
"current_speed": 0.00
}
if current_settings["user_cfg"]["sw_over_speed_alert"] is True:
if self.__locator.gps:
gps_data = self.__locator.gps.read()[1]
vtg_data = self.__gps_match.GxVTG(gps_data)
speed = self.__gps_parse.GxVTG_speed(vtg_data)
if speed and float(speed) >= current_settings["user_cfg"]["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)
log.debug("__device_speed_check: %s" % str(alert_data))
return alert_data
def __get_local_time(self):
return str(utime.mktime(utime.localtime()) * 1000)
def __get_alert_data(self, alert_code, alert_info):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
current_settings = self.__controller.settings_get()
alert_data = {}
if ALERTCODE.get(alert_code):
alert_status = current_settings.get("user_cfg", {}).get("sw_" + ALERTCODE.get(alert_code))
if alert_status:
alert_data = {ALERTCODE.get(alert_code): alert_info}
else:
log.warn("%s switch is %s" % (ALERTCODE.get(alert_code), alert_status))
else:
log.error("altercode (%s) is not exists. alert info: %s" % (alert_code, alert_info))
return alert_data
def __init_low_energy_method(self, period):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
current_settings = self.__controller.settings_get()
device_model = modem.getDevModel()
support_methds = LOWENERGYMAP.get(device_model, [])
method = "NULL"
if support_methds:
if period >= current_settings["user_cfg"]["work_mode_timeline"]:
if "PSM" in support_methds:
method = "PSM"
elif "POWERDOWN" in support_methds:
method = "POWERDOWN"
elif "PM" in support_methds:
method = "PM"
else:
if "PM" in support_methds:
method = "PM"
log.debug("__init_low_energy_method: %s" % method)
return method
def __get_ali_loc_data(self, loc_method, loc_data):
res = {"GeoLocation": {}}
if loc_method == 0x1:
gga_data = self.__gps_match.GxGGA(loc_data)
data = {}
if gga_data:
Latitude = self.__gps_parse.GxGGA_latitude(gga_data)
if Latitude:
data["Latitude"] = float("%.2f" % float(Latitude))
Longtitude = self.__gps_parse.GxGGA_longtitude(gga_data)
if Longtitude:
data["Longtitude"] = float("%.2f" % float(Longtitude))
Altitude = self.__gps_parse.GxGGA_altitude(gga_data)
if Altitude:
data["Altitude"] = float("%.2f" % float(Altitude))
if data:
data["CoordinateSystem"] = 1
res = {"GeoLocation": data}
elif loc_method in (0x2, 0x4):
if loc_data:
res["GeoLocation"] = {
"Longtitude": round(loc_data[0], 2),
"Latitude": round(loc_data[1], 2),
# "Altitude": 0.0,
"CoordinateSystem": 1
}
return res
def __get_quec_loc_data(self, loc_method, loc_data):
if loc_method == 0x1:
res = {"gps": []}
r = self.__gps_match.GxRMC(loc_data)
if r:
res["gps"].append(r)
r = self.__gps_match.GxGGA(loc_data)
if r:
res["gps"].append(r)
r = self.__gps_match.GxVTG(loc_data)
if r:
res["gps"].append(r)
return res
elif loc_method == 0x2:
return {"non_gps": ["LBS"]}
elif loc_method == 0x4:
return {"non_gps": []}
def __get_loc_data(self, loc_method, loc_data):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
current_settings = self.__controller.settings_get()
if current_settings["sys"]["cloud"] & SYSConfig._cloud.quecIot:
return self.__get_quec_loc_data(loc_method, loc_data)
elif current_settings["sys"]["cloud"] & SYSConfig._cloud.AliYun:
return self.__get_ali_loc_data(loc_method, loc_data)
return {}
def __read_battery(self):
if not self.__battery:
raise TypeError("self.__battery is not registered.")
res = {}
self.__battery.set_temp(20)
energy = self.__battery.get_energy()
res = {
"energy": energy,
"voltage": self.__battery.get_voltage(),
}
current_settings = self.__controller.settings_get()
if energy <= current_settings["user_cfg"]["low_power_alert_threshold"]:
alert_data = self.__get_alert_data(30002, {"local_time": self.__get_local_time()})
res.update(alert_data)
return res
def __read_sensor(self):
return {}
def __read_location(self):
if not self.__locator:
raise TypeError("self.__locator is not registered.")
res = {}
current_settings = self.__controller.settings_get()
# Get cloud location data
if current_settings["user_cfg"].get("loc_method"):
cfg_loc_method = current_settings["user_cfg"].get("loc_method")
elif current_settings["sys"]["base_cfg"]["LocConfig"]:
cfg_loc_method = current_settings["LocConfig"]["loc_method"]
else:
cfg_loc_method = 7
loc_info = self.__locator.read(cfg_loc_method)
if loc_info:
loc_method_dict = {v: k for k, v in _loc_method.__dict__.items()}
loc_data = None
for loc_method in loc_method_dict.keys():
if loc_info.get(loc_method):
log.debug("Location Data loc_method: %s" % loc_method_dict[loc_method])
loc_data = loc_info[loc_method]
if loc_method == _loc_method.gps:
gga_satellite = self.__gps_parse.GxGGA_satellite_num(self.__gps_match.GxGGA(loc_data))
log.debug("GxGGA Satellite Num %s" % gga_satellite)
gsv_satellite = self.__gps_parse.GxGSV_satellite_num(self.__gps_match.GxGSV(loc_data))
log.debug("GxGSV Satellite Num %s" % gsv_satellite)
break
if loc_data:
res = self.__get_loc_data(loc_method, loc_data)
return res
def add_module(self, module):
if isinstance(module, Controller):
self.__controller = module
return True
elif isinstance(module, DeviceCheck):
self.__devicecheck = module
return True
elif isinstance(module, Battery):
self.__battery = module
return True
elif isinstance(module, Sensor):
self.__sensor = module
return True
elif isinstance(module, Location):
self.__locator = module
return True
elif isinstance(module, History):
self.__history = module
return True
return False
def read_module_data(self, module):
if module == "Battery":
return self.__read_battery()
elif module == "Sensor":
return self.__read_sensor()
elif module == "Location":
return self.__read_location()
elif module == "History":
return self.__read_history()
def device_status_get(self):
if not self.__devicecheck:
raise TypeError("self.__devicecheck is not registered.")
if not self.__controller:
raise TypeError("self.__controller is not registered.")
device_status_data = {}
device_module_status = {}
alert_code = 20000
net_status = self.__devicecheck.net()
location_status = self.__devicecheck.location()
temp_status = self.__devicecheck.temp()
light_status = self.__devicecheck.light()
triaxial_status = self.__devicecheck.triaxial()
mike_status = self.__devicecheck.mike()
device_module_status["net"] = 1 if net_status == (3, 1) else 0
device_module_status["location"] = 1 if location_status else 0
# TODO: Check Sensor.
if temp_status is not None:
device_module_status["temp_sensor"] = 1 if temp_status else 0
if light_status is not None:
device_module_status["light_sensor"] = 1 if temp_status else 0
if triaxial_status is not None:
device_module_status["move_sensor"] = 1 if temp_status else 0
if mike_status is not None:
device_module_status["mike"] = 1 if temp_status else 0
device_status = True
# TODO: Led Show
if net_status == (3, 1) and location_status is True and \
(temp_status is True or temp_status is None) and \
(light_status is True or light_status is None) and \
(triaxial_status is True or triaxial_status is None) and \
(mike_status is True or mike_status is None):
# self.__controller.running_led_show(0.5)
device_status = True
else:
# self.__controller.running_led_show(2)
device_status = False
if device_status is False:
device_status_data = self.__get_alert_data(alert_code, {"local_time": self.__get_local_time()})
device_status_data.update({"device_module_status": device_module_status})
return device_status_data
def device_status_check(self):
device_status_check_res = self.device_status_get()
self.device_data_report(event_data=device_status_check_res)
def device_data_get(self, power_switch=True):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
current_settings = self.__controller.settings_get()
device_data = {
"power_switch": power_switch,
"local_time": self.__get_local_time(),
}
# Get ota status & drive behiver code
device_data.update({
"ota_status": current_settings["user_cfg"]["ota_status"],
"drive_behavior_code": current_settings["user_cfg"]["drive_behavior_code"],
})
# Get user settings info
device_data.update(current_settings["user_cfg"])
# Format loc method
device_data.update({"loc_method": self.__format_loc_method(current_settings["user_cfg"]["loc_method"])})
# Get cloud location data
device_data.update(self.__read_location())
# Get gps speed
device_data.update(self.__device_speed_check())
# Get battery energy
device_data.update(self.__read_battery())
# TODO: Add other machine info.
return device_data
def device_data_report(self, power_switch=True, event_data={}, msg=""):
# TODO: msg to mark post data source
if not self.__controller:
raise TypeError("self.__controller is not registered.")
device_data = self.device_data_get(power_switch)
if event_data:
device_data.update(event_data)
post_res = self.__controller.remote_post_data(device_data)
# OTA status rst
current_settings = self.__controller.settings_get() if self.__controller else {}
ota_status_info = current_settings["user_cfg"]["ota_status"]
if ota_status_info["upgrade_status"] in (3, 4):
self.ota_status_reset()
return post_res
def ota_status_reset(self):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
current_settings = self.__controller.settings_get()
ota_status_info = current_settings["user_cfg"]["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)
self.__controller.settings_set("ota_status", ota_status_info)
if current_settings["user_cfg"]["user_ota_action"] != -1:
self.__controller.settings_set("user_ota_action", -1)
def report_history(self):
if not self.__history:
raise TypeError("self.__history is not registered.")
if not self.__controller:
raise TypeError("self.__controller is not registered.")
res = True
hist = self.__history.read()
if hist["data"]:
pt_count = 0
for i, data in enumerate(hist["data"]):
pt_count += 1
if not self.__controller.remote_post_data(data):
res = False
break
hist["data"] = hist["data"][pt_count:]
if hist["data"]:
# Flush data in hist-dictionary to tracker_data.hist file.
self.__history.write(hist["data"])
return res
# Do cloud event downlink option by controller
def event_option(self, *args, **kwargs):
# TODO: Data Type Passthrough (Not Support Now).
return False
def event_done(self, *args, **kwargs):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
try:
setting_flag = 0
for arg in args:
if hasattr(UserConfig, arg[0]):
set_res = self.__controller.settings_set(arg[0], arg[1])
if set_res and setting_flag == 0:
setting_flag = 1
if hasattr(self, arg[0]):
getattr(self, arg[0])(arg[1])
if setting_flag:
self.__controller.settings_save()
return True
except:
return False
def event_query(self, *args, **kwargs):
return self.device_data_report()
def event_ota_plain(self, *args, **kwargs):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
current_settings = settings.get()
if current_settings["user_cfg"]["sw_ota"]:
if current_settings["user_cfg"]["sw_ota_auto_upgrade"] or current_settings["user_cfg"]["user_ota_action"] != -1:
if current_settings["user_cfg"]["sw_ota_auto_upgrade"]:
ota_action_val = 1
else:
if current_settings["user_cfg"]["user_ota_action"] != -1:
ota_action_val = current_settings["user_cfg"]["user_ota_action"]
else:
return
if current_settings["sys"]["cloud"] == SYSConfig._cloud.quecIot or \
current_settings["sys"]["cloud"] == SYSConfig._cloud.AliYun:
log.debug("ota_plain args: %s, kwargs: %s" % (str(args), str(kwargs)))
self.__controller.remote_ota_action(action=ota_action_val, module=kwargs.get("module"))
else:
log.error("Current Cloud (0x%X) Not Supported!" % current_settings["sys"]["cloud"])
def event_ota_file_download(self, *args, **kwargs):
# OAT MQTT File Download Is Not Supported Yet.
return False
def power_switch(self, flag=None):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
self.event_query(power_switch=flag)
if flag is False:
self.__controller.power_down()
def user_ota_action(self, action):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
current_settings = self.__controller.settings_get()
if current_settings["user_cfg"]["sw_ota"] and current_settings["user_cfg"]["sw_ota_auto_upgrade"] is False:
ota_status_info = current_settings["user_cfg"]["ota_status"]
if ota_status_info["upgrade_status"] == 1 and current_settings["user_cfg"]["user_ota_action"] == -1:
self.__controller.settings_set("user_ota_action", action)
self.__controller.settings_save()
self.__controller.remote_ota_check()
def ota_status(self, upgrade_info=None):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
current_settings = self.__controller.settings_get()
if upgrade_info and current_settings["user_cfg"]["sw_ota"]:
ota_status_info = current_settings["user_cfg"]["ota_status"]
if ota_status_info["sys_target_version"] == "--" and ota_status_info["app_target_version"] == "--":
ota_info = {}
if upgrade_info[0] == DEVICE_FIRMWARE_NAME:
ota_info["upgrade_module"] = 1
ota_info["sys_target_version"] = upgrade_info[2]
elif upgrade_info[0] == 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)
self.__controller.settings_set("ota_status", ota_status_info)
self.__controller.settings_save()
def power_restart(self, flag):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
self.event_query(power_switch=False)
self.__controller.power_restart()
def work_cycle_period(self, period):
# Reset work_cycle_period & Reset RTC
if not self.__controller:
raise TypeError("self.__controller is not registered.")
self.__controller.low_energy_stop()
self.__controller.low_energy_set_period(period)
method = self.__init_low_energy_method(period)
self.__controller.low_energy_set_method(method)
self.__controller.low_energy_init()
self.__controller.low_energy_start()
def cloud_init_params(self, params):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
self.__controller.settings_set("cloud", params)
self.__controller.settings_save()
def low_engery_option(self, low_energy_method):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
self.report_history()
current_settings = self.__controller.settings_get()
if current_settings["user_cfg"]["work_mode"] == UserConfig._work_mode.intelligent:
speed_info = self.__device_speed_check()
if speed_info.get("current_speed") > 0:
self.device_data_report()
else:
self.device_data_report()
self.__controller.low_energy_start()
if low_energy_method == "PSM":
# TODO: PSM option.
pass
elif low_energy_method == "POWERDOWN":
self.__controller.power_down()
def update(self, observable, *args, **kwargs):
if isinstance(observable, LowEnergyManage):
log.debug("Low Energy RTC Method: %s" % args[1])
self.low_engery_option(args[1])
class DeviceCheck(object):
def __init__(self):
self.__locator = None
self.__sensor = None
def add_module(self, module):
if isinstance(module, Location):
self.__locator = module
return True
elif isinstance(module, Sensor):
self.__sensor = module
return True
return False
def net(self):
current_settings = settings.get()
checknet = checkNet.CheckNetwork(PROJECT_NAME, PROJECT_VERSION)
timeout = current_settings.get("sys", {}).get("checknet_timeout", 60)
check_res = checknet.wait_network_connected(timeout)
log.debug("DeviceCheck.net res: %s" % str(check_res))
return check_res
def location(self):
# return True if OK
if not self.__locator:
raise TypeError("self.__locator is not registered")
current_settings = settings.get()
retry = 0
gps_data = None
sleep_time = 1
while retry < 5:
if current_settings["user_cfg"].get("loc_method"):
loc_method = current_settings["user_cfg"].get("loc_method")
elif current_settings["sys"]["base_cfg"]["LocConfig"]:
loc_method = current_settings["LocConfig"].get("loc_method")
else:
loc_method = 7
gps_data = self.__locator.read(loc_method)
if gps_data:
break
else:
retry += 1
utime.sleep(sleep_time)
sleep_time *= 2
if gps_data:
return True
return False
def temp(self):
# return True if OK
return None
def light(self):
# return True if OK
return None
def triaxial(self):
# return True if OK
return None
def mike(self):
# return True if OK
return None
class Controller(Singleton):
def __init__(self):
self.__remote_pub = None
self.__settings = None
self.__low_energy = None
self.__energy_led = None
self.__running_led = None
self.__power_key = None
self.__usb = None
self.__data_call = None
self.__ota_file_clear = None
def add_module(self, module, led_type=None, callback=None):
if isinstance(module, RemotePublish):
self.__remote_pub = module
return True
elif isinstance(module, Settings):
self.__settings = module
return True
elif isinstance(module, LowEnergyManage):
self.__low_energy = module
return True
elif isinstance(module, OTAFileClear):
self.__ota_file_clear = module
return True
elif isinstance(module, LED):
if led_type == "energy":
self.__energy_led = module
return True
elif led_type == "running":
self.running_led = module
return True
elif isinstance(module, PowerKey):
self.__power_key = module
if callback:
self.__power_key.powerKeyEventRegister(callback)
return True
elif isinstance(module, USB):
self.__usb = module
if callback:
self.__usb.setCallback(callback)
return True
elif module is dataCall:
self.__data_call = module
if callback:
self.__data_call.setCallback(callback)
return True
return False
def set_remote_pub(self, remote_pub):
if isinstance(remote_pub, RemotePublish):
self.__remote_pub = remote_pub
return True
return False
def set_settings(self, settings):
if isinstance(settings, Settings):
self.__settings = settings
return True
return False
def set_low_energy(self, low_energy_manage):
if isinstance(low_energy_manage, LowEnergyManage):
self.__low_energy = low_energy_manage
return True
return False
def set_energy_led(self, energy_led):
if isinstance(energy_led, LED):
self.__energy_led = energy_led
return True
return False
def set_running_led(self, running_led):
if isinstance(running_led, LED):
self.__running_led = running_led
return True
return False
def set_power_key(self, power_key, power_key_cb):
if isinstance(power_key, PowerKey):
self.__power_key = power_key
if power_key_cb:
self.__power_key.powerKeyEventRegister(power_key_cb)
return True
return False
def set_usb(self, usb, usb_cb):
if isinstance(usb, USB):
self.__usb = usb
if usb_cb:
self.__usb.setCallback(usb_cb)
return True
return False
def set_data_call(self, data_call, data_call_cb):
if data_call is dataCall:
self.__data_call = data_call
if data_call_cb:
self.__data_call.setCallback(data_call_cb)
return True
return False
def set_ota_file_clear(self, ota_file_clear):
if isinstance(ota_file_clear, OTAFileClear):
self.__ota_file_clear = ota_file_clear
return True
return False
def settings_get(self):
if not self.__settings:
raise TypeError("self.__settings is not registered.")
return self.__settings.get()
def settings_set(self, key, value):
if not self.__settings:
raise TypeError("self.__settings is not registered.")
if key == "loc_method":
v = "0b"
v += str(int(value.get(3, 0)))
v += str(int(value.get(2, 0)))
v += str(int(value.get(1, 0)))
value = int(v, 2)
set_res = self.__settings.set(key, value)
log.debug("__settings_set key: %s, val: %s, set_res: %s" % (key, value, set_res))
return set_res
def settings_save(self):
if not self.__settings:
raise TypeError("self.__settings is not registered.")
return self.__settings.save()
def power_restart(self):
Power.powerRestart()
def power_down(self):
Power.powerDown()
def remote_post_data(self, data):
if not self.__remote_pub:
raise TypeError("self.__remote_pub is not registered.")
return self.__remote_pub.post_data(data)
def remote_ota_check(self):
if not self.__remote_pub:
raise TypeError("self.__remote_pub is not registered.")
return self.__remote_pub.cloud_ota_check()
def remote_ota_action(self, action, module):
if not self.__remote_pub:
raise TypeError("self.__remote_pub is not registered.")
return self.__remote_pub.cloud_ota_action(action, module)
def low_energy_set_period(self, period):
if not self.__low_energy:
raise TypeError("self.__low_energy is not registered.")
return self.__low_energy.set_period(period)
def low_energy_set_method(self, method):
if not self.__low_energy:
raise TypeError("self.__low_energy is not registered.")
return self.__low_energy.set_low_energy_method(method)
def low_energy_init(self):
if not self.__low_energy:
raise TypeError("self.__low_energy is not registered.")
return self.__low_energy.low_energy_init()
def low_energy_start(self):
if not self.__low_energy:
raise TypeError("self.__low_energy is not registered.")
return self.__low_energy.start()
def low_energy_stop(self):
if not self.__low_energy:
raise TypeError("self.__low_energy is not registered.")
return self.__low_energy.stop()
def ota_file_clean(self):
if not self.__ota_file_clear:
raise TypeError("self.__ota_file_clear is not registered.")
self.__ota_file_clear.file_clear()
def running_led_show(self, period):
if not self.__running_led:
raise TypeError("self.__running_led is not registered.")
self.__running_led.set_period(period)
return self.__running_led.led_timer_start()
def energy_led_show(self, period):
if not self.energy_led_show:
raise TypeError("self.energy_led_show is not registered.")
self.__energy_led.set_period(period)
return self.__energy_led.led_timer_start()
def tracker(): def tracker():
current_settings = settings.get() current_settings = settings.get()

575
code/tracker_collector.py Normal file
View File

@ -0,0 +1,575 @@
# 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.
import utime
import modem
from usr.moudles.sensor import Sensor
from usr.moudles.battery import Battery
from usr.moudles.history import History
from usr.moudles.logging import getLogger
from usr.moudles.mpower import LowEnergyManage
from usr.moudles.common import Singleton, LOWENERGYMAP
from usr.moudles.location import Location, GPSMatch, GPSParse, _loc_method
from usr.settings import PROJECT_NAME, DEVICE_FIRMWARE_NAME, settings, UserConfig, SYSConfig
from usr.tracker_controller import Controller
from usr.tracker_devicecheck import DeviceCheck
log = getLogger(__name__)
ALERTCODE = {
20000: "fault_alert",
30002: "low_power_alert",
30003: "over_speed_alert",
30004: "sim_abnormal_alert",
30005: "disassemble_alert",
40000: "drive_behavior_alert",
50001: "sos_alert",
}
class Collector(Singleton):
def __init__(self):
self.__controller = None
self.__devicecheck = None
self.__battery = None
self.__sensor = None
self.__locator = None
self.__history = None
self.__gps_match = GPSMatch()
self.__gps_parse = GPSParse()
def __format_loc_method(self, data):
loc_method = "%04d" % int(bin(data)[2:])
gps = bool(int(loc_method[-1]))
cell = bool(int(loc_method[-2]))
wifi = bool(int(loc_method[-3]))
loc_method = {
"gps": gps,
"cell": cell,
"wifi": wifi,
}
return loc_method
def __device_speed_check(self):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
if not self.__locator:
raise TypeError("self.__locator is not registered")
current_settings = self.__controller.settings_get()
alert_data = {
"current_speed": 0.00
}
if current_settings["user_cfg"]["sw_over_speed_alert"] is True:
if self.__locator.gps:
gps_data = self.__locator.gps.read()[1]
vtg_data = self.__gps_match.GxVTG(gps_data)
speed = self.__gps_parse.GxVTG_speed(vtg_data)
if speed and float(speed) >= current_settings["user_cfg"]["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)
log.debug("__device_speed_check: %s" % str(alert_data))
return alert_data
def __get_local_time(self):
return str(utime.mktime(utime.localtime()) * 1000)
def __get_alert_data(self, alert_code, alert_info):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
current_settings = self.__controller.settings_get()
alert_data = {}
if ALERTCODE.get(alert_code):
alert_status = current_settings.get("user_cfg", {}).get("sw_" + ALERTCODE.get(alert_code))
if alert_status:
alert_data = {ALERTCODE.get(alert_code): alert_info}
else:
log.warn("%s switch is %s" % (ALERTCODE.get(alert_code), alert_status))
else:
log.error("altercode (%s) is not exists. alert info: %s" % (alert_code, alert_info))
return alert_data
def __init_low_energy_method(self, period):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
current_settings = self.__controller.settings_get()
device_model = modem.getDevModel()
support_methds = LOWENERGYMAP.get(device_model, [])
method = "NULL"
if support_methds:
if period >= current_settings["user_cfg"]["work_mode_timeline"]:
if "PSM" in support_methds:
method = "PSM"
elif "POWERDOWN" in support_methds:
method = "POWERDOWN"
elif "PM" in support_methds:
method = "PM"
else:
if "PM" in support_methds:
method = "PM"
log.debug("__init_low_energy_method: %s" % method)
return method
def __get_ali_loc_data(self, loc_method, loc_data):
res = {"GeoLocation": {}}
if loc_method == 0x1:
gga_data = self.__gps_match.GxGGA(loc_data)
data = {}
if gga_data:
Latitude = self.__gps_parse.GxGGA_latitude(gga_data)
if Latitude:
data["Latitude"] = float("%.2f" % float(Latitude))
Longtitude = self.__gps_parse.GxGGA_longtitude(gga_data)
if Longtitude:
data["Longtitude"] = float("%.2f" % float(Longtitude))
Altitude = self.__gps_parse.GxGGA_altitude(gga_data)
if Altitude:
data["Altitude"] = float("%.2f" % float(Altitude))
if data:
data["CoordinateSystem"] = 1
res = {"GeoLocation": data}
elif loc_method in (0x2, 0x4):
if loc_data:
res["GeoLocation"] = {
"Longtitude": round(loc_data[0], 2),
"Latitude": round(loc_data[1], 2),
# "Altitude": 0.0,
"CoordinateSystem": 1
}
return res
def __get_quec_loc_data(self, loc_method, loc_data):
if loc_method == 0x1:
res = {"gps": []}
r = self.__gps_match.GxRMC(loc_data)
if r:
res["gps"].append(r)
r = self.__gps_match.GxGGA(loc_data)
if r:
res["gps"].append(r)
r = self.__gps_match.GxVTG(loc_data)
if r:
res["gps"].append(r)
return res
elif loc_method == 0x2:
return {"non_gps": ["LBS"]}
elif loc_method == 0x4:
return {"non_gps": []}
def __get_loc_data(self, loc_method, loc_data):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
current_settings = self.__controller.settings_get()
if current_settings["sys"]["cloud"] & SYSConfig._cloud.quecIot:
return self.__get_quec_loc_data(loc_method, loc_data)
elif current_settings["sys"]["cloud"] & SYSConfig._cloud.AliYun:
return self.__get_ali_loc_data(loc_method, loc_data)
return {}
def __read_battery(self):
if not self.__battery:
raise TypeError("self.__battery is not registered.")
res = {}
self.__battery.set_temp(20)
energy = self.__battery.get_energy()
res = {
"energy": energy,
"voltage": self.__battery.get_voltage(),
}
current_settings = self.__controller.settings_get()
if energy <= current_settings["user_cfg"]["low_power_alert_threshold"]:
alert_data = self.__get_alert_data(30002, {"local_time": self.__get_local_time()})
res.update(alert_data)
return res
def __read_sensor(self):
return {}
def __read_location(self):
if not self.__locator:
raise TypeError("self.__locator is not registered.")
res = {}
current_settings = self.__controller.settings_get()
# Get cloud location data
if current_settings["user_cfg"].get("loc_method"):
cfg_loc_method = current_settings["user_cfg"].get("loc_method")
elif current_settings["sys"]["base_cfg"]["LocConfig"]:
cfg_loc_method = current_settings["LocConfig"]["loc_method"]
else:
cfg_loc_method = 7
loc_info = self.__locator.read(cfg_loc_method)
if loc_info:
loc_method_dict = {v: k for k, v in _loc_method.__dict__.items()}
loc_data = None
for loc_method in loc_method_dict.keys():
if loc_info.get(loc_method):
log.debug("Location Data loc_method: %s" % loc_method_dict[loc_method])
loc_data = loc_info[loc_method]
if loc_method == _loc_method.gps:
gga_satellite = self.__gps_parse.GxGGA_satellite_num(self.__gps_match.GxGGA(loc_data))
log.debug("GxGGA Satellite Num %s" % gga_satellite)
gsv_satellite = self.__gps_parse.GxGSV_satellite_num(self.__gps_match.GxGSV(loc_data))
log.debug("GxGSV Satellite Num %s" % gsv_satellite)
break
if loc_data:
res = self.__get_loc_data(loc_method, loc_data)
return res
def add_module(self, module):
if isinstance(module, Controller):
self.__controller = module
return True
elif isinstance(module, DeviceCheck):
self.__devicecheck = module
return True
elif isinstance(module, Battery):
self.__battery = module
return True
elif isinstance(module, Sensor):
self.__sensor = module
return True
elif isinstance(module, Location):
self.__locator = module
return True
elif isinstance(module, History):
self.__history = module
return True
return False
def read_module_data(self, module):
if module == "Battery":
return self.__read_battery()
elif module == "Sensor":
return self.__read_sensor()
elif module == "Location":
return self.__read_location()
elif module == "History":
return self.__read_history()
def device_status_get(self):
if not self.__devicecheck:
raise TypeError("self.__devicecheck is not registered.")
if not self.__controller:
raise TypeError("self.__controller is not registered.")
device_status_data = {}
device_module_status = {}
alert_code = 20000
net_status = self.__devicecheck.net()
location_status = self.__devicecheck.location()
temp_status = self.__devicecheck.temp()
light_status = self.__devicecheck.light()
triaxial_status = self.__devicecheck.triaxial()
mike_status = self.__devicecheck.mike()
device_module_status["net"] = 1 if net_status == (3, 1) else 0
device_module_status["location"] = 1 if location_status else 0
# TODO: Check Sensor.
if temp_status is not None:
device_module_status["temp_sensor"] = 1 if temp_status else 0
if light_status is not None:
device_module_status["light_sensor"] = 1 if temp_status else 0
if triaxial_status is not None:
device_module_status["move_sensor"] = 1 if temp_status else 0
if mike_status is not None:
device_module_status["mike"] = 1 if temp_status else 0
device_status = True
# TODO: Led Show
if net_status == (3, 1) and location_status is True and \
(temp_status is True or temp_status is None) and \
(light_status is True or light_status is None) and \
(triaxial_status is True or triaxial_status is None) and \
(mike_status is True or mike_status is None):
# self.__controller.running_led_show(0.5)
device_status = True
else:
# self.__controller.running_led_show(2)
device_status = False
if device_status is False:
device_status_data = self.__get_alert_data(alert_code, {"local_time": self.__get_local_time()})
device_status_data.update({"device_module_status": device_module_status})
return device_status_data
def device_status_check(self):
device_status_check_res = self.device_status_get()
self.device_data_report(event_data=device_status_check_res)
def device_data_get(self, power_switch=True):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
current_settings = self.__controller.settings_get()
device_data = {
"power_switch": power_switch,
"local_time": self.__get_local_time(),
}
# Get ota status & drive behiver code
device_data.update({
"ota_status": current_settings["user_cfg"]["ota_status"],
"drive_behavior_code": current_settings["user_cfg"]["drive_behavior_code"],
})
# Get user settings info
device_data.update(current_settings["user_cfg"])
# Format loc method
device_data.update({"loc_method": self.__format_loc_method(current_settings["user_cfg"]["loc_method"])})
# Get cloud location data
device_data.update(self.__read_location())
# Get gps speed
device_data.update(self.__device_speed_check())
# Get battery energy
device_data.update(self.__read_battery())
# TODO: Add other machine info.
return device_data
def device_data_report(self, power_switch=True, event_data={}, msg=""):
# TODO: msg to mark post data source
if not self.__controller:
raise TypeError("self.__controller is not registered.")
device_data = self.device_data_get(power_switch)
if event_data:
device_data.update(event_data)
post_res = self.__controller.remote_post_data(device_data)
# OTA status rst
current_settings = self.__controller.settings_get() if self.__controller else {}
ota_status_info = current_settings["user_cfg"]["ota_status"]
if ota_status_info["upgrade_status"] in (3, 4):
self.ota_status_reset()
return post_res
def ota_status_reset(self):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
current_settings = self.__controller.settings_get()
ota_status_info = current_settings["user_cfg"]["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)
self.__controller.settings_set("ota_status", ota_status_info)
if current_settings["user_cfg"]["user_ota_action"] != -1:
self.__controller.settings_set("user_ota_action", -1)
def report_history(self):
if not self.__history:
raise TypeError("self.__history is not registered.")
if not self.__controller:
raise TypeError("self.__controller is not registered.")
res = True
hist = self.__history.read()
if hist["data"]:
pt_count = 0
for i, data in enumerate(hist["data"]):
pt_count += 1
if not self.__controller.remote_post_data(data):
res = False
break
hist["data"] = hist["data"][pt_count:]
if hist["data"]:
# Flush data in hist-dictionary to tracker_data.hist file.
self.__history.write(hist["data"])
return res
# Do cloud event downlink option by controller
def event_option(self, *args, **kwargs):
# TODO: Data Type Passthrough (Not Support Now).
return False
def event_done(self, *args, **kwargs):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
try:
setting_flag = 0
for arg in args:
if hasattr(UserConfig, arg[0]):
set_res = self.__controller.settings_set(arg[0], arg[1])
if set_res and setting_flag == 0:
setting_flag = 1
if hasattr(self, arg[0]):
getattr(self, arg[0])(arg[1])
if setting_flag:
self.__controller.settings_save()
return True
except:
return False
def event_query(self, *args, **kwargs):
return self.device_data_report()
def event_ota_plain(self, *args, **kwargs):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
current_settings = settings.get()
if current_settings["user_cfg"]["sw_ota"]:
if current_settings["user_cfg"]["sw_ota_auto_upgrade"] or current_settings["user_cfg"]["user_ota_action"] != -1:
if current_settings["user_cfg"]["sw_ota_auto_upgrade"]:
ota_action_val = 1
else:
if current_settings["user_cfg"]["user_ota_action"] != -1:
ota_action_val = current_settings["user_cfg"]["user_ota_action"]
else:
return
if current_settings["sys"]["cloud"] == SYSConfig._cloud.quecIot or \
current_settings["sys"]["cloud"] == SYSConfig._cloud.AliYun:
log.debug("ota_plain args: %s, kwargs: %s" % (str(args), str(kwargs)))
self.__controller.remote_ota_action(action=ota_action_val, module=kwargs.get("module"))
else:
log.error("Current Cloud (0x%X) Not Supported!" % current_settings["sys"]["cloud"])
def event_ota_file_download(self, *args, **kwargs):
# OAT MQTT File Download Is Not Supported Yet.
return False
def power_switch(self, flag=None):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
self.event_query(power_switch=flag)
if flag is False:
self.__controller.power_down()
def user_ota_action(self, action):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
current_settings = self.__controller.settings_get()
if current_settings["user_cfg"]["sw_ota"] and current_settings["user_cfg"]["sw_ota_auto_upgrade"] is False:
ota_status_info = current_settings["user_cfg"]["ota_status"]
if ota_status_info["upgrade_status"] == 1 and current_settings["user_cfg"]["user_ota_action"] == -1:
self.__controller.settings_set("user_ota_action", action)
self.__controller.settings_save()
self.__controller.remote_ota_check()
def ota_status(self, upgrade_info=None):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
current_settings = self.__controller.settings_get()
if upgrade_info and current_settings["user_cfg"]["sw_ota"]:
ota_status_info = current_settings["user_cfg"]["ota_status"]
if ota_status_info["sys_target_version"] == "--" and ota_status_info["app_target_version"] == "--":
ota_info = {}
if upgrade_info[0] == DEVICE_FIRMWARE_NAME:
ota_info["upgrade_module"] = 1
ota_info["sys_target_version"] = upgrade_info[2]
elif upgrade_info[0] == 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)
self.__controller.settings_set("ota_status", ota_status_info)
self.__controller.settings_save()
def power_restart(self, flag):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
self.event_query(power_switch=False)
self.__controller.power_restart()
def work_cycle_period(self, period):
# Reset work_cycle_period & Reset RTC
if not self.__controller:
raise TypeError("self.__controller is not registered.")
self.__controller.low_energy_stop()
self.__controller.low_energy_set_period(period)
method = self.__init_low_energy_method(period)
self.__controller.low_energy_set_method(method)
self.__controller.low_energy_init()
self.__controller.low_energy_start()
def cloud_init_params(self, params):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
self.__controller.settings_set("cloud", params)
self.__controller.settings_save()
def low_engery_option(self, low_energy_method):
if not self.__controller:
raise TypeError("self.__controller is not registered.")
self.report_history()
current_settings = self.__controller.settings_get()
if current_settings["user_cfg"]["work_mode"] == UserConfig._work_mode.intelligent:
speed_info = self.__device_speed_check()
if speed_info.get("current_speed") > 0:
self.device_data_report()
else:
self.device_data_report()
self.__controller.low_energy_start()
if low_energy_method == "PSM":
# TODO: PSM option.
pass
elif low_energy_method == "POWERDOWN":
self.__controller.power_down()
def update(self, observable, *args, **kwargs):
if isinstance(observable, LowEnergyManage):
log.debug("Low Energy RTC Method: %s" % args[1])
self.low_engery_option(args[1])

173
code/tracker_controller.py Normal file
View File

@ -0,0 +1,173 @@
# 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.
import dataCall
from misc import Power
from usr.moudles.led import LED
from usr.moudles.ota import OTAFileClear
from usr.moudles.logging import getLogger
from usr.moudles.mpower import LowEnergyManage
from usr.moudles.remote import RemotePublish
from usr.moudles.common import Singleton
from usr.settings import Settings
try:
from misc import USB
except ImportError:
USB = None
try:
from misc import PowerKey
except ImportError:
PowerKey = None
log = getLogger(__name__)
class Controller(Singleton):
def __init__(self):
self.__remote_pub = None
self.__settings = None
self.__low_energy = None
self.__energy_led = None
self.__running_led = None
self.__power_key = None
self.__usb = None
self.__data_call = None
self.__ota_file_clear = None
def add_module(self, module, led_type=None, callback=None):
if isinstance(module, RemotePublish):
self.__remote_pub = module
return True
elif isinstance(module, Settings):
self.__settings = module
return True
elif isinstance(module, LowEnergyManage):
self.__low_energy = module
return True
elif isinstance(module, OTAFileClear):
self.__ota_file_clear = module
return True
elif isinstance(module, LED):
if led_type == "energy":
self.__energy_led = module
return True
elif led_type == "running":
self.running_led = module
return True
elif isinstance(module, PowerKey):
self.__power_key = module
if callback:
self.__power_key.powerKeyEventRegister(callback)
return True
elif isinstance(module, USB):
self.__usb = module
if callback:
self.__usb.setCallback(callback)
return True
elif module is dataCall:
self.__data_call = module
if callback:
self.__data_call.setCallback(callback)
return True
return False
def settings_get(self):
if not self.__settings:
raise TypeError("self.__settings is not registered.")
return self.__settings.get()
def settings_set(self, key, value):
if not self.__settings:
raise TypeError("self.__settings is not registered.")
if key == "loc_method":
v = "0b"
v += str(int(value.get(3, 0)))
v += str(int(value.get(2, 0)))
v += str(int(value.get(1, 0)))
value = int(v, 2)
set_res = self.__settings.set(key, value)
log.debug("__settings_set key: %s, val: %s, set_res: %s" % (key, value, set_res))
return set_res
def settings_save(self):
if not self.__settings:
raise TypeError("self.__settings is not registered.")
return self.__settings.save()
def power_restart(self):
Power.powerRestart()
def power_down(self):
Power.powerDown()
def remote_post_data(self, data):
if not self.__remote_pub:
raise TypeError("self.__remote_pub is not registered.")
return self.__remote_pub.post_data(data)
def remote_ota_check(self):
if not self.__remote_pub:
raise TypeError("self.__remote_pub is not registered.")
return self.__remote_pub.cloud_ota_check()
def remote_ota_action(self, action, module):
if not self.__remote_pub:
raise TypeError("self.__remote_pub is not registered.")
return self.__remote_pub.cloud_ota_action(action, module)
def low_energy_set_period(self, period):
if not self.__low_energy:
raise TypeError("self.__low_energy is not registered.")
return self.__low_energy.set_period(period)
def low_energy_set_method(self, method):
if not self.__low_energy:
raise TypeError("self.__low_energy is not registered.")
return self.__low_energy.set_low_energy_method(method)
def low_energy_init(self):
if not self.__low_energy:
raise TypeError("self.__low_energy is not registered.")
return self.__low_energy.low_energy_init()
def low_energy_start(self):
if not self.__low_energy:
raise TypeError("self.__low_energy is not registered.")
return self.__low_energy.start()
def low_energy_stop(self):
if not self.__low_energy:
raise TypeError("self.__low_energy is not registered.")
return self.__low_energy.stop()
def ota_file_clean(self):
if not self.__ota_file_clear:
raise TypeError("self.__ota_file_clear is not registered.")
self.__ota_file_clear.file_clear()
def running_led_show(self, period):
if not self.__running_led:
raise TypeError("self.__running_led is not registered.")
self.__running_led.set_period(period)
return self.__running_led.led_timer_start()
def energy_led_show(self, period):
if not self.energy_led_show:
raise TypeError("self.energy_led_show is not registered.")
self.__energy_led.set_period(period)
return self.__energy_led.led_timer_start()

View File

@ -0,0 +1,94 @@
# 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.
import utime
import checkNet
from usr.moudules.sensor import Sensor
from usr.moudules.logging import getLogger
from usr.moudules.location import Location
from usr.settings import PROJECT_NAME, PROJECT_VERSION, settings
log = getLogger(__name__)
class DeviceCheck(object):
def __init__(self):
self.__locator = None
self.__sensor = None
def add_module(self, module):
if isinstance(module, Location):
self.__locator = module
return True
elif isinstance(module, Sensor):
self.__sensor = module
return True
return False
def net(self):
current_settings = settings.get()
checknet = checkNet.CheckNetwork(PROJECT_NAME, PROJECT_VERSION)
timeout = current_settings.get("sys", {}).get("checknet_timeout", 60)
check_res = checknet.wait_network_connected(timeout)
log.debug("DeviceCheck.net res: %s" % str(check_res))
return check_res
def location(self):
# return True if OK
if not self.__locator:
raise TypeError("self.__locator is not registered")
current_settings = settings.get()
retry = 0
gps_data = None
sleep_time = 1
while retry < 5:
if current_settings["user_cfg"].get("loc_method"):
loc_method = current_settings["user_cfg"].get("loc_method")
elif current_settings["sys"]["base_cfg"]["LocConfig"]:
loc_method = current_settings["LocConfig"].get("loc_method")
else:
loc_method = 7
gps_data = self.__locator.read(loc_method)
if gps_data:
break
else:
retry += 1
utime.sleep(sleep_time)
sleep_time *= 2
if gps_data:
return True
return False
def temp(self):
# return True if OK
return None
def light(self):
# return True if OK
return None
def triaxial(self):
# return True if OK
return None
def mike(self):
# return True if OK
return None