gezigui.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import json
  5. import logging
  6. import time
  7. from decimal import Decimal
  8. import re
  9. from typing import TYPE_CHECKING
  10. from apilib.monetary import VirtualCoin
  11. from apilib.utils_datetime import to_datetime
  12. from apps.web.common.proxy import ClientConsumeModelProxy
  13. from apps.web.constant import Const, DeviceCmdCode
  14. from apps.web.core.adapter.base import SmartBox
  15. from apps.web.core.exceptions import ServiceException
  16. from apps.web.core.networking import MessageSender
  17. from apps.web.device.models import Device, Group
  18. from apps.web.user.models import ConsumeRecord, MyUser
  19. from apps.web.user.models import ServiceProgress
  20. from taskmanager.mediator import task_caller
  21. logger = logging.getLogger(__name__)
  22. if TYPE_CHECKING:
  23. pass
  24. class GeZiGuiBox(SmartBox):
  25. def __init__(self, device):
  26. super(GeZiGuiBox, self).__init__(device)
  27. # 解析获取设备信息的返回报文
  28. def analyze_event_data(self, data):
  29. pass
  30. def _check_package(self, port, package):
  31. """
  32. 获取设备启动的发送数据 根据设备的当前模式以及套餐获取
  33. :param package:
  34. :return:
  35. """
  36. sizeInfo = [{"port": str(_), "size": "small"} for _ in xrange(1, 25)]
  37. sizeInfo = self.device.get("otherConf", dict()).get("sizeInfo") or sizeInfo
  38. res = filter(lambda x: x["port"] == port, sizeInfo)
  39. result = False
  40. if not res:
  41. raise ServiceException({"result": 2, "description": u"未找到{}号该柜门".format(port)})
  42. name = package.get("name")
  43. size = res[0].get("size")
  44. if size == "large" and u"大" in name:
  45. result = True
  46. elif size == "medium" and u"中" in name:
  47. result = True
  48. elif size == "small" and u"小" in name:
  49. result = True
  50. else:
  51. pass
  52. if result == False:
  53. raise ServiceException({"result": 2, "description": u"未找到{}号该柜门".format(port)})
  54. def log_obj(self, obj):
  55. if isinstance(obj, dict):
  56. for k, v in obj.items():
  57. if isinstance(v, object):
  58. obj[k] = str(v)
  59. if isinstance(obj, list) or isinstance(obj, tuple) or isinstance(obj, set):
  60. obj = map(lambda x: str(x) if isinstance(x, object) else x, obj)
  61. if isinstance(obj, unicode):
  62. obj = str(obj)
  63. # print("\33[33m" + json.dumps(obj,ensure_ascii=True,encoding="utf-8") + "\33[0m")
  64. return "\33[33m" + json.dumps(obj, ensure_ascii=False, encoding="utf-8") + "\33[0m"
  65. def disable_app_device(self, switch=True):
  66. # type:(bool) -> None
  67. otherConf = self.device.get("otherConf", {})
  68. otherConf["disableDevice"] = switch
  69. Device.objects.filter(devNo=self.device["devNo"]).update(otherConf=otherConf)
  70. Device.invalid_device_cache(self.device["devNo"])
  71. @staticmethod
  72. def encode_str(data, length=2, ratio=1.0, base=16):
  73. # type:(str,int,float,int) -> str
  74. if not isinstance(data, Decimal):
  75. data = Decimal(data)
  76. if not isinstance(length, str):
  77. length = str(length)
  78. if not isinstance(ratio, Decimal):
  79. ratio = Decimal(ratio)
  80. end = "X" if base == 16 else "d"
  81. encodeStr = "%." + length + end
  82. encodeStr = encodeStr % (data * ratio)
  83. return encodeStr
  84. @staticmethod
  85. def decode_str(data, ratio=1.0, base=16):
  86. # type:(str,float,int) -> str
  87. """
  88. ratio:比率单位转换
  89. """
  90. if not isinstance(data, str):
  91. data = str(data)
  92. return "%.10g" % (int(data, base) * ratio)
  93. @staticmethod
  94. def reverse_hex(data):
  95. # type:(str) -> str
  96. if not isinstance(data, str):
  97. raise TypeError
  98. return "".join(list(reversed(re.findall(r".{2}", data))))
  99. def decode_long_hex_to_list(self, data, split=2, ratio=1.0, base=16, decode=True):
  100. # type:(str,int,float,int) -> list
  101. """
  102. return: list
  103. """
  104. if len(data) % split != 0:
  105. raise Exception("Invalid data")
  106. pattern = r".{%s}" % split
  107. hex_list = re.findall(pattern, data)
  108. if decode:
  109. hex_list = map(lambda x: self.decode_str(x, ratio=ratio, base=base), hex_list)
  110. return hex_list
  111. else:
  112. return hex_list
  113. @staticmethod
  114. def check_params_range(params, minData=None, maxData=None, desc=""):
  115. # type:(str,float,float,str) -> str
  116. """
  117. 检查参数,返回字符串参数
  118. """
  119. if params is None:
  120. raise ServiceException({"result": 2, "description": u"参数错误."})
  121. if not isinstance(params, Decimal):
  122. params = Decimal(params)
  123. if not minData and maxData:
  124. if not isinstance(maxData, Decimal):
  125. maxData = Decimal(maxData)
  126. if params <= maxData:
  127. return "%g" % params
  128. else:
  129. raise ServiceException({"result": 2, "description": u"%s超出可选范围,可选最大值为%g" % (desc, maxData)})
  130. if not maxData and minData:
  131. if not isinstance(minData, Decimal):
  132. minData = Decimal(minData)
  133. if minData <= params:
  134. return "%g" % params
  135. else:
  136. raise ServiceException({"result": 2, "description": u"%s超出可选范围,可选最小值为%g" % (desc, minData)})
  137. if not minData and not maxData:
  138. return "%g" % params
  139. else:
  140. if not isinstance(minData, Decimal):
  141. minData = Decimal(minData)
  142. if not isinstance(maxData, Decimal):
  143. maxData = Decimal(maxData)
  144. if minData <= params <= maxData:
  145. return "%g" % params
  146. else:
  147. raise ServiceException(
  148. {"result": 2, "description": u"%s参数超出可选范围,可取范围为%g-%g" % (desc, minData, maxData)})
  149. def send_mqtt(self, funCode, data, cmd=DeviceCmdCode.OPERATE_DEV_SYNC):
  150. """
  151. 发送mqtt 指令210 返回data
  152. """
  153. if not isinstance(funCode, str):
  154. funCode = str(funCode)
  155. if not isinstance(data, str):
  156. data = str(data)
  157. result = MessageSender.send(self.device, cmd,
  158. {"IMEI": self.device["devNo"], "funCode": funCode, "data": data}, timeout=10)
  159. if "rst" in result and result["rst"] != 0:
  160. if result["rst"] == -1:
  161. raise ServiceException(
  162. {"result": 2, "description": u"该设备正在玩命找网络,请您稍候再试", "rst": -1})
  163. elif result["rst"] == 1:
  164. raise ServiceException(
  165. {"result": 2, "description": u"该设备忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能", "rst": 1})
  166. else:
  167. if cmd in [DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE]:
  168. return
  169. else:
  170. return result
  171. def do_update_configs(self, updateDict):
  172. dev = Device.objects.get(devNo=self.device.devNo)
  173. deviceConfigs = dev.otherConf.get("deviceConfigs", {})
  174. deviceConfigs.update(updateDict)
  175. dev.otherConf['deviceConfigs'] = deviceConfigs
  176. dev.save()
  177. Device.invalid_device_cache(self.device.devNo)
  178. def start_device(self, package, openId, attachParas):
  179. portStr = attachParas.get("chargeIndex")
  180. if portStr is None:
  181. raise ServiceException({"result": 2, "description": u"未知端口"})
  182. self._check_package(portStr, package)
  183. ctrInfo = Device.get_dev_control_cache(self._device["devNo"])
  184. lineInfo = ctrInfo.get(portStr)
  185. if not lineInfo or lineInfo.get("status", 2) == Const.DEV_WORK_STATUS_IDLE:
  186. orderNo = attachParas.get("orderNo")
  187. delay = 1 * 60
  188. data = self.encode_str(portStr)
  189. data += "FFFF"
  190. data += self.encode_str(delay)
  191. # 发送指令
  192. result = self.send_mqtt(funCode="02", data=data)
  193. user = MyUser.objects.filter(openId=openId, groupId=self.device["groupId"]).first()
  194. portDict = {
  195. "status": Const.DEV_WORK_STATUS_WORKING,
  196. "isStart": True,
  197. "openId": openId,
  198. "orderNo": orderNo,
  199. "startTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  200. "package": package,
  201. "devNo": self.device.devNo,
  202. "nickName": user.nickname,
  203. 'consumeRecordId': str(attachParas['consumeRecordId'])
  204. }
  205. Device.update_dev_control_cache(self._device["devNo"], {str(portStr): portDict})
  206. otherConf = self.device.get("otherConf") or dict()
  207. result["consumeOrderNo"] = orderNo
  208. result["servicedInfo"] = {"chargeType": otherConf.get("chargeType", "time")}
  209. result["finishedTime"] = int(time.time()) + 7 * 24 * 60 * 60
  210. return result
  211. def get_default_port_nums(self):
  212. sizeInfo = self.device["otherConf"].get("sizeInfo", [])
  213. return len(sizeInfo) or 24
  214. def check_dev_status(self, attachParas=None):
  215. """
  216. 控制缓存里面查找有没有使用该端口使用情况 没有则返回 有则返回raise ServiceException({"result": 2, "description": u"当前洗衣机%s,请稍候使用" % info["statusInfo"]})
  217. attachParas:{"category": "chargeIndex", "chargeIndex": "2", "voltage": None, "batteryType": None, "waterIndex": None, "powerGear": None}
  218. """
  219. logger.info(self.log_obj("GZG_ do_____check_dev_status,attachParas: {}".format(attachParas)))
  220. chargeIndex = attachParas.get("chargeIndex")
  221. if not chargeIndex:
  222. raise ServiceException({"result": 2, "description": u"当前柜没法使用"})
  223. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  224. lineInfo = ctrInfo.get(chargeIndex)
  225. if not lineInfo:
  226. logger.debug("get null control cache from {}".format(repr(self.device)))
  227. return
  228. elif lineInfo.get("status", 0) == Const.DEV_WORK_STATUS_IDLE:
  229. return
  230. elif lineInfo.get("status", 0) == Const.DEV_WORK_STATUS_WORKING:
  231. # 第二次 扫码 点击了 startAction 准备拉起支付
  232. if 'consumeRecordId' in lineInfo:
  233. order = ClientConsumeModelProxy.get_one(
  234. shard_filter = {'ownerId': self.device.ownerId},
  235. id = lineInfo.get("consumeRecordId")) # type: ConsumeRecord
  236. else:
  237. # 升级兼容deprecated
  238. order = ConsumeRecord.objects.filter(orderNo = lineInfo.get("orderNo")).first() # type: ConsumeRecord
  239. if order:
  240. # 防止用户点一次之后取消又来点 做一个订单状态的修正
  241. if order.is_waitPay():
  242. order.update(status="end")
  243. else:
  244. raise ServiceException({"result": 2, "description": u"当前%s号柜,已有人使用稍候使用" % chargeIndex})
  245. else:
  246. raise ServiceException({"result": 2, "description": u"当前%s号柜,已有人使用稍候使用" % chargeIndex})
  247. def check_order_state(self, openId):
  248. """
  249. 通过 openId 以及设备来鉴别 订单
  250. :param openId:
  251. :return:
  252. """
  253. dealerId = self.device.ownerId
  254. devTypeCode = self.device.devType.get("code")
  255. # TODO: 性能问题
  256. orders = ConsumeRecord.objects.filter(ownerId=dealerId, openId=openId, devTypeCode=devTypeCode,
  257. status__ne="finished", isNormal=True).order_by('-id')
  258. if not orders:
  259. return
  260. else:
  261. orders.update(status="running")
  262. return orders[0]
  263. def deal_order_money(self, order):
  264. # type: (ConsumeRecord) -> ConsumeRecord
  265. """
  266. 传入order对象,给订单动态计算时间,使用情况,费用
  267. 主要做三个处理.
  268. 1 计算时间 价格
  269. 2 更新ConsumeRecord
  270. 3 更新ServiceProgress
  271. """
  272. order = self.update_order_info(order)
  273. money = order.money # type: VirtualCoin
  274. port = order.attachParas.get("chargeIndex")
  275. spQueryDict = {
  276. "open_id": order.openId,
  277. "device_imei": order.devNo,
  278. "port": int(port),
  279. "isFinished": False
  280. }
  281. sp = ServiceProgress.objects.filter(**spQueryDict).first()
  282. sp.consumeOrder.update({"needPayMoney": str(money)})
  283. sp.save()
  284. return order
  285. def update_order_info(self, order):
  286. """
  287. 获取用户当前需要扣除的金额
  288. """
  289. if order.status == "finished":
  290. return order
  291. ctrInfo = Device.get_dev_control_cache(order.devNo)
  292. port = order.attachParas.get("chargeIndex")
  293. lineInfo = ctrInfo.get(port)
  294. startTime = lineInfo.get("startTime")
  295. startTime = to_datetime(startTime)
  296. nowTime = datetime.datetime.now()
  297. if order.finishedTime:
  298. old_finish_time = order.finishedTime
  299. pay_order_time = round(((nowTime - old_finish_time).total_seconds() / 60.0))
  300. if pay_order_time > 3: # 如果付款时间大于3分钟,时间继续,订单费用刷新,否则订单金额不变
  301. usedTime = int(round(((nowTime - startTime).total_seconds() / 60.0)))
  302. finishedTime = nowTime
  303. else:
  304. usedTime = order.servicedInfo.get("duration", 0)
  305. finishedTime = order.finishedTime
  306. else:
  307. usedTime = int(round(((nowTime - startTime).total_seconds() / 60.0)))
  308. finishedTime = nowTime
  309. packageId = order.attachParas.get("packageId")
  310. washConfig = self.device.get("washConfig", dict()) # new
  311. package = washConfig.get(packageId)
  312. base_price = package.get("basePrice", 0)
  313. timePrice = package.get("price", 0)
  314. money = VirtualCoin(base_price) + VirtualCoin(float(timePrice) * float(usedTime) / 60.0)
  315. # money = VirtualCoin(0.01)
  316. consumeDict = {
  317. "port": order.attachParas.get("chargeIndex"),
  318. "duration": usedTime,
  319. # "finishedTime": finishedTime.strftime("%Y-%m-%d %H:%M:%S"),
  320. "spendMoney": str(money),
  321. }
  322. order.servicedInfo = consumeDict
  323. order.finishedTime = finishedTime
  324. order.money = money
  325. order.coin = money
  326. if usedTime < self.device["otherConf"].get("refundProtectionTime", 0):
  327. order.money = VirtualCoin(0)
  328. order.coin = VirtualCoin(0)
  329. order.save()
  330. order.reload()
  331. return order
  332. def finished_order_open_door(self, port):
  333. """
  334. 发送开门指令后 延时上报如果门没关 推送给经销商
  335. """
  336. portHex = self.encode_str(port, 2)
  337. delay = self.device.get("otherConf", {}).get("delay_check_close_door", 3)
  338. delayHex = self.encode_str(delay, length=4)
  339. data = portHex + "FFFF" + delayHex
  340. self.send_mqtt(funCode="02", data=data)
  341. def get_port_status(self, force=False):
  342. if force:
  343. pass
  344. devCache = Device.get_dev_control_cache(self._device["devNo"])
  345. allPorts = self.get_default_port_nums()
  346. if allPorts is None:
  347. raise ServiceException({"result": 2, "description": u"充电端口信息获取失败"})
  348. # 获取显示端口的数量 客户要求可以设置 显示给用户多少个端口
  349. showPortNum = self._device.get("otherConf", {}).get("actualPortNum", 24)
  350. statusDict = dict()
  351. showStatusDict = dict()
  352. for portNum in xrange(allPorts):
  353. portStr = str(portNum + 1)
  354. tempDict = devCache.get(portStr, {})
  355. if "status" in tempDict:
  356. statusDict[portStr] = {"status": tempDict["status"]}
  357. elif "isStart" in tempDict:
  358. if tempDict["isStart"]:
  359. statusDict[portStr] = {"status": Const.DEV_WORK_STATUS_WORKING}
  360. else:
  361. statusDict[portStr] = {"status": Const.DEV_WORK_STATUS_IDLE}
  362. else:
  363. statusDict[portStr] = {"status": Const.DEV_WORK_STATUS_IDLE}
  364. if int(portStr) <= int(showPortNum):
  365. showStatusDict[portStr] = {"status": statusDict[portStr]["status"]}
  366. allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
  367. portsDict = {"allPorts": allPorts, "usedPorts": usedPorts, "usePorts": usePorts}
  368. Device.update_dev_control_cache(self._device["devNo"], portsDict)
  369. return showStatusDict
  370. def get_port_info(self, port):
  371. # type:(str) -> dict
  372. """
  373. 获取单个端口状态
  374. funcCode :
  375. 发送:
  376. 返回:
  377. """
  378. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  379. return ctrInfo.get(port)
  380. def get_port_status_from_dev(self):
  381. """
  382. funCode : CC
  383. """
  384. portList = []
  385. allnums = self.get_default_port_nums()
  386. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  387. for key in xrange(1,allnums+1):
  388. lineInfo = ctrInfo.get(str(key))
  389. if not lineInfo:
  390. item = {
  391. "index": str(key),
  392. "status": "idle",
  393. }
  394. elif "isStart" in lineInfo or lineInfo.get("status") == Const.DEV_WORK_STATUS_WORKING:
  395. item = {
  396. "index": str(key),
  397. "status": "busy",
  398. "nickName": lineInfo.get("nickName"),
  399. "startTime": lineInfo.get("startTime"),
  400. }
  401. else:
  402. item = {
  403. "index": str(key),
  404. "status": "idle",
  405. }
  406. portList.append(item)
  407. return portList
  408. def _notify_user_service_over(self, managerialOpenId, port, usedTime, money, type=None):
  409. group = Group.get_group(self.device.get("groupId"))
  410. adminTel = self.device["otherConf"].get("deviceConfigs", {}).get("adminTel")
  411. serviceText = u"温馨提示,本次寄存服务已完成! \\n请带走您的行李物品,关好箱门\\n祝您生活愉快!!!"
  412. if type == "error":
  413. serviceText = u"检测到当前柜门无法正常开启\\n请联系管理员:{}".format(adminTel)
  414. if type == "dealer":
  415. serviceText = u"检测到当前柜门无法正常开启\\n由管理员远程开门,联系管理员:{}".format(adminTel)
  416. notifyData = {
  417. "title": u"\\n\\n设备编号:\\t\\t{logicalCode}-{port}号柜门\\n\\n服务地址:\\t\\t{group}\\n\\n使用时长:\\t\\t{chargeTime}分钟\\n\\n本单消费:\\t\\t{money}".format(
  418. logicalCode=self._device["logicalCode"],
  419. port=port,
  420. group=group["address"],
  421. chargeTime=usedTime,
  422. money=str(money),
  423. ),
  424. "service": serviceText,
  425. "finishTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  426. "remark": u"谢谢您的支持"
  427. }
  428. task_caller(
  429. func_name="report_to_user_via_wechat",
  430. openId=managerialOpenId,
  431. dealerId=self.device.get("ownerId"),
  432. templateName="service_complete",
  433. **notifyData
  434. )
  435. @property
  436. def isHaveStopEvent(self):
  437. return False
  438. def isHaveCallback(self):
  439. return True
  440. def do_callback(self, order):
  441. # type: (ConsumeRecord) -> None
  442. consumeDict = order.servicedInfo
  443. port = order.attachParas.get("chargeIndex")
  444. logger.info(self.log_obj(
  445. "receive finished callback,order={}, devNo={} openId={}, port={}, money={}".format(order.orderNo,
  446. order.devNo,
  447. order.openId,
  448. port,
  449. order.money)))
  450. openId = order.openId
  451. user = MyUser.objects.filter(openId=openId, groupId=self.device.get("groupId")).first()
  452. usedTime = consumeDict.get("duration", 0)
  453. # 重连四次
  454. try:
  455. self.finished_order_open_door(port)
  456. except Exception:
  457. self._notify_user_service_over(user.managerialOpenId, port, usedTime,
  458. order.money, type="error")
  459. order.update(errorDesc=u"开门失败,请联系管理员")
  460. logger.info("do finished callback is fail")
  461. return
  462. ServiceProgress.update_progress_and_consume_rcd(
  463. self._device["ownerId"],
  464. {
  465. "open_id": order.openId,
  466. "device_imei": self.device["devNo"],
  467. "port": int(port),
  468. "isFinished": False
  469. },
  470. consumeDict
  471. )
  472. self._notify_user_service_over(user.managerialOpenId, port, usedTime,
  473. order.money)
  474. Device.clear_port_control_cache(self.device.devNo, port)
  475. logger.info(self.log_obj("callback finished callback is over!!"))
  476. def test(self, port="1"):
  477. # type:(str) -> None
  478. """
  479. 打开某个柜门
  480. funcCode : 02
  481. """
  482. portHex = self.encode_str(port, 2)
  483. data = portHex + "FFFF"
  484. self.send_mqtt(funCode="02", data=data)
  485. def set_device_function(self, request, lastSetConf):
  486. print request
  487. if 'initDev' in request.POST:
  488. self._do_init_dev()
  489. def set_device_function_param(self, requestBody, lastSetConf):
  490. locakDelay = requestBody.POST.get("lockDelay")
  491. adminPhone = requestBody.POST.get("adminTel")
  492. refundProtectionTime = requestBody.POST.get("refundProtectionTime")
  493. configs = lastSetConf or dict()
  494. if locakDelay:
  495. configs.update({"lockDelay": locakDelay})
  496. if adminPhone:
  497. configs.update({"adminTel": adminPhone})
  498. if refundProtectionTime:
  499. configs.update({"refundProtectionTime": refundProtectionTime})
  500. self.do_update_configs(configs)
  501. def get_dev_setting(self):
  502. deviceConfigs = self.device["otherConf"].get("deviceConfigs", {})
  503. adminTel = deviceConfigs.get('adminTel', '')
  504. lockDelay = deviceConfigs.get('lockDelay', 0)
  505. return {'adminTel':adminTel, 'lockDelay':lockDelay}
  506. def unlockPort(self, port):
  507. """
  508. 经销商开门 如果里面有东西,订单信息里面需要记录是由经销商开门
  509. """
  510. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  511. lineInfo = ctrInfo.get(port)
  512. # TODO 经销商面板端口开启未使用柜门
  513. if not lineInfo or lineInfo.get("status", 2) == Const.DEV_WORK_STATUS_IDLE:
  514. portHex = self.encode_str(port)
  515. data = portHex + "FFFF"
  516. self.send_mqtt(funCode="02", data=data)
  517. # 用户联系经销商开门
  518. else:
  519. openId = lineInfo.get("openId")
  520. orderNo = lineInfo.get("orderNo")
  521. now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  522. logger.info(self.log_obj(
  523. "Door opened by dealer user_openId={},dealerId={},orderNo={},openedTime={}".format(openId, self.device[
  524. "ownerId"], orderNo, now))
  525. )
  526. portHex = self.encode_str(port)
  527. data = portHex + "FFFF"
  528. try:
  529. self.send_mqtt(funCode = "02", data = data)
  530. except ServiceException as e:
  531. logger.info(self.log_obj("open door is fail"))
  532. raise ServiceException(
  533. {"result": 2, "description": e.result.get("description"), "rst": -1})
  534. order = ConsumeRecord.objects.get(orderNo=orderNo)
  535. if order.status != "finished":
  536. order.isNormal = False
  537. order.desc = "该笔订单异常故障未付款,由经销商开门"
  538. order.coin = VirtualCoin(0)
  539. order.money = VirtualCoin(0)
  540. order.status = "finished"
  541. order.save()
  542. user = MyUser.objects.filter(openId=openId, groupId=self.device.get("groupId")).first()
  543. usedTime = order.servicedInfo.get("duration", 0)
  544. self._notify_user_service_over(user.managerialOpenId, port, usedTime,
  545. order.money, type="dealer")
  546. spQueryDict = {
  547. "open_id": openId,
  548. "device_imei": self.device["devNo"],
  549. "port": int(port),
  550. "isFinished": False
  551. }
  552. sp = ServiceProgress.objects.filter(**spQueryDict).first()
  553. if sp:
  554. logger.info(self.log_obj("now finished service progress,id={}".format(str(sp.id))))
  555. sp.isFinished = True
  556. sp.consumeOrder.update({"adminOpenedTime": now})
  557. sp.save()
  558. Device.clear_port_control_cache(self.device.devNo, port)
  559. def _do_init_dev(self):
  560. pass