# -*- coding: utf-8 -*- # !/usr/bin/env python import binascii import datetime import logging import os import random import time import simplejson as json from apilib.monetary import VirtualCoin, RMB from apps.web.common.proxy import ClientConsumeModelProxy from apps.web.constant import DeviceCmdCode, Const, MQTT_TIMEOUT from apps.web.core.adapter.base import SmartBox, reverse_hex, fill_2_hexByte from apps.web.core.device_define.ywt_chongdiangui import Calculater from apps.web.core.exceptions import ServiceException from apps.web.core.networking import MessageSender from apps.web.device.models import DevicePortReport, Group, Device, DeviceType from apps.web.user.models import ConsumeRecord, ServiceProgress, MyUser from apps.web.core.device_define.ywt_chongdiangui import DefaultParams from apps.web.utils import concat_user_login_entry_url from taskmanager.mediator import task_caller logger = logging.getLogger(__name__) class ChargeCabinet(SmartBox): def __init__(self, *args, **kwargs): super(ChargeCabinet, self).__init__(*args, **kwargs) # 主要用于计算费用 self.devNo = self.device.devNo @property def password(self): return random.randint(0x2710, 0xFFFF) def _send_data(self, funCode, sendData, cmd=None, timeout=MQTT_TIMEOUT.NORMAL, orderNo=None): """ 发送报文封装 :param funCode: :param sendData: :param cmd: :param timeout: :param orderNo: :return: """ if cmd is None: cmd = DeviceCmdCode.OPERATE_DEV_SYNC result = MessageSender.send(device = self.device, cmd = cmd, payload = { "IMEI": self._device["devNo"], "funCode": funCode, "data": sendData }, timeout = timeout) if result.has_key("rst"): if result["rst"] == -1: raise ServiceException({'result': 2, 'description': u'充电桩正在玩命找网络,请稍候再试'}) elif result["rst"] == 1: raise ServiceException({'result': 2, 'description': u'充电桩主板连接故障'}) elif result["rst"] == 0: return result else: raise ServiceException({'result': 2, 'description': u'系统错误'}) else: raise ServiceException({'result': 2, 'description': u'系统错误'}) @staticmethod def _to_str(data): return binascii.unhexlify(data) @staticmethod def _to_ascii(data): return binascii.hexlify(data) def _notify_user_service_over(self, managerialOpenId, port, chargeTime, stayTime, money, isPaid=True): group = Group.get_group(self.device.get("groupId")) notifyData = { "title": u"\\n\\n设备编号:\\t\\t{logicalCode}-{port}\\n\\n服务地址:\\t\\t{group}\\n\\n使用时长:\\t\\t{chargeTime}分钟\\n\\n占位时长:\\t\\t{stayTime}分钟\\n\\n本单消费:\\t\\t{money}".format( logicalCode=self._device["logicalCode"], port=port, group=group["address"], chargeTime=chargeTime, stayTime=stayTime, money=str(money), ), "service": u"已使用账户余额自动结算此次消费" if isPaid else u"您的账户余额已不足以抵扣此次消费,请前往账单中心进行支付", "finishTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "remark": u'谢谢您的支持' } task_caller( func_name='report_to_user_via_wechat', openId=managerialOpenId, dealerId=self.device.get("ownerId"), templateName="service_complete", **notifyData ) @staticmethod def _parse_event_A1(data): return dict() @staticmethod def _parse_event_A2(data): return dict() @staticmethod def _parse_event_B0(data): cardPre = ChargeCabinet._to_str(data[8: 16]) cardNo = ChargeCabinet._to_str(data[16: 32]) portStr = str(int(data[32: 34], 16)) return { "cardPre": cardPre, "cardNo": cardNo, "cardNoHex": data[8: 32], # zjl 16->8 "portStr": portStr, "portHex": data[32: 34] # zjl new } @staticmethod def _parse_event_B1(data): cardPre = ChargeCabinet._to_str(data[8: 16]) cardNo = ChargeCabinet._to_str(data[16: 32]) portStr = str(int(data[32: 34], 16)) orderNoHex = data[34: 48] orderNo = str(int(reverse_hex(orderNoHex), 16)) return { "cardPre": cardPre, "cardNo": cardNo, "cardNoHex": data[8: 32], # zjl 16->8 "portStr": portStr, "portHex": data[32:34], # zjl "orderNoHex": orderNoHex, "orderNo": orderNo } @staticmethod def _parse_event_C0(data): orderNoHex = data[8: 22] orderNo = str(int(reverse_hex(orderNoHex), 16)) portStr = str(int(data[22: 24], 16)) voltage = int(reverse_hex(data[24: 28]), 16) power = int(reverse_hex(data[32: 36]), 16) elec = int(reverse_hex(data[36: 44]), 16) / 1000.0 chargeTime = int(reverse_hex(data[44: 48]), 16) stayTime = int(reverse_hex(data[48:52]), 16) temperature = int(reverse_hex(data[52: 56]), 16) status = DefaultParams.STATUS_MAP.get(data[56: 58], Const.DEV_WORK_STATUS_IDLE) return { "orderNo": orderNo, "orderNoHex": orderNoHex, "portStr": portStr, "voltage": voltage, "power": power, "elec": elec, "chargeTime": chargeTime, "temperature": temperature, "status": status, "stayTime": stayTime } @staticmethod def _parse_event_E0(data): portStr = str(int(data[8: 10], 16)) faultHex = data[10: 14] fault = str(int(faultHex[2: 4] + faultHex[:2], 16)) faultReason = DefaultParams.FAULT_MAP.get(fault) return { "portHex": data[8: 10], "portStr": portStr, "faultCode": fault, "statusInfo": faultReason } @staticmethod def _parse_event_C1(data): orderNoHex = data[8: 22] orderNo = str(int(reverse_hex(orderNoHex), 16)) portHex = data[22: 24] portStr = str(int(data[22: 24], 16)) voltage = int(reverse_hex(data[24: 28]), 16) power = int(reverse_hex(data[32: 36]), 16) elec = int(reverse_hex(data[36: 44]), 16) / 1000.0 chargeTime = int(reverse_hex(data[44: 48]), 16) stayTime = int(reverse_hex(data[48:52]), 16) temperature = int(reverse_hex(data[52: 56]), 16) reasonCode = data[56: 58] reason = DefaultParams.STOP_REASON_MAP.get(reasonCode) return { "orderNoHex": orderNoHex, "orderNo": orderNo, "portHex": portHex, "portStr": portStr, "voltage": voltage, "power": power, "elec": elec, "chargeTime": chargeTime, "temperature": temperature, "reasonCode": reasonCode, "reason": reason, "stayTime": stayTime } @staticmethod def _parse_result_B2(data): orderNoHex = data[8: 22] orderNo = str(int(reverse_hex(orderNoHex), 16)) portHex = data[22: 24] portStr = str(int(data[22: 24], 16)) voltage = int(reverse_hex(data[24: 28]), 16) power = int(reverse_hex(data[32: 36]), 16) elec = int(reverse_hex(data[36: 44]), 16) / 1000.0 chargeTime = int(reverse_hex(data[44: 48]), 16) stayTime = int(reverse_hex(data[48:52]), 16) return { "orderNoHex": orderNoHex, "orderNo": orderNo, "portHex": portHex, "portStr": portStr, "voltage": voltage, "power": power, "elec": elec, "chargeTime": chargeTime, "stayTime": stayTime } @staticmethod def _parse_event_CA(data): data = data[8: -4] result = list() while data: orderNoHex = data[: 14] orderNo = str(int(reverse_hex(data[:14]), 16)) portStr = str(int(data[14: 16], 16)) voltage = int(reverse_hex(data[16: 20]), 16) power = int(reverse_hex(data[24: 28]), 16) elec = int(reverse_hex(data[28: 36]), 16) / 1000.0 chargeTime = int(reverse_hex(data[36: 40]), 16) stayTime = int(reverse_hex(data[40: 44]), 16) temperature = int(reverse_hex(data[44: 48]), 16) status = DefaultParams.STATUS_MAP.get(data[48: 50], Const.DEV_WORK_STATUS_IDLE) data = data[50: ] result.append({ "orderNo": orderNo, "orderNoHex": orderNoHex, "portStr": portStr, "voltage": voltage, "power": power, "elec": elec, "chargeTime": chargeTime, "temperature": temperature, "status": status, "stayTime": stayTime }) return {"subChargeList": result} def _start(self, port, orderNo, pw, operate, chargeTime=0xFFFF): """ :param port: :param orderNo: :param pw: :param chargeTime: :return: """ orderNoHex = fill_2_hexByte(hex(int(orderNo)), 14, reverse=True) portHex = fill_2_hexByte(hex(int(port)), 2, reverse=True) chargeTimeHex = fill_2_hexByte(hex(int(chargeTime)), 4, reverse=True) operateHex = operate pwHex = fill_2_hexByte(hex(int(pw)), 4, reverse=True) data = orderNoHex+portHex+chargeTimeHex+operateHex+pwHex result = self._send_data("B2", data, timeout=120) result.update({"pw": pw}) return result def _open(self, port, orderNo, pw, operate): """ :param port: :param orderNo: :param pw: :return: """ orderNoHex = fill_2_hexByte(hex(int(orderNo)), 14, reverse=True) portHex = fill_2_hexByte(hex(int(port)), 2, reverse=True) chargeTimeHex = '0000' operateHex = operate pwHex = fill_2_hexByte(hex(int(pw)), 4, reverse=True) data = orderNoHex+portHex+chargeTimeHex+operateHex+pwHex result = self._send_data("B2", data, timeout=120) result.update({"pw": pw}) return result def _stop(self, port, orderNo, door=True): orderNoHex = fill_2_hexByte(hex(int(orderNo)), 14, reverse=True) portHex = fill_2_hexByte(hex(int(port)), 2, reverse=True) operateHex = "12" if door else "11" data = "{}{}0000{}0000".format(orderNoHex, portHex, operateHex) result = self._send_data("B2", data) return result def _get_port_status(self): result = self._send_data("B3", "00") data = result.get("data", "")[8: -4] portInfo = dict() while data: tempPort = str(int(data[0: 2], 16)) tempStatus = DefaultParams.STATUS_MAP.get(data[2:4]) portInfo.update({tempPort: tempStatus}) data = data[4: ] return portInfo def _get_port_status_detail(self): result = self._send_data("B4", "00") data = result.get("data", "")[8: -4] portInfo = dict() while data: tempPort = str(int(data[0: 2], 16)) tempVoltage = int(reverse_hex(data[2: 6]), 16) tempAmpere = int(reverse_hex(data[6:10]), 16) / 1000.0 tempPower = int(reverse_hex(data[10: 14]), 16) tempElec = int(reverse_hex(data[14: 22]), 16) / 1000.0 tempTime = int(reverse_hex(data[22: 26]), 16) tempOccTime = int(reverse_hex(data[26:30]), 16) tempTemper = int(reverse_hex(data[30: 34]), 16) tempStatus = DefaultParams.STATUS_MAP.get(data[34:36]) portInfo.update({ tempPort: { "voltage": tempVoltage, "elec": tempElec, "ampere": tempAmpere, "power": tempPower, "chargeTime": tempTime, "temperature": tempTemper, "status": tempStatus, "occTime": tempOccTime } }) data = data[36: ] return portInfo def _reboot_device(self): return self._send_data("B5", "00") def _lock_device(self): operateHex = "01" return self._send_data("B6", operateHex) def _unlock_device(self): operateHex = "00" return self._send_data("B6", operateHex) def _sync_device_time(self): nowTime = datetime.datetime.now() yearHex = fill_2_hexByte(hex(nowTime.year), 4, reverse=True) monthHex = fill_2_hexByte(hex(nowTime.month), 2) dayHex = fill_2_hexByte(hex(nowTime.day), 2) hourHex = fill_2_hexByte(hex(nowTime.hour), 2) minuteHex = fill_2_hexByte(hex(nowTime.minute), 2) secondHex = fill_2_hexByte(hex(nowTime.second), 2) sendData = yearHex + monthHex + dayHex + hourHex + minuteHex + secondHex + "00" self._send_data("A1", sendData=sendData, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE) def _sync_device_settings(self): domain = os.environ.get("MY_DOMAIN") otherConf = self._device.get("otherConf", dict()) qr_code_url = concat_user_login_entry_url(l = self._device["logicalCode"]) snCode = otherConf.get("snCode", self._device["logicalCode"].replace("G", "")) minPower = otherConf.get("minPower", DefaultParams.DEFAULT_MIN_POWER) maxPower = otherConf.get("maxPower", DefaultParams.DEFAULT_MAX_POWER) floatTime = otherConf.get("floatTime", DefaultParams.DEFAULT_FLOAT_TIME) ICSetupCode = otherConf.get("ICSetupCode", DefaultParams.DEFAULT_IC_CODE) qrCodeLenHex = fill_2_hexByte(hex(len(qr_code_url)), 2) minPowerHex = fill_2_hexByte(hex(int(minPower)), 4, reverse=True) maxPowerHex = fill_2_hexByte(hex(int(maxPower)), 4, reverse=True) floatTimeHex = fill_2_hexByte(hex(int(floatTime)), 4, reverse=True) qrCodeHex = self._to_ascii(qr_code_url) snCodeHex = self._to_ascii(snCode) ICSetupCodeHex = self._to_ascii(ICSetupCode) sendData = snCodeHex + minPowerHex + maxPowerHex + floatTimeHex + ICSetupCodeHex + qrCodeLenHex + qrCodeHex self._send_data("A2", sendData=sendData, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE) # 添加特性 去掉前台的金额显示按钮 otherConf.update({"payAfterUse": True}) Device.objects.filter(devNo=self.device.devNo).update(otherConf=otherConf) Device.invalid_device_cache(self.device.devNo) def _update_device_conf(self, data): minAfterStartCoins = data.get("minAfterStartCoins", DefaultParams.DEFAULT_MIN_START_COINS) minPower = data.get("minPower", DefaultParams.DEFAULT_MIN_POWER) maxPower = data.get("maxPower", DefaultParams.DEFAULT_MAX_POWER) floatTime = data.get("floatTime", DefaultParams.DEFAULT_FLOAT_TIME) ICSetupCode = data.get("ICSetupCode", DefaultParams.DEFAULT_IC_CODE) actualPortNum = data.get("actualPortNum", DefaultParams.DEFAULT_PORT_NUM) chargeType = data.get("chargeType", DefaultParams.DEFAULT_CHARGE_TYPE) timeUnitPrice = data.get("timeUnitPrice", DefaultParams.DEFAULT_TIME_UNIT_PRICE) stayTimeUnitPrice = data.get("stayTimeUnitPrice", DefaultParams.DEFAULT_STAY_TIME_UNIT_PRICE) freeStayTime = data.get("freeStayTime", DefaultParams.DEFAULT_FREE_STAY_TIME) minConsume = data.get("minConsume", DefaultParams.DEFAULT_MIN_CONSUME) maxConsume = data.get("maxConsume", DefaultParams.DEFAULT_MAX_CONSUME) powerPackage = data.get("powerPackage", DefaultParams.DEFAULT_POWER_PACKAGE) otherConf = self._device.get("otherConf", {}) otherConf.update({ "minAfterStartCoins": int(minAfterStartCoins), "minPower": int(minPower), "maxPower": int(maxPower), "floatTime": int(floatTime), "ICSetupCode": ICSetupCode, "actualPortNum": int(actualPortNum), "chargeType": chargeType, "timeUnitPrice": float(timeUnitPrice), "stayTimeUnitPrice": float(stayTimeUnitPrice), "freeStayTime": float(freeStayTime), "minConsume": float(minConsume), "maxConsume": float(maxConsume), "powerPackage": powerPackage, }) devNo = self._device["devNo"] try: Device.objects.filter(devNo=devNo).update(otherConf=otherConf) except Exception as e: logger.error(e) raise ServiceException({'result': 2, 'description': u'设置错误,请重新操作试试'}) Device.invalid_device_cache(devNo) def _ack(self, ack_id): self._send_data(funCode='AK', sendData=ack_id, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE) def check_order_state(self, openId): """ 通过 openId 以及设备来鉴别 订单 :param openId: :return: """ dealerId = self.device.ownerId devTypeCode = self.device.devType.get("code") return ClientConsumeModelProxy.get_not_finished_record(ownerId = dealerId, openId = openId, devTypeCode = devTypeCode) def analyze_event_data(self, data): cmdCode = data[6:8] funcName = "_parse_event_{}".format(cmdCode.upper()) func = getattr(ChargeCabinet, funcName, None) if func and callable(func): eventData = func(data) eventData.update({"cmdCode": cmdCode}) return eventData else: logger.warning("<{}> device receive an undefined cmd <{}>, data is <{}>".format(self.devNo, cmdCode, data)) def get_dev_setting(self): otherConf = self._device.get("otherConf", {}) return { "minAfterStartCoins": otherConf.get("minAfterStartCoins", DefaultParams.DEFAULT_MIN_START_COINS), "minPower": otherConf.get("minPower", DefaultParams.DEFAULT_MIN_POWER), "maxPower": otherConf.get("maxPower", DefaultParams.DEFAULT_MAX_POWER), "floatTime": otherConf.get("floatTime", DefaultParams.DEFAULT_FLOAT_TIME), "ICSetupCode": otherConf.get("ICSetupCode", DefaultParams.DEFAULT_IC_CODE), "actualPortNum": otherConf.get("actualPortNum", DefaultParams.DEFAULT_PORT_NUM), "chargeType": otherConf.get("chargeType", DefaultParams.DEFAULT_CHARGE_TYPE), "timeUnitPrice": otherConf.get("timeUnitPrice", DefaultParams.DEFAULT_TIME_UNIT_PRICE), "stayTimeUnitPrice": otherConf.get("stayTimeUnitPrice", DefaultParams.DEFAULT_STAY_TIME_UNIT_PRICE), "freeStayTime": otherConf.get("freeStayTime", DefaultParams.DEFAULT_FREE_STAY_TIME), "minConsume": otherConf.get("minConsume", DefaultParams.DEFAULT_MIN_CONSUME), "maxConsume": otherConf.get("maxConsume", DefaultParams.DEFAULT_MAX_CONSUME), "powerPackage": otherConf.get("powerPackage", DefaultParams.DEFAULT_POWER_PACKAGE), } def set_device_function_param(self, request, lastSetConf): actualPortNum = request.POST.get("actualPortNum", None) if actualPortNum is not None and int(actualPortNum) > 30: raise ServiceException({"result": 2, "description": u"实际端口数量最大30"}) chargeType = request.POST.get("chargeType") if chargeType not in ("time", "power"): raise ServiceException({"result": 2, "description": u"暂不支持此消费模式"}) minPower = int(request.POST.get("minPower")) maxPower = int(request.POST.get("maxPower")) # 校验功率计费套餐 powerPackage = request.POST.get("powerPackage", None) if powerPackage is not None: if len(powerPackage) > 3: raise ServiceException({"result": 2, "description": u"功率计费不得大于三档"}) checkNum = None for index, item in enumerate(powerPackage): tempMin = int(item.get("lowLimit")) tempMax = int(item.get("upLimit")) if checkNum is not None and checkNum != tempMin: raise ServiceException({"result": 2, "description": u"功率计费区间不连续"}) if tempMin >= tempMax: raise ServiceException({"result": 2, "description": u"第{}功率计费区间最小值大于最大值".format(index+1)}) if tempMin < minPower: raise ServiceException({"result": 2, "description": u"第{}功率计费区间最小值小于功率最小值".format(index+1)}) if tempMax > maxPower: raise ServiceException({"result": 2, "description": u"第{}功率计费区间最大值大于功率最大值".format(index+1)}) # 校验计费套餐 maxConsume = request.POST.get("maxConsume") minConsume = request.POST.get("minConsume") if float(minConsume) < 0 or float(maxConsume) < 0: raise ServiceException({"result": 2, "description": u"最低消费金额和最高消费金额不能小于0"}) if float(minConsume) > float(maxConsume): raise ServiceException({"result": 2, "description": u"最低消费金额不得大于最高消费金额"}) self._update_device_conf(request.POST) def set_device_function(self, request, lastSetConf): lockDevice = request.POST.get("lockDevice", None) rebootDevice = request.POST.get("rebootDevice", None) setToDevice = request.POST.get("setToDevice", None) if rebootDevice is not None: self._reboot_device() return if lockDevice is not None and lockDevice in ("false", "true"): lockDevice = json.loads(lockDevice) self._lock_device() if lockDevice else self._unlock_device() return # 下发所有参数到设备,这个地方做一个保护 ,设备正在工作的时候不让设置参数 actualPortNum = self.device.get("otherConf", dict()).get("actualPortNum") if setToDevice is not None: devCache = Device.get_dev_control_cache(self._device["devNo"]) for _key, value in devCache.items(): if _key.isdigit() and int(_key) > actualPortNum: continue if isinstance(value, dict) and value.get("status", Const.DEV_WORK_STATUS_IDLE) == Const.DEV_WORK_STATUS_WORKING: break elif isinstance(value, dict) and value.get("isStart", False): break else: continue else: return self._sync_device_settings() raise ServiceException({'result': 2, 'description': u'设备正在工作中,无法下发参数,请等待设备空闲时设置!'}) def get_port_status(self, force=False): if force: self.get_port_status_from_dev() devCache = Device.get_dev_control_cache(self._device["devNo"]) if "allPorts" not in devCache: self.get_port_status_from_dev() devCache = Device.get_dev_control_cache(self._device["devNo"]) allPorts = devCache.get("allPorts") if allPorts is None: raise ServiceException({'result': 2, 'description': u'充电端口信息获取失败'}) # 获取显示端口的数量 客户要求可以设置 显示给用户多少个端口 showPortNum = self._device.get("otherConf", {}).get("actualPortNum", DefaultParams.DEFAULT_PORT_NUM) statusDict = dict() showStatusDict = dict() for portNum in xrange(allPorts): portStr = str(portNum + 1) tempDict = devCache.get(portStr, {}) if "status" in tempDict: statusDict[portStr] = {"status": tempDict["status"]} elif "isStart" in tempDict: if tempDict["isStart"]: statusDict[portStr] = {"status": Const.DEV_WORK_STATUS_WORKING} else: statusDict[portStr] = {"status": Const.DEV_WORK_STATUS_IDLE} else: statusDict[portStr] = {"status": Const.DEV_WORK_STATUS_IDLE} if int(portStr) <= int(showPortNum): showStatusDict[portStr] = {"status": statusDict[portStr]["status"]} allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict) portsDict = {"allPorts": allPorts, "usedPorts": usedPorts, "usePorts": usePorts} Device.update_dev_control_cache(self._device["devNo"], portsDict) # 返还的是要显示的端口数量 return showStatusDict def get_port_status_from_dev(self): portInfo = self._get_port_status() portDict = dict() for portStr, status in portInfo.items(): portDict[portStr] = {"status": status} # 更新可用端口数量 allPorts, usedPorts, usePorts = self.get_port_static_info(portDict) Device.update_dev_control_cache( self._device["devNo"], { "allPorts": allPorts, "usedPorts": usedPorts, "usePorts": usePorts } ) # 更新端口状态 devCache = Device.get_dev_control_cache(self._device["devNo"]) for port, info in portDict.items(): if port in devCache and isinstance(info, dict): devCache[port].update({"status": info["status"]}) else: devCache[port] = info Device.update_dev_control_cache(self._device["devNo"], devCache) return portDict def start_device(self, package, openId, attachParas): portStr = attachParas.get("chargeIndex") orderNo = attachParas.get("orderNo") if portStr is None: raise ServiceException({'result': 2, 'description': u'未知端口'}) # 发送指令 pw = self.password result = self._start(orderNo=orderNo, port=portStr, pw=pw, operate="02") portDict = { "status": Const.DEV_WORK_STATUS_WORKING, "vCardId": self._vcard_id, "isStart": True, "openId": openId, "orderNo": orderNo, "startTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "pw": pw } Device.update_dev_control_cache(self._device["devNo"], {str(portStr): portDict}) otherConf = self.device.get("otherConf") or dict() result["consumeOrderNo"] = orderNo result["servicedInfo"] = {"pw": pw, "chargeType": otherConf.get("chargeType", DefaultParams.DEFAULT_CHARGE_TYPE)} result["finishedTime"] = int(time.time()) + 7 * 24 * 60 * 60 return result def stop(self, port=None): """ 端口停止功能 用户主动停止了该端口 对于充电柜的业务来说 结束充电就意味着 需要打开柜门 同时结算订单 收到停止指令之后 服务器首先下发B2-12指令 然后根据返还的信息进行扣费处理 最后结存订单 :param port: :return: """ portStr = str(port) devCache = Device.get_dev_control_cache(self.device.devNo) portCache = devCache.get(portStr, dict()) # 校验订单状态 非正在运行的订单不能结束 考虑 是否 加锁 if not portCache: return if portCache.get("cardNo"): raise ServiceException({"result": 2, "description": u"刷卡启动的设备请使用刷卡结束"}) orderNo = portCache.get("orderNo", "") consumeOrder = ConsumeRecord.objects.filter(orderNo=orderNo).first() if consumeOrder is None or not consumeOrder.is_running(): return pw = portCache.get("pw") openId = portCache.get("openId") # 这个时候已经告诉主板停止了 result = self._start(port, orderNo, pw, "12") # 然后去读取主板的的最后一次的数据 data = result.get("data") curInfo = ChargeCabinet._parse_result_B2(data) DevicePortReport.create( devNo=self.device.devNo, port=curInfo.get("portStr"), orderNo=curInfo.get("orderNo"), openId=openId, voltage=curInfo.get("voltage"), power=curInfo.get("power"), elec=curInfo.get("elec"), chargeTime=curInfo.get("chargeTime"), stayTime=curInfo.get("stayTime"), isBilling=portCache.get("status", Const.DEV_WORK_STATUS_IDLE) == Const.DEV_WORK_STATUS_WORKING ) consumeMoney = Calculater(self.device, consumeOrder).result consumeOrder.update(money=RMB(consumeMoney).mongo_amount, coin=VirtualCoin(consumeMoney).mongo_amount) consumeOrder.reload() consumeOrder.s_to_e() consumeDict = { "chargeIndex": portStr, 'actualNeedTime':curInfo.get("chargeTime"), 'elec': curInfo.get("elec"), 'stayTime': curInfo.get("stayTime"), 'chargeTime': curInfo.get("chargeTime") } if consumeOrder.servicedInfo and isinstance(consumeOrder.servicedInfo, dict): consumeDict.update(consumeOrder.servicedInfo) ServiceProgress.update_progress_and_consume_rcd( self._device["ownerId"], { "open_id": consumeOrder.openId, "device_imei": self.device["devNo"], "port": int(portStr), "isFinished": False }, consumeDict ) # 通知用户充电柜业务结束 如果订单并没有使用余额结算 还需要通知用户及时去付款 try: openId = portCache.get("openId") user = MyUser.objects.filter(openId=openId, groupId=self.device.get("groupId")).first() self._notify_user_service_over(user.managerialOpenId, portStr, curInfo.get("chargeTime"), curInfo.get("stayTime"), consumeMoney, consumeOrder.is_finished()) except Exception as e: logger.exception(e) Device.clear_port_control_cache(self.device.devNo, portStr) return @property def isHaveStopEvent(self): return True def dealer_get_port_status(self): showPortNum = self._device.get("otherConf", {}).get("actualPortNum", DefaultParams.DEFAULT_PORT_NUM) showStatusDict = dict() portInfo = self._get_port_status_detail() devCache = Device.get_dev_control_cache(self.device.devNo) for port, item in portInfo.items(): if int(port) > showPortNum: continue portCache = devCache.get(port, dict()) item.update(portCache) if item.get("status", Const.DEV_WORK_STATUS_IDLE) in (Const.DEV_WORK_STATUS_WORKING, Const.DEV_WORK_STATUS_OCCUPY): item["status"] = Const.DEV_WORK_STATUS_WORKING item["usedTime"] = item.get("chargeTime") showStatusDict[port] = item else: showStatusDict[port] = {"status": item["status"]} return showStatusDict def get_current_use(self, order): # type: (ConsumeRecord) -> dict group = Group.get_group(order.groupId) item = ServiceProgress.objects.filter( device_imei=order.devNo, open_id=order.openId, attachParas__orderNo=order.orderNo, isFinished=False ).first() if not item: return dict() data = { 'startTime': time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(item.start_time)), 'order': item.consumeOrder, 'address': group.get('address', ''), 'groupName': group.get('groupName', ''), 'devType': self.device['devType'].get('name'), 'devTypeCode': self.device['devType'].get('code'), 'logicalCode': self.device['logicalCode'], 'status': Const.DEV_WORK_STATUS_WORKING, 'devNo': self.devNo, "port": item.port } data.update(DeviceType.get_services_button(self.device['devType']['id'])) devCache = Device.get_dev_control_cache(self.devNo) portCache = devCache.get(str(item.port)) or dict() data.update(portCache) return data def apiOpenCabinetDoor(self, record): portStr = record['chargeIndex'] orderNo = record['orderNo'] # 发送指令 pw = self.password result = self._open(orderNo=orderNo, port=portStr, pw=pw, operate="03") result["consumeOrderNo"] = orderNo result["servicedInfo"] = {"pw": pw} if result['rst'] == 0: result["description"] = u'开门成功' else: result["description"] = u'开门失败' result.pop('cmd') result.pop('IMEI') result.pop('servicedInfo') result.pop('data') return result def apiStartCharging(self, record): portStr = record['chargeIndex'] orderNo = record['orderNo'] chargeTime = record['chargeTime'] # 发送指令 pw = self.password result = self._start(orderNo=orderNo, port=portStr, pw=pw, operate="01", chargeTime=chargeTime) result["consumeOrderNo"] = orderNo result["servicedInfo"] = {"pw": pw} if result['rst'] == 0: result["description"] = u'启动成功' else: result["description"] = u'启动失败' result.pop('cmd') result.pop('IMEI') result.pop('servicedInfo') result.pop('data') return result def apiOpenAndStartCharging(self, record): portStr = record['chargeIndex'] orderNo = record['orderNo'] chargeTime = record['chargeTime'] # 发送指令 pw = self.password result = self._start(orderNo=orderNo, port=portStr, pw=pw, operate="02", chargeTime=chargeTime) result["consumeOrderNo"] = orderNo result["servicedInfo"] = {"pw": pw} if result['rst'] == 0: result["description"] = u'启动成功' else: result["description"] = u'启动失败' result.pop('cmd') result.pop('IMEI') result.pop('servicedInfo') result.pop('data') return result def apiGetDevicePortInfo(self, record): result = self.get_port_status_from_dev() # 0 代表 空闲 # 1 代表 繁忙 # 2 代表 故障 # 10 代表 占位 return result def apiStopChargingWithOpenDoor(self, record): portStr = record['chargeIndex'] orderNo = record['orderNo'] # 发送指令 pw = self.password result = self._stop(orderNo=orderNo, port=portStr, door=True) result["consumeOrderNo"] = orderNo result["servicedInfo"] = {"pw": pw} if result['rst'] == 0: result["description"] = u'停止充电成功' else: result["description"] = u'停止充电失败' result.pop('cmd') result.pop('IMEI') result.pop('servicedInfo') result.pop('data') return result def apiStopChargingWithCloseDoor(self, record): portStr = record['chargeIndex'] orderNo = record['orderNo'] # 发送指令 pw = self.password result = self._stop(orderNo=orderNo, port=portStr, door=False) result["consumeOrderNo"] = orderNo result["servicedInfo"] = {"pw": pw} if result['rst'] == 0: result["description"] = u'停止充电成功' else: result["description"] = u'停止充电失败' result.pop('cmd') result.pop('IMEI') result.pop('servicedInfo') result.pop('data') return result def apiGetPortInfoYwt(self, record): return self._get_port_status_detail()