ywt_chongdiangui_new.py 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. # 整个业务流程的 控制的最关键的三个函数 _open, _charge, _stop
  4. # 粤万通 充电柜 基本的业务流程可以描述为
  5. # 1. 扫码/刷卡 启动设备 此时调用 _open 函数,并创建相应的订单以及开锁密码 开锁密码在整个的充电过程中不能改变 用于设备离线时候直接从触控屏解锁
  6. # 2. 门锁打开的时候 等候用户下一步操作(放入电池、关闭门锁)
  7. # 3. 主板方检测到门锁关闭之后 上报门锁关闭信号 服务器接收到关锁信号之后 调用_charge 函数 ,注意此时仅仅充电不开锁 开启端口的充电
  8. # 4. 在整个充电的过程中 主板会不断地上报 该端口的充电数据 C0指令,其中 power 为瞬时功率 chargeTime 为累计充电时间 stayTime 为累计占位时间
  9. # 5. 在 到达最大充电时间 或者 主板检测电池充满 之后, 会上报充电结束的指令 C1,同时主板停止充电,开始进入 占位状态
  10. # 6. 需要注意的是,在充电柜业务流程中,停止充电并不意味着 流程结束,也不代表可以对其进行 费用结算
  11. # 7. 关于充电柜的业务结束一共有以下 3种情况:
  12. # 7.1 用户再次扫码,并点击停止充电按钮,此时触发 stop 函数,服务器通过 _stop 发起对主板的开锁,主板接收到之后 开锁并同时上报最后一次数据状态
  13. # 7.2 用户刷卡启动的设备,需要用户刷卡结束,用户第二次刷卡 后,主板上报刷卡请求充电指令,服务器接收到之后 通过 _stop 发起对于主板的开锁 同时上报最后一次充电状态
  14. # 7.3 用户直接在主板上选择密码开锁, 输入密码(开启充电时候下发的密码),主板接收到之后,无条件开锁,同时上报最后一次充电状态。一般用于设备离线的时候使用
  15. # 8. 用户以任何方式 结束充电柜业务之后,需要对其订单进行结算,并将整个过程中的费用记录到相应订单上
  16. # 关于粤万通新式充电柜的计费
  17. # 计费公式为: 总费用 = 充电费用 + 占位费用
  18. # 对于占位费用,目前的计费方式就是 占位时间 ✖ 占位单价
  19. # 对于充电费用,目前一共有 两种计费方式可供选择,以下
  20. # 充电计费方式一:按时间计费:
  21. # 本次粤万通计费将时间计费修改为按时段计费,例如
  22. # 经销商设置8:00-9:00 单价为1小时1元;9:00-10:00 单价为1小时2元;默认单价为1小时0.5元
  23. # 某用户从7:00充电一直到10:30 共四个小时
  24. # 7:00-8:00时间段经销商没有设置单价, 则费用为默认单价0.5元
  25. # 8:00-9:00时间段设置了单价,则费用为1元
  26. # 9:00-10:00时间段设置了单价,则费用为2元
  27. # 10:00-10:30 时间段没有设置单价,则费用为默认单价 0.5*0.5小时 为0.25元
  28. # 此次按时间充电的费用为 0.5+1+2+0.25 = 3.75元
  29. # 充电计费方式二:按功率计费,即经销商预设功率挡位以及相应单价 例如
  30. # 经销商设置为100-200w 1小时1元;200-400w 1小时2元,默认单价为0.5元
  31. # 用户充电过程中一共1小时,其中半小时功率为95w,半小时功率为 240w,则充电总费用为 1.5元
  32. # 充电计费的注意事项:
  33. # 1.按时间计费的时间是订单的初始时间-结束时间,也就是说只要最关键的报文结束报文 服务器能够接收到,就一定能计算出费用
  34. # 2.按功率计费需要依赖每次的充电状态上报,即C0指令,C0指令主板会有ack机制(最新的修改已经将ack置于模块侧回复),但若某次没有收到,则以最近一次的功率为主计算该段的费用
  35. # 3.计费有最低费用和最高费用,假设经销商设置的最低费用为1元,最高费用为10元,假设某次用户总费用为0.5元,则系统收费为1元;同理也不能超过最大费用
  36. import binascii
  37. import datetime
  38. import logging
  39. import os
  40. import random
  41. import time
  42. import simplejson as json
  43. from mongoengine import DoesNotExist
  44. from apilib.monetary import VirtualCoin, RMB
  45. from apilib.utils_string import make_title_from_dict
  46. from apps.web.common.proxy import ClientConsumeModelProxy
  47. from apps.web.constant import DeviceCmdCode, Const, MQTT_TIMEOUT
  48. from apps.web.core.adapter.base import SmartBox, reverse_hex, fill_2_hexByte
  49. from apps.web.core.exceptions import ServiceException
  50. from apps.web.core.networking import MessageSender
  51. from apps.web.device.models import Group, Device, DevicePortLastReport, DeviceType
  52. from apps.web.user.models import ConsumeRecord, ServiceProgress, MyUser
  53. from apps.web.core.device_define.ywt_chongdiangui_new import DefaultParams, Calculater
  54. from apps.web.utils import concat_user_login_entry_url
  55. from taskmanager.mediator import task_caller
  56. logger = logging.getLogger(__name__)
  57. class ChargeCabinet(SmartBox):
  58. def __init__(self, *args, **kwargs):
  59. super(ChargeCabinet, self).__init__(*args, **kwargs)
  60. # 主要用于计算费用
  61. self.devNo = self.device.devNo
  62. @property
  63. def is_support_auto_charge(self):
  64. """
  65. 是否支持自动供电
  66. 主板的新特性 发送新的指令不需要发送充电 关门后直接启动充电
  67. 兼容之前的设备特性和现在的设备类型特性
  68. """
  69. # 先找设备
  70. if self.device.support_dev_type_features("support_auto_charge_after_close_door"):
  71. return True
  72. return False
  73. @property
  74. def password(self):
  75. return random.randint(0x2710, 0xFFFF)
  76. def _send_data(self, funCode, sendData, cmd=None, timeout=MQTT_TIMEOUT.NORMAL, orderNo=None):
  77. """
  78. 发送报文封装
  79. :param funCode:
  80. :param sendData:
  81. :param cmd:
  82. :param timeout:
  83. :param orderNo:
  84. :return:
  85. """
  86. if cmd is None:
  87. cmd = DeviceCmdCode.OPERATE_DEV_SYNC
  88. result = MessageSender.send(device = self.device, cmd = cmd, payload = {
  89. "IMEI": self._device["devNo"],
  90. "funCode": funCode,
  91. "data": sendData
  92. }, timeout = timeout)
  93. if result.has_key("rst"):
  94. if result["rst"] == -1:
  95. raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请稍候再试'})
  96. elif result["rst"] == 1:
  97. raise ServiceException({'result': 2, 'description': u'充电桩主板连接故障'})
  98. elif result["rst"] == 0:
  99. return result
  100. else:
  101. raise ServiceException({'result': 2, 'description': u'系统错误'})
  102. else:
  103. raise ServiceException({'result': 2, 'description': u'系统错误'})
  104. @staticmethod
  105. def _to_str(data):
  106. return binascii.unhexlify(data)
  107. @staticmethod
  108. def _to_ascii(data):
  109. return binascii.hexlify(data)
  110. @staticmethod
  111. def _suit_power_package(package):
  112. """适配之前的 功率计费规则"""
  113. if not package or "lowLimit" not in package[0].keys():
  114. return package
  115. newPackage = list()
  116. for _item in package:
  117. newPackage.append({"max": _item["upLimit"], "min": _item["lowLimit"], "price": _item["price"] * 60 / _item["time"]})
  118. newPackage.append({"max": "default", "min": "default", "price": 0})
  119. return newPackage
  120. @staticmethod
  121. def _suit_time_package(package):
  122. """适配之前的 时间计费规则"""
  123. if not package or not isinstance(package, (int, str)):
  124. return package
  125. return [{"max": "default", "min": "default", "price": int(package)}]
  126. def _notify_user_service_over(self, managerialOpenId, extra, isPaid=True):
  127. title = make_title_from_dict(extra)
  128. notifyData = {
  129. "title": title,
  130. "service": u"已使用账户余额自动结算此次消费" if isPaid else u"您的账户余额已不足以抵扣此次消费,请前往账单中心进行支付",
  131. "finishTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  132. "remark": u'谢谢您的支持'
  133. }
  134. task_caller(
  135. func_name='report_to_user_via_wechat',
  136. openId=managerialOpenId,
  137. dealerId=self.device.get("ownerId"),
  138. templateName="service_complete",
  139. **notifyData
  140. )
  141. @staticmethod
  142. def _parse_event_A1(data):
  143. return dict()
  144. @staticmethod
  145. def _parse_event_A2(data):
  146. return dict()
  147. @staticmethod
  148. def _parse_event_B0(data):
  149. cardPre = ChargeCabinet._to_str(data[8: 16])
  150. cardNo = ChargeCabinet._to_str(data[16: 32])
  151. portStr = str(int(data[32: 34], 16))
  152. return {
  153. "cardPre": cardPre,
  154. "cardNo": cardNo,
  155. "cardNoHex": data[8: 32], # zjl 16->8
  156. "portStr": portStr,
  157. "portHex": data[32: 34] # zjl new
  158. }
  159. @staticmethod
  160. def _parse_event_B1(data):
  161. cardPre = ChargeCabinet._to_str(data[8: 16])
  162. cardNo = ChargeCabinet._to_str(data[16: 32])
  163. portStr = str(int(data[32: 34], 16))
  164. orderNoHex = data[34: 48]
  165. orderNo = str(int(reverse_hex(orderNoHex), 16))
  166. return {
  167. "cardPre": cardPre,
  168. "cardNo": cardNo,
  169. "cardNoHex": data[8: 32], # zjl 16->8
  170. "portStr": portStr,
  171. "portHex": data[32:34], # zjl
  172. "orderNoHex": orderNoHex,
  173. "orderNo": orderNo
  174. }
  175. @staticmethod
  176. def _parse_event_C0(data):
  177. orderNoHex = data[8: 22]
  178. orderNo = str(int(reverse_hex(orderNoHex), 16))
  179. portStr = str(int(data[22: 24], 16))
  180. voltage = int(reverse_hex(data[24: 28]), 16)
  181. power = int(reverse_hex(data[32: 36]), 16)
  182. elec = int(reverse_hex(data[36: 44]), 16) / 1000.0
  183. chargeTime = int(reverse_hex(data[44: 48]), 16)
  184. stayTime = int(reverse_hex(data[48:52]), 16)
  185. temperature = int(reverse_hex(data[52: 56]), 16)
  186. status = DefaultParams.STATUS_MAP.get(data[56: 58], Const.DEV_WORK_STATUS_IDLE)
  187. return {
  188. "orderNo": orderNo,
  189. "orderNoHex": orderNoHex,
  190. "portStr": portStr,
  191. "voltage": voltage,
  192. "power": power,
  193. "elec": elec,
  194. "chargeTime": chargeTime,
  195. "temperature": temperature,
  196. "status": status,
  197. "stayTime": stayTime
  198. }
  199. @staticmethod
  200. def _parse_event_E0(data):
  201. portStr = str(int(data[8: 10], 16))
  202. faultHex = data[10: 14]
  203. fault = str(int(faultHex[2:4]+faultHex[:2], 16))
  204. faultReason = DefaultParams.FAULT_MAP.get(fault)
  205. return {
  206. "portHex": data[8: 10],
  207. "portStr": portStr,
  208. "faultCode": fault,
  209. "statusInfo": faultReason
  210. }
  211. @staticmethod
  212. def _parse_event_C1(data):
  213. orderNoHex = data[8: 22]
  214. orderNo = str(int(reverse_hex(orderNoHex), 16))
  215. portHex = data[22: 24]
  216. portStr = str(int(data[22: 24], 16))
  217. voltage = int(reverse_hex(data[24: 28]), 16)
  218. power = int(reverse_hex(data[32: 36]), 16)
  219. elec = int(reverse_hex(data[36: 44]), 16) / 1000.0
  220. chargeTime = int(reverse_hex(data[44: 48]), 16)
  221. stayTime = int(reverse_hex(data[48:52]), 16)
  222. temperature = int(reverse_hex(data[52: 56]), 16)
  223. reasonCode = data[56: 58]
  224. reason = DefaultParams.STOP_REASON_MAP.get(reasonCode)
  225. return {
  226. "orderNoHex": orderNoHex,
  227. "orderNo": orderNo,
  228. "portHex": portHex,
  229. "portStr": portStr,
  230. "voltage": voltage,
  231. "power": power,
  232. "elec": elec,
  233. "chargeTime": chargeTime,
  234. "temperature": temperature,
  235. "reasonCode": reasonCode,
  236. "reason": reason,
  237. "stayTime": stayTime
  238. }
  239. @staticmethod
  240. def _parse_event_C3(data):
  241. portStr = str(int(data[8:10], 16))
  242. doorStatus = data[10: 12]
  243. return {
  244. "portStr": portStr,
  245. "doorStatus": doorStatus
  246. }
  247. @staticmethod
  248. def _parse_result_B2(data):
  249. orderNoHex = data[8: 22]
  250. orderNo = str(int(reverse_hex(orderNoHex), 16))
  251. portHex = data[22: 24]
  252. portStr = str(int(data[22: 24], 16))
  253. voltage = int(reverse_hex(data[24: 28]), 16)
  254. power = int(reverse_hex(data[32: 36]), 16)
  255. elec = int(reverse_hex(data[36: 44]), 16) / 1000.0
  256. chargeTime = int(reverse_hex(data[44: 48]), 16)
  257. stayTime = int(reverse_hex(data[48:52]), 16)
  258. return {
  259. "orderNoHex": orderNoHex,
  260. "orderNo": orderNo,
  261. "portHex": portHex,
  262. "portStr": portStr,
  263. "voltage": voltage,
  264. "power": power,
  265. "elec": elec,
  266. "chargeTime": chargeTime,
  267. "stayTime": stayTime
  268. }
  269. @staticmethod
  270. def _parse_event_CA(data):
  271. data = data[8: -4]
  272. result = list()
  273. while data:
  274. orderNoHex = data[: 14]
  275. orderNo = str(int(reverse_hex(data[:14]), 16))
  276. portStr = str(int(data[14: 16], 16))
  277. voltage = int(reverse_hex(data[16: 20]), 16)
  278. power = int(reverse_hex(data[24: 28]), 16)
  279. elec = int(reverse_hex(data[28: 36]), 16) / 1000.0
  280. chargeTime = int(reverse_hex(data[36: 40]), 16)
  281. stayTime = int(reverse_hex(data[40: 44]), 16)
  282. temperature = int(reverse_hex(data[44: 48]), 16)
  283. status = DefaultParams.STATUS_MAP.get(data[48: 50], Const.DEV_WORK_STATUS_IDLE)
  284. data = data[50: ]
  285. result.append({
  286. "orderNo": orderNo,
  287. "orderNoHex": orderNoHex,
  288. "portStr": portStr,
  289. "voltage": voltage,
  290. "power": power,
  291. "elec": elec,
  292. "chargeTime": chargeTime,
  293. "temperature": temperature,
  294. "status": status,
  295. "stayTime": stayTime
  296. })
  297. return {"subChargeList": result}
  298. # -----------------------------------------------将开门、充电以及停止充电区分开--------------------------------------------------------
  299. def _open(self, port, orderNo, pw):
  300. """
  301. 打开柜门 不启动充电 为用户使用的第一步
  302. :param port: 端口号
  303. :param orderNo: 订单编号
  304. :param pw: 开锁密码
  305. :return:
  306. """
  307. operHex = "03"
  308. chargeTimeHex = "0000"
  309. orderNoHex = fill_2_hexByte(hex(int(orderNo)), 14, reverse=True)
  310. portHex = fill_2_hexByte(hex(int(port)), 2, reverse=True)
  311. pwHex = fill_2_hexByte(hex(int(pw)), 4, reverse=True)
  312. data = orderNoHex+portHex+chargeTimeHex+operHex+pwHex
  313. return self._send_data("B2", data, timeout=15)
  314. def _charge(self, port, orderNo, pw, door=False, chargeTime=0x0258):
  315. """
  316. 直接下发的充电指令 不打开柜门
  317. :param port: 端口号
  318. :param orderNo: 订单编号
  319. :param pw: 开锁密码
  320. :param door: true 表示开锁 false 表示不开所
  321. :param chargeTime: 充电时间 最大充电时间为600分钟
  322. :return:
  323. """
  324. operHex = "01" if not door else "02"
  325. orderNoHex = fill_2_hexByte(hex(int(orderNo)), 14, reverse=True)
  326. portHex = fill_2_hexByte(hex(int(port)), 2, reverse=True)
  327. pwHex = fill_2_hexByte(hex(int(pw)), 4, reverse=True)
  328. chargeTimeHex = fill_2_hexByte(hex(int(chargeTime)), 4, reverse=True)
  329. data = orderNoHex+portHex+chargeTimeHex+operHex+pwHex
  330. return self._send_data("B2", data, timeout=15)
  331. def _new_charge(self,port, orderNo, pw, chargeTime=0x0258):
  332. operHex = "02"
  333. orderNoHex = fill_2_hexByte(hex(int(orderNo)), 14, reverse=True)
  334. portHex = fill_2_hexByte(hex(int(port)), 2, reverse=True)
  335. pwHex = fill_2_hexByte(hex(int(pw)), 4, reverse=True)
  336. chargeTimeHex = fill_2_hexByte(hex(int(chargeTime)), 4, reverse=True)
  337. data = orderNoHex + portHex + chargeTimeHex + operHex + pwHex
  338. return self._send_data("B2", data, timeout=15)
  339. def _stop(self, port, orderNo, pw, door=False):
  340. """
  341. 终止设备运行
  342. :param port: 端口号
  343. :param orderNo: 订单编号
  344. :param pw: 开锁密码
  345. :param door: true表示断电的同时开锁 false 表示仅仅断电
  346. :return:
  347. """
  348. operHex = "11" if not door else "12"
  349. orderNoHex = fill_2_hexByte(hex(int(orderNo or 0)), 14, reverse=True)
  350. portHex = fill_2_hexByte(hex(int(port)), 2, reverse=True)
  351. pwHex = fill_2_hexByte(hex(int(pw)), 4, reverse=True)
  352. data = orderNoHex+portHex+"0000"+operHex+pwHex
  353. return self._send_data("B2", data)
  354. # ----------------------------------------------------------------------------------------------------------------------
  355. def _get_door_status(self):
  356. try:
  357. result = self._send_data("BD", "00", timeout=5)
  358. except ServiceException:
  359. return dict()
  360. data = result.get("data", "")[8: -4]
  361. doorStatus = dict()
  362. port = 0
  363. while data:
  364. port = port + 1
  365. _door = u"打开" if data[: 2] == "01" else u"关闭"
  366. doorStatus[str(port)] = _door
  367. data = data[2:]
  368. return doorStatus
  369. def _get_port_status(self):
  370. result = self._send_data("B3", "00")
  371. data = result.get("data", "")[8: -4]
  372. lockPorts = self.device.otherConf.get("lockPorts") or list()
  373. portInfo = dict()
  374. while data:
  375. tempPort = str(int(data[0: 2], 16))
  376. tempStatus = DefaultParams.STATUS_MAP.get(data[2:4]) if tempPort not in lockPorts else Const.DEV_WORK_STATUS_FORBIDDEN
  377. portInfo.update({tempPort: tempStatus})
  378. data = data[4: ]
  379. return portInfo
  380. def _get_port_status_detail(self):
  381. result = self._send_data("B4", "00")
  382. data = result.get("data", "")[8: -4]
  383. lockPorts = self.device.otherConf.get("lockPorts") or list()
  384. portInfo = dict()
  385. while data:
  386. tempPort = str(int(data[0: 2], 16))
  387. tempVoltage = int(reverse_hex(data[2: 6]), 16)
  388. tempAmpere = int(reverse_hex(data[6:10]), 16) / 1000.0
  389. tempPower = int(reverse_hex(data[10: 14]), 16)
  390. tempElec = int(reverse_hex(data[14: 22]), 16) / 1000.0
  391. tempTime = int(reverse_hex(data[22: 26]), 16)
  392. tempOccTime = int(reverse_hex(data[26:30]), 16)
  393. tempTemper = int(reverse_hex(data[30: 34]), 16)
  394. tempStatus = DefaultParams.STATUS_MAP.get(data[34:36]) if tempPort not in lockPorts else Const.DEV_WORK_STATUS_FORBIDDEN
  395. portInfo.update({
  396. tempPort: {
  397. "voltage": tempVoltage,
  398. "elec": tempElec,
  399. "ampere": tempAmpere,
  400. "power": tempPower,
  401. "chargeTime": tempTime,
  402. "temperature": tempTemper,
  403. "status": tempStatus,
  404. "occTime": tempOccTime
  405. }
  406. })
  407. data = data[36: ]
  408. return portInfo
  409. def _reboot_device(self):
  410. return self._send_data("B5", "00")
  411. def _lock_device(self):
  412. operateHex = "01"
  413. return self._send_data("B6", operateHex)
  414. def _unlock_device(self):
  415. operateHex = "00"
  416. return self._send_data("B6", operateHex)
  417. def _sync_device_time(self):
  418. nowTime = datetime.datetime.now()
  419. yearHex = fill_2_hexByte(hex(nowTime.year), 4, reverse=True)
  420. monthHex = fill_2_hexByte(hex(nowTime.month), 2)
  421. dayHex = fill_2_hexByte(hex(nowTime.day), 2)
  422. hourHex = fill_2_hexByte(hex(nowTime.hour), 2)
  423. minuteHex = fill_2_hexByte(hex(nowTime.minute), 2)
  424. secondHex = fill_2_hexByte(hex(nowTime.second), 2)
  425. sendData = yearHex + monthHex + dayHex + hourHex + minuteHex + secondHex + "00"
  426. self._send_data("A1", sendData=sendData, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
  427. def _sync_device_settings(self):
  428. domain = os.environ.get("MY_DOMAIN")
  429. otherConf = self._device.get("otherConf", dict())
  430. qr_code_url = concat_user_login_entry_url(l = self._device["logicalCode"])
  431. snCode = otherConf.get("snCode", self._device["logicalCode"].replace("G", ""))
  432. minPower = otherConf.get("minPower", DefaultParams.DEFAULT_MIN_POWER)
  433. maxPower = otherConf.get("maxPower", DefaultParams.DEFAULT_MAX_POWER)
  434. floatTime = otherConf.get("floatTime", DefaultParams.DEFAULT_FLOAT_TIME)
  435. ICSetupCode = otherConf.get("ICSetupCode", DefaultParams.DEFAULT_IC_CODE)
  436. qrCodeLenHex = fill_2_hexByte(hex(len(qr_code_url)), 2)
  437. minPowerHex = fill_2_hexByte(hex(int(minPower)), 4, reverse=True)
  438. maxPowerHex = fill_2_hexByte(hex(int(maxPower)), 4, reverse=True)
  439. floatTimeHex = fill_2_hexByte(hex(int(floatTime)), 4, reverse=True)
  440. qrCodeHex = self._to_ascii(qr_code_url)
  441. snCodeHex = self._to_ascii(snCode)
  442. ICSetupCodeHex = self._to_ascii(ICSetupCode)
  443. sendData = snCodeHex + minPowerHex + maxPowerHex + floatTimeHex + ICSetupCodeHex + qrCodeLenHex + qrCodeHex
  444. self._send_data("A2", sendData=sendData, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
  445. # 添加特性 去掉前台的金额显示按钮
  446. otherConf.update({"payAfterUse": True})
  447. Device.objects.filter(devNo=self.device.devNo).update(otherConf=otherConf)
  448. Device.invalid_device_cache(self.device.devNo)
  449. def _update_device_conf(self, data):
  450. minPower = data.get("minPower", DefaultParams.DEFAULT_MIN_POWER)
  451. maxPower = data.get("maxPower", DefaultParams.DEFAULT_MAX_POWER)
  452. floatTime = data.get("floatTime", DefaultParams.DEFAULT_FLOAT_TIME)
  453. ICSetupCode = data.get("ICSetupCode", DefaultParams.DEFAULT_IC_CODE)
  454. actualPortNum = data.get("actualPortNum", DefaultParams.DEFAULT_PORT_NUM)
  455. chargeType = data.get("chargeType", DefaultParams.DEFAULT_CHARGE_TYPE)
  456. minAfterStartCoins = data.get("minAfterStartCoins", DefaultParams.DEFAULT_MIN_START_COINS)
  457. stayTimeUnitPrice = data.get("stayTimeUnitPrice", DefaultParams.DEFAULT_STAY_TIME_UNIT_PRICE)
  458. freeStayTime = data.get("freeStayTime", DefaultParams.DEFAULT_FREE_STAY_TIME)
  459. minConsume = data.get("minConsume", DefaultParams.DEFAULT_MIN_CONSUME)
  460. maxConsume = data.get("maxConsume", DefaultParams.DEFAULT_MAX_CONSUME)
  461. powerPackage = data.get("powerPackage", DefaultParams.DEFAULT_POWER_PACKAGE)
  462. timePricePackage = data.get("timePricePackage", DefaultParams.DEFAULT_TIME_PACKAGE)
  463. timeUnitPrice = data.get("timePrice", DefaultParams.DEFAULT_TIME_UNIT_PRICE)
  464. powerUnitPrice = data.get("powerPrice", DefaultParams.DEFAULT_POWER_UNIT_PRICE)
  465. elecCharge = data.get("elecCharge", DefaultParams.DEFAULT_ELEC_CHARGE)
  466. serviceCharge = data.get("serviceCharge", DefaultParams.DEFAULT_SERVICE_CHARGE)
  467. otherConf = self._device.get("otherConf", {})
  468. otherConf.update({
  469. "minAfterStartCoins": int(minAfterStartCoins),
  470. "minPower": int(minPower),
  471. "maxPower": int(maxPower),
  472. "floatTime": int(floatTime),
  473. "ICSetupCode": ICSetupCode,
  474. "actualPortNum": int(actualPortNum),
  475. "chargeType": chargeType,
  476. "stayTimeUnitPrice": float(stayTimeUnitPrice),
  477. "freeStayTime": float(freeStayTime),
  478. "minConsume": float(minConsume),
  479. "maxConsume": float(maxConsume),
  480. "timeUnitPrice": float(timeUnitPrice),
  481. "powerUnitPrice": float(powerUnitPrice),
  482. "powerPackage": powerPackage,
  483. "timePricePackage": timePricePackage,
  484. "elecCharge": float(elecCharge),
  485. "serviceCharge": float(serviceCharge),
  486. })
  487. devNo = self._device["devNo"]
  488. try:
  489. Device.objects.filter(devNo=devNo).update(otherConf=otherConf)
  490. except Exception as e:
  491. logger.error(e)
  492. raise ServiceException({'result': 2, 'description': u'设置错误,请重新操作试试'})
  493. Device.invalid_device_cache(devNo)
  494. def _dealer_start_device(self, package, attachParas):
  495. """
  496. 如果 是经销商启动 就当是取消当前端口的订单
  497. :param package:
  498. :param attachParas:
  499. :return:
  500. """
  501. portStr = attachParas.get("chargeIndex")
  502. if not portStr:
  503. raise ServiceException({"result": "2", "description": u"请选择插座号!"})
  504. devCache = Device.get_dev_control_cache(self.devNo)
  505. portCache = devCache.get(portStr, dict())
  506. orderNo = portCache.get("orderNo", 0xFFFFFFFFFFFFFF)
  507. pw = portCache.get("pw", 0)
  508. self._stop(portStr, orderNo, pw, door=True)
  509. # 清空端口的缓存
  510. Device.clear_port_control_cache(self.devNo, portStr)
  511. # 然后将订单直接取消
  512. try:
  513. consumeRecord = ConsumeRecord.objects.get(orderNo=orderNo, isNormal=True)
  514. except DoesNotExist:
  515. return
  516. consumeRecord.isNormal = False
  517. consumeRecord.status = "finished"
  518. consumeRecord.errorDesc = u"经销商关闭订单"
  519. consumeRecord.finishedTime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  520. consumeRecord.save()
  521. sp = ServiceProgress.objects.filter(
  522. **{"device_imei": self.devNo, "isFinished": False, "open_id": consumeRecord.openId, "port": int(portStr)}
  523. ).first()
  524. if not sp:
  525. return
  526. sp.isFinished = True
  527. sp.finished_time = int(time.time())
  528. sp.status = "finished"
  529. sp.save()
  530. def _stage_billing(self, devNo, port, orderNo, **kwargs):
  531. try:
  532. order = ConsumeRecord.objects.get(orderNo=orderNo)
  533. except DoesNotExist:
  534. logger.warning("[{}._stage_billing] not find order, devNo = {}, orderNo = {}".format(self.__class__.__name__, devNo, orderNo))
  535. return
  536. lastReport = DevicePortLastReport.get_last(devNo=devNo, port=port, orderNo=orderNo)
  537. newReport = DevicePortLastReport.save_last(devNo=devNo, port=port, orderNo=orderNo, **kwargs)
  538. cal = Calculater(device=self.device, order=order)
  539. chargeConsume, stayConsume = cal.stage_billing(lastReport, newReport)
  540. newReport.update_consume(chargeConsume, stayConsume)
  541. def _ack(self, ack_id):
  542. self._send_data(funCode='AK', sendData=ack_id, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
  543. def check_order_state(self, openId):
  544. """
  545. 通过 openId 以及设备来鉴别 订单
  546. :param openId:
  547. :return:
  548. """
  549. dealerId = self.device.ownerId
  550. devTypeCode = self.device.devType.get("code")
  551. return ClientConsumeModelProxy.get_not_finished_record(ownerId = dealerId,
  552. openId = openId,
  553. devTypeCode = devTypeCode)
  554. def analyze_event_data(self, data):
  555. cmdCode = data[6:8]
  556. funcName = "_parse_event_{}".format(cmdCode.upper())
  557. func = getattr(ChargeCabinet, funcName, None)
  558. if func and callable(func):
  559. eventData = func(data)
  560. eventData.update({"cmdCode": cmdCode})
  561. return eventData
  562. else:
  563. logger.warning("<{}> device receive an undefined cmd <{}>, data is <{}>".format(self.devNo, cmdCode, data))
  564. def get_dev_setting(self):
  565. otherConf = self._device.get("otherConf", {})
  566. return {
  567. "minPower": otherConf.get("minPower", DefaultParams.DEFAULT_MIN_POWER),
  568. "maxPower": otherConf.get("maxPower", DefaultParams.DEFAULT_MAX_POWER),
  569. "floatTime": otherConf.get("floatTime", DefaultParams.DEFAULT_FLOAT_TIME),
  570. "ICSetupCode": otherConf.get("ICSetupCode", DefaultParams.DEFAULT_IC_CODE),
  571. "actualPortNum": otherConf.get("actualPortNum", DefaultParams.DEFAULT_PORT_NUM),
  572. "minAfterStartCoins": otherConf.get("minAfterStartCoins", DefaultParams.DEFAULT_MIN_START_COINS),
  573. "stayTimeUnitPrice": otherConf.get("stayTimeUnitPrice", DefaultParams.DEFAULT_STAY_TIME_UNIT_PRICE),
  574. "freeStayTime": otherConf.get("freeStayTime", DefaultParams.DEFAULT_FREE_STAY_TIME),
  575. "minConsume": otherConf.get("minConsume", DefaultParams.DEFAULT_MIN_CONSUME),
  576. "maxConsume": otherConf.get("maxConsume", DefaultParams.DEFAULT_MAX_CONSUME),
  577. "chargeType": otherConf.get("chargeType", DefaultParams.DEFAULT_CHARGE_TYPE),
  578. "powerPackage": otherConf.get("powerPackage", DefaultParams.DEFAULT_POWER_PACKAGE),
  579. "timePricePackage": otherConf.get("timePricePackage", DefaultParams.DEFAULT_TIME_PACKAGE),
  580. "timePricePackageDefaultPrice": otherConf.get("powerUnitPrice", DefaultParams.DEFAULT_POWER_UNIT_PRICE),
  581. "powerPackageDefaultPrice": otherConf.get("timeUnitPrice", DefaultParams.DEFAULT_TIME_UNIT_PRICE),
  582. # 服务费 + 电量
  583. "elecCharge": otherConf.get("elecCharge", DefaultParams.DEFAULT_ELEC_CHARGE),
  584. "serviceCharge": otherConf.get("serviceCharge", DefaultParams.DEFAULT_SERVICE_CHARGE),
  585. }
  586. def set_device_function_param(self, request, lastSetConf):
  587. actualPortNum = request.POST.get("actualPortNum", None)
  588. if actualPortNum is not None and int(actualPortNum) > 30:
  589. raise ServiceException({"result": 2, "description": u"实际端口数量最大30"})
  590. chargeType = request.POST.get("chargeType")
  591. if chargeType not in ("time", "power", "elec"):
  592. raise ServiceException({"result": 2, "description": u"暂不支持此消费模式"})
  593. # 校验计费套餐
  594. maxConsume = request.POST.get("maxConsume")
  595. minConsume = request.POST.get("minConsume")
  596. if float(minConsume) < 0 or float(maxConsume) < 0:
  597. raise ServiceException({"result": 2, "description": u"最低消费金额和最高消费金额不能小于0"})
  598. if float(minConsume) > float(maxConsume):
  599. raise ServiceException({"result": 2, "description": u"最低消费金额不得大于最高消费金额"})
  600. # 时间计费规则校验
  601. timePricePackage = request.POST.get("timePricePackage")
  602. for _index, _package in enumerate(timePricePackage):
  603. if int(_package["max"]) <= int(_package["min"]):
  604. raise ServiceException({"result": 2, "description": u"第 {} 阶段 时间开始 {} 小于 时间结束 {}".format(_index+1, _package["max"], _package["min"])})
  605. powerPackage = request.POST.get("powerPackage")
  606. for _index, _package in enumerate(powerPackage):
  607. if int(_package["max"]) <= int(_package["min"]):
  608. raise ServiceException({"result": 2, "description": u"第 {} 阶段 功率开始 {} 小于 功率结束 {}".format(_index+1, _package["max"], _package["min"])})
  609. self._update_device_conf(request.POST)
  610. def set_device_function(self, request, lastSetConf):
  611. lockDevice = request.POST.get("lockDevice", None)
  612. rebootDevice = request.POST.get("rebootDevice", None)
  613. setToDevice = request.POST.get("setToDevice", None)
  614. if rebootDevice is not None:
  615. self._reboot_device()
  616. return
  617. if lockDevice is not None and lockDevice in ("false", "true"):
  618. lockDevice = json.loads(lockDevice)
  619. self._lock_device() if lockDevice else self._unlock_device()
  620. return
  621. # 下发所有参数到设备,这个地方做一个保护 ,设备正在工作的时候不让设置参数
  622. if setToDevice is not None:
  623. return self._sync_device_settings()
  624. def get_port_status(self, force=False):
  625. if force:
  626. self.get_port_status_from_dev()
  627. devCache = Device.get_dev_control_cache(self._device["devNo"])
  628. if "allPorts" not in devCache:
  629. self.get_port_status_from_dev()
  630. devCache = Device.get_dev_control_cache(self._device["devNo"])
  631. allPorts = devCache.get("allPorts")
  632. if allPorts is None:
  633. raise ServiceException({'result': 2, 'description': u'充电端口信息获取失败'})
  634. # 获取显示端口的数量 客户要求可以设置 显示给用户多少个端口
  635. showPortNum = self._device.get("otherConf", {}).get("actualPortNum", DefaultParams.DEFAULT_PORT_NUM)
  636. statusDict = dict()
  637. showStatusDict = dict()
  638. for portNum in xrange(allPorts):
  639. portStr = str(portNum + 1)
  640. tempDict = devCache.get(portStr, {})
  641. if "status" in tempDict:
  642. statusDict[portStr] = {"status": tempDict["status"]}
  643. elif "isStart" in tempDict:
  644. if tempDict["isStart"]:
  645. statusDict[portStr] = {"status": Const.DEV_WORK_STATUS_WORKING}
  646. else:
  647. statusDict[portStr] = {"status": Const.DEV_WORK_STATUS_IDLE}
  648. else:
  649. statusDict[portStr] = {"status": Const.DEV_WORK_STATUS_IDLE}
  650. if int(portStr) <= int(showPortNum):
  651. showStatusDict[portStr] = {"status": statusDict[portStr]["status"]}
  652. allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
  653. portsDict = {"allPorts": allPorts, "usedPorts": usedPorts, "usePorts": usePorts}
  654. Device.update_dev_control_cache(self._device["devNo"], portsDict)
  655. # 返还的是要显示的端口数量
  656. return showStatusDict
  657. def get_port_status_from_dev(self):
  658. portInfo = self._get_port_status()
  659. portDict = dict()
  660. for portStr, status in portInfo.items():
  661. portDict[portStr] = {"status": status}
  662. # 更新可用端口数量
  663. allPorts, usedPorts, usePorts = self.get_port_static_info(portDict)
  664. Device.update_dev_control_cache(
  665. self._device["devNo"],
  666. {
  667. "allPorts": allPorts,
  668. "usedPorts": usedPorts,
  669. "usePorts": usePorts
  670. }
  671. )
  672. # 更新端口状态
  673. devCache = Device.get_dev_control_cache(self._device["devNo"])
  674. for port, info in portDict.items():
  675. if port in devCache and isinstance(info, dict):
  676. devCache[port].update({"status": info["status"]})
  677. else:
  678. devCache[port] = info
  679. Device.update_dev_control_cache(self._device["devNo"], devCache)
  680. return portDict
  681. def start_device(self, package, openId, attachParas):
  682. if not openId:
  683. return self._dealer_start_device(package, attachParas)
  684. portStr = attachParas.get("chargeIndex")
  685. orderNo = attachParas.get("orderNo")
  686. pw = self.password
  687. lockPorts = self.device.otherConf.get("lockPorts") or list()
  688. if str(portStr) in lockPorts:
  689. raise ServiceException({"result": 2, "description": u"当前端口已被禁用"})
  690. devCache = Device.get_dev_control_cache(self.device.devNo) or dict()
  691. if devCache.get(portStr, dict()).get("status") == Const.DEV_WORK_STATUS_WORKING:
  692. raise ServiceException({"result": 2, "description": u"当前端口已被占用"})
  693. if portStr is None:
  694. raise ServiceException({'result': 2, 'description': u'请选择充电端口'})
  695. if orderNo is None:
  696. return ServiceException({'result': 2, "description": u"订单创建失败,请重新尝试"})
  697. # 支持发送关门自动断电的 发送新指令
  698. if self.is_support_auto_charge:
  699. result = self._new_charge(orderNo=orderNo, port=portStr, pw=pw)
  700. # 否则由服务器控制充电
  701. else:
  702. result = self._open(orderNo=orderNo, port=portStr, pw=pw)
  703. portDict = {
  704. "status": Const.DEV_WORK_STATUS_WORKING,
  705. "vCardId": self._vcard_id,
  706. "isStart": True,
  707. "openId": openId,
  708. "orderNo": orderNo,
  709. "startTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  710. "pw": pw,
  711. }
  712. Device.update_dev_control_cache(self._device["devNo"], {str(portStr): portDict})
  713. otherConf = self.device.get("otherConf") or dict()
  714. result["consumeOrderNo"] = orderNo
  715. result["servicedInfo"] = {"pw": pw, "chargeType": otherConf.get("chargeType", DefaultParams.DEFAULT_CHARGE_TYPE)}
  716. result["finishedTime"] = int(time.time()) + 7 * 24 * 60 * 60
  717. return result
  718. def stop(self, port=None):
  719. """
  720. 端口停止功能 用户主动停止了该端口
  721. 对于充电柜的业务来说 结束充电就意味着 需要打开柜门 同时结算订单
  722. 收到停止指令之后
  723. 服务器首先下发B2-12指令
  724. 然后根据返还的信息进行扣费处理
  725. 最后结存订单
  726. :param port:
  727. :return:
  728. """
  729. portStr = str(port)
  730. devCache = Device.get_dev_control_cache(self.device.devNo)
  731. portCache = devCache.get(portStr, dict())
  732. # 校验订单状态 非正在运行的订单不能结束 考虑 是否 加锁
  733. if not portCache:
  734. return
  735. if portCache.get("cardNo"):
  736. raise ServiceException({"result": 2, "description": u"刷卡启动的设备请使用刷卡结束"})
  737. orderNo = portCache.get("orderNo", "")
  738. consumeOrder = ConsumeRecord.objects.filter(orderNo=orderNo).first()
  739. if consumeOrder is None or not consumeOrder.is_running():
  740. return
  741. logger.info("device stop order, device = {}, orderNo = {}".format(self.device.devNo, orderNo))
  742. pw = portCache.get("pw")
  743. try:
  744. result = self._stop(port, orderNo, pw, True)
  745. # 然后去读取主板的的最后一次的数据
  746. data = result.get("data")
  747. curInfo = ChargeCabinet._parse_result_B2(data)
  748. curInfo.update({"orderNo": orderNo})
  749. # 记录最后一次的上报 然后获取订单的金额
  750. self._stage_billing(self.devNo, portStr, **curInfo)
  751. except ServiceException:
  752. # 保证一定能够结单 当真正门锁未开的时候,直接找经销商上分即可
  753. logger.error("device open door error! devNo = {}, orderNo = {}".format(self.device.devNo, orderNo))
  754. order = DevicePortLastReport.get_last_by_order(orderNo)
  755. curInfo = {
  756. "chargeTime": order.chargeTime,
  757. "elec": 0,
  758. "stayTime": order.stayTime,
  759. }
  760. consumeMoney = Calculater.get_consume_by_order(self.device, orderNo)
  761. consumeOrder.update(money=RMB(consumeMoney).mongo_amount, coin=VirtualCoin(consumeMoney).mongo_amount)
  762. consumeOrder.reload()
  763. consumeOrder.s_to_e()
  764. consumeDict = {
  765. "chargeIndex": portStr,
  766. 'actualNeedTime': curInfo.get("chargeTime"),
  767. 'elec': curInfo.get("elec"),
  768. 'stayTime': curInfo.get("stayTime"),
  769. 'chargeTime': curInfo.get("chargeTime")
  770. }
  771. if consumeOrder.servicedInfo and isinstance(consumeOrder.servicedInfo, dict):
  772. consumeDict.update(consumeOrder.servicedInfo)
  773. if consumeOrder.servicedInfo.get('chargeType') == 'elec':
  774. elec_charge, service_charge = self.calc_elecFee_and_serviceFee(consumeMoney)
  775. consumeDict.update({
  776. 'elecCharge': elec_charge.mongo_amount,
  777. 'serviceCharge': service_charge.mongo_amount,
  778. })
  779. ServiceProgress.update_progress_and_consume_rcd(
  780. self._device["ownerId"],
  781. {
  782. "open_id": consumeOrder.openId,
  783. "device_imei": self.device["devNo"],
  784. "port": int(portStr),
  785. "isFinished": False
  786. },
  787. consumeDict
  788. )
  789. # 通知用户充电柜业务结束 如果订单并没有使用余额结算 还需要通知用户及时去付款
  790. try:
  791. openId = portCache.get("openId")
  792. user = MyUser.objects.filter(openId=openId, groupId=self.device.get("groupId")).first()
  793. extra = [
  794. {u"设备编号": u"{}-{}".format(self.device["logicalCode"], port)},
  795. {u"服务地址": u"{}".format(self.device.group["address"])},
  796. {u"使用时长": u"{}分钟".format(curInfo.get("chargeTime"))},
  797. {u"占位时长": u"{}分钟".format(curInfo.get("stayTime"))},
  798. ]
  799. if consumeOrder.servicedInfo.get('chargeType') == 'elec':
  800. elec_charge, service_charge = self.calc_elecFee_and_serviceFee(consumeMoney)
  801. extra.extend([
  802. {u"使用电量": u"{}度".format(consumeDict['elec'])},
  803. {u"电量费用": u"{}".format(elec_charge)},
  804. {u"服务费用": u"{}".format(service_charge)},
  805. ])
  806. else:
  807. extra.append({u"本单消费": u"{}".format(consumeMoney)})
  808. self._notify_user_service_over(
  809. user.managerialOpenId,
  810. extra,
  811. consumeOrder.is_finished()
  812. )
  813. except Exception as e:
  814. logger.exception(e)
  815. Device.clear_port_control_cache(self.device.devNo, portStr)
  816. return
  817. def calc_elecFee_and_serviceFee(self, consumeMoney):
  818. elecCharge = float(self.device.otherConf.get("elecCharge", DefaultParams.DEFAULT_ELEC_CHARGE))
  819. serviceCharge = float(self.device.otherConf.get("serviceCharge", DefaultParams.DEFAULT_SERVICE_CHARGE))
  820. elec_charge = RMB(consumeMoney * (elecCharge / (elecCharge + serviceCharge)))
  821. service_charge = RMB(consumeMoney) - elec_charge
  822. return elec_charge, service_charge
  823. @property
  824. def isHaveStopEvent(self):
  825. return True
  826. def dealer_get_port_status(self):
  827. showPortNum = self._device.get("otherConf", {}).get("actualPortNum", DefaultParams.DEFAULT_PORT_NUM)
  828. showStatusDict = dict()
  829. portInfo = self._get_port_status_detail()
  830. devCache = Device.get_dev_control_cache(self.device.devNo)
  831. for port, item in portInfo.items():
  832. if int(port) > showPortNum:
  833. continue
  834. # 始终以 设备的状态为准
  835. portCache = devCache.get(port, dict())
  836. portCache.update(item)
  837. if portCache.get("status", Const.DEV_WORK_STATUS_IDLE) in (Const.DEV_WORK_STATUS_WORKING, Const.DEV_WORK_STATUS_OCCUPY):
  838. portCache["status"] = Const.DEV_WORK_STATUS_WORKING
  839. portCache["usedTime"] = portCache.get("chargeTime")
  840. # 如果是电量 + 服务费模式: 添加用户昵称, 添加电费金额 添加服务费金额
  841. try:
  842. order = ConsumeRecord.objects.get(orderNo=portCache['orderNo'])
  843. portCache['nickName'] = order.nickname
  844. portCache['startTime'] = portCache['startTime'][-14:]
  845. if order.servicedInfo.get('chargeType') == 'elec': # 电量 + 服务费模式
  846. consumeMoney = Calculater.get_consume_by_order(self.device, order.orderNo)
  847. portCache['elecFee'], portCache['serviceFee'] = self.calc_elecFee_and_serviceFee(consumeMoney)
  848. except:
  849. pass
  850. showStatusDict[port] = portCache
  851. else:
  852. showStatusDict[port] = {"status": portCache["status"]}
  853. return showStatusDict
  854. def get_current_use(self, order): # type: (ConsumeRecord) -> dict
  855. group = Group.get_group(order.groupId)
  856. item = ServiceProgress.objects.filter(
  857. device_imei=order.devNo,
  858. open_id=order.openId,
  859. attachParas__orderNo=order.orderNo,
  860. isFinished=False
  861. ).first()
  862. if not item:
  863. return dict()
  864. data = {
  865. 'startTime': time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(item.start_time)),
  866. 'order': item.consumeOrder,
  867. 'address': group.get('address', ''),
  868. 'groupName': group.get('groupName', ''),
  869. 'devType': self.device['devType'].get('name'),
  870. 'devTypeCode': self.device['devType'].get('code'),
  871. 'logicalCode': self.device['logicalCode'],
  872. 'status': Const.DEV_WORK_STATUS_WORKING,
  873. 'devNo': self.devNo,
  874. "port": item.port
  875. }
  876. data.update(DeviceType.get_services_button(self.device['devType']['id']))
  877. devCache = Device.get_dev_control_cache(self.devNo)
  878. portCache = devCache.get(str(item.port)) or dict()
  879. data.update(portCache)
  880. return data
  881. def active_deactive_port(self, port, active):
  882. if not active:
  883. devCache = Device.get_dev_control_cache(self.devNo)
  884. portCache = devCache.get(str(port))
  885. orderNo = portCache.get("orderNo") or 0
  886. pw = portCache.get("pw") or 0
  887. self._stop(port, orderNo, pw)
  888. @property
  889. def support_monthly_package(self):
  890. return True
  891. def lock_unlock_port(self, port, lock=True):
  892. """
  893. 禁用端口
  894. 主板不支持 禁用端口 只能服务器实现
  895. """
  896. otherConf = self.device.get("otherConf")
  897. lockPorts = otherConf.get("lockPorts", list())
  898. port = str(port)
  899. try:
  900. if lock:
  901. if port not in lockPorts:
  902. lockPorts.append(port)
  903. else:
  904. lockPorts.remove(port)
  905. except Exception as e:
  906. logger.error(e)
  907. otherConf.update({"lockPorts": lockPorts})
  908. try:
  909. Device.objects(devNo=self.device.devNo).update(otherConf=otherConf)
  910. Device.invalid_device_cache(self.device.devNo)
  911. except Exception as e:
  912. logger.error("update device %s lockPorts error the reason is %s " % (self.device.devNo, e))
  913. raise ServiceException({'result': 2, 'description': u'操作失败,请重新试试'})