device.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. from rest_framework.views import APIView
  2. from rest_framework.response import Response
  3. from django.conf import settings
  4. from django.core.paginator import Paginator
  5. from django.forms.models import model_to_dict
  6. import time
  7. import os
  8. import datetime
  9. import uuid
  10. import json
  11. import logging
  12. import requests
  13. from smartfarming.utils import get_addr_by_lag_lng
  14. from smartfarming.serializers.device_serializers import DeviceSerializers
  15. from smartfarming.models.device import MongoDevice, MongoCBDData, MongoSCDData, MongoXYCBData
  16. from smartfarming.models.worm_forecast import MongoCBDphoto
  17. from smartfarming.models.weather import (
  18. MongoQXZ_Base_Info,
  19. MongoQXZ_Alarm_Log_New,
  20. MongoQXZ_Conf,
  21. QXZdata_New,
  22. MongoQXZ_Alarm,
  23. QXZThresholdWarning
  24. )
  25. from django.db.models import Q
  26. from smartfarming.qxz import data_deal
  27. from collections import Counter, defaultdict
  28. from smartfarming.models.device import MongoDevice, DevicePestWarning, MongoDeviceType
  29. from kedong.decoration import kedong_deco, PortError
  30. from django.core.paginator import Paginator
  31. logger = logging.getLogger("data_ingestion")
  32. config_dict = settings.CONFIG
  33. device_type_en = config_dict.get("device_type_en")
  34. device_type_zh = config_dict.get("device_type_zh")
  35. class CbdScdXyDeviceSaveAPIView(APIView):
  36. permission_classes = []
  37. authentication_classes = []
  38. def post(self, request):
  39. # 测报灯 杀虫灯 性诱 设备及数据入库
  40. try:
  41. request_data = request.body
  42. data = json.loads(request_data)
  43. logger.warning(f"测报灯 杀虫灯 性诱 设备及数据入库原数据: {data}")
  44. topic = data.get("topic")
  45. payload = data.get("payload")
  46. cmd = payload.get("cmd")
  47. topic_msg = topic.split("/")
  48. now = int(time.time())
  49. if topic_msg and len(topic_msg) == 5 and cmd:
  50. device_id = topic_msg[-1]
  51. try:
  52. device_type = topic_msg[2]
  53. device_type_id = device_type_en.get(device_type)
  54. if device_type_id == 2:
  55. model = MongoSCDData
  56. elif device_type_id == 3:
  57. model = MongoCBDData
  58. elif device_type_id == 4:
  59. model = MongoXYCBData
  60. # 在设备信息表中查找是否有数据,如果没有数据则增加
  61. device_name = device_type_zh.get(str(device_type_id))
  62. device, is_created = MongoDevice.objects.get_or_create(
  63. device_id = device_id,
  64. defaults={
  65. "device_id": device_id,
  66. "device_type_id": device_type_id,
  67. "addtime": now
  68. }
  69. )
  70. if is_created:
  71. device.device_name = device_name
  72. device.save()
  73. logger.warning(f"新设备:{device_type} {device_id} 入库成功")
  74. # 获取数据并更新设备
  75. if cmd == "data":
  76. ext = payload.get("ext")
  77. if ext:
  78. # 获取设备上报的时间,同步设备
  79. try:
  80. stamp = ext.get("stamp")
  81. uptime = int((datetime.datetime.strptime(stamp, "%Y%m%d%H%M%S")).timestamp())
  82. except Exception as e:
  83. logger.error(f"同步设备时间失败:{device_id} {e}")
  84. # 增加设备数据
  85. model.objects.create(
  86. device_id = device.id,
  87. device_data = str(ext),
  88. addtime = now
  89. )
  90. lng = ext.get("lng")
  91. lat = ext.get("lat")
  92. dver_num = ext.get("dver")
  93. device.device_status = 1
  94. device.uptime = uptime
  95. if dver_num:
  96. device.dver_num = dver_num
  97. if lng and lat and dver_num and device.gps != 0:
  98. device.lng = lng
  99. device.lat = lat
  100. # 根据经纬度获取省市级
  101. is_success, province, city, district = get_addr_by_lag_lng(lat, lng)
  102. if is_success:
  103. # 更新地理位置坐标
  104. device.province = province
  105. device.city = city
  106. device.district = district
  107. device.save()
  108. elif cmd == "offline":
  109. ext = data.get("ext")
  110. if ext:
  111. # 增加设备数据
  112. model.objects.create(
  113. device_id=device_id,
  114. device_data = str(ext),
  115. addtime=now
  116. )
  117. # 更新设备状态
  118. device = MongoDevice.objects.filter(device_id=device_id).first()
  119. device.device_status = 0
  120. device.save()
  121. return Response({"code": 0, "msg": "success"})
  122. except Exception as e:
  123. logger.error(f"测报灯设备 {device_id} 处理上报数据或增加设备失败,错误原因:{e.args}")
  124. return Response({"code": 2, "msg": f"处理测报灯上报数据失败 {device_id}"})
  125. else:
  126. return Response({"code": 2, "msg": "请核对数据结构"})
  127. except Exception as e:
  128. logger.error(f"测报灯、杀虫灯、性诱设备 {e.args}")
  129. return Response({"code": 2, "msg": "failer"})
  130. class CbdPhotoAPIView(APIView):
  131. permission_classes = []
  132. authentication_classes = []
  133. def post(self, request):
  134. try:
  135. request_data = request.body
  136. request_data = json.loads(request_data)
  137. logger.warning(f"测报灯图片数据入库原数据: {request_data}")
  138. device_id = request_data.get("imei")
  139. device = MongoDevice.objects.filter(device_id=device_id)
  140. media = config_dict.get("media")
  141. img_content_url = f'{config_dict.get("image_url").get("image")}/media'
  142. if device:
  143. device = device.first()
  144. d_id = device.id
  145. # 把原图下载到本地
  146. result_image = request_data.get("Image")
  147. addr = config_dict.get("oss") + result_image if not result_image.startswith("http") else result_image
  148. inden_path = f"{media}/cbd/{device.device_id}"
  149. os.makedirs(inden_path) if not os.path.exists(inden_path) else None
  150. stamp = int(datetime.datetime.now().timestamp())
  151. unique_id = uuid.uuid4()
  152. combined_id = str(stamp) + "-" + str(unique_id)
  153. addr_org = os.path.join(inden_path, f"{combined_id}.jpg")
  154. remote_content = requests.get(addr).content
  155. with open(addr_org, "wb") as f:
  156. f.write(remote_content)
  157. # 把识别后的图片下载到本地
  158. result_temp = request_data.get("Result_image")
  159. indentify = config_dict.get("oss") + result_temp if not result_temp.startswith("http") else result_temp
  160. inden_path_tp = f"{media}/result/cbd/{device.device_id}"
  161. os.makedirs(inden_path_tp) if not os.path.exists(inden_path_tp) else None
  162. stamp = int(datetime.datetime.now().timestamp())
  163. unique_id = uuid.uuid4()
  164. combined_id = str(stamp) + "-" + str(unique_id)
  165. indentify_photo = os.path.join(inden_path_tp, f"{combined_id}.jpg")
  166. indentify_remote_content = requests.get(indentify).content
  167. with open(indentify_photo, "wb") as f:
  168. f.write(indentify_remote_content)
  169. data = {
  170. "device_id": d_id,
  171. "addr": addr_org.replace(media, "/media"),
  172. "indentify_photo":indentify_photo.replace(media, img_content_url),
  173. "indentify_result": request_data.get("Result"),
  174. "label": request_data.get("Result_code"),
  175. "photo_status": 1,
  176. "uptime": int(time.time()),
  177. "addtime": int(time.time())
  178. }
  179. photo = MongoCBDphoto(**data)
  180. photo.save()
  181. return Response({"code": 0, "msg": "success"})
  182. else:
  183. return Response({"code": 2, "msg": "该项目不存在此设备"})
  184. except Exception as e:
  185. logger.error(f"测报灯图片入库失败 {e.args}")
  186. return Response({"code": 2, "msg": "failer"})
  187. class QxzDeviceAddAPIViw(APIView):
  188. permission_classes = []
  189. authentication_classes = []
  190. def post(self, request):
  191. # 气象站上传数据
  192. try:
  193. request_data = request.body
  194. request_data = json.loads(request_data)
  195. logger.error(f"气象站源数据:{request_data}")
  196. device_id = request_data.get("StationID")
  197. uptime = request_data.get("MonitorTime")
  198. data = request_data.get("data")
  199. terminalStatus = request_data.get("terminalStatus")
  200. cmd = request_data.get("cmd")
  201. up = uptime.replace(" ", "")
  202. uptime_tp = int((datetime.datetime.strptime(up, "%Y-%m-%d%H:%M:%S")).timestamp())
  203. # 获取该设备的预警配置数据
  204. alarm = MongoQXZ_Alarm.objects.filter(device_id=device_id)
  205. qxz_e_conf = MongoQXZ_Conf.objects.filter(device_id=device_id)
  206. qxz_e_conf = qxz_e_conf.first() if qxz_e_conf else None
  207. mongo_device = MongoDevice.objects.filter(device_id=device_id)
  208. if mongo_device:
  209. mongo_device = mongo_device.first()
  210. if not qxz_e_conf:
  211. logger.error(f"该设备未配置预警阀值: {mongo_device.device_id}")
  212. return Response({"code": 2, "msg": "该设备未配置预警阀值"})
  213. if data:
  214. qxz_e_conf = model_to_dict(qxz_e_conf)
  215. qx_ek = {}
  216. result_tp_fin = ""
  217. for i in data:
  218. tp_value = i.get("eValue")
  219. if tp_value:
  220. ek = i.get("eKey")
  221. qx_ek[ek] = f"{tp_value}#{i.get('eNum')}#{ek}"
  222. if alarm:
  223. # 存在预警配置文件, 先查看预警配置文件中是否有配置 -- "0#6" 表示大于6则报警 "1#5" 表示小于5报警 "0#" 表示不配置
  224. alarms = alarm.first()
  225. alarm_config = eval(alarms.conf)
  226. dat = alarm_config.get("dat")
  227. for m, n in dat.items():
  228. n_sp = n.split("#")
  229. if n_sp[1]:
  230. if ek == m:
  231. # 查询具体含义
  232. zh = qxz_e_conf.get(m)
  233. zh_k = zh.split("#")
  234. result = ""
  235. if n_sp[0] == "1":
  236. if float(tp_value) > float(n_sp[1]):
  237. # 组织预警信息
  238. result = f"为{tp_value},大于{n_sp[1]}"
  239. elif n_sp[0] == "0":
  240. if float(tp_value) < float(n_sp[1]):
  241. result = f"为{tp_value},小于{n_sp[1]}"
  242. if result:
  243. QXZThresholdWarning.objects.create(
  244. device_id=device_id,
  245. warning_content= "大于预警" if n_sp[0] == "1" else "小于预警",
  246. ekey=ek,
  247. set_value=n_sp[1],
  248. current_value=tp_value,
  249. upltime=uptime_tp
  250. )
  251. result_tp = f"{zh_k[0]}{result}{zh_k[1]},"
  252. result_tp_fin += result_tp
  253. if result_tp_fin:
  254. alarm_new = MongoQXZ_Alarm_Log_New()
  255. alarm_new.warning_content = result_tp_fin
  256. alarm_new.upl_time = uptime_tp
  257. alarm_new.device_id = mongo_device.id
  258. alarm_new.warning_name = str(mongo_device.device_type_id) # 设备类型ID
  259. alarm_new.save()
  260. logger.error(f"产生预警:{device_id}")
  261. # 30分钟上报一次的数据
  262. qx_ek["device_id"] = device_id
  263. qx_ek["uptime"] = uptime_tp
  264. qxz_data = QXZdata_New(**qx_ek)
  265. qxz_data.save()
  266. mongo_device.uptime=uptime_tp
  267. mongo_device.device_status=1
  268. mongo_device.save()
  269. return Response({"code": 0, "msg": "success"})
  270. if terminalStatus:
  271. base_info_obj, is_created = MongoQXZ_Base_Info.objects.update_or_create(
  272. device_id=device_id,
  273. defaults={
  274. "volt": terminalStatus.get("VOLT"),
  275. "rssi": terminalStatus.get("RSSI"),
  276. "uptime": uptime_tp
  277. }
  278. )
  279. iccid = terminalStatus.get("ICCID")
  280. lng = terminalStatus.get("longitude")
  281. lat = terminalStatus.get("latitude")
  282. led = terminalStatus.get("Dotled")
  283. dver = terminalStatus.get("Version")
  284. device, is_created = MongoDevice.objects.get_or_create(device_id=device_id)
  285. if iccid:
  286. base_info_obj.iccid = iccid
  287. if lng:
  288. base_info_obj.lng = lng
  289. device.lng = lng
  290. if lat:
  291. base_info_obj.lat = lat
  292. device.lat = lat
  293. if led:
  294. base_info_obj.led = led
  295. if dver:
  296. base_info_obj.dver = dver
  297. # 如果经纬度均存在
  298. if lat and lng:
  299. is_success, province, city, district = get_addr_by_lag_lng(lat, lng)
  300. if is_success:
  301. device.province = province
  302. device.city = city
  303. device.district = district
  304. base_info_obj.save()
  305. device.uptime = uptime_tp
  306. device.save()
  307. return Response({"code": 0, "msg": "success"})
  308. if cmd:
  309. ext = request_data.get("ext")
  310. imei = ext.get("imei")
  311. device_info = MongoDevice.objects.get(device_id=imei)
  312. if cmd == "online":
  313. device_info.device_status = 1
  314. if cmd == "offline":
  315. device_info.device_status = 0
  316. device_info.save()
  317. except Exception as e:
  318. logger.error(f"气象站设备 {device_id} 处理上报数据或增加设备失败,错误原因:{e.args}")
  319. return Response({"code": 2, "msg": "failer"})
  320. class DeviceListAPIView(APIView):
  321. def post(self, request):
  322. # 设备列表
  323. request_data = request.data
  324. device_id = request_data.get("device_id")
  325. device_status = request_data.get("device_status")
  326. search = request_data.get("search")
  327. page_num = int(request_data.get("pagenum")) if request_data.get("pagenum") else 1
  328. page_size = int(request_data.get("pagesize")) if request_data.get("pagesize") else 10
  329. if device_id:
  330. queryset = MongoDevice.objects.filter(device_id=device_id).order_by("-uptime")
  331. elif device_status:
  332. queryset = MongoDevice.objects.filter(device_status=device_status).order_by("-uptime")
  333. elif search:
  334. queryset = MongoDevice.objects.filter(Q(device_name__icontains=search) | Q(device_id__icontains=search))
  335. else:
  336. queryset = MongoDevice.objects.all().order_by("-uptime")
  337. total_obj = queryset.count()
  338. paginator = Paginator(queryset, page_size)
  339. page_obj = paginator.get_page(page_num)
  340. serializers = DeviceSerializers(page_obj, many=True)
  341. return Response({"code": 0, "msg": "success", "data": serializers.data, "count": total_obj})
  342. class DeviceChangeAPIView(APIView):
  343. def post(self, request):
  344. # 修改设备信息
  345. request_data = request.data
  346. device_name = request_data.get("device_name")
  347. device_id = request_data.get("device_id")
  348. lng = request_data.get("lng")
  349. lat = request_data.get("lat")
  350. device = MongoDevice.objects.get(device_id=device_id)
  351. if device_name:
  352. device.device_name = device_name
  353. if lng and lat:
  354. device.lng = lng
  355. device.lat = lat
  356. is_success, province, city, district = get_addr_by_lag_lng(lat, lng)
  357. if is_success:
  358. # 更新地理位置坐标
  359. device.province = province
  360. device.city = city
  361. device.district = district
  362. device.save()
  363. return Response({"code": 0, "msg": "success"})
  364. class DeviceListInfoAPIView(APIView):
  365. def post(self, request):
  366. # 设备列表
  367. request_data = request.data
  368. device_id = request_data.get("device_id")
  369. device_status = request_data.get("device_status")
  370. search = request_data.get("search")
  371. page_num = int(request_data.get("pagenum")) if request_data.get("pagenum") else 1
  372. page_size = int(request_data.get("pagesize")) if request_data.get("pagesize") else 10
  373. if device_id:
  374. queryset = MongoDevice.objects.filter(device_id=device_id).order_by("-uptime")
  375. elif device_status:
  376. queryset = MongoDevice.objects.filter(device_status=device_status).order_by("-uptime")
  377. elif search:
  378. queryset = MongoDevice.objects.filter(Q(device_name__icontains=search) | Q(device_id__icontains=search))
  379. else:
  380. queryset = MongoDevice.objects.all().order_by("-uptime")
  381. total_obj = queryset.count()
  382. paginator = Paginator(queryset, page_size)
  383. page_obj = paginator.get_page(page_num)
  384. serializers = DeviceSerializers(page_obj, many=True)
  385. return Response({"code": 0, "msg": "success", "data": serializers.data, "count": total_obj})
  386. class DeviceListAPIView(APIView):
  387. def post(self, request):
  388. queryset = MongoDevice.objects.order_by('-id')
  389. type_dict = {d.id: d.type_name for d in MongoDeviceType.objects.all()}
  390. result = []
  391. offline_list = []
  392. type_counter = Counter()
  393. device_dict = defaultdict(list)
  394. for item in queryset:
  395. device_info = item
  396. device_type_id = device_info.device_type_id
  397. device_status = device_info.device_status
  398. is_offline = False if device_status == 1 else True
  399. device_id = device_info.device_id
  400. if is_offline:
  401. offline_list.append(device_id)
  402. type_counter[device_type_id] += 1
  403. device_dict[device_type_id].append(device_id)
  404. coordinates = ""
  405. lng = device_info.lng
  406. lat = device_info.lat
  407. if not (lng and lat):
  408. continue
  409. coordinates = f"[{str(float(lng))},{str(float(lat))}]"
  410. device_name = device_info.device_name
  411. result.append({
  412. 'ld_id': item.id,
  413. 'device_id': device_id,
  414. 'device_name': device_name or device_id,
  415. 'tpye_name': type_dict[device_info.device_type_id],
  416. 'device_type_id': device_type_id,
  417. 'coordinates': coordinates,
  418. 'offline': False if device_status == 1 else True,
  419. 'is_warning': False
  420. })
  421. statistic_list = []
  422. for k, v in type_counter.items():
  423. statistic_list.append({
  424. 'type_id': k,
  425. 'type_count': v,
  426. 'type_name': type_dict[k]
  427. })
  428. warning_model_dict = {
  429. 3: DevicePestWarning
  430. }
  431. warning_list = []
  432. for k, v in device_dict.items():
  433. try:
  434. model_obj = warning_model_dict[k]
  435. except KeyError as e:
  436. continue
  437. war_list = [d.device_id for d in model_obj.objects.filter(device_id__in=v, status=0)]
  438. if war_list:
  439. warning_list.extend(war_list)
  440. for item in result:
  441. device_id = item['device_id']
  442. if device_id in warning_list:
  443. item['is_warning'] = True
  444. warn_and_off = set(warning_list) & set(offline_list)
  445. data = {
  446. "statistic": statistic_list,
  447. 'offline': {
  448. 'count': len(offline_list),
  449. 'result': offline_list
  450. },
  451. "waring": {
  452. 'count': len(warning_list),
  453. 'result': warning_list
  454. },
  455. "warn_and_off": {
  456. 'count': len(warn_and_off),
  457. 'result': warning_list
  458. },
  459. "data": result
  460. }
  461. return Response({"code": 0, "message": "success", "data": data})