|
@@ -1,19 +1,28 @@
|
|
|
#!/usr/bin/env python3
|
|
|
# -*- coding: utf-8 -*-
|
|
|
-import os
|
|
|
import hashlib
|
|
|
+import json
|
|
|
+import os
|
|
|
+import threading
|
|
|
import time
|
|
|
|
|
|
import boto3
|
|
|
import botocore
|
|
|
+import requests
|
|
|
+from django.core.paginator import Paginator
|
|
|
from django.db import transaction
|
|
|
from django.views.generic.base import View
|
|
|
+from packaging import version as pacVer
|
|
|
|
|
|
from Ansjer.config import BASE_DIR, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
|
|
|
-from Object.TokenObject import TokenObject
|
|
|
+from Ansjer.config import LOGGER, CONFIG_TEST, SERVER_DOMAIN, CONFIG_CN, CONFIG_INFO
|
|
|
+from Model.models import Equipment_Version, App_Info, AppSetModel, App_Colophon, Pc_Info, CountryModel, \
|
|
|
+ Device_Info, UidSetModel, Device_User, IPAddr, DeviceVersionInfo, iotdeviceInfoModel
|
|
|
+from Object.RedisObject import RedisObject
|
|
|
from Object.ResponseObject import ResponseObject
|
|
|
+from Object.TokenObject import TokenObject
|
|
|
+from Object.UrlTokenObject import UrlTokenObject
|
|
|
from Service.CommonService import CommonService
|
|
|
-from Model.models import Equipment_Version, App_Info, AppSetModel, App_Colophon, Pc_Info
|
|
|
|
|
|
|
|
|
class VersionManagement(View):
|
|
@@ -70,6 +79,10 @@ class VersionManagement(View):
|
|
|
return self.editPcVersion(request_dict, response)
|
|
|
elif operation == 'deletePcInfo':
|
|
|
return self.deletePcInfo(request_dict, response)
|
|
|
+ elif operation == 'getCountryList':
|
|
|
+ return self.getCountryList(request_dict, response)
|
|
|
+ elif operation == 'deviceAutoUpdate':
|
|
|
+ return self.device_auto_update(userID, request_dict, response)
|
|
|
else:
|
|
|
return response.json(404)
|
|
|
|
|
@@ -87,7 +100,7 @@ class VersionManagement(View):
|
|
|
line = int(pageSize)
|
|
|
|
|
|
try:
|
|
|
- equipment_version_qs = Equipment_Version.objects.filter()
|
|
|
+ equipment_version_qs = Equipment_Version.objects.filter().order_by('-update_time')
|
|
|
if mci:
|
|
|
equipment_version_qs = equipment_version_qs.filter(mci=mci)
|
|
|
if lang:
|
|
@@ -98,10 +111,20 @@ class VersionManagement(View):
|
|
|
total = equipment_version_qs.count()
|
|
|
equipment_version_qs = equipment_version_qs.values()[(page - 1) * line:page * line]
|
|
|
equipment_version_list = CommonService.qs_to_list(equipment_version_qs)
|
|
|
+
|
|
|
+ for equipment_version in equipment_version_list:
|
|
|
+ new_equipment_version = equipment_version['version'][1:]
|
|
|
+ d_code = new_equipment_version.rsplit('.', 1)[1]
|
|
|
+ software_ver = new_equipment_version.rsplit('.', 1)[0].replace('V', '')
|
|
|
+ device_ver_info_qs = DeviceVersionInfo.objects.filter(d_code=d_code, software_ver=software_ver)
|
|
|
+ if device_ver_info_qs.exists():
|
|
|
+ equipment_version['is_hav_dev_ver_info'] = 1
|
|
|
+ else:
|
|
|
+ equipment_version['is_hav_dev_ver_info'] = 0
|
|
|
return response.json(0, {'list': equipment_version_list, 'total': total})
|
|
|
except Exception as e:
|
|
|
print(e)
|
|
|
- return response.json(500, repr(e))
|
|
|
+ return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
|
|
|
|
|
|
def upLoadFile(self, request, request_dict, response):
|
|
|
file = request.FILES.get('file', None)
|
|
@@ -114,66 +137,75 @@ class VersionManagement(View):
|
|
|
Description = request_dict.get('Description', '')
|
|
|
status = request_dict.get('status', 0)
|
|
|
isPopup = request_dict.get('isPopup', 0)
|
|
|
+ auto_update = request_dict.get('autoUpdate', 0)
|
|
|
+ data_json = request_dict.get('dataJson', None)
|
|
|
|
|
|
if not all([file, mci, lang, ESN, max_ver, channel, resolutionRatio]):
|
|
|
return response.json(444)
|
|
|
|
|
|
try:
|
|
|
- nowTime = CommonService.timestamp_to_str(timestamp=int(time.time()))
|
|
|
- channel = int(channel)
|
|
|
- resolutionRatio = int(resolutionRatio)
|
|
|
- status = int(status)
|
|
|
- isPopup = int(isPopup)
|
|
|
- # 文件名为设备版本,最后一个'.'的前面为软件版本,后面为设备规格名称
|
|
|
- # V2.2.4.16E201252CA,软件版本:2.2.4,设备规格名称:16E201252CA
|
|
|
- # V1.7.2.36C11680X30411F000600000150001Z,软件版本:1.7.2,设备规格名称:36C11680X30411F000600000150001Z
|
|
|
- file_name = str(file) # 文件名
|
|
|
- # .img和.tar.gz文件
|
|
|
- file_type_index = file_name.find('.img')
|
|
|
- if file_type_index == -1:
|
|
|
- file_type_index = file_name.find('.tar')
|
|
|
+ with transaction.atomic():
|
|
|
+ nowTime = CommonService.timestamp_to_str(timestamp=int(time.time()))
|
|
|
+ channel = int(channel)
|
|
|
+ resolutionRatio = int(resolutionRatio)
|
|
|
+ status = int(status)
|
|
|
+ isPopup = int(isPopup)
|
|
|
+ if data_json:
|
|
|
+ data_json = eval(data_json)
|
|
|
+ # 文件名为设备版本,最后一个'.'的前面为软件版本,后面为设备规格名称
|
|
|
+ # V2.2.4.16E201252CA,软件版本:2.2.4,设备规格名称:16E201252CA
|
|
|
+ # V1.7.2.36C11680X30411F000600000150001Z,软件版本:1.7.2,设备规格名称:36C11680X30411F000600000150001Z
|
|
|
+ file_name = str(file) # 文件名
|
|
|
+ # .img和.tar.gz文件
|
|
|
+ file_type_index = file_name.find('.img')
|
|
|
if file_type_index == -1:
|
|
|
- return response.json(903)
|
|
|
- version = file_name[:file_type_index] # 设备版本
|
|
|
- version_index = version.rindex('.')
|
|
|
- softwareVersion = version[1:version_index] # 软件版本
|
|
|
- code = version[version_index + 1:] # 设备规格名称
|
|
|
- chipModelList2Code = code[:4] # 主芯片
|
|
|
- type = code[8:10] # 设备机型
|
|
|
- companyCode = code[-1:] # 公司代码
|
|
|
- fileSize = file.size
|
|
|
- filePath = '/'.join(('static/otapack', mci, lang, file_name))
|
|
|
- file_data = file.read()
|
|
|
- fileMd5 = hashlib.md5(file_data).hexdigest()
|
|
|
- data_dict = {'mci': mci, 'lang': lang, 'ESN': ESN, 'max_ver': max_ver, 'channel': channel,
|
|
|
- 'resolutionRatio': resolutionRatio, 'Description': Description, 'status': status,
|
|
|
- 'version': version, 'softwareVersion': softwareVersion, 'code': code,
|
|
|
- 'chipModelList2Code': chipModelList2Code, 'type': type, 'companyCode': companyCode,
|
|
|
- 'fileSize': fileSize, 'filePath': filePath, 'fileMd5': fileMd5, 'update_time': nowTime,
|
|
|
- 'is_popup': isPopup}
|
|
|
- # Equipment_Version表创建或更新数据
|
|
|
- equipment_version_qs = Equipment_Version.objects.filter(code=code, lang=lang)
|
|
|
- if not equipment_version_qs.exists():
|
|
|
- Equipment_Version.objects.create(eid=CommonService.getUserID(getUser=False, setOTAID=True),
|
|
|
- **data_dict)
|
|
|
- else:
|
|
|
- equipment_version_qs.update(**data_dict)
|
|
|
-
|
|
|
- # 上传文件到服务器
|
|
|
- upload_path = '/'.join((BASE_DIR, 'static/otapack', mci, lang)).replace('\\', '/') + '/'
|
|
|
- if not os.path.exists(upload_path): # 上传目录不存在则创建
|
|
|
- os.makedirs(upload_path)
|
|
|
- # 文件上传
|
|
|
- full_name = upload_path + file_name
|
|
|
- if os.path.exists(full_name): # 删除同名文件
|
|
|
- os.remove(full_name)
|
|
|
- with open(full_name, 'wb+') as write_file:
|
|
|
- for chunk in file.chunks():
|
|
|
- write_file.write(chunk)
|
|
|
- return response.json(0)
|
|
|
+ file_type_index = file_name.find('.tar')
|
|
|
+ if file_type_index == -1:
|
|
|
+ return response.json(903)
|
|
|
+ version = file_name[:file_type_index] # 设备版本
|
|
|
+ version_index = version.rindex('.')
|
|
|
+ softwareVersion = version[1:version_index] # 软件版本
|
|
|
+ code = version[version_index + 1:] # 设备规格名称
|
|
|
+ chipModelList2Code = code[:4] # 主芯片
|
|
|
+ type = code[8:10] # 设备机型
|
|
|
+ companyCode = code[-1:] # 公司代码
|
|
|
+ fileSize = file.size
|
|
|
+ filePath = '/'.join(('static/otapack', mci, lang, file_name))
|
|
|
+ file_data = file.read()
|
|
|
+ fileMd5 = hashlib.md5(file_data).hexdigest()
|
|
|
+ data_dict = {'mci': mci, 'lang': lang, 'ESN': ESN, 'max_ver': max_ver, 'channel': channel,
|
|
|
+ 'resolutionRatio': resolutionRatio, 'Description': Description, 'status': status,
|
|
|
+ 'is_popup': isPopup, 'version': version, 'softwareVersion': softwareVersion, 'code': code,
|
|
|
+ 'chipModelList2Code': chipModelList2Code, 'type': type, 'companyCode': companyCode,
|
|
|
+ 'fileSize': fileSize, 'filePath': filePath, 'fileMd5': fileMd5, 'update_time': nowTime,
|
|
|
+ 'data_json': data_json, 'auto_update': auto_update}
|
|
|
+ # Equipment_Version表创建或更新数据
|
|
|
+ equipment_version_qs = Equipment_Version.objects.filter(code=code, lang=lang)
|
|
|
+ if not equipment_version_qs.exists():
|
|
|
+ Equipment_Version.objects.create(eid=CommonService.getUserID(getUser=False, setOTAID=True),
|
|
|
+ **data_dict)
|
|
|
+ else:
|
|
|
+ equipment_version_qs.update(**data_dict)
|
|
|
+
|
|
|
+ # 上传文件到服务器
|
|
|
+ upload_path = '/'.join((BASE_DIR, 'static/otapack', mci, lang)).replace('\\', '/') + '/'
|
|
|
+ if not os.path.exists(upload_path): # 上传目录不存在则创建
|
|
|
+ os.makedirs(upload_path)
|
|
|
+ # 文件上传
|
|
|
+ full_name = upload_path + file_name
|
|
|
+ if os.path.exists(full_name): # 删除同名文件
|
|
|
+ os.remove(full_name)
|
|
|
+ with open(full_name, 'wb+') as write_file:
|
|
|
+ for chunk in file.chunks():
|
|
|
+ write_file.write(chunk)
|
|
|
+ LOGGER.info('versionManagement/upLoadFile成功上传{}'.format(file_name))
|
|
|
+ if not DeviceVersionInfo.objects.filter(d_code=code, software_ver=softwareVersion).exists():
|
|
|
+ return response.json(0, "该版本尚未添加设备版本信息")
|
|
|
+ return response.json(0)
|
|
|
except Exception as e:
|
|
|
- print(e)
|
|
|
- return response.json(500, repr(e))
|
|
|
+ LOGGER.info(
|
|
|
+ 'versionManagement/upLoadFile接口异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
|
|
|
+ return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
|
|
|
|
|
|
def editVersionInformation(self, request_dict, response):
|
|
|
eid = request_dict.get('eid', None)
|
|
@@ -184,22 +216,26 @@ class VersionManagement(View):
|
|
|
resolutionRatio = request_dict.get('resolutionRatio', '')
|
|
|
Description = request_dict.get('Description', '')
|
|
|
is_popup = request_dict.get('is_popup', '')
|
|
|
+ auto_update = request_dict.get('autoUpdate', 0)
|
|
|
+ data_json = request_dict.get('dataJson', None)
|
|
|
|
|
|
if not eid:
|
|
|
return response.json(444)
|
|
|
status = 1 if status == 'true' else 0
|
|
|
+ if data_json:
|
|
|
+ data_json = eval(data_json)
|
|
|
try:
|
|
|
equipment_version_qs = Equipment_Version.objects.filter(eid=eid)
|
|
|
if not equipment_version_qs.exists():
|
|
|
return response.json(173)
|
|
|
data_dict = {'ESN': ESN, 'max_ver': max_ver, 'status': status, 'channel': channel,
|
|
|
- 'resolutionRatio': resolutionRatio, 'Description': Description,
|
|
|
- 'is_popup': is_popup}
|
|
|
+ 'auto_update': auto_update, 'data_json': data_json,
|
|
|
+ 'resolutionRatio': resolutionRatio, 'Description': Description, 'is_popup': is_popup}
|
|
|
equipment_version_qs.update(**data_dict)
|
|
|
return response.json(0)
|
|
|
except Exception as e:
|
|
|
print(e)
|
|
|
- return response.json(500, repr(e))
|
|
|
+ return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
|
|
|
|
|
|
def deleteEquipmentVersion(self, request_dict, response):
|
|
|
eid = request_dict.get('eid', None)
|
|
@@ -218,7 +254,7 @@ class VersionManagement(View):
|
|
|
return response.json(0)
|
|
|
except Exception as e:
|
|
|
print(e)
|
|
|
- return response.json(500, repr(e))
|
|
|
+ return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
|
|
|
|
|
|
def getAppVersionList(self, request_dict, response):
|
|
|
app_type = request_dict.get('app_type', None)
|
|
@@ -635,4 +671,243 @@ class VersionManagement(View):
|
|
|
return response.json(0)
|
|
|
except Exception as e:
|
|
|
print(e)
|
|
|
- return response.json(500, repr(e))
|
|
|
+ return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
|
|
|
+
|
|
|
+ def getCountryList(self, request_dict, response):
|
|
|
+ try:
|
|
|
+ country_qs = CountryModel.objects.all().values_list('country_name', flat=True)
|
|
|
+ return response.json(0, list(country_qs))
|
|
|
+ except Exception as e:
|
|
|
+ print(e)
|
|
|
+ return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def device_auto_update(cls, user_id, request_dict, response):
|
|
|
+ verId = request_dict.get('verId', None)
|
|
|
+ if not verId:
|
|
|
+ return response.json(444)
|
|
|
+ LOGGER.info(f'版本ID:{verId},操作用户{user_id}')
|
|
|
+ version_qs = Equipment_Version.objects.filter(eid=verId, auto_update=True, status=1)
|
|
|
+ if not version_qs.exists():
|
|
|
+ return response.json(173)
|
|
|
+
|
|
|
+ agent_thread = threading.Thread(
|
|
|
+ target=VersionManagement().device_async_update,
|
|
|
+ args=(version_qs.first(),) # 取单个对象
|
|
|
+ )
|
|
|
+ agent_thread.start()
|
|
|
+ return response.json(0)
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def device_async_update(version_qs):
|
|
|
+ code = version_qs.code
|
|
|
+ version = version_qs.softwareVersion
|
|
|
+ ver_data = version_qs.data_json
|
|
|
+ uid_set_qs = UidSetModel.objects.filter(ucode=code).exclude(version=version) \
|
|
|
+ .values('uid', 'ip', 'version')
|
|
|
+ LOGGER.info(f'通知设备自动升级查询符合数量{uid_set_qs.count()}')
|
|
|
+ if not uid_set_qs.exists():
|
|
|
+ return
|
|
|
+ try:
|
|
|
+ # 创建分页器,每页100条数据
|
|
|
+ paginator = Paginator(uid_set_qs, 100)
|
|
|
+ # 遍历每一页数据
|
|
|
+ for page_num in range(1, paginator.num_pages + 1):
|
|
|
+ # 获取当前页的数据
|
|
|
+ page_data = paginator.page(page_num)
|
|
|
+
|
|
|
+ # 遍历当前页的每一条数据
|
|
|
+ for data in page_data:
|
|
|
+ uid = data['uid']
|
|
|
+
|
|
|
+ result = VersionManagement.check_version_auto_update(uid, '', ver_data)
|
|
|
+ if not result:
|
|
|
+ LOGGER.info(f'{uid}判断是否符合自动升级:{result}')
|
|
|
+ continue
|
|
|
+ serial_number = CommonService.get_serial_number_by_uid(uid)
|
|
|
+ now_ver = data['version']
|
|
|
+ # 将版本号带V字符换成空
|
|
|
+ now_ver_clean = now_ver.replace("V", "")
|
|
|
+ # 采用packaging模块中的Version类进行版本号比较
|
|
|
+ now_version = pacVer.Version(now_ver_clean)
|
|
|
+ new_version = pacVer.Version(version)
|
|
|
+
|
|
|
+ if new_version > now_version: # 新版本大于当前设备版本执行MQTT发送升级
|
|
|
+ VersionManagement.send_auto_update_url(version_qs, uid, serial_number, code, now_ver)
|
|
|
+ LOGGER.info(f'uid={uid},ucode={code},当前版本{now_ver},最新版本{version}')
|
|
|
+ except Exception as e:
|
|
|
+ LOGGER.error(f'异步自动升级异常:ucode:{code},error:{repr(e)}')
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def check_version_auto_update(cls, uid, user_id, ver_data):
|
|
|
+ """
|
|
|
+
|
|
|
+ :param uid: 设备UID
|
|
|
+ :param user_id: 用户id
|
|
|
+ :param ver_data: 版本指定升级数据
|
|
|
+ :return: True | False
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ if not ver_data: # 过滤值默认当前版本所有设备自动升级
|
|
|
+ return True
|
|
|
+
|
|
|
+ if uid and ver_data['uid_list']: # 当前版本指定UID
|
|
|
+ return uid in ver_data['uid_list']
|
|
|
+ if user_id and ver_data['country_list'] and CONFIG_INFO not in [CONFIG_CN, CONFIG_TEST]: # 当前版本指定用户国家
|
|
|
+ user_qs = Device_User.objects.filter(userID=user_id).values('region_country')
|
|
|
+ if not user_qs.exists(): # 用户不存在不升级
|
|
|
+ return False
|
|
|
+ if user_qs[0]['region_country'] == 0: # 用户未选择国家 不进行升级提示
|
|
|
+ return False
|
|
|
+ return user_qs[0]['region_country'] in ver_data['country_list']
|
|
|
+ if CONFIG_INFO in [CONFIG_CN, CONFIG_TEST] and ver_data['addr']: # 中国区可指定城市
|
|
|
+ city_list = ver_data['addr']['city_list']
|
|
|
+ if not city_list:
|
|
|
+ return True
|
|
|
+ uid_set_qs = UidSetModel.objects.filter(uid=uid).values('ip')
|
|
|
+ if not uid_set_qs:
|
|
|
+ return False
|
|
|
+ ip_qs = IPAddr.objects.filter(ip=uid_set_qs[0]['ip']).values('city').order_by('-id')
|
|
|
+ if not ip_qs:
|
|
|
+ return False
|
|
|
+ return ip_qs[0]['city'] in city_list
|
|
|
+ return False
|
|
|
+ except Exception as e:
|
|
|
+ LOGGER.error(f'检测是否符合自动升级{repr(e)}')
|
|
|
+ return False
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def send_auto_update_url(version_qs, uid, serial_number, code, now_ver):
|
|
|
+ """
|
|
|
+ 发送自动升级URL
|
|
|
+ @param version_qs: 最新版本querySet
|
|
|
+ @param uid: 设备UID
|
|
|
+ @param serial_number: 设备序列号
|
|
|
+ @param code: 设备规格码
|
|
|
+ @param now_ver: 设备当前版本好
|
|
|
+ @return:
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ file_path = version_qs.filePath
|
|
|
+ version = version_qs.version
|
|
|
+ mci = version_qs.mci
|
|
|
+ ver = version_qs.softwareVersion
|
|
|
+ max_ver = version_qs.max_ver
|
|
|
+
|
|
|
+ if ver <= max_ver:
|
|
|
+ user_qs = Device_Info.objects.filter(UID=uid, isShare=False).values('userID_id')
|
|
|
+ if not user_qs.exists():
|
|
|
+ LOGGER.info(f'{uid}未添加该设备返回不发MQTT')
|
|
|
+ return False
|
|
|
+ user_id = user_qs[0]['userID_id']
|
|
|
+ # 创建url的token
|
|
|
+ param_url = "ansjer/" + CommonService.RandomStr(6) + "/" + file_path
|
|
|
+ data = {'Url': param_url, 'user_id': user_id,
|
|
|
+ 'uid': uid, 'serial_number': serial_number, 'old_version': "V" + now_ver + "." + code,
|
|
|
+ 'new_version': version, 'mci': mci}
|
|
|
+ device_info_value = json.dumps(data)
|
|
|
+ expire = 600
|
|
|
+
|
|
|
+ str_uuid = str(user_id)
|
|
|
+ if serial_number and serial_number != 'null':
|
|
|
+ str_uuid += serial_number
|
|
|
+ elif uid and uid != 'null':
|
|
|
+ str_uuid += uid
|
|
|
+ str_uuid += now_ver
|
|
|
+ device_info_key = 'ASJ:SERVER:VERSION:{}'.format(str_uuid)
|
|
|
+ LOGGER.info('缓存key={}'.format(device_info_key))
|
|
|
+ redisObject = RedisObject()
|
|
|
+ redisObject.set_data(device_info_key, device_info_value, expire)
|
|
|
+
|
|
|
+ url_tko = UrlTokenObject()
|
|
|
+ file_path = url_tko.generate(data={'uid': str_uuid})
|
|
|
+
|
|
|
+ url = SERVER_DOMAIN + 'dlotapack/' + file_path
|
|
|
+ # 主题名称
|
|
|
+ topic_name = f'ansjer/generic/{serial_number}'
|
|
|
+ # 发布消息内容
|
|
|
+ msg = f'{{"commandCode":1,"data":{{"url":"{url}"}}}}'
|
|
|
+
|
|
|
+ result = VersionManagement.publish_to_aws_iot_mqtt(serial_number, topic_name, msg)
|
|
|
+ LOGGER.info(f'uid={uid}发送数据msg={msg},发送MQTT结果={result}')
|
|
|
+ return True
|
|
|
+ return False
|
|
|
+ except Exception as e:
|
|
|
+ LOGGER.error(f'自动升级异常{repr(e)}')
|
|
|
+ return False
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def publish_to_aws_iot_mqtt(identification_code, topic_name, msg, qos=1):
|
|
|
+ """
|
|
|
+ 发布消息到AWS IoT MQTT(仅尝试一次,无重试)
|
|
|
+ @param identification_code: 标识码
|
|
|
+ @param topic_name: 主题名
|
|
|
+ @param msg: 消息内容(JSON字符串)
|
|
|
+ @param qos: QoS等级(0或1)
|
|
|
+ @return: 成功返回True,失败返回False
|
|
|
+ """
|
|
|
+ # 入参校验
|
|
|
+ if not isinstance(identification_code, str) or not identification_code.strip():
|
|
|
+ LOGGER.error("标识码为空或无效")
|
|
|
+ return False
|
|
|
+ if not isinstance(topic_name, str) or not topic_name.strip():
|
|
|
+ LOGGER.error("主题名为空或无效")
|
|
|
+ return False
|
|
|
+ if qos not in (0, 1):
|
|
|
+ LOGGER.warning("QoS不合法,默认使用1")
|
|
|
+ qos = 1
|
|
|
+
|
|
|
+ # 生成ThingName
|
|
|
+ thing_name = f'LC_{identification_code}' if identification_code.endswith(
|
|
|
+ '11L') else f'Ansjer_Device_{identification_code}'
|
|
|
+
|
|
|
+ try:
|
|
|
+ # 查询设备信息
|
|
|
+ iot_device = iotdeviceInfoModel.objects.filter(thing_name=thing_name).values('endpoint',
|
|
|
+ 'token_iot_number').first()
|
|
|
+ if not iot_device:
|
|
|
+ LOGGER.error(f"未查询到设备信息:{thing_name}")
|
|
|
+ return False
|
|
|
+
|
|
|
+ endpoint = iot_device.get('endpoint', '').strip()
|
|
|
+ token = iot_device.get('token_iot_number', '').strip()
|
|
|
+ if not endpoint or not token:
|
|
|
+ LOGGER.error("设备信息不完整(endpoint或token缺失)")
|
|
|
+ return False
|
|
|
+
|
|
|
+ # 构造请求
|
|
|
+ encoded_topic = requests.utils.quote(topic_name, safe='')
|
|
|
+ request_url = f"https://{endpoint}/topics/{encoded_topic}?qos={qos}"
|
|
|
+ signature = CommonService.rsa_sign(token)
|
|
|
+ if not signature:
|
|
|
+ LOGGER.error("Token签名失败")
|
|
|
+ return False
|
|
|
+
|
|
|
+ headers = {
|
|
|
+ 'x-amz-customauthorizer-name': 'Ansjer_Iot_Auth',
|
|
|
+ 'Token': token,
|
|
|
+ 'x-amz-customauthorizer-signature': signature
|
|
|
+ }
|
|
|
+
|
|
|
+ # 发送请求(仅一次)
|
|
|
+ response = requests.post(
|
|
|
+ url=request_url,
|
|
|
+ headers=headers,
|
|
|
+ data=msg,
|
|
|
+ timeout=10 # 10秒超时
|
|
|
+ )
|
|
|
+
|
|
|
+ # 结果判断
|
|
|
+ if response.status_code == 200:
|
|
|
+ res_json = response.json()
|
|
|
+ if res_json.get('message') == 'OK':
|
|
|
+ LOGGER.info(f"发布成功:{topic_name}")
|
|
|
+ return True
|
|
|
+ LOGGER.error(f"响应异常:{res_json}")
|
|
|
+ else:
|
|
|
+ LOGGER.error(f"请求失败,状态码:{response.status_code}")
|
|
|
+ return False
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ LOGGER.error(f"发布失败:{str(e)}")
|
|
|
+ return False
|