123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- # 换电贵业务流程整理
- # 1. 用户扫码,服务器开启指定端口
- # 2. 用户将电池放入柜子中,同时关闭门锁,主板检测门锁关闭后,上报相应报文
- # 3. 服务器收到指令之后 需要检测 电池是否已经成功连接充电器 ,避免空仓的情况发生
- # 4. 检测空仓 的方法是读取该端口的电池IMEI ,主板会有延迟,所以一次没有读到需要继续再读取一次 最多三次没有读取到就算是没有放入电池
- # 5. 检测到电池的情况下,发送开锁指令,打开一个有电池的柜门
- # 6. 用户关闭柜门, 主板再次上报,完成一次换电操作
- # 注意事项
- # 1. 整个换电可拆解为两次独立的开关门事件 即 开门--关门--开门--关门
- # 2. 目前并没有比较可靠的方式将两次事件关联起来,完全依赖服务器的缓存以及订单的存储信息,实际最好的方式是在发送串口数据的时候携带订单号,但是目前主板并没有做到这一点
- # 3. 安骑科技会有一个单独的电池管理系统,该系统下电池编号 用户ID 代理商ID 构成唯一的一条数据,也就是说一个用户一个经销商下,同一时间只可能拥有一块电池
- # 4. 初代主板问题比较大,经常会出现明明电池放进去了,但是还是没有读到电池的情况。更换主板后情况得到缓解,但需要注意,目前还有少量的初代设备正在运行
- import datetime
- import logging
- from apps.web.core.adapter.base import SmartBox, fill_2_hexByte
- from apps.web.core.exceptions import ServiceException
- from apps.web.core.networking import MessageSender
- from apps.web.dealer.models import Dealer
- from apps.web.device.models import Battery, Device, Group
- from apps.web.user.models import MyUser
- from taskmanager.mediator import task_caller
- from apps.web.constant import MQTT_TIMEOUT, DeviceCmdCode, Const
- logger = logging.getLogger(__name__)
- class AnQiBox(SmartBox):
- DOOR_STATUS_MAP = {
- "00": "opened",
- "01": "closed"
- }
- DEFAULT_CAN_USE_VOLTAGE = 65
- DEFAULT_MAX_VOLTAGE = 75
- DEFAULT_MIN_VOLTAGE = 10
- DEFAULT_VOICE_NUM = 1
- DEFAULT_REPAIR_DOOR = 11
- DEFAULT_REVERSE_LOCK_STATUS = False
- MAX_NO_LOAD_ELEC = 0.5
- INVALID_BATTERY_SN_HEX = "".join(["0" for r in xrange(48)])
- BATTERY_SN_ASCII = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
- def __init__(self, device):
- super(AnQiBox, self).__init__(device)
- self._devNo = device.get("devNo")
- self._portStatus = None
- def _send_data(self, funCode, data = None, cmd = None, timeout = MQTT_TIMEOUT.NORMAL):
- """
- 发送报文函数
- :return:
- """
- if cmd is None:
- cmd = DeviceCmdCode.OPERATE_DEV_SYNC
- if data is None:
- data = ""
- result = MessageSender.send(device = self.device,
- cmd = cmd,
- payload = {
- "IMEI": self._devNo,
- "funCode": funCode,
- "data": data
- }, timeout = timeout)
- if result.has_key('rst') and result['rst'] != 0:
- if result['rst'] == -1:
- raise ServiceException({'result': 2, 'description': u"网络故障"})
- elif result['rst'] == 1:
- raise ServiceException({'result': 2, 'description': u'主板无响应'})
- return result
- def _open_door(self, port):
- """
- 发送指令, 开启柜门
- :param port:
- :return:
- """
- try:
- self._turn_off_power(port)
- except Exception as e:
- pass
- portHex = fill_2_hexByte(hex(int(port)), 2)
- result = self._send_data("82", portHex, timeout = MQTT_TIMEOUT.START_DEVICE)
- data = result.get("data", "")
- if data[14: 16] != "00" or data[18: 20] != "00":
- return
- return result
- def _query_door_status(self):
- """
- 查询所有门锁的状态
- :return:
- """
- doorStatus = list()
- result = self._send_data("84")
- data = result.get("data", "")
- if data[12:14] != "00":
- return doorStatus
- lockNum = int(data[14: 16], 16)
- needData = data[16:-2]
- for i in xrange(lockNum):
- portStr = str(i + 1)
- doorStatus.append({
- portStr: self.DOOR_STATUS_MAP.get(needData[:2])
- })
- needData = needData[2:]
- return doorStatus
- def _query_all_status(self):
- """
- 查询所有的信息, 包括电压、电量、锁状态、电池SN号
- :return:
- """
- statusDict = dict()
- result = self._send_data("78")
- data = result.get("data", "")
- if data[14:16] != "00":
- return statusDict
- batteryNum = int(data[16:18], 16)
- needData = data[18: -2]
- for i in range(batteryNum):
- portStr = str(i + 1)
- batterySn = needData[8:56]
- if batterySn == self.INVALID_BATTERY_SN_HEX:
- batterySn = ""
- else:
- batterySn = needData[8:56 - 2].decode("hex")[8:]
- # 添加上电量百分比 为了不影响其他的逻辑 换个字段名称
- statusDict[portStr] = {
- "voltage": int(needData[: 4], 16) / 100.0,
- "elec": int(needData[4:6], 16),
- "doorStatus": self.DOOR_STATUS_MAP.get(needData[6:8]),
- "batteryImei": batterySn,
- }
- needData = needData[56:]
- return statusDict
- def _query_battery_imei(self, port):
- """
- 查询电池的 IMEI 号码
- :return:
- """
- imei = ""
- portHex = fill_2_hexByte(hex(int(port)), 2)
- result = self._send_data("77", portHex)
- data = result.get("data")
- if data[14:16] != "00":
- return imei
- return self.translate_battery_imei(data[20: -2].decode("hex")[8:])
- def _query_elec(self):
- """
- 查询当前的充电电流
- :return:
- """
- result = self._send_data("72")
- data = result["data"]
- if data[14: 16] != "00": # 读取电流信息失败
- raise ServiceException({'result': 2, 'description': u'无响应信息'})
- allPorts = int(data[16: 18], 16)
- statusDict = dict()
- for i in xrange(allPorts):
- portStr = str(i + 1)
- electric = int(data[18 + (i * 4):18 + (i * 4) + 4], 16)
- electric /= 1000.0
- chargeStatus = u"充电中" if electric > self.MAX_NO_LOAD_ELEC else u"停止充电"
- statusDict[portStr] = {"outputElec": "{:.2f}".format(electric), "chargeStatus": chargeStatus}
- return statusDict
- def _pay_voice(self):
- """
- 播报 音乐
- :return:
- """
- voiceNum = self._device.get("otherConf", dict()).get("voiceNum", self.DEFAULT_VOICE_NUM)
- voiceHex = fill_2_hexByte(hex(int(voiceNum)), 2)
- self._send_data("75", voiceHex, DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
- def _turn_on_power(self, port):
- """
- 开启充电
- :return:
- """
- portHex = fill_2_hexByte(hex(int(port)), 2)
- timeHex = "0258"
- self._send_data("70", portHex + timeHex, cmd = DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
- def _get_charge_limit(self):
- """
- 读取充电电流限制
- :return:
- """
- result = self._send_data("76")
- data = result.get("data", "")
- if data[14:16] != "00":
- raise ServiceException({"result": 2, "description": u"读取充电电流限制失败"})
- maxElec = int(data[16:20], 16)
- minElec = int(data[20:24], 16)
- return {
- "maxElec": maxElec,
- "minElec": minElec
- }
- def _turn_off_power(self, port):
- """
- 关闭充电
- :return:
- """
- portHex = fill_2_hexByte(hex(int(port)), 2)
- self._send_data("71", portHex, cmd = DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
- def _set_charge_limit(self, maxElec, minElec):
- """
- 设置充电电流限制
- :param maxElec:
- :param minElec:
- :return:
- """
- if maxElec and int(maxElec) > 10000:
- raise ServiceException({"result": 2, "description": u"可设置的最大充电电流为10000mA"})
- if minElec and int(minElec) < 0:
- raise ServiceException({"result": 2, "description": u"可设置的最小充电电流为0mA"})
- data = ""
- data += fill_2_hexByte(hex(int(maxElec)))
- data += fill_2_hexByte(hex(int(minElec)))
- result = self._send_data("74", data)
- if result.get("data", "")[14:16] != "00":
- raise ServiceException({"result": 2, "description": u"设置充电电流限制失败"})
- def _query_all_battery_imei(self):
- """
- 暂定 batteryImei 是 15位
- :return:
- """
- data = self._query_all_status()
- batteryInfo = dict()
- for port, item in data.items():
- batteryImei = item.get("batteryImei")
- if Battery.is_battery_sn_format(batteryImei):
- batteryInfo[port] = batteryImei
- return batteryInfo
- def _check_port_status(self, portStr, portInfo):
- """
- 根据 现在端口的内部信息(有无电池) 判定当前的端口状态 有电池为繁忙/无电池为空闲
- :param portStr:
- :param portInfo:
- :return:
- """
- # 先从设备缓存中读取被锁的设备端口列表
- lockPorts = self.device.get("otherConf", dict()).get("lockPorts", list())
- if portStr in lockPorts:
- return Const.DEV_WORK_STATUS_FAULT
- # 有电池的判定为繁忙 没有电池的判定为空闲
- if portInfo.get("batteryImei"):
- return Const.DEV_WORK_STATUS_WORKING
- else:
- return Const.DEV_WORK_STATUS_IDLE
- @staticmethod
- def _is_no_elec_port(portInfo):
- """
- 根据门锁状态以及电池状态 获取 可开启的仓门
- :param portInfo:
- :return:
- """
- doorStatus = portInfo.get("doorStatus")
- batteryImei = portInfo.get("batteryImei")
- if doorStatus == "closed" and not batteryImei:
- return True
- else:
- return False
- def _is_can_use_port(self, portStr, portInfo):
- """
- 检测 端口的电压是否可用
- :param portStr:
- :param portInfo:
- :return:
- """
- otherConf = self.device.get("otherConf", dict())
- lockPorts = otherConf.get("lockPorts", list())
- if portStr in lockPorts:
- return False, "", 0
- canUseVoltage = otherConf.get("canUseVoltage", self.DEFAULT_CAN_USE_VOLTAGE)
- voltage = portInfo.get("voltage")
- batteryImei = portInfo.get("batteryImei")
- # 用户拿电池的时候判断一下看是否被禁用 被禁用的电池直接跳过
- battery = Battery.get_one(self.device.ownerId, batteryImei)
- if battery and battery.disable:
- return False, "", voltage
- if all([voltage, batteryImei]) and voltage > float(canUseVoltage):
- return True, batteryImei, voltage
- else:
- return False, "", voltage
- def _on_point_start(self, chargeIndex):
- """
- 经销商远程上分
- :return:
- """
- otherConf = self._device.get("otherConf", dict())
- devCache = Device.get_dev_control_cache(self._devNo)
- currentUseInfo = devCache.get("currentUseInfo")
- if currentUseInfo and "orderNo" in currentUseInfo:
- raise ServiceException({'result': 2, 'description': u"用户正在使用, 请等待用户使用完成之后再远程上分"})
- result = self._open_door(chargeIndex)
- if not result:
- raise ServiceException({'result': 2, 'description': u'柜门开启失败,请重新试试'})
- # 经销商远程上分之后 也把设备锁死,需要清空一次信息
- currentUseInfo = {
- "openId": "dealer",
- "chargeIndex1": chargeIndex
- }
- Device.update_dev_control_cache(self._devNo, {"currentUseInfo": currentUseInfo})
- otherConf.update({"nextPort": chargeIndex})
- Device.objects.filter(devNo = self._devNo).update(otherConf = otherConf)
- Device.invalid_device_cache(self._devNo)
- return
- def _battery_voltage_warning(self, port, voltage, minVoltage, maxVoltage, batteryImei):
- """
- 电池电压故障报警给经销商
- :param port: 端口号
- :param voltage: 电池当前电压
- :param minVoltage: 允许最大电压
- :param maxVoltage: 允许最小电压
- :param batteryImei: 电池IMEI
- :return:
- """
- dealer = Dealer.objects.get(id = self._device["ownerId"])
- if not dealer or not dealer.managerialOpenId:
- return
- group = Group.get_group(self._device["groupId"])
- notifyData = {
- "title": "设备当前电池<{}>电压不正常".format(batteryImei),
- "device": u"{gNum}组-{lc}-{port}端口".format(gNum = self._device["groupNumber"],
- lc = self._device["logicalCode"], port = port),
- "location": u"{address}-{groupName}".format(address = group["address"], groupName = group["groupName"]),
- "fault": u"当前电压:{},允许最大电压:{},允许最小电压:{}".format(voltage, maxVoltage, minVoltage),
- "notifyTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- }
- task_caller(
- func_name = 'report_to_dealer_via_wechat',
- openId = dealer.managerialOpenId,
- dealerId = str(dealer.id),
- templateName = "device_fault",
- **notifyData
- )
- @property
- def no_elec_door_port(self):
- """
- 一定是直接从设备设置里面读取, 经销商远程上分的时候会重置下一个端口, 第一次一定是经销商上分
- :return:
- """
- portStatus = self.get_port_status_from_dev()
- # 这个地方先存入缓存, 鉴别电池电压之后立即删除
- self._portStatus = portStatus
- otherConf = self._device.get("otherConf", dict())
- nextPort = otherConf.get("nextPort")
- lockPorts = self.device.get("otherConf", dict()).get("lockPorts", list())
- # 双重校验 防止出错
- if nextPort not in lockPorts:
- return nextPort
- @property
- def can_use_voltage_port(self):
- """
- 获取 可用电压的端口号 以及相应的电池的IMEI
- 如果此次操作中 之前从设备上获取过端口的信息 就直接使用该信息 否则再从设备端请求一次
- :return:
- """
- if self._portStatus is None:
- self._portStatus = self.get_port_status_from_dev()
- portStatus = self._portStatus
- canUseList = list()
- for portStr, portInfo in portStatus.items():
- # elec in portInfo 是为了确认这个port表示的是端口信息
- if isinstance(portInfo, dict) and "elec" in portInfo:
- canUse, batteryImei, voltage = self._is_can_use_port(portStr, portInfo)
- if canUse:
- canUseList.append((portStr, batteryImei, voltage))
- Device.update_dev_control_cache(self.device.devNo, {"canUseBattery": len(canUseList)})
- if not canUseList:
- return None, ""
- canUseList.sort(key = lambda x: x[2], reverse = True)
- # 取第一个元素的前两个元素
- return canUseList[0][:2]
- @staticmethod
- def translate_door_status_to_unicode(doorStatus):
- """
- 将柜门状态更改为 汉字表示
- :param doorStatus:
- :return:
- """
- if doorStatus == "opened":
- return u"开启"
- elif doorStatus == "closed":
- return u"关闭"
- else:
- return u"读取失败,状态未知"
- @staticmethod
- def translate_battery_imei(battery):
- """
- 去掉battery中的非法字符
- :param battery:
- :return:
- """
- newBatterySn = ""
- for ch in battery:
- if ch in AnQiBox.BATTERY_SN_ASCII:
- newBatterySn += ch
- return newBatterySn
- def get_port_status_from_dev(self):
- """
- 从设备方获取 换电柜的基本信息 电量百分比 电压 电池IMEI 以及 门锁状态
- 每次获取的时候检测 电池的电压是否正常 非正常的电池电压上报经销商
- :return:
- """
- portStatus = self._query_all_status()
- otherConf = self._device.get("otherConf", dict())
- maxVoltage = float(otherConf.get("maxVoltage", self.DEFAULT_MAX_VOLTAGE))
- minVoltage = float(otherConf.get("minVoltage", self.DEFAULT_MIN_VOLTAGE))
- for portStr, portInfo in portStatus.items():
- voltage = portInfo.get("voltage")
- batteryImei = portInfo.get("batteryImei")
- if voltage and batteryImei and (voltage > maxVoltage or voltage < minVoltage):
- self._battery_voltage_warning(portStr, voltage, minVoltage, maxVoltage, batteryImei)
- return portStatus
- def dealer_get_port_status(self):
- """
- 经销商 上分的时候获取端口状态
- :return:
- """
- portStatus = self.get_port_status_from_dev()
- resultDict = dict()
- for portStr, portInfo in portStatus.items():
- tempStatus = self._check_port_status(portStr, portInfo)
- portInfo.update({"status": tempStatus})
- resultDict[portStr] = portInfo
- return resultDict
- def start_device(self, package, openId, attachParas):
- """
- 换电柜开门的时候,不需要用户选择几号门打开,系统自动选定空仓打开(开门规则在最下注释)
- 但是经销商上分的时候是一定会选择仓门进行打开的
- 除此之外,应当在每次设备启动的时候记录一次缓存信息,最终整个换电结束的时候将此次的信息清空掉,这个缓存信息伴随整个换电过程而生 currentUseInfo
- :param package:
- :param openId:
- :param attachParas:
- :return:
- """
- # 首先获取chargeIndex 如果没有chargeIndex 说明是终端用户使用 有chargeIndex没有openId的说明是经销商的远程上分
- chargeIndex = attachParas.get("chargeIndex")
- if chargeIndex and not openId:
- return self._on_point_start(chargeIndex)
- # 用户启动的,现在可以获取订单号 这个订单号伴随整个换电的过程直到结束
- orderNo = attachParas.get("orderNo")
- # 对于终端用户,需要校验身份认证是否通过
- user = MyUser.objects(openId = openId, groupId = self._device["groupId"]).first()
- if not user:
- raise ServiceException({'result': 2, 'description': u"无效的用户"})
- groupIds = Group.get_group_ids_of_dealer(str(self.device.ownerId))
- activeInfo = MyUser.get_active_info(openId, agentId = user.agentId, groupId__in = groupIds)
- if not activeInfo or not activeInfo.get("isMember"):
- raise ServiceException({'result': 2, 'description': u"请先完成身份激活信息"})
- # 查询缓存,如果当前正在使用的缓存存在,则说明上次的消费尚未结束
- devCache = Device.get_dev_control_cache(self._devNo)
- currentUseInfo = devCache.get("currentUseInfo", dict())
- if currentUseInfo:
- raise ServiceException({'result': 2, 'description': u'上一个客户尚未使用完成,或者尚未关闭柜门,暂时无法使用设备, 请检查是否有柜门尚未关闭。'})
- # 获取即将打开的空仓的信息
- chargeIndex = self.no_elec_door_port
- if not chargeIndex:
- raise ServiceException({'result': 2, 'description': u'当前设备没有可放置电池的柜门,请换个设备试试或联系经销商'})
- # 获取可以开的port以及Imei, 没有可用电池的情况下直接结束当前的服务
- toOpenChargeIndex, toOpenBattery = self.can_use_voltage_port
- if not toOpenChargeIndex:
- raise ServiceException({'result': 2, 'description': u'当前设备无可用电池,请换个设备试试'})
- # 记录下此次换电要开启的有电池的信息
- currentUseInfo.update({
- "toOpenChargeIndex": toOpenChargeIndex,
- "toOpenBattery": toOpenBattery
- })
- # 检验工作通过之后,生下来的就是记录缓存, 开启柜门 播放音乐等行为
- result = self._open_door(chargeIndex)
- if not result:
- raise ServiceException({'result': 2, 'description': u'柜门开启失败,请联系相应经销商解决'})
- self._pay_voice()
- # 记录下此次换电过程的当前信息,换电结束的时候清空掉
- currentUseInfo.update({
- "openId": openId,
- "chargeIndex1": chargeIndex,
- "orderNo": orderNo
- })
- Device.update_dev_control_cache(self._devNo, {"currentUseInfo": currentUseInfo})
- return result
- def analyze_event_data(self, data):
- """
- 换电柜的上报信息是只要门锁状态发生了变化就会上报,这个地方直接将信息透传给eventer,交由eventer处理
- :param data:
- :return:
- """
- cmd = data[12: 14]
- # 主动上传门锁状态变化 当门锁内部有电池的时候 会将电池编号一同上传
- if cmd == "85":
- try:
- batterySn = self.translate_battery_imei(data[20:-2].decode('hex')[8:])
- except Exception as e:
- batterySn = None
- result = {
- "portStr": str(int(data[14:16], 16)),
- "doorStatus": self.DOOR_STATUS_MAP.get(data[16:18])
- }
- if batterySn:
- result.update({"batteryImei": batterySn})
- return result
- else:
- logger.error("no use event cmd, cmd is {}".format(cmd))
- def get_dev_setting(self):
- """
- 返回设备设置的一些常量参数
- :return:
- """
- elecInfo = self._get_charge_limit()
- otherConf = self._device.get("otherConf", dict())
- repairDoorPort = otherConf.get("repairDoorPort", self.DEFAULT_REPAIR_DOOR)
- maxVoltage = otherConf.get("maxVoltage", self.DEFAULT_MAX_VOLTAGE)
- minVoltage = otherConf.get("minVoltage", self.DEFAULT_MIN_VOLTAGE)
- canUseVoltage = otherConf.get("canUseVoltage", self.DEFAULT_CAN_USE_VOLTAGE)
- voiceNum = otherConf.get("voiceNum", self.DEFAULT_VOICE_NUM)
- reverseLockStatus = otherConf.get("reverseLockStatus", self.DEFAULT_REVERSE_LOCK_STATUS)
- devCache = Device.get_dev_control_cache(self._devNo)
- currentUseInfo = devCache.get("currentUseInfo", dict())
- data = {
- "repairDoorPort": repairDoorPort,
- "maxVoltage": maxVoltage,
- "minVoltage": minVoltage,
- "canUseVoltage": canUseVoltage,
- "voiceNum": voiceNum,
- "reverseLockStatus": reverseLockStatus,
- }
- openId = currentUseInfo.get("openId", "")
- if openId == "invalid user":
- currentUser = u"非法开门"
- elif openId == "dealer":
- currentUser = u"经销商远程上分"
- else:
- user = MyUser.objects.filter(openId = openId).first()
- if openId and user:
- currentUser = user.nickname
- else:
- currentUser = u"无用户使用"
- data.update({"currentUser": currentUser})
- currentPort1 = currentUseInfo.get("chargeIndex1")
- currentPort2 = currentUseInfo.get("chargeIndex2")
- currentBatterySn1 = currentUseInfo.get("oldBatteryImei")
- currentBatterySn2 = currentUseInfo.get("newBatteryImei")
- if currentPort1:
- data.update({"currentPort1": currentPort1})
- if currentPort2:
- data.update({"currentPort2": currentPort2})
- if currentBatterySn1:
- data.update({"currentBatterySn1": currentBatterySn1})
- if currentBatterySn2:
- data.update({"currentBatterySn2": currentBatterySn2})
- data.update(elecInfo)
- return data
- def set_device_function_param(self, request, lastSetConf):
- """
- 设置设备参数
- :param request:
- :param lastSetConf:
- :return:
- """
- repairDoorPort = request.POST.get("repairDoorPort")
- maxVoltage = request.POST.get("maxVoltage")
- minVoltage = request.POST.get("minVoltage")
- canUseVoltage = request.POST.get("canUseVoltage")
- voiceNum = request.POST.get("voiceNum")
- maxElec = request.POST.get("maxElec")
- minElec = request.POST.get("minElec")
- otherConf = self._device.get("otherConf", dict())
- if repairDoorPort is not None:
- otherConf.update({"repairDoorPort": repairDoorPort})
- if maxVoltage is not None:
- otherConf.update({"maxVoltage": maxVoltage})
- if minVoltage is not None:
- otherConf.update({"minVoltage": minVoltage})
- if canUseVoltage is not None:
- otherConf.update({"canUseVoltage": canUseVoltage})
- if voiceNum is not None:
- otherConf.update({"voiceNum": voiceNum})
- if maxElec and minElec:
- otherConf.update({"maxElec": maxElec, "minElec": minElec})
- self._set_charge_limit(maxElec = maxElec, minElec = minElec)
- Device.objects.filter(devNo = self._devNo).update(otherConf = otherConf)
- Device.invalid_device_cache(self._devNo)
- def set_device_function(self, request, lastSetConf):
- """
- 设置设备功能参数
- :param request:
- :param lastSetConf:
- :return:
- """
- otherConf = self._device.get("otherConf", dict())
- repairDoorPort = otherConf.get("repairDoorPort", self.DEFAULT_REPAIR_DOOR)
- repairDoor = request.POST.get("repairDoor", False)
- reverseLockStatus = request.POST.get("reverseLockStatus", None)
- clearCurUse = request.POST.get("clearCurUse", False)
- if repairDoor:
- self._open_door(repairDoorPort)
- if reverseLockStatus is not None:
- otherConf.update({"reverseLockStatus": reverseLockStatus})
- Device.objects.filter(otherConf = otherConf)
- Device.invalid_device_cache(self._devNo)
- # 经销商清除当前使用的信息
- if clearCurUse:
- Device.clear_port_control_cache(self._devNo, "currentUseInfo")
- def lock_unlock_port(self, port, lock = True):
- """
- 禁用端口 换电柜禁用端口的时候就是做个标记,设备主板本身不支持禁用
- :param port:
- :param lock:
- :return:
- """
- otherConf = self._device.get("otherConf")
- lockPorts = otherConf.get("lockPorts", list())
- port = str(port)
- try:
- if lock:
- if port not in lockPorts:
- lockPorts.append(port)
- else:
- lockPorts.remove(port)
- except Exception as e:
- logger.error("lock or unlock port error: %s" % e)
- raise ServiceException({'result': 2, 'description': u'操作失败,请重新试试'})
- otherConf.update({"lockPorts": lockPorts})
- try:
- Device.objects(devNo = self._device["devNo"]).update(otherConf = otherConf)
- Device.invalid_device_cache(self._device["devNo"])
- except Exception as e:
- logger.error("update device %s lockPorts error the reason is %s " % (self._device["devNo"], e))
- raise ServiceException({'result': 2, 'description': u'操作失败,请重新试试'})
- def async_update_portinfo_from_dev(self):
- """
- 继承重写,换电柜每次都是获取的最新的端口信息,不再需要这个
- :return:
- """
- pass
- def active_deactive_port(self, port, charge):
- if charge:
- self._turn_on_power(port)
- else:
- self._turn_off_power(port)
- """
- 换电情况有以下种:
- 正常情况
- 用户扫码支付, 打开空仓, 放入电池, 关闭空仓, 取走电池 此时取走的端口号即为下一个开门的仓号
-
- 会重置下一个开门端口的情况:
- 1. 柜门被非法打开,不知道是什么原因柜门被打开,也不知道柜门被非法打开之后会发生什么情况,这个地方需要重置下一个端口,从主板读取
- 2. 经销商远程上分,同样不知道是远程上分打开的是什么柜子,到底有没有放入电池,这个地方需要重置下一个端口,从主板读取
- 3. 用户第二次没有拿走电池,或者用户第二次拿走了电池又放入了电池,必需重置下一个端口,否则会打开有电池的门
- 4. 经销商清除了正在使用的信息
- 5. 用户扫码放入电池后,有电池的没有打开,必须重置,否则会打开上一个用户扫码放入的电池的柜门
- 6. 经销商检修之后
- 7. 禁用某个端口之后
- """
|