zhongshanlvzhi.py 71 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import logging
  5. import re
  6. import time
  7. from decimal import Decimal
  8. from typing import TYPE_CHECKING
  9. from apilib.monetary import RMB, VirtualCoin
  10. from apilib.utils import is_number
  11. from apilib.utils_datetime import to_datetime
  12. from apps import serviceCache
  13. from apps.web.constant import Const, DeviceCmdCode, CONSUMETYPE
  14. from apps.web.core.adapter.base import SmartBox
  15. from apps.web.core.exceptions import ServiceException, InvalidParameter
  16. from apps.web.core.networking import MessageSender
  17. from apps.web.device.models import Device, Group
  18. from apps.web.user.models import Card, CardRechargeOrder, ConsumeRecord, MyUser
  19. logger = logging.getLogger(__name__)
  20. if TYPE_CHECKING:
  21. pass
  22. class ChargingZhongShanBox(SmartBox):
  23. def __init__(self, device):
  24. super(ChargingZhongShanBox, self).__init__(device)
  25. # 解析获取设备信息的返回报文
  26. def analyze_event_data(self, device_data):
  27. uart_data = device_data.get("data")
  28. if not uart_data:
  29. return
  30. data = str(uart_data)
  31. if data[:2] != "CC":
  32. return
  33. consumeModule = self.device["otherConf"].get("deviceConfigs", {}).get("consumeModule")
  34. if consumeModule is None:
  35. consumeModule = self.get_function_switch().get("consumeModule")
  36. self.get_consumption_rules()
  37. self._device = Device.get_dev(self.device.devNo)
  38. if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGE_ZHONGSHAN_BILLASSERVICE:
  39. billingType = "billAsService" if consumeModule == 1 else "elec"
  40. else:
  41. billingType = "time" if consumeModule == 1 else "elec"
  42. cmd = data[4:6]
  43. if cmd == "99":
  44. return {"cmd":cmd}
  45. if cmd == "04":
  46. port = self.decode_str(data[18:20])
  47. cardNo = "{:0>10}".format(self.decode_str(data[20:28]))
  48. eventCode = data[28:30]
  49. fee = self.decode_str(data[30:32], ratio=0.1, base=10)
  50. cardType = "IC" if data[32:34] == "02" else "ID"
  51. cardBalance = self.decode_str(data[34:38], ratio=0.1, base=10)
  52. return {"cmd": cmd, "cardNo": cardNo, "port": port, "eventCode": eventCode, "fee": fee,
  53. "cardType": cardType, "cardBalance": cardBalance, "billingType": billingType, "uart_data": uart_data}
  54. elif cmd == "05":
  55. reasonDict = {
  56. "01": "购买的充电时间、电量用完了",
  57. "02": "用户手动停止(拔插头,或是按了停止按钮)",
  58. "03": "您的爱车充满电了",
  59. "04": "设备或是端口出现问题,被迫停止",
  60. "05": "因充电器功率超过充电站的单路最大输出功率,切断输出",
  61. "06": "超高温(达到75度)",
  62. "07": "烟雾报警",
  63. "08": "漏电报警",
  64. "09": "负载电流过大(短路)",
  65. "0A": "开始充电未接充电器",
  66. "0B": "端口远程停止",
  67. }
  68. consumeTypeDict = {
  69. "01": "投币消费",
  70. "02": "离线卡消费",
  71. "03": "在线卡消费",
  72. "04": "扫码消费",
  73. "05": "远程开启",
  74. }
  75. port = self.decode_str(data[18:20])
  76. reasonCode = data[24:26]
  77. reason = reasonDict.get(reasonCode)
  78. backMoney = self.decode_str(data[34:38], ratio=0.01, base=10)
  79. consumeTypeCode = data[38:40]
  80. consumeType = consumeTypeDict.get(consumeTypeCode)
  81. result = {"cmd": cmd, "port": port, "reasonCode": reasonCode, "reason": reason, "backMoney": backMoney,
  82. "consumeTypeCode": consumeTypeCode,
  83. "consumeType": consumeType, "billingType": billingType, "uart_data": uart_data
  84. }
  85. if "ack_event_id" in device_data:
  86. result["ack_event_id"] = device_data["ack_event_id"]
  87. if billingType == "time":
  88. leftTime = self.decode_str(data[20:24])
  89. result["leftTime"] = leftTime
  90. elif billingType == "billAsService":
  91. serviceFee = self.decode_str(data[20:24], ratio=0.01)
  92. result["serviceFee"] = serviceFee
  93. else:
  94. leftElec = self.decode_str(data[20:24], ratio=0.01)
  95. result["leftElec"] = leftElec
  96. if consumeTypeCode == "02": # 离线卡
  97. cardNo = "{:0>10}".format(self.decode_str(data[26:34]))
  98. result["cardNo"] = cardNo
  99. elif consumeTypeCode == "03": # 扫码
  100. cardNo = "{:0>10}".format(self.decode_str(data[26:34]))
  101. result["cardNo"] = cardNo
  102. elif consumeTypeCode == "01": # 线下投币
  103. try:
  104. rechargeCode = self.decode_long_hex_to_list(data[26:34])
  105. rechargeCode = "".join(rechargeCode[:3])
  106. result["rechargeCode"] = rechargeCode
  107. except Exception:
  108. result["rechargeCode"] = "000"
  109. else:
  110. pass
  111. return result
  112. elif cmd == "09":
  113. errorDict = {
  114. "01": "端口输出故障",
  115. "02": "机器整机功率过大",
  116. "03": "电源故障",
  117. "04": "机箱超温(高于80摄氏度)",
  118. "05": "烟雾警告",
  119. "06": "漏电",
  120. "07": "端口故障",
  121. "08": "其他",
  122. }
  123. port = data[18:20] if data[18:20] != "FF" else None
  124. FaultCode = data[20:22]
  125. fault = errorDict.get(FaultCode)
  126. return {"status": Const.DEV_WORK_STATUS_FAULT, "statusInfo": fault, "cmdCode": cmd,
  127. "port": port, "FaultCode": FaultCode, "billingType": billingType, "uart_data": uart_data}
  128. elif cmd == "1D":
  129. cardNo = "{:0>10}".format(self.decode_str(data[18:26]))
  130. cardType = "IC" if data[26:28] == "02" else "ID"
  131. cardBalance = self.decode_str(data[28:32], ratio=0.1, base=10)
  132. return {"cmd": cmd, "cardNo": cardNo, "cardType": cardType, "cardBalance": cardBalance,
  133. "billingType": billingType, "uart_data": uart_data}
  134. elif cmd == "1F":
  135. cardNo = "{:0>10}".format(self.decode_str(data[18:26]))
  136. cardType = "IC" if data[26:28] == "02" else "ID"
  137. ayscMoney = self.decode_str(data[28:32], ratio=0.1, base=10)
  138. cardBalance = self.decode_str(data[32:36], ratio=0.1, base=10)
  139. status = data[36:38]
  140. return {"cmd": cmd, "cardNo": cardNo, "cardType": cardType, "ayscMoney": ayscMoney,
  141. "cardBalance": cardBalance, "status": status, "billingType": billingType, "uart_data": uart_data}
  142. elif cmd == "03":
  143. port = self.decode_str(data[18:20])
  144. coins = self.decode_str(data[20:22], ratio=0.1, base=10)
  145. return {"cmd": cmd, "port": port, "coins": coins, "billingType": billingType, "uart_data": uart_data}
  146. elif cmd == "F1": # 主板心跳
  147. data = self.decode_str(data[18:20])
  148. return {"cmd": cmd, "billingType": billingType, "data": data, "uart_data": uart_data}
  149. else:
  150. pass
  151. def _check_package(self, package):
  152. """
  153. 获取设备启动的发送数据 根据设备的当前模式以及套餐获取
  154. :param package:
  155. :return:
  156. """
  157. consumeModule = self.device.get("otherConf", dict()).get("deviceConfigs", dict()).get("consumeModule",None)
  158. if consumeModule is None:
  159. consumeModule = int(self.get_function_switch().get("consumeModule"))
  160. self.get_consumption_rules()
  161. self._device = Device.get_dev(self.device.devNo)
  162. unit = package.get("unit", u"分钟")
  163. _time = float(package.get("time", 0))
  164. if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGE_ZHONGSHAN_BILLASSERVICE:
  165. if consumeModule == 1:
  166. billingType = "billAsService"
  167. else:
  168. billingType = "elec"
  169. if unit != u"度":
  170. raise ServiceException({"result": 2, "description": u"套餐单位错误,请联系经销商"})
  171. else:
  172. _time = _time
  173. else:
  174. # 按时间计费
  175. if consumeModule == 1:
  176. billingType = "time"
  177. if unit == u"小时":
  178. _time = _time * 60
  179. elif unit == u"天":
  180. _time = _time * 24 * 60
  181. elif unit == u"秒":
  182. _time = _time / 60
  183. elif unit == u"分钟":
  184. _time = _time
  185. else:
  186. raise ServiceException({"result": 2, "description": u"套餐单位错误,请联系经销商"})
  187. # 按电量计费
  188. else:
  189. billingType = "elec"
  190. if unit != u"度":
  191. raise ServiceException({"result": 2, "description": u"套餐单位错误,请联系经销商"})
  192. else:
  193. _time = _time
  194. return _time, unit, billingType
  195. def disable_app_device(self, switch=True):
  196. # type:(bool) -> None
  197. otherConf = self.device.get("otherConf", {})
  198. otherConf["disableDevice"] = switch
  199. Device.objects.filter(devNo=self.device["devNo"]).update(otherConf=otherConf)
  200. Device.invalid_device_cache(self.device["devNo"])
  201. @staticmethod
  202. def encode_str(data, length=2, ratio=1.0, base=16):
  203. # type:(str,int,float,int) -> str
  204. if not isinstance(data, Decimal):
  205. data = Decimal(data)
  206. if not isinstance(length, str):
  207. length = str(length)
  208. if not isinstance(ratio, Decimal):
  209. ratio = Decimal(ratio)
  210. end = "X" if base == 16 else "d"
  211. encodeStr = "%." + length + end
  212. encodeStr = encodeStr % (data * ratio)
  213. return encodeStr
  214. @staticmethod
  215. def decode_str(data, ratio=1.0, base=16):
  216. # type:(str,float,int) -> str
  217. """
  218. ratio:比率单位转换
  219. """
  220. if not isinstance(data, str):
  221. data = str(data)
  222. return "%.10g" % (int(data, base) * ratio)
  223. @staticmethod
  224. def reverse_hex(data):
  225. # type:(str) -> str
  226. if not isinstance(data, str):
  227. raise TypeError
  228. return "".join(list(reversed(re.findall(r".{2}", data))))
  229. def decode_long_hex_to_list(self, data, split=2, ratio=1.0, base=16):
  230. # type:(str,int,float,int) -> list
  231. """
  232. return: list
  233. """
  234. if len(data) % split != 0:
  235. raise Exception("Invalid data")
  236. pattern = r".{%s}" % split
  237. hex_list = re.findall(pattern, data)
  238. hex_list = map(lambda x: self.decode_str(x, ratio=ratio, base=base), hex_list)
  239. return hex_list
  240. @staticmethod
  241. def check_params_range(params, minData=None, maxData=None, desc=""):
  242. # type:(str,float,float,str) -> str
  243. """
  244. 检查参数,返回字符串参数
  245. """
  246. if params is None:
  247. raise ServiceException({"result": 2, "description": u"参数错误."})
  248. if not isinstance(params, Decimal):
  249. params = Decimal(params)
  250. if not minData and maxData:
  251. if not isinstance(maxData, Decimal):
  252. maxData = Decimal(maxData)
  253. if params <= maxData:
  254. return "%g" % params
  255. else:
  256. raise ServiceException({"result": 2, "description": u"%s超出可选范围,可选最大值为%g" % (desc, maxData)})
  257. if not maxData and minData:
  258. if not isinstance(minData, Decimal):
  259. minData = Decimal(minData)
  260. if minData <= params:
  261. return "%g" % params
  262. else:
  263. raise ServiceException({"result": 2, "description": u"%s超出可选范围,可选最小值为%g" % (desc, minData)})
  264. if not minData and not maxData:
  265. return "%g" % params
  266. else:
  267. if not isinstance(minData, Decimal):
  268. minData = Decimal(minData)
  269. if not isinstance(maxData, Decimal):
  270. maxData = Decimal(maxData)
  271. if minData <= params <= maxData:
  272. return "%g" % params
  273. else:
  274. raise ServiceException(
  275. {"result": 2, "description": u"%s参数超出可选范围,可取范围为%g-%g" % (desc, minData, maxData)})
  276. def send_mqtt(self, funCode, data, cmd=DeviceCmdCode.OPERATE_DEV_SYNC):
  277. """
  278. 发送mqtt 指令210 返回data
  279. """
  280. if not isinstance(funCode, str):
  281. funCode = str(funCode)
  282. if not isinstance(data, str):
  283. data = str(data)
  284. result = MessageSender.send(self.device, cmd,
  285. {"IMEI": self.device["devNo"], "funCode": funCode, "data": data})
  286. if "rst" in result and result["rst"] != 0:
  287. if result["rst"] == -1:
  288. raise ServiceException(
  289. {"result": 2, "description": u"该设备正在玩命找网络,请您稍候再试", "rst": -1})
  290. elif result["rst"] == 1:
  291. raise ServiceException(
  292. {"result": 2, "description": u"该设备忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能", "rst": 1})
  293. else:
  294. if cmd in [DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE]:
  295. return
  296. if result.get("data") == "00":
  297. raise ServiceException({"result": 2, "description": u"设备操作失败.请重试"})
  298. else:
  299. return str(result["data"][18:-2])
  300. @staticmethod
  301. def port_is_busy(port_dict):
  302. if not port_dict:
  303. return False
  304. if "billingType" not in port_dict:
  305. return False
  306. if "status" not in port_dict:
  307. return False
  308. if "coins" not in port_dict:
  309. return False
  310. if port_dict["billingType"] not in ["time", "elec"]:
  311. return False
  312. if port_dict["billingType"] == "time":
  313. if "needTime" not in port_dict:
  314. return False
  315. else:
  316. if "needElec" not in port_dict:
  317. return False
  318. if port_dict["status"] == Const.DEV_WORK_STATUS_WORKING:
  319. return True
  320. else:
  321. return False
  322. def ack_05_event(self, ack_event_id):
  323. try:
  324. MessageSender.send(device=self.device, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, payload={
  325. "IMEI": self.device.devNo,
  326. "data": "00",
  327. "cmd": DeviceCmdCode.OPERATE_DEV_SYNC,
  328. "ack_event_id": ack_event_id,
  329. "funCode": "CA"})
  330. except Exception:
  331. pass
  332. def send_F1_heartbeat(self):
  333. self.send_mqtt(funCode="F1", data="00")
  334. def do_update_configs(self, updateDict):
  335. dev = Device.objects.get(devNo=self.device.devNo)
  336. deviceConfigs = dev.otherConf.get("deviceConfigs", {})
  337. deviceConfigs.update(updateDict)
  338. dev.otherConf['deviceConfigs'] = deviceConfigs
  339. dev.save()
  340. Device.invalid_device_cache(self.device.devNo)
  341. def start_device(self, package, openId, attachParas):
  342. # type: (...)-> dict
  343. if attachParas is None:
  344. raise ServiceException({"result": 2, "description": u"请您选择合适的充电线路、电池类型信息"})
  345. if "chargeIndex" not in attachParas:
  346. raise ServiceException({"result": 2, "description": u"请您选择合适的充电线路"})
  347. dev = Device.objects.get(devNo=self.device["devNo"])
  348. refundProtection = dev.otherConf.get("refundProtection", 0)
  349. refundProtectionTime = dev.otherConf.get("refundProtectionTime", 5)
  350. port = attachParas["chargeIndex"]
  351. portHex = self.encode_str(port, 2)
  352. _time, unit, billingType = self._check_package(package)
  353. coins = float(package.get("coins", 0))
  354. coinsHex = self.encode_str(coins, length=4, ratio=10, base=10)
  355. unitHex = self.encode_str(_time, length=4)
  356. unit = package["unit"]
  357. price = float(package['price'])
  358. if unit == "度":
  359. unitHex = self.encode_str(_time, length=4, ratio=100)
  360. orderNo = attachParas.get("orderNo")
  361. data = portHex + coinsHex + unitHex
  362. logger.info("lz_start_device devNo:{}, cmd:220 funcCode:02 uart_send:{}".format(self.device["devNo"], data))
  363. devInfo = MessageSender.send(
  364. device=self.device,
  365. cmd=DeviceCmdCode.OPERATE_DEV_SYNC,
  366. payload={
  367. "IMEI": self.device["devNo"],
  368. "funCode": "02",
  369. "data": data
  370. },
  371. timeout=120
  372. )
  373. if "rst" in devInfo and devInfo["rst"] != 0:
  374. if devInfo["rst"] == -1:
  375. raise ServiceException(
  376. {"result": 2, "description": u"充电桩正在玩命找网络,您的金币还在,重试不需要重新付款,建议您试试旁边其他设备,或者稍后再试哦"})
  377. elif devInfo["rst"] == 1:
  378. self.check_serial_port_for_startcmd(attachParas["chargeIndex"])
  379. # 收到回应报文后,需要ack响应报文。
  380. logger.info("lz_start_device devNo:{}, funcCode:02 uart_rece:{}".format(self.device["devNo"], devInfo))
  381. data = devInfo["data"][18::]
  382. usePort = int(attachParas["chargeIndex"])
  383. result = data[2:4]
  384. if result == "01": # 成功
  385. pass
  386. elif result == "02":
  387. newValue = {str(usePort): {"status": Const.DEV_WORK_STATUS_FAULT, "statusInfo": u"充电站故障"}}
  388. Device.update_dev_control_cache(self.device["devNo"], newValue)
  389. raise ServiceException({"result": 2, "description": u"充电站故障"})
  390. elif result == "03":
  391. newValue = {str(usePort): {"status": Const.DEV_WORK_STATUS_WORKING, "statusInfo": u""}}
  392. Device.update_dev_control_cache(self.device["devNo"], newValue)
  393. raise ServiceException({"result": 2, "description": u"该端口正在使用中"})
  394. portDict = {
  395. "startTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  396. "status": Const.DEV_WORK_STATUS_WORKING,
  397. "billingType": billingType,
  398. "isStart": True,
  399. "openId": openId,
  400. "refunded": False,
  401. "refundProtection": refundProtection,
  402. "refundProtectionTime": refundProtectionTime,
  403. "doPowerLevel": False,
  404. "consumeType": "mobile",
  405. "orderNo": orderNo,
  406. "port": usePort
  407. }
  408. servicedInfo = {"chargeIndex":usePort}
  409. ConsumeRecord.objects.filter(orderNo = orderNo).update(servicedInfo=servicedInfo,remarks='扫码启动')
  410. ctrInfo = Device.get_dev_control_cache(self.device["devNo"])
  411. lastPortInfo = ctrInfo.get(str(usePort), {})
  412. if (lastPortInfo is not None) and \
  413. lastPortInfo.get('status', '') == Const.DEV_WORK_STATUS_WORKING and \
  414. lastPortInfo.get('openId') == openId and \
  415. lastPortInfo.get('billingType') == billingType:
  416. is_continus = True
  417. else:
  418. is_continus = False
  419. if 'linkedRechargeRecordId' in attachParas:
  420. portDict['rechargeRcdId'] = str(attachParas['linkedRechargeRecordId'])
  421. if billingType == "time":
  422. if is_continus:
  423. portDict["coins"] = float(coins) + lastPortInfo["coins"]
  424. portDict["price"] = float(price) + lastPortInfo["price"]
  425. portDict["needTime"] = _time + lastPortInfo["needTime"]
  426. else:
  427. portDict["coins"] = float(coins)
  428. portDict["price"] = float(price)
  429. portDict.update({"needTime": _time})
  430. finishedTime = int(time.time()) + int(portDict["needTime"] * 60)
  431. elif billingType == "elec":
  432. if is_continus:
  433. portDict["coins"] = float(coins) + lastPortInfo["coins"]
  434. portDict["needElec"] = _time + lastPortInfo["needElec"]
  435. else:
  436. portDict["coins"] = float(coins)
  437. portDict.update({"needElec": _time})
  438. finishedTime = int(time.time()) + 60 * 60 * 12
  439. else:
  440. if is_continus:
  441. portDict["coins"] = float(coins) + lastPortInfo["coins"]
  442. else:
  443. portDict["coins"] = float(coins)
  444. finishedTime = int(time.time()) + 60 * 60 * 12
  445. portDict.update({"finishedTime": finishedTime})
  446. if "orderNo" in attachParas:
  447. portDict.update({"orderNo": attachParas["orderNo"]})
  448. Device.update_port_control_cache(self.device["devNo"], portDict)
  449. devInfo["finishedTime"] = finishedTime
  450. return devInfo
  451. def get_device_ID(self):
  452. # type:() -> dict
  453. """
  454. 获取设备唯一ID
  455. funcCode : 55
  456. """
  457. result = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  458. {"IMEI": self.device["devNo"], "funCode": "55", "data": "00"})
  459. if "rst" in result and result["rst"] != 0:
  460. if result["rst"] == -1:
  461. raise ServiceException(
  462. {"result": 2, "description": u"该设备正在玩命找网络,请您稍候再试", "rst": -1})
  463. elif result["rst"] == 1:
  464. raise ServiceException(
  465. {"result": 2, "description": u"该设备忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能", "rst": 1})
  466. else:
  467. if result.get("data") == "00":
  468. raise ServiceException({"result": 2, "description": u"设备操作失败.请重试"})
  469. else:
  470. data = result.get("data")
  471. data_list = self.decode_long_hex_to_list(data[6:-2], base=10)
  472. return {"deviceId": "".join(data_list)}
  473. def get_default_port_nums(self):
  474. allPorts = self.device["otherConf"].get("deviceConfigs", {}).get("allPorts")
  475. if not allPorts:
  476. data = self.send_mqtt(funCode="01", data="00")
  477. allPorts = int(data[:2], 16)
  478. self.do_update_configs({"allPorts": allPorts})
  479. return allPorts
  480. def get_mcu_version(self):
  481. devInfo = MessageSender.send(self.device, DeviceCmdCode.OPERATE_DEV_SYNC,
  482. {"IMEI": self.device["devNo"], "funCode": "25", "data": "00"})
  483. if "rst" in devInfo and devInfo["rst"] != 0:
  484. if devInfo["rst"] == -1:
  485. raise ServiceException(
  486. {"result": 2, "description": u"充电桩正在玩命找网络,请您稍候再试"})
  487. elif devInfo["rst"] == 1:
  488. raise ServiceException(
  489. {"result": 2, "description": u"充电桩忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能"})
  490. confData = devInfo["data"][18::]
  491. mcuVersion = int(confData[0:4], 16)
  492. return {"mcuVersion": mcuVersion}
  493. def get_port_status(self, force=False):
  494. if force:
  495. return self.get_all_port_status()
  496. ctrInfo = Device.get_dev_control_cache(self.device.devNo)
  497. if "allPorts" in ctrInfo and ctrInfo["allPorts"] > 0:
  498. allPorts = ctrInfo["allPorts"]
  499. else:
  500. allPorts = self.get_default_port_nums()
  501. statusDict = {}
  502. for ii in range(allPorts):
  503. tempDict = ctrInfo.get(str(ii + 1), {})
  504. if "status" in tempDict:
  505. statusDict[str(ii + 1)] = {"status": tempDict.get("status")}
  506. elif "isStart" in tempDict:
  507. if tempDict["isStart"]:
  508. statusDict[str(ii + 1)] = {"status": Const.DEV_WORK_STATUS_WORKING}
  509. else:
  510. statusDict[str(ii + 1)] = {"status": Const.DEV_WORK_STATUS_IDLE}
  511. else:
  512. statusDict[str(ii + 1)] = {"status": Const.DEV_WORK_STATUS_IDLE}
  513. return statusDict
  514. def get_port_info(self, port):
  515. # type:(str) -> dict
  516. """
  517. 获取单个端口状态
  518. funcCode : 06
  519. 发送:CC09060009090909090107
  520. 返回:AA0D06000909090909 01 0054 0364 30
  521. """
  522. data = self.encode_str(port, 2)
  523. data = self.send_mqtt(funCode="06", data=data)
  524. if self.device.bill_as_service_feature.on:
  525. billingType = "billAsService" if self.device["otherConf"].get("deviceConfigs", dict()).get("consumeModule",
  526. 0) == 1 else "elec"
  527. else:
  528. billingType = "time" if self.device["otherConf"].get("deviceConfigs", dict()).get("consumeModule",
  529. 0) == 1 else "elec"
  530. port = self.decode_str(data[:2])
  531. if billingType == "time":
  532. leftTime = int(self.decode_str(data[2:6]))
  533. else:
  534. if self.device.bill_as_service_feature.on:
  535. if billingType == 'billAsService':
  536. serviceFee = float(self.decode_str(data[2:6], ratio=0.01))
  537. else:
  538. leftElec = float(self.decode_str(data[2:6], ratio=0.01))
  539. leftTime = int(self.decode_str(data[10:14]))
  540. else:
  541. leftElec = float(self.decode_str(data[2:6], ratio=0.01))
  542. leftTime = int(self.decode_str(data[10:14]))
  543. power = self.decode_str(data[6:10], ratio=0.1, base=10)
  544. leftBalance = self.decode_str(data[14:18], ratio=0.01, base=10)
  545. result = locals()
  546. result.pop("self")
  547. return result
  548. def get_all_port_status(self):
  549. # type:() -> dict
  550. """
  551. 获取所有端口的状态 空闲,使用,禁用,故障
  552. funcCode : 01
  553. 发送:CC09010009090909090001
  554. 返回:AA1301000909090909 0A 1000 00 00 00 00 00 00 00 00 01
  555. """
  556. data = self.send_mqtt(funCode="01", data="00")
  557. result = {}
  558. allPorts = int(data[:2], 16)
  559. usedPorts = 0
  560. portData = re.findall(r"..", data[2:])
  561. for i, statusTemp in enumerate(portData[:allPorts]):
  562. if statusTemp == "00":
  563. status = {"status": Const.DEV_WORK_STATUS_IDLE}
  564. elif statusTemp == "01":
  565. status = {"status": Const.DEV_WORK_STATUS_WORKING}
  566. usedPorts += 1
  567. elif statusTemp == "02":
  568. status = {"status": Const.DEV_WORK_STATUS_FORBIDDEN}
  569. usedPorts += 1
  570. elif statusTemp == "03":
  571. status = {"status": Const.DEV_WORK_STATUS_FAULT}
  572. usedPorts += 1
  573. else:
  574. status = {"status": Const.DEV_WORK_STATUS_FAULT}
  575. usedPorts += 1
  576. result[str(i + 1)] = status
  577. usePorts = allPorts - usedPorts
  578. # 这里存在多线程更新缓存的场景,可能性比较低,但是必须先取出来,然后逐个更新状态,然后再记录缓存
  579. ctrInfo = Device.get_dev_control_cache(self.device["devNo"])
  580. for strPort, info in result.items():
  581. if strPort in ctrInfo:
  582. ctrInfo[strPort].update({"status": info["status"]})
  583. else:
  584. ctrInfo[strPort] = info
  585. ctrInfo.update({
  586. "allPorts": allPorts, "usedPorts": usedPorts, "usePorts": usePorts
  587. })
  588. Device.update_dev_control_cache(self.device.devNo, ctrInfo)
  589. # 01指令读不到非法充电的充电状态,所以需要从缓存中查找是否有非法充电的端口
  590. cacheInfo = Device.get_dev_control_cache(self.device.devNo)
  591. for k,v in cacheInfo.items():
  592. if type(k) == unicode:
  593. k = k.encode('gbk')
  594. if str.isdigit(k):
  595. if v.get('statusErrorInfo') == '非法充电':
  596. if result[k]["status"] == Const.DEV_WORK_STATUS_IDLE:
  597. result[k] = {"status": Const.DEV_WORK_STATUS_WORKING,"errorInfo":True}
  598. return result
  599. def get_all_port_info(self):
  600. # type:() -> dict
  601. """
  602. 获取全部端口的信息
  603. funcCode : 07
  604. 发送:CC09070009090909090007
  605. 返回:AA330700000000000100081D00000000000000000000000000000000000519840011006000000000000000000000000000000000C9
  606. """
  607. if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGE_ZHONGSHAN_BILLASSERVICE:
  608. billingType = "billAsservice" if self.device["otherConf"].get("deviceConfigs", dict()).get("consumeModule",
  609. 0) == 1 else "elec"
  610. else:
  611. billingType = "time" if self.device["otherConf"].get("deviceConfigs", dict()).get("consumeModule",
  612. 0) == 1 else "elec"
  613. data = self.send_mqtt(funCode="07", data="00")
  614. totalAmpere = self.decode_str(data[:4], ratio=0.1, base=10)
  615. temperature = self.decode_str(data[4:6])
  616. data = data[6:]
  617. lis = []
  618. for i in xrange(0, 80, 16):
  619. lis.append(data[i:i + 16])
  620. allPorts = self.get_default_port_nums()
  621. if allPorts > 5:
  622. data = self.send_mqtt(funCode="30", data="00")
  623. for i in xrange(0, 80, 16):
  624. lis.append(data[i:i + 16])
  625. result = {}
  626. if billingType == "time":
  627. for index, item in enumerate(lis):
  628. result[str(index + 1)] = {
  629. "leftTime": int(self.decode_str(item[:4])),
  630. "power": self.decode_str(item[4:8], ratio=0.1, base=10),
  631. "leftBalance": self.decode_str(item[12:], ratio=0.01, base=10)
  632. }
  633. elif billingType == "elec":
  634. for index, item in enumerate(lis):
  635. result[str(index + 1)] = {
  636. "leftElec": float(self.decode_str(item[:4], ratio=0.01)),
  637. "power": self.decode_str(item[4:8], ratio=0.1, base=10),
  638. "leftTime": int(self.decode_str(item[8:12])),
  639. "leftBalance": self.decode_str(item[12:], ratio=0.01, base=10)
  640. }
  641. elif billingType == "billAsservice":
  642. # 新增电费+服务费模式
  643. for index, item in enumerate(lis):
  644. result[str(index + 1)] = {
  645. "serviceFee": float(self.decode_str(item[:4],ratio=0.01)),
  646. "power": self.decode_str(item[4:8], ratio=0.1, base=10),
  647. "leftTime": int(self.decode_str(item[8:12])),
  648. "leftBalance": self.decode_str(item[12:], ratio=0.01, base=10)
  649. }
  650. result.update({"totalAmpere": totalAmpere, "temperature": temperature, "billingType": billingType})
  651. return result
  652. def get_port_status_from_dev(self):
  653. all_port_status = self.get_all_port_status()
  654. # 做一个兼容,不是所有主板都支持06指令,支持06指令的主板优先是由06指令查询到的数据
  655. try:
  656. all_port_detail = dict()
  657. for port, status in all_port_status.items():
  658. if status['status'] != 0:
  659. port_detail = self.get_port_info(port)
  660. if float(port_detail['power']) <= 5 and status['errorInfo']:
  661. Device.clear_port_control_cache(self.device.devNo, port)
  662. all_port_detail.update({port: port_detail})
  663. except:
  664. all_port_detail = self.get_all_port_info()
  665. cacheInfo = Device.get_dev_control_cache(self.device.devNo)
  666. parse_status_code = {
  667. "0": u"端口空闲",
  668. "1": u"端口正在使用",
  669. "2": u"端口禁用",
  670. "3": u"端口故障",
  671. }
  672. lis = []
  673. for port, info in all_port_status.items():
  674. lineInfo = cacheInfo.get(port, {})
  675. if info["status"] == 0:
  676. item = {"index": port, "status": "idle", "desc": parse_status_code.get(str(info["status"]))}
  677. elif info["status"] == 1:
  678. if not lineInfo.get("startTime"):
  679. # 端口启动 没有任何订单消息 最直接的原因就经销商直接换了模块 服务器没有任何信息
  680. item = {"index": port,
  681. "status": "busy",
  682. "desc": parse_status_code.get(str(info["status"])),
  683. "leftElec": all_port_detail.get(port, {}).get("leftElec"),
  684. "leftMoney": all_port_detail.get(port, {}).get("leftBalance"),
  685. "leftTime": all_port_detail.get(port, {}).get("leftTime"),
  686. "power": all_port_detail.get(port, {}).get("power"),
  687. }
  688. else:
  689. nowTime = datetime.datetime.now()
  690. startTime = to_datetime(lineInfo["startTime"])
  691. usedTime = int(round((((nowTime - startTime).total_seconds() + 59) / 60.0)))
  692. if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGE_ZHONGSHAN_BILLASSERVICE:
  693. billingType = lineInfo.get("billingType")
  694. if billingType == "elec":
  695. item = {
  696. "startTime": lineInfo.get("startTime", '')[2:],
  697. "index": port,
  698. "status": "busy",
  699. "usedTime": usedTime,
  700. "power": all_port_detail.get(port, {}).get("power"),
  701. "desc": parse_status_code.get(str(info["status"])),
  702. "leftMoney": all_port_detail.get(port, {}).get("leftBalance"),
  703. "consumeMoney": round(float(lineInfo.get("coins")) - float(all_port_detail.get(port, {}).get("leftBalance")),2),
  704. "coins": str(lineInfo.get("coins")) + "币",
  705. "consumeType": lineInfo.get("consumeType"),
  706. "leftElec":all_port_detail.get(port, {}).get("leftElec"),
  707. "needElec":lineInfo['needElec'],
  708. "usedElec":round(float(lineInfo['needElec']) - float(all_port_detail.get(port, {}).get("leftElec")), 2),
  709. "leftTime":all_port_detail.get(port, {}).get("leftTime")
  710. }
  711. # 记录非法充电的功率,非法充电时功率只能通过06指令查到
  712. elif billingType == "faultCharge":
  713. # data = self.encode_str(port, 2)
  714. # res = self.send_mqtt(funCode='06',data=data)
  715. item = {
  716. "startTime": lineInfo.get("startTime", '')[2:],
  717. "index": port,
  718. "status": "busy",
  719. "usedTime": usedTime,
  720. "power": all_port_detail.get(port, {}).get("power"),
  721. "statusErrorInfo": lineInfo.get('statusErrorInfo')
  722. }
  723. else:
  724. item = {
  725. "startTime": lineInfo.get("startTime", '')[2:],
  726. "index": port,
  727. "status": "busy",
  728. "usedTime": usedTime,
  729. "power": all_port_detail.get(port, {}).get("power"),
  730. "desc": parse_status_code.get(str(info["status"])),
  731. "leftMoney": all_port_detail.get(port, {}).get("leftBalance"),
  732. "consumeMoney": round(
  733. float(lineInfo.get("coins")) - float(
  734. all_port_detail.get(port, {}).get("leftBalance")),
  735. 2),
  736. "coins": str(lineInfo.get("coins")) + "币",
  737. "consumeType": lineInfo.get("consumeType"),
  738. "serviceFee" : all_port_detail.get(port, {}).get("serviceFee"),
  739. "elecFee" : round(float(lineInfo.get("coins")) - float(all_port_detail.get(port, {}).get("leftBalance")) - float(all_port_detail.get(port, {}).get("serviceFee")),2),
  740. }
  741. else:
  742. billingType = lineInfo.get("billingType")
  743. if billingType == "faultCharge":
  744. item = {
  745. "startTime": lineInfo.get("startTime", '')[2:],
  746. "index": port,
  747. "status": "busy",
  748. "usedTime": usedTime,
  749. "power": all_port_detail.get(port, {}).get("power"),
  750. "statusErrorInfo": lineInfo.get('statusErrorInfo')
  751. }
  752. else:
  753. item = {
  754. "startTime": lineInfo.get("startTime", '')[2:],
  755. "index": port,
  756. "status": "busy",
  757. "leftTime": all_port_detail.get(port, {}).get("leftTime"),
  758. "usedTime": usedTime,
  759. "actualNeedTime": usedTime + int(all_port_detail.get(port, {}).get("leftTime")),
  760. "leftElec": all_port_detail.get(port, {}).get("leftElec"),
  761. "power": all_port_detail.get(port, {}).get("power"),
  762. "desc": parse_status_code.get(str(info["status"])),
  763. "leftMoney": all_port_detail.get(port, {}).get("leftBalance"),
  764. "consumeMoney": round(float(lineInfo.get("coins")) - float(all_port_detail.get(port, {}).get("leftBalance")),2),
  765. "coins": str(lineInfo.get("coins")) + "币",
  766. "consumeType": lineInfo.get("consumeType"),
  767. }
  768. if 'needElec' in lineInfo:
  769. item['needElec'] = lineInfo['needElec']
  770. item['usedElec'] = round(float(lineInfo['needElec']) - float(item['leftElec']), 2)
  771. if lineInfo.get("cardNo") and lineInfo.get("cardNo") != "0000000000":
  772. item.update({"cardNo": lineInfo.get("cardNo")})
  773. if lineInfo.get("openId"):
  774. user = MyUser.objects.filter(openId=lineInfo.get("openId"), groupId=self.device["groupId"]).first()
  775. if user:
  776. item.update({"nickName": user.nickname})
  777. if 'rechargeRcdId' in lineInfo:
  778. item['coins'] = str(lineInfo.get("coins")) + "元"
  779. if 'needTime' in lineInfo:
  780. item['needTime'] = lineInfo['needTime']
  781. elif info["status"] == 2:
  782. item = {"index": port,
  783. "status": "ban",
  784. "desc": parse_status_code.get(str(info["status"]))}
  785. elif info["status"] == 3:
  786. item = {"index": port,
  787. "status": "fault",
  788. "desc": parse_status_code.get(str(info["status"]))}
  789. else:
  790. pass
  791. lis.append(item)
  792. lis = sorted(lis, key=lambda x: int(x["index"]))
  793. return lis
  794. @property
  795. def isHaveStopEvent(self):
  796. return True
  797. def test(self, port="1", coins="1", time="100", elec="0"):
  798. # type:(...) -> dict
  799. """
  800. 启动某个端口
  801. funcCode : 02
  802. """
  803. portHex = self.encode_str(port, 2)
  804. coinsHex = self.encode_str(coins, 4, ratio=10)
  805. if elec:
  806. unitHex = self.encode_str(int(elec) * 100, 4)
  807. else:
  808. unitHex = self.encode_str(int(time), 4)
  809. data = portHex + coinsHex + unitHex
  810. result = self.send_mqtt(funCode="02", data=data)
  811. if result:
  812. port = result[:2]
  813. status = "Charging success" if result[2:4] == "01" else "Charging failed"
  814. return {"port": port, "status": status}
  815. def remote_device(self, port, mode="start"):
  816. # type:(str,str) -> dict
  817. """
  818. 远程操作某个端口
  819. funCode: 08
  820. 发送:CC0A08000909090909020009
  821. 返回:AA0B0800090909090902021913"
  822. """
  823. port = self.encode_str(port)
  824. if mode == "start":
  825. data = port + "01"
  826. elif mode == "stop":
  827. data = port + "00"
  828. else:
  829. return {}
  830. data = self.send_mqtt(funCode="08", data=data)
  831. # TODO parse dataHex
  832. port = self.decode_str(data[:2])
  833. result = self.decode_str(data[4:], base=16)
  834. if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGE_ZHONGSHAN_BILLASSERVICE:
  835. billingType = self.device["otherConf"].get("deviceConfigs", dict()).get("consumeModule", "elec")
  836. if billingType == "serviceFee":
  837. return {"port": port, "serviceFee": result}
  838. else:
  839. return {"port": port, "leftElec": result}
  840. else:
  841. return {"port": port, "leftTime": result}
  842. def set_consumption_rules(self, configDict):
  843. # type:(dict) -> None
  844. """
  845. 设置单次刷卡,投币,远程 用户获取量(时间:分钟 ,电量: 度)
  846. funCode: 0A
  847. """
  848. if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGE_ZHONGSHAN_BILLASSERVICE:
  849. remoteElec = self.check_params_range(params=configDict.get("remoteElec"), minData=0, maxData=5.00,
  850. desc="扫码计费标准")
  851. serviceCharge = self.check_params_range(params=configDict.get("serviceFee"), minData=0, maxData=5.00,
  852. desc="服务费计费标准")
  853. time = self.check_params_range(params=configDict.get("time"), minData=0, maxData=600, desc="消费1元充电时间上限")
  854. coinElec = self.check_params_range(params=configDict.get("coinElec"), minData=0, maxData=5.00,
  855. desc="投币计费标准")
  856. cardElec = self.check_params_range(params=configDict.get("cardElec"), minData=0, maxData=5.00,
  857. desc="刷卡计费标准")
  858. data = self.encode_str(time, length=4, ratio=1)
  859. data += self.encode_str(coinElec, length=4, ratio=100)
  860. data += self.encode_str(cardElec, length=4, ratio=100)
  861. data += self.encode_str(remoteElec, length=4, ratio=100)
  862. data += self.encode_str(serviceCharge, length=4, ratio=100)
  863. self.send_mqtt(funCode="0A", data=data)
  864. self.device.update_device_obj(**{
  865. 'devType.features.billAsService.serviceCharge': serviceCharge,
  866. 'devType.features.billAsService.elecCharge': remoteElec,
  867. })
  868. else:
  869. coinTime = self.check_params_range(params=configDict.get("coinTime"), minData=0, maxData=600, desc="投币一个的充电时间")
  870. coinElec = self.check_params_range(params=configDict.get("coinElec"), minData=0, maxData=5.00, desc="投币一个的充电电量")
  871. cardTime = self.check_params_range(params=configDict.get("cardTime"), minData=0, maxData=600, desc="刷卡一次的充电时间")
  872. cardElec = self.check_params_range(params=configDict.get("cardElec"), minData=0, maxData=5.00, desc="刷卡一次的充电电量")
  873. remoteTime = self.check_params_range(params=configDict.get("remoteTime"), minData=0, maxData=600,
  874. desc="扫码一元的充电时间")
  875. remoteElec = self.check_params_range(params=configDict.get("remoteElec"), minData=0, maxData=5.00,
  876. desc="扫码一元的充电电量")
  877. data = self.encode_str(coinTime, length=4, ratio=1)
  878. data += self.encode_str(coinElec, length=4, ratio=100)
  879. data += self.encode_str(cardTime, length=4, ratio=1)
  880. data += self.encode_str(cardElec, length=4, ratio=100)
  881. data += self.encode_str(remoteTime, length=4, ratio=1)
  882. data += self.encode_str(remoteElec, length=4, ratio=100)
  883. self.send_mqtt(funCode="0A", data=data)
  884. def get_consumption_rules(self):
  885. # type:() -> dict
  886. """
  887. 获取单次刷卡,投币,远程 用户获取量(时间:分钟 ,电量: 度)
  888. funCode: 0B
  889. """
  890. data = self.send_mqtt(funCode="0B", data="00")
  891. if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGE_ZHONGSHAN_BILLASSERVICE:
  892. time = self.decode_str(data[:4], ratio=1)
  893. coinElec = self.decode_str(data[4:8], ratio=0.01)
  894. cardElec = self.decode_str(data[8:12], ratio=0.01)
  895. remoteElec = self.decode_str(data[12:16], ratio=0.01)
  896. serviceCharge = self.decode_str(data[16:20], ratio=0.01)
  897. result = {"time": time, "coinElec": coinElec, "cardElec": cardElec, "remoteElec": remoteElec,
  898. "serviceFee": serviceCharge}
  899. self.do_update_configs(result)
  900. self.device.update_device_obj(**{
  901. 'devType.features.billAsService.serviceCharge': serviceCharge,
  902. 'devType.features.billAsService.elecCharge': remoteElec,
  903. })
  904. return result
  905. coinTime = self.decode_str(data[:4], ratio=1)
  906. coinElec = self.decode_str(data[4:8], ratio=0.01)
  907. cardTime = self.decode_str(data[8:12], ratio=1)
  908. cardElec = self.decode_str(data[12:16], ratio=0.01)
  909. remoteTime = self.decode_str(data[16:20], ratio=1)
  910. remoteElec = self.decode_str(data[20:24], ratio=0.01)
  911. result = {"coinTime": coinTime, "coinElec": coinElec, "cardTime": cardTime, "cardElec": cardElec,
  912. "remoteTime": remoteTime, "remoteElec": remoteElec}
  913. self.do_update_configs(result)
  914. return result
  915. def set_function_switch(self, configDict):
  916. # type:(dict) -> None
  917. """
  918. 设置功能开关: ic卡(00关闭,01可用),刷卡退费(00关闭,01可用),充满自停(00关闭,01可用),免费充电(00关闭,01可用),消费模式(00电量,01时间)
  919. funCode: 0C
  920. """
  921. coinSwitch = configDict.get("coinSwitch")
  922. cardSwitch = configDict.get("cardSwitch")
  923. cardRefund = configDict.get("cardRefund")
  924. autoStop = configDict.get("autoStop")
  925. chargeFree = configDict.get("chargeFree")
  926. consumeModule = configDict.get("consumeModule")
  927. data = self.encode_str(coinSwitch)
  928. data += self.encode_str(cardSwitch)
  929. data += self.encode_str(cardRefund)
  930. data += self.encode_str(autoStop)
  931. data += self.encode_str(chargeFree)
  932. data += self.encode_str(consumeModule)
  933. self.send_mqtt(funCode="0C", data=data)
  934. updateConfigs = {
  935. "coinSwitch": int(coinSwitch),
  936. "cardSwitch": int(cardSwitch),
  937. "cardRefund": int(cardRefund),
  938. "autoStop": int(autoStop),
  939. "chargeFree": int(chargeFree),
  940. "consumeModule": int(consumeModule)
  941. }
  942. self.do_update_configs(updateConfigs)
  943. if self.device.devTypeCode == Const.DEVICE_TYPE_CODE_CHARGE_ZHONGSHAN_BILLASSERVICE:
  944. billAsServiceSwitch = True if consumeModule == "1" else False
  945. self.device.update_device_obj(**{
  946. 'devType.features.billAsService.on': billAsServiceSwitch,
  947. 'otherConf.deviceConfigs.consumeModule' : 1 if consumeModule == "1" else 0
  948. })
  949. def get_function_switch(self):
  950. # type:() -> dict
  951. """
  952. 设置功能开关: ic卡(00关闭,01可用),刷卡退费(00关闭,01可用),充满自停(00关闭,01可用),免费充电(00关闭,01可用),消费模式(00电量,01时间)
  953. funCode: 0D
  954. """
  955. data = self.send_mqtt(funCode="0D", data="00")
  956. result_list = self.decode_long_hex_to_list(data)
  957. # 给前台做兼容 结果的值转为数字
  958. result_list = map(lambda x: int(x), result_list)
  959. name_list = ["coinSwitch", "cardSwitch", "cardRefund", "autoStop", "chargeFree", "consumeModule"]
  960. retult = dict(zip(name_list, result_list))
  961. self.do_update_configs(retult)
  962. return retult
  963. def set_icMoney_maxPower_floatingCharge(self, configDict):
  964. # type:(dict) -> None
  965. """
  966. 设置 刷卡金额 最大功率 浮充功率 浮充时间
  967. funCode: 0E
  968. """
  969. icMoney = self.check_params_range(params=configDict.get("icMoney"), minData=1, maxData=99,
  970. desc="单次刷卡需要消耗的金额") # TODO 单位角
  971. maxPower = self.check_params_range(params=configDict.get("maxPower"), minData=1, maxData=999, desc="最大功率")
  972. floatingPower = self.check_params_range(params=configDict.get("floatingPower"), minData=1, maxData=99,
  973. desc="浮充功率")
  974. floatingTime = self.check_params_range(params=configDict.get("floatingTime"), minData=1, maxData=120,
  975. desc="浮充时间")
  976. data = self.encode_str(icMoney, length=2, base=10) # TODO 文档中是十进制待验证!
  977. data += self.encode_str(maxPower, length=4)
  978. data += self.encode_str(floatingPower, length=2)
  979. data += self.encode_str(floatingTime, length=2)
  980. result = self.send_mqtt(funCode="0E", data=data)
  981. def get_icMoney_maxPower_floatingCharge(self):
  982. # type:() -> dict
  983. """
  984. 获取 刷卡金额 最大功率 浮充功率 浮充时间
  985. funCode: 0F
  986. """
  987. data = self.send_mqtt(funCode="0F", data="00")
  988. icMoney = self.decode_str(data[:2], base=10)
  989. maxPower = self.decode_str(data[2:6])
  990. floatingPower = self.decode_str(data[6:8])
  991. floatingTime = self.decode_str(data[8:10])
  992. return {"icMoney": icMoney, "maxPower": maxPower, "floatingPower": floatingPower, "floatingTime": floatingTime}
  993. def set_noloadTime_volume(self, configDict):
  994. # type:(dict) -> None
  995. """
  996. 设置 空载时间(noloadTime) 喇叭音量(volume)
  997. funCode: 10
  998. """
  999. noloadTime = self.check_params_range(params=configDict.get("noloadTime"), minData=1, maxData=180, desc="空载检测时间")
  1000. volume = self.check_params_range(params=configDict.get("volume"), minData=0, maxData=15, desc="喇叭音量")
  1001. data = self.encode_str(noloadTime)
  1002. data += self.encode_str(volume)
  1003. result = self.send_mqtt(funCode="10", data=data)
  1004. def get_noloadTime_volume(self):
  1005. # type:() -> dict
  1006. """
  1007. 读取 空载时间(noloadTime) 喇叭音量(volume)
  1008. funCode: 11
  1009. """
  1010. data = self.send_mqtt(funCode="11", data="00")
  1011. noloadTime = self.decode_str(data[:2])
  1012. volume = self.decode_str(data[2:4])
  1013. return {"noloadTime": noloadTime, "volume": volume}
  1014. def set_powerPs(self, configDict):
  1015. # type:(dict) -> None
  1016. """
  1017. 设置 修正系数(powerPs)[20B] 如0x0100表示1.00
  1018. funCode: 12
  1019. """
  1020. data = ""
  1021. for i in xrange(1, 11):
  1022. powerPs = self.check_params_range(params=configDict.get("powerPs{}".format(i)), minData=0, maxData=9.99,
  1023. desc="修正系数")
  1024. data += self.encode_str(powerPs, length=4, ratio=100, base=10) # TODO 文档中是十进制待验证!
  1025. result = self.send_mqtt(funCode="12", data=data)
  1026. def get_powerPs(self):
  1027. # type:() -> dict
  1028. """
  1029. 获取 修正系数(powerPs)[20B] 如0x0100表示1.00
  1030. funCode: 13
  1031. """
  1032. data = self.send_mqtt(funCode="13", data="00")
  1033. data_list = self.decode_long_hex_to_list(data, split=4, ratio=0.01, base=10)
  1034. result = {}
  1035. for i in xrange(1, len(data_list) + 1):
  1036. result["powerPs{}".format(i)] = data_list[i - 1]
  1037. return result
  1038. def reset_device(self):
  1039. # type:() -> None
  1040. """
  1041. 复位设备
  1042. funCode: 14
  1043. 下发:CC09140009090909090001
  1044. 返回:AA09140009090909090115
  1045. """
  1046. self.send_mqtt(funCode="14", data="00")
  1047. def set_dev_fault(self, fault):
  1048. # type:(dict) -> None
  1049. """
  1050. 开启禁用设备 (01:开启设备 00:禁用设备)
  1051. funCode: 15
  1052. """
  1053. if fault == True:
  1054. data = "00"
  1055. self.send_mqtt(funCode="15", data=data)
  1056. self.disable_app_device(True)
  1057. elif fault == False:
  1058. data = "01"
  1059. self.send_mqtt(funCode="15", data=data)
  1060. self.disable_app_device(False)
  1061. else:
  1062. raise ServiceException({"result": 2, "description": u"设备操作故障!"})
  1063. def reset_factory_Mode(self):
  1064. # type:() -> None
  1065. """
  1066. 恢复工厂模式:
  1067. funCode: 16
  1068. """
  1069. self.send_mqtt(funCode="16", data="00")
  1070. def get_total_consumption(self):
  1071. # type:() -> dict
  1072. """
  1073. 获取消费总额 totalCoins[4B] + totalCard[4B] + totalRemote[4B] + totalAll[4B] + totalElec[4B]
  1074. funCode: 17
  1075. """
  1076. data = self.send_mqtt(funCode="17", data="00")
  1077. data_list = self.decode_long_hex_to_list(data, split=8, ratio=0.1, base=10)
  1078. name_list = ["totalCoins", "totalCard", "totalRemote", "totalAll", "totalElec"]
  1079. return dict(zip(name_list, data_list))
  1080. def get_total_refund(self):
  1081. # type:() -> dict
  1082. """
  1083. 获取退费总额度 totalRefundCoins[4B] + totalRefundCard[4B] + totalRefundRemote[4B]
  1084. funCode: 18
  1085. """
  1086. data = self.send_mqtt(funCode="18", data="00")
  1087. data_list = self.decode_long_hex_to_list(data, split=8, ratio=0.1, base=10)
  1088. name_list = ["totalRefundCoins", "totalRefundCard", "totalRefundRemote"]
  1089. return dict(zip(name_list, data_list))
  1090. def lock_unlock_port(self, ports, switch=True):
  1091. # type:(str,str) -> None
  1092. """
  1093. 远程禁用某个或多个端口 port[10B] 0x00:禁用 0x01:开启
  1094. funCode: 19
  1095. """
  1096. if isinstance(ports, list) or isinstance(ports, tuple):
  1097. ports = map(lambda x: str(x), ports)
  1098. elif isinstance(ports, dict):
  1099. ports = map(lambda x: str(x), ports.keys())
  1100. else:
  1101. ports = [str(ports)] # 防止下面1判断成10
  1102. if switch:
  1103. switchStr1 = "00"
  1104. switchStr2 = "01"
  1105. else:
  1106. switchStr1 = "01"
  1107. switchStr2 = "01"
  1108. data = ""
  1109. for port in xrange(1, 11):
  1110. if str(port) in ports:
  1111. data += switchStr1
  1112. else:
  1113. data += switchStr2
  1114. self.send_mqtt(funCode="19", data=data)
  1115. def set_device_time(self, configDict):
  1116. # type:(dict) -> None
  1117. """
  1118. 设置设备系统时间 year + month + date + day + hour + minute + second
  1119. funCode: 1A
  1120. """
  1121. year = self.check_params_range(configDict.get("year"), minData=0, maxData=99, desc="年份")
  1122. month = self.check_params_range(configDict.get("month"), minData=1, maxData=12, desc="月份")
  1123. date = self.check_params_range(configDict.get("date"), minData=1, maxData=31, desc="日份")
  1124. day = self.check_params_range(configDict.get("day"), minData=1, maxData=7, desc="星期")
  1125. hour = self.check_params_range(configDict.get("hour"), minData=1, maxData=23, desc="时")
  1126. minute = self.check_params_range(configDict.get("minute"), minData=1, maxData=59, desc="分")
  1127. second = self.check_params_range(configDict.get("second"), minData=1, maxData=59, desc="秒")
  1128. data = self.encode_str(year)
  1129. data += self.encode_str(month)
  1130. data += self.encode_str(date)
  1131. data += self.encode_str(day)
  1132. data += self.encode_str(hour)
  1133. data += self.encode_str(minute)
  1134. data += self.encode_str(second)
  1135. self.send_mqtt(funCode="1A", data=data)
  1136. def get_device_time(self):
  1137. # type:() -> dict
  1138. """
  1139. 获取设备系统时间
  1140. funCode: 1B
  1141. """
  1142. data = self.send_mqtt(funCode="1B", data="00")
  1143. data_list = self.decode_long_hex_to_list(data)
  1144. name_list = ["year", "month", "date", "day", "hour", "minute", "second"]
  1145. return dict(zip(name_list, data_list))
  1146. def get_charging_record(self, date):
  1147. # type:(str) -> None
  1148. """
  1149. 获取某天的充电纪录,连续返回,连续返回,连续返回!!!
  1150. funCode: 1C
  1151. example:201109 20年11月9日
  1152. """
  1153. year = date[:2]
  1154. month = date[2:4]
  1155. day = date[4:]
  1156. data = self.encode_str(year)
  1157. data += self.encode_str(month)
  1158. data += self.encode_str(day)
  1159. self.send_mqtt(funCode="1C", data=data)
  1160. # TODO 收到指令后进行解析
  1161. def parse_reson(self, code):
  1162. # type:(str) -> str
  1163. """
  1164. code 传入解码的字符串
  1165. """
  1166. if not isinstance(code, str):
  1167. code = str(code)
  1168. infoDict = {
  1169. "1": "购买的充电时间、电量用完了",
  1170. "2": "用户手动停止(拔插头,或是按了停止按钮)",
  1171. "3": "充电满了,自动停止",
  1172. "4": "设备或是端口出现问题,被迫停止",
  1173. "5": "因充电器功率超过充电站的单路最大输出功率,切断输出",
  1174. "6": "超高温(达到75度)",
  1175. "7": "烟雾报警",
  1176. "8": "漏电报警",
  1177. "9": "负载电流过大(短路)",
  1178. "0A": "开始充电未接充电器",
  1179. "0B": "端口远程停止",
  1180. }
  1181. return infoDict.get(code)
  1182. def read_charging_record(self, data):
  1183. # type:(str) -> dict
  1184. """
  1185. 接上一条,连续发送后收到解析
  1186. """
  1187. year = self.decode_str(data[:2])
  1188. month = self.decode_str(data[2:4])
  1189. date = self.decode_str(data[4:6])
  1190. day = self.decode_str(data[6:8])
  1191. hour = self.decode_str(data[8:10])
  1192. minute = self.decode_str(data[10:12])
  1193. second = self.decode_str(data[12:14])
  1194. dateNum = self.decode_str(data[14:16])
  1195. port = self.decode_str(data[16:18])
  1196. consumeType = self.decode_str(data[18:20])
  1197. chargeStatus = self.decode_str(data[20:22])
  1198. reason = self.parse_reson(data[22:24])
  1199. payMoney = self.decode_str(data[24:28], ratio=0.1, base=10)
  1200. leftMoney = self.decode_str(data[28:32], ratio=0.1, base=10)
  1201. leftTime = self.decode_str(data[32:36], ratio=0.1, base=10)
  1202. cardNo = "{:0>10}".format(self.decode_str(data[36:44]))
  1203. randomCode = self.decode_str(data[44:48])
  1204. usedElec = self.decode_str(data[48:52], ratio=0.01, base=10)
  1205. cardBalance = self.decode_str(data[52:56], ratio=0.1, base=10)
  1206. TotalRecordSum = self.decode_str(data[56:60])
  1207. return locals()
  1208. def recharge_ic_card(self, cardNo, coin):
  1209. # type:(str,str)->None
  1210. """
  1211. 下发充电指令
  1212. funcCode: 1E
  1213. """
  1214. cardNo = self.encode_str(cardNo)
  1215. cardType = "02"
  1216. money = self.encode_str(coin, length=4, ratio=10, base=10)
  1217. data = cardNo + cardType + money
  1218. self.send_mqtt(funCode="1E", data=data, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
  1219. def stop(self, port="1"):
  1220. # type: (str) -> None
  1221. self.remote_device(port, "stop")
  1222. time.sleep(1)
  1223. def response_card_status(self, cardNo, cardBalance, cardType, result):
  1224. # type: (str,RMB,str,str) -> None
  1225. """
  1226. 刷卡上报后回复设备,在上报事件里面报
  1227. funCode: 04
  1228. """
  1229. data = self.encode_str(cardNo)
  1230. data += self.encode_str(str(cardBalance), length=4, ratio=10, base=10)
  1231. data += self.encode_str(cardType)
  1232. data += self.encode_str(result)
  1233. self.send_mqtt(funCode="04", data=data)
  1234. def get_dev_setting(self):
  1235. # type: () -> dict
  1236. result = {}
  1237. data = self.get_device_ID()
  1238. result.update(data)
  1239. data = self.get_function_switch()
  1240. result.update(data)
  1241. data = self.get_consumption_rules()
  1242. result.update(data)
  1243. data = self.get_powerPs()
  1244. result.update(data)
  1245. data = self.get_icMoney_maxPower_floatingCharge()
  1246. result.update(data)
  1247. data = self.get_noloadTime_volume()
  1248. result.update(data)
  1249. time = self.get_device_time()
  1250. result.update(time)
  1251. totalRecords = self.get_total_consumption()
  1252. result.update(totalRecords)
  1253. refundProtection = self.device.get("otherConf", {}).get("refundProtection", 1)
  1254. refundProtectionTime = self.device.get("otherConf", {}).get("refundProtectionTime", "5")
  1255. consumeModule = result.get("consumeModule")
  1256. result.update({"refundProtection": refundProtection, "refundProtectionTime": refundProtectionTime,
  1257. "consumeModule": consumeModule})
  1258. return result
  1259. def set_refundProtection_params(self, configDict):
  1260. refundProtection = configDict.get("refundProtection", 1)
  1261. # TODO给前端做兼容
  1262. refundProtection = int(refundProtection)
  1263. refundProtectionTime = configDict.get("refundProtectionTime", "5")
  1264. otherConf = self.device["otherConf"]
  1265. otherConf.update({"refundProtection": refundProtection, "refundProtectionTime": refundProtectionTime})
  1266. Device.objects.filter(devNo=self.device["devNo"]).update(otherConf=otherConf)
  1267. Device.invalid_device_cache(self.device.devNo)
  1268. def set_device_function(self, request, lastSetConf):
  1269. updateSet = set(request.POST.keys())
  1270. nameSet = {"coinSwitch", "cardSwitch", "cardRefund", "autoStop", "chargeFree"}
  1271. if len(updateSet & nameSet):
  1272. lastSetConf.update(request.POST)
  1273. self.set_function_switch(lastSetConf)
  1274. nameSet = {"refundProtection"}
  1275. if len(updateSet & nameSet):
  1276. lastSetConf.update(request.POST)
  1277. self.set_refundProtection_params(lastSetConf)
  1278. nameSet = {"reset"}
  1279. if len(updateSet & nameSet):
  1280. self.reset_factory_Mode()
  1281. nameSet = {"reboot"}
  1282. if len(updateSet & nameSet):
  1283. self.reset_device()
  1284. def set_device_function_param(self, request, lastSetConf):
  1285. update = request.POST.copy()
  1286. lastSetConf = lastSetConf.copy()
  1287. if "logicalCode" in update:
  1288. update.pop("logicalCode", None)
  1289. if "logicalCode" in lastSetConf:
  1290. lastSetConf.pop("logicalCode", None)
  1291. update = dict(set(update.items()) - set(lastSetConf.items()))
  1292. updateSet = set(update.keys())
  1293. if not updateSet:
  1294. return
  1295. nameSet = {"cardTime", "cardElec", "coinElec", "remoteElec", "coinTime", "remoteTime","time","serviceFee"}
  1296. if len(updateSet & nameSet):
  1297. lastSetConf.update(update)
  1298. self.set_consumption_rules(lastSetConf)
  1299. nameSet = {"powerPs1", "powerPs2", "powerPs3", "powerPs4", "powerPs5", "powerPs6", "powerPs7", "powerPs8",
  1300. "powerPs9", "powerPs10", }
  1301. if len(updateSet & nameSet):
  1302. lastSetConf.update(update)
  1303. self.set_powerPs(lastSetConf)
  1304. nameSet = {"icMoney", "maxPower", "floatingPower", "floatingTime"}
  1305. if len(updateSet & nameSet):
  1306. lastSetConf.update(update)
  1307. self.set_icMoney_maxPower_floatingCharge(lastSetConf)
  1308. nameSet = {"noloadTime", "volume"}
  1309. if len(updateSet & nameSet):
  1310. lastSetConf.update(update)
  1311. self.set_noloadTime_volume(lastSetConf)
  1312. nameSet = {"refundProtectionTime"}
  1313. if len(updateSet & nameSet):
  1314. lastSetConf.update(update)
  1315. self.set_refundProtection_params(lastSetConf)
  1316. nameSet = {"consumeModule"}
  1317. if len(updateSet & nameSet):
  1318. lastSetConf.update(update)
  1319. self.set_function_switch(lastSetConf)
  1320. nameSet = {"hour", "day", "month", "second", "year", "date", "minute"}
  1321. if len(updateSet & nameSet):
  1322. lastSetConf.update(update)
  1323. self.set_device_time(lastSetConf)
  1324. def active_deactive_port(self, port, active):
  1325. if active == False:
  1326. self.stop(port)
  1327. def updata_no_balance_list(self, devNo, card, mode="update"):
  1328. # type: (str,Card,str) -> None
  1329. key = "100255_no_balance_list_%s" % (devNo)
  1330. if not serviceCache.get(key):
  1331. no_balance_list = []
  1332. else:
  1333. no_balance_list = serviceCache.get(key)
  1334. if mode == "update":
  1335. cardModel = {
  1336. "cardId": card.id,
  1337. "cardNo": card.cardNo,
  1338. "balance": card.balance,
  1339. }
  1340. if card.cardNo:
  1341. cardModel.update({"cardName": card.cardName})
  1342. if card.phone:
  1343. cardModel.update({"phone": card.phone})
  1344. for item in no_balance_list:
  1345. if card.cardNo == item["cardNo"]:
  1346. no_balance_list.remove(item)
  1347. if len(no_balance_list) > 49:
  1348. no_balance_list = no_balance_list[:49]
  1349. no_balance_list.insert(0, cardModel)
  1350. elif mode == "remove":
  1351. for item in no_balance_list:
  1352. if card.cardNo == item["cardNo"]:
  1353. no_balance_list.remove(item)
  1354. serviceCache.set(key, no_balance_list)
  1355. def get_no_balance_list(self, devNo):
  1356. key = "100255_no_balance_list_%s" % (devNo)
  1357. if not serviceCache.get(key):
  1358. no_balance_list = []
  1359. else:
  1360. no_balance_list = serviceCache.get(key)
  1361. return no_balance_list
  1362. def remote_charge_ic_card_by_dealer(self, cardIds, amount):
  1363. """
  1364. 经销商远程充值接口
  1365. """
  1366. cards = Card.objects.filter(id__in=cardIds)
  1367. for card in cards:
  1368. openId = None
  1369. cardId = card.id
  1370. cardNo = card.cardNo
  1371. money = 0
  1372. coins = VirtualCoin(amount)
  1373. group = Group.get_group(self.device["groupId"])
  1374. rechangeOrder = None
  1375. cardRechangeOrder = CardRechargeOrder.new_one(openId, cardId, cardNo, money, coins, group, rechangeOrder,
  1376. rechargeType="offline")
  1377. cardRechangeOrder.update(remarks="经销商线下充卡")
  1378. self.recharge_ic_card(card.cardNo, amount)
  1379. cardRechangeOrder.update(status="finished")
  1380. self.updata_no_balance_list(self.device.devNo, card, mode="remove")
  1381. def set_service_fee_info(self, payload):
  1382. # 显示展示给用户信息部分
  1383. if payload.get('displaySwitchs'):
  1384. displaySwitchs = payload.get('displaySwitchs')
  1385. else:
  1386. displaySwitchs = {'displayCoinsSwitch': False,
  1387. 'displayTimeSwitch': True,
  1388. 'displayPriceSwitch': True,
  1389. 'setPulseAble': False,
  1390. 'setBasePriceAble': False}
  1391. # 套餐部分
  1392. packages = payload.get('packages')
  1393. # 调整SN套餐顺序
  1394. for i, item in enumerate(packages):
  1395. item['sn'] = i
  1396. # 套餐id 去重
  1397. existIds = list(map(lambda _: _.get('id'), packages))
  1398. washConfig = {}
  1399. for rule in packages:
  1400. if 'price' in rule:
  1401. if not is_number(rule['price'] and is_number(rule.get('time', 0))):
  1402. raise InvalidParameter(u'金币数目或者时间或者价格必须是数字')
  1403. if RMB(rule['price']) >= self.device.owner.maxPackagePrice:
  1404. raise InvalidParameter(u'套餐金额超限')
  1405. if len(rule['name']) > 20:
  1406. raise InvalidParameter(u'套餐名字只能取1-20位')
  1407. if 'id' in rule:
  1408. ruleId = rule['id']
  1409. else:
  1410. ruleId = list(set(range(1, 71)) - set([int(ruleId) for ruleId in washConfig.keys()]) - set(existIds))[0]
  1411. washConfig[str(ruleId)] = {
  1412. 'billingMethod': CONSUMETYPE.BILL_AS_SERVICE,
  1413. 'name': rule['name'],
  1414. 'coins': float(rule['price']),
  1415. 'price': float(rule['price']),
  1416. 'time': float(rule.get('time', 0)),
  1417. 'description': rule.get('description', ''),
  1418. 'imgList': rule.get('imgList', []),
  1419. 'unit': rule.get('unit', u'分钟'),
  1420. 'switch': rule.get('switch', True),
  1421. 'sn': rule.get('sn')
  1422. }
  1423. self.device.update_device_obj(**{
  1424. 'washConfig': washConfig,
  1425. 'otherConf.displaySwitchs': displaySwitchs,
  1426. })
  1427. dealer = self.device.owner
  1428. dealer.defaultWashConfig[self.device.devTypeId] = self.device['washConfig'].values()
  1429. dealer.save()
  1430. def get_service_fee_info(self):
  1431. ruleList = []
  1432. billAsService = self.device.bill_as_service_feature
  1433. billAsService['on'] = billAsService.on
  1434. billAsService['elecCharge'] = billAsService.elec_charge
  1435. billAsService['serviceCharge'] = billAsService.service_charge
  1436. method = "电费+服务费计费" if billAsService.on else "电量计费"
  1437. for packageId, rule in self.device['washConfig'].items():
  1438. item = {
  1439. 'id': packageId,
  1440. 'name': rule['name'],
  1441. 'coins': rule['coins'],
  1442. 'price': rule.get('price', rule['coins']),
  1443. 'time': rule.get('time', 20),
  1444. 'description': rule.get('description', ''),
  1445. 'imgList': rule.get('imgList', []),
  1446. 'unit': rule.get('unit', u'分钟'),
  1447. 'switch': rule.get('switch', True)
  1448. }
  1449. if 'sn' in rule:
  1450. item['sn'] = rule['sn']
  1451. ruleList.append(item)
  1452. devData = {
  1453. 'id': self.device.devNo,
  1454. 'isManager': True,
  1455. 'groupName': self.device.group['groupName'],
  1456. 'groupNumber': self.device['groupNumber'],
  1457. 'devNo': self.device.devNo,
  1458. 'devTypeName': self.device.devTypeName,
  1459. 'devTypeCode': self.device.devTypeCode
  1460. }
  1461. if "displaySwitchs" in self.device.my_obj.otherConf:
  1462. displaySwitchs = self.device.my_obj.otherConf.get('displaySwitchs')
  1463. else:
  1464. displaySwitchs = {
  1465. 'displayCoinsSwitch': True,
  1466. 'displayTimeSwitch': True,
  1467. 'displayPriceSwitch': True,
  1468. "setPulseAble": False,
  1469. "setBasePriceAble": False
  1470. }
  1471. ruleList = sorted(ruleList, key=lambda x: (x.get('sn'), x.get('id')))
  1472. return {
  1473. 'ruleList': ruleList,
  1474. 'billAsService': billAsService,
  1475. 'devData': devData,
  1476. 'displaySwitchs': displaySwitchs,
  1477. 'method': method
  1478. }
  1479. def format_upload_power(self, power):
  1480. return float(power / 10)