--- /dev/null
+import app_fota
+from misc import Power
+import modem
+import ujson
+import request
+import ql_fs
+import checkNet
+import sys
+import sim
+
+from queue import Queue
+import usys
+import _thread
+import fota
+import utime
+import log
+import uos
+import osTimer
+import net
+import ql_fs
+from ucryptolib import aes
+import hashlib
+from usr.EventMesh import eventMesh
+# 设置日志输出级别
+log.basicConfig(level=log.INFO)
+fota_log = log.getLogger("china_mobile")
+import modem
+import base64
+import request
+import ujson
+
+#中国移动入库
+class chinamobile:
+ def __init__(self):
+ self.retryIntervalTimer = osTimer()#异常重试定时器
+ self.multiDayResend_timer = osTimer()#12小时定时器 多日重发的机制
+
+ self.multimer_count = 0#多日重发机制周期Count
+ self.multimer_init = 0
+ self.power_on_multiflag = 0
+ self.timer_count = 0
+ self.timerPowerNum = 0
+ self.telecom_init_state = 0#初始化状态开机检查标识
+ self.isRegistOk = 0#注册成功
+
+ self.regist_queue = Queue()
+ _thread.stack_size(10*1024)
+ _thread.start_new_thread(self.regist_queue_thread, ())
+ _thread.stack_size(8*1024)
+
+ eventMesh.subscribe("publish_message_event_registinfor",self.publish_message_event_registinfor)
+ self.__net_connected = 0
+ eventMesh.subscribe("get_network_state",self.get_network_state)#订阅消息
+ eventMesh.subscribe("set_network_state",self.set_network_state)#订阅消息
+
+ eventMesh.subscribe("set_dev_version",self.set_dev_version)#订阅消息
+ eventMesh.subscribe("get_dev_version",self.get_dev_version)#订阅消息
+
+ eventMesh.subscribe("set_dev_mac",self.set_dev_mac)#订阅消息
+ eventMesh.subscribe("get_dev_mac",self.get_dev_mac)#订阅消息
+
+ self.regist_timeSec = 0
+ self.iccid_data = ""
+ self.software_version = ""
+ self.mac_data =""
+
+
+ self.retryInterval = 0#异常重试间隔时间 单位分钟
+ self.retryNum = 0#异常重试最大次数
+ self.heartBeatTime = 0#心跳上报频率 单位分钟
+ self.reportTime = 0#最大上报频率 自注册+心跳上报的频率次数不能大于最大上报频率次数
+ self.reportNum = 0 #最大上报次数
+
+ self.reportTime_old = 0 #保存本地 保存时间戳下一次开机使用
+ self.report_Num_old = 0#保存本地
+ self.retryNum_old = 0#保存本地
+ self.init_flag = 0#初始化状态指初始化一次
+ self.retryTimer = osTimer()#异常重试定时器
+ self.retpoertTimerC = osTimer()#上报次数大于下一个周期再触发
+ self.heartreportTimer = osTimer()#心跳上报定时器
+ self.reportTime_Sec = 0
+
+ pass
+ def regist_queue_thread(self):
+ while True:
+ data = self.regist_queue.get()
+ #EventMesh.debug_log("regist_queue:data = {}".format(data))
+ if data != None:
+ try:
+ data["func"](data["data"])
+ except Exception as e:
+ print("regist_queue:ERROR:{}".format(e))
+ usys.print_exception(e)
+ def publish_message_event_registinfor(self,event,msg):
+ self.regist_queue.put(msg)
+
+ def publish_regist_event_cb(self, *args, **kwargs):
+ eventMesh.publish("publish_message_event_registinfor",{"func":self.timer_cb,"data":0})
+
+ def set_network_state(self,event,msg):#设置网络是否正常
+ self.__net_connected = int(msg)
+
+ def get_network_state(self,event,msg):#获取网络是否正常
+ return self.__net_connected
+
+ def set_dev_version(self,event,msg):#获取固件版本号
+ self.software_version = str(msg)
+
+ def get_dev_version(self,event,msg):#获取固件版本号
+ return self.software_version
+
+ def set_dev_mac(self,event,msg):#设置BLE MAC地址
+ self.mac_data = str(msg)
+
+ def get_dev_mac(self,event,msg):#获取BLE MAC地址
+ return self.mac_data
+
+ def sha256_encode(self,key,data):#对数据进行AES256加密
+ h = hashlib.sha256()
+ h.update(key)
+ crypto_key = h.digest()
+ #IV是偏移量 默认16个十六进制00
+ IV = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+
+ crypto = aes(crypto_key, 2, IV)
+
+ #进行PKCS7Padding填充。
+ block_num = len(data) // BLOCK_SIZE+1
+ k = len(data) % BLOCK_SIZE
+ j = len(data) // BLOCK_SIZE
+ padding = BLOCK_SIZE - k
+ i = 0
+ szDataIn = bytearray(data)
+ # print(szDataIn)
+ szDataIn = szDataIn+bytearray(block_num * BLOCK_SIZE -len(data))
+ # print(szDataIn)
+
+ for i in range(padding):
+ szDataIn[j * BLOCK_SIZE + k + i] = padding
+ # print("szDataIn:{} {} {}\r\n".format(szDataIn,j * BLOCK_SIZE + k + i,padding))
+
+ indata = bytes(szDataIn)
+ enc = crypto.encrypt(indata)
+ #最后将加密AES256的数据转换成base64的编码格式
+ msg_tmp = base64.b64encode(enc)
+ data_encode = msg_tmp.decode('ascii')#转换成字符串
+ return data_encode
+
+
+
+ def china_mobile_first_get_config(self,url):
+ ##############第一步#######获取采集规则配置#######
+ brand = "KUMI"#品牌
+
+ model = "Watch Soar"#型号
+ appKey = "M100001131"#不需要就填***appKey 厂商标识
+ sdkVersion = "***"#非SDK传输
+ apiVersion = "4.0.1"#接口定义 固定4.0.1
+ apiType = "I"#非Android
+ templateId = "TY000014"#根据产品分类在平台查询
+ IMEI = modem.getDevImei()
+ imei1 = IMEI#IMEI1或者CMEI
+ imei2 = "***"
+ configType = "autoRegist"#自注册填autoRegist 心跳配置传输 keepAlive
+ preField1 = "***"#预留字段1
+ preField2 = "***"#预留字段2
+ preField3 = "***"#预留字段3
+
+
+
+ #配置拉取数据格式
+ config_data = {"endpoint":"{}||{}||{}||{}||{}||{}||{}||{}||{}||{}||{}||{}".format(brand,model,appKey,sdkVersion,apiVersion,apiType,templateId,imei1,imei2,preField1,preField2,preField3),"configType":"{}".format(configType)}
+
+ #配置拉取数据上报地址
+ url = "https://b.fxltsbl.com//dm-multi-accept/multi/getMultiTerminalConfig"
+ headers_T ={"Content-Type":"application/json"}
+ print("ujson.dumps(config_data):{}".format(ujson.dumps(config_data)))
+ response = request.post(url, data=ujson.dumps(config_data),headers =headers_T)
+ result_json = ""
+ if response.status_code == 200:#服务器响应成功并返回数据
+ content = response.content
+ c = next(content)
+ result_json = ujson.loads(c)
+ print("response.json:{}".format(ujson.loads(c)))
+ return result_json
+
+
+ def first_get_config_json(self):#开机上电获取配置信息
+ getresult = 0
+ if self.__net_connected != 1:#网络不正常
+ return getresult
+ url = "https://b.fxltsbl.com//dm-multi-accept/multi/getMultiTerminalConfig"
+ result_json = self.china_mobile_first_get_config(url)
+ # {
+ # "reportTime":1440,
+ # "reportNum":8,
+ # "heartBeatTime":1440,
+ # "retryInterval":10,
+ # "retryNum":1
+ # }#默认采集规则配置
+ #默认参数
+
+
+ if "rspCode" in result_json:#返回结课code 0000成功 其他参考文档
+ rspCode = result_json["rspCode"]
+ rspCode_zh = result_json["rspMsg"]
+ if rspCode == "0000" or rspCode_zh == "成功":
+ getresult = 1
+ if "result" in result_json:
+ msg_data = result_json["result"] #采集规则配置信息
+ if "ruleConfig" in msg_data:#不存在就是空
+ msg_data = msg_data["ruleConfig"]
+
+ msg_data = ujson.loads(msg_data)#转换成json格式
+ if "reportTime" in msg_data:
+ self.reportTime = int(msg_data["reportTime"])
+ if "heartBeatTime" in msg_data:
+ self.heartBeatTime = int(msg_data["heartBeatTime"])
+ if "reportNum" in msg_data:
+ self.reportNum = int(msg_data["reportNum"])
+ if "retryInterval" in msg_data:
+ self.retryInterval = int(msg_data["retryInterval"])
+ if "retryNum" in msg_data:
+ self.retryNum = int(msg_data["retryNum"])
+ print(self.reportTime,self.heartBeatTime,self.reportNum,self.retryInterval,self.retryNum)
+
+ print("true")
+ else:
+ print("false")
+ return getresult #1 获取成功 0 获取失败
+
+
+ def china_mobile_autoregist_infor(self,url,configType_data):
+ #########################第二步############自注册上报###################
+ #设备自注册上报
+ dev_autoregist_result = 0 #默认是1成功 0是失败
+ if self.__net_connected != 1:#网络不正常
+ return dev_autoregist_result
+ #密钥
+ key = "f74c005251dc40c4b64a283827da3812"
+
+ brand = "{}".format("KUMI")#品牌
+
+ model = "Watch Soar"#型号
+ appKey = "M100001131"#不需要就填***appKey 厂商标识
+ sdkVersion = "***"#非SDK传输
+ apiVersion = "4.0.1"#接口定义 固定4.0.1
+ apiType = "I"#非Android
+ templateId = "TY000014"#根据产品分类在平台查询
+ IMEI = modem.getDevImei()
+ imei1 = IMEI#IMEI1或者CMEI
+ imei2 = "***"
+ configType = configType_data#自注册填autoRegist 心跳配置传输 keepAlive 暂时不用
+ preField1 = "***"#预留字段1
+ preField2 = "***"#预留字段2
+ preField3 = "***"#预留字段3
+
+ #不需要加密 #||分隔符不能超过11个 否则平台会解析出错
+ config_data = "{}||{}||{}||{}||{}||{}||{}||{}||{}||{}||{}||{}".format(brand,model,appKey,sdkVersion,apiVersion,apiType,templateId,imei1,imei2,configType,preField1,preField2)
+
+
+ #需要加密的数据明文
+ if (len(self.mac_data) == 0)#获取失败了
+ self.mac_data = "28:09:78:56:01:06"
+
+ MAC = self.mac_data
+ # base64_MAC = sha256_encode(key,MAC)
+ RAM = "35MB"
+ # base64_RAM = sha256_encode(key,RAM)
+ ROM = "144MB"
+ # base64_ROM = sha256_encode(key,ROM)
+ CPU = "YH601S"
+ # base64_CPU = sha256_encode(key,CPU)
+ sysVersion = "OpenHarmony 3.2"
+ # base64_sysVersion = sha256_encode(key,sysVersion)
+ if (len(self.software_version) == 0)#获取失败了
+ self.software_version = "Watch Soar V1.0"
+ softwareVer = self.software_version
+ # base64_softwareVer = sha256_encode(key,softwareVer)
+ softwareName ="FD230"
+ # base64_softwareName = sha256_encode(key,softwareName)
+ volte="0" #0:开;1:关;-1:不支持
+ # base64_volte = sha256_encode(key,volte)
+ netType="4G"
+ # base64_netType = sha256_encode(key,netType)
+
+ resolution="466*466"
+ # base64_resolution = sha256_encode(key,resolution)
+
+ screenSize = "1.43"
+ # base64_screenSize = sha256_encode(key,screenSize)
+ wearingStatus ="***"
+ # base64_wearingStatus = sha256_encode(key,wearingStatus)
+
+ networkStatus ="0"#1:连接;0:未连接;-1:不支持
+ # base64_networkStatus = sha256_encode(key,networkStatus)
+ encyr_data = [MAC,RAM,ROM,CPU,sysVersion,softwareVer,softwareName,volte,netType,resolution,screenSize,wearingStatus,networkStatus]
+ print(len(encyr_data))
+ encyr_new_data=[]
+ for i in range(len(encyr_data)):
+ tmp_data = sha256_encode(key,encyr_data[i])
+ encyr_new_data.append(tmp_data)
+
+ # deviceInfo_tmp = {
+ # "bluetoothMac":"{}".format(base64_MAC),#mac地址可填可不填
+ # "ram":"{}".format(base64_RAM),#ram信息
+ # "rom":"{}".format(base64_ROM),#rom信息
+ # "cpu":"{}".format(base64_CPU),#cpu信息
+ # "sysVersion":"{}".format(base64_sysVersion),#操作系统版本号
+ # "softwareVer":"{}".format(base64_softwareVer),#固件版本号
+ # "softwareName":"{}".format(base64_softwareName),#固件名称
+ # "volte":"{}".format(base64_volte),#volte开关 默认是开
+ # "netType":"{}".format(base64_netType),#当前网络类型4G
+ # "resolution":"{}".format(base64_resolution),#屏幕分辨率466*466
+ # "screenSize":"{}".format(base64_screenSize),#屏幕尺寸默认1.43
+ # "wearingStatus":"{}".format(base64_wearingStatus),#佩戴状态不支持
+ # "networkStatus":"{}".format(base64_networkStatus)#网络状态
+
+ # }
+ deviceInfo_tmp = {
+ "bluetoothMac":"{}".format(encyr_new_data[0]),#mac地址可填可不填
+ "ram":"{}".format(encyr_new_data[1]),#ram信息
+ "rom":"{}".format(encyr_new_data[2]),#rom信息
+ "cpu":"{}".format(encyr_new_data[3]),#cpu信息
+ "sysVersion":"{}".format(encyr_new_data[4]),#操作系统版本号
+ "softwareVer":"{}".format(encyr_new_data[5]),#固件版本号
+ "softwareName":"{}".format(encyr_new_data[6]),#固件名称
+ "volte":"{}".format(encyr_new_data[7]),#volte开关 默认是开
+ "netType":"{}".format(encyr_new_data[8]),#当前网络类型4G
+ "resolution":"{}".format(encyr_new_data[9]),#屏幕分辨率466*466
+ "screenSize":"{}".format(encyr_new_data[10]),#屏幕尺寸默认1.43
+ "wearingStatus":"{}".format(encyr_new_data[11]),#佩戴状态不支持
+ "networkStatus":"{}".format(encyr_new_data[12])#网络状态
+ }
+ json_data = ujson.dumps(deviceInfo_tmp)#需要转义的字符串
+
+ send_autoregist = {
+ "endpoint":config_data,
+ "deviceInfo":"{}".format(json_data)
+ }
+
+ test_data = ujson.dumps(send_autoregist)#需要转义的字符串
+ print("test_data:{}".format(test_data))
+ headers_T ={"Content-Type":"application/json"}
+ response = request.post(url, data=ujson.dumps(send_autoregist),headers =headers_T)
+ result_json = ""
+
+ if response.status_code == 200:
+ dev_autoregist_result = 1
+ content = response.content
+ c = next(content)
+ result_json = ujson.loads(c)#解析成json格式
+ else:
+
+
+ if "rspCode" in result_json:
+ rspCode = result_json["rspCode"]
+ rspMsg = result_json["rspMsg"]
+ if rspCode == "0000" or rspMsg == "成功":
+ dev_autoregist_result = 1
+ else:
+ dev_autoregist_result = 0
+ else:
+ dev_autoregist_result = 0
+
+ return dev_autoregist_result
+
+ def read_config_file(self):#获取配置文件,一开机是没有配置文件的
+ self.retryInterval = 10
+ self.retryNum = 1#异常重试最大次数
+ self.heartBeatTime = 1440#心跳上报频率 单位分钟
+ self.reportTime = 1440#最大上报频率 自注册+心跳上报的频率次数不能大于最大上报频率次数
+ self.reportNum = 8 #最大上报次数
+ msg = None
+ IFEXIST = ql_fs.path_exists("/usr/config_msg.json") #不存在
+ if IFEXIST == True:
+ with open("/usr/config_msg.json","r",encoding='utf-8') as fd:
+ msg = ujson.loads(fd)
+
+ if msg != None:
+ if "retryInterval" in msg:
+ self.retryInterval = int(msg["retryInterval"])
+ if "retryNum" in msg:
+ self.retryNum = int(msg["retryNum"])
+ if "heartBeatTime" in msg:
+ self.heartBeatTime = int(msg["heartBeatTime"])
+ if "reportTime" in msg:
+ self.reportTime = int(msg["reportTime"])
+ if "reportNum" in msg:
+ self.reportNum = int(msg["reportNum"])
+ print("read_config_file:self.retryInterval:{} self.retryNum:{} self.heartBeatTime:{} self.reportTime:{} self.reportNum:{}".format(self.retryInterval,self.retryNum,self.heartBeatTime,self.reportTime,self.reportNum))
+ def retryTimerCb(self,*argc):#异常重试机制定时器回调
+ self.retryNum_old+=1 #保存本地
+
+ if self.retryNum_old > self.retryNum:
+ self.retryTimer.stop()
+ return
+ #触发查询上报总数流程
+ self.search_report_num_step()
+ pass
+
+ def error_retryNum_step(self):#判读报错次数是否大于self.retryNum 步骤
+ print("error_retryNum_step:self.retryNum_oldd:{} self.retryNum:{}".format(self.retryNum_old,self.retryNum))
+ if self.retryNum_old > self.retryNum:#self.retryNum_old > 10
+ self.retpoertTimerC.start(self.reportTime*60*1000,0,self.reportTimerIntervalCb)
+ else:
+ get_result = self.first_get_config_json()#发起向服务端请求配置信息下发到设备端
+ if get_result != 1:#获取失败或者没有网络 走异常重试机制
+ self.retryTimer.start(self.retryInterval*60*1000,0,self.retryTimerCb)
+ pass
+ else:#清空报错次数
+ self.retryNum_old = 0#保存本地
+ data = {
+ "retryInterval":self.retryInterval,
+ "retryNum":self.retryNum,
+ "reportTime":self.reportTime,
+ "reportTime":self.reportTime,
+ "reportNum":self.reportNum
+ }
+ with open("/usr/config_msg.json", 'w+',encoding='utf-8') as fd:#写文件数据
+ ujson.dump(data,fd)
+ #服务端注册返回成功
+ #第一步触发自注册
+ report_result = self.self_regis_report_cb()
+
+ #第二步触发心跳上报次数
+ if report_result == 1:#注册完成之后根据服务端下发的配置 间隔上报心跳
+ self.heart_report_cb()
+ self.heartreportTimer.start(self.heartBeatTime*60*1000,1,self.heartreportTimerCb)
+
+ self.heartreportTimerCb(self,*argc):
+ self.heart_report_cb()
+
+ def check_current_time(self):#检测当前时间是否超过self.reportTime 超过清空报错次数 清空上报总次数
+ #
+ date_tmp = utime.localtime()
+ time_tmp = utime.mktime(date_tmp)#获取时间戳
+ if time_tmp < self.reportTime_old:
+ time_tmp = self.reportTime_old
+ if self.reportTime_old == 0:
+ self.reportTime_old = time_tmp
+ #self.reportTime = 20*60
+ print("check_current_time:time_tmp - self.reportTime_old:{} self.reportTime*60:{}".format(time_tmp - self.reportTime_old,self.reportTime*60))
+ if (time_tmp - self.reportTime_old) >= self.reportTime*60:#是否大于上报的时间self.reportTime 清空保存的上报总数 self.report_Num_old = 0 上报错误次数 self.retryNum_old = 0
+ #大于一个周期时间需要等下一个周期触发 不能立马去检查 需要定时reportTime之后再去清空数据
+ self.reportTime_Sec = time_tmp
+ self.retpoertTimerC.start(10*60*1000,1,self.retpoertTimerCB)
+
+ else:#当前时间-旧的保存时间 小于reportTime 判断报错次数是否大于retryNum=10
+ self.error_retryNum_step()
+
+ def retpoertTimerCB(self,*argc):
+ date_tmp = utime.localtime()
+ time_tmp = utime.mktime(date_tmp)#获取时间戳
+ if time_tmp - self.reportTime_Sec >= self.reportTime*60:
+ self.report_Num_old = 0#清空上报总次数 #保存本地
+ self.retryNum_old = 0#清空上报错误次数 #保存本地
+
+ self.retpoertTimerC.stop()#关闭定时器
+ self.error_retryNum_step()#清空报错次数重新走判断报错次数
+ pass
+
+ def search_report_num_step(self):#查询上报总数步骤
+ print("search_report_num_step:self.retryInterval:{} self.retryNum:{}".format(self.report_Num_old,self.reportNum))
+ if self.report_Num_old > self.reportNum:#查询上报的总次数是否大于存储上报总次数
+ #答:报错次数过多,结束LWM2M是结束这次会话,ruleConfig有最大上报时间频率,
+ # reportTime字段,有最大上报次数,reportNum字段。表示最大上报时间频率内,不能超过最大上报次数,
+ # 如reportTime=10,reportNum=3表示10分钟内最大上报3次,如果超过3次,则需要等待10分钟之后再重新发起注册流程。
+ #检查当前时间是否大于reportTimer 1440分钟之内最多上报8次 需要自注册上报或者心跳上报成功后保存当前的时间戳 如果下一个心跳间隔时间到了次数加1大于上报最大次数
+ #用时间戳相减 大于 reportTimer就不上报了 就等下一个14440分钟了
+ #
+ self.check_current_time()#检测当前时间是否超过self.reportTime 超过清空报错次数 清空上报总次数
+ else:#读取报错次数
+ self.error_retryNum_step()
+
+ def reportTimerIntervalCb(self,*argc):#最大上报频率时间间隔定时器
+ self.search_report_num_step()#重新查询
+
+ def self_regis_report_cb(self):#自注册上报信息
+ date_tmp = utime.localtime()
+ time_tmp = utime.mktime(date_tmp)#获取时间戳
+ print("self_regis_report_cb:self.report_Num_old:{} self.reportNum:{}".format(self.report_Num_old,self.reportNum))
+ self.report_Num_old+=1 ##保存本地
+ if report_Num_old > self.reportNum:
+
+ self.retryTimer.stop()#关定时器
+ self.retpoertTimerC.stop()
+ self.heartreportTimer.stop()
+ self.reportTime_old = time_tmp #需要保存本地
+ self.retpoertTimerC.start(self.reportTime*60*1000,0,self.reportTimerIntervalCb)
+ return 0
+ self.reportTime_old = time_tmp #需要保存本地
+ #设备自注册上报信息
+ url = "https://b.fxltsbl.com//dm-multi-accept/multi/acceptMultiTerminalRegInfo"#上报地址
+ confing_type = "autoRegist"#上报传输配置
+ result = self.china_mobile_autoregist_infor(url,confing_type)
+ if result != 1:
+ #触发异常重试进制
+ self.retryTimer.start(self.retryInterval*60*1000,0,self.retryTimerCb)
+ return 0
+ else:
+ return 1
+
+ def heart_report_cb(self):#心跳上报信息
+ #设备心跳上报信息
+ print("heart_report_cb:self.report_Num_old:{} self.reportNum:{}".format(self.report_Num_old,self.reportNum))
+ date_tmp = utime.localtime()
+ time_tmp = utime.mktime(date_tmp)#获取时间戳
+ self.report_Num_old+=1#保存本地
+ if report_Num_old > self.reportNum:
+ self.retryTimer.stop()#关定时器
+ self.retpoertTimerC.stop()
+ self.heartreportTimer.stop()
+ self.reportTime_old = time_tmp#需要保存本地
+
+ self.retpoertTimerC.start(self.reportTime*60*1000,0,self.reportTimerIntervalCb)
+ return
+
+ url = "https://b.fxltsbl.com//dm-multi-accept/multi/acceptMultiTerminalHeartbeatInfo"#上报地址
+ confing_type = "keepAlive"#上报传输配置
+ result = self.china_mobile_autoregist_infor(url,confing_type)
+ if result != 1:
+ #触发异常重试进制
+ self.retryTimer.start(self.retryInterval*60*1000,0,self.retryTimerCb)
+ return
+
+ def china_mobile_init(self):#初始化
+ #读配置文件
+ self.read_config_file()#只读一下配置文件
+ #查询上报总数流程
+ self.search_report_num_step()
+
+
+
+
+chinamobileCb = chinamobile()
+
+
+
+
+
+
+
+
+
+
+
+