initial commit

This commit is contained in:
rivern yuan 2021-08-30 14:33:18 +08:00
parent 4333f64e96
commit 46c4af2024
24 changed files with 2772 additions and 0 deletions

Binary file not shown.

Binary file not shown.

20
README.md Normal file
View File

@ -0,0 +1,20 @@
# HeliosService
**API文档 - 里面有API文档**
**helios service文档 \- 框架文档**
**Code \- 代码**
**API documentation - It has API documentation**
**Helios Service document \- Framework document**
**Code \- code**

View File

@ -0,0 +1,98 @@
__all__ = [
"CloudService",
"CloudServiceMonitor",
]
from usr.bin.components.abstract_service import AbstractService
from usr.bin.components.monitor import ServiceMonitor
from usr.utils.service_utils import Singleton
from usr.bin.components.OTA import UCloudOTA
import _thread
SH_UPGRADE = 1
BIN_UPGRADE = 0
ENABLE_ = "enable"
class CloudServiceMonitor(ServiceMonitor):
@staticmethod
def create_monitor(config=None):
if config is None:
return None
level = 0
try:
level = config.get("level", 0)
uid = config["params"]["uid"]
module_type = config["params"]["module_type"]
pk = config["params"]["pk"]
battery = config["params"].get("battery", 100)
reboot = config["params"].get("reboot", False)
cloud_service = CloudService(uid=uid, module_type=module_type, pk=pk, battery=battery, reboot=bool(reboot))
except Exception as e:
return None
else:
m = CloudServiceMonitor(cloud_service)
m.set_level(level)
if config is not None:
m.set_exception_handlers(config.get('exceptionHandlers', None))
return m
@Singleton
class CloudService(AbstractService):
def __init__(self, uid, module_type, pk, battery=100, reboot=False):
super().__init__("CLOUD")
self.reboot = reboot
self.module_type = module_type
self.pk = pk
self.uid = uid
self.ota_upgrade = UCloudOTA(battery)
self.__upgrade_status_info = {
SH_UPGRADE: None,
BIN_UPGRADE: None
}
self.server_status = {
SH_UPGRADE: {ENABLE_: True},
BIN_UPGRADE: {ENABLE_: False}
}
@property
def upgrade_status_info(self):
return self.__upgrade_status_info
def set_enable(self, sr, enable):
"""set start"""
self.server_status[sr][ENABLE_] = enable
def __upgrade_sh(self):
# 触发升级脚本操作
if self.server_status[SH_UPGRADE][ENABLE_]:
code = self.ota_upgrade.start_upgrade_sh_event(self.module_type, self.uid, self.pk, reboot=self.reboot)
self.send_msg(message=dict(code=code), msg_type=SH_UPGRADE)
def __upgrade_bin(self):
# 触发升级bin包操作
if self.server_status[BIN_UPGRADE][ENABLE_]:
code = self.ota_upgrade.start_upgrade_bin_event(self.module_type, self.uid, self.pk, reboot=self.reboot)
self.send_msg(message=dict(code=code), msg_type=BIN_UPGRADE)
def _status_update(self, *args, **kwargs):
msg = "message"
em = kwargs.get(msg, False)
if em:
msg_type = em["msg_type"]
self.__upgrade_status_info[msg_type] = em[msg]["code"]
def get_app_code(self):
return self.__upgrade_status_info[SH_UPGRADE]
def prepare_before_start(self):
self.signal.connect(self._status_update, sender="anonymous")
def start(self):
super().start()
# _thread.start_new_thread(self.__upgrade_bin, ())
_thread.start_new_thread(self.__upgrade_sh, ())
def commit_log(self, message):
self.ota_upgrade.commit_log(self.pk, message)

View File

@ -0,0 +1,499 @@
import modem, ubinascii, net, uhashlib, request, ujson, app_fota, fota, utime, uos, log
from misc import Power
from unzip import UnZipUtils
from app_fota_download import update_download_stat, delete_update_file
PROCESS_CODE = {
"DO_NOT_UPGRADE": 0,
"DOWNLOADING_FIRMWARE": 3,
"DOWNLOADED_NOTIFY_UPDATE": 4,
"DOWNLOAD_FAILED": 5,
"UPDATE_START": 6,
"UPGRADE_SUCCESS": 7,
"UPGRADE_FAILED": 8,
}
USR = "/usr/"
CONFIG_PATH = "ota_config.json"
MAIN_PY = "main.py"
BIN_MODE = "0"
APP_MODE = "1"
UPDATER_DIR = '/usr/.updater'
# 后续版本支持批量升级
# BULK_APP_MODE = "2"
log.basicConfig(level=log.INFO)
ota_log = log.getLogger("ota")
fota_bin_file = "FotaFile.bin"
MODE_MAP = {
BIN_MODE: "_upgrade_fota_bin",
APP_MODE: "_upgrade_fota_sh",
# 后续支持BULK升级
# BULK_APP_MODE: "_upgrade_fota_bulk_sh",
}
ZIP = ".zip"
class RET:
OK = "20000"
# 系统组件错误
UPGRADE = "3001"
NOUPGRADE = "3002"
# 网络协议错误
TOKENERR = "4001"
GETUPGRADEURLERR = "4002"
DOWNLOADERR = "4003"
PARAMSERR = "4004"
MODENOTEXIST = "4005"
FOTAVERIFAILD = "4006"
COMMIT_LOG_ERROR = "4007"
# 配置错误
SAVECONFIGERR = "4021"
GETCONFIGERR = "4022"
NETERR = "4023"
REQERR = "4024"
WIOERR = "4025"
FWSTREAMERR = "4026"
RIOERR = "4027"
UNZIPERROR = "4028"
UNZIPFILEPROTOCOLERROR = "4029"
error_map = {
RET.OK: u"成功",
# 系统
RET.UPGRADE: u"有升级计刿",
RET.NOUPGRADE: u"无升级计刿",
# 协议
RET.TOKENERR: u"获取token失败",
RET.GETUPGRADEURLERR: u"获取升级地址失败",
RET.DOWNLOADERR: u"下载固件失败",
RET.PARAMSERR: u"参数获取失败",
RET.MODENOTEXIST: u"模式不存圿",
###
RET.SAVECONFIGERR: u"保存配置错误",
RET.GETCONFIGERR: u"获取配置错误",
RET.NETERR: u"网络错误",
RET.WIOERR: u"文件写错诿",
RET.FWSTREAMERR: u"FOTA字节流作牿",
RET.RIOERR: u"文件读错诿",
RET.UNZIPERROR: u"文件解压失败",
RET.UNZIPFILEPROTOCOLERROR: u"压缩文件解析失败",
}
class OTAUtil(object):
@classmethod
def rm_upgrade_file(cls, file_name):
uos.remove(UPDATER_DIR + file_name)
delete_update_file(file_name)
@classmethod
def add_upgrade_file(cls, url, file_name):
update_download_stat(url, USR + file_name, uos.stat(UPDATER_DIR + USR + file_name)[6])
class UCloudOTA(object):
def __init__(self, battery=100, url="http://220.180.239.212:8274/v1"):
self.imei = modem.getDevImei()
self.rsrp = net.csqQueryPoll()
self.url = url
self.module_type = ""
self.battery = battery
self.version = ""
self.uid = ""
self.pk = ""
self.cellId = ""
self.mnc = ""
self.mcc = ""
self.lac = ""
self.report_url = "/fota/status/report"
self.access_token = ""
self.upgrade_info = {}
self.bin_path = "/usr/FotaFile.bin"
self.ota_config_info = {
"version": "V1.0.0",
"flag": "0",
"fw_version": modem.getDevFwVersion()
}
self.info_map = {
APP_MODE: {
"reboot": True,
"uid": "",
"pk": ""
},
BIN_MODE: {
"reboot": True,
"uid": "",
"pk": ""
}
}
def _upgrade_fota(self, mode):
"升级fota bin"
try:
action = self.upgrade_info["action"]
url = self.upgrade_info["url"]
except Exception as e:
return RET.PARAMSERR
try:
if action:
self.report("DOWNLOADING_FIRMWARE")
if mode == APP_MODE:
if url.endswith(ZIP):
# APP 升级
code = self._upgrade_fota_bulk_sh(url)
if code != RET.OK:
return code
else:
self._upgrade_fota_sh(url)
if self.info_map[APP_MODE]["reboot"]:
self.power_restart()
elif mode == BIN_MODE:
# fota 升级
self._upgrade_fota_bin(url)
ota_log.info("self.info_map[BIN_MODE][reboot] === {}".format(self.info_map[BIN_MODE]["reboot"]))
if self.info_map[BIN_MODE]["reboot"]:
self.power_restart()
else:
# 模式 不匹酿
return RET.MODENOTEXIST
self.report("DOWNLOADED_NOTIFY_UPDATE")
return RET.UPGRADE
else:
self.report("DO_NOT_UPGRADE")
return RET.NOUPGRADE
except Exception as e:
ota_log.error(e)
self.report(PROCESS_CODE[5])
return RET.DOWNLOADERR
def _upgrade_fota_bulk_sh(self, url):
"""
批量升级
:param url:
:return:
"""
zip_file = url.split("/")[-1]
fota = app_fota.new()
fota.download(url, USR + zip_file)
zip_util = UnZipUtils(dest_dir=UPDATER_DIR + USR)
try:
zip_util.set_data(zip_file)
except Exception as e:
ota_log.error(e)
return RET.UNZIPERROR
try:
code = zip_util.run()
if not code:
return code
except Exception as e:
return RET.UNZIPFILEPROTOCOLERROR
for file_name in zip_util.file_name_list:
OTAUtil.add_upgrade_file(url, file_name)
OTAUtil.rm_upgrade_file(USR + zip_file)
self.report("DOWNLOADED_NOTIFY_UPDATE")
fota.set_update_flag()
try:
self.ota_config_info["version"] = self.upgrade_info["targetVersion"]
self.ota_config_info["flag"] = "1"
with open(USR + CONFIG_PATH, "w") as f:
f.write(ujson.dumps(self.ota_config_info))
except Exception as e:
ota_log.info(e)
self.report("UPDATE_START")
return RET.OK
def _upgrade_fota_sh(self, url):
"""
升级单个脚本
:param cover_file: 升级后覆盖的文件
:return:
"""
zip_file = url.split("/")[-1]
fota = app_fota.new()
# 覆盖cover_file
cover_file = USR + zip_file[zip_file.find("_") + 1:]
fota.download(url, cover_file)
self.report("DOWNLOADED_NOTIFY_UPDATE")
fota.set_update_flag()
try:
self.ota_config_info["version"] = self.upgrade_info["targetVersion"]
self.ota_config_info["flag"] = "1"
with open(USR + CONFIG_PATH, "w") as f:
f.write(ujson.dumps(self.ota_config_info))
except Exception as e:
ota_log.info(e)
self.report("UPDATE_START")
def _upgrade_fota_bin(self, url):
self.report("DOWNLOADED_NOTIFY_UPDATE")
try:
r = request.get(url, sizeof=4096)
except Exception as e:
return RET.REQERR
if r.status_code == 200 or r.status_code == 206:
file_size = int(r.headers['Content-Length'])
fota_obj = fota()
count = 0
try:
while True:
c = next(r.content)
length = len(c)
for i in range(0, length, 4096):
count += len(c[i:i + 4096])
fota_obj.write(c[i:i + 4096], file_size)
except StopIteration:
r.close()
except Exception as e:
r.close()
return RET.FWSTREAMERR
else:
r.close()
res = fota_obj.verify()
if res != 0:
return RET.FOTAVERIFAILD
self.report("DOWNLOADED_NOTIFY_UPDATE")
return RET.OK
else:
return RET.REQERR
def _upgrade_template(self, mode, module_type="", version=""):
"""
开启固件脚本升级方弿
:return: STATE
"""
self.module_type = modem.getDevFwVersion()[:5] if not module_type else module_type
# 判斷模式
if mode == APP_MODE:
state = self.register_upgrade_sh(version=version)
ota_log.info("the device app currently version is {}".format(self.version))
elif mode == BIN_MODE:
state = self.register_upgrade_bin()
ota_log.info("the device fireware currently version is {}".format(self.version))
else:
return RET.MODENOTEXIST
# 注册upgrade sh
if state != RET.OK:
return state
# 升级脚本
state = self.get_upgrade_url(mode)
if state != RET.OK:
return state
# 升级脚本
state = self._upgrade_fota(mode)
return state
def get_token(self, prefix="/oauth/token", kwargs=None):
# 获取token
if net.csqQueryPoll() == 99 or net.csqQueryPoll() == -1:
return RET.NETERR
uri = self.url + prefix
try:
secret = ubinascii.hexlify(uhashlib.md5("QUEC" + str(self.imei) + "TEL").digest())
secret = secret.decode()
uri = uri + "?imei=" + self.imei + "&" + "secret=" + secret
ota_log.info("uri = {}".format(uri))
resp = request.get(uri)
json_data = resp.json()
ota_log.info("get_token = {}".format(json_data))
self.access_token = json_data["data"]["access_Token"]
return RET.OK
except Exception as e:
ota_log.error("get_token error:{}".format(e))
return RET.TOKENERR
def report(self, code, msg=None):
data_info = {
"version": str(self.version),
"ver": "v1.0",
"imei": str(self.imei),
"code": PROCESS_CODE.get(code, 0),
"msg": str(msg) if msg else code
}
ota_log.info(data_info)
uri = self.url + self.report_url
headers = {"access_token": self.access_token, "Content-Type": "application/json"}
try:
resp = request.post(uri, data=ujson.dumps(data_info), headers=headers)
return resp
except Exception as e:
return -1
def get_upgrade_url(self, mode, prefix="/fota/fw"):
ota_log.info("module_type == {}".format(self.module_type))
params = "?" + "version=" + str(self.version) + "&" + "imei=" + str(self.imei) + "&" + "moduleType=" + str(
self.module_type) + "&" + "battery=" + str(
self.battery) + "&" + "rsrp=" + str(
self.rsrp) + "&" + "uid=" + str(
self.info_map[mode]["uid"]) + "&" + "pk=" + str(
self.info_map[mode]["pk"])
ota_log.info(params)
uri = self.url + prefix + params
headers = dict(access_token=self.access_token)
try:
resp = request.get(uri, headers=headers)
json_data = resp.json()
ota_log.info("get_upgrade_url json data {}".format(json_data))
if json_data["code"] == 200:
self.upgrade_info = json_data
return RET.OK
else:
ota_log.info("no upgrade plan for this device")
return RET.NOUPGRADE
except Exception as e:
ota_log.info("get_upgrade_url {}".format(e))
return RET.GETUPGRADEURLERR
def _register_upgrade(self, mode, version=""):
"""
注册脚本升级
:param version:版本叿
:return:
"""
try:
list_dirs = uos.listdir(USR)
if CONFIG_PATH in list_dirs:
with open(USR + CONFIG_PATH, "r") as f:
self.ota_config_info = ujson.loads(f.read())
else:
if mode == APP_MODE:
# 应用检测版本号
if version:
self.ota_config_info["version"] = version
with open(USR + CONFIG_PATH, "w") as f:
f.write(ujson.dumps(self.ota_config_info))
except Exception as e:
ota_log.error("register_upgrade_sh = {}".format(e))
# 读写config 错误
return RET.GETCONFIGERR
# 获取token
state = self.get_token()
if state != RET.OK:
return state
try:
if mode == APP_MODE:
# app mode
self.version = self.ota_config_info["version"]
if self.ota_config_info["flag"] == "1":
# 升级成功
self.report("UPGRADE_SUCCESS", self.ota_config_info["version"])
self.ota_config_info["flag"] = "0"
with open(USR + CONFIG_PATH, "w") as f:
f.write(ujson.dumps(self.ota_config_info))
elif mode == BIN_MODE:
# bin mode
self.version = modem.getDevFwVersion()
if self.ota_config_info["fw_version"] != self.version:
# OTA升级成功
self.report("UPGRADE_SUCCESS", self.version)
self.ota_config_info['fw_version'] = self.version
with open(USR + CONFIG_PATH, "w") as f:
f.write(ujson.dumps(self.ota_config_info))
else:
return RET.MODENOTEXIST
return RET.OK
except Exception as e:
return RET.SAVECONFIGERR
def register_upgrade_sh(self, version=""):
return self._register_upgrade(APP_MODE, version=version)
def register_upgrade_bin(self):
return self._register_upgrade(BIN_MODE)
def start_upgrade_sh_event(self, module_type, uid, pk, version="", reboot=True):
"""
开启固件脚本升级方弿
:return: STATE
"""
self.info_map[APP_MODE]["reboot"] = reboot
self.info_map[APP_MODE]["uid"] = uid
self.info_map[APP_MODE]["pk"] = pk
return self._upgrade_template(APP_MODE, module_type=module_type, version=version)
def start_upgrade_bin_event(self, module_type, uid, pk, reboot=True):
"""
开启固件升级方弿
:return: STATE
"""
# 升级脚本
self.info_map[BIN_MODE]["reboot"] = reboot
self.info_map[BIN_MODE]["uid"] = uid
self.info_map[BIN_MODE]["pk"] = pk
return self._upgrade_template(BIN_MODE, module_type=module_type)
def power_restart(self):
"""
重启前的动作
重启设备
:return:
"""
self.power_restart_before()
Power.powerRestart()
def power_restart_before(self):
"""
定义重启的动使
:return:
"""
pass
def commit_log(self, pk, message, battery=100, prefix="/fota/msg/report"):
v2_url = self.url[:-2] + "v2"
access_token = self.__get_v2_token(pk, v2_url)
if access_token == RET.TOKENERR:
return RET.TOKENERR
try:
cl = net.getCellInfo()[2][0]
data_info = {
"imei": self.imei,
"version": self.ota_config_info["version"],
"reportMsg": str(message),
"battery": battery,
"signalStrength": net.csqQueryPoll(),
"cellInfos": [
{
"lac": cl[5],
"mcc": str(cl[2]),
"mnc": str(cl[3]),
"cid": cl[1],
"signalStrength": net.csqQueryPoll(),
}
]
}
headers = {
"access_token": access_token,
"Content-Type": "application/json"
}
resp = request.post(self.url + prefix, data=ujson.dumps(data_info),
headers=headers)
return resp.json()
except Exception as e:
ota_log.info("commit_log exception {}".format(e))
return RET.COMMIT_LOG_ERROR
def __get_v2_token(self, pk, v2_url, prefix="/oauth/token"):
# 获取token
if net.csqQueryPoll() == 99 or net.csqQueryPoll() == -1:
return RET.NETERR
uri = v2_url + prefix
try:
uri = uri + "?imei=" + self.imei + "&" + "pk=" + pk + "&" + "grantType=1"
ota_log.info("uri = {}".format(uri))
resp = request.get(uri)
json_data = resp.json()
ota_log.info("get_v2_token = {}".format(json_data))
access_token = json_data["data"]["access_Token"]
return access_token
except Exception as e:
ota_log.error("get_v2_token error:{}".format(e))
return RET.TOKENERR

View File

@ -0,0 +1,203 @@
from usr.bin.components.blinker_min import Signal, signal
from event_message import Event, EventManager, EventMessageObject
from usr.bin.components.service_models import QMessageModel, LockMsgModel
import _thread
ANY = "anonymous"
class AbstractService(object):
def __init__(self, sign):
self.__name = sign
self.__signal = signal(sign)
self.__event = Event(sign)
self.__em = EventManager()
self.__message_id = 0
self.__service_status = 0
self.__em.register_event(self.__event)
self.__mode = 1
def set_mode(self, mode):
self.__mode = mode
@property
def mode(self):
return self.__mode
@property
def name(self):
return self.__name
@property
def message_id(self):
"""return self.next message id."""
return self.__message_id + 1
@property
def signal(self):
"""signal to get by this method"""
return self.__signal
def ms_id_increase(self):
if self.__message_id > 99999:
self.__message_id = 0
else:
self.__message_id += 1
return self.message_id
def _get_message(self, sender=None, msg_type=0xFF, message=None, callback=None, lock_msg=None):
"""get message"""
if callback is not None and not callable(callback):
return None
ms_id = self.ms_id_increase()
qm = QMessageModel(ms_id=ms_id, msg_type=msg_type, message=message, sender=sender, from_event=self.__event.name,
lock_msg=lock_msg)
return qm
def _callback(self, **kwargs):
"""event callback"""
em = kwargs.get("event_message", False)
if em.msg:
msg = em.msg
try:
self.signal.send(em.msg.sender, message=msg())
except Exception as e:
success = 0
else:
success = 1
resp_data = dict(message=msg(), success=success)
if em.callback is not None:
try:
em.callback(**resp_data)
except Exception as e:
pass
if msg.lock_msg is not None:
msg.lock_msg.msg = resp_data
msg.lock_msg.lock.release()
def _clear(self, *args, **kwargs):
""" stop crontab task"""
self.__event.clear()
def _add_default_handler(self):
"""add default handler"""
self.add_handler(self._callback)
def register_event(self):
"""achieve by class extend AbstractService"""
pass
def prepare_before_start(self):
"""prepare before start action"""
pass
def prepare_before_stop(self):
"""prepare after start action"""
pass
def start_crontab(self, *args, **kwargs):
"""achieve by timer task"""
pass
def add_handler(self, handler):
"""
support for scan addition and manual sword
:param handler: deal handler
:return:
"""
self.__event.add_handler(handler)
return handler
def add_handler_via(self):
"""
Support for multiple annotations
:return:
"""
def decorator(fn):
self.add_handler(fn)
return fn
return decorator
def send_msg_async(self, msg_type=0xFF, message=None, callback=None, sender=None):
if self.status():
qm = self._get_message(sender=sender, msg_type=msg_type, message=message, callback=callback)
self.__event.post(qm, callback=callback)
else:
return 0
def send_msg_sync(self, msg_type=0xFF, message=None, callback=None, sender=None):
if self.status():
lock = _thread.allocate_lock()
lock_msg = LockMsgModel(lock)
qm = self._get_message(sender=sender, msg_type=msg_type, message=message, callback=callback,
lock_msg=lock_msg)
self.__event.post(qm, callback=callback)
lock_msg.lock.acquire()
msg = lock_msg.msg
del lock_msg
return msg
else:
return 0
def send_msg(self, msg_type=0xFF, message=None, callback=None, sender=None, mode=None):
mode_pattern = self.mode
if mode is not None:
mode_pattern = mode
if mode_pattern:
return self.send_msg_async(msg_type=msg_type, message=message, callback=callback, sender=sender)
else:
return self.send_msg_sync(msg_type=msg_type, message=message, callback=callback, sender=sender)
def _component_start(self):
"""start components"""
if not self.__service_status:
self.register_event()
self._add_default_handler()
self.prepare_before_start()
self.start_crontab()
def start(self):
"""
start the service
:return: 1 or 0
"""
self._component_start()
self.__service_status = self.__em.start()
return self.__service_status
def status(self):
"""
status of service
:return:
"""
return self.__em.status
def stop(self):
"""
stop the service
:return:
"""
self.prepare_before_stop()
self.__service_status = self.__em.stop()
return self.__service_status
def close(self):
self._clear()
def subscribe(self, cb, sender=ANY):
self.signal.connect(cb, sender=sender)
def unsubscribe(self, cb, sender=ANY):
self.signal.disconnect(cb, sender=sender)
def publish(self, msg, sender=ANY, msg_type=0XFF):
self.send_msg(message=msg, msg_type=msg_type, sender=sender)

View File

@ -0,0 +1,541 @@
# -*- coding: utf-8; fill-column: 76 -*-
import gc
__all__ = ["signal", "Signal"]
class attrgetter:
__slots__ = ('_attrs', '_call')
def __init__(self, attr, *attrs):
if not attrs:
if not isinstance(attr, str):
raise TypeError('attribute name must be a string')
self._attrs = (attr,)
names = attr.split('.')
def func(obj):
for name in names:
obj = getattr(obj, name)
return obj
self._call = func
else:
self._attrs = (attr,) + attrs
getters = tuple(map(attrgetter, self._attrs))
def func(obj):
return tuple(getter(obj) for getter in getters)
self._call = func
def __call__(self, obj):
return self._call(obj)
def __repr__(self):
return '%s.%s(%s)' % (self.__class__.__module__,
self.__class__.__qualname__,
', '.join(map(repr, self._attrs)))
class ReferenceType(object):
def __new__(cls, *args, **kwargs):
return super(ReferenceType, cls).__new__(cls)
def __call__(self, *args, **kwargs):
return
def __init__(self, *args, **kwargs):
self.params = dict()
def __setitem__(self, key, value):
self.__dict__[key] = value
def __getitem__(self, key):
if key not in self.__dict__:
self.__dict__[key] = None
return self.__dict__[key]
__callback__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
ref = ReferenceType
class KeyedRef(ref):
__slots__ = "key",
def __new__(type, ob, callback, key):
self = ref.__new__(type, ob, callback)
self.key = key
return self
def __init__(self, ob, callback, key):
super().__init__(ob, callback)
class WeakValueDictionary(dict):
def __init__(self, dict=None):
self.data = {}
def remove(k, selfref=ref(self)):
self = selfref()
if self is not None:
if self._iterating:
self._pending_removals.append(k)
else:
del self.data[k]
self._remove = remove
self._pending_removals = []
self._iterating = set()
self._dirty_len = False
if dict is not None:
self.update(dict)
def _commit_removals(self):
gc.collect()
def __getitem__(self, key):
if self._pending_removals:
self._commit_removals()
o = self.data[key]()
if o is None:
raise KeyError(key)
else:
return o
def __setitem__(self, key, value):
if self._pending_removals:
self._commit_removals()
self.data[key] = KeyedRef(value, self._remove, key)
def get(self, key, default=None):
if self._pending_removals:
self._commit_removals()
try:
wr = self.data[key]
except KeyError:
return default
else:
o = wr()
if o is None:
return default
else:
return o
def update(self, other=None, **kwargs):
if self._pending_removals:
self._commit_removals()
d = self.data
if other is not None:
if not hasattr(other, "items"):
other = dict(other)
for key, o in other.items():
d[key] = KeyedRef(o, self._remove, key)
for key, o in kwargs.items():
d[key] = KeyedRef(o, self._remove, key)
try:
callable
except NameError:
def callable(object):
return hasattr(object, '__call__')
get_self = attrgetter('__self__')
get_func = attrgetter('__func__')
class BoundMethodWeakref(object):
_all_instances = WeakValueDictionary()
def __new__(cls, target, on_delete=None, *arguments, **named):
key = cls.calculate_key(target)
current = cls._all_instances.get(key)
if current is not None:
current.deletion_methods.append(on_delete)
return current
else:
base = super(BoundMethodWeakref, cls).__new__(cls)
cls._all_instances[key] = base
base.__init__(target, on_delete, *arguments, **named)
return base
def __init__(self, target, on_delete=None):
def remove(self=self):
methods = self.deletion_methods[:]
del self.deletion_methods[:]
try:
del self.__class__._all_instances[self.key]
except KeyError:
pass
for function in methods:
try:
if callable(function):
function(self)
except Exception as e:
print('Exception during saferef %s '
'cleanup function %s: %s' % (self, function, e))
self.deletion_methods = [on_delete]
self.key = self.calculate_key(target)
im_self = get_self(target)
im_func = get_func(target)
self.weak_self = ref(im_self, remove)
self.weak_func = ref(im_func, remove)
self.self_name = str(im_self)
self.func_name = str(im_func.__name__)
def calculate_key(cls, target):
return (id(get_self(target)), id(get_func(target)))
calculate_key = classmethod(calculate_key)
def __str__(self):
return "%s(%s.%s)" % (
self.__class__.__name__,
self.self_name,
self.func_name,
)
__repr__ = __str__
def __call__(self):
target = self.weak_self()
if target is not None:
function = self.weak_func()
if function is not None:
return function.__get__(target)
return None
class defaultdict(object):
params = dict()
def __init__(self, default_factory=None, *a, **kw):
super().__init__(*a, **kw)
self.default_factory = default_factory
def __getitem__(self, key):
try:
result = self.params[key]
return result
except KeyError:
return self.__missing__(key)
def __setitem__(self, key, value):
try:
self.params[key] = set(value)
except TypeError:
return "Type error"
def __missing__(self, key):
if self.default_factory is None:
raise KeyError(key)
self.params[key] = value = self.default_factory()
return value
def __reduce__(self):
if self.default_factory is None:
args = tuple()
else:
args = self.default_factory,
return type(self), args, None, None, self.items()
def copy(self):
return self.__copy__()
def pop(self, key, default):
return self.params.pop(key, default)
def values(self):
return self.params.values()
def items(self):
return self.params.items()
def clear(self):
return self.params.clear()
def __copy__(self):
return type(self)(self.default_factory, self)
def __repr__(self):
return 'defaultdict(%s, %s)' % (self.default_factory,
dict.__repr__(self))
def __contains__(self, key):
if key in self.params:
return True
else:
return False
class _symbol(object):
def __init__(self, name):
self.__name__ = self.name = name
def __reduce__(self):
return symbol, (self.name,)
def __repr__(self):
return self.name
_symbol.__name__ = 'symbol'
class symbol(object):
symbols = {}
def __new__(cls, name):
try:
return cls.symbols[name]
except KeyError:
return cls.symbols.setdefault(name, _symbol(name))
try:
text = (str, unicode)
except NameError:
text = str
def hashable_identity(obj):
if hasattr(obj, '__func__'):
return (id(obj.__func__), id(obj.__self__))
elif isinstance(obj, text):
return obj
else:
return id(obj)
WeakTypes = (ref, BoundMethodWeakref)
class annotatable_weakref(ref):
"""A weakref.ref that supports custom instance attributes."""
def reference(object, callback=None, **annotations):
if callable(object):
weak = callable_reference(object, callback)
else:
weak = annotatable_weakref(object, callback)
for key, value in annotations.items():
setattr(weak, key, value)
return weak
def callable_reference(object, callback=None):
if hasattr(object, 'im_self') and object.im_self is not None:
return BoundMethodWeakref(target=object, on_delete=callback)
elif hasattr(object, '__self__') and object.__self__ is not None:
return BoundMethodWeakref(target=object, on_delete=callback)
return annotatable_weakref(object, callback)
ANY = symbol('ANY')
ANY.__doc__ = 'Token for "any sender".'
ANY_ID = 0
class Signal(object):
ANY = ANY
def receiver_connected(self):
return Signal(doc="Emitted after a receiver connects.")
def receiver_disconnected(self):
return Signal(doc="Emitted after a receiver disconnects.")
def __init__(self, doc=None):
self.receivers = {}
self._by_receiver = defaultdict(default_factory=set)
self._by_sender = defaultdict(default_factory=set)
self._weak_senders = {}
def __repr__(self):
return "<%s at %#x>" % (self.__class__.__name__, id(self))
def connect(self, receiver, sender=ANY, weak=False):
receiver_id = hashable_identity(receiver)
weak = False
receiver_ref = receiver
if sender is ANY:
sender_id = ANY_ID
else:
sender_id = hashable_identity(sender)
self.receivers.setdefault(receiver_id, receiver_ref)
self._by_sender[sender_id].add(receiver_id)
self._by_receiver[receiver_id].add(sender_id)
del receiver_ref
if sender is not ANY and sender_id not in self._weak_senders:
try:
sender_ref = reference(sender, self._cleanup_sender)
sender_ref.sender_id = sender_id
except TypeError:
pass
else:
self._weak_senders.setdefault(sender_id, sender_ref)
del sender_ref
if ('receiver_connected' in self.__dict__ and
self.receiver_connected.receivers):
try:
self.receiver_connected.send(self,
receiver=receiver,
sender=sender,
weak=weak)
except:
self.disconnect(receiver, sender)
raise
if receiver_connected.receivers and self is not receiver_connected:
try:
receiver_connected.send(self,
receiver_arg=receiver,
sender_arg=sender,
weak_arg=weak)
except:
self.disconnect(receiver, sender)
raise
return receiver
def connect_via(self, sender=ANY, weak=False):
def decorator(fn):
self.connect(fn, sender, weak)
return fn
return decorator
def send(self, *sender, **kwargs):
if len(sender) == 0:
sender = None
elif len(sender) > 1:
raise TypeError('send() accepts only one positional argument, '
'%s given' % len(sender))
else:
sender = sender[0]
if not self.receivers:
return []
else:
return [(receiver, receiver(sender, **kwargs))
for receiver in self.receivers_for(sender)]
def has_receivers_for(self, sender):
if not self.receivers:
return False
if self._by_sender[ANY_ID]:
return True
if sender is ANY:
return False
return hashable_identity(sender) in self._by_sender
def receivers_for(self, sender):
if self.receivers:
sender_id = hashable_identity(sender)
if sender_id in self._by_sender:
ids = (self._by_sender[ANY_ID] |
self._by_sender[sender_id])
else:
ids = self._by_sender[ANY_ID].copy()
for receiver_id in ids:
receiver = self.receivers.get(receiver_id)
if receiver is None:
continue
if isinstance(receiver, WeakTypes):
strong = receiver()
if strong is None:
self._disconnect(receiver_id, ANY_ID)
continue
receiver = strong
yield receiver
def disconnect(self, receiver, sender=ANY):
if sender is ANY:
sender_id = ANY_ID
else:
sender_id = hashable_identity(sender)
receiver_id = hashable_identity(receiver)
self._disconnect(receiver_id, sender_id)
if ('receiver_disconnected' in self.__dict__ and
self.receiver_disconnected.receivers):
self.receiver_disconnected.send(self,
receiver=receiver,
sender=sender)
def _disconnect(self, receiver_id, sender_id):
if sender_id == ANY_ID:
if self._by_receiver.pop(receiver_id, False):
for bucket in self._by_sender.values():
bucket.discard(receiver_id)
self.receivers.pop(receiver_id, None)
else:
self._by_sender[sender_id].discard(receiver_id)
self._by_receiver[receiver_id].discard(sender_id)
def _cleanup_receiver(self, receiver_ref):
self._disconnect(receiver_ref.receiver_id, ANY_ID)
def _cleanup_sender(self, sender_ref):
sender_id = sender_ref.sender_id
assert sender_id != ANY_ID
self._weak_senders.pop(sender_id, None)
for receiver_id in self._by_sender.pop(sender_id, ()):
self._by_receiver[receiver_id].discard(sender_id)
def _cleanup_bookkeeping(self):
for mapping in (self._by_sender, self._by_receiver):
for _id, bucket in list(mapping.items()):
if not bucket:
mapping.pop(_id, None)
def _clear_state(self):
self._weak_senders.clear()
self.receivers.clear()
self._by_sender.clear()
self._by_receiver.clear()
receiver_connected = Signal("""\
Sent by a :class:`Signal` after a receiver connects.
""")
class NamedSignal(Signal):
def __init__(self, name, doc=None):
Signal.__init__(self, doc)
self.name = name
def __repr__(self):
base = Signal.__repr__(self)
return "%s; %r>" % (base[:-1], self.name)
class Namespace(dict):
def signal(self, name, doc=None):
try:
return self[name]
except KeyError:
return self.setdefault(name, NamedSignal(name, doc))
signal = Namespace().signal
if __name__ == '__main__':
dice_roll = signal('dice_roll')
def odd_subscriber(sender, **kwargs):
print("Observed dice roll %r. %r" % (sender, kwargs))
dice_roll.connect(odd_subscriber, sender=0x03)
result = dice_roll.send(3)

View File

@ -0,0 +1,96 @@
__all__ = [
"ServiceMonitor",
]
from misc import Power
import osTimer
class MonitorInterface(object):
def start(self):
"""start"""
def stop(self):
"""stop"""
def status(self):
"""status"""
class ServiceMonitor(MonitorInterface):
THRESHOLD = 3
def __init__(self, service):
self.service = service
self.__failed_count = 0
self.__level = 0
self.__timer = None
self.__ping_count = 0
self.__exception_handlers = {}
self.service.signal.connect(self.ping_handler, sender=0xFE)
def set_level(self, level):
if self.__level != level:
self.__level = level
def __timer_handle(self, *args):
"""定时器调度"""
if self.__ping_count:
self.__failed_count += 1
self.__ping_count = self.__ping_count + 1
self.service.signal.send(0xFE)
if self.__failed_count:
self.__failed_handle()
def set_exception_handlers(self, handler):
if handler is not None:
self.__exception_handlers = handler
def __failed_handle(self):
"""失败处理"""
try:
for k, v in self.__exception_handlers.items():
if k == "reboot":
if v.get("failCount") == self.__failed_count:
# 关机
Power.powerDown()
elif k == "stop":
if v.get("failCount") == self.__failed_count:
# 停止服务
self.stop()
else:
continue
except Exception as e:
print(e)
def ping_handler(self, *args, **kwargs):
self.__ping_count = 0
def start(self):
if self.__level != self.THRESHOLD:
self.service.start()
if self.__timer is None:
self.__timer = osTimer()
self.__timer.start(15000, 1, self.__timer_handle)
return self.status()
def status(self):
return self.service.status()
def clear(self):
self.__failed_count = 0
self.__level = 0
if self.__timer is not None:
self.__timer.stop()
self.__timer.delete()
self.__timer = None
self.__ping_count = 0
def stop(self):
if self.__level != self.THRESHOLD:
self.service.stop()
self.clear()
return self.status
def __call__(self, *args, **kwargs):
return self.service

View File

@ -0,0 +1,72 @@
__all__ = [
"QMessageModel"
]
class ModelDTO(object):
def model_to_dict(self, *args, **kwargs):
pass
def __call__(self, *args, **kwargs):
return self.model_to_dict(*args, **kwargs)
class QMessageModel(ModelDTO):
def __init__(self, ms_id=None, msg_type=0xFF, message=None, sender=None, from_event=None, lock_msg=None):
self.__msg_type = msg_type
self.__message_id = ms_id
self.__message = message
self.__sender = "anonymous" if sender is None else sender
self.__from_event = from_event
self.__lock_msg = lock_msg
@property
def msg_type(self):
return self.__msg_type
@property
def lock_msg(self):
return self.__lock_msg
@property
def message_id(self):
return self.__message_id
@property
def sender(self):
return self.__sender
@property
def message(self):
return self.__message
@property
def from_event(self):
return self.__from_event
def model_to_dict(self):
return dict(message_id=self.message_id, msg_type=self.msg_type, message=self.message, sender=self.sender,
from_event=self.from_event)
class LockMsgModel(ModelDTO):
"""传输对象"""
def __init__(self, lock):
self.__lock = lock
self.__msg = dict(message=None)
@property
def msg(self):
return self.__msg
@msg.setter
def msg(self, msg):
self.__msg = msg
@property
def lock(self):
return self.__lock
def model_to_dict(self, *args, **kwargs):
return dict(lock=self.__lock, msg=self.__msg)

View File

@ -0,0 +1,32 @@
from usr.bin.components.abstract_service import AbstractService
from usr.bin.components.monitor import ServiceMonitor
from usr.utils.service_utils import Singleton
class ExceptionServiceMonitor(ServiceMonitor):
@staticmethod
def create_monitor(config=None):
esm = ExceptionServiceMonitor(ExceptionService())
if config is not None:
esm.set_exception_handlers(config.get('exceptionHandlers', None))
return esm
@Singleton
class ExceptionService(AbstractService):
def __init__(self):
super().__init__("EXCEPTION")
self.error_message = []
def handler_error(self, *args, **kwargs):
msg = "message"
em = kwargs.get(msg, False)
if em:
print(em[msg])
def prepare_before_start(self):
self.signal.connect(self.handler_error, sender="anonymous")
def __call__(self, message):
self.send_msg(message=message)

232
code/usr/bin/guard.py Normal file
View File

@ -0,0 +1,232 @@
from usr.utils.JsonParserUtils import JsonParser
from usr.bin.exception_service import ExceptionService
from usr.utils.service_utils import Singleton
from usr.bin.components.monitor import MonitorInterface
from usr.bin.log_service import LogAdapter
import uos as os
__GROUP_ID = "qpy.quectel.com"
__ARTIFACT_ID = "qpy-framework"
__VERSION = "1.0.0.RELEASE"
def version():
return {"GROUP_ID": __GROUP_ID, "artifact_id": __ARTIFACT_ID, "VERSION": __VERSION}
@Singleton
class Guard(object):
def __init__(self):
self.monitor_service = set()
self.timer = None
self.__status = 0
def register_monitor(self, monitor):
self.monitor_service.add(monitor)
def start(self):
for monitor in self.monitor_service:
monitor.start()
self.__status = 1
def reload_level(self):
for monitor in self.monitor_service:
monitor.set_level(0)
monitor.start()
self.__status = 1
def stop(self):
for monitor in self.monitor_service:
monitor.stop()
self.__status = 0
def status(self):
return self.__status
def upgrade(self):
pass
def monitor_map(self):
return {m.service.name: m.service for m in self.monitor_service}
@Singleton
class GuardContext(object):
def __init__(self):
self.__guard = Guard()
self.system_config = {}
self.service_config = {}
self.config_map = {
"/usr/etc/system_config": self.system_config,
"/usr/etc/app_config": self.service_config
}
self.config_parser = JsonParser()
self.monitor_map = dict()
self.error_handler = ExceptionService()
self.error_handler.start()
def servers(self):
return self.monitor_map.copy()
def get_server(self, name):
return self.monitor_map[name]()
def stop_server(self, name):
return self.monitor_map[name].stop()
@staticmethod
def get_logger(name):
return LogAdapter(name)
@staticmethod
def path_exist(check_path):
try:
os.stat(check_path)
except Exception as e:
return 0
else:
return 1
def load_config_definitions(self):
# load config
# root path /usr/etc
root_path = "/usr/etc/"
stat = self.path_exist(root_path)
if not stat:
return
self.load_configs(root_path)
def load_configs(self, root_path):
# system_path of service /usr/etc/system_config
stat = self.path_exist(root_path)
if not stat:
self.error_handler("[ WARN ] {} path is not exist".format(root_path))
return True
for par_path in os.listdir(root_path):
abs_path = root_path + par_path
for sub_path in os.listdir(abs_path):
truth_path = abs_path + "/" + sub_path
rep_d = self.config_parser.parse(truth_path)
if rep_d["status"] == 0:
# 错误日志收集
self.error_handler("[ WARN ] read {} status {}".format(truth_path, 0))
else:
self.config_map[abs_path][sub_path] = rep_d["data"]
def create_monitors(self):
# 创建monitors
monitor_map = dict()
monitor_map.update(self.create_system_monitors())
monitor_map.update(self.create_app_monitors())
self.monitor_map = monitor_map
def start(self):
# 启动守卫 拉起所有的服务
self.__guard.start()
def register_monitors(self):
# 注册monitor
for k, v in self.monitor_map.items():
self.__guard.register_monitor(v)
def register_monitor(self, k, v):
flag = False
if isinstance(v, MonitorInterface) and isinstance(k, str):
self.monitor_map[k] = v
self.__guard.register_monitor(v)
flag = not flag
return flag
def refresh(self):
self.load_config_definitions()
# 初始化monitor
self.create_monitors()
self.register_monitors()
self.start()
def reload(self):
# 刷新通知
self.__guard.reload_level()
def create_system_monitors(self):
monitor_map = dict()
try:
from usr.bin.net_service import NetServiceMonitor
NET = "net"
net_monitor = NetServiceMonitor.create_monitor(self.system_config.get(NET, None))
monitor_map[NET] = net_monitor
print("[ OK ] create sys monitor net service")
except Exception as e:
# 异常重定向
self.error_handler("[ FAILED ] load net monitor error reason:{}".format(e))
try:
from usr.bin.log_service import LogServiceMonitor
LOG = "log"
log_monitor = LogServiceMonitor.create_monitor(self.system_config.get(LOG, None))
monitor_map[LOG] = log_monitor
print("[ OK ] create sys monitor log service")
except Exception as e:
self.error_handler("[ FAILED ] load log monitor error reason:{}".format(e))
return monitor_map
def create_app_monitors(self):
monitor_map = dict()
try:
from usr.bin.media_service import MediaServiceMonitor
MEDIA = "media"
md_monitor = MediaServiceMonitor.create_monitor(self.service_config.get(MEDIA, None))
if md_monitor is not None:
monitor_map[MEDIA] = md_monitor
else:
raise Exception("media service load error")
print("[ OK ] create app monitor media service")
except Exception as e:
# 异常重定向
self.error_handler("[ FAILED ] load exception monitor error reason: [{}]".format(e))
try:
from usr.bin.exception_service import ExceptionServiceMonitor
EXCEPTION = "exception"
ex_monitor = ExceptionServiceMonitor.create_monitor(self.service_config.get(EXCEPTION, None))
if ex_monitor is not None:
monitor_map[EXCEPTION] = ex_monitor
else:
raise Exception("exception service load error")
print("[ OK ] create app monitor exception service")
except Exception as e:
# 异常重定向
self.error_handler("[ FAILED ] load exception monitor error reason:[{}]".format(e))
try:
from usr.bin.cloud_service import CloudServiceMonitor
CLOUD = "cloud"
cd_monitor = CloudServiceMonitor.create_monitor(self.service_config.get(CLOUD, None))
if cd_monitor is not None:
monitor_map[CLOUD] = cd_monitor
else:
raise Exception("cloud service load error")
print("[ OK ] create app monitor cloud service")
except Exception as e:
# 异常重定向
self.error_handler("[ FAILED ] load cloud monitor error reason:[{}]".format(e))
try:
from usr.bin.pm_service import PMServiceMonitor
PM = "pm"
pm_monitor = PMServiceMonitor.create_monitor(self.service_config.get(PM, None))
if pm_monitor is not None:
monitor_map[PM] = pm_monitor
else:
raise Exception("pm service load error")
print("[ OK ] create app monitor pm service")
except Exception as e:
# 异常重定向
self.error_handler("[ FAILED ] load pm monitor error reason:[{}]".format(e))
return monitor_map
def version(self):
return version()

232
code/usr/bin/log_service.py Normal file
View File

@ -0,0 +1,232 @@
from usr.bin.components.abstract_service import AbstractService
from usr.utils.service_utils import Singleton
from usr.utils.resolver import TimeResolver
from usr.bin.components.monitor import ServiceMonitor
from machine import UART
import uos
LOGGER = "LOG"
class LOG_LV:
DEBUG = "DEBUG"
INFO = "INFO"
WARNING = "WARNING"
ERROR = "ERROR"
CRITICAL = "CRITICAL"
class AbstractLogOutputUtil(object):
def open(self):
pass
def output(self, message, **kwargs):
pass
def close(self):
pass
def __call__(self, message, **kwargs):
self.output(message, **kwargs)
@Singleton
class UartLogOutputUtil(AbstractLogOutputUtil):
def __init__(self, UARTn=UART.UART2):
self.uart = UART(UARTn, 115200, 8, 0, 1, 0)
def output(self, message, **kwargs):
self.uart.write(message)
@Singleton
class PrintLogOutputUtil(AbstractLogOutputUtil):
def output(self, message, **kwargs):
print(message)
@Singleton
class MqttLogOutputUtil(AbstractLogOutputUtil):
def __init__(self):
super().__init__()
def output(self, message, **kwargs):
pass
@Singleton
class ConsoleLogOutputUtil(AbstractLogOutputUtil):
def __init__(self):
super().__init__()
def output(self, message, **kwargs):
pass
@Singleton
class FileLogOutputUtil(AbstractLogOutputUtil):
def __init__(self):
super().__init__()
self.file_name = "log.txt"
self.abs_path = "/usr/log/"
self.content = []
self.f = None
def open(self):
if "log" not in uos.listdir("/usr"):
uos.mkdir(self.abs_path)
self.f = open(self.abs_path + self.file_name, "w+")
def output(self, message, **kwargs):
self.open()
if len(self.content) > 10:
self.close()
def close(self):
self.f.close()
self.f = None
class AbstractFormatUtil(object):
def format(self, *args, **kwargs):
pass
class LogFormatUtil(AbstractFormatUtil):
def format(self, *args, **kwargs):
log_format = "{} {} [{}] - {}\n".format(args[0], args[1], args[2], args[3])
return log_format
class LogServiceMonitor(ServiceMonitor):
@staticmethod
def create_monitor(config=None):
log_service = LogService()
if config is not None:
level = config.get('level')
log_service.set_level(level)
lsm = LogServiceMonitor(log_service)
if config is not None:
lsm.set_exception_handlers(config.get('exceptionHandlers', None))
return lsm
@Singleton
class LogService(AbstractService):
"""
default log is async queue
you can set
"""
def __init__(self):
super().__init__(LOGGER)
self.__reporter = PrintLogOutputUtil()
self.__tr = TimeResolver()
self.format_util = LogFormatUtil()
self.__level_map = {
LOG_LV.DEBUG: 0,
LOG_LV.INFO: 1,
LOG_LV.WARNING: 2,
LOG_LV.ERROR: 3,
LOG_LV.CRITICAL: 4
}
self.low_level = 0
def __set_report(self, report):
if isinstance(report, AbstractLogOutputUtil):
self.__reporter = report
def set_output(self, out_obj):
if isinstance(out_obj, AbstractLogOutputUtil):
self.__reporter.close()
self.__reporter = out_obj
self.__reporter.open()
else:
self.log_send(self.name, LOG_LV.ERROR, '"{}" is not extend AbstractLogOutputUtil'.format(out_obj))
raise Exception('"{}" is not extend AbstractLogOutputUtil'.format(out_obj))
def set_level(self, level):
if level in self.__level_map:
self.low_level = self.__level_map[level]
else:
self.low_level = 0
def log_send(self, sign, level, msg, mode=1):
"""send log deal"""
if self.mode is not None:
mode = self.mode
if self.__level_map[level] >= self.low_level:
if mode:
self.send_msg_async(message=self.format_msg(sign, level, msg))
else:
self.send_msg_sync(message=self.format_msg(sign, level, msg))
def format_msg(self, sign, level, msg):
"""
format msg
year-month-day hour-minute-second weekday service [level] - message
"""
return self.format_util.format(self.__tr.resolver(), sign, level, msg)
def output_msg(self, *args, **kwargs):
msg = "message"
em = kwargs.get(msg, False)
if em:
self.__reporter.output(em[msg])
def prepare_before_stop(self):
self.__reporter.close()
def prepare_before_start(self):
self.signal.connect(self.output_msg, sender="anonymous")
self.__reporter.open()
class LogAdapter(object):
"""
log adapter mode
mode used : adapter proxy single LogService
"""
def __init__(self, name, enable=1):
self.log_service = LogService()
self.name = name
self.enable = enable
self.mode = 1
self.tag = None
def get_tag(self):
if self.tag is None:
return self.name
else:
return self.tag
def critical(self, msg):
if self.enable:
self.log_service.log_send(self.name, LOG_LV.CRITICAL, msg, self.mode)
def debug(self, msg):
if self.enable:
self.log_service.log_send(self.name, LOG_LV.DEBUG, msg, self.mode)
def info(self, msg):
if self.enable:
self.log_service.log_send(self.name, LOG_LV.INFO, msg, self.mode)
def warning(self, msg):
if self.enable:
self.log_service.log_send(self.name, LOG_LV.WARNING, msg, self.mode)
def error(self, msg):
if self.enable:
self.log_service.log_send(self.name, LOG_LV.ERROR, msg, self.mode)
if __name__ == '__main__':
net_ser = LogAdapter("Net")
ls = LogService()
ls.start()
net_ser.debug("111")

View File

@ -0,0 +1,98 @@
from usr.bin.components.abstract_service import AbstractService
from usr.bin.components.monitor import ServiceMonitor
import audio
import utime
MEDIA = "MEDIA"
MEDIA_TYPE_MAP = {
"AUDIO": 0,
"TTS": 1
}
class MediaServiceMonitor(ServiceMonitor):
@staticmethod
def create_monitor(config=None):
media_service = MediaService()
if config is not None:
try:
if config.get('enable', True):
mode = config["params"]["mode"]
return MediaServiceMonitor(media_service)
except Exception as e:
# 这里异常重定向
return None
msm = MediaServiceMonitor(media_service)
if config is not None:
msm.set_exception_handlers(config.get('exceptionHandlers', None))
return msm
class MediaService(AbstractService):
def __init__(self, device=0):
super().__init__(MEDIA)
self.__tts = audio.TTS(device)
self.__audio = audio.Audio(device)
def set_tts(self, mode):
self.__tts = audio.TTS(mode)
def set_audio(self, mode):
self.__audio = audio.Audio(mode)
def set_pa(self, pa):
return self.__audio.set_pa(pa)
def set_mode(self, mode):
if mode in range(0x03):
self.set_audio(mode)
self.set_tts(mode)
else:
raise Exception("mode {} must in mid of [0,1,2]".format(mode))
@property
def tts(self):
return self.__tts
@property
def audio(self):
return self.__audio
def _play(self, sender, **kwargs):
msg_type = kwargs["message"]['msg_type']
msg = kwargs["message"]['message']
# 策略
ret = self.__start_play(msg_type, msg)
while ret == -2:
print(ret, msg)
ret = self.__start_play(msg_type, msg)
utime.sleep(1)
def __start_play(self, msg_type, msg):
if msg_type == MEDIA_TYPE_MAP["TTS"]:
ret = self.__tts.play(msg["priority"], msg["breakin"], msg["mode"], msg['play_data'])
else:
ret = self.__audio.play(msg["priority"], msg["breakin"], msg['play_data'])
return ret
def tts_play(self, priority=4, breakin=0, mode=2, play_data="", sender=None):
message = dict(priority=priority, breakin=breakin, mode=mode, play_data=play_data)
self.send_msg(msg_type=MEDIA_TYPE_MAP["TTS"], sender=sender, message=message)
def audio_play(self, priority=4, breakin=0, play_data="", sender=None):
message = dict(priority=priority, breakin=breakin, play_data=play_data)
self.send_msg(msg_type=MEDIA_TYPE_MAP["AUDIO"], sender=sender, message=message)
def register_event(self):
super().register_event()
self.signal.connect(self._play, sender="anonymous")
if __name__ == '__main__':
media = MediaService()
media.start()
media.audio.setVolume(2)
media.tts_play(play_data="123")

143
code/usr/bin/net_service.py Normal file
View File

@ -0,0 +1,143 @@
from usr.bin.components.abstract_service import AbstractService
from usr.utils.service_utils import Singleton
from usr.bin.components.monitor import ServiceMonitor
import dataCall
import net
import sim
import checkNet
__all__ = ["NetService"]
NET = "NET"
ENABLE_ = "enable"
NET_ = "NET"
DATACALL_ = "DATACALL"
class NetServiceMonitor(ServiceMonitor):
@staticmethod
def create_monitor(config=None):
net_service = NetService()
nsm = NetServiceMonitor(net_service)
if config is not None:
nsm.set_exception_handlers(config.get('exceptionHandlers', None))
return nsm
@Singleton
class NetService(AbstractService):
def __init__(self):
super().__init__(NET)
self.__data_call = dataCall
self.__net = net
self.__sim = sim
self.__net_connect_status = False
self.__data_call_status = False
self.server_status = {
DATACALL_: {ENABLE_: 1},
NET_: {ENABLE_: 0}
}
self.check_net = checkNet.CheckNetwork("QuecPython_Helios_Framework", "this latest version")
@property
def sim(self):
return self.__sim
@property
def data_call(self):
return self.__data_call
@property
def net(self):
return self.__net
def set_enable(self, sr, enable):
"""set start"""
self.server_status[sr][ENABLE_] = enable
def set_apn(self, profileIdx, ipType, apn, username, password, authType):
return self.__data_call.setApn(profileIdx, ipType, apn, username, password, authType)
def ev_dc(self, args):
if self.server_status[DATACALL_][ENABLE_]:
profile_idx = args[0]
datacall_status = args[1]
msg_dict = \
{
"sim_status": None,
"net_status": None,
"datacall_status": datacall_status,
"profile_id": profile_idx,
"ip_type": None,
"IPv4": None,
"IPv4_DNS1": None,
"IPv4_DNS2": None,
"IPv6": None,
"IPv6_DNS1": None,
"IPv6_DNS2": None,
}
sim_status = self.__sim.getStatus()
net_status = self.__net.getState()
datacall_info = self.__data_call.getInfo(profile_idx, 2)
msg_dict.update({"sim_status": sim_status})
if net_status != -1:
if net_status[0][0] == 0 or net_status[1][0] == 0:
msg_dict.update({"net_status": 0})
else:
msg_dict.update({"net_status": net_status[1][0]})
else:
msg_dict.update({"net_status": -1})
if datacall_info != -1:
if datacall_info[2][0] == 1 or datacall_info[3][0] == 1:
msg_dict.update({"datacall_status": 1})
else:
msg_dict.update({"datacall_status": 0})
msg_dict.update({"ip_type": datacall_info[1]})
msg_dict.update({"IPv4": datacall_info[2][2]})
msg_dict.update({"IPv4_DNS1": datacall_info[2][3]})
msg_dict.update({"IPv4_DNS2": datacall_info[2][4]})
msg_dict.update({"IPv6": datacall_info[3][2]})
msg_dict.update({"IPv6_DNS1": datacall_info[3][3]})
msg_dict.update({"IPv6_DNS2": datacall_info[3][4]})
self.send_msg(msg_type=1, message=msg_dict)
def ev_nc(self, args):
if self.server_status[NET_][ENABLE_]:
net_status = args[1]
self.send_msg(msg_type=0, message={"network_register_status": net_status})
def wait_connect(self, timeout):
self.check_net.poweron_print_once()
return self.check_net.wait_network_connected(timeout)
def get_net_status(self):
self.__data_call_status = self.__data_call.getInfo(1, 0)[2][0]
return self.__data_call_status
def register_event(self):
self.__data_call.setCallback(self.ev_dc)
# self.__net.setCallback(self.ev_nc)
if __name__ == '__main__':
net_ser = NetService()
@net_ser.signal.connect_via(0X01)
def test(*args, **kwargs):
print(kwargs)
net_ser.start()
net_ser.send(sender=1, message={})

View File

@ -0,0 +1,52 @@
import pm
from usr.bin.components.abstract_service import AbstractService
from usr.utils.service_utils import Singleton
from usr.bin.components.monitor import ServiceMonitor
ENABLE_ = "enable"
NET_ = "NET"
DATACALL_ = "DATACALL"
class PMServiceMonitor(ServiceMonitor):
@staticmethod
def create_monitor(config=None):
ps_service = PMService()
psm = PMServiceMonitor(ps_service)
if config is not None:
psm.set_exception_handlers(config.get('exceptionHandlers', None))
return psm
PM = "pm"
@Singleton
class PMService(AbstractService):
def __init__(self, flag=1):
super().__init__(PM)
self.pm = pm
self.__pm_lock = pm.create_wakelock("pm_lock", len("pm_lock"))
self.__flag = flag
self.__count = 0
def lock(self):
self.__count += 1
self.pm.wakelock_lock(self.__pm_lock)
def unlock(self):
self.__count -= 1
if self.count() < 1:
self.pm.wakelock_unlock(self.__pm_lock)
def count(self):
if self.__count < 0:
self.__count = 0
return self.__count
def auto_sleep(self, flag):
self.pm.autosleep(flag)
def register_event(self):
self.pm.autosleep(1)

View File

@ -0,0 +1,202 @@
try:
import uos as os
import utime as time
import ujson as json
except:
import os
import time
import json
def Singleton(cls):
_instance = {}
def _singleton(*args, **kargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
return _instance[cls]
return _singleton
@Singleton
class OfflineStorage:
def __init__(self):
self.status = None
self.split_file = False
self.single_file_max = 100
self._file_name_no = 1
self._rec_count = 1
# self.default_dir = "/usr/offline_storage/"
self.default_dir = "./"
self._check_dir()
def _check_dir(self):
try:
os.chdir(self.default_dir)
except:
raise SystemError("Directory is not exist!")
@staticmethod
def _msg_no_gen():
# ts = time.time()
ts = int(time.time())
ts_str = str(ts)
if len(ts_str) > 5:
ts = int(ts_str[-5:])
# 避免时间戳生成时间太短导致重复
time.sleep(1)
return ts
def _get_file_list(self):
file_list = os.listdir(self.default_dir)
file_list.sort()
# 筛选后缀名
file_filtered = []
for file in file_list:
try:
if file[-5:] == ".json":
file_filtered.append(file)
except:
continue
return file_filtered
def _pre_load(self):
# 预读取
data_map = dict()
file_list = self._get_file_list()
if not file_list:
return data_map
file = file_list.pop()
with open(self.default_dir + file, 'r', encoding="utf-8") as f:
try:
file_map = json.load(f)
except:
file_map = dict()
self._rec_count = len(file_map.keys())
data_map.update(file_map)
return data_map
def _write_file(self, data):
file_list = self._get_file_list()
file_list.sort()
if not file_list:
if self.split_file:
if self._rec_count > self.single_file_max:
self._file_name_no += 1
file_name = "data%d.json" % self._file_name_no
else:
file_name = "data.json"
else:
file_name = file_list.pop()
with open(self.default_dir+file_name, "w+", encoding="utf-8") as f:
json.dump(data, f)
self._rec_count += 1
def deposit(self, data):
if self.status == 'r':
return False
self.status = 'w'
# 序号生成
index = self._msg_no_gen()
format_data = {index: data}
data_map = self._pre_load()
data_map.update(format_data)
self._write_file(data_map)
self.status = None
return index
def take_out(self):
if self.status == 'w':
return False
self.status = 'r'
file_list = self._get_file_list()
if not file_list:
return False
file_list.sort()
data_map = dict()
for file in file_list:
with open(self.default_dir + file, 'r', encoding="utf-8") as f:
try:
data_map.update(json.load(f))
except:
pass
os.remove(self.default_dir + file)
self.status = None
return data_map
def take_out_iter(self):
data_map = self.take_out()
for k, v in data_map.items():
yield k, v
def take_out_list(self):
data_map = self.take_out()
return list(data_map.values())
def take_out_by_index(self, index):
if self.status == 'w':
return False
self.status = 'r'
file_list = self._get_file_list()
if not file_list:
return None
file_list.sort()
for file in file_list:
with open(self.default_dir + file, 'r+', encoding="utf-8") as f:
file_map = json.load(f)
if index in file_map:
data = file_map.pop(index)
json.dump(file_map, f)
return data
return None
def take_out_last(self, count=1):
if self.status == 'w':
return False
self.status = 'r'
file_list = self._get_file_list()
if not file_list:
return None
file_list.sort(reverse=True)
take_out_count = 0
data_map = dict()
for file in file_list:
with open(self.default_dir + file, 'r+', encoding="utf-8") as f:
file_map = json.load(f)
items = sorted(file_map.items())
while take_out_count <= count and items:
key, values = items.pop()
data_map[key] = values
file_map.pop(key)
json.dump(file_map, f)
if not items:
os.remove(self.default_dir + file)
return data_map
def count(self):
file_list = self._get_file_list()
data_count = 0
for file in file_list:
with open(self.default_dir + file, 'r', encoding="utf-8") as f:
try:
data_count += len(json.load(f))
except:
pass
return data_count
def preview_data(self):
data_map = dict()
file_list = self._get_file_list()
if not file_list:
return data_map
file_list.sort()
for file in file_list:
with open(self.default_dir + file, 'r', encoding="utf-8") as f:
try:
data_map.update(json.load(f))
except:
pass
return data_map

View File

@ -0,0 +1,189 @@
import osTimer, sys_bus, utime
from machine import Pin, ExtInt
import _thread
"""
ExInt阻断器
enable
disable
"""
class ExIntInterrupter(object):
def __init__(self, gpio, trige_mode=ExtInt.IRQ_RISING, pull_mode=ExtInt.PULL_DISABLE, callback=None):
self.__ext = ExtInt(gpio, trige_mode, pull_mode, callback)
def enable(self):
return self.__ext.enable()
def disable(self):
return self.__ext.disable()
def start(self):
return self.enable()
def stop(self):
return self.disable()
"""
Pin阻断器
write
read
"""
class PinInterrupter(object):
def __init__(self, gpio, trige_mode=Pin.OUT, pull_mode=Pin.PULL_DISABLE, mode=0):
self.gpio = Pin(gpio, trige_mode, pull_mode, mode)
def write(self, value):
self.gpio.write(value)
def read(self):
return self.gpio.read()
def blinker(self, keep_time):
self.write(1)
utime.sleep(keep_time)
self.write(0)
def __blinker(self, count, keep_time):
while count > 0:
self.blinker(keep_time)
def freq_blinker(self, freq, keep_time):
return _thread.start_new_thread(self.blinker, (freq, keep_time))
"""
具有BUS功能的ExInt阻断器
publish
"""
class BusInterrupter(object):
def register_callback(self, callback):
if callback is not None:
sys_bus.subscribe(self.topic, callback)
def default_callback(self, *args, **kwargs):
self.publish(*args, **kwargs)
def publish(self, *args, **kwargs):
sys_bus.publish(self.topic, kwargs)
class BusExIntInterrupter(ExIntInterrupter, BusInterrupter):
def __init__(self, topic, gpio, trige_mode=ExtInt.IRQ_RISING, pull_mode=ExtInt.PULL_DISABLE):
self.topic = topic
super().__init__(gpio, trige_mode, pull_mode, self.default_callback)
def default_callback(self, *args, **kwargs):
self.publish(**dict(gpio=args[0][0], pressure=args[0][1]))
"""
定时器阻断器
start
stop
"""
class TimerInterrupter(object):
def __init__(self, keep_time, callback, period=1):
"""
@param period: 周期
@param callback: 回调
@param loop: 1 循环 0 是一次
"""
self.__timer = osTimer()
self.__keep_time = keep_time
self.__callback = callback
self.__period = period
def start(self):
self.__timer.start(self.__keep_time, self.__period, self.__callback)
def stop(self):
self.__timer.stop()
"""
看门狗
继承于BusExInt 具有bus和ExInt的能力
组合了
定时器阻断器
Pin阻断器
"""
class WatchDog(object):
TOPIC = "WDT_KICK_TOPIC"
def __init__(self, gpio, mode, keep_time, done_pin=None, trige_mode=ExtInt.IRQ_RISING,
pull_mode=ExtInt.PULL_DISABLE):
self.timer_inter = TimerInterrupter(keep_time, self.__process)
self.pin_inter = PinInterrupter(gpio, mode=mode)
if done_pin is not None:
self.bus_ext = BusExIntInterrupter(self.TOPIC, done_pin, trige_mode=trige_mode, pull_mode=pull_mode)
self.bus_ext.enable()
def start(self):
self.timer_inter.start()
def stop(self):
self.timer_inter.stop()
def __process(self, *args, **kwargs):
self.pin_inter.blinker(1)
kwargs.update(dict(msg=self.TOPIC + "_FEED"))
sys_bus.publish(self.TOPIC + "_FEED", kwargs)
"""
ExInt处理器
继承于BusExInt 具有bus和ExInt的能力
"""
class ExIntProcess(BusExIntInterrupter):
TOPIC = "GPIO{}_EXINT"
def __init__(self, pin, trige_mode=Pin.OUT, pull_mode=Pin.PULL_DISABLE):
super().__init__(self.TOPIC.format(pin), pin, trige_mode, pull_mode)
if __name__ == '__main__':
def pin_interrupt_callback(topic, message):
print("pin_interrupt_callback, topic: {}, message: {}".format(topic, message))
def ext_interrupt_callback(topic, message):
print("ext_interrupt_callback, topic: {}, message: {}".format(topic, message))
def kick_feed_interrupt_callback(topic, message):
print("kick_feed_interrupt_callback, topic: {}, message: {}".format(topic, message))
# 订阅gpio的中断
sys_bus.subscribe("GPIO18_EXINT", pin_interrupt_callback)
# 订阅WDT_KICK_TOPIC订阅喂狗中断的回调
sys_bus.subscribe("WDT_KICK_TOPIC", ext_interrupt_callback)
# 订阅喂狗的回调, 每次喂狗都会触发次订阅的回调
sys_bus.subscribe("WDT_KICK_TOPIC_FEED", kick_feed_interrupt_callback)
# 初始化狗
wd = WatchDog(Pin.GPIO15, 1, 20000, Pin.GPIO8)
# 开启喂狗
wd.start()
# 初始化中断的处理器
ep = ExIntProcess(Pin.GPIO5)
# 开启中断
ep.start()

View File

@ -0,0 +1,33 @@
import uos
import ujson
class Parse(object):
def parse(self, *args, **kwargs):
"""parse interface"""
class JsonParser(object):
DEFAULT_FILE_NAME = "config.json"
@classmethod
def composite_url(cls, url):
if not url.endswith("/"):
url += "/"
return url + cls.DEFAULT_FILE_NAME
@classmethod
def parse(cls, url):
rep_d = dict(
status=1,
data=dict()
)
try:
url = cls.composite_url(url)
with open(url, "r") as f:
rep_d["data"] = ujson.load(f)
except Exception as e:
rep_d["status"] = 0
return rep_d
else:
return rep_d

View File

@ -0,0 +1,21 @@
import utime
class TimeResolver(object):
def __init__(self):
self.output_format = "{ascdate} {asctime} {ascweek}"
self.weekday_list = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
def resolver(self, rt=None):
if rt is None:
rt = utime.localtime()
d_f = "{0:02}"
return self.output_format.format(
ascdate=str(rt[0]) + "-" + d_f.format(rt[1]) + "-" + d_f.format(rt[2])
, asctime=d_f.format(rt[3]) + ":" + d_f.format(rt[4]) + ":" + d_f.format(rt[5]),
ascweek=self.weekday_list[rt[6]])
if __name__ == '__main__':
print(TimeResolver().resolver())

View File

@ -0,0 +1,9 @@
def Singleton(cls):
_instance = {}
def _singleton(*args, **kargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
return _instance[cls]
return _singleton

Binary file not shown.

Binary file not shown.

Binary file not shown.