123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- # coding=utf-8
- import logging
- import typing
- from apilib.monetary import VirtualCoin
- from apps.web.constant import Const, DeviceCmdCode, MQTT_TIMEOUT
- from apps.web.core.adapter.base import SmartBox
- from apps.web.core.adapter.cmCZ import CZGateway
- from apps.web.core.device_define.cmCZ import PORT_STATUS_MAP, DEV_PREFIX, DEFAULT_ACCOUNT_RULE, CARD_CST, CARD_CST_MIN
- from apps.web.core.exceptions import ServiceException
- from apps.web.device.models import Device
- if typing.TYPE_CHECKING:
- from apps.web.user.models import ConsumeRecord
- logger = logging.getLogger(__name__)
- class CZBox(SmartBox):
- """
- 诚马的插座 实际网络的发送就由网关的发送进行处理
- """
- def __init__(self, device):
- super(CZBox, self).__init__(device)
- self._Gateway = CZGateway(self.masterDevice)
- def _send_data(self, funCode, data, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, timeout=MQTT_TIMEOUT.NORMAL):
- return self._Gateway._send_data(funCode, data, cmd, timeout, addr=int(self.addr))
- @property
- def masterDevice(self):
- devNo = self.device.otherConf.get("master")
- return Device.get_dev(devNo)
- @property
- def addr(self):
- """
- 模块的devNo 的格式类似于 cm-addr
- """
- return self.device.devNo.replace(DEV_PREFIX, "")
- def _read_port_status(self):
- """
- 读取端口状态
- """
- result = self._send_data("0F", "00")
- content = result["data"][16: -2]
- num = int(content[: 2], 16)
- portDict = dict()
- offset = 2
- for _portIndex in range(num):
- _port, _status = content[offset: offset+2], content[offset+2: offset+4]
- portDict[str(int(_port))] = {"status": PORT_STATUS_MAP.get(_status, Const.DEV_WORK_STATUS_FAULT)}
- offset += 4
- return portDict
- def _lock_port(self, port):
- port, status = int(port), 0x00
- data = "{:02X}{:02X}".format(port, status)
- return self._send_data(funCode="0C", data=data)
- def _unlock_port(self, port):
- port, status = int(port), 0x01
- data = "{:02X}{:02X}".format(port, status)
- return self._send_data(funCode="0C", data=data)
- def _stop_port(self, port):
- port, _type = int(port), 0x00
- data = "{:02X}{:02X}".format(port, _type)
- return self._send_data(funCode="0D", data=data)
- def _start_device(self, port, money, _time, elec, sid, _type):
- data = "{:02X}{:04X}{:04X}{:04X}{:08X}{:02X}".format(port, money, _time, elec, sid, _type)
- self._send_data(funCode="27", data=data)
- def _get_port_info(self, port):
- data = "{:02X}".format(int(port))
- result = self._send_data(funCode="2E", data=data)
- content = result["data"][16: -2]
- # 插座的充电模式下 剩余电量和剩余时间没有用
- return {
- "port": int(content[: 2], 16),
- "status": PORT_STATUS_MAP.get(content[2: 4], Const.DEV_WORK_STATUS_FAULT),
- # "leftTime": int(content[4: 8], 16),
- "power": int(content[8: 12], 16),
- # "leftElec": int(content[12: 16], 16) * 0.01,
- # "leftMoney": int(content[16: 20], 16) * 0.1,
- "V": int(content[20: 24], 16) * 0.1,
- "A": int(content[24: 28], 16) * 0.01,
- # "maxTime": int(content[28: 32], 16)
- }
- def _get_port_order(self, port):
- data = {"port": int(port)}
- result = self._send_data(funCode="QO", data=data)
- return result
- def remove_from_gateway(self):
- self._Gateway.remove_node(self.device)
- otherConf = self.device.otherConf
- otherConf.pop("master", None)
- Device.objects.filter(devNo=self.device.devNo).first().update(otherConf=otherConf)
- Device.invalid_device_cache(self.device.devNo)
- @staticmethod
- def _parse_26(data):
- nums, content = int(data[16: 18], 16), data[18: -2]
- portInfo = dict()
- for _portIndex in range(nums):
- offset = 26 * _portIndex
- _port = str(int(content[offset: offset+2], 16))
- portInfo[_port] = dict()
- portInfo[_port]["type"] = content[offset+2: offset+4]
- portInfo[_port]["status"] = PORT_STATUS_MAP.get(content[offset+4: offset+6], Const.DEV_WORK_STATUS_IDLE)
- # portInfo[_port]["leftTime"] = int(content[offset+6: offset+10], 16)
- portInfo[_port]["power"] = int(content[offset+10: offset+14], 16)
- # portInfo[_port]["leftElec"] = int(bin(int(content[offset+14: offset+18], 16))[2:][1:], 2) * 0.01
- portInfo[_port]["V"] = int(content[offset+18: offset+22], 16) * 0.1
- portInfo[_port]["A"] = int(content[offset+22: offset+26], 16) * 0.01
- return portInfo
- @staticmethod
- def _parse_2C(data):
- content = data[16: -2]
- return {
- "portStr": str(int(content[: 2], 16)),
- "leftTime": int(content[2: 6], 16),
- "leftElec": int(content[6: 10], 16) * 0.01,
- "cardNo": str(int(content[10: 18], 16)),
- "cardCst": int(content[18: 22], 16) * 0.1,
- "cardType": content[22: 24],
- "reason": str(content[24: 26]),
- "sessionId": content[26: 34]
- }
- @staticmethod
- def _parse_0A(data):
- content = data[16: -2]
- return {
- "port": str(int(content[: 2], 16)),
- "errorCode": content[2: 6]
- }
- @staticmethod
- def _parse_10(data):
- """
- 解析刷卡上报指令
- """
- content = data[16: -2]
- return {
- "cardNo": str(int(content[: 8], 16)),
- "cardCst": int(content[8:10], 16),
- "cardOpe": int(content[10:12], 16)
- }
- def analyze_event_data(self, data):
- funCode = data[4: 6]
- eventData = {"cmdCode": funCode}
- if funCode == "26":
- eventData["portInfo"] = self._parse_26(data)
- elif funCode == "2C":
- eventData.update(self._parse_2C(data))
- elif funCode == "0A":
- eventData.update(self._parse_0A(data))
- elif funCode == "10":
- eventData.update(self._parse_10(data))
- else:
- logger.info("[CZBox analyze_event_data] device <{}> get un parse event data = {}".format(self.device.devNo, data))
- return
- return eventData
- def get_dev_setting(self):
- otherConf = self.device.otherConf
- return {
- "powerPackage": otherConf.get("rule", DEFAULT_ACCOUNT_RULE["rule"]),
- "powerPackageDefaultPrice": otherConf.get("defaultPrice", DEFAULT_ACCOUNT_RULE["defaultPrice"]),
- "cardCst": otherConf.get("cardCst", CARD_CST),
- "cardCstMin": otherConf.get("cardCstMin", CARD_CST_MIN)
- }
- def set_device_function_param(self, request, lastSetConf):
- powerPackage = request.POST.get("powerPackage", DEFAULT_ACCOUNT_RULE["rule"])
- powerPackageDefaultPrice = request.POST.get("powerPackageDefaultPrice", DEFAULT_ACCOUNT_RULE["rule"])
- cardCst = request.POST.get("cardCst", CARD_CST)
- cardCstMin = request.POST.get("cardCstMin", CARD_CST_MIN)
- # 对参数进行一次数据转换 主要是数据类型
- powerPackageDefaultPrice = float(powerPackageDefaultPrice)
- powerPackage = [{"min": int(_["min"]), "max": int(_["max"]), "price": float(_["price"])} for _ in powerPackage]
- cardCst = float(cardCst)
- cardCstMin = float(cardCstMin)
- for _index, _rule in enumerate(powerPackage):
- if int(_rule["min"]) >= int(_rule["max"]):
- raise ServiceException({"result": 2, "description": u"第{}功率计费区间最小值大于最大值".format(_index)})
- other = {
- "defaultPrice": powerPackageDefaultPrice,
- "rule": powerPackage,
- "cardCst": cardCst,
- "cardCstMin": cardCstMin
- }
- self.device.otherConf.update(other)
- Device.objects.get(devNo=self.device.devNo).update(otherConf=self.device.otherConf)
- Device.invalid_device_cache(self.device.devNo)
- def stop(self, port=None):
- return self._stop_port(int(port))
- def get_port_info(self, port):
- result = self._get_port_order(int(port))
- payload = {
- "port": int(port),
- "status": result["status"],
- "power": result["power"]
- }
- portCache = Device.get_port_control_cache(self.device.devNo, str(port))
- if "spendMoney" in result and "coins" in portCache:
- consumeMoney = VirtualCoin(result["spendMoney"] / 3600)
- leftMoney = VirtualCoin(portCache["coins"]) - consumeMoney
- payload.update({
- "consumeMoney": consumeMoney.amount,
- "leftMoney": leftMoney.amount
- })
- return payload
- def get_port_status(self, force=False):
- if force:
- self.get_port_status_from_dev()
- devCache = Device.get_dev_control_cache(self.device.devNo)
- portStatus = dict()
- for _item in devCache:
- if isinstance(_item, (unicode, str)) and _item.isdigit():
- portStatus[_item] = devCache.get(_item)
- return portStatus
- def get_port_status_from_dev(self):
- result = self._read_port_status()
- allPorts, usedPorts, usePorts = self.get_port_static_info(result)
- Device.update_dev_control_cache(
- self._device.devNo,
- {'allPorts': allPorts, 'usedPorts': usedPorts, 'usePorts': usePorts}
- )
- ctrInfo = Device.get_dev_control_cache(self._device['devNo'])
- for strPort, info in result.items():
- if strPort in ctrInfo:
- ctrInfo[strPort].update({'status': info['status']})
- else:
- ctrInfo[strPort] = info
- Device.update_dev_control_cache(self._device['devNo'], ctrInfo)
- return result
- def start_device_realiable(self, order): # type:(ConsumeRecord) -> dict
- if order.is_on_point:
- raise ServiceException({"result": 2, "description": u"当前设备不支持远程上分,请直接给用户派币启动"})
- attachParas = order.attachParas
- package = order.package
- if attachParas is None:
- raise ServiceException({"result": 2, "description": u"请您选择合适的充电线路"})
- if "chargeIndex" not in attachParas:
- raise ServiceException({"result": 2, "description": u"请您选择合适的充电线路"})
- portInfo = self._get_port_info(attachParas["chargeIndex"])
- if portInfo["status"] != Const.DEV_WORK_STATUS_IDLE:
- raise ServiceException({"result": 2, "description": u"当前端口正在工作中,请选择其他端口"})
- port = int(attachParas["chargeIndex"])
- coins = package.get("coins")
- rule = self.device.otherConf.get("rule", DEFAULT_ACCOUNT_RULE["rule"])
- defaultPrice = self.device.otherConf.get("defaultPrice", DEFAULT_ACCOUNT_RULE["defaultPrice"])
- data = {
- 'order_type': "com_start",
- "balance": coins,
- "rule": rule,
- "defaultPrice": defaultPrice,
- "port": port,
- "order_id": order.orderNo,
- "addr": int(self.addr)
- }
- return self._send_data(funCode="27", data=data)
- def active_deactive_port(self, port, active):
- return self._stop_port(int(port))
- def response_card(self, res, cst, balance, cardNo=None):
- rule = self.device.otherConf.get("rule", DEFAULT_ACCOUNT_RULE["rule"])
- defaultPrice = self.device.otherConf.get("defaultPrice", DEFAULT_ACCOUNT_RULE["defaultPrice"])
- data = {
- "res": res,
- "balance": VirtualCoin(cst).amount,
- "cardBalance": VirtualCoin(balance).amount,
- "rule": rule,
- "defaultPrice": defaultPrice,
- "cardNo": cardNo,
- "addr": int(self.addr)
- }
- return self._send_data(funCode="10", data=data)
|