device.py 23 KB

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