changyuan4.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import logging
  5. import time
  6. import uuid
  7. from apilib.monetary import RMB
  8. from apilib.utils_datetime import timestamp_to_dt
  9. from apps.web.constant import MQTT_TIMEOUT, DeviceCmdCode, Const
  10. from apps.web.common.models import TempValues
  11. from apps.web.core.adapter.base import SmartBox, fill_2_hexByte, start_error_timer
  12. from apps.web.core.exceptions import ServiceException
  13. from apps.web.core.networking import MessageSender
  14. from apps.web.dealer.models import Dealer
  15. from apps.web.device.models import Device, Group
  16. from taskmanager.mediator import task_caller
  17. logger = logging.getLogger(__name__)
  18. class SendFunCode(object):
  19. GET_DEV_SETTINGS = "D0"
  20. SET_DEV_SETTINGS = "D1"
  21. START_PAY = "D2"
  22. SYNC_CARD_BALANCE = "D3"
  23. BEFORE_SYNC_CARD_BALANCE = "D4"
  24. RESET_DEV = "D5"
  25. STOP_DEV = "D6"
  26. SET_GROUP_PW = "DB"
  27. SET_SEND_CARD = "DC"
  28. ANSWER_CMD = "DF"
  29. SET_DEV_DISABLE = "DD"
  30. class AnalyzeCode(object):
  31. pass
  32. class ChargingCY4Box(SmartBox):
  33. START_TYPE_MAP = {
  34. "00": "card",
  35. "01": "mobile",
  36. "02": "coin"
  37. }
  38. STOP_TYPE_MAP = {
  39. "01": u"充满停止充电",
  40. "02": u"超出功率停止充电",
  41. "03": u"充电时间结束停止充电",
  42. "04": u"远程停止充电"
  43. }
  44. NEED_REFUND = ["01", "04"]
  45. FULL_REFUND_TIME = 180
  46. MAX_TEMPERATURE = 70
  47. MIN_TEMPERATURE = -15
  48. DEFAULT_DISCOUNT = "0"
  49. REFUND_PRODUCTION_TIME = 5
  50. VIR_CARD_TIME = 300
  51. def __init__(self, device):
  52. super(ChargingCY4Box, self).__init__(device)
  53. def send_data(self, funCode, data = None, timeout = MQTT_TIMEOUT.NORMAL):
  54. if data is None:
  55. data = "00"
  56. result = MessageSender.send(device = self.device,
  57. cmd = DeviceCmdCode.OPERATE_DEV_SYNC,
  58. payload = {
  59. "IMEI": self._device["devNo"],
  60. "funCode": funCode,
  61. "data": data
  62. }, timeout = timeout)
  63. if "rst" in result and result.get("rst") != 0:
  64. if result.get("rst") == -1:
  65. raise ServiceException({"result": 2, "description": u"网络故障,请重新试试"})
  66. if result.get("rst") == 1:
  67. raise ServiceException({"result": 2, "description": u"充电桩无响应,请稍后再试试"})
  68. return result
  69. def test(self, coins):
  70. """
  71. 测试端口 测试机器启动 固定端口为 01
  72. """
  73. hexCoins = fill_2_hexByte(hex(int(float(coins))), 2)
  74. hexPort = "01"
  75. data = hexCoins + "0000" + hexPort
  76. return self.send_data(SendFunCode.START_PAY, data)
  77. def start_device(self, package, openId, attachParas):
  78. """
  79. 指令实例 {"IMEI":"865650042848537", "cmd":210, "funCode": "D2", "data": "1E(金额)0000(预留)0(折扣)3(端口)"}
  80. """
  81. # TODO 这个版本先将 续充的入口封死 然后运行一段时间 下次发布的时候就可以彻底将流程归并
  82. if attachParas is None:
  83. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路、电池类型信息'})
  84. if "chargeIndex" not in attachParas:
  85. raise ServiceException({'result': 2, 'description': u'请您选择合适的充电线路'})
  86. # 参数获取
  87. price = package.get("price")
  88. coins = package.get("coins")
  89. chargeIndex = attachParas.get("chargeIndex")
  90. devCache = Device.get_dev_control_cache(self.device.devNo)
  91. portCache = devCache.get(str(chargeIndex))
  92. # 跟CY进行沟通 由于指令时序的不稳定 去掉续充保证稳定 彻底的将续充的口子封死
  93. if portCache and "status" in portCache and portCache["status"] != Const.DEV_WORK_STATUS_IDLE:
  94. raise ServiceException({'result': 2, 'description': u'当前端口正在使用中,请选择其他端口进行充电'})
  95. # 获取充值订单的ID 用来判断是现金启动还是金币启动
  96. rechargeRcdId = str(attachParas.get("linkedRechargeRecordId", ""))
  97. # 首先获取折扣信息 以及端口信息 共一个字节
  98. hexDiscount = self.get_discount(rechargeRcdId)
  99. hexPort = hexDiscount + fill_2_hexByte(hex(int(chargeIndex)), 1)
  100. # 获取下发设备的支付金额, 单位是角 共一个字节
  101. hexCoins = fill_2_hexByte(hex(int(float(coins * 10))), 2)
  102. # 如果是虚拟卡(包月卡启动设备),获取经销商设置的虚拟卡启动时间
  103. timeHex = "0000"
  104. if self._vcard_id:
  105. _time = self._device.get("otherConf", dict()).get("vCardTime", self.VIR_CARD_TIME)
  106. timeHex = fill_2_hexByte(hex(int(_time)))
  107. # 拼接发送的指令数据域
  108. dataHex = hexCoins + timeHex + hexPort
  109. result = self.send_data(SendFunCode.START_PAY, dataHex, timeout = MQTT_TIMEOUT.START_DEVICE)
  110. data = result.get("data")
  111. if data[6: 8] != '4F': # 表示成功
  112. raise ServiceException({'result': 2, 'description': u'启动充电端口失败,您的金币还在,您重新启动试试看'})
  113. # 非续充 实现续充模式的时候 需要对支付类型限定判断 只能同类型支付模式的续充 即只支持mobile模式的续充
  114. # 先设置启动时间为0 后续会更新上来
  115. start_timestamp = int(time.time())
  116. result['finishedTime'] = start_timestamp + 12 * 60 * 60
  117. # 由于不会有续充了 重新清理下端口缓存 然后再放入
  118. Device.clear_port_control_cache(self.device.devNo, chargeIndex)
  119. # TODO 缓存的数据结构还是保持不变 保证下个版本平稳过渡
  120. cacheDict = {
  121. "startTime": timestamp_to_dt(start_timestamp).strftime("%Y-%m-%d %H:%M:%S"),
  122. "status": Const.DEV_WORK_STATUS_WORKING,
  123. 'finishedTime': result['finishedTime'],
  124. "coins": float(coins),
  125. "price": float(price),
  126. "openId": openId,
  127. "consumeType": "mobile",
  128. "payInfo": [{
  129. "vCardId": self._vcard_id,
  130. "coins": float(coins),
  131. "price": float(price),
  132. "rechargeRcdId": rechargeRcdId,
  133. "needTime": 0,
  134. }],
  135. "waitD9": True
  136. }
  137. Device.update_dev_control_cache(self._device["devNo"], {str(chargeIndex): cacheDict})
  138. # 如果是正常启动的话
  139. if openId:
  140. result["rechargeRcdId"] = rechargeRcdId
  141. return result
  142. def apiStartDeviceForCy4(self, record):
  143. price = record['price'] # int
  144. discount = record.get('discount', 0) # int
  145. port = record['port'] # int
  146. extOrderNo = record.get('extOrderNo', '')
  147. hexCoins = fill_2_hexByte(hex(int(float(price * 10))), 2)
  148. timeHex = '0000'
  149. hexPort = str(discount) + fill_2_hexByte(hex(int(port)), 1)
  150. dataHex = hexCoins + timeHex + hexPort
  151. result = self.send_data(SendFunCode.START_PAY, dataHex, timeout=MQTT_TIMEOUT.START_DEVICE)
  152. data = result.get("data")
  153. if data[6: 8] != '4F': # 表示成功
  154. raise ServiceException({"result": 2, "description": u"启动失败,请稍后再试试"})
  155. cacheDict = {
  156. 'isAPI': True,
  157. 'orderNo': 'MOBILE' + str(uuid.uuid4()),
  158. 'startTime': datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  159. 'status': Const.DEV_WORK_STATUS_WORKING,
  160. 'price': float(price),
  161. 'coins': float(price),
  162. 'consumeType': 'mobile',
  163. 'waitD9': True,
  164. 'extOrderNo': extOrderNo
  165. }
  166. Device.update_dev_control_cache(self._device["devNo"], {str(port): cacheDict})
  167. return result
  168. def apiGetDevSettingsFromCy4(self, record):
  169. result = self.get_dev_setting()
  170. result.pop('temperature')
  171. result.pop('disableButton')
  172. result.pop('discount')
  173. result.pop('vCardTime')
  174. return result
  175. def apiSetDevSettingsFromCy4(self, record):
  176. self.set_dev_setting(record)
  177. return {}
  178. def apiStopChargingPortForCy4(self, record):
  179. port = record['port']
  180. self.stop_charging_port(port)
  181. return {}
  182. def apiGetPortStatusFromCy4(self, record):
  183. return self.get_port_status_from_dev()
  184. def apiGetPortInfoFromCy4(self, record):
  185. return self.get_port_info(record['port'])
  186. def get_discount(self, rechargeRcdId):
  187. """
  188. 根据启动方式的不通获取不同的折扣 金币启动的不打折 其余的根据设备的折扣而定
  189. """
  190. if rechargeRcdId:
  191. return self._device.get("otherConf", dict()).get("discount", self.DEFAULT_DISCOUNT)
  192. else:
  193. return self.DEFAULT_DISCOUNT
  194. def stop_charging_port(self, port):
  195. """
  196. 指令实例 {"IMEI":"865650042848537", "cmd":210, "funCode": "D6", "data": "03(端口)"}
  197. """
  198. portHex = fill_2_hexByte(hex(int(port)), 2)
  199. result = self.send_data(SendFunCode.STOP_DEV, data=portHex)
  200. data = result.get("data")
  201. if data[6: 8] != "4F":
  202. raise ServiceException({"result": 2, "description": u"停止充电失败,请重新试试"})
  203. return {'port': port}
  204. def stop(self, port=None):
  205. """
  206. :param port:
  207. :return:
  208. """
  209. devCache = Device.get_dev_control_cache(self._device.get("devNo"))
  210. portCache = devCache.get(str(port), dict())
  211. if not portCache.get("coins"):
  212. raise ServiceException({'result': 2, 'description': u'无法停止该端口,请重新试试或者联系经销商'})
  213. infoDict = self.stop_charging_port(port)
  214. return infoDict
  215. @property
  216. def isHaveStopEvent(self):
  217. return True
  218. def get_port_status_from_dev(self):
  219. devCache = Device.get_dev_control_cache(self._device["devNo"])
  220. allPorts = devCache.get("allPorts", 10)
  221. portInfo = {}
  222. for i in xrange(allPorts):
  223. portStr = str(i+1)
  224. portTemp = devCache.get(portStr)
  225. if not portTemp: portTemp = {"status": Const.DEV_WORK_STATUS_IDLE}
  226. portInfo[portStr] = portTemp
  227. return portInfo
  228. def get_temperature(self, tempHex):
  229. if tempHex[0: 2] == "00":
  230. temperature = int(tempHex[2: 4], 16)
  231. else:
  232. temperature = -int(tempHex[2: 4], 16)
  233. return temperature
  234. def parse_port_data(self, data):
  235. """
  236. 解析端口上传的数据
  237. :param data:
  238. :return:
  239. """
  240. # 每一路都空闲的时候,只上报温度 2个字节
  241. if data[4: 6] == "02":
  242. portInfo = {str(portNum+1): {"status": Const.DEV_WORK_STATUS_IDLE} for portNum in xrange(10)}
  243. temperature = self.get_temperature(data[6: 10])
  244. else:
  245. statusHex = data[6: 26]
  246. powerHex = data[26: 66]
  247. leftHex = data[66: 106]
  248. temperature = self.get_temperature(data[106: 110])
  249. portInfo = dict()
  250. for portNum in xrange(10):
  251. tempStatus = statusHex[portNum*2: portNum*2+2]
  252. tempPower = powerHex[portNum*4: portNum*4+4]
  253. tempLeft = leftHex[portNum*4: portNum*4+4]
  254. if tempStatus == "00":
  255. status = Const.DEV_WORK_STATUS_IDLE
  256. elif tempStatus == "01":
  257. status = Const.DEV_WORK_STATUS_WORKING
  258. elif tempStatus == "04":
  259. status = Const.DEV_WORK_STATUS_FAULT_RELAY_CONNECT
  260. else:
  261. logger.error("error port status, device is %s, port is %s, statusHex is %s" % (self._device["devNo"], str(portNum+1), tempStatus))
  262. status = Const.DEV_WORK_STATUS_IDLE
  263. power = int(tempPower, 16)
  264. leftTime = int(tempLeft, 16)
  265. portInfo[str(portNum+1)] = {"status": status, "power": power}
  266. if leftTime:
  267. portInfo[str(portNum+1)].update({"leftTime": leftTime})
  268. return {"temperature": temperature, "portInfo": portInfo}
  269. def analyze_event_data(self, data):
  270. cmdCode = data[2:4]
  271. #: D7 火警报警
  272. if cmdCode == 'D7':
  273. return {'cmdCode': cmdCode}
  274. #: 状态上报
  275. elif cmdCode == 'D8':
  276. resultDict = self.parse_port_data(data)
  277. # 处理静态端口相关事情
  278. allPorts, usedPorts, usePorts = self.get_port_static_info(resultDict.get("portInfo"))
  279. Device.update_dev_control_cache(self._device['devNo'], {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  280. resultDict.update({"cmdCode": cmdCode})
  281. return resultDict
  282. elif cmdCode == 'D9':
  283. cardNo = data[6: 14]
  284. cardBalance = int(data[14:18], 16)
  285. power = int(data[18:22], 16)
  286. coins = int(data[22:24], 16)
  287. result = True if data[24:26] == '00' else False
  288. port = str(int(data[26:28], 16))
  289. consumeType = self.START_TYPE_MAP.get(data[28:30])
  290. needTime = int(data[30:34], 16)
  291. return {
  292. 'cardNo': cardNo,
  293. 'cmdCode': cmdCode,
  294. 'balance': cardBalance,
  295. 'power': power,
  296. 'coins': coins,
  297. 'result': result,
  298. 'port': port,
  299. 'consumeType': consumeType,
  300. 'needTime': needTime,
  301. 'data':data
  302. }
  303. elif cmdCode == 'DA':
  304. port = str(int(data[6: 8], 16))
  305. reasonCode = data[8: 10]
  306. reason = self.STOP_TYPE_MAP.get(reasonCode)
  307. elec = int(data[10: 14], 16) / 1000.0
  308. leftTime = int(data[14: 18], 16)
  309. return {'port': port, 'reason': reason, 'spendElec': elec, 'leftTime': leftTime, "cmdCode": cmdCode, "reasonCode": reasonCode}
  310. elif cmdCode == 'D4': # 刷卡充值
  311. cardNo = data[6: 14]
  312. cardBalance = int(data[14:18], 16)
  313. return {
  314. 'cmdCode': cmdCode,
  315. 'cardNo': cardNo,
  316. 'balance': cardBalance
  317. }
  318. elif cmdCode == 'D3':
  319. cardNo = data[6:14]
  320. cardBalance = int(data[14:18], 16)
  321. result = True if data[18:20] == 'AA' else False
  322. sid = str(int(data[20:24], 16))
  323. return {'cmdCode':cmdCode,'cardNo':cardNo,'balance':cardBalance,'result':result,'sid':sid}
  324. def get_port_status(self, force = False):
  325. devCache = Device.get_dev_control_cache(self._device['devNo'])
  326. statusDict = dict()
  327. allPorts = devCache.get('allPorts', 10)
  328. for portNum in range(allPorts):
  329. tempDict = devCache.get(str(portNum + 1), {})
  330. if "status" in tempDict:
  331. statusDict[str(portNum + 1)] = {'status': tempDict.get('status')}
  332. elif "isStart" in tempDict:
  333. if tempDict['isStart']:
  334. statusDict[str(portNum + 1)] = {'status': Const.DEV_WORK_STATUS_WORKING}
  335. else:
  336. statusDict[str(portNum + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  337. else:
  338. statusDict[str(portNum + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
  339. allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
  340. Device.update_dev_control_cache(self._device['devNo'], {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
  341. return statusDict
  342. def active_deactive_port(self, port, active):
  343. if not active:
  344. self.stop_charging_port(port)
  345. else:
  346. raise ServiceException({'result': 2, 'description': u'此设备不支持直接打开端口'})
  347. # 获取设备配置参数
  348. def get_dev_setting(self):
  349. result = self.send_data(SendFunCode.GET_DEV_SETTINGS)
  350. confData = result.get("data")[6:]
  351. time1 = int(confData[0:4], 16)
  352. time2 = int(confData[4:8], 16)
  353. time3 = int(confData[8:12], 16)
  354. time4 = int(confData[12:16], 16)
  355. powerMax1 = int(confData[16:20], 16)
  356. powerMax2 = int(confData[20:24], 16)
  357. powerMax3 = int(confData[24:28], 16)
  358. powerMax4 = int(confData[28:32], 16)
  359. elecCheckMin = int(confData[32:34], 16)
  360. elecCheckTime = int(confData[34:36], 16)
  361. voice = int(confData[36:38], 16)
  362. devCache = Device.get_dev_control_cache(self.device.devNo)
  363. temperature = devCache.get("temperature", 0)
  364. resultDict = {
  365. 'time1': time1,
  366. 'time2': time2,
  367. 'time3': time3,
  368. 'time4': time4,
  369. 'powerMax1': powerMax1,
  370. 'powerMax2': powerMax2,
  371. 'powerMax3': powerMax3,
  372. 'powerMax4': powerMax4,
  373. 'elecCheckMin': elecCheckMin,
  374. 'elecCheckTime': elecCheckTime,
  375. 'voice': voice,
  376. "temperature": temperature
  377. }
  378. resultDict.update({
  379. "disableButton": int(self._device.get("otherConf", {}).get("disableDevice", False)),
  380. "discount": self._device.get("otherConf", dict()).get("discount", self.DEFAULT_DISCOUNT),
  381. "vCardTime": self._device.get("otherConf", dict()).get("vCardTime", self.VIR_CARD_TIME),
  382. "needBindCard": self._device.get("otherConf", dict()).get("needBindCard", True),
  383. })
  384. return resultDict
  385. # 设置设备配置参数
  386. def set_dev_setting(self, setConf):
  387. data = ''
  388. data += fill_2_hexByte(hex(setConf['time1']), 4)
  389. data += fill_2_hexByte(hex(setConf['time2']), 4)
  390. data += fill_2_hexByte(hex(setConf['time3']), 4)
  391. data += fill_2_hexByte(hex(setConf['time4']), 4)
  392. data += fill_2_hexByte(hex(setConf['powerMax1']), 4)
  393. data += fill_2_hexByte(hex(setConf['powerMax2']), 4)
  394. data += fill_2_hexByte(hex(setConf['powerMax3']), 4)
  395. data += fill_2_hexByte(hex(setConf['powerMax4']), 4)
  396. data += fill_2_hexByte(hex(setConf['elecCheckMin']), 2)
  397. data += fill_2_hexByte(hex(setConf['elecCheckTime']), 2)
  398. data += fill_2_hexByte(hex(setConf['voice']), 2)
  399. if int(setConf["time1"]) < 10 or setConf["time2"] < 10 or setConf["time3"] < 10 or setConf["time4"] < 10:
  400. raise ServiceException({"result": 2, "description": "功率时间最小为 10 分钟"})
  401. result = self.send_data(SendFunCode.SET_DEV_SETTINGS, data=data)
  402. data = result.get("data")
  403. if data[6: 10] != "4F4B":
  404. raise ServiceException({"result": 2, "description": u"参数设置失败,请重新试试!"})
  405. def reboot_dev(self):
  406. """
  407. 重启设备
  408. """
  409. self.send_data(SendFunCode.RESET_DEV, data="0000")
  410. @staticmethod
  411. def transform_password(password):
  412. passwordHex = ""
  413. while password:
  414. passwordHex += fill_2_hexByte(hex(int(password[: 2])), 2)
  415. password = password[2:]
  416. return passwordHex
  417. def set_password(self, setConf):
  418. """
  419. 设置设备的小区密码
  420. 密码是否还需要再校验,以及是否是0开头
  421. """
  422. password = setConf.get("pwd")
  423. if len(password) != 10:
  424. raise ServiceException({"result": 0, "description": u"密码长度必须为10位"})
  425. passwordHex = self.transform_password(password)
  426. self.send_data(SendFunCode.SET_GROUP_PW, data=passwordHex)
  427. def set_send_card(self, setConf):
  428. """
  429. 设置卡机模式 用于重置 实体卡的小区密码
  430. :param setConf:
  431. :return:
  432. """
  433. # TODO 同上 需要设置开关
  434. oldPassword = setConf.get("old_pwd")
  435. newPassword = setConf.get("new_pwd")
  436. if len(oldPassword) != 10 or len(newPassword) != 10:
  437. raise ServiceException({"result": 0, "description": u"密码长度必须为10位"})
  438. oldPasswordHex = self.transform_password(oldPassword)
  439. newPasswordHex = self.transform_password(newPassword)
  440. self.send_data(SendFunCode.SET_SEND_CARD, data=oldPasswordHex+newPasswordHex)
  441. # 给实体卡充值
  442. def recharge_card(self, cardNo, money, orderNo=None):
  443. # type:(str,RMB,str)->(dict, RMB)
  444. """
  445. # TODO zjl 重点测试
  446. 昌原卡充值流程大致为
  447. 手机端卡充值成功,数据成功记录服务器
  448. 实体卡靠近 充值机器 然后主板发送 D4 指令 上传卡号以及卡的余额 (上抛事件)
  449. 服务器接受指令之后,判断是否正常卡,不是正常卡 下发同步余额为0 没有充过值 直接过滤(异步指令)
  450. 桩接受之后,返回同步状态 (指令模块压栈, 上抛事件)
  451. 服务器接受之后,返回 DF 指令(异步指令)
  452. 整个流程结束
  453. {"IMEI":"865650042848537", "cmd": 210, "data": "01090001", "funCode": "D3"}
  454. :param **kwargs:
  455. """
  456. # 获取充值的标志编号
  457. sidKey = '%s-%s' % (self._device['devNo'], cardNo)
  458. sid = TempValues.get(sidKey)
  459. # 数据域为卡内当前总金额(元) + 订单编号
  460. dataHex = fill_2_hexByte(hex(int(money)), 4) + fill_2_hexByte(hex(int(sid)), 4)
  461. MessageSender.send(device = self.device,
  462. cmd = DeviceCmdCode.OPERATE_DEV_NO_RESPONSE,
  463. payload = {
  464. "IMEI": self._device["devNo"],
  465. "funCode": SendFunCode.SYNC_CARD_BALANCE,
  466. "data": dataHex
  467. })
  468. def ack_event(self):
  469. """
  470. 应答报文 应答D7 D9 DA D3 指令
  471. :return:
  472. """
  473. MessageSender.send(device = self.device,
  474. cmd = DeviceCmdCode.OPERATE_DEV_NO_RESPONSE,
  475. payload = {
  476. "IMEI": self._device["devNo"],
  477. "funCode": SendFunCode.ANSWER_CMD,
  478. "data": "4F4B"
  479. })
  480. def get_port_info(self, line=None):
  481. devCache = Device.get_dev_control_cache(self._device["devNo"])
  482. return devCache.get(str(line), {})
  483. def set_device_function_param(self, request, lastSetConf):
  484. time1 = request.POST.get('time1', None)
  485. time2 = request.POST.get('time2', None)
  486. time3 = request.POST.get('time3', None)
  487. time4 = request.POST.get('time4', None)
  488. powerMax1 = request.POST.get('powerMax1', None)
  489. powerMax2 = request.POST.get('powerMax2', None)
  490. powerMax3 = request.POST.get('powerMax3', None)
  491. powerMax4 = request.POST.get('powerMax4', None)
  492. elecCheckMin = request.POST.get('elecCheckMin', None)
  493. elecCheckTime = request.POST.get('elecCheckTime', None)
  494. voice = request.POST.get('voice', None)
  495. pw = request.POST.get("pwd", None)
  496. oldPw = request.POST.get("old_pwd", None)
  497. newPw = request.POST.get("new_pwd", None)
  498. discount = request.POST.get("discount")
  499. vCardTime = request.POST.get("vCardTime")
  500. maxTime = request.POST.get("maxTime")
  501. # 功率需要从小到大排序,这个地方要做一个校验
  502. if all([powerMax1, powerMax2, powerMax3, powerMax4]) and not int(powerMax1) < int(powerMax2) < int(powerMax3) < int(powerMax4):
  503. raise ServiceException({"result": 0, "description": u"功率参数必须从小到大设置!"})
  504. if time1:
  505. lastSetConf.update({'time1': int(time1)})
  506. if time2:
  507. lastSetConf.update({'time2': int(time2)})
  508. if time3:
  509. lastSetConf.update({'time3': int(time3)})
  510. if time4:
  511. lastSetConf.update({'time4': int(time4)})
  512. if powerMax1:
  513. lastSetConf.update({'powerMax1': int(powerMax1)})
  514. if powerMax2:
  515. lastSetConf.update({'powerMax2': int(powerMax2)})
  516. if powerMax3:
  517. lastSetConf.update({'powerMax3': int(powerMax3)})
  518. if powerMax4:
  519. lastSetConf.update({'powerMax4': int(powerMax4)})
  520. if elecCheckMin:
  521. lastSetConf.update({'elecCheckMin': int(elecCheckMin)})
  522. if elecCheckTime:
  523. lastSetConf.update({'elecCheckTime': int(elecCheckTime)})
  524. if voice:
  525. lastSetConf.update({'voice': int(voice)})
  526. if pw:
  527. lastSetConf.update({"pwd": pw})
  528. if oldPw:
  529. lastSetConf.update({"old_pwd": oldPw})
  530. if newPw:
  531. lastSetConf.update({"new_pwd": newPw})
  532. # 设备参数设置
  533. if any([time1, time2, time3, time4, powerMax1, powerMax2, powerMax3, powerMax4, elecCheckMin, elecCheckTime, voice]):
  534. self.set_dev_setting(lastSetConf)
  535. # 小区密码设置
  536. if pw:
  537. self.set_password(lastSetConf)
  538. # 发卡机器模式
  539. if all([oldPw, newPw]):
  540. self.set_send_card(lastSetConf)
  541. # 设置安全充电时长
  542. if maxTime is not None:
  543. self.set_max_time(int(maxTime))
  544. if discount is not None:
  545. otherConf = self._device.get("otherConf", dict())
  546. otherConf.update({"discount": str(discount)})
  547. Device.objects.filter(devNo=self._device["devNo"]).update(otherConf=otherConf)
  548. Device.invalid_device_cache(self._device["devNo"])
  549. if vCardTime is not None:
  550. otherConf = self._device.get("otherConf", dict())
  551. otherConf.update({"vCardTime": str(vCardTime)})
  552. Device.objects.filter(devNo=self._device["devNo"]).update(otherConf=otherConf)
  553. Device.invalid_device_cache(self._device["devNo"])
  554. def set_device_function(self, request, lastSetConf):
  555. if "disableButton" in request.POST:
  556. if "dealerDisableDevice" not in self.device.owner:
  557. raise ServiceException({"result": 2, "description": "抱歉,您无此操作权限,请联系厂家获取权限"})
  558. return self.set_dev_disable(request.POST["disableButton"])
  559. if "reboot" in request.POST and request.POST.get("reboot"):
  560. return self.reboot_dev()
  561. if "needBindCard" in request.POST:
  562. needBindCard = request.POST.get('needBindCard')
  563. otherConf = self.device.get("otherConf", dict())
  564. otherConf.update({"needBindCard": needBindCard})
  565. Device.objects.get(devNo=self.device.devNo).update(otherConf=otherConf)
  566. Device.invalid_device_cache(self.device.devNo)
  567. def set_max_time(self, maxTime):
  568. data = "{:04X}".format(maxTime)
  569. try:
  570. self.send_data("F8", data, timeout=5)
  571. except ServiceException as e:
  572. raise ServiceException({"result": 2, "description": u"该设备暂不支持充电时长,请联系厂家进行升级"})
  573. def set_dev_disable(self, disable):
  574. """
  575. 设备端锁定解锁设备
  576. :param disable:
  577. :return:
  578. """
  579. if disable:
  580. status = "00AA"
  581. else:
  582. status = "0055"
  583. result = self.send_data(funCode=SendFunCode.SET_DEV_DISABLE, data=status)
  584. data = result["data"]
  585. if data[6: 10] != "4F4B":
  586. raise ServiceException({'result': 2, 'description': u'设置失败,请重试吧'})
  587. otherConf = self._device.get("otherConf", {})
  588. otherConf["disableDevice"] = disable
  589. Device.objects.filter(devNo=self._device["devNo"]).update(otherConf=otherConf)
  590. Device.invalid_device_cache(self._device["devNo"])
  591. def handle_clear_start_error(self, port):
  592. dealer = Dealer.objects.get(id=self._device["ownerId"])
  593. if not dealer or not dealer.managerialOpenId:
  594. return
  595. group = Group.get_group(self._device["groupId"])
  596. notifyData = {
  597. "title": "设备故障报警解除",
  598. "device": u"{gNum}组-{lc}-{port}端口".format(gNum=self._device["groupNumber"], lc=self._device["logicalCode"], port=port),
  599. "location": u"{address}-{groupName}".format(address=group["address"], groupName=group["groupName"]),
  600. "fault": u"当前端口已被正常启动,报警解除",
  601. "notifyTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  602. }
  603. task_caller(
  604. func_name='report_to_dealer_via_wechat',
  605. openId=dealer.managerialOpenId,
  606. dealerId=str(dealer.id),
  607. templateName="device_fault",
  608. **notifyData
  609. )
  610. def handle_out_start_error(self, port):
  611. dealer = Dealer.objects.get(id=self._device["ownerId"])
  612. if not dealer or not dealer.managerialOpenId:
  613. return
  614. group = Group.get_group(self._device["groupId"])
  615. times = Device.get_error_start_times(self._device["devNo"], port)
  616. notifyData = {
  617. "title": u"注意,您的设备可能发生故障!",
  618. "device": u"{gNum}组-{lc}-{port}端口".format(gNum=self._device["groupNumber"], lc=self._device["logicalCode"], port=port),
  619. "location": u"{address}-{groupName}".format(address=group["address"], groupName=group["groupName"]),
  620. "fault": u"当前设备端口连续启动 {times} 失败,请注意该端口是否发生故障".format(times=times),
  621. "notifyTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  622. }
  623. task_caller(
  624. func_name='report_to_dealer_via_wechat',
  625. openId=dealer.managerialOpenId,
  626. dealerId=str(dealer.id),
  627. templateName="device_fault",
  628. **notifyData
  629. )