diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..da148f2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "code/modules"] + path = code/modules + url = https://gitee.com/qpy-solutions/modules.git diff --git a/code/aliyunIot.py b/code/aliyunIot.py deleted file mode 100644 index 3071920..0000000 --- a/code/aliyunIot.py +++ /dev/null @@ -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 diff --git a/code/battery.py b/code/battery.py deleted file mode 100644 index 8e44db1..0000000 --- a/code/battery.py +++ /dev/null @@ -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 diff --git a/code/common.py b/code/common.py deleted file mode 100644 index 3320f32..0000000 --- a/code/common.py +++ /dev/null @@ -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 diff --git a/code/history.py b/code/history.py deleted file mode 100644 index da0bd13..0000000 --- a/code/history.py +++ /dev/null @@ -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:])) diff --git a/code/led.py b/code/led.py deleted file mode 100644 index 8958f50..0000000 --- a/code/led.py +++ /dev/null @@ -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) diff --git a/code/location.py b/code/location.py deleted file mode 100644 index 9c16730..0000000 --- a/code/location.py +++ /dev/null @@ -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 Data(RMC)""" - 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 Data(GGA)""" - 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 Speed(VTG)""" - 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 View(GSV)""" - 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 diff --git a/code/logging.py b/code/logging.py deleted file mode 100644 index 79074ff..0000000 --- a/code/logging.py +++ /dev/null @@ -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) diff --git a/code/main.py b/code/main.py index 4333329..980f036 100644 --- a/code/main.py +++ b/code/main.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from usr.logging import getLogger +from usr.modules.logging import getLogger from usr.tracker import tracker from usr.settings import PROJECT_NAME, PROJECT_VERSION, DEVICE_FIRMWARE_NAME, DEVICE_FIRMWARE_VERSION diff --git a/code/modules b/code/modules new file mode 160000 index 0000000..561ad0c --- /dev/null +++ b/code/modules @@ -0,0 +1 @@ +Subproject commit 561ad0c23a8f81e8f5f2e7a0cecb4b1151aeab40 diff --git a/code/mpower.py b/code/mpower.py deleted file mode 100644 index 51e18f7..0000000 --- a/code/mpower.py +++ /dev/null @@ -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() diff --git a/code/ota.py b/code/ota.py deleted file mode 100644 index 8419c2b..0000000 --- a/code/ota.py +++ /dev/null @@ -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") diff --git a/code/quec_object_model.json b/code/quec_object_model.json index 41fed2a..d6d00dc 100644 --- a/code/quec_object_model.json +++ b/code/quec_object_model.json @@ -905,4 +905,4 @@ "desc":"" } ] -} \ No newline at end of file +} diff --git a/code/quecthing.py b/code/quecthing.py deleted file mode 100644 index 0658fa6..0000000 --- a/code/quecthing.py +++ /dev/null @@ -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 diff --git a/code/remote.py b/code/remote.py deleted file mode 100644 index d230307..0000000 --- a/code/remote.py +++ /dev/null @@ -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 diff --git a/code/sensor.py b/code/sensor.py deleted file mode 100644 index 54411af..0000000 --- a/code/sensor.py +++ /dev/null @@ -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) diff --git a/code/settings.py b/code/settings.py index fb87133..ba78aa3 100644 --- a/code/settings.py +++ b/code/settings.py @@ -19,8 +19,8 @@ import ujson import modem import _thread -from usr.common import Singleton -from usr.common import option_lock +from usr.moudles.common import Singleton +from usr.moudles.common import option_lock from usr.settings_sys import SYSConfig from usr.settings_loc import LocConfig from usr.settings_alicloud import AliCloudConfig diff --git a/code/test_tracker.py b/code/test_tracker.py index 3e66b79..a0bc117 100644 --- a/code/test_tracker.py +++ b/code/test_tracker.py @@ -12,63 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import modem -import utime -import osTimer - -from usr.logging import Logger from usr.tracker import tracker -from usr.battery import Battery -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"])) +from usr.settings import 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"])) -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(): tracker() def main(): - # test_logger() # test_settings() - # test_battery() - # test_history() - # test_location() - # test_quecthing() - # test_aliyuniot() - # test_remote() - # test_low_energy_manage() test_tracker() diff --git a/code/tracker.py b/code/tracker.py index a8b127c..7765170 100644 --- a/code/tracker.py +++ b/code/tracker.py @@ -13,28 +13,24 @@ # limitations under the License. import sim -import utime import modem -import checkNet import dataCall -from misc import Power - -from usr.led import LED -from usr.sensor import Sensor -from usr.battery import Battery -from usr.ota import OTAFileClear -from usr.history import History -from usr.logging import getLogger -from usr.mpower import LowEnergyManage -from usr.remote import RemotePublish, RemoteSubscribe -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.modules.sensor import Sensor +from usr.modules.battery import Battery +from usr.modules.ota import OTAFileClear +from usr.modules.history import History +from usr.modules.logging import getLogger +from usr.modules.mpower import LowEnergyManage +from usr.modules.remote import RemotePublish, RemoteSubscribe +from usr.modules.aliyunIot import AliYunIot, AliObjectModel +from usr.modules.quecthing import QuecThing, QuecObjectModel +from usr.modules.location import Location from usr.settings import PROJECT_NAME, PROJECT_VERSION, \ - DEVICE_FIRMWARE_NAME, DEVICE_FIRMWARE_VERSION, settings, UserConfig, \ - SYSConfig, Settings + DEVICE_FIRMWARE_NAME, DEVICE_FIRMWARE_VERSION, settings, SYSConfig +from usr.tracker_collector import Collector +from usr.tracker_controller import Controller +from usr.tracker_devicecheck import DeviceCheck try: from misc import USB @@ -50,16 +46,6 @@ log = getLogger(__name__) 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): if status == 0: @@ -93,809 +79,6 @@ def nw_callback(args): 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(): current_settings = settings.get() diff --git a/code/tracker_collector.py b/code/tracker_collector.py new file mode 100644 index 0000000..73dcdbe --- /dev/null +++ b/code/tracker_collector.py @@ -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]) diff --git a/code/tracker_controller.py b/code/tracker_controller.py new file mode 100644 index 0000000..cef9706 --- /dev/null +++ b/code/tracker_controller.py @@ -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() diff --git a/code/tracker_devicecheck.py b/code/tracker_devicecheck.py new file mode 100644 index 0000000..f7bbd8f --- /dev/null +++ b/code/tracker_devicecheck.py @@ -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 diff --git a/ali_cloud_module.json b/object_model_demo/ali_cloud_object_model.json similarity index 100% rename from ali_cloud_module.json rename to object_model_demo/ali_cloud_object_model.json diff --git a/quec_cloud_module.json b/object_model_demo/quec_cloud_object_model.json similarity index 100% rename from quec_cloud_module.json rename to object_model_demo/quec_cloud_object_model.json