123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import logging
- import sys
- import time
- import datetime
- from apilib.monetary import RMB, VirtualCoin
- from apps.web.device.models import DevicePortReport, Device, Group
- from apps.web.south_intf.platform import notify_event_to_north_v2
- from apps.web.user.models import ServiceProgress, MonthlyPackage, CardRechargeOrder, ConsumeRecord, CardConsumeRecord, \
- MyUser
- from apps.common.utils import int_to_hex
- from apps.web.eventer import EventBuilder
- from apps.web.core.device_define.ywt_chongdiangui import DefaultParams, Calculater
- from apps.web.constant import DeviceCmdCode, Const, DEALER_CONSUMPTION_AGG_KIND
- from apps.web.eventer.base import FaultEvent, WorkEvent
- logger = logging.getLogger(__name__)
- class builder(EventBuilder):
- def __getEvent__(self, device_event):
- event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
- if event_data is None or 'cmdCode' not in event_data:
- return None
- # 粤万通的版本比较复杂 没有一个统一的版本 这次力争升级到一个统一的版本
- # TODO 补丁
- if "ack_id" in device_event and self.device.driverVersion not in [
- "v1.0.0", "v1.0.1", "v1.0.2", "v3.0.1", "v3.0.2", "v3.0.3", "v4.0.0", "v4.0.1"
- ]:
- self.deviceAdapter._ack(device_event["ack_id"])
- cmdCode = event_data.get("cmdCode", "")
- className = "ChargeCabinetEvent{}".format(cmdCode.capitalize())
- myClass = getattr(sys.modules[__name__], className, None)
- if myClass:
- return myClass(self.deviceAdapter, event_data)
- # 工具类
- class ChargeCabinetEventBase(object):
- @property
- def device_adapter(self):
- return getattr(self, 'deviceAdapter')
- @staticmethod
- def reverse_hex(s):
- if len(s) == 0:
- return ""
- return s[-2:] + ChargeCabinetEventBase.reverse_hex(s[:-2])
- def is_start_by_card(self, port):
- devCache = Device.get_dev_control_cache(self.device_adapter.device.devNo)
- return "cardNo" in devCache.get(port, dict())
- def response_card_start(self, _type, portStrHex, cardNoHex, balance = 0, orderNo = "0", chargeTime = 0, pw = 0,
- cardType = 0, leftDays = 0):
- balance = ChargeCabinetEventBase.reverse_hex("{:0>8X}".format(int(balance * 100) & 0xFFFFFFFF))
- cardType = ChargeCabinetEventBase.reverse_hex("{:0>2X}".format(int(cardType)))
- leftDays = ChargeCabinetEventBase.reverse_hex("{:0>4X}".format(int(leftDays)))
- orderNo = ChargeCabinetEventBase.reverse_hex("{:0>14X}".format(int(orderNo)))
- chargeTime = ChargeCabinetEventBase.reverse_hex("{:0>4X}".format(int(chargeTime)))
- pw = ChargeCabinetEventBase.reverse_hex("{:0>4X}".format(int(pw)))
- sendData = cardNoHex + portStrHex + _type + balance + cardType + leftDays + orderNo + chargeTime + pw
- self.device_adapter._send_data(funCode="B0", sendData=sendData, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
- def response_finish(self, orderNoHex, portStr):
- portStrHex = int_to_hex(portStr, lens=2)
- sendData = orderNoHex + portStrHex + "01"
- self.device_adapter._send_data("C1", sendData, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
- def start_device(self, port, orderNo, pw, operate):
- return self.device_adapter._start(port, orderNo, pw, operate)
- def response_fault(self, port):
- portStrHex = "{:02X}".format(port)
- sendData = portStrHex + "01"
- self.device_adapter._send_data("E0", sendData, cmd = DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
- def response_card_finish(self, cardNoHex, portStrHex, balance, consumeMoney, cardType = 1, leftDays = 0,
- cardStatus = "01"):
- balanceHex = ChargeCabinetEventBase.reverse_hex("{:0>8X}".format(int(balance * 100)))
- consumeMoneyHex = ChargeCabinetEventBase.reverse_hex("{:0>8X}".format(int(consumeMoney * 100)))
- cardTypeHex = ChargeCabinetEventBase.reverse_hex("{:0>2X}".format(int(cardType)))
- leftDaysHex = ChargeCabinetEventBase.reverse_hex("{:0>4X}".format(int(leftDays)))
- sendData = cardNoHex + portStrHex + cardStatus + balanceHex + cardTypeHex + leftDaysHex + consumeMoneyHex
- self.device_adapter._send_data("B1", sendData, cmd = DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
- def deduct_for_card(self, portStr, card, money, consumeOrder, cardConsumeOrder):
- devCache = Device.get_dev_control_cache(self.device_adapter.device.devNo)
- portCache = devCache.get(portStr)
- monthlyPackageId = portCache.get("monthlyPackageId")
- if monthlyPackageId:
- monthlyPackage = MonthlyPackage.objects.get(id = monthlyPackageId)
- monthlyPackage.deduct(consumeOrder)
- else:
- cardBalance = card.balance - RMB(money)
- card.balance = cardBalance
- card.save()
- # 更新
- consumeOrder.update(money = RMB(money).mongo_amount, coin = VirtualCoin(money).mongo_amount, status = "finished")
- cardConsumeOrder.update(money = RMB(money).mongo_amount, coin = VirtualCoin(money).mongo_amount)
- # 故障
- class ChargeCabinetEventE0(FaultEvent, ChargeCabinetEventBase):
- def do(self, **args):
- group = Group.get_group(self.device["groupId"])
- if self.is_notify_dealer():
- self.notify_dealer(
- "device_fault",
- title=u"注意注意您的设备发生故障",
- device=u"组号:{},二维码编号:{}".format(self.device["groupNumber"], self.device["logicalCode"]),
- location=u"组名称:{},地址:{}".format(group["groupName"], group["address"]),
- fault=self.event_data["statusInfo"],
- notifyTime=str(datetime.datetime.now())[:19]
- )
- warningData = {
- "warningStatus": 2,
- "warningDesc": self.event_data["statusInfo"],
- "warningTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- }
- # 整机告警
- part = self.event_data.get("portStr", "0")
- Device.update_dev_warning_cache(self.device.devNo, {part: warningData})
- try:
- faultRecord = self.record(faultCode=self.event_data.get("faultCode", ""), detail={"errorCode": self.event_data["faultCode"]})
- self.north_to_liangxi(faultRecord)
- except Exception as e:
- logger.error(e)
- finally:
- sendData = self.event_data.get("portHex") + "01"
- self.deviceAdapter._send_data("E0", sendData = sendData, cmd = DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
- # 时间同步
- class ChargeCabinetEventA1(WorkEvent, ChargeCabinetEventBase):
- def do(self, **args):
- logger.info("charge cabinet <{}> request to synchronization time".format(self.device.devNo))
- self.deviceAdapter._sync_device_time()
- # 参数设置同步
- class ChargeCabinetEventA2(WorkEvent, ChargeCabinetEventBase):
- def do(self, **args):
- logger.info("charge cabinet <{}> request to device params".format(self.device.devNo))
- self.deviceAdapter._sync_device_settings()
- # 刷卡上报
- class ChargeCabinetEventB0(WorkEvent, ChargeCabinetEventBase):
- def do(self, **args):
- """
- 两个问题 刷卡的时候主板会判断当前端口是否已经被占用
- :param args:
- :return:
- """
- logger.info("charge cabinet <{}> send a card <{}> start signal ".format(self.device.devNo,self.event_data.get("cardNo")))
- cardNo = self.event_data.get("cardNo")
- portStr = self.event_data.get("portStr")
- portStrHex = self.event_data['portHex']
- cardNoHex = self.event_data['cardNoHex']
- card = self.update_card_dealer_and_type(cardNo)
- if not card or not card.openId or card.frozen:
- self.response_card_start(_type="03", portStrHex=portStrHex, cardNoHex=cardNoHex)
- logger.info("bad card, cardNo is %s, cannot start port!" % cardNo)
- return
- # 充值未到账的卡的处理 有些卡充值在没有上报类型之前 充值结果不会到账的 这个地方需要处理一下
- card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
- self.recharge_id_card(card=card, rechargeType='append', order=card_recharge_order)
- card.reload()
- # 能够刷卡的最低的金额
- monthlyPackage = card.monthlyPackage
- if monthlyPackage:
- cardType = 2
- leftDays = (monthlyPackage.expireDateTime - datetime.datetime.now()).days
- else:
- # 能够刷卡的最低的金额
- minCardMoney = self.device.get("otherConf", dict()).get("minAfterStartCoins",
- DefaultParams.DEFAULT_MIN_START_COINS)
- if float(card.balance) < float(minCardMoney):
- self.response_card_start(_type = "02", portStrHex = portStrHex, cardNoHex = cardNoHex,
- balance = card.balance)
- logger.info("negative card, card No is %s, card balance is %s" % (cardNo, card.balance))
- return
- cardType = 1
- leftDays = 0
- # 制造启动前的各种参数 密码 时间等等
- chargeTime = 0xFFFF
- pw = self.deviceAdapter.password
- otherConf = self.device.get("otherConf")
- servicedInfo = {"pw": pw, "chargeType": otherConf.get("chargeType", DefaultParams.DEFAULT_CHARGE_TYPE)}
- orderNo, cardConsumeOrderNo = self.record_consume_for_card(
- card=card,
- money=RMB(0),
- servicedInfo=servicedInfo,
- attachParas={"pw": pw}
- )
- # 返回刷卡 OK 的信息
- self.response_card_start(
- "01",
- portStrHex,
- cardNoHex=cardNoHex,
- balance=card.balance,
- orderNo=orderNo,
- chargeTime=chargeTime,
- pw=pw,
- cardType=cardType,
- leftDays=leftDays
- )
- logger.info("card start ok! event has been report!")
- # 主动下发指令B2-02 ,开启柜门开启充电启动设备
- self.start_device(portStr, orderNo, pw, "02")
- # 启动设备之后,注册服务,并更新端口缓存值
- ServiceProgress.register_card_service(
- self.device,
- int(portStr),
- card,
- {
- "orderNo": orderNo,
- "cardOrderNo": cardConsumeOrderNo
- },
- finishedTime = int(time.time()) + 7 * 24 * 60 * 60
- )
- portDict = {
- "status": Const.DEV_WORK_STATUS_WORKING,
- "isStart": True,
- "orderNo": orderNo,
- "cardOrderNo": cardConsumeOrderNo,
- "cardNo": cardNo,
- "openId": card.openId,
- "startTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
- "pw": pw,
- "monthlyPackageId": None if cardType == 1 else str(monthlyPackage.id)
- }
- Device.update_dev_control_cache(self.device["devNo"], {portStr: portDict})
- # 通知用户已经启动了
- self.notify_user(
- managerialOpenId = card.managerialOpenId,
- templateName = "consume_notify",
- title = u"您正在刷卡启动充电柜业务",
- money = u"使用结束后计算费用",
- serviceType = u"刷卡消费, 当前启动为: {}柜门".format(portStr),
- finishTime = datetime.datetime.now().strftime(Const.DATETIME_FMT)
- )
- # 刷卡结束上报
- class ChargeCabinetEventB1(WorkEvent, ChargeCabinetEventBase):
- def do(self): # zjl 去掉参数
- """
- 刷卡结束的时候, 仅仅会接受到刷卡上报的信息 和主板商沟通过不会再有充电结束的上报
- :return:
- """
- portStrHex = self.event_data['portHex'] # zjl
- cardNoHex = self.event_data['cardNoHex'] # zjl
- cardNo = self.event_data.get("cardNo")
- portStr = self.event_data.get("portStr")
- orderNo = self.event_data.get("orderNo")
- devCache = Device.get_dev_control_cache(self.device.devNo)
- portCache = devCache.get(portStr) or dict()
- if not portCache:
- logger.error("no port cache, error card finished!, card is <{}>".format(cardNo))
- self.response_card_finish(cardNoHex, portStrHex, cardStatus="03")
- return
- if cardNo != portCache.get("cardNo"):
- logger.error("card no is not equal , card is <{}>".format(cardNo))
- self.response_card_finish(cardNoHex, portStrHex, cardStatus="03")
- return
- pw = portCache.get("pw", "")
- openId = portCache.get("openId", "")
- cardOrderNo = portCache.get("cardOrderNo", "")
- try:
- card = self.update_card_dealer_and_type(cardNo)
- consumeOrder = ConsumeRecord.objects.get(orderNo=orderNo)
- cardConsumeOrder = CardConsumeRecord.objects.get(orderNo=cardOrderNo)
- except Exception as e:
- logger.exception(e)
- return
- # 请求结束充电 发送
- result = self.start_device(portStr, orderNo, pw, "12")
- data = result.get("data")
- curInfo = self.deviceAdapter._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
- self.deduct_for_card(portStr, card, consumeMoney, consumeOrder, cardConsumeOrder)
- # 结束服务
- consumeDict = {
- "chargeIndex": portStr,
- 'actualNeedTime': curInfo.get("chargeTime"),
- 'elec': curInfo.get("elec"),
- 'stayTime': curInfo.get("stayTime"),
- 'chargeTime': curInfo.get("chargeTime"),
- }
- ServiceProgress.update_progress_and_consume_rcd(
- self.device["ownerId"],
- {
- "cardId": str(card.id),
- "open_id": consumeOrder.openId,
- "device_imei": self.device["devNo"],
- "port": int(portStr),
- "isFinished": False
- },
- consumeDict
- )
- # 最后回复B1 指令
- monthlyPackageId = portCache.get("monthlyPackageId")
- if monthlyPackageId:
- monthlyPackage = MonthlyPackage.objects.get(id = monthlyPackageId)
- self.response_card_finish(
- cardNoHex = cardNoHex,
- portStrHex = portStrHex,
- balance = RMB(0),
- consumeMoney = RMB(consumeMoney),
- cardType = 2,
- leftDays = max((monthlyPackage.expireDateTime - datetime.datetime.now()).days, 0)
- )
- extra = [({"消费金额": "{}元".format(consumeMoney), "余额": "{}元".format(0)})]
- else:
- self.response_card_finish(
- cardNoHex = cardNoHex,
- portStrHex = portStrHex,
- balance = RMB(card.balance),
- consumeMoney = RMB(consumeMoney)
- )
- extra=[({"消费金额": "{}元".format(consumeMoney), "余额": "{}元".format(card.balance)})]
- self.notify_user_service_complete(
- service_name='充电',
- openid=card.managerialOpenId,
- port=portStr,
- address=self.device.group['address'],
- reason=self.event_data.get('reasonDesc'),
- finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
- extra=extra)
- Device.clear_port_control_cache(self.device.devNo, portStr)
- # 状态上报
- class ChargeCabinetEventC0(WorkEvent, ChargeCabinetEventBase):
- def do(self, **args):
- orderNo = self.event_data.get("orderNo")
- orderNoHex = self.event_data.get("orderNoHex")
- portStr = self.event_data.get("portStr")
- voltage = self.event_data.get("voltage")
- power = self.event_data.get("power")
- elec = self.event_data.get("elec")
- chargeTime = self.event_data.get("chargeTime")
- temperature = self.event_data.get("temperature")
- status = self.event_data.get("status")
- stayTime = self.event_data.get("stayTime")
- devCache = Device.get_dev_control_cache(self.device["devNo"])
- portCache = devCache.get(portStr, dict())
- # 首先回复设备表示该状态已经收到
- if self.device.driverVersion in ['v1.0.0', 'v1.0.1']:
- sendData = orderNoHex + "{:0>2X}".format(int(portStr)) + "01"
- self.deviceAdapter._send_data(funCode="C0", sendData=sendData, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
- # 空闲状态的端口过滤
- if status == Const.DEV_WORK_STATUS_IDLE:
- logger.info("device {} port {} report event, useless info!".format(self.device["devNo"], portStr))
- return
- openId = portCache.get("openId")
- if not openId:
- logger.info("port report event, has not openId, useless info!")
- return
- if orderNo != portCache.get("orderNo"):
- logger.info("port report event, not equal orderNo, useless info!{}".format(portStr))
- return
- # 保存数据库, 更新端口缓存
- data = {
- "devNo": self.device["devNo"],
- "port": portStr,
- "orderNo": orderNo,
- "openId": openId,
- "voltage": voltage,
- "power": power,
- "elec": elec,
- "chargeTime": chargeTime,
- "stayTime": stayTime
- }
- if status == Const.DEV_WORK_STATUS_OCCUPY:
- data.update({"isBilling": False})
- reportRecord = DevicePortReport.create(**data)
- if not reportRecord:
- logger.info("port report event, save db error!")
- return
- portCache.update({
- "voltage": voltage,
- "elec": elec,
- "temperature": temperature
- })
- Device.update_dev_control_cache(self.device["devNo"], {portStr: portCache})
- # 充电结束上报
- class ChargeCabinetEventC1(WorkEvent, ChargeCabinetEventBase):
- def do(self, **args):
- orderNo = self.event_data.get("orderNo")
- portStr = self.event_data.get("portStr")
- orderNoHex = self.event_data.get("orderNoHex")
- chargeTime = int(self.event_data.get("chargeTime"))
- elec = self.event_data.get("elec")
- voltage = self.event_data.get("voltage")
- power = self.event_data.get("power")
- stayTime = self.event_data.get("stayTime")
- devCache = Device.get_dev_control_cache(self.device["devNo"])
- portCache = devCache.get(portStr, dict())
- openId = portCache.get("openId")
- self.response_finish(orderNoHex, portStr)
- if portCache.get("orderNo") != orderNo:
- logger.warning("charge finish, but the orderNo not equal {}".format(portCache))
- # TODO 这种情况怎么去处理
- # 更新充电的时间 结束充电的时候 is_billing 一定是有效的 也就是说这一笔记录是需要算充电钱的
- DevicePortReport.create(
- devNo=self.device.devNo,
- port=portStr,
- orderNo=orderNo,
- openId=openId,
- voltage=voltage,
- power=power,
- elec=elec,
- chargeTime=chargeTime,
- stayTime=stayTime
- )
- # 更新新的portCache 这一轮的计费结束 就是 下一轮 占位费的开始
- portCache.update({
- "status": Const.DEV_WORK_STATUS_OCCUPY, # 状态更新进入占位状态
- "occupyStartTime": int(time.time()) # 占位的起始时间
- })
- Device.update_dev_control_cache(self.device.devNo, {portStr: portCache})
- if self.event_data.get("reasonCode") == "06":
- # 密码开锁的,需要结单, 这个可能是离线的信息上报 需要直接结单
- consumeOrder = ConsumeRecord.objects.get(orderNo = orderNo) # type: ConsumeRecord
- consumeMoney = Calculater(self.device, consumeOrder).result
- consumeDict = {
- "chargeIndex": portStr,
- 'actualNeedTime': self.event_data.get("chargeTime"),
- 'elec': self.event_data.get("elec"),
- 'stayTime': self.event_data.get("stayTime"),
- 'chargeTime': self.event_data.get("chargeTime"),
- }
- if self.is_start_by_card(portStr):
- cardNo = portCache.get("cardNo")
- card = self.update_card_dealer_and_type(cardNo)
- cardOrderNo = portCache.get("cardOrderNo", "")
- cardConsumeOrder = CardConsumeRecord.objects.get(orderNo = cardOrderNo)
- self.deduct_for_card(portStr, card, consumeMoney, consumeOrder, cardConsumeOrder)
- consumeDict = {
- "chargeIndex": portStr,
- 'actualNeedTime': self.event_data.get("chargeTime"),
- 'elec': self.event_data.get("elec"),
- 'stayTime': self.event_data.get("stayTime"),
- 'chargeTime': self.event_data.get("chargeTime"),
- DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: RMB(consumeMoney).mongo_amount
- }
- else:
- # 扫码启动的设备的情况
- # 切换到运行结束的状态
- consumeOrder.update(money=RMB(consumeMoney).mongo_amount, coin=VirtualCoin(consumeMoney).mongo_amount)
- consumeOrder.reload()
- consumeOrder.s_to_e()
- ServiceProgress.update_progress_and_consume_rcd(
- self.device["ownerId"],
- {
- "open_id": consumeOrder.openId,
- "device_imei": self.device["devNo"],
- "port": int(portStr),
- "isFinished": False
- },
- consumeDict
- )
- Device.clear_port_control_cache(self.device.devNo, portStr)
- # 开始通知用户充电结束 进入占位状态
- else:
- user = MyUser.objects.filter(openId=portCache.get("openId"), groupId=self.device["groupId"]).first()
- group = Group.get_group(self.device["groupId"])
- self.notify_user(
- managerialOpenId=user.managerialOpenId if user else "",
- templateName="service_complete",
- title=u"\\n\\n结束原因:\\t\\t{reason}\\n\\n设备编号:\\t\\t{logicalCode}-{port}\\n\\n服务地址:\\t\\t{group}\\n\\n充电时间:\\t\\t{chargeTime}分钟".format(
- reason=self.event_data["reason"],
- logicalCode=self.device["logicalCode"],
- port=self.event_data["portStr"],
- group=group["address"],
- chargeTime=chargeTime,
- ),
- service=u"充电柜充电服务",
- finishTime=str(datetime.datetime.now())[: 19],
- remark=u'充电已经结束,请及时扫码,结束本次订单,取走您的电池!'
- )
- # 北向接口, 传递给API调用方
- notify_event_to_north_v2(self.device["devNo"], self.event_data)
- class ChargeCabinetEventCa(WorkEvent, ChargeCabinetEventBase):
- def do(self, **args):
- logger.info("device <{}> receive CA, event data = {}".format(self.device.devNo, self.event_data))
- subChargeList = self.event_data["subChargeList"]
- for _sub in subChargeList:
- ChargeCabinetEventC0(self.deviceAdapter, _sub).do()
|