123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479 |
- # -*- coding: utf-8 -*-
- #!/usr/bin/env python
- import time
- from apps.web.constant import Const, DeviceCmdCode, MQTT_TIMEOUT
- from apps.web.core.adapter.base import SmartBox, reverse_hex, hexbyte_2_bin, fill_2_hexByte, pack_float, unpack_float
- from apps.web.core.exceptions import ServiceException
- from apps.web.core.networking import MessageSender
- from apps.web.device.models import Device
- class FunCode(object):
- SET_POWER_MAX = "02"
- GET_POWER_MAX = ""
- GET_POWER_MIN = ""
- SET_POWER_MIN = "16"
- GET_DEV_STATUS = "14"
- START_CHARGING = "06"
- REBOOT_DEV = "0A"
- class ChargingNanjiguangBox(SmartBox):
- MAX_CHARGING_TIME = 2**32 - 1
- MAX_CHARGING_ELEC = 999.0
- DEFAULT_POWER_MAX_LIMIT = 1000
- DEFAULT_DEVICE_MIN_LIMIT = 50
- STATUS_MAP = {
- "00": Const.DEV_WORK_STATUS_IDLE,
- "01": Const.DEV_WORK_STATUS_WORKING,
- "02": Const.DEV_WORK_STATUS_FAULT,
- "03": Const.DEV_WORK_STATUS_FAULT
- }
- def _send_data(self, funcCode, sendData=None, cmd=None, timeout=MQTT_TIMEOUT.NORMAL,orderNo=None):
- """
- 发送 报文
- :param funcCode: 串口命令
- :param sendData: 发送数据
- :param cmd: 报文命令
- :param timeout: 超时时间
- :return:
- """
- if sendData is None:
- sendData = ""
- if cmd is None:
- cmd = DeviceCmdCode.OPERATE_DEV_SYNC
- if timeout is None:
- timeout = MQTT_TIMEOUT.NORMAL
- result = MessageSender.send(device = self.device, cmd = cmd, payload = {
- "IMEI": self._device["devNo"],
- "funCode": funcCode,
- "data": sendData
- }, timeout = timeout)
- if "rst" in result:
- 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 _parse_result_data(result):
- """
- 报文返回结果 返还命令字(1) + 数据长度(2) + 数据域(n) 低前高后
- :param result: 报文回执
- :return:
- """
- data = result.get("data")
- receiveCmd = data[: 2]
- receiveLen = int(reverse_hex(data[2: 6]), 16)
- return receiveCmd, receiveLen, data[6: ]
- @staticmethod
- def _parse_card(cardNo):
- return cardNo
- def _get_device_status(self):
- """
- 从设备端获取设备端口状态 烟感报警 最大功率等
- :return:
- """
- result = self._send_data(funcCode=FunCode.GET_DEV_STATUS)
- _, __ , data = self._parse_result_data(result)
- # 烟感报警字段 暂时不用
- smokeWarning = hexbyte_2_bin(data[: 2])[0]
- portStatusData = data[2: -4]
- powerMax = data[-4: ]
- portStatus = dict()
- portNum = 1
- while portStatusData:
- tempStatus = portStatusData[: 2]
- portStatus[str(portNum)] = {"status": self.STATUS_MAP.get(tempStatus)}
- portStatusData = portStatusData[2: ]
- portNum += 1
- return portStatus
- def _charging(self, port, chargeType, timeStamp, maxTime, maxPower,orderNo=None):
- """
- 授权充电
- :param port: 端口号
- :param chargeType: 充电类型
- :param timeStamp: 时间戳
- :param maxTime: 最大时间
- :param maxPower: 最大电量
- :return:
- """
- portHex = fill_2_hexByte(hex(int(port)), 2)
- chargeHex = fill_2_hexByte(hex(int(chargeType)), 2)
- timeHex = fill_2_hexByte(hex(int(timeStamp)), 8, reverse=True)
- maxTimeHex = fill_2_hexByte(hex(int(maxTime)), 8, reverse=True)
- maxPowerHex = pack_float(maxPower, reverse=True)
- dataHex = portHex + chargeHex + timeHex + maxTimeHex + maxPowerHex
- result = self._send_data(funcCode = FunCode.START_CHARGING, sendData = dataHex,
- timeout = MQTT_TIMEOUT.START_DEVICE, orderNo = orderNo)
- _, __, data = self._parse_result_data(result)
- if data[10: 12] != "00":
- return
- return result
- def _trans_time(self, needTime, unit):
- """
- 根据所给的单位将时间转换成分钟
- :param needTime:
- :param unit:
- :return:
- """
- if unit == u"小时":
- needTime = needTime * 60 * 60
- elif unit == u"分钟":
- needTime = needTime * 60
- elif unit == u"秒":
- needTime = needTime
- else:
- needTime = None
- return needTime
- def _make_time_stamp(self):
- return int(time.time())
- def _reboot(self):
- """
- 重启设备
- :return:
- """
- self._send_data(funcCode=FunCode.REBOOT_DEV)
- def _set_port_max_power(self, powerDict):
- """
- 设置端口 功率上限
- :param powerDict: 如果不存在power则是设置为FF 禁用端口
- :return:
- """
- powerHex = ""
- baseName = "portPower{port}"
- for i in xrange(10):
- power = powerDict.get(baseName.format(port=str(i+1)))
- if power is None:
- powerHex += "FFFFFFFF"
- continue
- powerHex += fill_2_hexByte(hex(int(power)), 8, reverse=True)
- self._send_data(funcCode=FunCode.SET_POWER_MAX, sendData=powerHex)
- def _set_device_power_min(self, minPower):
- """
- 设置最小充电功率
- :param minPower:
- :return:
- """
- minPowerHex = fill_2_hexByte(hex(int(minPower)), 4, reverse=True)
- self._send_data(funcCode=FunCode.SET_POWER_MIN, sendData=minPowerHex)
- def _get_port_max_power(self):
- """
- 获取 端口 功率上限
- :return:
- """
- otherConf = self._device.get("otherConf", {})
- baseName = "portPower{port}"
- portMaxDict = {
- baseName.format(port=str(i+1)): otherConf.get(baseName.format(port=str(i+1)), self.DEFAULT_POWER_MAX_LIMIT) for i in xrange(10)
- }
- return portMaxDict
- def _get_device_min_power(self):
- """
- 获取 设备 停止充电功率
- :return:
- """
- otherConf = self._device.get("otherConf", {})
- devMinPower = otherConf.get("devMinPower", self.DEFAULT_DEVICE_MIN_LIMIT)
- return {"devMinPower": devMinPower}
- def start_device(self, package, openId, attachParas):
- """
- 启动设备
- :param package:
- :param openId:
- :param attachParas:
- :return:
- """
- if "chargeIndex" not in attachParas:
- raise ServiceException({"result": 2, "description": u"未知端口,请联系经销商"})
- portStr = attachParas.get("chargeIndex")
- coins = package.get("coins")
- price = package.get("price")
- unit = package.get("unit")
- needTime = package.get("time")
- needTime = self._trans_time(needTime, unit)
- if needTime is None:
- raise ServiceException({"result": "2", "description": u"套餐错误,请联系经销商"})
- timeStamp = self._make_time_stamp()
- # TODO 还需要考虑充满自停的模式
- chargeType = "01"
- maxPower = self.MAX_CHARGING_ELEC
- result = self._charging(port=portStr, chargeType=chargeType, timeStamp=timeStamp, maxTime=needTime, maxPower=maxPower)
- if result is None:
- raise ServiceException({"result": 2, "description": u"设备启动失败"})
- finishedTime = timeStamp + needTime
- result.update({"finishedTime": finishedTime})
- dataDict = {
- "status": Const.DEV_WORK_STATUS_WORKING,
- "finishedTime": result['finishedTime'],
- "coins": coins,
- "price": price,
- "needTime": needTime / 60,
- "port": portStr,
- "chargeType": chargeType,
- "openId": openId,
- "vCardId": self._vcard_id,
- "isStart": True,
- "needElec": maxPower,
- }
- Device.update_dev_control_cache(self._device["devNo"], {str(portStr): dataDict})
- return result
-
- def get_port_status_from_dev(self):
- """
- :return:
- """
- portStatus = self._get_device_status()
- allPorts, usedPorts, usePorts = self.get_port_static_info(portStatus)
- Device.update_dev_control_cache(self._device['devNo'], {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
- # 逐个更新端口状态
- devCache = Device.get_dev_control_cache(self._device["devNo"])
- for portStr, portInfo in portStatus.items():
- if portStr in devCache:
- devCache[portStr].update(portInfo)
- else:
- devCache[portStr] = portInfo
- Device.update_dev_control_cache(self._device["devNo"], devCache)
- return portStatus
- def get_port_status(self, force = False):
- """
- view 对接函数
- :param force:
- :return:
- """
- if force:
- return self.get_port_status_from_dev()
- devCache = Device.get_dev_control_cache(self._device['devNo'])
- statusDict = {}
- if not 'allPorts' in devCache:
- self.get_port_status_from_dev()
- devCache = Device.get_dev_control_cache(self._device['devNo'])
- allPorts = devCache.get('allPorts', 10)
- for ii in range(allPorts):
- tempDict = devCache.get(str(ii + 1), {})
- if tempDict.has_key('status'):
- statusDict[str(ii + 1)] = {'status': tempDict.get('status')}
- elif tempDict.has_key('isStart'):
- if tempDict['isStart']:
- statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_WORKING}
- else:
- statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
- else:
- statusDict[str(ii + 1)] = {'status': Const.DEV_WORK_STATUS_IDLE}
- allPorts, usedPorts, usePorts = self.get_port_static_info(statusDict)
- Device.update_dev_control_cache(self._device['devNo'], {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts})
- return statusDict
- def get_dev_setting(self):
- """
- 获取设备设置 对接view接口
- :return:
- """
- setConf = dict()
- portMaxPower = self._get_port_max_power()
- devMinPower = self._get_device_min_power()
- setConf.update(portMaxPower)
- setConf.update(devMinPower)
- # setConf.update({"portNum": len(portMaxPower.get("portMaxPower"))})
- return setConf
- def set_device_function_param(self, request, lastSetConf):
- devMinPower = request.POST.get("devMinPower")
- if devMinPower:
- # 更新缓存
- otherConf = self._device["otherConf"]
- otherConf.update({"devMinPower": devMinPower})
- Device.objects.filter(devNo=self._device["devNo"]).update(otherConf=otherConf)
- Device.get_and_update_device_cache(self._device["devNo"], otherConf=otherConf)
- self._set_device_power_min(devMinPower)
- # 端口功率上限设置
- portPower1 = request.POST.get("portPower1")
- if portPower1:
- baseName = "portPower{port}"
- portMaxDict = {
- baseName.format(port=str(i + 1)): request.POST.get(baseName.format(port=str(i + 1))) for i in xrange(10)
- }
- # 更新缓存
- otherConf = self._device["otherConf"]
- otherConf.update(portMaxDict)
- Device.objects.filter(devNo=self._device["devNo"]).update(otherConf=otherConf)
- Device.get_and_update_device_cache(self._device["devNo"], otherConf=otherConf)
- self._set_port_max_power(portMaxDict)
- def set_device_function(self, request, lastSetConf):
- if request.POST.get("reboot"):
- self._reboot()
- def lock_unlock_port(self, port, lock=True):
- """
- 禁用端口
- 经测试 设备不支持禁用端口
- :param port:
- :param lock:
- :return:
- """
- # devCache = Device.get_dev_control_cache(self._device["devNo"])
- # portCache = devCache.get(str(port), {})
- #
- # if lock and portCache.get("status", Const.DEV_WORK_STATUS_IDLE) != Const.DEV_WORK_STATUS_IDLE:
- # raise ServiceException({"result": "2", "description": u"端口非空闲不能设置禁用"})
- # if not lock and portCache.get("status", Const.DEV_WORK_STATUS_IDLE) != Const.DEV_WORK_STATUS_FORBIDDEN:
- # raise ServiceException({"result": "2", "description": u"端口非禁用不能设置解除禁用"})
- #
- # portName = "portPower{}".format(port)
- # portMaxPower = self._get_port_max_power()
- #
- # if lock:
- # portMaxPower[portName] = None
- # Device.update_dev_control_cache(self._device["devNo"], {str(port): {"status": Const.DEV_WORK_STATUS_IDLE}})
- #
- # else:
- # otherConf = self._device["otherConf"]
- # portMaxPower[portName] = otherConf.get(portName, self.DEFAULT_POWER_MAX_LIMIT)
- #
- # Device.update_dev_control_cache(self._device["devNo"], {str(port): {"status": Const.DEV_WORK_STATUS_FORBIDDEN}})
- #
- # self._set_port_max_power(portMaxPower)
- pass
- def analyze_event_data(self, data):
- """
- 解析上报的报文
- :param data:
- :return:
- """
- cmdCode = data[36: 38].upper()
- # 结束充电
- if cmdCode == "08":
- portStr = str(int(data[42: 44], 16))
- chargeType = data[44: 46]
- timeStamp = int(reverse_hex(data[46: 54]), 16)
- spendTime = int(reverse_hex(data[54: 62]), 16)
- # spendElec = int(reverse_hex(data[62: 80]), 16)
- spendElec = unpack_float(data[62: 70], reverse=True)
- return {
- "port": portStr,
- "chargeType": chargeType,
- "timeStamp": timeStamp,
- "spendTime": spendTime,
- "spendElec": spendElec,
- "cmdCode": cmdCode,
- "portHex": data[42: 44],
- "timeStampHex": data[46: 54]
- }
- # 刷卡充电开始
- elif cmdCode == "0C":
- portStr = str(int(data[42: 44], 16))
- chargeType = data[44: 46]
- timeStamp = int(reverse_hex(data[46: 54]), 16)
- cardNoLen = int(data[54: 56], 16) * 2
- cardNo = self._parse_card(data[56: 56+cardNoLen])
- coins = round(unpack_float(data[56+cardNoLen: 64+cardNoLen], reverse=True), 2)
- return {
- "port": portStr,
- "chargeType": chargeType,
- "timeStamp": timeStamp,
- "cardNo": cardNo,
- "coins": coins,
- "cmdCode": cmdCode,
- "portHex": data[42: 44],
- "timeStampHex": data[46: 54]
- }
- # 刷卡充电结束
- elif cmdCode == "0E":
- portStr = str(int(data[42: 44], 16))
- chargeType = data[44: 46]
- timeStamp = int(reverse_hex(data[46: 54]), 16)
- spendTime = int(reverse_hex(data[54: 62]), 16)
- spendElec = unpack_float(data[62: 70], reverse=True)
- return {
- "port": portStr,
- "chargeType": chargeType,
- "timeStamp": timeStamp,
- "spendTime": spendTime,
- "spendElec": spendElec,
- "cmdCode": cmdCode,
- "portHex": data[42: 44],
- "timeStampHex": data[46: 54]
- }
- # 推送的报警信息
- elif cmdCode == "12":
- timeStamp = int(reverse_hex(data[42: 50]), 16)
- warningCode = data[50: 52]
- return {
- "timeStamp": timeStamp,
- "warningCode": warningCode,
- "cmdCode": cmdCode,
- "timeStampHex": data[42: 50]
- }
- else:
- return
|