# coding=utf-8 import logging from apps.web.constant import MQTT_TIMEOUT, DeviceCmdCode, Const from apps.web.core.adapter.base import SmartBox from apps.web.core.device_define.cmCZ import PORT_STATUS_MAP, DEV_PREFIX from apps.web.core.exceptions import ServiceException from apps.web.core.networking import MessageSender from apps.web.device.models import DeviceDict, Device logger = logging.getLogger(__name__) class CZGateway(SmartBox): """ 诚马插座的网络代理 """ def _send_data(self, funCode, data, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, timeout=MQTT_TIMEOUT.NORMAL, addr=None): # 若是网关发送 则addr地址填充为0 payload = { "IMEI": self.device.devNo, "funCode": funCode } if isinstance(data, dict): payload.update(data) payload.update({"addr": addr}) else: addr = "{:08X}".format(addr or 0) payload["data"] = "{}{}".format(data, addr) result = MessageSender.send( device=self.device, cmd=cmd, payload=payload, timeout=timeout ) if "rst" not in result: raise ServiceException({"result": 2, "description": u"串口通信异常(10001)"}) if result["rst"] != 0: if result["rst"] == -1: raise ServiceException({"result": 2, "description": u"网络通信异常"}) if result["rst"] == 1: raise ServiceException({"result": 2, "description": u"串口通信异常(10002)"}) return result def _add_sub_device(self, subAddr): """ 添加一台从机 """ _type, data = 0x01, int(subAddr) data = "{:02X}{:08X}".format(_type, data) result = self._send_data(funCode="37", data=data) if int(result["data"][16: 18], 16) == 0x01: raise ServiceException({"result": 2, "description": u"新增节点数量达到上限,添加失败"}) if int(result["data"][16: 18], 16) == 0x02: raise ServiceException({"result": 2, "description": u"该节点已经存在,无需重复添加"}) def _remove_sub_device(self, subAddr): _type, data = 0x02, int(subAddr) data = "{:02X}{:08X}".format(_type, data) result = self._send_data(funCode="37", data=data) # if int(result["data"][16: 18], 16) == 0x01: # raise ServiceException({"result": 2, "description": u"没有节点数量,删除失败"}) # if int(result["data"][16: 18], 16) == 0x02: # raise ServiceException({"result": 2, "description": u"该设备尚未添加,删除失败"}) def _read_sub_device(self): """ 读取所有的从机 """ result = self._send_data(funCode="38", data="00") content = result["data"][16: -2] num = int(content[: 2], 16) offset = 2 subs = list() for _subIndex in range(num): _sub = int(content[offset: offset+8], 16) subs.append(_sub) offset += 8 return subs def _add_sub_device_many(self, subs): """ 批量的同步从机地址 """ num = len(subs) data = "{:02X}".format(num) for _sub in subs: data += "{:08X}".format(_sub.addr) return self._send_data(funCode="39", data=data) def _get_gateway_channel(self): """ 读取设备的信道 """ result = self._send_data(funCode="42", data="00") channel = int(result["data"][16: 20], 16) return channel def _set_gateway_channel(self, channel): """ 设置设备的信道值 """ data = "{:04X}".format(int(channel)) self._send_data(funCode="43", data=data) @staticmethod def _parse_40(data): """ 解析从机的端口状态同步 经过实际测试 此指令不支持 """ subNums, content = int(data[16: 18], 16), data[18: -2] portContentLen = 13 * 2 result = dict() # 一共两个循环 第一层循环解析从机 第二层循环解析端口 for _subIndex in range(subNums): _subAddr = str(int(content[: 8], 16)) _portNums = int(content[8: 10], 16) _portContent = content[10: portContentLen*_portNums+10] result[_subAddr] = dict() for _portIndex in range(_portNums): _port = str(int(_portContent[portContentLen*_portIndex: portContentLen*_portIndex+2], 16)) result[_subAddr][_port]["status"] = PORT_STATUS_MAP.get(_portContent[portContentLen*_portIndex+4: portContentLen*_portIndex+6], Const.DEV_WORK_STATUS_IDLE) result[_subAddr][_port]["leftTime"] = int(_portContent[portContentLen*_portIndex+6: portContentLen*_portIndex+10], 16) result[_subAddr][_port]["power"] = int(_portContent[portContentLen*_portIndex+10: portContentLen*_portIndex+14], 16) result[_subAddr][_port]["leftElec"] = int(bin(int(_portContent[portContentLen*_portIndex+14: portContentLen*_portIndex+18], 16))[2:][1:], 2) * 0.01 result[_subAddr][_port]["V"] = int(_portContent[portContentLen*_portIndex+18: portContentLen*_portIndex+22], 16) * 0.1 result[_subAddr][_port]["A"] = int(_portContent[portContentLen*_portIndex+22: portContentLen*_portIndex+26], 16) * 0.01 content = content[portContentLen*_portNums+10:] return result def add_node(self, sub): # type:(DeviceDict) -> None self._add_sub_device(sub.deviceAdapter.addr) other = sub.otherConf other["master"] = self.device.devNo # 将子节点的信息同步一次 Device.objects.filter(devNo=sub.devNo).first().update( otherConf=other, coreVer=self.device.coreVer, softVer=self.device.softVer, hwVer=self.device.hwVer, driverCode=self.device.driverCode, driverVersion=self.device.driverVersion ) Device.invalid_device_cache(sub.devNo) def get_dev_setting(self): channel = self._get_gateway_channel() return {"channel": channel} def set_device_function_param(self, request, lastSetConf): channel = request.POST.get("channel") if not channel or int(channel) > 440 or int(channel) < 430: raise ServiceException({"result": 0, "description": u"信道值错误,范围430---440"}) self._set_gateway_channel(int(channel)) def get_node_list(self): subNos = self._read_sub_device() subs = list() for _subNo in subNos: _dev = Device.get_dev("{}{}".format(DEV_PREFIX, _subNo)) if not _dev: continue subs.append(_dev) return subs def remove_node(self, sub): self._remove_sub_device(sub.deviceAdapter.addr) def analyze_event_data(self, data): funCode = data[4: 6] eventData = dict() if funCode == "40": result = self._parse_40(data) eventData["subInfo"] = result else: logger.info("[CZGateway analyze_event_data] device <{}> get un parse event data = {}".format(self.device.devNo, data)) return eventData.update({"cmdCode": funCode}) return eventData def get_event_analyze_parser(self, data): """AA 22 26 055D 5515 010201000100000000800000000000020001000000008000000000001C""" if int(data[6: 14], 16) == 0: return self addr = data[6: 14] devNo = "{}{}".format(DEV_PREFIX, int(addr, 16)) return Device.get_dev(devNo).deviceAdapter